| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 5021 人关注过本帖, 1 人收藏
标题:WINDOWS下HOOK函数的方法
只看楼主 加入收藏
东海一鱼
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:48
帖 子:757
专家分:4760
注 册:2009-8-10
结帖率:100%
收藏(1)
 问题点数:0 回复次数:11 
WINDOWS下HOOK函数的方法
前两天和dvff谈论的一些技术总结。
程序代码:
/////////////////////////////////////////////////////////////////////////////////////
//                    HOOKAPI DEMO PROGRAM
//文件名:HOOKTEST.C
//
//描述: 演示WINNT下HOOK系统API过程及方法
//      (扩充为HOOK任意函数的方法)
//
//作者:东海一鱼
//
//时间: 2010.7.22       
//
//使用编译器: VC2003
//
//使用第三方库: NULL
//
//
//Bug、修复纪录:
//          1、2010.8.4 增加HOOK私有函数代码(被hook函数名TestFn,hook函数名MyHookFn2)
//
//            2、2010.8.6 增加HOOK类虚拟函数代码(被hook函数名TestVirtualFn,
//               hook函数名TestVirtualFn2)
//
//               HOOK类成员函数,主要难度在于成员函数地址的获得。
//             普通成员函数指针在VC6下无法进行强制类型转换,
//             解决手段见GetClassFnAddress辅助函数。
//             类虚拟成员函数HOOK需要获得类实例对象虚表中的虚拟函数指针。                   
////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

//#pragma pointers_to_members(best_case)

#ifdef _X86_

#define FLATJMPCODE_LENGTH 5            //x86 平坦内存模式下,绝对跳转指令长度
#define FLATJMPCMD_LENGTH  1            //机械码0xe9长度
#define FLATJMPCMD         0xe9

#else

#error WINVER ERRORSLECT  ONLY USES ON X86!!!

#endif

///////////////////////////////////////////////////////////////////////////
//测试类函数HOOK
class TestHookFnClass
{
public:
    virtual void TestVirtualFn(char* szPlay);
    virtual void TestVirtualFn2(char* szPlay);
    void TestHookFn(char* szPlay);
    void TestHookFn2(char* szPlay);   
};

void TestHookFnClass::TestHookFn(char* szPlay)
{
    printf("%s\n",szPlay);
}

void TestHookFnClass::TestHookFn2(char* szPlay)
{
    printf("%s - %s\n ",szPlay,"类函数已被HOOK!");
}

void TestHookFnClass::TestVirtualFn(char* szPlay)
{
    printf("%s\n",szPlay);
}

void TestHookFnClass::TestVirtualFn2(char* szPlay)
{
    printf("%s - %s\n",szPlay,"Virtual2 HOOKS");
}

/////////////////////////////////////////////////////////////////////////
#ifdef _X86_
//利用C变参特性,进行类成员函数指针类型转换
LPVOID GetClassFnAddress(...)                    //Add  2010.8.6
{
    LPVOID FnAddress;
   
    __asm
    {
        lea eax,FnAddress
        mov edx,[ebp+8]
        mov [eax],edx
    }
    return FnAddress;
}
#endif

//获得类虚拟成员函数指针
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;
}
/////////////////////////////////////////////////////////////////////////
//我的新API函数
int __stdcall MyHookFn(HWND hwnd,char* sztext,char* szTitle,int stly)
{          
   
    return printf("%s - %s\n",sztext,"原API函数已被HOOK!");   //
}

//我的新私有桩函数
void MyHookFn2(char* szTxt)
{
    char* pBuf = (char*)malloc(strlen(szTxt) + sizeof(char*) * 32);
   
    __try
    {
        sprintf(pBuf,"%s - %s",szTxt," 原函数已被HOOK!");
        printf("%s\n",pBuf);
    }
    __finally
    {
        free(pBuf);
    }
}

//私有测试函数
void TestFn(char* szTitle)
{
    printf("%s",szTitle);
}

//////////////////////////////////////////////////////////////////////
//HOOK函数
BOOL HookApi(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))                            //修改页面为可写
    {
        *(BYTE*)ApiFun = FLATJMPCMD;                                     
        *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
            (DWORD)ApiFun - FLATJMPCODE_LENGTH;
       
        VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
            MemInfo.Protect,&TempProtectVar);                               //改回原属性
       
        IsSuccess = TRUE;
    }
   
    return IsSuccess;
}

int main(int argc,char** argv)
{
    HMODULE hDll;
    LPVOID  OldFun;    

    TestHookFnClass* TestClass = new TestHookFnClass();

    __try
    {
        hDll = GetModuleHandle("User32.dll");
        OldFun = GetProcAddress(hDll,"MessageBoxA");  //要HOOK的API函数
   
        if(OldFun)
        {
            if(HookApi(OldFun,MyHookFn))  //如果HOOK成功
                MessageBoxA(0,"call Api MessageBox","Is Hookd?",MB_OK); //调用原API,测试HOOK

            if(HookApi(TestFn,MyHookFn2))
                TestFn("Private Function Hook Test");  //调用私有函数,测试HOOK   
           
            if(HookApi(GetClassFnAddress(TestHookFnClass::TestHookFn),
                GetClassFnAddress(TestHookFnClass::TestHookFn2)))
                TestClass->TestHookFn("Hook Class Member Function Test!");//调用类成员函数,测试HOOK

            if(HookApi(GetClassVirtualFnAddress(TestClass,0),
                GetClassFnAddress(TestHookFnClass::TestHookFn2)))
                TestClass->TestVirtualFn("Call Class Virtual Function Test!");//调用类虚拟函数,测试HOOK
           
            if(HookApi(GetClassVirtualFnAddress(TestClass,0),
                GetClassVirtualFnAddress(TestClass,1)))                    
                TestClass->TestVirtualFn("Call Class Virtual Function Test2!");//同上,桩函数为另一虚函数
        }
    }
    __finally
    {
        if(hDll)
            FreeLibrary(hDll);
        if(TestClass)
            delete(TestClass);
    }
   
    return 0;
}

