我有另一个进程启动的应用程序,该程序加载一些dll(具有很多导出的函数)并在启动后的短时间内调用它们。由于我们不能在此处使用传统的API挂钩(通过替换指向函数的指针),并且我不会使用蹦床(因为它需要反汇编程序来获取指令长度),因此我已经编写了代码以修补dll文件,因此所有导出的函数都将以“无限跳转”操作码开头:{0xEB,0xFE}。
操作顺序为:
以下是“跳过”功能的代码:
extern "C" void asm_proc();
#define PATCH_SIZE 2
UCHAR g_Patch[PATCH_SIZE] = { 0xEB, 0xFE };
void SkipFunction(FUNCTION_ITEM *func, HANDLE hProcess, HANDLE hThread)
{
CONTEXT lcContext;
UCHAR *ReturnAddress;
UCHAR *TargetAddress;
UCHAR *BlockAddress;
DWORD ProcSize;
UCHAR Ret;
Ret = 0xC3;
ProcSize = 14;
lcContext.ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(hThread, &lcContext)) DbgRaiseAssertionFailure();
BlockAddress = (UCHAR*)VirtualAllocEx(hProcess, NULL, sizeof(ReturnAddress) + ProcSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
TargetAddress = BlockAddress + sizeof(ReturnAddress);
ReadProcessMemoryEx(hProcess, lcContext.Rsp, (UCHAR*)&ReturnAddress, sizeof(ReturnAddress));
WriteProcessMemoryEx(hProcess, lcContext.Rsp, (UCHAR*)&TargetAddress, sizeof(TargetAddress));
WriteProcessMemoryEx(hProcess, (DWORD64)BlockAddress, (UCHAR*)&ReturnAddress, sizeof(ReturnAddress));
// disable incremental linking to make it work
WriteProcessMemoryEx(hProcess, (DWORD64)TargetAddress, (UCHAR*)asm_proc, ProcSize);
WriteProcessMemoryEx(hProcess, func->Rip, func->OriginalBytes, PATCH_SIZE);
while (TRUE)
{
Sleep(100);
lcContext.ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(hThread, &lcContext)) DbgRaiseAssertionFailure();
if (lcContext.Rip == (DWORD64)(TargetAddress + 12)) break;
}
WriteProcessMemoryEx(hProcess, func->Rip, g_Patch, PATCH_SIZE);
WriteProcessMemoryEx(hProcess, (DWORD64)(TargetAddress + 12), &Ret, sizeof(Ret));
VirtualFreeEx(hProcess, BlockAddress, 0, MEM_RELEASE);
}
和 asm_proc 功能代码:
PUBLIC asm_proc
.code
asm_proc PROC
call $+5
pop rcx
sub rcx, 13
push qword ptr [rcx]
jmp $
asm_proc ENDP
END
func-> Rip 保留修补功能的IP地址 func-> OriginalBytes 保留原始函数字节,因为它们出现在原始dll文件中
基本上,我们使用VirtualAllocEx分配内存,复制我们的代码和原始返回地址,将返回地址替换为堆栈上的内容,然后进入无限循环,因此我们可以再次对函数进行修补。这种方法应该适用于单线程挂钩(我不需要处理多个线程)。
但是,有时,当我们“跳过”功能时,应用程序只是崩溃了,我不明白为什么会这样。您能说些什么?怎么了谢谢。