一个简单的桩实现类:

#define JMPCODE_LENGTH 5            //x86 平坦内存模式下,绝对跳转指令长度
#define JMPCMD_LENGTH 1 //机械码0xe9长度
#define JMPCMD 0xe9 //对应汇编的jmp指令 // 一个简化的打桩类的实现
class XSimpleStub
{
public:
explicit XSimpleStub(void* pOrigFunc, void* pNewFunc, bool need_lock_other_thread = false);
~XSimpleStub(); private:
// 源函数地址
void * str_func_addr;
// 是否打桩成功
bool is_stub_succ;
// 是否打桩成功
bool need_lock_other_thread_;
// 源指令数据的备份
unsigned char str_instruct_back[JMPCODE_LENGTH];
};

函数就只有两个函数体,分别如下

#include <tlhelp32.h>

BOOL LockOtherThread()
{
DWORD dwCurrPid = GetCurrentProcessId();
DWORD dwCurrTid = GetCurrentThreadId(); HANDLE hThread = NULL;
HANDLE hThreadSnap = NULL;
THREADENTRY32 te32 = { };
te32.dwSize = sizeof(THREADENTRY32); // 遍历线程
if (Thread32First(hThreadSnap, &te32))
{
do
{
if (te32.th32OwnerProcessID == dwCurrPid) {
if (te32.th32ThreadID != dwCurrTid){
// 获取句柄
hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
if (NULL != hThread){
SuspendThread(hThread);
}
CloseHandle(hThread);
}
}
} while (Thread32Next(hThreadSnap, &te32));
}
CloseHandle(hThreadSnap); return TRUE;
} BOOL UnlockOtherThread()
{
DWORD dwCurrPid = GetCurrentProcessId();
DWORD dwCurrTid = GetCurrentThreadId(); HANDLE hThread = NULL;
HANDLE hThreadSnap = NULL;
THREADENTRY32 te32 = { };
te32.dwSize = sizeof(THREADENTRY32); // 遍历线程
if (Thread32First(hThreadSnap, &te32))
{
do
{
if (te32.th32OwnerProcessID == dwCurrPid) {
if (te32.th32ThreadID != dwCurrTid){
// 获取句柄
hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
if (NULL != hThread){
ResumeThread(hThread);
}
CloseHandle(hThread);
}
}
} while (Thread32Next(hThreadSnap, &te32));
}
CloseHandle(hThreadSnap); return TRUE;
} static void __inner_memcpy(unsigned char* pDest, unsigned char* pSrc, unsigned int count)
{
while(count > ) {
*pDest++ = *pSrc++;
count --;
}
} XSimpleStub::XSimpleStub(void* pOrigFunc, void* pNewFunc, bool need_lock_other_thread):
str_func_addr(pOrigFunc), is_stub_succ(false), need_lock_other_thread_(need_lock_other_thread)
{
// 源地址、目标地址需要进行一次判定
if (nullptr != pOrigFunc && nullptr != pNewFunc)
{
DWORD ProtectVar; // 保护属性变量
MEMORY_BASIC_INFORMATION MemInfo; //内存分页属性信息 // 取得对应内存的原始属性
if ( != VirtualQuery(pOrigFunc, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION)))
{
// 如果需要锁住所有其他线程,则先执行锁定动作
if (need_lock_other_thread) {
LockOtherThread();
} // 修改页面为可写
if(VirtualProtect(MemInfo.BaseAddress, MemInfo.RegionSize, PAGE_READWRITE, &MemInfo.Protect))
{
// 备份原数据,防止自身需要使用memcpy,不能使用类似接口
__inner_memcpy((unsigned char*)str_instruct_back, (unsigned char*)pOrigFunc, JMPCODE_LENGTH); // 修改目标地址指令为 jmp pDestFunc
*(unsigned char*)pOrigFunc = JMPCMD; //拦截API,在函数代码段前面注入jmp xxx
*(DWORD*)((unsigned char*)pOrigFunc + JMPCMD_LENGTH) = (DWORD)pNewFunc - (DWORD)pOrigFunc - JMPCODE_LENGTH; // 改回原属性
VirtualProtect(MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Protect, &ProtectVar); // 修改后,还需要刷新cache
FlushInstructionCache(GetCurrentProcess(), pOrigFunc, JMPCODE_LENGTH); is_stub_succ = true;
} // 如果需要锁住所有其他线程,则先执行锁定动作
if (need_lock_other_thread) {
UnlockOtherThread();
}
}
}
} XSimpleStub::~XSimpleStub()
{
if (is_stub_succ)
{
DWORD TempProtectVar; //临时保护属性变量
MEMORY_BASIC_INFORMATION MemInfo; //内存分页属性信息 if ( != VirtualQuery(str_func_addr, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION)))
{
// 如果需要锁住所有其他线程,则先执行锁定动作
if (need_lock_other_thread_) {
LockOtherThread();
} // 修改页面为可写
if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize, PAGE_READWRITE,&MemInfo.Protect))
{
// 恢复代码段
__inner_memcpy((unsigned char*)str_func_addr, (unsigned char*)str_instruct_back, JMPCODE_LENGTH); //改回原属性
VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize, MemInfo.Protect,&TempProtectVar); // 修改后,还需要刷新cache
FlushInstructionCache(GetCurrentProcess(), str_func_addr, JMPCODE_LENGTH);
} // 如果需要锁住所有其他线程,则先执行锁定动作
if (need_lock_other_thread_) {
UnlockOtherThread();
}
}
} }