搜索更多相关主题的帖子: HOOK 函数 WINDOWS 
2010-08-07 11:22
vfdff
Rank: 6Rank: 6
等 级:侠之大者
威 望:8
帖 子:2172
专家分:425
注 册:2005-7-15
收藏
得分:0 
您太谦虚了,是一直我向您请教的!
对了,上面的程序编译提示:error C2712 ,您没有这个问题?

~~~~~~~~~~~~~~~好好学习~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-08-07 13:45
vfdff
Rank: 6Rank: 6
等 级:侠之大者
威 望:8
帖 子:2172
专家分:425
注 册:2005-7-15
收藏
得分:0 
加上 /GX-   开关就好了,原来一直加 /GX开关发现不行

请问 /GX-和/GX开关有什么区别吗?

~~~~~~~~~~~~~~~好好学习~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-08-07 15:14
vfdff
Rank: 6Rank: 6
等 级:侠之大者
威 望:8
帖 子:2172
专家分:425
注 册:2005-7-15
收藏
得分:0 
程序代码:
LPVOID GetClassFnAddress(...)                    //Add  2010.8.6
{
    LPVOID FnAddress;
  

    __asm
    {
        lea eax,FnAddress
        mov edx,[ebp+8]
        mov [eax],edx
    }
    return FnAddress;
}
这个函数有点诡异,ASNSI   C   不是要求可变参数函数定义至少带一个固定参数 ??

~~~~~~~~~~~~~~~好好学习~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2010-08-07 15:22
yxwsbobo
Rank: 5Rank: 5
等 级:职业侠客
帖 子:345
专家分:306
注 册:2007-10-29
收藏
得分:0 
楼主用的是修改目标函数头5字节为 jmp address

这样总在多线程情况下容易出错,HOOK API的话修改IAT比较好

How are you 怎么是你?
How old are you   怎么老是你?
2010-08-07 16:29
东海一鱼
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:48
帖 子:757
专家分:4760
注 册:2009-8-10
收藏
得分:0 
以下是引用vfdff在2010-8-7 15:14:29的发言:

加上 /GX-   开关就好了,原来一直加 /GX开关发现不行

请问 /GX-和/GX开关有什么区别吗?
啊,是我疏漏。
 其实去掉/GX选项就等同于/GX-
 这个选项指定要使用编译器的异常处理模型,如果超出了超出了作用域释放C++对象将被作为一个异常。

 VC 2005后这个选项改为/EH,对编译器的行为有更详细的微调。


举世而誉之而不加劝,举世而非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。彼其于世未数数然也。
2010-08-07 16:35
东海一鱼
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:48
帖 子:757
专家分:4760
注 册:2009-8-10
收藏
得分:0 
以下是引用vfdff在2010-8-7 15:22:06的发言:

LPVOID GetClassFnAddress(...)                    //Add  2010.8.6
{
    LPVOID FnAddress;
   
 
    __asm
    {
        lea eax,FnAddress
        mov edx,[ebp+8]
        mov [eax],edx
    }
    return FnAddress;
}这个函数有点诡异,ASNSI   C   不是要求可变参数函数定义至少带一个固定参数 ??
是啊,这正是VC6不标准的地方。

举世而誉之而不加劝,举世而非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。彼其于世未数数然也。
2010-08-07 16:37
东海一鱼
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:48
帖 子:757
专家分:4760
注 册:2009-8-10
收藏
得分:0 
以下是引用yxwsbobo在2010-8-7 16:29:03的发言:

楼主用的是修改目标函数头5字节为 jmp address
 
这样总在多线程情况下容易出错,HOOK API的话修改IAT比较好
愿闻其详。请细讲。   

举世而誉之而不加劝,举世而非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。彼其于世未数数然也。
2010-08-07 16:40
yxwsbobo
Rank: 5Rank: 5
等 级:职业侠客
帖 子:345
专家分:306
注 册:2007-10-29
收藏
得分:0 
不是容易 是有可能

        *(BYTE*)ApiFun = FLATJMPCMD;                                    
        *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
            (DWORD)ApiFun - FLATJMPCODE_LENGTH;


比如此时运行完第一句,然后切换到另一个线程调用这个函数,此时这个函数第一个字节被修改为e9 而跳转地址还没来得及修改 所以就出错了

各有利弊

How are you 怎么是你?
How old are you   怎么老是你?
2010-08-07 16:43
东海一鱼
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:48
帖 子:757
专家分:4760
注 册:2009-8-10
收藏
得分:0 
谢谢指教。

举世而誉之而不加劝,举世而非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。彼其于世未数数然也。
2010-08-07 16:54
快速回复:WINDOWS下HOOK函数的方法
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.046806 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved