首页 > 代码库 > 如何通过HOOK改变windows的API函数(找到函数的相对偏移)

如何通过HOOK改变windows的API函数(找到函数的相对偏移)

我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,应首先把该DLL加载到当前的进程空间中,调用的系统函数的入口地址,可以通过GetProcAddress函数进行获取。当系统函数进行调用的时候,首先把所必要的信息保存下来(包括参数和返回地址,等一些别的信息),然后就跳转到函数的入口地址,继续执行。其实函数地址,就是系统函数“可执行代码”的开始地址。那么怎么才能让函数首先执行我们的函数呢?呵呵,应该明白了吧,把开始的那段可执行代码替换为我们自己定制的一小段可执行代码,这样系统函数调用时,不就按我们的意图乖乖行事了吗?其实,就这么简单。Very   very简单。   :P.实际的说,就可以修改系统函数入口的地方,让他调转到我们的函数的入口点就行了。采用汇编代码就能简单的实现Jmp   XXXX,   其中XXXX就是要跳转的相对地址。   
    
  我们的做法是:把系统函数的入口地方的内容替换为一条Jmp指令,目的就是跳到我们的函数进行执行。而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异,再减去我们这条指令的大小。用公式表达如下:   
    
  int   nDelta   =   UserFunAddr   –   SysFunAddr   -   (我们定制的这条指令的大小);   
    
  Jmp   nDleta;   
    
  为了保持原程序的健壮性,我们的函数里做完必要的处理后,要回调原来的系统函数,然后返回。所以调用原来系统函数之前必须先把原来修改的系统函数入口地方给恢复,否则,系统函数地方被我们改成了Jmp   XXXX就会又跳到我们的函数里,死循环了。   
    
  那么说一下程序执行的过程。   
    
  我们的dll“注射”入被hook的进程   ->   保存系统函数入口处的代码   ->   替换掉进程中的系统函数入口指向我们的函数   ->   当系统函数被调用,立即跳转到我们的函数   ->   我们函数进行处理   ->   恢复系统函数入口的代码   ->   调用原来的系统函数   ->   再修改系统函数入口指向我们的函数(为了下次hook)->   返回。   
    
  于是,一次完整的Hook就完成了。   
    
    
    
  好,这个问题明白以后,讲一下下个问题,就是如何进行dll“注射”?即将我们的dll注射到要Hook的进程中去呢?   
    
  很简单哦,这里我们采用调用Windows提供给我们的一些现成的Hook来进行注射。举个例子,鼠标钩子,键盘钩子,大家都知道吧?我们可以给系统装一个鼠标钩子,然后所有响应到鼠标事件的进程,就会“自动”(其实是系统处理了)载入我们的dll然后设置相应的钩子函数。其实我们的目的只是需要让被注射进程载入我们的dll就可以了,我们可以再dll实例化的时候进行函数注射的,我们的这个鼠标钩子什么都不干的。   
    
    
    
    
    
  4简单的例子OneAddOne   
    
  讲了上面的原理,现在我们应该实战一下了。先不要考虑windows系统那些繁杂的函数,我们自己编写一个API函数来进行Hook与被Hook的练习吧,哈哈。   
    
    
    
  ////////////////////   
    
  第一步,首先编写一个add.dll,很简单,这个dll只输出一个API函数,就是add啦。   
    
  新建一个win32   dll工程,   
    
  add.cpp的内容:   
    
    
    
  #i   nclude   "stdafx.h"   
    
    
    
  int   WINAPI   add(int   a,int   b){     file://千万别忘记声明WINAPI   否则调用的时候回产生声明错误哦!   
    
    return   a+b;   
    
  }   
    
    
    
  BOOL   APIENTRY   DllMain(   HANDLE   hModule,   
    
                                                DWORD     ul_reason_for_call,   
    
                                                LPVOID   lpReserved   
    
              )   
    
  {   
    
          return   TRUE;   
    
  }   
    
    
    
    
    
  然后别忘了在add.def里面输出函数:   
    
    
    
  LIBRARY     Add   
    
  DESCRIPTION   "ADD   LA"   
    
  EXPORTS   
    
    add     @1;   
    
    
    
  编译,ok,我们获得了add.dll   
    
    
    
  ////////////////////   
    
  第二步,编写1+1主程序   
    
  新建一个基于对话框的工程One,   
    
  在   OnOK()里面调用add函数:   
    
  ConeDlg.h里面加入一些变量的声明:   
    
  ….   
    
  Public:   
    
    HINSTANCE   hAddDll;   
    
    typedef   int   (WINAPI*AddProc)(int   a,int   b);   
    
    AddProc   add;   
    
  …   
    
    
    
  ConeDlg.cpp里进行调用:   
    
    
    
  void   COneDlg::OnOK()   
    
  {   
    
    //   TODO:   Add   extra   validation   here   
    
    if   (hAddDll==NULL)   
    
      hAddDll=::LoadLibrary("add.dll");   
    
    
    
    add=(AddProc)::GetProcAddress(hAddDll,"add");   
    
    
    
    int   a=1;   
    
    int   b=2;   
    
    int   c=add(a,b);   
    
    CString   temp;   
    
    temp.Format("%d+%d=%d",a,b,c);   
    
    AfxMessageBox(temp);   
    
  }   
    
    
    
  OK,编译运行,正确的话就会显示1+2=3咯   
    
  ////////////////////   
    
  第3步,要动手Hook咯,爽阿   
    
  新建一个MFC的dll工程,Hook   
    
  在Hook.dll工程里:   
    
    
    
  添加一个鼠标Hook   MouseProc,鼠标hook什么也不做   
    
  LRESULT   CALLBACK   MouseProc(int   nCode,WPARAM   wParam,LPARAM   lParam)   
    
  {   
    
    LRESULT   RetVal=   CallNextHookEx(hhk,nCode,wParam,lParam);   
    
    return   RetVal;   
    
  }   
    
    
    
  添加鼠标钩子的安装和卸载函数:   
    
  BOOL   InstallHook()   
    
  {   
    
    
    
    hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);   
    
    
    
    ….   
    
    return   true;   
    
  }   
    
    
    
  void   UninstallHook()   
    
  {   
    
    ::UnhookWindowsHookEx(hhk);   
    
  }   
    
    
    
  再实例化中获得一些参数   
    
  BOOL   CHookApp::InitInstance()   
    
  {   
    
    获得dll   实例,进程句柄   
    
  hinst=::AfxGetInstanceHandle();   
    
    DWORD   dwPid=::GetCurrentProcessId();   
    
    hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);   
    
  调用注射函数   
    
    Inject();   
    
    return   CWinApp::InitInstance();   
    
  }   
    
    
    
  好,最重要的注射函数:   
    
  void   Inject()   
    
  {   
    
    
    
    if   (m_bInjected==false)   
    
    {   保证只调用1次   
    
      m_bInjected=true;   
    
    
    
      获取add.dll中的add()函数   
    
      HMODULE   hmod=::LoadLibrary("add.dll");   
    
      add=(AddProc)::GetProcAddress(hmod,"add");   
    
      pfadd=(FARPROC)add;   
    
    
    
      if   (pfadd==NULL)   
    
      {   
    
        AfxMessageBox("cannot   locate   add()");   
    
      }   
    
    
    
      //   将add()中的入口代码保存入OldCode[]   
    
      _asm   
    
      {   
    
        lea   edi,OldCode   
    
        mov   esi,pfadd   
    
        cld   
    
        movsd   
    
        movsb   
    
      }   
    
    
    
      NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令   
    
      //获取Myadd()的相对地址   
    
      _asm   
    
      {   
    
        lea   eax,Myadd   
    
        mov   ebx,pfadd   
    
        sub   eax,ebx   
    
        sub   eax,5   
    
        mov   dword   ptr   [NewCode+1],eax   
    
      }   
    
      //填充完毕,现在NewCode[]里的指令相当于Jmp   Myadd   
    
      HookOn();   //可以开启钩子了   
    
    }   
    
  }   
    
    
    
  开启钩子的函数   
    
  void   HookOn()   
    
  {   
    
    ASSERT(hProcess!=NULL);   
    
    
    
    DWORD   dwTemp=0;   
    
    DWORD   dwOldProtect;   
    
    
    
  //将内存保护模式改为可写,老模式保存入dwOldProtect   
    
    VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);   
    
            //将所属进程中add()的前5个字节改为Jmp   Myadd   
    
    WriteProcessMemory(hProcess,pfadd,NewCode,5,0);   
    
    //将内存保护模式改回为dwOldProtect   
    
    VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);   
    
    
    
    bHook=true;   
    
  }   
    
    
    
  关闭钩子的函数   
    
  void   HookOff()//将所属进程中add()的入口代码恢复   
    
  {   
    
    ASSERT(hProcess!=NULL);   
    
    
    
    DWORD   dwTemp=0;   
    
    DWORD   dwOldProtect;   
    
    
    
    VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);   
    
    WriteProcessMemory(hProcess,pfadd,OldCode,5,0);   
    
    VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);   
    
    bHook=false;   
    
  }   
    
    
    
  然后,写我们自己的Myadd()函数   
    
  int   WINAPI   Myadd(int   a,int   b)   
    
  {   
    
    //截获了对add()的调用,我们给a,b都加1   
    
    a=a+1;   
    
    b=b+1;   
    
    
    
    HookOff();//关掉Myadd()钩子防止死循环   
    
    
    
    int   ret;   
    
    ret=add(a,b);   
    
    
    
    HookOn();//开启Myadd()钩子   
    
    
    
    return   ret;   
    
  }   
    
    
    
  然后别忘记在hook.def里面输出   
    
  InstallHook   
    
    MouseProc   
    
    Myadd   
    
    UninstallHook   
    
  四个函数。   
    
    
    
    
  好到这里基本上大功告成咯   
    
    
    
  ////////////////////   
    
    
    
  第4步,我们就可以修改前面的One的测试程序了   
    
    
    
  增加一个安装钩子的函数/按钮   
    
  void   COneDlg::doHook()   
    
  {   
    
    hinst=LoadLibrary("hook.dll");   
    
    if(hinst==NULL)   
    
    {   
    
      AfxMessageBox("no   hook.dll!");   
    
      return;   
    
    }   
    
    typedef   BOOL   (CALLBACK   *inshook)();   
    
    inshook   insthook;   
    
    
    
    insthook=::GetProcAddress(hinst,"InstallHook");   
    
    if(insthook==NULL)   
    
    {   
    
      AfxMessageBox("func   not   found!");   
    
      return;   
    
    }   
    
    
    
    DWORD   pid=::GetCurrentProcessId();   
    
    BOOL   ret=insthook();   
    
  }   
    
    
    
  别忘了退出时卸掉钩子   
    
  void   COneDlg::OnCancel()   
    
  {   
    
    //   TODO:   Add   extra   cleanup   here   
    
    typedef   BOOL   (CALLBACK   *UnhookProc)();   
    
    UnhookProc   UninstallHook;   
    
    
    
    UninstallHook=::GetProcAddress(hinst,"UninstallHook");   
    
    if(UninstallHook==NULL)   UninstallHook();   
    
    if   (hinst!=NULL)   
    
    {   
    
      ::FreeLibrary(hinst);   
    
    }   
    
    if   (hAddDll!=NULL)   
    
    {   
    
      ::FreeLibrary(hAddDll);   
    
    }   
    
    CDialog::OnCancel();   
    
  }   

 

http://blog.csdn.net/jiangxinyu/article/details/5385821

如何通过HOOK改变windows的API函数(找到函数的相对偏移)