Python 바이트코드 컴파일 및 디컴파일 기술 분석

Python에서의 다양한 파일 확장자와 역할

Python 개발 과정에서는 여러 종류의 파일 형식이 사용되며, 각각은 고유한 목적과 처리 방식을 가집니다.

.py – 소스 코드 파일

일반적인 Python 스크립트 파일로, 텍스트 에디터로 편집 가능하며 인터프리터에 의해 직접 실행됩니다. 이 파일은 런타임 시점에 파싱되고 바이트코드로 변환되어 실행됩니다.

.pyc – 컴파일된 바이트코드 파일

  • Python 인터프리터가 .py 파일을 컴파일하여 생성하는 바이너리 형식의 출력물입니다.
  • 실행 속도 향상을 위해 사용되며, 동일한 소스가 반복 실행될 때 재컴파일을 방지합니다.
  • 파이썬 버전마다 호환성이 달라, 특정 버전에서 생성된 .pyc는 다른 버전에서 로드되지 않을 수 있습니다.
  • 기본적으로 __pycache__ 디렉터리 내부에 module_name.cpython-XX.pyc 형태로 저장됩니다.

.pyo – 최적화된 바이트코드 (Python 3.5 이전)

옵션 -O 또는 -OO를 사용해 실행 시 생성되는 최적화된 바이트코드입니다. Python 3.5 이후부터는 .pyo 확장자를 사용하지 않고, 대신 .opt-1.pyc 또는 .opt-2.pyc 형식으로 구분됩니다.

.pyd – 다이내믹 링크 라이브러리 모듈

윈도우 환경에서 C/C++ 등으로 작성된 외부 확장 모듈을 의미합니다. 실제로는 DLL이지만 Python에서 임포트 가능한 형식으로 제공되며, 주로 성능이 중요한 부분을 네이티브 코드로 구현할 때 사용됩니다.

.pyz – 압축된 실행 가능 파이썬 아카이브

Python 3.5부터 도입된 ZIP 기반 패키징 형식으로, 하나의 파일 안에 전체 애플리케이션과 의존성을 포함할 수 있습니다. 표준 라이브러리 zipapp 모듈을 통해 생성 및 관리할 수 있으며, 단일 배포 파일로서 유용합니다.

바이트코드 파일 생성 방법

.pyc 파일은 자동으로 생성되지 않으며, 명시적인 트리거가 필요합니다. 일반적으로 다음과 같은 두 가지 모듈을 활용합니다:

단일 파일 컴파일: py_compile

import py_compile
py_compile.compile('example.py')

이 코드는 현재 디렉터리에 __pycache__/example.cpython-39.pyc와 같은 이름의 파일을 생성합니다.

디렉터리 전체 컴파일: compileall

import compileall
compileall.compile_dir('src/')

또는 커맨드라인에서:

python -m compileall src/

모든 하위 디렉터리의 .py 파일들을 재귀적으로 찾아 바이트코드로 변환합니다.

최적화된 컴파일 수행

명령어에 -O 플래그를 추가하면 최적화된 바이트코드가 생성됩니다:

python -O -m compileall .

결과로 example.cpython-39.opt-1.pyc 파일이 생성되며, 이는 docstring 제거 및 일부 어설션 비활성화 등의 최적화를 포함합니다.

pyc 파일 직접 실행

생성된 .pyc 파일은 Python 인터프리터로 직접 실행 가능합니다:

python __pycache__/sample.cpython-39.pyc

출력 결과는 원본 .py 파일 실행 시와 동일하게 나타납니다. 이는 독립형 배포나 보안 감사 시 유용하게 활용될 수 있습니다.

바이트코드 역컴파일 도구: uncompyle6 활용법

uncompyle6는 다양한 파이썬 버전의 바이트코드를 원본 소스 코드로 복원할 수 있는 강력한 도구입니다.

설치 절차

pip install uncompyle6

설치 후 다음과 같이 기본 정보를 확인할 수 있습니다:

pip show uncompyle6

실제 디컴파일 예제

특정 .pyc 파일을 소스로 복원하는 명령:

uncompyle6 __pycache__/main.cpython-39.pyc > restored.py

결과 파일 restored.py에는 원본과 매우 유사한 코드가 포함됩니다:

# uncompyle6 version 3.8.0
# Python bytecode 3.9 (3425)
# Decompiled from: Python 3.9.1
# Embedded file name: main.py
print('Hello Python !')
# okay decompiling main.cpython-39.pyc

디렉터리 전체 재귀 디컴파일

다수의 파일을 한 번에 처리하기 위해 -r 옵션을 사용할 수 있습니다:

uncompyle6 -r -o ./output /path/to/pyc/files

이 경우 모든 .pyc/.pyo 파일이 탐색되어 지정된 출력 경로에 복원됩니다.

검증 기능 사용

--verify 플래그를 통해 복원된 코드가 원본 바이트코드와 일치하는지 검사할 수 있습니다:

uncompyle6 --verify sample.cpython-39.pyc

이 기능은 정확한 리버스 엔지니어링을 보장하는 데 중요합니다.

대체 디컴파일 도구 소개

Decompyle++

C++ 기반의 고성능 디컴파일러로, 일부 복잡한 바이트코드에 대해 더 나은 복원률을 제공합니다. GitHub 저장소에서 소스 코드를 제공하며, 컴파일 후 사용 가능합니다.

  • 주요 장점: 빠른 처리 속도, 간결한 출력
  • 단점: 최신 파이썬 문법 지원이 제한적일 수 있음

Easy Python Decompiler

GUI 기반 도구로, 기술적 배경이 부족한 사용자에게 적합합니다. SourceForge에서 배포되며, 드래그앤드롭 방식으로 .pyc 파일을 열 수 있습니다.

주의할 점은 오랜 시간 업데이트되지 않았을 가능성과, 최신 언어 기능에 대한 지원 부족입니다.

보안 및 법적 고려사항

디컴파일은 학습 및 정당한 유지보수 목적이 아닌 경우 저작권 침해로 간주될 수 있습니다. 항상 라이선스 조건을 확인하고, 허용 범위 내에서만 사용해야 합니다.

태그: python bytecode pyc pyo decompilation

7월 24일 08:41에 게시됨