리버싱을 하다보면 필요에 따라 자신만의 툴을 만드는 경우가 종종 있습니다.
툴을 만들다보면 윈도우 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 는 문제없이 사용할 수 있을거라 생각합니다. @_@;;;