Linux下一样有类似技术,可以参考IBM的一个文档:

https://www.ibm.com/developerworks/cn/linux/l-knldebug/index.html

这其中的差别在跳转指针的设计上,Linux上,是使用了7个字节,并不需要计算原函数、新函数的地址距离

整个替换流程的实现分为如下几个步骤:

()    替换指令码:
b8 /*movl $, $eax;这里的$0将被具体替换函数的地址所取代*/
ff e0 /*jmp *$eax ;跳转函数*/
将上述7个指令码存放在一个字符数组中:
replace_code[] () 用替换函数的地址覆盖第一条指令中的后面8个0,并保留原来的指令码:
memcpy (orig_code, func, ); /* 保留原函数的指令码 */
*((long*)&replace_code[])= (long) replace_func; /* 赋替换函数的地址 */
memcpy (func, replace_code, ); /* 用新的指令码替换原函数指令码 */ () 恢复过程用保留的指令码覆盖原函数代码:
memcpy (func, orig_code, )

Linux下,实现代码页属性修改使用函数接口:mprotect

而相应的Linux下线程挂起、恢复使用如下接口:

#include <signal.h>
pthread_kill(ThreadID, SIGSTOP);  // suspend
pthread_kill(ThreadID, SIGCONT);  // resume

通过查看/proc/pid/task得知一个任务下的所有线程数

Windows下对函数打桩,及Linux类似技术的更多相关文章

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

    windows 下实现函数打桩:拦截API方式            近期由于工作须要,開始研究函数打桩的方法. 由于不想对project做过多的改动,于是放弃了使用Google gmock的想法. ...

  2. windows下的c语言和linux 下的c语言以及C标准库和系统API

    1.引出我们的问题? 标准c库都是一样的!大家想必都在windows下做过文件编程,在linux下也是一样的函数名,参数都一样.当时就有了疑问,因为我们非常清楚 其本质是不可能一样的,源于这是俩个操作 ...

  3. 转 windows下安装pycharm并连接Linux的python环境 以及 windows 下notepad ++编辑 linux 的文件

    ######sample 1:windows下安装pycharm并连接Linux的python环境 https://www.cnblogs.com/junxun/p/8287998.html wind ...

  4. Windows 下目录及文件向Linux同步

    本文解决的是Windows 下目录及文件向Linux同步的问题,Windows向 Windows同步的请参考:http://www.idcfree.com/article-852-1.html 环境介 ...

  5. windows下的mysql迁移到linux下

    最近做毕业设计,需要把windows下的mysql移植到linux下 曾经有过在window下移植mysql数据库的经验,只需要把msql的数据文件复制到另一台安装mysql的机器的数据存放位置,然后 ...

  6. 将Windows下的文件同步到Linux下

    需求:把Windows下的某些文件自动传送到Linux指定目录下 实现: 1. Windows下安装 WinSCP工具,并把Liunx服务器信息保存 2. 编写脚本,实现双击工具就把Windows下的 ...

  7. Windows下python3登陆和操作linux服务器

    一.环境准备 python3远程连接需要用到pycrytodome和paramiko库,其中后者依赖前者,所以按照顺序来安装 1. 安装pycrytodome 1 pip install pycryt ...

  8. windows下安装pycharm并连接Linux的python环境

    1. 下载安装Pycharm专业版 具体方法略.Pycharm5激活方法参考http://www.cnblogs.com/snsdzjlz320/p/7110186.html 2. 添加配置连接远程服 ...

  9. [转载+补充][PY3]——环境配置(2)——windows下安装pycharm并连接Linux的python环境

    原文地址:<你所会用到的Python学习环境和工具> 1. 下载安装Pycharm专业版 具体方法略.Pycharm5激活方法参考http://www.cnblogs.com/snsdzj ...

