본문 바로가기
SW 개발/Python

Python code 숨기는 방법: PyInstaller로 실행 파일 만들기

by Kibua20 2020. 7. 21.

파이썬을 작성한 코드는 byte code인 pyc파이로 변환하더라도 디컴파일이 쉽게 되기 때문에 코드를 숨길 수가 없습니다. (참고) 이에 비해 PyInstaller는 Py파일을 실행파일로 만들 수 있어 쉽게 코드를 숨길 수 있고, 필요한 lib 도 포함하고 있어 상용 서버에 파이썬으로 개발한 SW를 배포하기에 편리합니다. PyInstaller에 대한 설명은 아래 동영상을 참고해주세요.  ※ 실행파일(.exe or ELF)도 Reverse Engineering으로 디컴파일이 가능하기 때문에 절대 신뢰하면 안되고, py나 pyc 보다는  그나마 코드를 숨기는 방법입니다. 

PyInstaller 설명 (출처: https://www.youtube.com/watch?v=RMkPGjGhzxg)

주의해야 점

  • PyInstaller는 C로 컴파일하고 shared lib를 사용하기 때문에  Windows와 Linux 호환이 안되며, 32 bit와 64bit 환경에 각각 배포 파일을 생성해야 함
출처: https://pyinstaller.readthedocs.io/en/stable/usage.html#platform-specific-notes

Under GNU/Linux, 
PyInstaller does not bundle libc (the C standard library, usually glibc, the Gnu version) with the app. Instead, the app expects to link dynamically to the libc from the local OS where it runs. The interface between any app and libc is forward compatible to newer releases, but it is not backward compatible to older releases.

For this reason, if you bundle your app on the current version of GNU/Linux, it may fail to execute (typically with a runtime dynamic link error) if it is executed on an older version of GNU/Linux.

The solution is to always build your app on the oldest version of GNU/Linux you mean to support. It should continue to work with the libc found on newer versions.

 

The GNU/Linux standard libraries such as glibc are distributed in 64-bit and 32-bit versions, and these are not compatible. As a result you cannot bundle your app on a 32-bit system and run it on a 64-bit installation, nor vice-versa. You must make a unique version of the app for each word-length supported.

 

 

PyInstaller 설치 

PyInstaller 설치는 Python 2.7+ 버전 이상, 3.5+ 이상에서 설치를 해야 합니다. 우분투 14.04 버전인 경우 3.4 버전이 디폴트로 설치되어 있어 3.5 버전을 추가 설치해야 합니다. (방법)  우분투16.04 버전부터는 Python 버전이 3.5이상 버전이 기본 설치되어 있어 추가 설치는 필요하지 않습니다. 

 

$ sudo pip3 install pyinstaller

pyinstaller 설치 

PyInstaller 사용 방법:  (참고 사이트) 

Pyinstaller 사용법은  pyinstaller [test.py] 파일로 실행하면  ./dist 폴더에 하나의 폴더 또는 실행 파일로 만듭니다.  필요한 옵션은 붉은색으로 표시했습니다.  특히 Key 옵션은 AES256으로 Bytecode를 암호화하기 때문에 코드를 숨길 때 유용합니다Key 옵션을 사용하기 위해서는 PyCrypto 설치가 필요합니다. 

 

사용 법:

pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
    [--add-data <SRC;DEST or SRC:DEST>]
    [--add-binary <SRC;DEST or SRC:DEST>] [-p DIR]
   [--key KEY] [-d {all,imports,bootloader,noarchive}] [-s]

 

optional arguments:
--distpath DIR Where to put the bundled app (default: ./dist)
--workpath WORKPATH Where to put all the temporary work files, .log, .pyz and etc. (default: ./build)
-y, --noconfirm Replace output directory (default: SPECPATH/dist/SPECNAME) without asking for confirmation 
--upx-dir UPX_DIR Path to UPX utility (default: search the execution path)
-a, --ascii Do not include unicode encoding support (default: included if available)
--clean Clean PyInstaller cache and remove temporary files before building.

--log-level LEVEL Amount of detail in build-time console messages. LEVEL
may be one of TRACE, DEBUG, INFO, WARN, ERROR,
CRITICAL (default: INFO).

 

What to generate:
  -D, --onedir          Create a one-folder bundle containing an executable  (default)
  -F, --onefile         Create a one-file bundled executable.   (하나의 실행 파일로 생성)
  --specpath DIR        Folder to store the generated spec file (default:    current directory)
  -n NAME, --name NAME  Name to assign to the bundled app and spec file  (실행 파일 이름을 변경)

 

key 옵션

Key 옵션을 사용하기 위해서는 PyCrypto가 설치해야 합니다. key 옵션 설명은 아래와 같습니다. 코드 보안을 위해서는 꼭 key 옵션을 사용하고 키는 외부로 유출하면 안됩니다.

 

$ sudo pip3 install PyCrypto  

 

To encrypt the Python bytecode modules stored in the bundle, pass the --key=key-string argument on the command line. For this to work, you must have the PyCrypto module installed. The key-string is a string of 16 characters which is used to encrypt each file of Python byte-code before it is stored in the archive inside the executable file.ip install pycrypto

 

PyInstaller 실행 결과 (폴더로 생성)

$pyinstaller hello.py을 실행하면 아래와 같이 Py 파일의 종속성을 분석하고 필요한 lib을 /dist 폴더에 copy 합니다   실행 결과는 /dist에 hello elf와 필요한 shared lib가 같이 포함되어 있습니다. 

pyinstaller 실행 
pyinstaller 실행 결과: 하나의 폴더로 생성

 

PyInstaller 실행 결과 (Byte code 암호화 및  실행 파일 하나로 만들기)

PyInstaller 실행 시 -F 옵션과 --key 옵션을 사용해서 아래와 같이 실행합니다. 키 값으로 임의의 16 자리 스트링을 전달하면 해당 값을 기준으로 암호화합니다. 

 

$ pyinstaller -F  --clean --key 123456789123456789 hello.py

pyinstaller 실행 결과: 하나의 파일로 생성

 

PyInstaller 에러 디버깅

PyInstaller에서 실행 파일 빌드 시 'OSError: Python library not found'가 발생하는 경우가 있습니다. 이 문제는 Python 설치 시 python의 share lib가 설치가 안되어 발생하는 것으로, shared lib를  apt install python-dev 또는 python3-dev로 설치해야 합니다.  만일 Python을 여러 버전을 설치했다면 각각 버전의 shared lib를 설치해야 합니다. 

 

* 에러 메시지: 

5115 INFO: Python library not in binary depedencies. Doing additional searching...
Traceback (most recent call last):

(중략) 

File "/usr/local/lib/python3.4/site-packages/PyInstaller/building/build_main.py", line 626, in  check_python_library
raise IOError(msg)

OSError: Python library not found: libpython3.4m.so.1.0, libpython3.4mu.so.1.0, libpython3.4.so.1.0
This would mean your Python installation doesn't come with proper library files.
This usually happens by missing development package, or unsuitable build parameters of Python installation.

* 해결 방법:

sudo apt install python-dev

sudo apt install python3-dev

sudo apt install python3.5-dev    # 여러 버전이 설치된 경우 사용하는 Python 버전의 dev lib를 설치

 

 

관련 글

[모바일 SW 개발/Python] - Python 여러 버전 설치 방법 (3.x and 3.y 동시 설치)

[모바일 SW 개발/Python] - Python 폴더 및 파일 처리 함수 모음

[모바일 SW 개발/Python] - Python: 폴더 백업 기능 구현 (7zip 압축, Sample code)

[모바일 SW 개발/REST API] - JWT(JSON Web Token) Encoding 방법 (Python sample code)

[모바일 SW 개발/Python] - Python 에러: /usr/bin/env: `python3\r': 그런 파일이나 디렉터리가 없습니다

[모바일 SW 개발/Python] - Python 소스 숨기는 방법: pyc 활용 (Bytecode로 컴파일)

[모바일 SW 개발/Python] - Python 표준 입출력(stdin/stdout) 활용 - 리눅스 프로그램과 연동

[모바일 SW 개발/Python] - Python JSON 사용 시 TypeError: Object of type bytes is not JSON serializable

[모바일 SW 개발/Python] - Python smtplib 사용한 email 발송 예제 (gmail)

[모바일 SW 개발/Python] - Python SyntaxError: Non-ASCII character in file on, but no encoding declared

[모바일 SW 개발/Python] - Python 2.7과 3.8호환성: a bytes-like object is required, not 'str'에러 수정

[개발환경] - [실패 사례] gcc 버전이 낮은 상용 리눅스 서버에서 프로그램 설치 시 GLIBCXX' not found 에러

[모바일 SW 개발/Python] - [Tips] Python: XML Parsing 시 multiple elements on top level

[모바일 SW 개발/Python] - [Tips] Python 에서 XML comment 처리 - Sample code 제공




댓글