BOOL engine_UnHookFunctionInProcess(HANDLE hProcess,LPSTR lpModuleName,LPSTR lpFunctionName,LPVOID lpOldFunctionAddress,DWORD dwFunctionSize) { LPVOID lpModule=NULL; LPVOID lpFunction=NULL; MEMORY_BASIC_INFORMATION mbi; CHAR lpLocalStub[MAX_FUNC_LEN*2]; DWORD dwFree=0; DWORD dwBytesWritten; // Get module address lpModule=(LPVOID)engine_GetRemoteModuleHandle(hProcess,lpModuleName); if (!lpModule) return FALSE; // Get function address lpFunction=engine_GetRemoteProcAddress(hProcess,lpModule,lpFunctionName); if (!lpFunction) return FALSE; // Get info about the function address if (!NT_SUCCESS(SafeNtQueryVirtualMemory(hProcess,lpFunction,MemoryBasicInformation,&mbi,sizeof(mbi),NULL))) return FALSE; // Flush instruction cache NtFlushInstructionCache(hProcess,mbi.BaseAddress,mbi.RegionSize); // Change the protection for the region if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect))) return FALSE; // Read old functions instructions if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpOldFunctionAddress,lpLocalStub,dwFunctionSize,NULL))) { // restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return FALSE; } // Restore original function if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpFunction,lpLocalStub,dwFunctionSize,&dwBytesWritten))) { // restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return FALSE; } // Free stub memory NtFreeVirtualMemory(hProcess,&lpOldFunctionAddress,&dwFree,MEM_RELEASE); // Restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return TRUE; }
/* * @implemented */ BOOL WINAPI FlushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize) { NTSTATUS Status; Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, dwSize); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(Status); return FALSE; } return TRUE; }
LPVOID engine_HookFunctionInProcess(HANDLE hProcess,LPSTR lpModuleName,LPSTR lpFunctionName,LPVOID lpHookFunctionAddress,PDWORD pdwHookFunctionSize,LPVOID* lpFunctionAddress,INT iRndJmp) { LPVOID lpModule=NULL; LPVOID lpFunction=NULL; MEMORY_BASIC_INFORMATION mbi; CHAR lpTmpFunction[MAX_FUNC_LEN*2]; CHAR lpLocalStub[MAX_FUNC_LEN*3]; CHAR lpLocalFunc[MAX_FUNC_LEN*3]; DWORD dwBytesRead; DWORD dwReadLen=0; DWORD dwExistingJMP=0; DWORD dwStubSize; DWORD dwFree=0; DWORD dwBytesWritten; DWORD dwOldProtect; LPVOID lpRemoteStub=NULL; INT iFuncLen; PBYTE pReadAddress; NTSTATUS ntStatus; // Get module address lpModule=(LPVOID)engine_GetRemoteModuleHandle(hProcess,lpModuleName); if (!lpModule) return NULL; // Get function address lpFunction=engine_GetRemoteProcAddress(hProcess,lpModule,lpFunctionName); if (!lpFunction) return NULL; // Get info about the function address if (!NT_SUCCESS(SafeNtQueryVirtualMemory(hProcess,lpFunction,MemoryBasicInformation,&mbi,sizeof(mbi),NULL))) return NULL; // Flush instruction cache NtFlushInstructionCache(hProcess,mbi.BaseAddress,mbi.RegionSize); // Change the protection for the region if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect))) return NULL; // Fill stub buffer with nops RtlFillMemory(lpLocalStub,MAX_FUNC_LEN*3,NOP); // Read MAX_FUNC_LEN instruction(s) from the function into our function buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpTmpFunction,MAX_FUNC_LEN*2,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } pReadAddress=(PBYTE)lpTmpFunction; // check if first opcode in the function is a another jump if (*pReadAddress==LONG_JMP_OPCODE) { // get relative address memcpy(&dwExistingJMP,pReadAddress+1,4); // get absolute address dwExistingJMP=(DWORD)lpFunction+dwExistingJMP; // readlen dwReadLen=jtJmpTable[RELATIVE_JMP].iCodeSize engine_BuildJMPBuffer((CHAR*)lpLocalStub,((DWORD)lpRemoteStub+dwReadLen)-dwExistingJMP,RELATIVE_JMP); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Get the length of the first instruction(s) // This part is done by Z0MBiE's LDE32 v1.05 iFuncLen=disasm_main(pReadAddress); // get first instruction length while (iFuncLen!=-1 && dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize) { dwReadLen+=iFuncLen; pReadAddress+=iFuncLen; iFuncLen=disasm_main(pReadAddress); // next instruction length } // API code is too short or too long too hook this way (for now ;)) if (dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize||dwReadLen>MAX_FUNC_LEN*2) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Read the first instruction(s) from the function into our stub buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpLocalStub,dwReadLen,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Allocate space with read/write access for our "stub" // note: always use a relative jump for our stub -> RELATIVE_JMP dwStubSize=dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize; if (!NT_SUCCESS(NtAllocateVirtualMemory(hProcess,&lpRemoteStub,0,&dwStubSize,MEM_COMMIT|MEM_TOP_DOWN,PAGE_READWRITE))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Check if (dwStubSize<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } engine_BuildJMPBuffer((CHAR*)lpLocalStub+dwReadLen,jtJmpTable[RELATIVE_JMP].jcStub((DWORD)lpFunction+dwReadLen,(DWORD)lpRemoteStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize),RELATIVE_JMP); // Copy the "stub" buffer to process memory if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpRemoteStub,lpLocalStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize,&dwBytesWritten))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Check if (dwBytesWritten<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // change access if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&lpRemoteStub,&dwStubSize,PAGE_EXECUTE_READ,&dwOldProtect))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Fill it with NOP RtlFillMemory(lpLocalFunc,MAX_FUNC_LEN*3,NOP); // Prepare jmpcode engine_BuildJMPBuffer((CHAR*)lpLocalFunc,jtJmpTable[iRndJmp].jcFunc((DWORD)lpHookFunctionAddress,(DWORD)lpFunction,(DWORD)jtJmpTable[iRndJmp].iCodeSize),iRndJmp); ntStatus=NtWriteVirtualMemory(hProcess,lpFunction,lpLocalFunc,dwReadLen,&dwBytesWritten); // Check that we really wrote our jmpcode completely if (!NT_SUCCESS(ntStatus) || dwBytesWritten!=dwReadLen) { // Try to fix stuff if (dwBytesWritten) NtWriteVirtualMemory(hProcess,lpFunction,lpRemoteStub,dwBytesWritten,&dwBytesWritten); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Save size of read function length if (pdwHookFunctionSize) *pdwHookFunctionSize=dwReadLen; // Save address of function if (lpFunctionAddress) *lpFunctionAddress=lpFunction; return lpRemoteStub; }