PVOID HookFunction(LPTSTR ModuleName, LPCSTR FunctionName, PVOID MyFunction) { PVOID oldFunction = NULL; PVOID proxyFunction = NULL; LPBYTE opCode = NULL; DWORD backupLen = 0; DWORD oldProtect = 0; TCHAR tzTemp[MAX_PATH] = {0}; // Get original function address oldFunction = GetProcAddress(GetModuleHandle(ModuleName), FunctionName); if (!oldFunction) { wsprintf(tzTemp, TEXT("Failed to find the function: %hs\n"), FunctionName); OutputDebugText(tzTemp); return NULL; } // Get the exact length while (backupLen < JumpCodeSize) backupLen += size_of_code((LPBYTE)((DWORD)oldFunction + backupLen), &opCode); // Fill the data *(DWORD *)(JumpCode + 1) = (DWORD)MyFunction; *(DWORD *)(JumpbackCode + 1) = (DWORD)oldFunction + backupLen; // Allocate space for proxy function proxyFunction = VirtualAlloc(NULL, backupLen + JumpCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!proxyFunction) { wsprintf(tzTemp, TEXT("Failed to allocate space for the function: %hs\n"), FunctionName); OutputDebugText(tzTemp); return NULL; } // Fill proxy function and flush instructions RtlCopyMemory(proxyFunction, oldFunction, backupLen); RtlCopyMemory((PVOID)((DWORD)proxyFunction + backupLen), JumpbackCode, JumpbackCodeSize); FlushInstructionCache(GetModuleHandle(NULL), proxyFunction, backupLen + JumpCodeSize); // Modify original function VirtualProtect(oldFunction, JumpCodeSize, PAGE_EXECUTE_READWRITE, &oldProtect); RtlCopyMemory(oldFunction, JumpCode, JumpCodeSize); VirtualProtect(oldFunction, JumpCodeSize, oldProtect, &oldProtect); FlushInstructionCache(GetModuleHandle(NULL), oldFunction, JumpCodeSize); return proxyFunction; }
ULONG SpliceFunctionStart( IN PVOID OriginalAddress, IN PVOID HookFunction, OUT PVOID SplicingBuffer, IN ULONG MaxLength, OUT PVOID BackupBuffer, OUT PULONG BytesWritten, IN BOOLEAN WorkAtCurrentIrql ) { ULONG cr0; KIRQL Irql; ULONG Len; ULONG Ptr, NextAddress; NTSTATUS Status = STATUS_UNSUCCESSFUL; // ULONG CapturedBytesWritten; KdPrint(("Entering SpliceFunctionStart( 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x )\n", OriginalAddress, HookFunction, SplicingBuffer, sizeof(MaxLength), BytesWritten)); KdPrint(("Saving state\n")); // Save cr0 = IntDisableWP( ); // Disable Write-Protection on system pages if( !WorkAtCurrentIrql ) KeRaiseIrql( HIGH_LEVEL, &Irql ); // Raise IRQL KdPrint(("cr0=0x%08x\n", cr0)); // // Copy integer number of instructions to the buffer // KdPrint(("Copying instructions from OriginalAddress=%08x\n", OriginalAddress)); *BytesWritten = 0; for( Ptr = (ULONG)OriginalAddress; Ptr < ((ULONG)OriginalAddress+5); Ptr+=Len ) { Len = size_of_code( (UCHAR*)Ptr ); KdPrint(("Command decoded at address 0x%08x, length 0x%08x\n", Ptr, Len)); KdPrint(("Ptr=%08x,OriginalAddress=%08x, MaxLength=%08x, Ptr-OriginalAddress+5=%08x\n", Ptr, OriginalAddress, MaxLength, (Ptr-(ULONG)OriginalAddress+5))); if( Ptr < ((ULONG)OriginalAddress+5) ) { if( (Ptr-(ULONG)OriginalAddress+5) >= MaxLength ) { KdPrint(("Error: buffer is too small\n")); Status = STATUS_INFO_LENGTH_MISMATCH; goto _exit; } memcpy( (PVOID)((ULONG)SplicingBuffer+(Ptr-(ULONG)OriginalAddress)), (PVOID)Ptr, Len ); *BytesWritten += Len; } } NextAddress = Ptr; KdPrint(("*BytesWritten = 0x%08x, Ptr = 0x%08x, NextAddress = 0x%08x\n", *BytesWritten, Ptr, NextAddress)); /* // Fixup buffer commands KdPrint(("Fixing up instructions\n")); CapturedBytesWritten = *BytesWritten; for( Ptr = (ULONG)SplicingBuffer; Ptr < ((ULONG)SplicingBuffer+CapturedBytesWritten); Ptr+=Len ) { BOOLEAN FixupPrevious; ULONG PreviousFixupOffset, PreviousPreviousFixupOffset; Len = size_of_code( (BYTE*)Ptr ); FixupPrevious = DcpFixupCommand( (PVOID)Ptr, (ULONG)OriginalAddress+Ptr-(ULONG)SplicingBuffer, SplicingBuffer, FALSE, 0, 0, BytesWritten, &Len, &PreviousFixupOffset ); if( FixupPrevious ) { // Fixup all previous commands ULONG TempPtr, TempLen; for( TempPtr = (ULONG)SplicingBuffer; TempPtr < Ptr; TempPtr+=TempLen ) { TempLen = size_of_code( (BYTE*)TempPtr ); if( DcpFixupCommand( (PVOID)TempPtr, (ULONG)OriginalAddress+TempPtr-(ULONG)SplicingBuffer, SplicingBuffer, TRUE, PreviousFixupOffset, Ptr, BytesWritten, &TempLen, &PreviousPreviousFixupOffset) ) { KdPrint(("Nested fixing up is not supported (nested depth >2, PreviousPreviousFixupOffset=%d)\n", PreviousPreviousFixupOffset)); // Hard case. Break Status = STATUS_INVALID_PARAMETER; goto _exit; } KdPrint((" -> Temp command fixed up at address 0x%08x, length 0x%08x\n", TempPtr, TempLen)); } KdPrint((" -> Finished fixing up previous commands\n")); } KdPrint(("Command fixed up at address 0x%08x, length 0x%08x\n", Ptr, Len)); } */ KdPrint(("Generating splicing buffer\n")); // // Emit splicing jump to the buffer // memcpy( BackupBuffer, OriginalAddress, 5 ); EmitJumpCommand( OriginalAddress, HookFunction ); KdPrint(("Original address bytes: %02x %02x %02x %02x %02x\n", ((PUCHAR)OriginalAddress)[0], ((PUCHAR)OriginalAddress)[1], ((PUCHAR)OriginalAddress)[2], ((PUCHAR)OriginalAddress)[3], ((PUCHAR)OriginalAddress)[4] )); // // Emit continuation jump to the function continuation // Ptr = ((ULONG)SplicingBuffer+*BytesWritten); EmitJumpCommand( Ptr, NextAddress ); Status = STATUS_SUCCESS; _exit: // Restore KdPrint(("Irql = %x, cr0 = %x\n", Irql, cr0)); if( !WorkAtCurrentIrql ) KeLowerIrql( Irql ); // Lower IRQL IntRestoreWP( cr0 ); // Restore Write-Protection bit return Status; }