日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

调用未知DLL中的导出函数

發(fā)布時(shí)間:2023/12/9 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 调用未知DLL中的导出函数 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

不知道諸位看官是否有過(guò)這樣的經(jīng)歷:在不經(jīng)意之間發(fā)現(xiàn)一個(gè)DLL文件,它里邊有不少有趣的導(dǎo)出函數(shù)——但是由于你不知道如何調(diào)用這些函數(shù),所以只能大發(fā)感慨而又無(wú)能為力焉。固然有些知名的DLL可以直接通過(guò)搜索引擎來(lái)找到它的使用方式(比如本文中的例子ipsearcher.dll),不過(guò)我們誠(chéng)然不能希望自己總能交到這樣的好運(yùn)。所以在本文中,李馬希望通過(guò)自己文理不甚通達(dá)的講解能夠給大家以授人以漁的效果。

先決條件

閱讀本文,你需要具備以下先決條件:

  • 初步了解匯編語(yǔ)言,雖然你并不一定需要去讀懂DLL中導(dǎo)出函數(shù)的匯編代碼,但是你至少應(yīng)該了解諸如push、mov這些常用的匯編指令。
  • 一個(gè)能夠查看DLL中導(dǎo)出函數(shù)的工具,Visual Studio中自帶的Dependency Walker就足夠勝任了,當(dāng)然你也可以選擇eXeScope。
  • 一個(gè)調(diào)試器。理論上講VC也可以完成調(diào)試的工作,但它畢竟是更加針對(duì)于源代碼一級(jí)調(diào)試的工具,所以你最好選擇一個(gè)專(zhuān)用的匯編調(diào)試器。在本文中我用的是OllyDbg——我不會(huì)介紹有關(guān)這個(gè)調(diào)試工具的任何東西,而只是簡(jiǎn)要介紹我的調(diào)試過(guò)程。

準(zhǔn)備好了嗎?那么我們做一個(gè)熱身運(yùn)動(dòng)吧先。

熱身——函數(shù)調(diào)用約定

這里要詳細(xì)介紹的是有關(guān)函數(shù)調(diào)用約定的內(nèi)容,如果你已經(jīng)了解了這方面的內(nèi)容,可以跳過(guò)本節(jié)。

你可能在學(xué)習(xí)Windows程序設(shè)計(jì)的時(shí)候早已接觸過(guò)“函數(shù)調(diào)用約定”這個(gè)詞匯了,那個(gè)時(shí)候你所了解的內(nèi)容可能是一個(gè)籠統(tǒng)的概念,內(nèi)容大抵是說(shuō)函數(shù)調(diào)用約定就是指的函數(shù)參數(shù)進(jìn)棧順序以及堆棧修正方式。譬如cdecl調(diào)用約定是函數(shù)參數(shù)自右而左進(jìn)棧,由調(diào)用者修復(fù)堆棧;stdcall調(diào)用約定亦是函數(shù)參數(shù)自右而左進(jìn)棧,但是由被調(diào)用者修復(fù)堆?!薏?#xff0c;這太晦澀了——在源代碼上我們是無(wú)法看到這些東西的!

那么我們別無(wú)選擇,只有深入到匯編一層了。考慮以下C++代碼:

#include?<stdio.h>

int?__cdecl?max1(?int?a,?int?b?)
{
????return?a?>?b???a?:?b;
}

int?__stdcall?max2(?int?a,?int?b?)
{
????return?a?>?b???a?:?b;
}

int?main()
{
????printf(?"max(?1,?2?)?of?cdecl?version:?%d\n",?max1(?1,?2?)?);
????printf(?"max(?1,?2?)?of?stdcall?version:?%d\n",?max2(?1,?2?)?);
????return?0;
}

對(duì)應(yīng)的匯編代碼為:

; int __cdecl max1( int a, int b )
00401000 MOV EAX,DWORD PTR SS:[ESP+4]
00401004 MOV ECX,DWORD PTR SS:[ESP+8]
00401008 CMP EAX,ECX
0040100A JG SHORT CppTest.0040100E
0040100C MOV EAX,ECX
0040100E RETN

; int __stdcall max2( int a, int b )
00401010 MOV EAX,DWORD PTR SS:[ESP+4]
00401014 MOV ECX,DWORD PTR SS:[ESP+8]
00401018 CMP EAX,ECX
0040101A JG SHORT CppTest.0040101E
0040101C MOV EAX,ECX
0040101E RETN 8 ; 被調(diào)用者的堆棧修正

; max1( 1, 2 )
00401030 PUSH 2
00401032 PUSH 1
00401034 CALL CppTest.00401000
00401039 ADD ESP,8 ; 調(diào)用者的堆棧修正

; max2( 1, 2 )
0040104A PUSH 2
0040104C PUSH 1
0040104E CALL CppTest.00401010

