windows 下实现函数打桩:拦截API方式

近期由于工作须要,開始研究函数打桩的方法。

由于不想对project做过多的改动,于是放弃了使用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,   //保存内存区域的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,FnAddressmov 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;                                       //pthis 是对象的指针,index是在虚函数表中的偏移量*(int*)&FnAddress = *(int*)pthis;                       //lpvtable    *(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://www.cnblogs.com/HeroZearin/articles/2772100.html

本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5360668.html,如需转载请自行联系原作者

windows 下实现函数打桩:拦截API方式相关推荐

  1. Windows下进程通信的几种方式介绍

    Windows下进程通信的几种方式 一.消息通信 所谓消息通信,就是指Windows发出的一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键都会使Windows发送 ...

  2. Windows下打开命令窗口几种方式

    Windows下打开命令窗口几种方式 1.点击开始 --> Windows系统 -->点击击命令提示符 ,就可以运行 注意:如果以管理员身份运行,就选中命令提示符右击更多,点击以管理员身份 ...

  3. Windows下的函数

    在进行Windows应用程序设计中,程序员除了需要知道有关一个函数的常用信息(例如函数的名字,近函数或远函数,返回类型以及应如何调用)之外,同时还要知道更多的内容:一个回调函数.引出函数或是一个引入函 ...

  4. Windows 下创建目录链接 映射文件的方式不想改变路径直接使用映射路径的方式

    Windows 下创建目录链接 发表于 2019-10-27 | 分类于 后端 | 没有评论 在 Windows 下如果通过右键菜单->创建快捷方式生成的文件或文件夹,其实是生成了一个后辍为.l ...

  5. windows下延时函数

    VC中提供了很多关于时间操作的函数,编写程序时我们可以跟据定时的不同精度要求选择不同的时间函数来完成定时和计时操作. 方式一:VC中的WM_TIMER消息映射能进行简单的时间控制.首先调用函数SetT ...

  6. Windows下system()函数详解

    Start~~ 在C++/C的Windows环境中,对于控制台窗口的处理,有一个函数必须知道: system(" "); 是的,没错.它可以调动Cmd控制台中的命令,来方便程序员的 ...

  7. windows下system函数的使用

    system函数 是可以调用一些DOS命令,比如 system("cls");//清屏,等于在DOS上使用cls命令 写可执行文件路径,可以运行它···· 下面列出常用的DOS命令 ...

  8. windows下三种web服务器搭建方式

    一.第一种方法(live-server) 1.安装方法: Npm安装: npm install -g live-server(前提是先安装node.js) 然后在所在项目目录下,打开命令行工具,输入 ...

  9. windows下创建目录函数_mkdir

    创建目录函数_mkdir(path),返回0表示成功,-1失败 只能创建一级目录,即path倒数第二级必须是已经存在,否则创建不成功. 如果需要创建多级目录,如下: char szPath[256] ...

最新文章

  1. 2D池化IPoolingLayer
  2. 界面怎么使用pip_从零开始学Python——10Pyinstaller的使用
  3. 第一次scrapy爬虫记录
  4. 如何给DPM服务器附加磁盘?
  5. linux编程:getenv,putenv,setenv
  6. vue2.0中的$router 和 $route的区别
  7. this到底指向哪里
  8. 2440 linux文件写,添加yaffs2文件系统 - Linux2.6.39在S3C2440上的移植_Linux编程_Linux公社-Linux系统门户网站...
  9. gsm模块 java 录音_Android GSM驱动模块详细分析
  10. c语言内循环外循环怎么使用,开高速, 用内循环还是外循环? 教你正确使用内外循环!...
  11. linux 邮件服务器pop3,linux下用pop3收web电子邮箱_邮件服务器
  12. java工程师可能需要的视频
  13. 读书笔记 : 人类简史
  14. 阿里云云解析DNS怎么样?云解析DNS有什么优势?
  15. Arduino与Proteus仿真实例-74HC573锁存器驱动仿真
  16. 关于阻容耦合电路及阻容耦合分压
  17. 将m3u8文件转MP4、TS、flv、mkv等文件
  18. 《图说VR入门》——googleVR 他山之玉
  19. HTML 计算奖金小程序
  20. Ext JS框架入门

热门文章

  1. Win8兼容ARM十大意义:打破垄断拉低价格
  2. 【CocoaPods】CocoaPods:Objective-C依赖库管理(XCode 4.6)
  3. 如何解决经常出现Windows数据执行保护的提示?
  4. hadoop搭建在Ubuntu16.04上
  5. 【the service mysql57 failed the most】
  6. 自己制作 SPx N合1 自动安装盘(x86)
  7. C#中一些易混知识的比较
  8. java+flash在线拍照和编辑,保存到服务器(Spring3.2.2+swf+jquery)
  9. VMware虚拟产品简介
  10. linux 文件与目录管理命令