如何修改栈结构统计每个DLL的函数使用信息

在文章如何Hook Windows API中,我们讨论了如何Hook Windows API。此种方式的结果是每个DLL都会跳转到相同的函数,所以不便实现针对每个DLL的函数使用信息。如果希望得到基于每个DLL的信息,可以通过修改栈的结构的方式实现。

假设现在要Hook的函数是: void __stdcall Func(int, int);

其调用时的栈结构如图1所示:

image

图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的步骤如下:

  1. 定义一个C++类,使其一个非静态成员函数拥有和被Hook函数相同的声明;
  2. 创建该类的一个对象;
  3. 创建一个_stdcallThunk对象,调用其Init()函数,参数设置如下表所示.
  4. 把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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值