好了,我來(lái)簡(jiǎn)要介紹一下。函數(shù)參數(shù)傳入函數(shù)體是借由堆棧段完成的,也就是將各個(gè)參數(shù)依某種次序推入SS中——在cdecl與stdcall約定中,這個(gè)次序都是自右而左的。另外,由于將參數(shù)推入了堆棧致使堆棧指針ESP發(fā)生了變化,所以要在函數(shù)結(jié)束的時(shí)候重新修正ESP。從上邊的匯編代碼中你也可以很清楚地看到,cdecl約定是在調(diào)用max1之后修正的ESP,而stdcall約定則是在max2返回時(shí)借由RETN 8完成了這個(gè)修正工作。

另外,從上邊的匯編代碼中還可以看到,函數(shù)的返回值是由EAX帶回的。

庖丁解牛

在了解了以上的知識(shí)后,我們就可以使用調(diào)試器來(lái)調(diào)試那個(gè)未知的DLL了。可以說(shuō),這整個(gè)的調(diào)試過(guò)程充滿(mǎn)了驚險(xiǎn)和刺激,而且我們還需要一定的技巧——如果你像我一樣不喜歡閱讀匯編代碼的話(huà)。

在本文中,我所選擇的調(diào)試示例是FTerm中附帶的ipsearcher.dll,它提供了對(duì)純真IP數(shù)據(jù)庫(kù)的查詢(xún)接口。下圖是用Dependency Walker對(duì)其分析的結(jié)果:

你可以看到,這里邊有兩個(gè)導(dǎo)出函數(shù):LookupAddress和_GetAddress,那么我們可以按照返回值、調(diào)用約定、函數(shù)名、參數(shù)列表的順序?qū)⑺鼈兟暶魅缦?#xff1a;

? ? LookupAddress( ? );
? ? _GetAddress( ? );

是的,有太多的未知,下面李馬將要逐一地破解這些問(wèn)號(hào)。

調(diào)試器不可能孤立地對(duì)DLL進(jìn)行調(diào)試,我們所需要的應(yīng)該是一個(gè)合適的EXE,這樣有助于我們的探究工作。在這里我選擇的EXE是我編寫(xiě)的ipsearcher.exe,當(dāng)然這可能會(huì)讓你認(rèn)為我這篇文章的組織順序有問(wèn)題——畢竟是我已經(jīng)知道了這兩個(gè)導(dǎo)出函數(shù)之后(編寫(xiě)了ipsearcher.exe)還要假裝成不知道的樣子來(lái)對(duì)ipsearcher.dll來(lái)進(jìn)行探究,所以我決定在下文中不對(duì)ipsearcher.exe的代碼進(jìn)行任何關(guān)注,而是直接進(jìn)入到ipsearcher.dll的領(lǐng)空。

打開(kāi)調(diào)試器,載入ipsearcher.exe。當(dāng)ipsearcher.dll被裝載后,會(huì)引發(fā)一個(gè)訪(fǎng)問(wèn)異常,可以忽略這個(gè)異常繼續(xù)調(diào)試。根據(jù)Dependency Walker的分析結(jié)果,在ipsearcher.dll的0x00001BB0和0x00001C40處各下一個(gè)斷點(diǎn)?,F(xiàn)在在“IP地址”中輸入一個(gè)IP地址(這里以寒泉BBS的IP為例),點(diǎn)擊“查詢(xún)”,會(huì)發(fā)現(xiàn)指令跳入0x00001C40中(也就是_GetAddress),它的代碼如下:

10001C40 MOV EAX,DWORD PTR SS:[ESP+4] ; 一個(gè)參數(shù)
10001C44 PUSH ipsear_1.10009BE8
10001C49 PUSH EAX
10001C4A CALL ipsear_1.LookupAddress ; 兩個(gè)參數(shù)
10001C4F ADD ESP,8 ; LookupAddress是cdecl調(diào)用約定
10001C52 MOV EAX,ipsear_1.10009BE8
10001C57 RETN ; _GetAddress這廝也是cdecl調(diào)用約定

很短的幾行代碼,不過(guò)它已經(jīng)可以提供這些信息了:

  • 從SS的使用來(lái)看,_GetAddress只帶有一個(gè)參數(shù)。
  • _GetAddress中調(diào)用了LookupAddress,后者帶有兩個(gè)參數(shù)。
  • 調(diào)用LookupAddress之后進(jìn)行了堆棧修正,所以L(fǎng)ookupAddress是cdecl調(diào)用約定。
  • _GetAddress返回時(shí)并未進(jìn)行堆棧修正,所以_GetAddress也是cdecl調(diào)用約定。

于是,我們可以替換一下剛才的問(wèn)號(hào)了:

? CDECL LookupAddress( ?, ? );
? CDECL _GetAddress( ? );

下面可以進(jìn)行單步調(diào)試了,當(dāng)代碼步至10001C44時(shí),你會(huì)發(fā)現(xiàn)寄存器窗口發(fā)生了如下的變化:

