Process-wide API spying - an ultimate hack 摘要翻译(二)
通過目標進程中掛鉤所有對DLL的調用
由于通過修改目標進程的輸入表IAT只能對當前進程有效,所以我們即使對當前所有已加載模塊都掛鉤了對kernel32.dll的調用,但在這之后啟動的進程將不會被掛鉤。因此除了對已加載進程修改IAT以外,還需要修改kernel32.dll的輸出表EAT。
1、修改目標進程的IAT表對所有已加載的模塊有效
2、修改kernel32.dll的EAT表,所有將來加載的DLL有效。
因此兩種方法必須同時使用。注意,現(xiàn)在說的不是系統(tǒng)鉤子,而僅是對目標進程中的操作有效。
EAT表的操作如下:(說明略去)
IMAGE_DOS_HEADER * dosheader=(IMAGE_DOS_HEADER *)hMod;
IMAGE_OPTIONAL_HEADER * opthdr =(IMAGE_OPTIONAL_HEADER *)
((BYTE*)hMod+dosheader->e_lfanew+24);
IMAGE_EXPORT_DIRECTORY *exp=(IMAGE_EXPORT_DIRECTORY *)((BYTE*) hMod
+opthdr->DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress);
ULONG *addressoffunctions=(ULONG*)((BYTE*) hMod +exp->AddressOfFunctions);
ULONG * addressofnames=(ULONG*)((BYTE*) hMod +exp->AddressOfNames);
for(DWORD x=0; x < exp->NumberOfFunctions;x++)
{
char*functionname=(char*)((BYTE*) hMod +addressofnames[x]);
DWORD functionaddress=(DWORD)((BYTE*) hMod +addressoffunctions[x]);
}
這里,不能用代理函數(shù)的實際內(nèi)存地址替換EAT的函數(shù)地址,而必須使用RVA來替換。由于RVA地址不能為負數(shù)(譯注:原因不清楚,但是RVA至少不能小,否則會被認為是輸出轉向),因為代理函數(shù)的地址必須位于Kernel32.dll模塊基地址之上(因為我們要對kernel32.dll掛鉤)
BYTE* writebuff=(BYTE*)VirtualAllocEx(GetCurrentProcess(),0,5*4096,
? MEM_RESERVE|MEM_TOP_DOWN,PAGE_EXECUTE_READWRITE);
writebuff=(BYTE*)VirtualAllocEx(GetCurrentProcess(),writebuff,5*4096,
? MEM_COMMIT|MEM_TOP_DOWN,PAGE_EXECUTE_READWRITE);
for(int x=1;x<=exp->NumberOfFunctions;x++)
{
//這是因為在windows2000上kernel32.dll的輸出函數(shù)一共有823個,而對每一個替換需要6字節(jié)的間接指令,和16字節(jié)的RelocationFunction結構體的大小,也就是需要22字節(jié),按照32位機的4字節(jié)對齊方式,補足到24字節(jié)。另外對于Intel CPU一頁內(nèi)存是4K字節(jié),可以容納170個24字節(jié),還有16字節(jié)的剩余,但考慮到按頁對齊,所以不在它上面另作分配。這樣我們就需要用5頁內(nèi)存來存放對kernel32.dll的輸出表的替換。
DWORD a=(x-1)/170,pos=a*16+(x-1)*24;
BYTE*currentchunk= &writebuff[pos];
DWORD offset=(DWORD)writebuff-(DWORD)hMod+pos;
//get name and address of the target function
char*functionname=(char*)((BYTE*) hMod +addressofnames[x-1]);
DWORD functionaddress=(DWORD)((BYTE*) hMod +addressoffunctions[x-1]);
// load virtual memory with machine instructions and relocation information
DWORD addr=(DWORD)&writebuff[pos+6];
currentchunk[0]=0xFF;currentchunk[1]=0x15;
memmove(currenttchunk[2],&addr,4);
RelocatedFunction * reloc=(RelocatedFunction*)currenttchunk[6];
reloc->funcname= functionname;
reloc->funcptr=functionaddress;
reloc->proxyptr=(DWORD)&ProxyProlog;
// overwrite export address table
DWORD byteswritten;
WriteProcessMemory(GetCurrentProcess(),&addressoffunctions[x-1],
?&offset,4,&byteswritten);
}
使用overwrite函數(shù),通過ToolHelp32把所有已經(jīng)加載的模塊文件的輸入表全部進行替換。
void overwrite(HMODULE hMod)
{
IMAGE_DOS_HEADER * dosheader=(IMAGE_DOS_HEADER *)hMod;
IMAGE_OPTIONAL_HEADER * opthdr =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hMod+dosheader->e_lfanew+24);
IMAGE_IMPORT_DESCRIPTOR *descriptor= (IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)dosheader+opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
HANDLE hand=GetCurrentProcess();
HMODULE ker=GetModuleHandle("kernel32.dll");
while(descriptor->FirstThunk)
{
???? char*dllname=(char*)((BYTE*)hMod+descriptor->Name);
???? if(lstrcmp(dllname,"KERNEL32.dll")){descriptor++;continue;}
???? IMAGE_THUNK_DATA* thunk=(IMAGE_THUNK_DATA*)((BYTE*)dosheader+descriptor->OriginalFirstThunk);
???? int x=0;
???? while(thunk->u1.Function)
???? {
?????? char*functionname=(char*)((BYTE*)dosheader + (unsigned)thunk->u1.AddressOfData+2);
?????? DWORD*IATentryaddress=(DWORD*)((BYTE*)dosheader+descriptor->FirstThunk)+x;
?????? DWORD addr=(DWORD)GetProcAddress(ker,functionname);
?????? DWORD byteswritten;???
?????? WriteProcessMemory(hand,IATentryaddress, &addr,4,&byteswritten);
?????? x++;thunk++;
????? }
????? descriptor++;
}
CloseHandle(hand);
}
HANDLE snap=
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());
MODULEENTRY32 mod;mod.dwSize=sizeof(MODULEENTRY32);
Module32First(snap,&mod);
HMODULE first=mod.hModule;
overwrite(first);
while(Module32Next(snap,&mod))
{
??? HMODULE next=mod.hModule;
??? if(next==currenthandle)continue;
??? overwrite(next);
}
在目標進程注射掛鉤DLL
在這里不能使用Jeffrey Richter提出的用CreateRemoteThread()的方法。因為我們把被掛鉤函數(shù)的地址保存在線程本地存貯器上,如果要目標進程能夠正確地進行掛鉤,就需要在該進程的每一個線程中動態(tài)的分配內(nèi)存來作為線程本地存貯器,比如掛鉤DLL的DllMain()函數(shù)被目標進程的每一個線程調用。這就要求目標進程在掛鉤DLL被裝載以后首先執(zhí)行其DllMain()函數(shù),然后創(chuàng)建目標進程的其他線程。另一方面,使用CreateRemoteThread()的方法注入DLL文件,目標進程的所有線程都是在掛鉤DLL注入前就已經(jīng)創(chuàng)建了。因此不能使用CreateRemoteThread()方法。
有兩種方法能夠完成對掛鉤DLL的注射,相比較而言第一種方法要相對容易一些。
1、把掛鉤DLL文件注射到目標進程的主線程,而且必須在目標進程創(chuàng)建其他線程之前注入,比如在目標進程創(chuàng)建時盡可能早的階段。
2、讓目標進程已經(jīng)運行的線程調用掛鉤DLL文件的入口點函數(shù)。
在用戶創(chuàng)建的目標進程中注射掛鉤進程
void install(char* filename)
{
// 獲取創(chuàng)建目標進程的入口點
DWORD bytes;char buff[4096];
HANDLE file=CreateFile(filename ,
GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
ReadFile(file,buff,1024,&bytes,0);
CloseHandle(file);
IMAGE_DOS_HEADER * dosheader=(IMAGE_DOS_HEADER *)buff;
IMAGE_OPTIONAL_HEADER *optionalheader=(IMAGE_OPTIONAL_HEADER
*)((BYTE*)buff+dosheader->e_lfanew+24);
DWORD entryptr=optionalheader->AddressOfEntryPoint+optionalheader->ImageBase;
// 創(chuàng)建目標進程,并把它掛起(Suspend)
STARTUPINFO startup;GetStartupInfo(&startup);PROCESS_INFORMATION procinfo;
CreateProcess(filename,0,0,0,TRUE,CREATE_SUSPENDED,0,0,&startup,&procinfo);
// 在目標進程上分配內(nèi)存
BYTE* writebuff=(BYTE*)VirtualAllocEx(procinfo.hProcess,0,4096,MEM_RESERVE,PAGE_EXECUTE_READWRITE);
writebuff=(BYTE*)VirtualAllocEx(procinfo.hProcess,writebuff,4096,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
// 獲取LoadLibraryA的地址
DWORD function=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA");
// 構造需要運行的機器指令
// push pointer_to_dllname
// push address_of_entrypoint
// jmp dword ptr[_imp_LoadLibraryA]
DWORD stringptr=(DWORD)&writebuff[20];strcpy(&buff[20],"spydll.dll");
DWORD funcptr=(DWORD)&writebuff[16];memmove(&buff[16],&function,4);
buff[0]=0x68;
memmove(&buff[1],&stringptr,4);
buff[5]=0x68;
memmove(&buff[6],&entryptr,4);
buff[10]=0xFF;buff[11]=0x25;
memmove(&buff[12],&funcptr,4);
// 把構造的機器指令拷貝到目標進程上
WriteProcessMemory(procinfo.hProcess,writebuff,buff,4096,&bytes);
// 改變目標進程的執(zhí)行上下文中的EIP,去執(zhí)行寫入的機器指令
CONTEXT Context;Context.ContextFlags=CONTEXT_CONTROL;
GetThreadContext(procinfo.hThread,&Context);
Context.Eip=(DWORD)writebuff;
SetThreadContext(procinfo.hThread,&Context);
ResumeThread(procinfo.hThread);
}
總結
以上是生活随笔為你收集整理的Process-wide API spying - an ultimate hack 摘要翻译(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 创建android studio桌面图标
- 下一篇: matlab 发布商,MathWork发