随机推荐

  1. Librepilot-Spark2固件的烧写

    1.通过USB连接Librepilot_Spark2飞控,启动Librepilot GCS地面站(博主的GCS是16.09版本),界面如下. 2.点击Firmware标签页,点击终止系统(若板子未带系 ...

  2. Qualcomm_Mobile_OpenCL.pdf 翻译-8-kernel性能优化

    这章将会说明一些kernel优化的小技巧. 8.1 kernel合并或者拆分 一个复杂的应用程序可能包含很多步骤.对于OpenCL的移植性和优化,可能会问需要开发有多少个kernel.这个问题很难回答 ...

  3. PAT Basic 1039 到底买不买 (20 分)

    小红想买些珠子做一串自己喜欢的珠串.卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖.于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如果是,那么告诉她有多少多余的珠子 ...

  4. python核心编程正则表达式练习题1-2匹配由单个空格分隔的任意单词对,也就是性和名

    # 匹配由单个空格分隔的任意单词对,也就是姓和名 import re patt = '[A-Za-z]+ [A-Za-z]+' # 方法一 +加号操作符匹配它左边的正则表达式至少出现一次的情况 # p ...

  5. FPDF_CHAR_INFO

    typedef struct { FX_WCHAR m_Unicode; FX_WCHAR m_Charcode; FX_INT32 m_Flag; FX_FLOAT m_FontSize; FX_F ...

  6. Spring的DI和AOP

    Spring 为了降低Java开发的复杂性,Spring采取了以下4种关键策略:* 基于POJO的轻量级和最小入侵性编程:* 通过依赖注入和面向接口实现松耦合:* 基于切面和惯例进行声明式编程:* 通 ...

  7. pushd&popd&dirs命令

    dirs 显示当前目录栈中的所有记录 -p  一个目录一行显示  -l  以完整格式显示  -c  删除目录栈中的所有记录  -v  每行一个目录来显示,每个目录前加上编号  +N  从左到右的第n个 ...

  8. c语言模块化编程

    C语言模块化编程(封装) 模块即是一个.c 文件和一个.h 文件的结合,头文件(.h)中是对于该模块接口的声明.模块化的实现方法和实质:将一个功能模块的代码单独编写成一个.c文件,然后把该模块的接口函 ...

  9. 动态树(Link-Cut-Tree)简单总结(指针版本)

    Link-Cut-Tree(动态树 LCT) 1.定义 1. 偏爱子节点: 一颗子树内最后访问的点若在该子树根节点 X 的某个儿子节点 P 的子树中,则称 P 为 X 的偏爱子节点. 偏爱边:连向偏爱 ...

  10. POJ-2104-Kth Number(主席树)

    链接: https://vjudge.net/problem/POJ-2104#author=malic 题意: 给定一个数组 a[1...n],数组元素各不相同,你的程序要对每次查询Q(i,j,k) ...