windows 下实现函数打桩:拦截API方式
2020-12-13 04:53
标签:c++函数打桩windows api拦截 dll内存注入
windows 下实现函数打桩:拦截API方式 标签:c++函数打桩windows api拦截 dll内存注入 原文地址:http://blog.csdn.net/liaoyoujinb/article/details/37953755windows 下实现函数打桩:拦截API方式
一、基础准备
1. VirtualQuery
WINBASEAPI
SIZE_T
WINAPI
VirtualQuery(
__in_opt LPCVOID lpAddress, //所查内存地址
__out_bcount_part(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer, //保存内存区域的buffer
__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; //拦截API,在函数代码段前面注入jmp xxx
*(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] // ebp+8 为第一个形参的地址,ebp+C 为第二个形参的地址,以此类推
mov [eax],edx
}
return FnAddress;
}
2. 类的虚成员函数地址转换
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index) //Add 2010.8.6
{
LPVOID FnAddress;
*(int*)&FnAddress = *(int*)pthis; //lpvtable
*(int*)&FnAddress = *(int*)((int*)FnAddress + Index);
return FnAddress;
}