0X00
ctypes 是強大的,使用它我們就能夠調 用動態鏈接庫中函數,同時創建各種復雜的 C 數據類型和底層操作函數。使得python也具備了底層內存操作的能力,再配合python本身強大的表達能力,這才知道為什么python是黑客必學的編程語言。
0x01 ctypes使用
ctypes 提供了三種方法調用動態鏈接庫:cdll(), windll(), 和 oledll()。
它們的不同之處就在 於,函數的調用方法和返回值。cdll() 加載的庫,其導出的函數必須使用標准的 cdecl 調用約定。
windll()方法加載的庫,其導出的函數必須使用 stdcall 調用約定(Win32API 的原生約 定)。
oledll()方法和 windll()類似,不過如果函數返回一個 HRESULT 錯誤代碼,可以使用 COM 函數得到具體的錯誤信息。
0X02 調用約定
調用約定專指函數的調用方法。其中包括,函數參數的傳遞方法,順序(壓入棧或 者傳給寄存器),以及函數返回時,棧的平衡處理。
下面這兩種約定是我們最常用到的: cdecl and stdcall。
cdecl 調用約定: 函數的參數從右往左依次壓入棧內,函數的調用者, 在函數執行完成后,負責函數的平衡。這種約定常用於 x86 架構的 C 語言里。
In C
int python_rocks(reason_one,reason_two,reason_three);
In x86 Assembly
push reason_three push reason_two push reason_one call python_rocks add esp,12
從上面的匯編代碼中,可以清晰的看出參數的傳遞順序,最后一行,棧指針增加了 12 個字節(三個參數傳遞個函數,每個被壓入棧的指針都占 4 個字節,共 12
個), 使得 函數調用之后的棧指針恢復到調用前的位置。
stdcall調用約定:參數傳遞的順序也是從右到左,不過棧的平衡處理由函數 my_socks 自己完成,而不是調用者。
In C
int my_socks(color_onecolor_two,color_three);
In x86 Assembly
push color_three
push color_two
push color_one
call my_socks
最后一點,這兩種調用方式的返回值都存儲在 EAX 中。
0x03 使用方法
Windows下:
from ctypes import * msvcrt = cdll.msvcrt msg = "Hello world!\n" msvcrt.printf("Testing: %s", msg)
Linux下:
from ctypes import * libc = CDLL("libc.so.6") msg = "Hello, world!\n" libc.printf("Testing: %s", msg)
使用 Python 創建一個 C數據類型很簡單,可以很容易的使用由C或者C++些的組件。 下面這張圖很好的表示了映射關系。
0x04 定義結構和聯合
In C
union { long barley_long; int barley_int; char barley_char[8]; }barley_amount;
In Python
class barley_amount(Union): _fields_ = [ ("barley_long", c_long), ("barley_int", c_int), ("barley_char", c_char * 8), ]
eg:
from ctypes import * class barley_amount(Union): _fields_ = [ ("barley_long", c_long), ("barley_int", c_int), ("barley_char", c_char * 8), ] value = raw_input("Enter the amount of barley to put into the beer vat:") my_barley = barley_amount(int(value)) print "Barley amount as a long: %ld" % my_barley.barley_long print "Barley amount as an int: %d" % my_barley.barley_int print "Barley amount as a char: %s" % my_barley.barley_char
輸出結果:
Enter the amount of barley to put into the beer vat:66 Barley amount as a long: 66 Barley amount as an int: 66 Barley amount as a char: B
給聯合賦一個值就能得到三種不同的表現方式。最后一個 barley_char 輸出的結果是 B, 因為 66 剛好是 B 的 ASCII 碼。
barley_char 成員同時也是個數組,一個八個字符大小的數組。在 ctypes 中申請一個數組, 只要簡單的將變量類型乘以想要申請的數量就可以了。