**轉載** **我自己看了看這片文章,還不錯!程序也可以編譯出來**
文章作者:kiki 信息來源:邪惡八進制信息安全團隊(
www.eviloctal.com
) 本文章首發
黑色海岸線網絡安全技術論壇
,后由kiki友情提交到邪惡八進制信息安全團隊
?????????攔截api的技術有很多種,大體分為用戶層和內核層的攔截.這里只說說用戶層的攔截.而用戶層也分為許多種:修改PE文件導入表,直接修改要攔截的api的內存(從開始到最后,使程序跳轉到指定的地址執行).不過大部分原理都是修改程序流程,使之跳轉到你要執行的地方,然后再返回到原地址.原來api的功能必須還能實現.否則攔截就失去作用了.修改文件導入表的方法的缺點是如果用戶程序動態加載(使用LoadLibrary和GetProcAddress函數),攔截將變得復雜一些.所以這里介紹一下第二種方法,直接修改api,當然不是全局的.(后面會說到)
需要了解的一些知識:
1.windows內存的結構屬性和進程地址空間
2.函數堆棧的一些知識
一:win2000和xp的內存結構和進程地址空間
? windows采用4GB平坦虛擬地址空間的做法。即每個進程單獨擁有4GB的地址空間。每個進程只能訪問自己的這4GB的虛擬空間,而對于其他進程的地址空間則是不可見的。這樣保證了進程的安全性和穩定性。但是,這4GB的空間是一個虛擬空間,在使用之前,我們必須先保留一段虛擬地址,然后再為這段虛擬地址提交物理存儲器。可是我們的內存大部分都還沒有1GB,那么這4GB的地址空間是如何實現的呢?事實上windows采用的內存映射這種方法,即把物理磁盤當作內存來使用,比如我們打開一個可執行文件的時候,操作系統會為我們開辟這個4GB的地址空間:0x00000000--0xffffffff。其中0x00000000--0x7fffffff是屬于用戶層的空間.0x80000000--0xffffffff則屬于共享內核方式分區,主要是操作系統的線程調度,內存管理,文件系統支持,網絡支持和所有設備驅動程序。對于用戶層的進程,這些地址空間是不可訪問的。任何訪問都將導致一個錯誤。開辟這4GB的虛擬地址空間之后,系統會把磁盤上的執行文件映射到進程的地址空間中去(一般是在地址0x00400000,可以通過修改編譯選項來修改這個地址)而一個進程運行所需要的動態庫文件則一般從0x10000000開始加載。但是如果所有的動態庫都加載到這個位置肯定會引起沖突。因此必須對一些可能引起沖突的dll編譯時重新修改基地址。但是對于所有的操作系統所提供的動態庫windows已經定義好了映射在指定的位置。這個位置會隨著版本的不同而會有所改變,不過對于同一臺機器上的映射地址來說都是一樣的。即在a進程里映射的kernel32.dll的地址和在進程b里的kernel32.dll的地址是一樣的。對于文件映射是一種特殊的方式,使得程序不需要進行磁盤i/o就能對磁盤文件進行操作,而且支持多種保護屬性。對于一個被映射的文件,主要是使用CreateFileMapping函數,利用他我們可以設定一些讀寫屬性:PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY.第一參數指定只能對該映射文件進行讀操作。任何寫操作將導致內存訪問錯誤。第二個參數則指明可以對映射文件進行讀寫。這時候,任何對文件的讀寫都是直接操作文件的。而對于第三個參數PAGE_WRITECOPY顧名思義就是寫入時拷貝,任何向這段內存寫入的操作(因為文件是映射到進程地址空間的,對這段空間的讀寫就相當于對文件進行的直接讀寫)都將被系統捕獲,并重新在你的虛擬地址空間重新保留并分配一段內存,你所寫入的一切東西都將在這里,而且你原先的指向映射文件的內存地址也會實際指向這段重新分配的內存,于是在進程結束后,映射文件內容并沒有改變,只是在運行期間在那段私有拷貝的內存里面存在著你修改的內容。windows進程運行所需要映射的一些系統dll就是以這種方式映射的,比如常用的ntdll.dll,kernel32.dll,gdi32.dll.幾乎所有的進程都會加載這三個動態庫。如果你在一個進程里修改這個映射文件的內容,并不會影響到其他的進程使用他們。你所修改的只是在本進程的地址空間之內的。事實上原始文件并沒有被改變。 這樣,在后面的修改系統api的時候,實際就是修改這些動態庫地址內的內容。前面說到這不是修改全局api就是這個原因,因為他們都是以寫入時拷貝的方式來映射的。不過這已經足夠了,windows提供了2個強大的內存操作函數ReadProcessMemory和WriteProcessMemory.利用這兩個函數我們就可以隨便對任意進程的任意用戶地址空間進行讀寫了。但是,現在有一個問題,我們該寫什么,說了半天,怎么實現跳轉呢?現在來看一個簡單的例子: MessageBox(NULL, "World", "Hello", 0); 我們在執行這條語句的時候,調用了系統api MessageBox,實際上在程序中我沒有定義UNICODE宏,系統調用的是MessageBox的ANSI版本MessageBoxA,這個函數是由user32.dll導出的。下面是執行這條語句的匯編代碼: 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)] 前面四條指令分別為參數壓棧,因為MessageBoxA是__stdcall調用約定,所以參數是從右往左壓棧的。最后再CALL 0x0042428c
看看0042428c這段內存的值: 0042428C 0B 05 D5 77 00 00 00 可以看到這個值0x77d5050b,正是user32.dll導出函數MessageBoxA的入口地址。
這是0x77D5050B處的內容, 77D5050B 8B FF ? ? ? ? ? mov ? ? ? edi,edi 77D5050D 55 ? ? ? ? ? ? push ? ? ebp 77D5050E 8B EC ? ? ? ? ? mov ? ? ? ebp,esp 理論上只要改變api入口和出口的任何機器碼,都可以攔截該api。這里我選擇最簡單的修改方法,直接修改api入口的前十個字節來實現跳轉。為什么是十字節呢?其實修改多少字節都沒有關系,只要實現了函數的跳轉之后,你能把他們恢復并讓他繼續運行才是最重要的。在CPU的指令里,有幾條指令可以改變程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。這里我選擇CALL指令,因為他是以函數調用的方式來實現跳轉的,這樣可以帶一些你需要的參數。到這里,我該說說函數的堆棧了。
總結:windows進程所需要的動態庫文件都是以寫入時拷貝的方式映射到進程地址空間中的。這樣,我們只能攔截指定的進程。修改目標進程地址空間中的指定api的入口和出口地址之間的任意數據,使之跳轉到我們的攔截代碼中去,然后再恢復這些字節,使之能順利工作。
二:函數堆棧的一些知識
? 正如前面所看到MessageBoxA函數執行之前的匯編代碼,首先將四個參數壓棧,然后CALL MessageBoxA,這時候我們的線程堆棧看起來應該是這樣的:
| ? | ? <---ESP |返回地址| |參數1| |參數2| |參數3| |參數4| |.. ? |
我們再看MessageBoxA的匯編代碼, 77D5050B 8B FF ? ? ? ? ? mov ? ? ? edi,edi 77D5050D 55 ? ? ? ? ? ? push ? ? ebp 77D5050E 8B EC ? ? ? ? ? mov ? ? ? ebp,esp 注意到堆棧的操作有PUSH ebp,這是保存當前的基址指針,以便一會兒恢復堆棧后返回調用線程時使用,然后再有mov ebp,esp就是把當前esp的值賦給ebp,這時候我們就可以使用 ebp+偏移 來表示堆棧中的數據,比如參數1就可以表示成[ebp+8],返回地址就可以表示成[ebp+4]..如果我們在攔截的時候要對這些參數和返回地址做任何處理,就可以使用這種方法。如果這個時候函數有局部變量的話,就通過減小ESP的值的方式來為之分配空間。接下來就是保存一些寄存器:EDI,ESI,EBX.要注意的是,函數堆棧是反方向生長的。這時候堆棧的樣子: |....| |EDI| <---ESP |ESI| |EBX| |局部變量| |EBP ? | ? |返回地址| |參數1| |參數2| |參數3| |參數4| |.. ? |
在函數返回的時候,由函數自身來進行堆棧的清理,這時候清理的順序和開始入棧的順序恰恰相反,類似的匯編代碼可能是這樣的:
pop edi pop esi pop ebx add esp, 4 pop ebp ret 0010 先恢復那些寄存器的值,然后通過增加ESP的值的方式來釋放局部變量。這里可以用mov esp, ebp來實現清空所有局部變量和其他一些空閑分配空間。接著函數會恢復EBP的值,利用指令POP EBP來恢復該寄存器的值。接著函數運行ret 0010這個指令。該指令的意思是,函數把控制權交給當前棧頂的地址的指令,同時清理堆棧的16字節的參數。如果函數有返回值的話,那在EAX寄存器中保存著當前函數的返回值。如果是__cdecl調用方式,則執行ret指令,對于堆棧參數的處理交給調用線程去做。如wsprintf函數。
這個時候堆棧又恢復了原來的樣子。線程得以繼續往下執行... 在攔截api的過程之中一個重要的任務就是保證堆棧的正確性。你要理清每一步堆棧中發生了什么。
三:形成思路 ? ? 呵呵,不知道你現在腦海是不是有什么想法。怎么去實現攔截一個api? ? 這里給出一個思路,事實上攔截的方法真的很多,理清了一個,其他的也就容易了。而且上面所說的2個關鍵知識,也可以以另外的形式來利用。 ? 我以攔截CreateFile這個api為例子來簡單說下這個思路吧: ? ? 首先,既然我們要攔截這個api就應該知道這個函數在內存中的位置吧,至少需要知道從哪兒入口。CreateFile這個函數是由kernel32.dll這個動態庫導出的。我們可以使用下面的方法來獲取他映射到內存中的地址: ? HMODULE hkernel32 = LoadLibrary("Kernel32.dll"); ? PVOID dwCreateFile = GetProcAddress(hkernei32, "CreateFileA"); 這就可以得到createfile的地址了,注意這里是獲取的createfile的ansic版本。對于UNICODE版本的則獲取CreateFileW。這時dwCreateFile的值就是他的地址了。對于其他進程中的createfile函數也是這個地址,前面說過windows指定了他提供的所有的dll文件的加載地址。 ? ? 接下來,我們該想辦法實現跳轉了。最簡單的方法就是修改這個api入口處的代碼了。但是我們該修改多少呢?修改的內容為什么呢?前面說過我們可以使用CALL的方式來實現跳轉,這種方法的好處是可以為你的攔截函數提供一個或者多個參數。這里只要一個參數就足夠了。帶參數的函數調用的匯編代碼是什么樣子呢,前面也已經說了,類似與調用MessageBoxA時的代碼:
PUSH 參數地址 CALL 函數入口地址(這里為一個偏移地址)
執行這2條指令就能跳轉到你要攔截的函數了,但是我們該修改成什么呢。首先,我們需要知道這2條指令的長度和具體的機器代碼的值。其中PUSH對應0x68,而CALL指令對應的機器碼為0xE8,而后面的則分別對應攔截函數的參數地址和函數的地址。注意第一個是一個直接的地址,而第二個則是一個相對地址。當然你也可以使用0xFF0x15這個CALL指令來進行直接地址的跳轉。 下面就是計算這2個地址的值了, 對于參數和函數體的地址,要分情況而定,對于對本進程中api的攔截,則直接取地址就可以了。對于參數,可以先定義一個參數變量,然后取變量地址就ok了。 如果是想攔截其他進程中的api,則必須使用其他一些方法,最典型的方法是利用VirtualAllocEx函數來在其他進程中申請和提交內存空間。然后用WriteProcessMemory來分別把函數體和參數分別寫入申請和分配的內存空間中去。然后再生成要修改的數據,最后用WriteProcessMemory來修改api入口,把入口的前10字節修改為剛剛生成的跳轉數據。比如在遠程進程中你寫入的參數和函數體的內存地址分別為0x00010000和0x00011000,則生成的跳轉數據為 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000 CALL 00011000),這樣程序運行createfile函數的時候將會先運行PUSH 00010000 CALL 00011000,這樣就達到了跳轉的目的。此刻我們應該時刻注意堆棧的狀態,對于CreateFile有 HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); 可以看到其有7個參數,于是在調用之前,堆棧應該已經被壓入了這7個參數,堆棧的樣子: |....| ? <---ESP |createfile執行后的下一條指令地址| |參數1| |參數2| |參數3| |參數4| |參數5| |參數6| |參數7| |..|
這是執行到我們的跳轉語句:PUSH 00010000,于是堆棧又變了:
|....| ? <---ESP |00010000| |createfile執行后的下一條指令地址| |參數1| |參數2| |參數3| |參數4| |參數5| |參數6| |參數7| |..|
接著執行CALL 00011000,堆棧變為: |...| <---ESP |api入口之后的第11個字節的指令的地址| ? |00010000| |createfile執行后的下一條指令地址| |參數1| |參數2| |參數3| |參數4| |參數5| |參數6| |參數7| |..|
接下來就到了我們的攔截函數中拉,當然,函數肯定也會做一些類似動作,把EBP壓棧,為局部變量分配空間等。這時候堆棧的樣子又變了:
|EDI| <---ESP |ESI| |EBX| |局部變量| |EBP| ? <---EBP |api入口之后的第11個字節的指令的地址| ? |00010000| |createfile執行后的下一條指令地址| |參數1| |參數2| |參數3| |參數4| |參數5| |參數6| |參數7| |..|
這時候,你想做什么就盡情地做吧,獲取參數信息,延緩執行CreateFile函數等等。拿獲取打開文件句柄的名字來說吧,文件名是第一個參數,前面說過我們可以用[EBP+8]來獲取參數,但是對照上面的堆棧形狀,中間又加了另外一些數據,所以我們用[EBP+16]來獲取第一個參數的地址。比如: char* PFileName = NULL; __asm{ MOV EAX,[EBP+16] MOV [szFileName], EAX }
比如我們用一個messagebox來彈出一個信息,說明該程序即將打開一個某謀路徑的文件句柄。但是有一個要注意的是,如果你想攔截遠程進程的話,對于那個攔截函數中所使用到的任何函數或者以任何形式的相對地址的調用都要停止。因為每個進程中的地址分配都是獨立的,比如上面的CALL MessageBoxA改成直接地址的調用。對于使用messagebox,我們應該定義一個函數指針,然后把這個指針的值賦值為user32.dll中導出該函數的直接地址。然后利用這個指針來進行函數調用。對于messagebox函數的調用可以這樣,在源程序中定義一個參數結構體,參數中包含一個導出函數的地址,把這個地址設為MessageBoxA的直接地址,獲取地址的方法就不說了。然后把這個參數傳給攔截函數,就可以使用拉。這也是利用一個參數的原因。類似代碼如下:
typedef struct _RemoteParam { ? DWORD dwMessageBox; } RemoteParam, * PRemoteParam;
typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);//定義一個函數指針
//攔截函數 void HookCreateFile(LPVOID lParam) { RemoteParam* pRP = (RemoteParam*)lParam;//獲取參數地址 char* PFileName = NULL;//定義一個指針 __asm{ MOV EAX,[EBP+16] MOV [szFileName], EAX //把CreateFile第一個參數的值,文件的路徑的地址傳 ? ? ? ? ? ? ? //給szFileName }
//定 義一個函數指針 PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;
pfnMessageBox(NULL, PFileName, PFileName, MB_ICONINFORMATION |MB_OK); //輸出要打開的文件的路徑 //..... }
對于你要使用的其他函數,都是使用同樣的方式,利用這個參數來傳遞我們要傳遞的函數的絕對地址,然后定義這個函數指針,就可以使用了。
好了,接下來我們該讓被攔截的api正常工作了,這個不難,把他原來的數據恢復一下就可以了。那入口的10個字節。我們在改寫他們的時候應該保存一下,然后也把他放在參數中傳遞給攔截函數,呵呵,參數的作用可多了。接著我們就可以用WriteProcessMemory函數來恢復這個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); 其中這些函數指針的定義和上面的類似。 而參數中的szoldcode則是在源程序中在修改api之前保存好,然后傳給攔截函數,在源程序中是用ReadProcessMemory函數來獲取他的前10個字節的: ReadProcessMemory(GetCurrentProcess(), ? ? ? ? ? ? ? ? ? ? ? (LPCVOID)RParam.dwCreateFile, ? ? ? ? ? ? ? ? ? ? ? oldcode, ? ? ? ? ? ? ? ? ? ? ? 10, ? ? ? ? ? ? ? ? ? ? ? &dwPid) strcat((char*)RParam.szOldCode, (char*)oldcode);
接下來如果你還繼續保持對該api的攔截,則又該用WriteProcessMemory 來修改入口了,跟前面的恢復入口是一樣的,只不過把szOldCode換成了szNewCode了而已。這樣你又能對CreateFile繼續攔截了。
好了,接下來該進行堆棧的清理了,也許你還要做點其他事情,盡管做去。但是清理堆棧是必須要做的,在函數結束的時候,因為在我們放任api恢復執行之后,他又return 到我們的函數中來了,這個時候的堆棧是什么樣子呢? |EDI| <---ESP |ESI| |EBX| |局部變量| |EBP| ? <---EBP |api入口之后的第11個字節的指令的地址| ? |00010000| |createfile執行后的下一條指令地址| |參數1| |參數2| |參數3| |參數4| |參數5| |參數6| |參數7| |..|
我們的目標是把返回值記錄下來放到EAX寄存器中去,把返回地址記錄下來,同時把堆棧恢復成原來的樣子。 首先我們恢復那些寄存器的值,接著釋放局部變量,可以用mov esp, ebp.因為我們不清楚具體的局部變量分配了多少空間。所以使用這個方法。
__asm {POP EDI POP ESI POP EBX ? //恢復那些寄存器 MOV EDX, [NextIpAddr]//把返回地址放到EDX中,因為待會兒 ? ? ? ? ? ? //EBX被恢復后,線程中的所有局部變量就不能正常使用了。 ? ? ? MOV EAX, [RetValue]//返回值放到EAX中,當然也可以修改這個返回值 MOV ESP, EBP//清理局部變量 POP EBP//恢復EBP的值 ADD ESP, 28H //清理參數和返回地址,注意一共(7+1+1+1)*4 PUSH EDX //把返回地址壓棧,這樣棧中就只有這一個返回地址了,返回之后棧 ? ? ? //就空了 RET }
這樣,一切就完成了,堆棧恢復了應該有的狀態,而你想攔截的也攔截到了。
四:后記 ? 攔截的方式多種多樣,不過大體的思路卻都相同。要時刻注意你要攔截的函數的堆棧狀態以及在攔截函數中的對數據的引用和函數的調用(地址問題)。
? ////////////////////////////////////////////////////////////////////// 附錄:一個攔截CreateFile函數的簡單實現 //////////////////////////////////////////////////////////////////////
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請輸入要攔截的進程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"); ? ? ? CloseHandle(hTargetProcess); ? ? ? return -1; ? }
? DWORD dwPramaAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, sizeof(RemoteParam), ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
? if((LPVOID)dwPramaAddr == NULL) ? { ? ? ? printf("申請參數內存失敗!\n"); ? ? ? CloseHandle(hTargetProcess); ? ? ? return -1; ? }
? printf("\n線程內存地址:%.8x\n" ? ? ? ? "參數內存地址:%.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; }
|