• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            elva

            調(diào)用未知DLL中的導(dǎo)出函數(shù)

             

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

            先決條件

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

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

            準(zhǔn)備好了嗎?那么我們做一個熱身運動吧先。

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

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

            你可能在學(xué)習(xí)Windows程序設(shè)計的時候早已接觸過“函數(shù)調(diào)用約定”這個詞匯了,那個時候你所了解的內(nèi)容可能是一個籠統(tǒng)的概念,內(nèi)容大抵是說函數(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ù)堆棧……噢不,這太晦澀了——在源代碼上我們是無法看到這些東西的!

            那么我們別無選擇,只有深入到匯編一層了。考慮以下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;
            }

            對應(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

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

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

            庖丁解牛

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

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

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

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

            是的,有太多的未知,下面李馬將要逐一地破解這些問號。

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

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

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

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

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

            于是,我們可以替換一下剛才的問號了:

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

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

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

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

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

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

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

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

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

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

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

            GetProcAddress?

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

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

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

            LIBRARY "ipsearcher"

            EXPORTS
            LookupAddress @1
            _GetAddress   @2

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

            lib /def:ipsearcher.def

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

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

            #ifndef IPSEARCHER_H
            #define IPSEARCHER_H

            #include <windows.h>

            #pragma commentlib, "ipsearcher.lib" )

            extern "C"
            {

            BOOL CDECL LookupAddress( PCSTR, PSTR* );

            PSTR* CDECL _GetAddress( PCSTR );

            };

            #endif // IPSEARCHER_H

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

            總結(jié)一下再

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

            posted on 2007-10-30 15:20 葉子 閱讀(2118) 評論(2)  編輯 收藏 引用 所屬分類: 技術(shù)研究

            Feedback

            # re: 調(diào)用未知DLL中的導(dǎo)出函數(shù) 2008-12-09 11:21 風(fēng)君

            我認(rèn)為更好的方法是直接用IDA,裝反匯編成C語言的插件,找到要看的函數(shù),直接F5~~  回復(fù)  更多評論   

            # re: 調(diào)用未知DLL中的導(dǎo)出函數(shù) 2008-12-19 13:17 elva

            有些函數(shù)是F5出不來的,如果函數(shù)比較復(fù)雜,也總不能全都F5吧。。小型的還可以。  回復(fù)  更多評論   

            久久人妻少妇嫩草AV蜜桃| 日韩精品久久久久久久电影| 国产成人精品久久| 久久丫精品国产亚洲av不卡| 国内精品久久久人妻中文字幕| 久久精品国产精品亚洲精品| 久久久无码精品亚洲日韩软件| 久久久久久久久久免免费精品| 亚洲精品乱码久久久久久蜜桃不卡| 国产成人精品久久一区二区三区| 精品无码人妻久久久久久| 伊人久久综合无码成人网| 一本久久久久久久| 亚洲AV无码久久精品狠狠爱浪潮| 国产999精品久久久久久| 久久久久久亚洲Av无码精品专口| 久久综合综合久久97色| 久久精品国产亚洲AV香蕉| 国产精品99久久久久久www| 久久久久99精品成人片试看| 久久一区二区三区99| 久久91精品国产91久久户| 亚洲精品高清国产一线久久| 久久青青草原精品国产软件| 久久精品欧美日韩精品| 久久天天躁狠狠躁夜夜不卡 | 久久久久亚洲av成人无码电影| 伊人久久大香线蕉综合Av| 久久久久亚洲AV综合波多野结衣| 18岁日韩内射颜射午夜久久成人| 色婷婷久久综合中文久久蜜桃av| 午夜精品久久影院蜜桃| 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲 | 国产A三级久久精品| 思思久久99热免费精品6| 狠狠色综合久久久久尤物| 久久精品一区二区三区不卡| 欧美亚洲国产精品久久蜜芽| 国产精品福利一区二区久久| 97超级碰碰碰久久久久| 国产欧美久久一区二区|