10.4.4 使用ctypes调用kernel32.dll中的函数
10.4.4 使用ctypes調用kernel32.dll中的函數
2007-10-17 14:41 孫廣磊 人民郵電出版社 字號:T | T綜合評級:
想讀(5)??在讀(0)??已讀(6)?? 品書齋鑒(1)?? 已有11人發表書評
《征服Python—語言基礎與典型應用》第十章主要講的是系統編程,本節介紹了使用ctypes調用kernel32.dll中的函數。
AD:2014WOT全球軟件技術峰會北京站 課程視頻發布
10.4.4? 使用ctypes調用kernel32.dll中的函數
使用ctypes模塊可以使Python調用位于動態鏈接庫中的函數。在Python 2.5版中已經包含了ctypes模塊。如果使用其他版本的Python,可以到http://python.net/crew/theller/ctypes網站下載安裝。ctypes適用于Python 2.3版本及以上。
1.ctypes簡介
ctypes為Python提供了調用動態鏈接庫中函數的功能。使用ctypes可以方便地調用由C語言編寫的動態鏈接庫,并向其傳遞參數。ctypes定義了C語言中的基本數據類型,并且可以實現C語言中的結構體和聯合體。ctypes可以工作在Windows、Windows CE、Mac OS X、Linux、Solaris、FreeBSD、OpenBSD等平臺上,基本上實現了跨平臺。
以下的實例使用ctypes實現了在Windows下直接調用user32.dll中的MessageBoxA函數。運行后如圖10-6所示。
| >>> from ctypes import * >>> user32 = windll.LoadLibrary('user32.dll')????# 加載動態鏈接庫 >>> user32.MessageBoxA(0, 'Ctypes is cool!', 'Ctypes', 0) # 調用MessageBoxA函數 |
1
圖10-6? 使用ctypes
2.數據類型與結構體
ctypes實現C語言的基本數據類型,如表10-2所示列出了幾個基本的數據類型的對照。
表10-2?????????? ?數據類型對照
| ctypes數據類型 | C數據類型 | ctypes數據類型 | C數據類型 |
| c_char | char | c_float | float |
| c_short | short | c_double | double |
| c_int | int | c_void_p | void * |
| c_long | long | ? | ? |
在Python中要實現C語言的結構體,需要使用類。在Python中使用ctypes實現Windows中的PROCESS_INFORMATION結構體如下所示。
| typedef struct _PROCESS_INFORMATION { HANDLE hProcess;? HANDLE hThread;? DWORD dwProcessId;? DWORD dwThreadId; } PROCESS_INFORMATION, *LPPROCESS_INFORMATION; 在Python中由ctypes實現。 class _PROCESS_INFORMATION(Structure): _fields_ = [('hProcess', c_void_p), ('hThread', c_void_p), ('dwProcessId', c_ulong), ('dwThreadId', c_ulong)] |
要聲明一個PROCESS_INFORMATION類型的數據只要使用如下語句即可。
ProcessInfo = _PROCESS_INFORMATION()
如果在函數中要向結構體成員變量中賦值,可以使用byref。byref相當于C語言中的“&”。
3.使用kernel32.dll中函數更改程序流程
在某些情況下,因為沒有程序的源代碼,但是又想讓該程序在一定的情況下按照某一特定的方式執行。此時就可以使用WriteProcessMemory函數,在創建程序進程后,修改其內存地址,按照要求執行。WriteProcessMemory的函數原型如下所示。
| BOOL WriteProcessMemory( HANDLE??hProcess, LPVOID??lpBaseAddress, LPCVOID?lpBuffer, SIZE_T ?nSize, SIZE_T* ?lpNumberOfBytesWritten ); |
其參數含義如下。
??hProcess:要寫內存的進程句柄。
??lpBaseAddress:要寫的內存起始地址。
??lpBuffer:寫入值的地址。
??nSize:寫入值的大小。
??lpNumberOfBytesWritten?:實際寫入的大小。
首先,在Visual C++ 6.0中創建一個示例程序。在Visual C++中創建一個新的Win32 Application,工程名為“ModifyMe”,如圖10-7所示。
圖10-7? 創建工程對話框
單擊【OK】按鈕,彈出如圖10-8所示的對話框。單擊【Finish】按鈕后,會彈出一個確認對話框,單擊【OK】按鈕完成工程創建。新建一個C/C++文件,將其命名為ModifyMe.c,輸入如下所示代碼。編譯ModifyMe后運行ModifyMe.exe,如圖10-9所示。
| /*? ModifyMe.c */ #include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int a = 0; int b = 1; if ( a != b )? /* 此處即需要編寫Python腳本修改的地方 */ { MessageBox(NULL, "Bad Python", "Python", MB_OK); } else { MessageBox(NULL, "Good Python", "Python", MB_OK); } } |
圖10-8? 工程屬性對話框?
????????????? 圖10-9? 修改前的ModifyMe程序
為找到“if ( a != b )”的反匯編后的代碼,需要在ModifyMe.c中設置斷點,進入調試模式,查看匯編代碼,如下所示。可以看出,關鍵是位于地址0040103C處的je指令。
| 7:??????? if ( a != b ) 00401036?? mov???????? eax,dword ptr [ebp-4] 00401039?? cmp???????? eax,dword ptr [ebp-8] 0040103C?? je????????? WinMain+4Dh (0040105d) |
0040103C處的je指令表示如果a與b的值相等,則程序跳轉至0040105d處執行。而程序中a與b的值并不相等,因此程序沒有跳轉。這里需要將je指令改為jne。其中je指令反匯編后的十六進制值為0x74,而jne則為0x75。如果要修改程序流程,只要向0040103C地址處寫入一個字節,將je改為jne,即向0040103C處寫入0x75。編寫的修改腳本代碼如下所示。
| #? -*- coding:utf-8 -*- #? file: ModifyMemory.py # from ctypes import * # 定義_PROCESS_INFORMATION結構體 class _PROCESS_INFORMATION(Structure):?? _fields_ = [('hProcess', c_void_p), ('hThread', c_void_p), ('dwProcessId', c_ulong), ('dwThreadId', c_ulong)] # 定義_STARTUPINFO結構體 class _STARTUPINFO(Structure): _fields_ = [('cb',c_ulong), ('lpReserved', c_char_p),? ('lpDesktop', c_char_p),? ('lpTitle', c_char_p), ('dwX', c_ulong), ('dwY', c_ulong), ('dwXSize', c_ulong), ('dwYSize', c_ulong), ('dwXCountChars', c_ulong), ('dwYCountChars', c_ulong), ('dwFillAttribute', c_ulong), ('dwFlags', c_ulong), ('wShowWindow', c_ushort),? ('cbReserved2', c_ushort), ('lpReserved2', c_char_p),? ('hStdInput', c_ulong),? ('hStdOutput', c_ulong), ('hStdError', c_ulong)] NORMAL_PRIORITY_CLASS = 0x00000020 ????# 定義NORMAL_PRIORITY_CLASS kernel32 = windll.LoadLibrary("kernel32.dll")??# 加載kernel32.dll CreateProcess = kernel32.CreateProcessA???# 獲得CreateProcess函數地址 ReadProcessMemory = kernel32.ReadProcessMemory? # 獲得ReadProcessMemory函數地址 WriteProcessMemory = kernel32.WriteProcessMemory? # 獲得WriteProcessMemory函數地址 TerminateProcess = kernel32.TerminateProcess # 聲明結構體 ProcessInfo = _PROCESS_INFORMATION() StartupInfo = _STARTUPINFO() file = 'ModifyMe.exe'???????# 要進行修改的文件 address = 0x0040103c????????# 要修改的內存地址 buffer = c_char_p("_") ???????# 緩沖區地址 bytesRead = c_ulong(0)???????# 讀入的字節數 bufferSize =? len(buffer.value)?????# 緩沖區大小 # 創建進程 if CreateProcess(file, 0, 0, 0, 0, NORMAL_PRIORITY_CLASS, 0, 0, byref(StartupInfo), byref(ProcessInfo)): # 讀取要修改的內存地址,以判斷是否是要修改的文件 if ReadProcessMemory(ProcessInfo.hProcess, address, buffer, bufferSize, byref(bytesRead)): if buffer.value == '\x74': buffer.value = '\x75'?????# 修改緩沖區內的值,將其寫入內存 # 修改內存 if WriteProcessMemory(ProcessInfo.hProcess, address, buffer, bufferSize, byref(bytesRead)): print '成功改寫內存!' else: print '寫內存錯誤!' else: print '打開了錯誤的文件!' TerminateProcess(ProcessInfo.hProcess,0)? # 如果不是要修改的文件,則終止進程 else: print '讀內存錯誤!' else: print '不能創建進程!' |
運行腳本后,如圖10-10所示。
圖10-10? 修改后的ModifyMe
總結
以上是生活随笔為你收集整理的10.4.4 使用ctypes调用kernel32.dll中的函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金字塔原理只需读前几页
- 下一篇: 机器学习 | 聚类评估指标