C++ Hook(钩子)编程,通过内联汇编,使类成员函数代替全局函数(静态函数)

    技术2022-05-19  41

     

    C++ Hook(钩子)编程,通过内联汇编,使类成员函数代替全局函数(静态函数)

     

    编程语言:C/C++

    编译环境:Visual Studio 2008

    核心方法::通过内联汇编,构造类对象独享的函数(委托),完成了类成员函数到普通全局函数的转化,并在Windows Hook(钩子)编程中得到成功的实践。

    关键字:C++,委托,内联汇编,Hook,成员函数

     

     

    引文:

    前段时间曾编写了一个自认为很完善的.NET平台上的Hook(钩子)动态链接库(DLL),并进一步完成了GUI界面,但是在部署时却发现的其局限性,很多用户都没有安装.NET平台,其平台的最小安装(.NET 2.0)也需要21M,如果使用NOMO(2.0)免安装发布的话也需要小10M(而且使用NOMO在自动运行和兼容性上也有待商榷)

    因此,我下定决心,准备彻底和托管代码决裂,回归C/C++,再次实现上述功能,然而真正编写时才发现,经典的C++果然不是盖的,昔日被C的各种调试不通过折磨的记忆还未消退,今日又在开始时陷入苦战,正如前人所说,C++这种强类型语言,喜欢在繁琐的符号和语法上做文章。如果处理不好这些,大概做不了大项目,还是去.NETJava的快乐平台逍遥去吧~微微感慨,赶快进入正题。

     

    正文:

    本文的目的是研究类成员函数与普通函数之区别,以及不同调用方式之间的区别,进而通过内联汇编语句模仿特定的调用,从而完成通过普通函数指针调用类成员函数的功能。因此主要部分为研究和尝试的过程,如希望直接查看Hook编程的结果,可直接查看尾部代码。

    使用过Windows Hook编程的同志们都知道,在程序中是通过如下语句来加载钩子,卸载钩子。

     

    C/C++ Code:

    HHOOK SetWindowsHookEx( //加载钩子 int idHook, HOOKPROC lpfn, //钩子函数的指针 HINSTANCE hMod, DWORD dwThreadId ); BOOL UnhookWindowsHookEx( //卸载钩子 HHOOK hhk );  

    其中最重要的就是HOOKPROC lpfn这个函数指针,查看此函数的形式,为:

    C/C++ Code:

     

    LRESULT CALLBACK HookProcess( //钩子函数 int nCode, WPARAM wParam, LPARAM lParam ); 

    //其中CALLBACK 的定义为 #define CALLBACK    __stdcall

     

    其中,值得注意的是,这个钩子函数需要是一个普通__stdcall调用的C函数,而在C++中可以理解为某个全局函数(非类成员函数,全局访问)或者是一个类的静态函数(static,全局访问)。

    让我们再观察一个类成员函数和全局函数的区别,我建立了一个简单的演示代码,如下代码:

     

    C/C++ Code:

    #include <windows.h> LRESULT CALLBACK HookProcess1( // HookProcess1是一个全局函数 int nCode, WPARAM wParam, LPARAM lParam){ return NULL;} class WinHook { public: int value; //定义一个成员变量,以便于测试访问 LRESULT CALLBACK HookProcess2(// HookProcess2是一个类的成员函数 int nCode, WPARAM wParam, LPARAM lParam) { this->value++; //检查成员变量的访问性 return NULL; } }; int main(){ //定义一个函数指针HookProcess,它可以成功作为参数传递给SetWindowsHookEx LRESULT (CALLBACK * HookProcess) (int nCode, WPARAM wParam, LPARAM lParam); //试图将HookProcess指向HookProcess1 HookProcess = HookProcess1; //赋值成功,这个指针能够指向全局函数 HookProcess (0,0,0);//尝试调用之. //试图将HookProcess指向HookProcess2 WinHook *myHook = new WinHook(); HookProcess = myHook->HookProcess2; //编译失败,提示类型不能转化 //此句将在之后修改 HookProcess (0,0,0);//尝试调用之. return 0; } 

     

     

    查看错误代码,终于发现类的成员函数和普通的全局函数是不一样的,

    // error C2440:

    //=: 无法从“LRESULT (__stdcall WinHook::* )(int,WPARAM,LPARAM)”转换为“LRESULT (__stdcall *)(int,WPARAM,LPARAM)

    关键问题就在于“WinHook::”的区别,可以看到,无论如何,一个类的成员函数(非静态)是不能与普通的C全局函数转化的。这点在后面再详细的说明。

    然而,当我们使用C++编程的时候,往往不希望使用全局函数;如果使用类的静态函数,那么一个类只能实现一个钩子,且不能利用C++中类的继承,多态等特性,从实质上沦落为普通C编程。

    在网上查找了很多方法,发现可以通过内联汇编语句强制将指针转化,如下:

    C/C++ Code:

    //修改上述main中代码,将类成员函数指针强制转化为一般函数指针 WinHook *myHook = new WinHook(); //HookProcess = myHook->HookProcess2; __asm //NEW: 内联汇编语句 { push eax lea eax, WinHook::HookProcess2 mov HookProcess, eax pop eax } HookProcess (0,0,0);//尝试调用之. 

     

    然而在使用这种方法时,却发现了问题,即在底层调用WinHook HookProcess2中,其中的this指针为0,因此所有this相关的操作均不可行(即不能访问本对象的其他成员)。此外,在HookProcess2结束后,底层也发现了堆栈不平衡而导致的错误。

     

    继续阅读网上的文章,如

    (成员函数指针与高性能的C++委托)

    http://www.cnblogs.com/jans2002/archive/2006/10/13/528160.html

    (普通函数指针变量可以保存成员函数地址吗?

    http://topic.csdn.net/u/20100905/21/0bfacc26-cdd5-463b-8724-98c626e2b3dc.html

    (使非MFC窗口程序的窗口回调过程成为C++类的成员函数)

    http://www.systhinker.com/html/90/n-1990.html

    等等

     

    我终于理解的类成员函数与普通函数的区别,那就是this指针,当一个类的成员函数被调用时,如:

    C/C++ Code:

    WinHook *myHook = new WinHook(); HookProcess = myHook->HookProcess2(…); 

     

    此时,当HookProcess2的参数被传送之后,myHook的地址也被传送在CPU寄存器中(或堆栈中,这要看传调用方式的不同,在之后说明),并在之后在HookProcess2函数中赋值给this,使得类成员函数能够知道调用的是哪个类的对象。

    为了测试这一点,我们可以在强制转化了HookProcess2指针之后,在HookProcess2中修复this指针,看函数的this功能是否正常。如下代码:

    C/C++ Code:

    class WinHook //修改WinHook类的函数HookProcess2 { public: int value; //定义一个成员变量,以便于测试访问 LRESULT CALLBACK HookProcess2(// HookProcess2是一个类的成员函数 int nCode, WPARAM wParam, LPARAM lParam) { //假设主函数中的myHook指针指向0x003d3450,修复this指针 __asm //内联汇编语句 { push eax //保护eax mov eax, 0x003d3450 mov this, eax //设置本类的this指针(__asm不检查访问性) pop eax //还原eax } //此时查看this指针,就能够发现this正常工作 this->value++; return NULL; } }; 

     

    注:虽然this指针能够运行成功,但是函数结束会产生如下错误:

     

    这是由于__stdcallthis传送方式是堆栈,强制访问HookProcess2却未将this压栈会导致堆栈不平衡。因此,将CALLBACK这个修饰符注销掉,即:

    C/C++ Code:

    class WinHook //进一步修改WinHook类的函数HookProcess2 { public: int value; //定义一个成员变量,以便于测试访问 LRESULT /*CALLBACK*/ HookProcess2(// HookProcess2是一个类的成员函数 int nCode, WPARAM wParam, LPARAM lParam) { //假设主函数中的myHook指针指向0x003d3450,修复this指针 __asm //内联汇编语句 { push eax //保护eax mov eax, 0x003d3450 mov this, eax //设置本类的this指针(__asm不检查访问性) pop eax //还原eax } //此时查看this指针,就能够发现this正常工作 this->value++; return NULL; } }; 

    此时,发现整个函数能够顺利运行。(注意,在实际测试时,需要在main中加断点检查myHook的地址,并动态的修改mov eax, 0x003d3450的数值。否则不能测试通过)

     

    这是因为,注销掉CALLBACK的修饰符(即__stdcall)之后,函数采用默认的__thiscall调用方式,此时,this指针是通过CUP寄存器的ecx传送,此时就不会产生堆栈不平衡的错误了。

    进一步的,我们遇到的新的问题,尽管能够成功模拟一个类成员函数的调用,修复了this指针,但是对于SetWindowsHookEx来说,每个类的所有成员是共享一个函数空间的。即如下图所示。

    因此,如果使用HookProcess2的程序入口(内存首地址)作为SetWindowsHookEx的参数传入,在引发钩子事件的时候就不能够区分到底引发了哪个类的HookProcess2函数,即不能设定this指针。

    为了解决这个问题,我们需要生成一个动态的函数入口,这个入口是每个对象独享的,因此不同的对象将引发不同的函数,这就能够区分类的不同对象。根据其他语言对它的描述,我们将这个函数入口暂称为委托(Delegate)。

    在实际上,我们是通过定义一个成员变量(如byte类型的数组,可便于的赋值)来实现这个委托的,其中实际上保存了一段机器码,这段机器码(根据汇编语法)可以动态的设定this指针,并实现到真正函数首地址的跳转。这个委托的示意过程如下:

     

    下面说明这个委托的声明和设定代码:

    C/C++ Code:

    class WinHook //进一步修改WinHook类的函数HookProcess2 { public: byte DelegateThisCall[10]; //定义委托 WinHook(void){ //初始化委托 byte * DelegatePoint = DelegateThisCall; DelegateThisCall[0] = 0xb9; //0-4:__asm mov ecx, this DelegateThisCall[5] = 0xe9; //5-9:__asm jmp, CoreHookProcess __asm { push eax //保护eax push ebx //保护ebx mov eax, this //取得this地址 mov ebx, DelegatePoint //获取DelegateThisCall地址 mov dword ptr [ebx+1], eax //this 地址, 双字(DWORD) } //计算jmp地址参考:http://zhidao.baidu.com/question/105950930.html __asm { lea eax, HookProcess2 //取得HookProcess2地址 mov ebx, DelegatePoint //获取jmp地址= DelegatePoint + 5 add ebx, 5 add ebx, 5 //JMP地址=目标地址-(本句地址+本句指令字节数) sub eax, ebx //JMP地址= HookProcess2 – [(DelegatePoint+5) + 5] mov dword ptr [ebx-4], eax //HookProcess2地址, 双字(DWORD) pop ebx //还原ebx pop eax //还原eax } } int value; //定义一个成员变量,以便于测试访问 LRESULT /*CALLBACK*/ HookProcess2(// HookProcess2是一个类的成员函数 int nCode, WPARAM wParam, LPARAM lParam) { //查看this指针,就能够发现this正常工作 this->value++; return NULL; } }; 

     

    进一步的,修改主函数:

    C/C++ Code:

    int main(){ //定义一个函数指针HookProcess,它可以成功作为参数传递给SetWindowsHookEx LRESULT (CALLBACK * HookProcess) (int nCode, WPARAM wParam, LPARAM lParam); //将HookProcess指向HookProcess2 WinHook *myHook = new WinHook(); //HookProcess = myHook->HookProcess2; //赋值失败,提示类型不能转化 byte * DelegatePoint = myHook->DelegateThisCall; //获取委托首地址 __asm //内联汇编语句 { push eax mov eax, DelegatePoint mov HookProcess, eax //强制将委托的地址赋值给函数指针 pop eax } HookProcess (0,0,0);//尝试调用之. 调用成功 return 0; } 

     

    以上,就成功的完成了类成员函数的钩子过程,通过一个委托,完成了此功能。

     

    最后,为了演示上述方法在建立Windows Hook编程上的使用,特给出编写的动态链接库的实现WinHook功能的BaseHook类的代码,此类完成了成员函数的Hook加载卸载等管理,用户通过继承此类,并重写HookProcess函数(如下),便可完成所有Hook功能。

    C/C++ Code:

    //BaseHook 类的虚函数,当Hook事件发生时会调用此函数。 //用户通过继承并重写此函数完成Hook功能 virtual LRESULT /*CALLBACK*/ HookProcess (int nCode, WPARAM wParam, LPARAM lParam); 

     

     

    源码部分:

    C/C++ Code: BaseHook.h

    /********************************************************* {COPYRIGHT-TOP} * * RealZYC Confidential * OCO Source Materials * * (C) Copyright RealZYC Corp. 2011 All Rights Reserved. * * The source code for this program is not published or otherwise * divested of its trade secrets, irrespective of what has been * deposited with the China Copyright Office. ********************************************************** {COPYRIGHT-END} */ #pragma once #include <windows.h> /*************************************** *The basic defination of windows hook ****************************************/ class BaseHook { /*************************************** * Enum ****************************************/ #pragma region Enum public: /*************************************** *The available types of windows hook ****************************************/ enum HookTypes: int { //Message filter hook - WH_MSGFILTER = -1 MsgFilter = -1, //Journal record hook - WH_JOURNALRECORD = 0 JournalRecord = 0, //Journal playback hook - WH_JOURNALPLAYBACK = 1 JournalPlayback = 1, //Keyboard hook - WH_KEYBOARD = 2 Keyboard = 2, //Get message hook - WH_GETMESSAGE = 3 GetMessage = 3, //Call wnd proc hook - WH_CALLWNDPROC = 4 CallWndProc = 4, //CBT hook - WH_CBT = 5 CBT = 5, //System message filter hook - WH_SYSMSGFILTER = 6 SysMsgFilter = 6, //Mouse hook - WH_MOUSE = 7 Mouse = 7, //Hardware hook - WH_HARDWARE = 8 Hardware = 8, //Debug hook - WH_DEBUG = 9 Debug = 9, //Shell hook - WH_SHELL = 10 Shell = 10, //Fore ground idle hook - WH_FOREGROUNDIDLE = 11 ForeGroundIdle = 11, //Call wnd proc ret hook - WH_CALLWNDPROCRET = 12 CallWndProcRet = 12, //Keyboard low level hook - WH_KEYBOARD_LL = 13 KeyboardLL = 13, //Mouse low level hook - WH_MOUSE_LL = 14 MouseLL = 14 }; #pragma endregion /*************************************** * Value ****************************************/ #pragma region Value protected: //The hook type HookTypes int_HookType; //The hook object thread id, give 0 for all thread DWORD dword_ThreadId; //The hook id, give 0 for not set HHOOK point_HookID; //Dll entrance static HINSTANCE hangle_HinstDLL; protected: //The this call delegate for CoreHookProcess byte DelegateThisCall[10]; #pragma endregion /*************************************** * New ****************************************/ #pragma region New public: /*************************************** ''' <summary> ''' Initial function ''' </summary> ''' <param name="HookType">The hook type</param> ''' <param name="ThreadId">The hook object thread id, give 0 for all thread</param> ****************************************/ BaseHook(HookTypes HookType, DWORD ThreadId); //Dispose function ~BaseHook(void); #pragma endregion /*************************************** * Property ****************************************/ #pragma region Property public: //Set / get the hook type inline HookTypes GetHookType(); inline void SetHookType(HookTypes HookType); //Set / get the hook object thread id, give 0 for all thread inline DWORD GetThreadId(); inline void SetThreadId(DWORD ThreadId); //Set / get whether the hook is running bool GetEnabled(); void SetEnabled(bool Enabled); //Set / get dll hinst static HINSTANCE GetHinstDll(); static void SetHinstDll(HINSTANCE HinstDLL); #pragma endregion /*************************************** * Sub / Function ****************************************/ #pragma region Sub / Function protected: /*************************************** ///<summary> /// The defination of core hook process ///</summary> ///<param name="nCode">Specifies the hook code passed to the current hook procedure. The next hook procedure uses this code to determine how to process the hook information</param> ///<param name="wParam">Specifies the wParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> ///<param name="lParam">Specifies the lParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> ///<returns>The nCode, use 0 to pass the information to next hook, others to ignore the current information</returns> ///<remarks>Use for SetWindowsHookEx</remarks> ****************************************/ LRESULT /*CALLBACK*/ CoreHookProcess(int nCode, WPARAM wParam, LPARAM lParam); public: /*************************************** /// <summary> /// Set the hook data /// </summary> /// <param name="HookType">The hook type</param> /// <param name="ThreadId">The hook object thread id, give 0 for all thread</param> /// <remarks>Restart the hook after the hook data changed</remarks> ****************************************/ void SetHook(HookTypes HookType, DWORD ThreadId); /*************************************** /// <summary> /// Start the hook /// </summary> /// <remarks>Check the Enabled for operation result</remarks> ****************************************/ void Start(); /*************************************** /// <summary> /// Stop the hook /// </summary> /// <remarks>Check the Enabled for operation result</remarks> ****************************************/ void Stop(); /*************************************** /// <summary> /// The user defined hook process /// </summary> /// <param name="nCode">Specifies the hook code passed to the current hook procedure. The next hook procedure uses this code to determine how to process the hook information</param> /// <param name="wParam">Specifies the wParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> /// <param name="lParam">Specifies the lParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> /// <returns>The nCode, use 0 to pass the information to next hook, others to ignore the current information</returns> /// <remarks>Use for CoreHookProcess</remarks> ****************************************/ virtual LRESULT /*CALLBACK*/ HookProcess(int nCode, WPARAM wParam, LPARAM lParam); #pragma endregion };  

     

    C/C++ Code: BaseHook.cpp

    /********************************************************* {COPYRIGHT-TOP} * * RealZYC Confidential * OCO Source Materials * * (C) Copyright RealZYC Corp. 2011 All Rights Reserved. * * The source code for this program is not published or otherwise * divested of its trade secrets, irrespective of what has been * deposited with the China Copyright Office. ********************************************************** {COPYRIGHT-END} */ #include "BaseHook.h" /*************************************** *class BaseHook ****************************************/ /*************************************** * Static ****************************************/ #pragma region Static HINSTANCE BaseHook::hangle_HinstDLL = NULL; #pragma endregion /*************************************** * Property ****************************************/ #pragma region Property //Set / get the hook type BaseHook::HookTypes BaseHook::GetHookType(){return int_HookType;} void BaseHook::SetHookType(HookTypes HookType){int_HookType = HookType;} //Set / get the hook object thread id, give 0 for all thread DWORD BaseHook::GetThreadId(){return dword_ThreadId;} void BaseHook::SetThreadId(DWORD ThreadId){dword_ThreadId = ThreadId;} //Set / get whether the hook is running bool BaseHook::GetEnabled(){return ( point_HookID != NULL );} void BaseHook::SetEnabled(bool Enabled) { if(Enabled != GetEnabled()) { if(Enabled)Start(); else Stop(); } } //Set / get dll hinst HINSTANCE BaseHook::GetHinstDll() { try { return (hangle_HinstDLL != NULL)? hangle_HinstDLL: GetModuleHandle((LPCTSTR)"WinHook"); } catch(...) { return NULL; } } void BaseHook::SetHinstDll(HINSTANCE HinstDLL){hangle_HinstDLL = HinstDLL;} #pragma endregion /*************************************** * New ****************************************/ #pragma region New /*************************************** /// <summary> /// Initial function /// </summary> /// <param name="HookType">The hook type</param> /// <param name="ThreadId">The hook object thread id, give 0 for all thread</param> ****************************************/ BaseHook::BaseHook(BaseHook::HookTypes HookType, DWORD ThreadId) { point_HookID = NULL; //******************************************// //初始化委托 byte * DelegatePoint = DelegateThisCall; DelegateThisCall[0] = 0xb9; //__asm mov ecx, this DelegateThisCall[5] = 0xe9; //__asm jmp, CoreHookProcess __asm { push eax //保护eax push ebx //保护ebx mov eax, this //取得this地址 mov ebx, DelegatePoint //获取DelegateThisCall地址 mov dword ptr [ebx+1], eax //this 地址, 双字(DWORD) } __asm //计算jmp地址参考:http://zhidao.baidu.com/question/105950930.html { lea eax, CoreHookProcess //取得CoreHookProcess地址 mov ebx, DelegatePoint //获取jmp地址= DelegatePoint + 5 add ebx, 5 add ebx, 5 sub eax, ebx //JMP地址=目标地址-(本句地址+本句指令字节数) mov dword ptr [ebx-4], eax //CoreHookProcess 地址, 双字(DWORD) pop ebx //还原ebx pop eax //还原eax } //******************************************// SetHook(HookType, ThreadId); } //Dispose function BaseHook::~BaseHook(void) { SetEnabled(false); } #pragma endregion /*************************************** * Sub / Function ****************************************/ #pragma region Sub / Function /*************************************** ///<summary> /// The defination of core hook process ///</summary> ///<param name="nCode">Specifies the hook code passed to the current hook procedure. The next hook procedure uses this code to determine how to process the hook information</param> ///<param name="wParam">Specifies the wParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> ///<param name="lParam">Specifies the lParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> ///<returns>The nCode, use 0 to pass the information to next hook, others to ignore the current information</returns> ///<remarks>Use for SetWindowsHookEx</remarks> ****************************************/ LRESULT /*CALLBACK*/ BaseHook::CoreHookProcess(int nCode, WPARAM wParam, LPARAM lParam) { //调用模式:如果使用__stdcall (即加入CALLBACK关键字), // 那么this指针是通过push this来传送的, 非法调用时会产生堆栈的不平衡, 会在函数返回时出错 // 如果使用__thiscall,那么this指针是通过ecx来来传送的, 不会产生堆栈变化, 测试成功 //参考自:http://wenku.baidu.com/view/ad102ccf05087632311212ad.html LRESULT Result = this->HookProcess(nCode, wParam, lParam); //此时访问时, 由于设置了this指针,成功运行 if(Result == NULL) Result = CallNextHookEx(point_HookID, nCode, wParam, lParam); return Result; //由于注释掉CALLBACK,不会产生堆栈不平衡的问题,函数返回后无报错 } /*************************************** /// <summary> /// Set the hook data /// </summary> /// <param name="HookType">The hook type</param> /// <param name="ThreadId">The hook object thread id, give 0 for all thread</param> /// <remarks>Restart the hook after the hook data changed</remarks> ****************************************/ void BaseHook::SetHook(BaseHook::HookTypes HookType, DWORD ThreadId) { int_HookType = HookType; dword_ThreadId = ThreadId; } /*************************************** /// <summary> /// Start the hook /// </summary> /// <remarks>Check the Enabled for operation result</remarks> ****************************************/ void BaseHook::Start() { try { if(GetEnabled())SetEnabled(false); HINSTANCE hMod = NULL; if(GetThreadId() == 0) { hMod = GetHinstDll(); } else { if(GetCurrentThreadId() == GetThreadId()) { hMod = NULL; } else hMod = GetHinstDll(); } //=============================================================== //将类成员函数指针转化为一般函数指针 LRESULT (CALLBACK * PCoreHookProcess)(int nCode, WPARAM wParam, LPARAM lParam); byte * DelegatePoint = DelegateThisCall; __asm //内联汇编语句 { push eax //保护eax mov eax, DelegatePoint mov PCoreHookProcess, eax //将委托地址强制转化给PCoreHookProcess pop eax //还原eax } //=============================================================== point_HookID = SetWindowsHookEx(GetHookType(), PCoreHookProcess, hMod, GetThreadId()); } catch(...) { point_HookID = NULL; } } /*************************************** /// <summary> /// Stop the hook /// </summary> /// <remarks>Check the Enabled for operation result</remarks> ****************************************/ void BaseHook::Stop() { try { UnhookWindowsHookEx(point_HookID); } catch(...) { } point_HookID = NULL; } /*************************************** /// <summary> /// The user defined hook process /// </summary> /// <param name="nCode">Specifies the hook code passed to the current hook procedure. The next hook procedure uses this code to determine how to process the hook information</param> /// <param name="wParam">Specifies the wParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> /// <param name="lParam">Specifies the lParam value passed to the current hook procedure. The meaning of this parameter depends on the type of hook associated with the current hook chain</param> /// <returns>The nCode, use 0 to pass the information to next hook, others to ignore the current information</returns> /// <remarks>Use for CoreHookProcess</remarks> ****************************************/ LRESULT /*CALLBACK*/ BaseHook::HookProcess(int nCode, WPARAM wParam, LPARAM lParam) { return NULL; } #pragma endregion  

     

     

    Made by RealZYC

    2011-3-7

     


    最新回复(0)