리버싱을 하다보면 필요에 따라 자신만의 툴을 만드는 경우가 종종 있습니다.
툴을 만들다보면 윈도우 API 를 사용해야 할 때가 있는데...
파이썬에서 ctypes 로 윈도우 API 를 사용하는 방법을 정리해봤습니다.
우선은 ctypes 와 ctypes.wintypes(자료형 모음) 를 임포트 해줍니다.
1 2 | from ctypes import * from ctypes.wintypes import * |
윈도우 API 는 대체로 windll 을 이용하면 되는데요~
아래처럼 모듈 이름과 함수 이름을 적어주면 됩니다.
1 2 3 4 5 6 7 8 9 | # 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
1 2 3 4 5 6 7 8 | 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 을 직접 지정해줘야 주소값을 제대로 가져옵니다.
1 2 | Kernel32.GetModuleHandleW.restype = c_void_p Kernel32_BaseAddr = Kernel32.GetModuleHandleW( "KERNEL32.DLL" ) |
API 사용 예 #2 - GetModuleFileNameW
1 2 3 4 5 6 7 8 9 10 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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 는 문제없이 사용할 수 있을거라 생각합니다. @_@;;;
감사합니다! 많이 배우고 가네요
답글삭제방문 감사합니다~ :)
삭제관련해서 찾고 있었는데 도움이 많이 됬네요. 감사합니다!
답글삭제도움이 되었다니 뭔가 뿌듯하네요~ :)
삭제