• <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>

            Do What You think !!

             

            用戶層下攔截系統(tǒng)api的原理與實(shí)現(xiàn) (轉(zhuǎn)載)

            文章作者:kiki

            攔截api的技術(shù)有很多種,大體分為用戶層和內(nèi)核層的攔截.這里只說說用戶層的攔截.而用戶層也分為許多種:修改PE文件導(dǎo)入表,直接修改要攔截的api的內(nèi)存(從開始到最后,使程序跳轉(zhuǎn)到指定的地址執(zhí)行).不過大部分原理都是修改程序流程,使之跳轉(zhuǎn)到你要執(zhí)行的地方,然后再返回到原地址.原來api的功能必須還能實(shí)現(xiàn).否則攔截就失去作用了.修改文件導(dǎo)入表的方法的缺點(diǎn)是如果用戶程序動態(tài)加載(使用LoadLibrary和GetProcAddress函數(shù)),攔截將變得復(fù)雜一些.所以這里介紹一下第二種方法,直接修改api,當(dāng)然不是全局的.(后面會說到)

              需要了解的一些知識:

              1.windows內(nèi)存的結(jié)構(gòu)屬性和進(jìn)程地址空間

              2.函數(shù)堆棧的一些知識



            一:win2000和xp的內(nèi)存結(jié)構(gòu)和進(jìn)程地址空間

              windows采用4GB平坦虛擬地址空間的做法。即每個進(jìn)程單獨(dú)擁有4GB的地址空間。每個進(jìn)程只能訪問自己的這4GB的虛擬空間,而對于其他進(jìn)程的地址空間則是不可見的。這樣保證了進(jìn)程的安全性和穩(wěn)定性。但是,這4GB的空間是一個虛擬空間,在使用之前,我們必須先保留一段虛擬地址,然后再為這段虛擬地址提交物理存儲器。可是我們的內(nèi)存大部分都還沒有1GB,那么這4GB的地址空間是如何實(shí)現(xiàn)的呢?事實(shí)上windows采用的內(nèi)存映射這種方法,即把物理磁盤當(dāng)作內(nèi)存來使用,比如我們打開一個可執(zhí)行文件的時候,操作系統(tǒng)會為我們開辟這個4GB的地址空間:0x00000000--0xffffffff。其中0x00000000--0x7fffffff是屬于用戶層的空間.0x80000000--0xffffffff則屬于共享內(nèi)核方式分區(qū),主要是操作系統(tǒng)的線程調(diào)度,內(nèi)存管理,文件系統(tǒng)支持,網(wǎng)絡(luò)支持和所有設(shè)備驅(qū)動程序。對于用戶層的進(jìn)程,這些地址空間是不可訪問的。任何訪問都將導(dǎo)致一個錯誤。開辟這4GB的虛擬地址空間之后,系統(tǒng)會把磁盤上的執(zhí)行文件映射到進(jìn)程的地址空間中去(一般是在地址0x00400000,可以通過修改編譯選項(xiàng)來修改這個地址)而一個進(jìn)程運(yùn)行所需要的動態(tài)庫文件則一般從0x10000000開始加載。但是如果所有的動態(tài)庫都加載到這個位置肯定會引起沖突。因此必須對一些可能引起沖突的dll編譯時重新修改基地址。但是對于所有的操作系統(tǒng)所提供的動態(tài)庫windows已經(jīng)定義好了映射在指定的位置。這個位置會隨著版本的不同而會有所改變,不過對于同一臺機(jī)器上的映射地址來說都是一樣的。即在a進(jìn)程里映射的kernel32.dll的地址和在進(jìn)程b里的kernel32.dll的地址是一樣的。對于文件映射是一種特殊的方式,使得程序不需要進(jìn)行磁盤i/o就能對磁盤文件進(jìn)行操作,而且支持多種保護(hù)屬性。對于一個被映射的文件,主要是使用CreateFileMapping函數(shù),利用他我們可以設(shè)定一些讀寫屬性:PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY.第一參數(shù)指定只能對該映射文件進(jìn)行讀操作。任何寫操作將導(dǎo)致內(nèi)存訪問錯誤。第二個參數(shù)則指明可以對映射文件進(jìn)行讀寫。這時候,任何對文件的讀寫都是直接操作文件的。而對于第三個參數(shù)PAGE_WRITECOPY顧名思義就是寫入時拷貝,任何向這段內(nèi)存寫入的操作(因?yàn)槲募怯成涞竭M(jìn)程地址空間的,對這段空間的讀寫就相當(dāng)于對文件進(jìn)行的直接讀寫)都將被系統(tǒng)捕獲,并重新在你的虛擬地址空間重新保留并分配一段內(nèi)存,你所寫入的一切東西都將在這里,而且你原先的指向映射文件的內(nèi)存地址也會實(shí)際指向這段重新分配的內(nèi)存,于是在進(jìn)程結(jié)束后,映射文件內(nèi)容并沒有改變,只是在運(yùn)行期間在那段私有拷貝的內(nèi)存里面存在著你修改的內(nèi)容。windows進(jìn)程運(yùn)行所需要映射的一些系統(tǒng)dll就是以這種方式映射的,比如常用的ntdll.dll,kernel32.dll,gdi32.dll.幾乎所有的進(jìn)程都會加載這三個動態(tài)庫。如果你在一個進(jìn)程里修改這個映射文件的內(nèi)容,并不會影響到其他的進(jìn)程使用他們。你所修改的只是在本進(jìn)程的地址空間之內(nèi)的。事實(shí)上原始文件并沒有被改變。
            這樣,在后面的修改系統(tǒng)api的時候,實(shí)際就是修改這些動態(tài)庫地址內(nèi)的內(nèi)容。前面說到這不是修改全局api就是這個原因,因?yàn)樗麄兌际且詫懭霑r拷貝的方式來映射的。不過這已經(jīng)足夠了,windows提供了2個強(qiáng)大的內(nèi)存操作函數(shù)ReadProcessMemory和WriteProcessMemory.利用這兩個函數(shù)我們就可以隨便對任意進(jìn)程的任意用戶地址空間進(jìn)行讀寫了。但是,現(xiàn)在有一個問題,我們該寫什么,說了半天,怎么實(shí)現(xiàn)跳轉(zhuǎn)呢?現(xiàn)在來看一個簡單的例子:
            MessageBox(NULL, "World", "Hello", 0);
            我們在執(zhí)行這條語句的時候,調(diào)用了系統(tǒng)api MessageBox,實(shí)際上在程序中我沒有定義UNICODE宏,系統(tǒng)調(diào)用的是MessageBox的ANSI版本MessageBoxA,這個函數(shù)是由user32.dll導(dǎo)出的。下面是執(zhí)行這條語句的匯編代碼:
            0040102A   push     0
            0040102C   push     offset string "Hello" (0041f024)
            00401031   push     offset string "World" (0041f01c)
            00401036   push     0
            00401038   call     dword ptr [__imp__MessageBoxA@16 (0042428c)]
            前面四條指令分別為參數(shù)壓棧,因?yàn)镸essageBoxA是__stdcall調(diào)用約定,所以參數(shù)是從右往左壓棧的。最后再CALL 0x0042428c

            看看0042428c這段內(nèi)存的值:
            0042428C 0B 05 D5 77 00 00 00
            可以看到這個值0x77d5050b,正是user32.dll導(dǎo)出函數(shù)MessageBoxA的入口地址。

            這是0x77D5050B處的內(nèi)容,
            77D5050B 8B FF           mov       edi,edi
            77D5050D 55             push     ebp
            77D5050E 8B EC           mov       ebp,esp
            理論上只要改變api入口和出口的任何機(jī)器碼,都可以攔截該api。這里我選擇最簡單的修改方法,直接修改api入口的前十個字節(jié)來實(shí)現(xiàn)跳轉(zhuǎn)。為什么是十字節(jié)呢?其實(shí)修改多少字節(jié)都沒有關(guān)系,只要實(shí)現(xiàn)了函數(shù)的跳轉(zhuǎn)之后,你能把他們恢復(fù)并讓他繼續(xù)運(yùn)行才是最重要的。在CPU的指令里,有幾條指令可以改變程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。這里我選擇CALL指令,因?yàn)樗且院瘮?shù)調(diào)用的方式來實(shí)現(xiàn)跳轉(zhuǎn)的,這樣可以帶一些你需要的參數(shù)。到這里,我該說說函數(shù)的堆棧了。

            總結(jié):windows進(jìn)程所需要的動態(tài)庫文件都是以寫入時拷貝的方式映射到進(jìn)程地址空間中的。這樣,我們只能攔截指定的進(jìn)程。修改目標(biāo)進(jìn)程地址空間中的指定api的入口和出口地址之間的任意數(shù)據(jù),使之跳轉(zhuǎn)到我們的攔截代碼中去,然后再恢復(fù)這些字節(jié),使之能順利工作。




            二:函數(shù)堆棧的一些知識

              正如前面所看到MessageBoxA函數(shù)執(zhí)行之前的匯編代碼,首先將四個參數(shù)壓棧,然后CALL MessageBoxA,這時候我們的線程堆棧看起來應(yīng)該是這樣的:

            |   |   <---ESP
            |返回地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |..   |

            我們再看MessageBoxA的匯編代碼,
            77D5050B 8B FF           mov       edi,edi
            77D5050D 55             push     ebp
            77D5050E 8B EC           mov       ebp,esp
            注意到堆棧的操作有PUSH ebp,這是保存當(dāng)前的基址指針,以便一會兒恢復(fù)堆棧后返回調(diào)用線程時使用,然后再有mov ebp,esp就是把當(dāng)前esp的值賦給ebp,這時候我們就可以使用 ebp+偏移 來表示堆棧中的數(shù)據(jù),比如參數(shù)1就可以表示成[ebp+8],返回地址就可以表示成[ebp+4]..如果我們在攔截的時候要對這些參數(shù)和返回地址做任何處理,就可以使用這種方法。如果這個時候函數(shù)有局部變量的話,就通過減小ESP的值的方式來為之分配空間。接下來就是保存一些寄存器:EDI,ESI,EBX.要注意的是,函數(shù)堆棧是反方向生長的。這時候堆棧的樣子:
            |....|
            |EDI| <---ESP
            |ESI|
            |EBX|
            |局部變量|
            |EBP   |  
            |返回地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |..   |

            在函數(shù)返回的時候,由函數(shù)自身來進(jìn)行堆棧的清理,這時候清理的順序和開始入棧的順序恰恰相反,類似的匯編代碼可能是這樣的:

            pop edi
            pop esi
            pop ebx
            add esp, 4
            pop ebp
            ret 0010
            先恢復(fù)那些寄存器的值,然后通過增加ESP的值的方式來釋放局部變量。這里可以用mov esp, ebp來實(shí)現(xiàn)清空所有局部變量和其他一些空閑分配空間。接著函數(shù)會恢復(fù)EBP的值,利用指令POP EBP來恢復(fù)該寄存器的值。接著函數(shù)運(yùn)行ret 0010這個指令。該指令的意思是,函數(shù)把控制權(quán)交給當(dāng)前棧頂?shù)牡刂返闹噶睿瑫r清理堆棧的16字節(jié)的參數(shù)。如果函數(shù)有返回值的話,那在EAX寄存器中保存著當(dāng)前函數(shù)的返回值。如果是__cdecl調(diào)用方式,則執(zhí)行ret指令,對于堆棧參數(shù)的處理交給調(diào)用線程去做。如wsprintf函數(shù)。

            這個時候堆棧又恢復(fù)了原來的樣子。線程得以繼續(xù)往下執(zhí)行...
            在攔截api的過程之中一個重要的任務(wù)就是保證堆棧的正確性。你要理清每一步堆棧中發(fā)生了什么。



            三:形成思路
             
              呵呵,不知道你現(xiàn)在腦海是不是有什么想法。怎么去實(shí)現(xiàn)攔截一個api?
              這里給出一個思路,事實(shí)上攔截的方法真的很多,理清了一個,其他的也就容易了。而且上面所說的2個關(guān)鍵知識,也可以以另外的形式來利用。
              我以攔截CreateFile這個api為例子來簡單說下這個思路吧:
             
              首先,既然我們要攔截這個api就應(yīng)該知道這個函數(shù)在內(nèi)存中的位置吧,至少需要知道從哪兒入口。CreateFile這個函數(shù)是由kernel32.dll這個動態(tài)庫導(dǎo)出的。我們可以使用下面的方法來獲取他映射到內(nèi)存中的地址:
              HMODULE hkernel32 = LoadLibrary("Kernel32.dll");
              PVOID dwCreateFile = GetProcAddress(hkernei32, "CreateFileA");
            這就可以得到createfile的地址了,注意這里是獲取的createfile的ansic版本。對于UNICODE版本的則獲取CreateFileW。這時dwCreateFile的值就是他的地址了。對于其他進(jìn)程中的createfile函數(shù)也是這個地址,前面說過windows指定了他提供的所有的dll文件的加載地址。
             
              接下來,我們該想辦法實(shí)現(xiàn)跳轉(zhuǎn)了。最簡單的方法就是修改這個api入口處的代碼了。但是我們該修改多少呢?修改的內(nèi)容為什么呢?前面說過我們可以使用CALL的方式來實(shí)現(xiàn)跳轉(zhuǎn),這種方法的好處是可以為你的攔截函數(shù)提供一個或者多個參數(shù)。這里只要一個參數(shù)就足夠了。帶參數(shù)的函數(shù)調(diào)用的匯編代碼是什么樣子呢,前面也已經(jīng)說了,類似與調(diào)用MessageBoxA時的代碼:

            PUSH 參數(shù)地址
            CALL 函數(shù)入口地址(這里為一個偏移地址)

            執(zhí)行這2條指令就能跳轉(zhuǎn)到你要攔截的函數(shù)了,但是我們該修改成什么呢。首先,我們需要知道這2條指令的長度和具體的機(jī)器代碼的值。其中PUSH對應(yīng)0x68,而CALL指令對應(yīng)的機(jī)器碼為0xE8,而后面的則分別對應(yīng)攔截函數(shù)的參數(shù)地址和函數(shù)的地址。注意第一個是一個直接的地址,而第二個則是一個相對地址。當(dāng)然你也可以使用0xFF0x15這個CALL指令來進(jìn)行直接地址的跳轉(zhuǎn)。
            下面就是計算這2個地址的值了,
            對于參數(shù)和函數(shù)體的地址,要分情況而定,對于對本進(jìn)程中api的攔截,則直接取地址就可以了。對于參數(shù),可以先定義一個參數(shù)變量,然后取變量地址就ok了。
            如果是想攔截其他進(jìn)程中的api,則必須使用其他一些方法,最典型的方法是利用VirtualAllocEx函數(shù)來在其他進(jìn)程中申請和提交內(nèi)存空間。然后用WriteProcessMemory來分別把函數(shù)體和參數(shù)分別寫入申請和分配的內(nèi)存空間中去。然后再生成要修改的數(shù)據(jù),最后用WriteProcessMemory來修改api入口,把入口的前10字節(jié)修改為剛剛生成的跳轉(zhuǎn)數(shù)據(jù)。比如在遠(yuǎn)程進(jìn)程中你寫入的參數(shù)和函數(shù)體的內(nèi)存地址分別為0x00010000和0x00011000,則生成的跳轉(zhuǎn)數(shù)據(jù)為 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000 CALL 00011000),這樣程序運(yùn)行createfile函數(shù)的時候?qū)冗\(yùn)行PUSH 00010000 CALL 00011000,這樣就達(dá)到了跳轉(zhuǎn)的目的。此刻我們應(yīng)該時刻注意堆棧的狀態(tài),對于CreateFile有
            HANDLE CreateFile(
            LPCTSTR lpFileName,
            DWORD dwDesiredAccess,
            DWORD dwShareMode,
            LPSECURITY_ATTRIBUTES lpSecurityAttributes,
            DWORD dwCreationDisposition,
            DWORD dwFlagsAndAttributes,
            HANDLE hTemplateFile
            );
            可以看到其有7個參數(shù),于是在調(diào)用之前,堆棧應(yīng)該已經(jīng)被壓入了這7個參數(shù),堆棧的樣子:
            |....|   <---ESP
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            這是執(zhí)行到我們的跳轉(zhuǎn)語句:PUSH 00010000,于是堆棧又變了:

            |....|   <---ESP
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            接著執(zhí)行CALL 00011000,堆棧變?yōu)椋?br>|...| <---ESP
            |api入口之后的第11個字節(jié)的指令的地址|  
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            接下來就到了我們的攔截函數(shù)中拉,當(dāng)然,函數(shù)肯定也會做一些類似動作,把EBP壓棧,為局部變量分配空間等。這時候堆棧的樣子又變了:

            |EDI| <---ESP
            |ESI|
            |EBX|
            |局部變量|
            |EBP|   <---EBP
            |api入口之后的第11個字節(jié)的指令的地址|  
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            這時候,你想做什么就盡情地做吧,獲取參數(shù)信息,延緩執(zhí)行CreateFile函數(shù)等等。拿獲取打開文件句柄的名字來說吧,文件名是第一個參數(shù),前面說過我們可以用[EBP+8]來獲取參數(shù),但是對照上面的堆棧形狀,中間又加了另外一些數(shù)據(jù),所以我們用[EBP+16]來獲取第一個參數(shù)的地址。比如:
            char* PFileName = NULL;
            __asm{
            MOV EAX,[EBP+16]
            MOV [szFileName], EAX
            }

            比如我們用一個messagebox來彈出一個信息,說明該程序即將打開一個某謀路徑的文件句柄。但是有一個要注意的是,如果你想攔截遠(yuǎn)程進(jìn)程的話,對于那個攔截函數(shù)中所使用到的任何函數(shù)或者以任何形式的相對地址的調(diào)用都要停止。因?yàn)槊總€進(jìn)程中的地址分配都是獨(dú)立的,比如上面的CALL MessageBoxA改成直接地址的調(diào)用。對于使用messagebox,我們應(yīng)該定義一個函數(shù)指針,然后把這個指針的值賦值為user32.dll中導(dǎo)出該函數(shù)的直接地址。然后利用這個指針來進(jìn)行函數(shù)調(diào)用。對于messagebox函數(shù)的調(diào)用可以這樣,在源程序中定義一個參數(shù)結(jié)構(gòu)體,參數(shù)中包含一個導(dǎo)出函數(shù)的地址,把這個地址設(shè)為MessageBoxA的直接地址,獲取地址的方法就不說了。然后把這個參數(shù)傳給攔截函數(shù),就可以使用拉。這也是利用一個參數(shù)的原因。類似代碼如下:


            typedef struct _RemoteParam {
              DWORD dwMessageBox;
            } RemoteParam, * PRemoteParam;

            typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);//定義一個函數(shù)指針

            //攔截函數(shù)
            void HookCreateFile(LPVOID lParam)
            {
            RemoteParam* pRP = (RemoteParam*)lParam;//獲取參數(shù)地址
            char* PFileName = NULL;//定義一個指針
            __asm{
            MOV EAX,[EBP+16]
            MOV [szFileName], EAX //把CreateFile第一個參數(shù)的值,文件的路徑的地址傳               //給szFileName
            }

            //定 義一個函數(shù)指針
            PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;

            pfnMessageBox(NULL, PFileName, PFileName, MB_ICONINFORMATION |MB_OK);
            //輸出要打開的文件的路徑
            //.....
            }

            對于你要使用的其他函數(shù),都是使用同樣的方式,利用這個參數(shù)來傳遞我們要傳遞的函數(shù)的絕對地址,然后定義這個函數(shù)指針,就可以使用了。


            好了,接下來我們該讓被攔截的api正常工作了,這個不難,把他原來的數(shù)據(jù)恢復(fù)一下就可以了。那入口的10個字節(jié)。我們在改寫他們的時候應(yīng)該保存一下,然后也把他放在參數(shù)中傳遞給攔截函數(shù),呵呵,參數(shù)的作用可多了。接著我們就可以用WriteProcessMemory函數(shù)來恢復(fù)這個api的入口了,代碼如下:
            PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess;
            PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory;
             
            if(!pfnWriteProcessMemory(pfnGetCurrentProcess(),
                                        (LPVOID)pfnConnect,
                                        (LPCVOID)pRP->szOldCode,
                                        10,
                                        NULL))
            pfnMessageBox(NULL, pRP->szModuleName1, pRP->szModuleName2, MB_ICONINFORMATION | MB_OK);
            其中這些函數(shù)指針的定義和上面的類似。
            而參數(shù)中的szoldcode則是在源程序中在修改api之前保存好,然后傳給攔截函數(shù),在源程序中是用ReadProcessMemory函數(shù)來獲取他的前10個字節(jié)的:
            ReadProcessMemory(GetCurrentProcess(),
                                  (LPCVOID)RParam.dwCreateFile,
                                  oldcode,
                                  10,
                                  &dwPid)
            strcat((char*)RParam.szOldCode, (char*)oldcode);


            接下來如果你還繼續(xù)保持對該api的攔截,則又該用WriteProcessMemory 來修改入口了,跟前面的恢復(fù)入口是一樣的,只不過把szOldCode換成了szNewCode了而已。這樣你又能對CreateFile繼續(xù)攔截了。

            好了,接下來該進(jìn)行堆棧的清理了,也許你還要做點(diǎn)其他事情,盡管做去。但是清理堆棧是必須要做的,在函數(shù)結(jié)束的時候,因?yàn)樵谖覀兎湃蝍pi恢復(fù)執(zhí)行之后,他又return 到我們的函數(shù)中來了,這個時候的堆棧是什么樣子呢?
            |EDI| <---ESP
            |ESI|
            |EBX|
            |局部變量|
            |EBP|   <---EBP
            |api入口之后的第11個字節(jié)的指令的地址|  
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            我們的目標(biāo)是把返回值記錄下來放到EAX寄存器中去,把返回地址記錄下來,同時把堆棧恢復(fù)成原來的樣子。
            首先我們恢復(fù)那些寄存器的值,接著釋放局部變量,可以用mov esp, ebp.因?yàn)槲覀儾磺宄唧w的局部變量分配了多少空間。所以使用這個方法。


            __asm
            {POP EDI
            POP ESI
            POP EBX   //恢復(fù)那些寄存器
            MOV EDX, [NextIpAddr]//把返回地址放到EDX中,因?yàn)榇龝?            //EBX被恢復(fù)后,線程中的所有局部變量就不能正常使用了。
                 
            MOV EAX, [RetValue]//返回值放到EAX中,當(dāng)然也可以修改這個返回值
            MOV ESP, EBP//清理局部變量
            POP EBP//恢復(fù)EBP的值
            ADD ESP, 28H //清理參數(shù)和返回地址,注意一共(7+1+1+1)*4
            PUSH EDX //把返回地址壓棧,這樣棧中就只有這一個返回地址了,返回之后棧       //就空了
            RET
            }

            這樣,一切就完成了,堆棧恢復(fù)了應(yīng)該有的狀態(tài),而你想攔截的也攔截到了。


            四:后記
              攔截的方式多種多樣,不過大體的思路卻都相同。要時刻注意你要攔截的函數(shù)的堆棧狀態(tài)以及在攔截函數(shù)中的對數(shù)據(jù)的引用和函數(shù)的調(diào)用(地址問題)。

             
            //////////////////////////////////////////////////////////////////////
            附錄:一個攔截CreateFile函數(shù)的簡單實(shí)現(xiàn)
            //////////////////////////////////////////////////////////////////////
            Copy code
            #include <stdio.h>
            #include <windows.h>
            #include <Psapi.h>

            #pragma comment(lib, "psapi.lib")

            typedef struct _RemoteParam {
              DWORD dwCreateFile;
              DWORD dwMessageBox;
              DWORD dwGetCurrentProcess;
              DWORD dwWriteProcessMemory;
              unsigned char szOldCode[10];
              DWORD FunAddr;
            } RemoteParam, * PRemoteParam;

            typedef HANDLE (__stdcall * PFN_CREATEFILE)(LPCTSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
            typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);
            typedef BOOL (__stdcall * PFN_WRITEPROCESSMEMORY)(HANDLE,LPVOID,LPCVOID,SIZE_T,SIZE_T*);
            typedef HANDLE (__stdcall * PFN_GETCURRENTPROCESS)(void);

            #define PROCESSNUM 128
            #define MYMESSAGEBOX "MessageBoxW"
            #define MYCREATEFILE "CreateFileW"

            void HookCreateFile(LPVOID lParam)
            {

              RemoteParam* pRP = (RemoteParam*)lParam;


              DWORD NextIpAddr = 0;
              DWORD dwParamaAddr = 0;

              HANDLE RetFpHdl = INVALID_HANDLE_VALUE;
              LPCTSTR lpFileName;
              DWORD dwDesiredAccess;
              DWORD dwShareMode;
              LPSECURITY_ATTRIBUTES lpSecurityAttributes;
              DWORD dwCreationDisposition;
              DWORD dwFlagsAndAttributes;
              HANDLE hTemplateFile;
              PFN_CREATEFILE pfnCreatefile = (PFN_CREATEFILE)pRP->dwCreateFile;


              __asm
              {
              MOV EAX,[EBP+8]
              MOV [dwParamaAddr], EAX
              MOV EAX,[EBP+12]      
              MOV [NextIpAddr], EAX
              MOV EAX,[EBP+16]
              MOV [lpFileName], EAX
              MOV EAX,[EBP+20]
              MOV [dwDesiredAccess],EAX
              MOV EAX,[EBP+24]
              MOV [dwShareMode],EAX
              MOV EAX,[EBP+28]
              MOV [lpSecurityAttributes],EAX
              MOV EAX,[EBP+32]
              MOV [dwCreationDisposition],EAX
              MOV EAX,[EBP+36]
              MOV [dwFlagsAndAttributes],EAX
              MOV EAX,[EBP+40]
              MOV [hTemplateFile],EAX  
              }

              PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;
              int allowFlag = pfnMessageBox(NULL, lpFileName, NULL, MB_ICONINFORMATION | MB_YESNO);
             
              if(allowFlag == IDYES)
              {
              unsigned char szNewCode[10];
              int PramaAddr = (int)dwParamaAddr;
              szNewCode[4] = PramaAddr>>24;
              szNewCode[3] = (PramaAddr<<8)>>24;
              szNewCode[2] = (PramaAddr<<16)>>24;
              szNewCode[1] = (PramaAddr<<24)>>24;
              szNewCode[0] = 0x68;
             
              int funaddr = (int)pRP->FunAddr - (int)pfnCreatefile - 10 ;
              szNewCode[9] = funaddr>>24;
              szNewCode[8] = (funaddr<<8)>>24;
              szNewCode[7] = (funaddr<<16)>>24;
              szNewCode[6] = (funaddr<<24)>>24;
              szNewCode[5] = 0xE8;
             
             
              PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess;
              PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory;
              pfnWriteProcessMemory(pfnGetCurrentProcess(),
                            (LPVOID)pfnCreatefile,
                            (LPCVOID)pRP->szOldCode,
                            10,
                            NULL);

              RetFpHdl = pfnCreatefile(lpFileName,
                              dwDesiredAccess,
                              dwShareMode,
                              lpSecurityAttributes,
                              dwCreationDisposition,
                              dwFlagsAndAttributes,
                              hTemplateFile);
              pfnWriteProcessMemory(pfnGetCurrentProcess(),
                            (LPVOID)pfnCreatefile,
                            (LPCVOID)szNewCode,
                            10,
                            NULL);
              }


              __asm
                {POP EDI
                  POP ESI
                  POP EBX
                  MOV EDX, [NextIpAddr]
                  MOV EAX, [RetFpHdl]
                  MOV ESP, EBP
                  POP EBP
                  ADD ESP, 28H
                  PUSH EDX
                  RET
                }

             
            }



            BOOL AdjustProcessPrivileges(LPCSTR szPrivilegesName)
            {
              HANDLE hToken;
              TOKEN_PRIVILEGES tkp;

              if(!OpenProcessToken(GetCurrentProcess(),
                TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
              {
                return FALSE;
              }

              if(!LookupPrivilegeValue(NULL,szPrivilegesName,
                              &tkp.Privileges[0].Luid))
              {
                CloseHandle(hToken);
                return FALSE;
              }
             
              tkp.PrivilegeCount = 1;
              tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
             
              if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(tkp),NULL,NULL))
              {
                CloseHandle(hToken);
                return FALSE;
              }
             
              CloseHandle(hToken);
              return TRUE;
            }


            void printProcessNameByPid( DWORD ProcessId )
            {
              HANDLE pHd;
              HMODULE pHmod;
              char ProcessName[MAX_PATH] = "unknown";
              DWORD cbNeeded;
              pHd = OpenProcess( PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, ProcessId );
              if(pHd == NULL)
                return;
             
              if(!EnumProcessModules( pHd, &pHmod, sizeof(pHmod), &cbNeeded))
                return;
              if(!GetModuleFileNameEx( pHd, pHmod, ProcessName, MAX_PATH))
                return;
             
              printf( "%d\t%s\n", ProcessId, ProcessName);
              CloseHandle( pHd );
              return;
            }


            int main(void)
            {

              if(!AdjustProcessPrivileges(SE_DEBUG_NAME))
              {
                  printf("AdjustProcessPrivileges Error!\n");
                  return -1;
              }

              DWORD Pids[PROCESSNUM];
              DWORD dwProcessNum = 0;
              if(!EnumProcesses(Pids, sizeof(Pids), &dwProcessNum))
              {
                  printf("EnumProcess Error!\n");
                  return -1;
              }
             
              for( DWORD num = 0; num < (dwProcessNum / sizeof(DWORD)); num++)
                  printProcessNameByPid(Pids[num]);

              printf("\nAll %d processes running. \n", dwProcessNum / sizeof(DWORD));

              DWORD dwPid = 0;
              printf("\n請輸入要攔截的進(jìn)程id:");
              scanf("%d", &dwPid);
             
              HANDLE hTargetProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, dwPid);
              if(hTargetProcess == NULL)
              {
                  printf("OpenProcess Error!\n");
                  return -1;
              }

              DWORD dwFunAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, 8192,
                                          MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
             
              if((LPVOID)dwFunAddr == NULL)
              {
                  printf("申請線程內(nèi)存失敗!\n");
                  CloseHandle(hTargetProcess);
                  return -1;
              }

              DWORD dwPramaAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, sizeof(RemoteParam),
                                          MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

              if((LPVOID)dwPramaAddr == NULL)
              {
                  printf("申請參數(shù)內(nèi)存失敗!\n");
                  CloseHandle(hTargetProcess);
                  return -1;
              }

              printf("\n線程內(nèi)存地址:%.8x\n"
                    "參數(shù)內(nèi)存地址:%.8x\n",
                    dwFunAddr, dwPramaAddr);
                RemoteParam RParam;
              ZeroMemory(&RParam, sizeof(RParam));
              HMODULE hKernel32 = LoadLibrary("kernel32.dll");
              HMODULE hUser32 = LoadLibrary("user32.dll");

              RParam.dwCreateFile = (DWORD)GetProcAddress(hKernel32, MYCREATEFILE);
              RParam.dwGetCurrentProcess = (DWORD)GetProcAddress(hKernel32, "GetCurrentProcess");
              RParam.dwWriteProcessMemory = (DWORD)GetProcAddress(hKernel32, "WriteProcessMemory");
              RParam.dwMessageBox = (DWORD)GetProcAddress(hUser32, MYMESSAGEBOX);
             
              unsigned char oldcode[10];
              unsigned char newcode[10];
              int praadd = (int)dwPramaAddr;
              int threadadd = (int)dwFunAddr;
              newcode[4] = praadd>>24;
              newcode[3] = (praadd<<8)>>24;
              newcode[2] = (praadd<<16)>>24;
              newcode[1] = (praadd<<24)>>24;
              newcode[0] = 0x68;
             
              int offsetaddr = threadadd - (int)RParam.dwCreateFile - 10 ;
              newcode[9] = offsetaddr>>24;
              newcode[8] = (offsetaddr<<8)>>24;
              newcode[7] = (offsetaddr<<16)>>24;
              newcode[6] = (offsetaddr<<24)>>24;
              newcode[5] = 0xE8;

              printf("NewCode:");
              for(int j = 0; j < 10; j++)
                  printf("0x%.2x ",newcode[j]);
              printf("\n\n");



              if(!ReadProcessMemory(GetCurrentProcess(),
                              (LPCVOID)RParam.dwCreateFile,
                              oldcode,
                              10,
                              &dwPid))
              {
                  printf("read error");
                  CloseHandle(hTargetProcess);
                  FreeLibrary(hKernel32);
                  return -1;
              }

              strcat((char*)RParam.szOldCode, (char*)oldcode);
              RParam.FunAddr = dwFunAddr;

              printf(
                    "RParam.dwCreateFile:%.8x\n"
                    "RParam.dwMessageBox:%.8x\n"
                    "RParam.dwGetCurrentProcess:%.8x\n"
                    "RParam.dwWriteProcessMemory:%.8x\n"
                    "RParam.FunAddr:%.8x\n",
                    RParam.dwCreateFile,
                    RParam.dwMessageBox,
                    RParam.dwGetCurrentProcess,
                    RParam.dwWriteProcessMemory,
                    RParam.FunAddr);
              printf("RParam.szOldCode:");
              for( int i = 0; i< 10; i++)
                  printf("0x%.2x ", RParam.szOldCode[i]);
              printf("\n");
             
             
              if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwFunAddr, (LPVOID)&HookCreateFile, 8192, &dwPid))
              {
                  printf("WriteRemoteProcessesMemory Error!\n");
                  CloseHandle(hTargetProcess);
                  FreeLibrary(hKernel32);
                  return -1;
              }

              if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwPramaAddr, (LPVOID)&RParam, sizeof(RemoteParam), &dwPid))
              {
                  printf("WriteRemoteProcessesMemory Error!\n");
                  CloseHandle(hTargetProcess);
                  FreeLibrary(hKernel32);
                  return -1;
              }
             
              if(!WriteProcessMemory(hTargetProcess, (LPVOID)RParam.dwCreateFile, (LPVOID)newcode, 10, &dwPid))
              {
                  printf("WriteRemoteProcessesMemory Error!\n");
                  CloseHandle(hTargetProcess);
                  FreeLibrary(hKernel32);
                  return -1;
              }

              printf("\nThat's all, good luck :)\n");
              CloseHandle(hTargetProcess);
              FreeLibrary(hKernel32);
              return 0;
            }

            posted on 2007-04-28 17:23 零宇 閱讀(613) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            導(dǎo)航

            統(tǒng)計

            常用鏈接

            留言簿(4)

            隨筆檔案(8)

            文章分類(4)

            文章檔案(4)

            Windows Mobile 論壇

            道中人

            最新隨筆

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            人妻无码αv中文字幕久久| 欧美麻豆久久久久久中文| 少妇无套内谢久久久久| 性做久久久久久久| 久久免费精品一区二区| 久久国产精品免费一区二区三区 | 久久水蜜桃亚洲av无码精品麻豆 | 久久综合伊人77777麻豆| 97精品伊人久久大香线蕉| 国产成人综合久久综合| 亚洲欧美日韩精品久久亚洲区 | 久久久网中文字幕| 婷婷久久五月天| 国产ww久久久久久久久久| 亚洲中文字幕无码久久2020| 国产精品久久久久久久午夜片| 狠狠综合久久AV一区二区三区| 国产精品午夜久久| 久久精品人人做人人爽电影蜜月 | 久久久久青草线蕉综合超碰| 久久精品成人免费看| 影音先锋女人AV鲁色资源网久久 | 亚洲中文精品久久久久久不卡| 欧美激情精品久久久久| 一本色综合网久久| 深夜久久AAAAA级毛片免费看| 久久亚洲综合色一区二区三区| 色综合久久久久久久久五月| 久久笫一福利免费导航 | 久久国产福利免费| 日本福利片国产午夜久久| 亚洲欧美成人综合久久久| 久久人妻少妇嫩草AV蜜桃| 久久无码精品一区二区三区| 91久久成人免费| 狠狠色丁香婷婷综合久久来来去| 久久免费精品一区二区| 日韩一区二区久久久久久| 国产69精品久久久久99| 久久精品国产只有精品66 | aaa级精品久久久国产片|