리버싱을 하다보면 필요에 따라 자신만의 툴을 만드는 경우가 종종 있습니다.
툴을 만들다보면 윈도우 API 를 사용해야 할 때가 있는데...
파이썬에서 ctypes 로 윈도우 API 를 사용하는 방법을 정리해봤습니다.
우선은 ctypes 와 ctypes.wintypes(자료형 모음) 를 임포트 해줍니다.
from ctypes import * from ctypes.wintypes import *
윈도우 API 는 대체로 windll 을 이용하면 되는데요~
아래처럼 모듈 이름과 함수 이름을 적어주면 됩니다.
# Type 1 GetModuleFileName = windll.kernel32.GetModuleFileNameW GetModuleHandle = windll.kernel32.GetModuleHandleW # Type 2 Kernel32 = windll.kernel32 GetModuleFileName = Kernel32.GetModuleFileNameW GetModuleHandle = Kernel32.GetModuleHandleW
입력하기 쉽게 "GetModuleFileName", "GetModuleHandle" 로 했을 뿐...
windll.kernel32.GetModuleFileNameW / Kernel32.GetModuleFileNameW 이나
windll.kernel32.GetModuleHandleW / Kernel32.GetModuleHandleW 를 그대로 사용해도 됩니다.
사용하려는 API 가 많을 경우 Type 2 처럼 사용하는게 더 편하지 않을까 생각되네요 ^^;;;
API 사용 예 #1 - GetModuleHandleW
from ctypes import * from ctypes.wintypes import * Kernel32 = windll.kernel32 print("[*] GetModuleHandleW [*]") Kernel32_BaseAddr = Kernel32.GetModuleHandleW("KERNEL32.DLL") print(" - KERNEL32.DLL = 0x%X" % Kernel32_BaseAddr)
GetModuleHandleW 호출 결과 |
가장 단순한 형태의 사용 예입니다. 그냥 인자를 넣어주기만 하면 되는거죠~ :)
참고로 64비트 파이썬에서는 GetModuleHandleW 를 호출하기 전에...
restype 을 직접 지정해줘야 주소값을 제대로 가져옵니다.
Kernel32.GetModuleHandleW.restype = c_void_p Kernel32_BaseAddr = Kernel32.GetModuleHandleW("KERNEL32.DLL")
API 사용 예 #2 - GetModuleFileNameW
from ctypes import * from ctypes.wintypes import * Kernel32 = windll.kernel32 path = create_unicode_buffer(MAX_PATH) print("[*] GetModuleFileNameW [*]") Kernel32.GetModuleFileNameW(0, path, MAX_PATH) print(" - Path = %s" % path.value)
GetModuleFileNameW 호출 결과 |
윈도우 API 중에는 GetModuleFileName 처럼 데이터를 담을 버퍼를 인자로 받아서
그 버퍼에 데이터를 넘겨주는 방식도 있습니다.
create_string_buffer, create_unicode_buffer 로 데이터를 담을 수 있는 객체를 만들 수 있는데요...
create_string_buffer 는 C 언어의 "char *", 파이썬의 "bytes" 와 대응되며,
create_unicode_buffer 는 C 언어의 "wchar *", 파이썬의 "str" 과 대응됩니다.
~A 계열 함수를 사용할 때는 create_string_buffer 를 사용하고,
~W 계열 함수를 사용할 때는 create_unicode_buffer 를 사용하면 됩니다.
객체에 담겨진 실제 데이터는 value 를 통해 얻을 수 있습니다.
create_string_buffer / create_unicode_buffer |
API 사용 예 #3 - CreateProcessW
from ctypes import * from ctypes.wintypes import * class PROCESS_INFORMATION(Structure): _fields_ = [("hProcess", HANDLE), ("hThread", HANDLE), ("dwProcessId", DWORD), ("dwThreadId", DWORD)] class STARTUPINFO(Structure): _fields_ = [('cb', DWORD), ('lpReserved', LPWSTR), ('lpDesktop', LPWSTR), ('lpTitle', LPWSTR), ('dwX', DWORD), ('dwY', DWORD), ('dwXSize', DWORD), ('dwYSize', DWORD), ('dwXCountChars', DWORD), ('dwYCountChars', DWORD), ('dwFillAttribute', DWORD), ('dwFlags', DWORD), ('wShowWindow', WORD), ('cbReserved2', WORD), ('lpReserved2', LPBYTE), ('hStdInput', HANDLE), ('hStdOutput', HANDLE), ('hStdError', HANDLE)] Kernel32 = windll.kernel32 startupinfo = STARTUPINFO() processinfo = PROCESS_INFORMATION() print("[*] CreateProcessW [*]") Kernel32.CreateProcessW("C:\\Windows\\NOTEPAD.exe", None, None, None, 0, 0, None, None, byref(startupinfo), byref(processinfo)) print(" - hProcess = %X" % processinfo.hProcess) print(" - dwProcessId = %d (%X)" % (processinfo.dwProcessId, processinfo.dwProcessId))
CreateProcessW 호출 결과 |
앞의 두 예제와 비교하면 코드의 양이 꽤 깁니다...;;; ( 이게 다 구조체 때문임.. =_=;;;; )
파이썬은 C 언어의 '구조체' 를 그대로 사용할 수 없기 때문에...
'구조체' 를 인자로 받는 API 를 사용하기 위해서는 추가 작업이 필요합니다.
ctypes 의 "Structure" 클래스를 상속받아서 임의의 클래스를 만든 다음...
"_fields_" 에 구조체 멤버들을 추가해주면 됩니다.
함수 인자에 참조 연산자('&')를 사용하는 경우가 있는데 파이썬은 "byref" 를 이용하면 됩니다.
이상의 세가지 형태의 API 사용 방법을 숙지하고 있으면...
대부분의 윈도우 API 는 문제없이 사용할 수 있을거라 생각합니다. @_@;;;