“202.207.177.9”終于出現(xiàn)了,這樣一來(lái)我們可以繼續(xù)對(duì)問(wèn)號(hào)進(jìn)行替換了:

? CDECL LookupAddress( PCSTR, ? );
? CDECL _GetAddress( PCSTR );

現(xiàn)在繼續(xù)對(duì)代碼進(jìn)行跟蹤,是進(jìn)入LookupAddress的時(shí)候了。我們可以從先前_GetAddress的代碼中可以發(fā)現(xiàn),這兩個(gè)導(dǎo)出函數(shù)一直在圍繞10009BE8這個(gè)地址做文章,那么我們就要在單步調(diào)試LookupAddress的同時(shí)關(guān)注這個(gè)地址的數(shù)據(jù)改變。幾步跟蹤之后,你會(huì)發(fā)現(xiàn)10009BE8開(kāi)頭的8字節(jié)(兩個(gè)DWORD)數(shù)據(jù)發(fā)生了改變,變成了10009AB4和10009B1C。那么我們?cè)俎D(zhuǎn)向這兩個(gè)地址,會(huì)發(fā)現(xiàn):

這樣一來(lái)就很清楚了,10009BE8是一個(gè)字符串指針的數(shù)組,它有兩個(gè)元素。也就是說(shuō),我們的函數(shù)聲明可以換成這樣:

? CDECL LookupAddress( PCSTR, PSTR* );
PSTR* CDECL _GetAddress( PCSTR );

接下來(lái)需要確定的就是LookupAddress的返回值了。縱觀LookupAddress的返回代碼,你會(huì)發(fā)現(xiàn)這樣的片斷:

; 片斷1
10001C0B XOR EAX,EAX
10001C0D POP ESI
10001C0E RETN
; 片斷2
10001C2B MOV EAX,1
10001C30 POP ESI
10001C31 RETN

也就是說(shuō),這個(gè)函數(shù)有兩個(gè)返回值:0或1。那么最后的真相終于大白于天下——

BOOL CDECL LookupAddress( PCSTR, PSTR* );
PSTR* CDECL _GetAddress( PCSTR );

GetProcAddress?

到此為止,這兩個(gè)函數(shù)的聲明終于讓我們找出來(lái)了。也許你會(huì)覺(jué)得這就夠了——接下來(lái)就是用typedef定義函數(shù)指針,然后使用LoadLibrary、GetProcAddress調(diào)用這些函數(shù)的事情了。

如果你真的這么認(rèn)為的話(huà),那我認(rèn)為我有必要向你介紹這另外的一種方式。

首先請(qǐng)你建立一個(gè)名為ipsearcher.def的文件,然后在其中寫(xiě)入如下內(nèi)容:

LIBRARY "ipsearcher"

EXPORTS
LookupAddress @1
_GetAddress?? @2

將文件保存后,進(jìn)入到命令行模式下,輸入以下命令(前提是你擁有Visual Studio的附帶工具lib.exe并有正確的路徑指向。以Visual Studio 6.0為例,這個(gè)工具通常位于Microsoft Visual Studio\VC98\Bin下):

lib /def:ipsearcher.def

執(zhí)行的結(jié)果有一個(gè)警告,不必理會(huì)。這時(shí)候我們會(huì)發(fā)現(xiàn),lib為我們生成了一個(gè)ipsearcher.lib。

然后,我們繼續(xù)編寫(xiě)ipsearcher.h文件,如下:

#ifndef?IPSEARCHER_H
#define?IPSEARCHER_H

#include?<windows.h>

#pragma?comment(?lib,?"ipsearcher.lib"?)

extern?"C"
{

BOOL?CDECL?LookupAddress(?PCSTR,?PSTR*?);

PSTR*?CDECL?_GetAddress(?PCSTR?);

};

#endif?//?IPSEARCHER_H

大功告成!這樣我們就為這個(gè)光禿禿的ipsearcher.dll做了一份SDK開(kāi)發(fā)包,而不必再使用動(dòng)態(tài)加載的方法了。

總結(jié)一下再

其實(shí),探究一個(gè)DLL并非像我這里所講述的這么簡(jiǎn)單。這項(xiàng)工作很可能需要閱讀大量的匯編代碼,了解DLL函數(shù)體的流程才能使真相大白于天下。另外,還不能排除有的DLL被加密、加殼、反跟蹤……也就是說(shuō)對(duì)于ipsearcher.dll,那簡(jiǎn)直就是我撿了個(gè)便宜來(lái)借花獻(xiàn)佛了。

點(diǎn)這里下載ipsearcher SDK

轉(zhuǎn)載于:https://www.cnblogs.com/flying_bat/archive/2006/09/27/516416.html

總結(jié)

以上是生活随笔為你收集整理的调用未知DLL中的导出函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。