如何修改栈结构统计每个DLL的函数使用信息
2010-08-02 14:38
393 查看
在文章如何Hook Windows API中,我们讨论了如何Hook Windows API。此种方式的结果是每个DLL都会跳转到相同的函数,所以不便实现针对每个DLL的函数使用信息。如果希望得到基于每个DLL的信息,可以通过修改栈的结构的方式实现。
假设现在要Hook的函数是: void __stdcall Func(int, int);
其调用时的栈结构如图1所示:
图1: Thunk对栈结构的调整
现在我们把Hook的函数替换成如下的thunk代码:
#pragma pack(push,1)
struct _stdcallThunk
{
BYTE m_push[3];// push dword ptr [esp]
DWORD m_mov; // move dword ptr [esp+0x4],pThis
DWORD m_this; // pThis, which serves as the operand for mov instruction
BYTE m_jmp; // jmp proc
DWORD m_relproc; // proc, which serves as the operand for jmp instruction
VOID Init(INT_PTR proc, INT_PTR pThis, INT_PTR pThunk)
{
m_push[0] = 0xFF;
m_push[1] = 0x34;
m_push[2] = 0x24;
m_mov = 0x042444C7;
m_this = (DWORD)pThis;
m_jmp = 0xE9;
m_relproc = (DWORD)(proc - (pThunk + sizeof(_stdcallThunk)));
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallThunk));
}
};
那么当函数调用转到Hook之后,其栈结构会被调整,如图1所示。调整之后的栈结构正好是一个对C++对象的成员函数调用之后的栈结构。所以只要我们定义一个C++类,使其中的一个非静态成员函数拥有和被Hook函数相同的声明,我们就可以利用上面的Thunk把函数的调用分发给为每个DLL创建的该C++类的对象,从而记录每个DLL中对该函数的使用信息。
使用thunk的步骤如下:
定义一个C++类,使其一个非静态成员函数拥有和被Hook函数相同的声明;
创建该类的一个对象;
创建一个_stdcallThunk对象,调用其Init()函数,参数设置如下表所示.
把Hook函数的地址设成thunk对象的首地址。
这里有如下几个问题需要注意:
1. 如何取得非静态C++函数的地址
C++标准不允许用提取静态成员函数的方式提取非静态函数的地址,可以使用如下的方式:
a) 定义如下共用体:
union {
INT_PTR dwFunc;
RETURN_TYPE (CLASS::*pfn)(ARGS);
} pfn;
b)把非静态函数指针赋值给pfn.pfn,然后通过pfn.dwFunc取出值。
2. 防止DEP设置导致程序crash
由于thunk是堆上的对象,所以如果OS打开DEP,那么程序可能会crash。解决方法是使用VirtualAlloc()创建一个具有read/write/execute属性的内存地址,把thunk分到该地址空间中。
3. 对齐地址防止cache不同步
需要对thunk的起始地址进行对齐,以防止代码被加载到指令缓存的时候出现不同步,从而导致程序crash。
假设现在要Hook的函数是: void __stdcall Func(int, int);
其调用时的栈结构如图1所示:
图1: Thunk对栈结构的调整
现在我们把Hook的函数替换成如下的thunk代码:
#pragma pack(push,1)
struct _stdcallThunk
{
BYTE m_push[3];// push dword ptr [esp]
DWORD m_mov; // move dword ptr [esp+0x4],pThis
DWORD m_this; // pThis, which serves as the operand for mov instruction
BYTE m_jmp; // jmp proc
DWORD m_relproc; // proc, which serves as the operand for jmp instruction
VOID Init(INT_PTR proc, INT_PTR pThis, INT_PTR pThunk)
{
m_push[0] = 0xFF;
m_push[1] = 0x34;
m_push[2] = 0x24;
m_mov = 0x042444C7;
m_this = (DWORD)pThis;
m_jmp = 0xE9;
m_relproc = (DWORD)(proc - (pThunk + sizeof(_stdcallThunk)));
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallThunk));
}
};
那么当函数调用转到Hook之后,其栈结构会被调整,如图1所示。调整之后的栈结构正好是一个对C++对象的成员函数调用之后的栈结构。所以只要我们定义一个C++类,使其中的一个非静态成员函数拥有和被Hook函数相同的声明,我们就可以利用上面的Thunk把函数的调用分发给为每个DLL创建的该C++类的对象,从而记录每个DLL中对该函数的使用信息。
使用thunk的步骤如下:
定义一个C++类,使其一个非静态成员函数拥有和被Hook函数相同的声明;
创建该类的一个对象;
创建一个_stdcallThunk对象,调用其Init()函数,参数设置如下表所示.
把Hook函数的地址设成thunk对象的首地址。
参数名 | 取值 |
proc | 步骤1中定义的非静态成员函数的函数指针 |
pThis | 步骤2中创建对象的指针 |
pThunk | 步骤3中创建的对象的指针 |
1. 如何取得非静态C++函数的地址
C++标准不允许用提取静态成员函数的方式提取非静态函数的地址,可以使用如下的方式:
a) 定义如下共用体:
union {
INT_PTR dwFunc;
RETURN_TYPE (CLASS::*pfn)(ARGS);
} pfn;
b)把非静态函数指针赋值给pfn.pfn,然后通过pfn.dwFunc取出值。
2. 防止DEP设置导致程序crash
由于thunk是堆上的对象,所以如果OS打开DEP,那么程序可能会crash。解决方法是使用VirtualAlloc()创建一个具有read/write/execute属性的内存地址,把thunk分到该地址空间中。
3. 对齐地址防止cache不同步
需要对thunk的起始地址进行对齐,以防止代码被加载到指令缓存的时候出现不同步,从而导致程序crash。
相关文章推荐
- eclipse 中main()函数中的String[] args如何使用?通过String[] args验证账号密码的登录类?静态的主方法怎样才能调用非static的方法——通过生成对象?在类中制作一个方法——能够修改对象的属性值?
- 如何使用ASP.NET代码查看、修改AD用户信息
- 在vb中使用Iphlpapi.dll获取网络信息 第三章 第一节 IPHLPAPI 的其它函数
- 在vb中使用Iphlpapi.dll获取网络信息 第三章 第一节 IPHLPAPI 的其它函数
- 线程中通过信号和槽函数传递信息的时候,由于用到了自己定义的参数结构,所以使用qRegisterMetaType
- 如何知道执行计划使用了那些统计信息
- 信息: 更正 SetFont() 函数,在 MFC 的使用 修改字体
- SQL优化----如何使用工具快速诊断出统计信息有问题?
- 如何修改使用 DocumentProperties() 函数的打印机设置
- 查询优化器如何使用(索引)统计信息 (How the Query Optimizer Uses Statistics)
- 在vb中使用Iphlpapi.dll获取网络信息 第四章 第二节 一个重要函数
- PowerShell 调用系统 DLL 中的函数,如何使用返回参数中的值
- 在vb中使用Iphlpapi.dll获取网络信息 第四章 第二节 一个重要函数
- 【转】如何知道执行计划使用了那些统计信息
- [Unity3D]使用OnGUI函数显示状态栏并且使用触发器修改玩家当前信息
- 如何使用dbms_stats分析统计信息
- 如何使用len()函数统计字段值的长度
- VS6.0下使用 IP Help API(Iphlpapi.dll)系列函数之GetAdaptersAddresses(GetAdaptersInfo) 获取网卡信息简述
- 求助,如何使用批处理命令查询本机有几个盘,然后每个盘总大小空间和可用大小空间,然后将这些信息生成一个文档,求高手指教,谢谢。
- 如何使用dbms_stats分析统计信息?