以前CSDN上有过另一个HookApi的办法, 我贴在下面, 看完后请大家帮忙回答这样的问题:
  为什么必须有一个Dll才行呢? 我曾试图将Dll和测试程序中的诸函数写到同一个程序中, 发现只是在这一个程序中HookApi起作用, 其它的MessageBox照旧, 这是什么原因呢? 难道User32.dll在内存中不仅有一个实例? 我查看内存发现在我的程序中MessageBoxA的入口处被修改了, 而在别的程序中仍是原来的, 这让我不明所以.
  请各位不吝键盘, up有分.

解决方案 »

  1.   

    以下转:
    -----------------------------------------------------
    smhpnuaa(2002-7-18 14:56:13)  这么多高手在这里,哎,小弟愿意向各位高手学习 
      Api拦截并不是一个新的技术,很多商业软件都采用这种技术。对windows的Api函数的拦截,不外乎两种方法,第一种是Mr. Jeffrey Richter 的修改exe文件的模块输入节,种方法,很安全,但很复杂,而且有些exe文件,没有Dll的输入符号的列表,有可能出现拦截不到的情况。第二种方法就是常用的JMP XXX的方法,虽然很古老,却很简单实用。 
        本文一介绍第二种方法在Win2k下的使用。第二种方法,Win98/me 下因为进入Ring0级的方法很多,有LDT,IDT,Vxd等方法,很容易在内存中动态修改代码,但在Win2k下,这些方法都不能用,写WDM太过复杂,表面上看来很难实现, 
    其实不然。Win2k为我们提供了一个强大的内存Api操作函数---VirtualProtectEx,WriteProcessMemeory,ReadProcessMemeory,有了它们我们就能在内存中动态修改代码了,其原型为: 
            BOOL VirtualProtectEx( 
                                  HANDLE hProcess,    // 要修改内存的进程句柄 
                                  LPVOID lpAddress,    // 要修改内存的起始地址 
                                  DWORD dwSize,        // 修改内存的字节 
                                  DWORD flNewProtect,  // 修改后的内存属性 
                                  PDWORD lpflOldProtect  // 修改前的内存属性的地址 
                                    ); 
            BOOL WriteProcessMemory( 
                                  HANDLE hProcess,  // 要写进程的句柄 
                                  LPVOID lpBaseAddress,  // 写内存的起始地址 
                                  LPVOID lpBuffer,  // 写入数据的地址 
                                  DWORD nSize,      // 要写的字节数 
                                  LPDWORD lpNumberOfBytesWritten  // 实际写入的子节数 
                                  ); 
          BOOL ReadProcessMemory( 
                                  HANDLE hProcess,  // 要读进程的句柄 
                                  LPCVOID lpBaseAddress,  // 读内存的起始地址 
                                  LPVOID lpBuffer,  // 读入数据的地址 
                                  DWORD nSize,      // 要读入的字节数 
                                  LPDWORD lpNumberOfBytesRead    // 实际读入的子节数 
                                    ); 
    具体的参数请参看MSDN帮助。在Win2k下因为Dll和所属进程在同一地址空间,这点又和Win9x/me存在所有进程存在共享的地址空间不同, 
    因此,必须通过钩子函数和远程注入进程的方法,现以一个简单采用钩子函数对MessageBoxA进行拦截例子来说明: 
    其中Dll文件为: 
              HHOOK g_hHook; 
              HINSTANCE g_hinstDll; 
              FARPROC pfMessageBoxA; 
              int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption,UINT uType); 
              BYTE OldMessageBoxACode[5],NewMessageBoxACode[5]; 
              HMODULE hModule ; 
              DWORD dwIdOld,dwIdNew; 
              BOOL bHook=false; 
              void HookOn(); 
              void HookOff(); 
              BOOL init(); 
    LRESULT WINAPI MousHook(int nCode,WPARAM wParam,LPARAM lParam); 
    BOOL APIENTRY DllMain( HANDLE hModule, 
                          DWORD  ul_reason_for_call, 
                          LPVOID lpReserved 
                        ) 

        switch (ul_reason_for_call) 
        { 
            case DLL_PROCESS_ATTACH: 
                if(!init()) 
                { 
                              MessageBoxA(NULL,"Init","ERROR",MB_OK); 
                              return(false); 
                } 
            case DLL_THREAD_ATTACH: 
            case DLL_THREAD_DETACH: 
            case DLL_PROCESS_DETACH: 
                          if(bHook) UnintallHook();  
                        break; 
        } 
        return TRUE; 

    LRESULT WINAPI Hook(int nCode,WPARAM wParam,LPARAM lParam)//空的钩子函数 

        
        return(CallNextHookEx(g_hHook,nCode,wParam,lParam)); 

    HOOKAPI2_API BOOL InstallHook()//输出安装空的钩子函数 
    {  
      g_hinstDll=LoadLibrary("HookApi2.dll"); 
      g_hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,g_hinstDll,0); 
      if (!g_hHook) 
      { 
            MessageBoxA(NULL,"SET ERROR","ERROR",MB_OK); 
            return(false); 
      } 
      
              
      return(true); 

    HOOKAPI2_API BOOL UninstallHook()//输出御在钩子函数 

      
        return(UnhookWindowsHookEx(g_hHook)); 
    } BOOL init()//初始化得到MessageBoxA的地址,并生成Jmp XXX(MyMessageBoxA)的跳转指令 

        hModule=LoadLibrary("user32.dll"); 
        pfMessageBoxA=GetProcAddress(hModule,"MessageBoxA"); 
        if(pfMessageBoxA==NULL) 
          return false; 
        _asm 
        { 
            lea edi,OldMessageBoxACode 
            mov esi,pfMessageBoxA 
            cld 
            movsd 
            movsb 
        } 
        NewMessageBoxACode[0]=0xe9;//jmp MyMessageBoxA的相对地址的指令 
        _asm 
        { 
            lea eax,MyMessageBoxA 
            mov ebx,pfMessageBoxA 
            sub eax,ebx 
            sub eax,5 
            mov dword ptr [NewMessageBoxACode+1],eax 
        } 
        dwIdNew=GetCurrentProcessId(); //得到所属进程的ID 
        dwIdOld=dwIdNew; 
        HookOn();//开始拦截 
        return(true); 
    } int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType )//首先关闭拦截,然后才能调用被拦截的Api 函数 
    {  
        int nReturn=0; 
        HookOff(); 
        nReturn=MessageBoxA(hWnd,"Hook",lpCaption,uType); 
        HookOn(); 
        return(nReturn); 

    void HookOn() 

        HANDLE hProc; 
        dwIdOld=dwIdNew; 
        hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);//得到所属进程的句柄 
        VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);//修改所属进程中MessageBoxA的前5个字节的属性为可写 
        WriteProcessMemory(hProc,pfMessageBoxA,NewMessageBoxACode,5,0);//将所属进程中MessageBoxA的前5个字节改为JMP 到MyMessageBoxA 
        VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);//修改所属进程中MessageBoxA的前5个字节的属性为原来的属性 
        bHook=true; 

    void HookOff()//将所属进程中JMP MyMessageBoxA的代码改为Jmp MessageBoxA 

        HANDLE hProc; 
        dwIdOld=dwIdNew; 
        hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld); 
        VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld); 
        WriteProcessMemory(hProc,pfMessageBoxA,OldMessageBoxACode,5,0); 
        VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld); 
        bHook=false; 

    //测试文件: 
    int APIENTRY WinMain(HINSTANCE hInstance, 
                        HINSTANCE hPrevInstance, 
                        LPSTR    lpCmdLine, 
                        int      nCmdShow) 

        
        if(!InstallHook()) 
        { 
            MessageBoxA(NULL,"Hook Error!","Hook",MB_OK); 
            return 1; 
        } 
        MessageBoxA(NULL,"TEST","TEST",MB_OK);//可以看见Test变成了Hook,也可以在其他进程中看见 
        if(!UninstallHook()) 
        { 
            MessageBoxA(NULL,"Uninstall Error!","Hook",MB_OK); 
            return 1; 
        } 
        return 0; 

     
      

  2.   

    大概啊。因为只有dll函数材能注入其它进程德空间,你的和序不可能注入其它进程德空间吧。
      

  3.   

    可是在我的那个合二为一的程序中是起作用的呀,只是在别的程序中不行。
    另外,在我的程序中看起来的确是将代码注入到User32.dll中了,为什么rujor(rujor)说“你的和序不可能注入其它进程德空间吧”呢?dll和我的程序有什么区别呢?
      

  4.   

    学习
    ================================================================我是anothervip
      

  5.   

    一般是进程内hook在程序内,进程外hook要做成动态库。
    ================================================================
    好好学习,天天向上
    ================================================================
      

  6.   

    exe能被轻松注入到其它进程里去吗?
    dll可以的阿~
      

  7.   

    事实上我并没有用到钩子呀。我的程序是这样的:
    1、建立对话框程序
    2、添加两个按钮BtnInit和Button1
    3、加入以下代码:
    FARPROC pfMessageBoxA; 
    int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption,UINT uType); 
    BYTE OldMessageBoxACode[5],NewMessageBoxACode[5]; 
    HMODULE hModule ; 
    DWORD dwIdOld,dwIdNew; 
    BOOL bHook=false; void HookOn(); 
    void HookOff(); 
    BOOL init(); 
    int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType );BOOL init()//初始化得到MessageBoxA的地址,并生成Jmp XXX(MyMessageBoxA)的跳转指令 

        hModule=LoadLibrary("user32.dll"); 
        pfMessageBoxA=GetProcAddress(hModule,"MessageBoxA");
        if(pfMessageBoxA==NULL) 
    return false; 
        _asm 
        { 
            lea edi,OldMessageBoxACode 
            mov esi,pfMessageBoxA 
            cld 
            movsd 
            movsb 
        } 
        NewMessageBoxACode[0]=0xe9;//jmp MyMessageBoxA的相对地址的指令 
        _asm 
        { 
            lea eax,MyMessageBoxA 
            mov ebx,pfMessageBoxA 
            sub eax,ebx 
            sub eax,5 
            mov dword ptr [NewMessageBoxACode+1],eax 
        } 
        //dwIdNew=GetCurrentProcessId(); //得到所属进程的ID 
        //dwIdOld=dwIdNew; 
        dwIdOld=GetCurrentProcessId(); //得到所属进程的ID 
        HookOn();//开始拦截 
        return(true); 
    } int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType )//首先关闭拦截,然后才能调用被拦截的Api 函数 
    {  
        int nReturn=0; 
        HookOff(); 
    // nReturn=MessageBoxA(hWnd,"Hook",lpCaption,uType); 
    nReturn=MessageBoxA(hWnd,"HHHHHHHHook","Hahahaha",uType); 
        HookOn(); 
        return(nReturn); 

    void HookOn() 

        HANDLE hProc; 
    DWORD dwOldProtect;
        //dwIdOld=dwIdNew; 
        hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);//得到所属进程的句柄 
        VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwOldProtect);//修改所属进程中MessageBoxA的前5个字节的属性为可写 
        WriteProcessMemory(hProc,pfMessageBoxA,NewMessageBoxACode,5,0);//将所属进程中MessageBoxA的前5个字节改为JMP 到MyMessageBoxA 
        VirtualProtectEx(hProc,pfMessageBoxA,5,dwOldProtect,&dwOldProtect);//修改所属进程中MessageBoxA的前5个字节的属性为原来的属性 
        bHook=true; 

    void HookOff()//将所属进程中JMP MyMessageBoxA的代码改为Jmp MessageBoxA 

        HANDLE hProc; 
    DWORD dwOldProtect;
        //dwIdOld=dwIdNew; 
        hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld); 
        VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwOldProtect); 
        WriteProcessMemory(hProc,pfMessageBoxA,OldMessageBoxACode,5,0); 
        VirtualProtectEx(hProc,pfMessageBoxA,5,dwOldProtect,&dwOldProtect); 
        bHook=false; 
    } void CXXXDlg::OnButton1() 
    {
        ::MessageBoxA(NULL,"TEST","TEST",MB_OK);
    }void CXXXDlg::OnBtnInit() 
    {
    init();
    }
    4、运行。点BtnInit来初始化,然后点Button1可以看到对话框变了。
    5、运行别的程序(含有对话框的),发现对话框没变。
      

  8.   

    哦,还说没有,你hook的时时候,修改了函数的入点改为jmp,呵呵,你jmp的函数在你自已的程序中,人家怎么jmp过来?在dll中,人家可以load进来,当然dll也是可以共享的嘛,你看了windows核心编程了吗?
      

  9.   

    做成DLL是因为需要把DLL注入其他进程的内存空间并修改其它进程的DLL代码,因为WinNT/2000的内存保护方式不一样: Win9x 系统中,系统DLL被装入实际的物理存储器,然后映射到每个进程的0x80000000~0xBFFFFFFF共享内存区,如果修改这段区域的DLL代码,则对于所有进程都有效(实际Win98对主要的系统DLL作了保护,除非进入ring0才能修改)。Win2000/NT 的进程空间不存在共享内存区,尽管DLL被装入之初在实际的物理存储器只有一份拷贝,但是Win2000/NT对DLL映射的内存页面采用copy-on-write保护机制,如果任何进程试图写入DLL所在的页面,系统将在实际的物理存储器创建一份该页面的拷贝,然后修改该进程的地址映射,使之指向拷贝的页面,这样进程对DLL页面的修改将仅仅在此进程有效。
      

  10.   

    to rujor(rujor) :
      别的程序即使不能jmp到我的程序中,至少函数入口处我是修改了的呀,可是我在调试别的程序时观察内存,发现函数入口处根本不是我的E9XXXXXXXX,仍然是原来的代码。这是为什么呢?
      

  11.   

    to In355Hz(好象一条狗):
      刚刚看到你的回复,好像明白了许多。但是,如你所说,做成DLL去修改内存内容也应该是修改的一份拷贝呀,为什么却能起到作用呢?
      

  12.   

    因为hook的时候dll映射到其它进程德空间吧,In355Hz(好象一条狗) 说过了啊,这样dll修改就是修改的映射的进程的吧?
      

  13.   

    你的程序在2000/NT下~
    你没有修改别的程序的代码~
    你只是改了自己程序的代码~
    而做成dll使用了Hook就不一样了~
    把你的修改程序注入到各个进程的地址空间~
    就可以随心所欲的修改了~
    在98下由于机制不一样~
    就不一定了~
      

  14.   

    我的意思是,我用做成的DLL去修改User32.dll,是仅仅修改了一份拷贝呢?还是真的把物理内存的内容修改了?
      

  15.   

    学习怎么oldworm没来发表看法?
      

  16.   

    参考MSDN:
    Copy on Write Page Protection for Windows NT(Q103858)WriteProcessMemory所修改的pfMessageBoxA页面不是原始的DLL页面,而是原始页面的拷贝。这个拷贝映射到了本进程的pfMessageBoxA地址上,而其他的进程pfMessageBoxA的地址还是映射的原始页面。
      

  17.   

    我是这样认为的, 你每建一个进程,windows就copy一份需要的dll,所以每个进程的dll都是相对独立的, 即相互的数据互不影响, 所以你的程序修改的只是你的程序自己对应的dll, 如果做成dll那这个dll就可以被注入到每个程序中,但是这并不代表每个程序对应的dll就可以相互通信, 你需要再你的dll中开辟一个共享的内存区域, 在你要拦截的程序里用hook把消息拦截下来, 把需要的数据保存在共享的内存区域中, 然后用自己的程序把那个共享的内存区域中的数据弄出来, 然后加以修改存回那个共享区域, 你的dll就可以把那个数据送到它原来去的地方,这样就起到了拦截的作用.
    以上只是我对于hook的看法(再Win 2k中),小弟也是初学者, 如有什么地方说错了,还希望各位大虾纠正,先谢谢了.