新聞中心
Windows操作系統(tǒng)下,為了避免各個(gè)進(jìn)程相互影響,每個(gè)進(jìn)程地址空間都是被隔離的。所謂 “遠(yuǎn)程線(xiàn)程”,并不是跨計(jì)算機(jī)的,而是跨進(jìn)程的。簡(jiǎn)單來(lái)說(shuō),就是進(jìn)程A要在進(jìn)程B中創(chuàng)建一個(gè)線(xiàn)程,這就叫遠(yuǎn)程線(xiàn)程。

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。創(chuàng)新互聯(lián)建站推出鎮(zhèn)康免費(fèi)做網(wǎng)站回饋大家。
遠(yuǎn)程線(xiàn)程被木馬、外掛等程序廣泛使用,反病毒軟件中也離不開(kāi)遠(yuǎn)程線(xiàn)程的技術(shù)。技術(shù)應(yīng)用的兩面性取決于自己的個(gè)人行為意識(shí),良性的技術(shù)學(xué)習(xí)對(duì)自己的人生發(fā)展是非常有好處的,就算談不上好處,至少不會(huì)給自己帶來(lái)不必要的麻煩。
關(guān)于遠(yuǎn)程線(xiàn)程的知識(shí),本文介紹3個(gè)例子,分別是DLL的注入、卸載遠(yuǎn)程DLL和不依賴(lài)DLL進(jìn)行代碼注入。
1. DLL遠(yuǎn)程注入
木馬或病毒編寫(xiě)的好壞取決于其隱藏的程度,而不在于其功能的多少。無(wú)論是木馬還是病毒,都是可執(zhí)行程序。如果它們是EXE文件的話(huà),那么在運(yùn)行時(shí)必定會(huì)產(chǎn)生一個(gè)進(jìn)程,就很容易被發(fā)現(xiàn)。為了不被發(fā)現(xiàn),在編寫(xiě)木馬或病毒時(shí)可以選擇將其編寫(xiě)為DLL文件。DLL文件的運(yùn)行不會(huì)單獨(dú)創(chuàng)建一個(gè)進(jìn)程,它的運(yùn)行被加載到進(jìn)程的地址空間中,因此其隱蔽性相對(duì)較好。DLL文件如果不被進(jìn)程加載又如何在進(jìn)程的地址空間中運(yùn)行呢?方式是強(qiáng)制讓某進(jìn)程加載DLL文件到其地址空間中去,這個(gè)強(qiáng)制的手段就是現(xiàn)在要介紹的遠(yuǎn)程線(xiàn)程。
創(chuàng)建遠(yuǎn)程線(xiàn)程的函數(shù)CreateRemoteThread()的定義如下:
- HANDLE CreateRemoteThread(
- HANDLE hProcess,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- DWORD dwStackSize,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- DWORD dwCreationFlags,
- LPDWORD lpThreadId
- );
該函數(shù)的功能是創(chuàng)建一個(gè)遠(yuǎn)程的線(xiàn)程。我們把CreateThread()函數(shù)和CreateRemoteThread()函數(shù)進(jìn)行比較。對(duì)于CreateThread()函數(shù)來(lái)說(shuō),CreateRem oteThread()函數(shù)比其多了一個(gè)hProcess參數(shù),該參數(shù)是指定要?jiǎng)?chuàng)建線(xiàn)程的進(jìn)程句柄。其實(shí)CreateThread()函數(shù)的內(nèi)容實(shí)現(xiàn)就是依賴(lài)于CreateRemoteThread()函數(shù)來(lái)完成的。CreateThread()函數(shù)的代碼實(shí)現(xiàn)如下:
- /*
- * @implemented
- */
- HANDLE
- WINAPI
- CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
- DWORD dwStackSize,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- DWORD dwCreationFlags,
- LPDWORD lpThreadId)
- {
- /* 創(chuàng)建遠(yuǎn)程線(xiàn)程
- return CreateRemoteThread(NtCurrentProcess(),
- lpThreadAttributes,
- dwStackSize,
- lpStartAddress,
- lpParameter,
- dwCreationFlags,
- lpThreadId);
- }
在上面的代碼中,NtGetCurrentProcess()函數(shù)的功能是獲得當(dāng)前進(jìn)程的句柄。
CreateRemoteThread()函數(shù)是給其他進(jìn)程創(chuàng)建線(xiàn)程使用的,其第一個(gè)參數(shù)是指定某進(jìn)程的句柄,獲取進(jìn)程的句柄使用API函數(shù)OpenProcess(),該函數(shù)需要提供PID作為參數(shù)。
除了hProcess參數(shù)以外,剩余的關(guān)鍵參數(shù)就只有l(wèi)pStartAddress和lpParameter兩個(gè)了。lpStartAddress指定線(xiàn)程函數(shù)的地址,lpParameter指定傳遞給線(xiàn)程函數(shù)的參數(shù)。前面提到,每個(gè)進(jìn)程的地址空間是隔離的,那么新創(chuàng)建的線(xiàn)程函數(shù)的地址也應(yīng)該在目標(biāo)進(jìn)程中,而不應(yīng)該在調(diào)用CreateRemoteThread()函數(shù)的進(jìn)程中。同樣,傳遞給線(xiàn)程函數(shù)的參數(shù)也應(yīng)該在目標(biāo)進(jìn)程中。
如何讓線(xiàn)程函數(shù)的地址在目標(biāo)進(jìn)程中呢?如何讓線(xiàn)程函數(shù)的參數(shù)也可以傳遞到目標(biāo)進(jìn)程中呢?在討論這個(gè)問(wèn)題以前,先來(lái)考慮線(xiàn)程函數(shù)要完成的功能。這里主要完成的功能是注入一個(gè)DLL文件到目標(biāo)進(jìn)程中,那么線(xiàn)程函數(shù)的功能就是加載DLL文件。加載DLL文件使用的是LoadLibrary()函數(shù)。LoadLibrary()函數(shù)的定義:
- HMODULE LoadLibrary(
- LPCTSTR lpFileName
- );
- 看一下線(xiàn)程函數(shù)的定義格式,具體如下:
- DWORD WINAPI ThreadProc(
- LPVOID lpParameter
- );
比較兩個(gè)函數(shù)可以發(fā)現(xiàn),除了函數(shù)的返回值類(lèi)型和參數(shù)類(lèi)型以外,其函數(shù)格式是相同的。這里只考慮其相同的部分。因?yàn)槠浜瘮?shù)的格式相同,首先調(diào)用約定相同,都是WINAPI(也就是__stdcall方式);其次函數(shù)個(gè)數(shù)相同,都只有一個(gè)。那么,可以直接把LoadLibrary()函數(shù)作為線(xiàn)程函數(shù)創(chuàng)建到指定的進(jìn)程中。LoadLibrary()的參數(shù)是欲加載的DLL文件的完整路徑,只要在CreateRemoteThread()函數(shù)中賦值一個(gè)指向DLL文件完整路徑的指針給LoadLibrary()函數(shù)即可。這樣使用CreateRemoteThread()函數(shù)就可以創(chuàng)建一個(gè)遠(yuǎn)程線(xiàn)程了。不過(guò),還有兩個(gè)問(wèn)題沒(méi)有解決,首先是如何將LoadLibrary()函數(shù)的地址放到目標(biāo)進(jìn)程空間中讓CreateRemoteThread()調(diào)用,其次是傳遞給LoadLibrary()函數(shù)的參數(shù)也需要在目標(biāo)進(jìn)程空間中,并且要通過(guò)CreateRemoteThread()函數(shù)指定給LoadLibrary()函數(shù)。
首先解決第1個(gè)問(wèn)題,即如何將LoadLibrary()函數(shù)的地址放到目標(biāo)進(jìn)程空間中。LoadLibrary()函數(shù)是系統(tǒng)中的Kernel32.dll的導(dǎo)出函數(shù),Kernel32.dll這個(gè)DLL文件在任何進(jìn)程中的加載位置都是相同的,也就是說(shuō),LoadLibrary()函數(shù)的地址在任何進(jìn)程中的地址都是相同的。因此,只要在進(jìn)程中獲得LoadLibrary()函數(shù)的地址,那么該地址在目標(biāo)進(jìn)程中也可以使用。CreateRemoteThread()函數(shù)的線(xiàn)程地址參數(shù)直接傳遞LoadLibrary()函數(shù)的地址即可。
其次解決第2個(gè)問(wèn)題,即如何將欲加載的DLL文件完整路徑寫(xiě)入目標(biāo)進(jìn)程中。這需要借助WriteProcessMemory()函數(shù),其定義如下:
- BOOL WriteProcessMemory(
- HANDLE hProcess, // handle to process
- LPVOID lpBaseAddress, // base of memory area
- LPVOID lpBuffer, // data buffer
- DWORD nSize, // number of bytes to write
- LPDWORD lpNumberOfBytesWritten // number of bytes written
- );
該函數(shù)的功能是把lpBuffer中的內(nèi)容寫(xiě)到進(jìn)程句柄是hProcess進(jìn)程的lpBaseAddress地址處,寫(xiě)入長(zhǎng)度為nSize。
參數(shù)說(shuō)明如下。
hProcess:該參數(shù)是指定進(jìn)程的進(jìn)程句柄。
lpBaseAddress:該參數(shù)是指定寫(xiě)入目標(biāo)進(jìn)程內(nèi)存的起始地址。
lpBuffer:該參數(shù)是要寫(xiě)入目標(biāo)進(jìn)程內(nèi)存的緩沖區(qū)起始地址。
nSize:該參數(shù)是指定寫(xiě)入目標(biāo)內(nèi)存中的緩沖區(qū)的長(zhǎng)度。
lpNumberOfBytesWritten:該參數(shù)用于接收實(shí)際寫(xiě)入內(nèi)容的長(zhǎng)度。
該函數(shù)的功能非常強(qiáng)大,比如在破解方面,用該函數(shù)可以實(shí)現(xiàn)一個(gè)“內(nèi)存補(bǔ)丁”;在開(kāi)發(fā)方面,該函數(shù)可以用于修改目標(biāo)進(jìn)程中指定的值(比如游戲修改器可以修改游戲中的錢(qián)、紅、藍(lán)等)。
使用該函數(shù)可以把DLL文件的完整路徑寫(xiě)入到目標(biāo)進(jìn)程的內(nèi)存地址中,這樣就可以在目標(biāo)進(jìn)程中用LoadLibrary()函數(shù)加載指定的DLL文件了。解決了上面的兩個(gè)問(wèn)題,還有第3個(gè)問(wèn)題需要解決。WriteProcessMemory()函數(shù)的第2個(gè)參數(shù)是指定寫(xiě)入目標(biāo)進(jìn)程內(nèi)存的緩沖區(qū)起始地址。這個(gè)地址在目標(biāo)進(jìn)程中,那么這個(gè)地址在目標(biāo)進(jìn)程的哪個(gè)位置呢?目標(biāo)進(jìn)程中的內(nèi)存塊允許把DLL文件的路徑寫(xiě)進(jìn)去嗎?
第3個(gè)要解決的問(wèn)題是如何確定應(yīng)該將DLL文件的完整路徑寫(xiě)入目標(biāo)進(jìn)程的哪個(gè)地址。對(duì)于目標(biāo)進(jìn)程來(lái)說(shuō),事先是不會(huì)準(zhǔn)備一塊地址讓用戶(hù)進(jìn)行寫(xiě)入的,用戶(hù)能做的是自己在目標(biāo)進(jìn)程中申請(qǐng)一塊內(nèi)存,然后把DLL文件的路徑進(jìn)行寫(xiě)入,寫(xiě)入在目標(biāo)進(jìn)程新申請(qǐng)到的內(nèi)存空間中。在目標(biāo)進(jìn)程中申請(qǐng)內(nèi)存的函數(shù)是VirtualAllocEx(),其定義如下:
- LPVOID VirtualAllocEx(
- HANDLE hProcess,
- LPVOID lpAddress,
- SIZE_T dwSize,
- DWORD flAllocationType,
- DWORD flProtect
- );
VirtualAllocEx()函數(shù)的參數(shù)說(shuō)明如下。
hProcess:該參數(shù)是指定進(jìn)程的進(jìn)程句柄。
lpAddress:該參數(shù)是指在目標(biāo)進(jìn)程中申請(qǐng)內(nèi)存的起始地址。
dwSize:該參數(shù)是指在目標(biāo)進(jìn)程中申請(qǐng)內(nèi)存的長(zhǎng)度。
flAllocationType:該參數(shù)指定申請(qǐng)內(nèi)存的狀態(tài)類(lèi)型。
flProtect:該參數(shù)指定申請(qǐng)內(nèi)存的屬性。
該函數(shù)的返回值是在目標(biāo)進(jìn)程申請(qǐng)到的內(nèi)存塊的起始地址。
到此,關(guān)于編寫(xiě)一個(gè)DLL注入的所有知識(shí)都已經(jīng)具備了?,F(xiàn)在開(kāi)始編寫(xiě)一個(gè)DLL注入的工具,其界面如圖1所示。
圖1 DLL注入/卸載器
該工具有2個(gè)作用,分別是注入DLL和卸載被注入的DLL。關(guān)于卸載被注入的DLL的功能,將在后面進(jìn)行介紹。在界面上要求輸入兩部分內(nèi)容,第1部分是欲注入的DLL文件的完整路徑(一定要是完整路徑),第2部分是進(jìn)程的名稱(chēng)。
首先看一下關(guān)于界面的操作,代碼如下:
- void CInjectDllDlg::OnBtnInject()
- {
- // 添加處理程序代碼
- char szDllName[MAX_PATH] = { 0 };
- char szProcessName[MAXBYTE] = { 0 };
- DWORD dwPid = 0;
- GetDlgItemText(IDC_EDIT_DLLFILE, szDllName, MAX_PATH);
- GetDlgItemText(IDC_EDIT_PROCESSNAME, szProcessName, MAXBYTE);
- // 由進(jìn)程名獲得 PID
- dwPid = GetProcId(szProcessName);
- // 注入 szDllName 到 dwPid
- InjectDll(dwPid, szDllName);
- }
代碼中調(diào)用了另外兩個(gè)函數(shù),第1個(gè)是由進(jìn)程名獲得PID的函數(shù),第2個(gè)是用于DLL注入的函數(shù)。GetProcId()函數(shù)的代碼如下:
- DWORD CInjectDllDlg::GetProcId(char *szProcessName)
- {
- BOOL bRet;
- PROCESSENTRY32 pe32;
- HANDLE hSnap;
- hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
- pe32.dwSize = sizeof(pe32);
- bRet = Process32First(hSnap, &pe32);
- while ( bRet )
- {
- // strupr()函數(shù)是將字符串轉(zhuǎn)化為大寫(xiě)
- if ( lstrcmp(strupr(pe32.szExeFile),strupr(szProcessName)) == 0 )
- {
- return pe32.th32ProcessID;
- }
- bRet = Process32Next(hSnap, &pe32);
- }
- return 0;
- } +
InjectDll()函數(shù)的代碼如下:
- VOID CInjectDllDlg::InjectDll(DWORD dwPid, char *szDllName)
- {
- if ( dwPid == 0 || lstrlen(szDllName) == 0 )
- {
- return ;
- }
- char *pFunName = "LoadLibraryA";
- // 打開(kāi)目標(biāo)進(jìn)程
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPid);
- if ( hProcess == NULL )
- {
- return ;
- }
- // 計(jì)算欲注入 DLL 文件完整路徑的長(zhǎng)度
- int nDllLen = lstrlen(szDllName) + sizeof(char);
- // 在目標(biāo)進(jìn)程申請(qǐng)一塊長(zhǎng)度為 nDllLen 大小的內(nèi)存空間
- PVOID pDllAddr = VirtualAllocEx(hProcess,NULL, nDllLen,MEM_COMMIT,PAGE_READWRITE);
- if ( pDllAddr == NULL )
- {
- CloseHandle(hProcess);
- return ;
- }
- DWORD dwWriteNum = 0;
- // 將欲注入 DLL 文件的完整路徑寫(xiě)入在目標(biāo)進(jìn)程中申請(qǐng)的空間內(nèi)
- WriteProcessMemory(hProcess, pDllAddr, szDllName,nDllLen, &dwWriteNum);
- // 獲得 LoadLibraryA()函數(shù)的地址
- FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"),pFunName);
- // 創(chuàng)建遠(yuǎn)程線(xiàn)程
- HANDLE hThread = CreateRemoteThread(hProcess,NULL, 0,(LPTHREAD_START_ROUTINE)pFunAddr,pDllAddr, 0, NULL);
- WaitForSingleObject(hThread, INFINITE);
- CloseHandle(hThread);
- CloseHandle(hProcess);
- }
InjectDll()函數(shù)有 2 個(gè)參數(shù),分別是目標(biāo)進(jìn)程的 ID 值和要被注入的 DLL 文件的完整路徑。在代碼中獲得的不是 LoadLibrary()函數(shù)的地址,而是 LoadLibraryA()函數(shù)的地址。在系統(tǒng)中其實(shí)沒(méi)有 LoadLibrary()函數(shù),有的只是 LoadLibraryA()和 LoadLibraryW()兩個(gè)函數(shù)。這兩個(gè)函數(shù)分別針對(duì) ANSI 字符串和 UNICODE 字符串。而 LoadLibrary()函數(shù)只是一個(gè)宏。在編寫(xiě)程序的時(shí)候,直接使用該宏是可以的。如果要獲取 LoadLibrary()函數(shù)的地址,就要明確指定是獲取 LoadLibraryA()還是 LoadLibraryW()。
LoadLibrary()宏定義如下:
- #ifdef UNICODE
- #define LoadLibrary LoadLibraryW
- #else
- #define LoadLibrary LoadLibraryA
- #endif // !UNICODE
只要涉及字符串的函數(shù),都會(huì)有相應(yīng)的ANSI版本和UNICODE版本;其余不涉及字符串的函數(shù),沒(méi)有ANSI版本和UNICODE版本的區(qū)別。
為了測(cè)試DLL加載是否成功,在代碼的DllMain()函數(shù)中加入如下代碼:
- case DLL_PROCESS_ATTACH:
- {
- MsgBox("!DLL_PROCESS_ATTACH!");
- break;
- }
現(xiàn)在測(cè)試一下注入的效果,如圖2和圖3所示。
圖2 DLL文件被注入成功的提示
圖3 查看進(jìn)程中的DLL列表確認(rèn)被裝載成功
在圖2中,彈出的對(duì)話(huà)框是DLL程序在DLL_PROCESS_ATTACH時(shí)出現(xiàn)的。其所在的進(jìn)程為notepad.exe。從圖2中可以看出,彈出提示框的標(biāo)題處是notepad.exe進(jìn)程的路徑。圖3是用工具查看進(jìn)程中所加載的DLL文件列表,可以看出,通過(guò)注入工具注入的DLL文件已經(jīng)被加載到notepad.exe的進(jìn)程空間中。
如果要對(duì)系統(tǒng)進(jìn)程進(jìn)行注入的話(huà),由于進(jìn)程權(quán)限的關(guān)系是無(wú)法注入成功的。在打開(kāi)目標(biāo)進(jìn)程時(shí)用到了OpenProcess()函數(shù),由于權(quán)限不夠,會(huì)導(dǎo)致無(wú)法打開(kāi)進(jìn)程并獲得進(jìn)程句柄。通過(guò)調(diào)整當(dāng)前進(jìn)程的權(quán)限,可以打開(kāi)系統(tǒng)進(jìn)程并獲得進(jìn)程句柄。如果在Win8或更高版本上運(yùn)行注入程序的話(huà),需要選中注入工具單擊右鍵,選擇“以管理員身份運(yùn)行”才可以完成注入。
2. 卸載被注入的DLL文件
DLL注入如果應(yīng)用在木馬方面,危害很大,這里完成一個(gè)卸載被注入DLL的程序。卸載被注入DLL程序的思路和注入的思路是一樣的,而且代碼的改動(dòng)也非常小。區(qū)別在于現(xiàn)在的功能是卸載,而不是注入。
DLL卸載使用的API函數(shù)是FreeLiabrary(),其定義如下:
- BOOL FreeLibrary(
- HMODULE hModule // handle to DLL module
- );
該函數(shù)的參數(shù)是要卸載的模塊的句柄。
FreeLibrary()函數(shù)使用的模塊句柄可以通過(guò)Module32First()和Module32Next()兩個(gè)函數(shù)獲取。在使用Module32First()和Module32Next()兩個(gè)函數(shù)的時(shí)候,需要用到MODULEENTRY32結(jié)構(gòu)體,該結(jié)構(gòu)體中保存了模塊的句柄。MODULEENTRY32結(jié)構(gòu)體的定義如下:
- typedef struct tagMODULEENTRY32 {
- DWORD dwSize;
- DWORD th32ModuleID;
- DWORD th32ProcessID;
- DWORD GlblcntUsage;
- DWORD ProccntUsage;
- BYTE * modBaseAddr;
- DWORD modBaseSize;
- HMODULE hModule;
- TCHAR szModule[MAX_MODULE_NAME32 + 1];
- TCHAR szExePath[MAX_PATH];
- } MODULEENTRY32;
- typedef MODULEENTRY32 *PMODULEENTRY32;
該結(jié)構(gòu)體中的hModule為模塊的句柄,szModule為模塊的名稱(chēng),szExePath是完整的模塊的名稱(chēng)(所謂完整,包括路徑和模塊名稱(chēng))。
卸載遠(yuǎn)程進(jìn)程中DLL模塊的代碼如下:
- VOID CInjectDllDlg::UnInjectDll(DWORD dwPid, char *szDllName)
- {
- if ( dwPid == 0 || lstrlen(szDllName) == 0 )
- {
- return ;
- }
- HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwPid);
- MODULEENTRY32 me32;
- me32.dwSize = sizeof(me32);
- // 查找匹配的進(jìn)程名稱(chēng)
- BOOL bRet = Module32First(hSnap, &me32);
- while ( bRet )
- {
- if ( lstrcmp(strupr(me32.szExePath),
- strupr(szDllName)) == 0 )
- {
- break;
- }
- bRet = Module32Next(hSnap, &me32);
- }
- CloseHandle(hSnap);
- char *pFunName = "FreeLibrary";
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPid);
- if ( hProcess == NULL )
- {
- return ;
- }
- FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"),pFunName);
- HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
- (LPTHREAD_START_ROUTINE)pFunAddr,me32.hModule, 0, NULL);
- WaitForSingleObject(hThread, INFINITE);
- CloseHandle(hThread);
- CloseHandle(hProcess);
- }
卸載遠(yuǎn)程進(jìn)程中DLL的實(shí)現(xiàn)代碼比DLL注入的代碼要簡(jiǎn)單,這里就不做過(guò)多的介紹了。
3. 無(wú)DLL的代碼注入
DLL文件的注入與卸載都完成了,整個(gè)注入與卸載的過(guò)程其實(shí)就是讓遠(yuǎn)程線(xiàn)程執(zhí)行一次LoadLibrary()函數(shù)或FreeLibrary()函數(shù)。遠(yuǎn)程線(xiàn)程裝載一個(gè)DLL文件,通過(guò)DllMain()調(diào)用DLL中的具體功能代碼,這樣注入DLL后就可以讓DLL做很多事情了。是否可以不依賴(lài)DLL文件直接向目標(biāo)進(jìn)程寫(xiě)入要執(zhí)行的代碼,以完成特定的功能呢?答案是可以。
要在目標(biāo)進(jìn)程中完成一定的功能,就需要使用相關(guān)的API函數(shù),不同的API函數(shù)實(shí)現(xiàn)在不同的DLL中。Kernel32.dll文件在每個(gè)進(jìn)程中的地址是相同的,但是并不代表其他DLL文件在每個(gè)進(jìn)程中的地址都是一樣的。這樣,在目標(biāo)進(jìn)程中調(diào)用API函數(shù)時(shí),必須使用LoadLibrary()函數(shù)和GetProcAddress()函數(shù)動(dòng)態(tài)調(diào)用用到的每個(gè)API函數(shù)。把想要使用的API函數(shù)及API函數(shù)所在的DLL文件都封裝到一個(gè)結(jié)構(gòu)體中,直接寫(xiě)入目標(biāo)進(jìn)程的空間中。同時(shí)也直接把要在遠(yuǎn)程執(zhí)行的代碼也寫(xiě)入目標(biāo)進(jìn)程的內(nèi)存空間中,最后調(diào)用CreateRemoteThread()函數(shù)即可將其運(yùn)行。
通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的例子讓遠(yuǎn)程線(xiàn)程彈出一個(gè)提示對(duì)話(huà)框,但是不借助于DLL。本程序所使用的API函數(shù)在前面都已經(jīng)介紹過(guò)了。根據(jù)前面的步驟先來(lái)定義一個(gè)結(jié)構(gòu)體,其定義如下:
- #define STRLEN 20
- typedef struct _DATA
- {
- DWORD dwLoadLibrary;
- DWORD dwGetProcAddress;
- DWORD dwGetModuleHandle;
- DWORD dwGetModuleFileName;
- char User32Dll[STRLEN];
- char MessageBox[STRLEN];
- char Str[STRLEN];
- }DATA, *PDATA;
該結(jié)構(gòu)體中保存了LoadLibraryA()、GetProcAddress()、GetModuleHandle()和GetModu leFileName()四個(gè)API函數(shù)的地址。這四個(gè)API函數(shù)都屬于Kernel32.dll的導(dǎo)出函數(shù),因此可以在注入前進(jìn)行獲取。User32Dll中保存“User32.dll”字符串,因?yàn)镸essageBoxA()函數(shù)是由User32.dll的導(dǎo)出函數(shù)。Str中保存的是通過(guò)MessageBoxA()函數(shù)彈出的字符串。
注入代碼類(lèi)似于前面介紹的注入代碼,不過(guò)需要在注入代碼中定義一個(gè)結(jié)構(gòu)體變量,并進(jìn)行相應(yīng)的初始化,代碼如下:
- VOID CNoDllInjectDlg::InjectCode(DWORD dwPid)
- {
- // 打開(kāi)進(jìn)程并獲取進(jìn)程句柄
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPid);
- if ( hProcess == NULL )
- {
- return ;
- }
- DATA Data = { 0 };
- // 獲取 kernel32.dll 中相關(guān)的導(dǎo)出函數(shù)
- Data.dwLoadLibrary = (DWORD)GetProcAddress(
- GetModuleHandle("kernel32.dll"),"LoadLibraryA");
- Data.dwGetProcAddress = (DWORD)GetProcAddress(
- GetModuleHandle("kernel32.dll"),"GetProcAddress");
- Data.dwGetModuleHandle = (DWORD)GetProcAddress(
- GetModuleHandle("kernel32.dll"),"GetModuleHandleA");
- Data.dwGetModuleFileName = (DWORD)GetProcAddress(
- GetModuleHandle("kernel32.dll"),"GetModuleFileNameA");
- // 需要的其他 DLL 和導(dǎo)出函數(shù)
- lstrcpy(Data.User32Dll, "user32.dll");
- lstrcpy(Data.MessageBox, "MessageBoxA");
- // MessageBoxA()彈出的字符串
- lstrcpy(Data.Str, "Inject Code !!!");
- // 在目標(biāo)進(jìn)程申請(qǐng)空間
- LPVOID lpData = VirtualAllocEx(hProcess, NULL, sizeof(Data),
- MEM_COMMIT | MEM_RELEASE,PAGE_READWRITE);
- DWORD dwWriteNum = 0;
- WriteProcessMemory(hProcess, lpData, &Data,
- sizeof(Data), &dwWriteNum);
- // 在目標(biāo)進(jìn)程空間申請(qǐng)的用于保存代碼的長(zhǎng)度
- DWORD dwFunSize = 0x4000;
- LPVOID lpCode = VirtualAllocEx(hProcess, NULL, dwFunSize,
- MEM_COMMIT,PAGE_EXECUTE_READWRITE);
- WriteProcessMemory(hProcess, lpCode, &RemoteThreadProc,
- dwFunSize, &dwWriteNum);
- HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0
- (LPTHREAD_START_ROUTINE)lpCode,lpData, 0, NULL);
- WaitForSingleObject(hThread, INFINITE);
- CloseHandle(hThread);
- CloseHandle(hProcess);
- }
上面的注入代碼除了對(duì)結(jié)構(gòu)體變量初始化外,還將線(xiàn)程函數(shù)代碼寫(xiě)入目標(biāo)進(jìn)程空間的內(nèi)存中。線(xiàn)程函數(shù)的代碼如下:
- DWORD WINAPI RemoteThreadProc(LPVOID lpParam)
- {
- PDATA pData = (PDATA)lpParam;
- // 定義 API 函數(shù)原型
- HMODULE (__stdcall *MyLoadLibrary)(LPCTSTR);
- FARPROC (__stdcall *MyGetProcAddress)(HMODULE, LPCSTR);
- HMODULE (__stdcall *MyGetModuleHandle)(LPCTSTR);
- int (__stdcall *MyMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT);
- DWORD (__stdcall *MyGetModuleFileName)(HMODULE, LPTSTR, DWORD);
- // 對(duì)各函數(shù)地址進(jìn)行賦值
- MyLoadLibrary = (HMODULE (__stdcall *)(LPCTSTR))
- pData->dwLoadLibrary;
- MyGetProcAddress = (FARPROC (__stdcall *)(HMODULE, LPCSTR))
- pData->dwGetProcAddress;
- MyGetModuleHandle = (HMODULE (__stdcall *)(LPCSTR))
- pData->dwGetModuleHandle;
- MyGetModuleFileName = (DWORD (__stdcall *)(HMODULE, LPTSTR, DWORD))
- pData->dwGetModuleFileName;
- // 加載 User32.dll
- HMODULE hModule = MyLoadLibrary(pData->User32Dll);
- // 獲得 MessageBoxA 函數(shù)的地址
- MyMessageBox = (int (__stdcall *)(HWND, LPCTSTR, LPCTSTR, UINT))
- MyGetProcAddress(hModule, pData->MessageBox);
- char szModuleFileName[MAX_PATH] = { 0 };
- MyGetModuleFileName(NULL, szModuleFileName, MAX_PATH);
- MyMessageBox(NULL, pData->Str, szModuleFileName, MB_OK);
- return 0;
- }
上面就是無(wú)DLL注入的全部代碼,編譯連接并運(yùn)行它。啟動(dòng)一個(gè)記事本程序來(lái)進(jìn)行測(cè)試,可惜報(bào)錯(cuò)了。問(wèn)題出在哪里呢?VC6的默認(rèn)編譯是Debug版本,這樣會(huì)加入很多調(diào)試信息。而某些調(diào)試信息并不存在于代碼中,而是在其他DLL模塊中。這樣,當(dāng)執(zhí)行到調(diào)試相關(guān)的代碼時(shí)會(huì)訪問(wèn)不存在的DLL模塊中的代碼,就導(dǎo)致了報(bào)錯(cuò)。
將以上代碼使用Release方式進(jìn)行編譯連接,然后可以無(wú)誤地執(zhí)行,如圖4所示。
圖4 Release方式下編譯注入成功
編譯的Debug版也可以進(jìn)行無(wú)DLL的注入,只是實(shí)現(xiàn)起來(lái)略有不同。
名稱(chēng)欄目:網(wǎng)絡(luò)安全編程:遠(yuǎn)程線(xiàn)程編程
網(wǎng)頁(yè)路徑:http://fisionsoft.com.cn/article/cdshids.html


咨詢(xún)
建站咨詢(xún)
