本文共 5833 字,大约阅读时间需要 19 分钟。
最近因为工作需要,开始研究函数打桩的方法。由于不想对工程做过多的修改,于是放弃了使用Google gmock的想法。但是也足足困扰另外我一天一宿。经过奋战,终于有所收获。闲话少说,开始看看有什么方法。 一、基础准备
1. 函数调用的原理:通过函数名(函数的入口地址)对函数进行访问,假设我们能够改变函数首地址指向的内存的话,使其跳转到另一个函数去执行的话,那么就可以实现函数打桩了。 2. 方法:对函数首地址出写入一条汇编语言 jmp xxx (其中xxx是要跳转的相对地址)。 3. 令原函数为oldFun,新函数为newFun,那么打桩时函数跳转的相对地址 offset = newFun - oldFun - (我们制定的这条指令的大小),此处为绝对跳转指令的长度=5。 jmp xxx一共6字节。 1. VirtualQuery
- WINBASEAPI
- SIZE_T
- WINAPI
- VirtualQuery(
- __in_opt LPCVOID lpAddress,
- __out_bcount_part(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,
- __in SIZE_T dwLength
- );
该函数用于查询
某一段内存区域的内存信息,事实VirtualQueryEx也可以使用。 2. VirtualProtect
- WINBASEAPI
- BOOL
- WINAPI
- VirtualProtect(
- __in LPVOID lpAddress,
- __in SIZE_T dwSize,
- __in DWORD flNewProtect,
- __out PDWORD lpflOldProtect
- );
该函数用于修改指定内存区dwSize个字节的保护模式。
3. VirtualProtectEx
- WINBASEAPI
- BOOL
- WINAPI
- VirtualProtectEx(
- __in HANDLE hProcess,
- __in LPVOID lpAddress,
- __in SIZE_T dwSize,
- __in DWORD flNewProtect,
- __out PDWORD lpflOldProtect
- );
VirtualProtectEx 用于改变指定进程内存段的保护模式,默认情况下函数的内存空间不可写,这就是为什么要用改变保护属性的函数。
4. ReadProcessMemory
- WINBASEAPI
- BOOL
- WINAPI
- ReadProcessMemory(
- __in HANDLE hProcess,
- __in LPCVOID lpBaseAddress,
- __out_bcount_part(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,
- __in SIZE_T nSize,
- __out_opt SIZE_T * lpNumberOfBytesRead
- );
读取进程内存,lpProcess是首地址,而lpBuffer用于保存读出的数据,nSize是需要读出的字节数。
5. WriteProcessMemory
- WINBASEAPI
- BOOL
- WINAPI
- WriteProcessMemory(
- __in HANDLE hProcess,
- __in LPVOID lpBaseAddress,
- __in_bcount(nSize) LPCVOID lpBuffer,
- __in SIZE_T nSize,
- __out_opt SIZE_T * lpNumberOfBytesWritten
- );
该函数用于写进程的内存空间,可以向进程内存注入想要注入的数据,例如函数等。
6. GetCurrentProcess
- WINBASEAPI
- __out
- HANDLE
- WINAPI
- GetCurrentProcess(
- VOID
- );
该函数返回一个伪进程句柄0xffffffff,任何需要进程句柄的内存都可以使用它。
二、对库中API打桩
方案一:
- #define FLATJMPCODE_LENGTH 5 //x86 平坦内存模式下,绝对跳转指令长度
- #define FLATJMPCMD_LENGTH 1 //机械码0xe9长度
- #define FLATJMPCMD 0xe9 //对应汇编的jmp指令
-
-
- BYTE g_apiBackup[FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH];
-
- BOOL setStub(LPVOID ApiFun,LPVOID HookFun)
- {
- BOOL IsSuccess = FALSE;
- DWORD TempProtectVar;
- MEMORY_BASIC_INFORMATION MemInfo;
-
- VirtualQuery(ApiFun,&MemInfo,sizeof(MEMORY_BASIC_INFORMATION));
-
- if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- PAGE_READWRITE,&MemInfo.Protect))
- {
- memcpy((void*)g_apiBackup,(const void*)ApiFun, sizeof(g_apiBackup));
-
- *(BYTE*)ApiFun = FLATJMPCMD;
- *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
- (DWORD)ApiFun - FLATJMPCODE_LENGTH;
-
- VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- MemInfo.Protect,&TempProtectVar);
-
- IsSuccess = TRUE;
- }
-
- return IsSuccess;
- }
清桩: - BOOL clearStub(LPVOID ApiFun)
- {
- BOOL IsSuccess = FALSE;
- DWORD TempProtectVar;
- MEMORY_BASIC_INFORMATION MemInfo;
-
- VirtualQuery(ApiFun,&MemInfo,sizeof(MEMORY_BASIC_INFORMATION));
-
- if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- PAGE_READWRITE,&MemInfo.Protect))
- {
- memcpy((void*)ApiFun, (const void*)g_apiBackup, sizeof(g_apiBackup));
-
- VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- MemInfo.Protect,&TempProtectVar);
-
- IsSuccess = TRUE;
- }
-
- return IsSuccess;
- }
方案二:
- bool setStub(LPVOID ApiFun,LPVOID HookFun)
- {
- HANDLE file_handler = GetCurrentProcess();
- DWORD oldProtect,TempProtectVar;
- char newCode[6];
- int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;
- if(!VirtualProtectEx(file_handler,ApiFun,SIZE,PAGE_READWRITE,&oldProtect))
- {
- return false;
- }
- if(!ReadProcessMemory(file_handler,ApiFun,newCode,SIZE,NULL))
- {
- return false;
- }
- memcpy((void*)g_apiBackup,(const void*)newCode, sizeof(g_apiBackup));
- *(BYTE*)ApiFun = FLATJMPCMD;
- *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun - (DWORD)ApiFun - FLATJMPCODE_LENGTH;
- VirtualProtectEx(file_handler,ApiFun,SIZE,oldProtect,&TempProtectVar);
- }
清桩: - bool clearStub(LPVOID ApiFun)
- {
- BOOL IsSuccess = FALSE;
- HANDLE file_handler = GetCurrentProcess();
- DWORD oldProtect,TempProtectVar;
- int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;
- if(VirtualProtectEx(file_handler,ApiFun,SIZE,PAGE_READWRITE,&oldProtect))
- {
- memcpy((void*)ApiFun, (const void*)g_apiBackup, sizeof(g_apiBackup));
- VirtualProtectEx(file_handler,ApiFun,SIZE,oldProtect,&TempProtectVar);
- IsSuccess = TRUE;
- }
-
- return IsSuccess;
- }
方案三:
- bool setStub(LPVOID ApiFun,LPVOID HookFun)
- {
- HANDLE file_handler = GetCurrentProcess();
- DWORD oldProtect,TempProtectVar;
- char newCode[6];
- int SIZE = FLATJMPCODE_LENGTH+FLATJMPCMD_LENGTH;
- if(!ReadProcessMemory(file_handler,ApiFun,newCode,SIZE,NULL))
- {
- return false;
- }
- memcpy((void*)g_apiBackup,(const void*)newCode, sizeof(g_apiBackup));
- *(BYTE*)newCode = FLATJMPCMD;
- *(DWORD*)((BYTE*)newCode + FLATJMPCMD_LENGTH) = (DWORD)HookFun - (DWORD)ApiFun - FLATJMPCODE_LENGTH;
- if(!WriteProcessMemory(file_handler,ApiFun,newCode,FLATJMPCODE_LENGTH,NULL))
- {
- return false;
- }
- }
说来也怪,这个方案没有改变读取权限,居然也可以,这里写入的方式是用WriteProcessMemory来实现,与直接用指针同理。清桩同上。但是如果直接用指针来写就会出错,暂时不知道原因。
至此我们实现了函数的打桩,但是有个小小的问题,若仅仅是如此,对类函数中成员函数打桩有点小问题,指针无法转换,这是因为类成员函数的指针不仅仅是一个普通的指针,他还包括其他信息。所有这里需要解决这个问题,网上找到了两个方法: 1. 类的普通函数成员地址转换
- LPVOID GetClassFnAddress(...)
- {
- LPVOID FnAddress;
- __asm
- {
- lea eax,FnAddress
- mov edx,[ebp+8]
- mov [eax],edx
- }
- return FnAddress;
- }
2. 类的虚成员函数地址转换
- LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index)
- {
- LPVOID FnAddress;
- *(int*)&FnAddress = *(int*)pthis;
- *(int*)&FnAddress = *(int*)((int*)FnAddress + Index);
- return FnAddress;
- }
至此函数打桩的介绍告一段落。
3. 普通成员函数转换
- <pre name="code" class="cpp">template<class T>
- void * getAddr(T f)
- {
- long addr;
- memcpy(&addr,&f,sizeof(T));
- return (int*)addr;
- }
资料:(很有参考价值)
转载地址:http://cehab.baihongyu.com/