본문 바로가기
Development

파이썬(Python) 모듈 동적 로딩하기

by _Jay_ 2022. 5. 7.
반응형

작년에 개인적으로 파이썬을 이용하여 안티바이러스를 구현하는 프로젝트를 진행했었다. 다른 것 보다도 특히 코드 보호에 신경을 쓰면서 구현했었는데, 여러 방법 중에서 자신이 만든 모듈을 동적으로 로딩하는 부분에 대해서 소개하고자 한다.

 

파이썬을 비롯한 다양한 언어에서 모듈 동적 로딩의 경우, 자신이 만든 코드를 남들에게 보이고 싶지 않을 때나 코드가 변조되어선 안되는 상황에서 많이 사용하게 된다. 내가 구현하는 프로젝트에서는 파이썬으로 제작한 모듈을 바이트 코드로 변환 후, 이를 암호화하여 배포하고 프로그램 실행 시 메모리에서 모듈을 복호화하여 동적 로딩을 통해 사용하도록 하였다.

 

물론 이 방법도 복호화하여 모듈을 로딩하는 순간에 pyc 코드를 별도로 저장하여 추출한 뒤 디컴파일 한다면 코드가 유출될 수 있지만, 모듈 로더 부분을 스크립트가 아닌 exe 파일로 배포하여 어렵게 만드는 식으로 대응을 할 수 있다. 여기서는 바이트 코드를 암복호화하는 부분은 제외하고, 자신이 만든 모듈을 바이트 코드화 시켜서 이를 메모리 상에 가져와서 동적으로 로딩하는 프로세스를 설명하겠다.

 

실제 환경에서는 암호화된 모듈을 복호화하거나, 서버와 통신을 통해 실행 코드를 받아와서 로딩하는 식으로 구현할 수 있다. 먼저 파이썬 코드를 바이트 코드로 변환하기 위해 'hello.py' 파일을 만들고, 파일이 있는 위치에서 파이썬 인터프리터를 실행하여 py_compile 모듈을 import하고 컴파일 시킨다. 여기서는 단순하게 hello를 출력하는 printHello함수 하나를 작성하였다.

 

>>> import py_compile
>>> py_compile.compile('hello.py', 'hello.pyc')
'hello.pyc'

그러면 같은 디렉토리에 pyc 파일이 생성된 것을 확인 할 수 있으며 'hello.py' 파일을 삭제하고 'python hello.pyc' 명령어를 입력하여도 동일하게 동작하는 것을 확인할 수 있다. 해당 바이트 코드를 파이썬 코드에서 import하는 형태가 아닌, 바이트 코드 자체를 읽어와서 동적으로 로딩하도록 아래와 같은 코드를 작성한다.

 

# load.py
import types    # 동적 모듈 생성과 관련된 모듈로 파이썬 2버전에서는 imp 모듈을 사용함
import marshal    # 코드 로딩과 관련된 모듈

pyc = open('hello.pyc', 'rb').read()    # 바이트 코드를 읽어옴
code = marshal.loads(pyc[16:])    # pyc에서 헤더를 제외한 코드를 로딩함
module = types.ModuleType('hello')    # 'hello'라는 새로운 모듈 생성, 파이썬2에서는 imp.new_module() 함수를 사용할 수 있음

exec(code, module.__dict__)    # pyc 코드와 모듈을 연결함
module.printHello()    # hello 모듈의 printHello() 함수 호출

 

marshal.loads() 함수의 헤더를 제외하고 코드는 파이썬 버전 별로 상이할 수 있다(3.3버전 이하는 헤더의 크기가 8, 3.7미만은 12, 3.7이상은 16).

실제 환경에서는 pyc가 아닌 암호화된 파일로 배포한 후, 이를 다시 복호화하는 과정이 추가되고, 로딩하는 코드 또한 잘 숨겨져야 코드가 안전하게 보호될 수 있다. 실제 기업에서 소프트웨어를 개발할 때 중요 로직에 대한 코드를 보호하는데 신경쓰고 있는데, 이와 더불어 암호화/난독화 같은 코드 보호 기법을 같이 이용한다면 좀 더 안전하게 개발을 진행할 수 있을 것이다.

 

반응형

댓글