1. 背景資料
通常開發(fā)者獲取到的通用dll函數(shù)庫都會有說明文件,里面對dll庫中公開的函數(shù)都會有api接口說明,比如函數(shù)的名稱,返回值,參數(shù)類型以及參數(shù)的數(shù)量.
但是有時開發(fā)者也會找到一些dll庫函數(shù),這些庫函數(shù)只能獲取到函數(shù)名稱,而沒有關(guān)于函數(shù)的參數(shù)信息.
這種dll一般是來自第三方的,或是偷來的(我的項目中就是屬于此類,我知道這個dll函數(shù)的功能很不錯,我就是想將它嵌入到我的程序中.)).
對于前者,具有資料完備API編程信息,我們可以在c++,vb或.NET平臺中輕松編寫調(diào)用接口來執(zhí)行dll中的函數(shù),而后者由于沒有參數(shù)信息,想調(diào)用就不那么簡單了.
通常系統(tǒng)自帶的dll函數(shù)庫都會有api說明,或者也可以通過某些工具來獲取dll庫中的函數(shù)描述信息.
可是一些第三方的dll函數(shù)可能不會將函數(shù)信息寫到dll中,這樣我們就不能夠獲取參數(shù)信息了,也就無法調(diào)用了,真的沒辦法嗎?
2. 解決辦法
即然沒有參數(shù)信息就沒有辦法聲明函數(shù)的調(diào)用接口,不過沒關(guān)系.
我想到一種簡單的方式:用匯編語言來直接調(diào)用.
首先要有完整的第三方程序,先看看第三方程序在匯編語言層面上是如何調(diào)用函數(shù)的(通過反匯編).
通過調(diào)試軟件(如:windbg,od,softace等)將第三方的程序運行起來,然后在我們需要的那個dll模塊函數(shù)上加斷點,分析它的匯編碼.
其實很簡單的,如果函數(shù)需要參數(shù),那就看看它是通過棧的方式傳的參數(shù)還是通過寄存器來傳的參數(shù).
一般匯編語言中參數(shù)都是傳的參數(shù)地址,而不是值.另外傳遞參數(shù)的規(guī)則也要弄清.
如果傳參方式是棧方式傳參數(shù),還要區(qū)分是調(diào)用者清除棧還是被調(diào)用者清除棧.
比如下面代碼是調(diào)用md5加密,我就是用棧方式傳遞兩個指針參數(shù)給函數(shù).
mov eax,dword ptr [cb] //傳入待加密字符串地址
push eax //參數(shù)2 入棧
mov eax,dword ptr [result] //反回值字符串地址
push eax //參數(shù)1 入棧
call pFun //執(zhí)行函數(shù)
結(jié)果result里就是我們加密后的結(jié)果了.
上面代碼使用的是調(diào)用者清除棧方式,而清除棧方式是被調(diào)用者清除棧(通過反匯編可以發(fā)現(xiàn)指定函數(shù)里是使用哪種清棧方式.): 如果需要我們清除棧,那就在上面代碼的最后寫 add esp,8 代表釋放兩個指針參數(shù)的空間8個字節(jié).
關(guān)于函數(shù)清棧方式參見我的另一篇隨筆:函數(shù)的可變數(shù)目參數(shù) 與 棧
3. 如何加載dll與函數(shù)地址
我使用win32 api 來加載dll庫與函數(shù)地址,使用方式:
HINSTANCE dll = LoadLibrary(L"d:\\*******.dll");
FARPROC pFun = GetProcAddress(dll,"要調(diào)用的函數(shù)名稱");
_asm
{
mov eax,dword ptr [cb]
push eax mov eax,dword ptr [result]
push eax
call pFun
}
4. 注意事項
需要特別注意的是,對于(傳入的/返回的)參數(shù)指針指向的數(shù)據(jù)區(qū)域大小需要多次試驗,因為有時從匯編中很難發(fā)現(xiàn)局部變量的長度,所以盡量將我們聲明的變量地址空間多一些.
還有就是dll函數(shù)的清棧方式,要仔細觀察反匯編的結(jié)果,看看它用的是哪種調(diào)用方式,這樣才能保障棧平橫,不至于緩沖區(qū)溢出.