如何通过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

时间: 2024-10-13 00:16:26

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

windows API中CreateWindow()函数详解

CreateWindow函数详解 在注册完窗口类后就需要进行窗口的创建,用到的函数理所当然就是CreateWindow(), 而这个函数是基于窗口类的,所以还需要指定几个参数来制定特定的窗口.而且像一些不带边框的窗口是怎么创建的也是具有相当的技巧的,就是创建的是不带标题和边框的窗口,然后自己在客户区绘制程序的内容,能够制作个性化的应用程序. API解释 该函数创建一个重叠式窗口.弹出式窗口或子窗口.它指定窗口类,窗口标题,窗口风格,以及窗口的初始位置及大小(可选的).函数也指该窗口的父窗口或所属

Windows API 编程----EnumWindows()函数的用法

1. 函数原型: BOOL WINAPI EnumWindows( _In_ WNDENUMPROC lpEnumFunc, _In_ LPARAM lParam); lpEnumFunc: 应用程序定义的回调函数的指针 lParam:         传递给回调函数的应用程序定义的值 MSDN中对EnumWindows的解释: Enumerates all top-level windows on the screen by passing the handle to each window,

windows内核Api的学习

windows内核api就是ntoskrnl.exe导出的函数.我们可以跟调用应用层的api一样,调用内核api.不过内核api需要注意的是,如果函数导出了,并且函数文档化(也就是可以直接在msdn上搜索到).ExFreePool函数导出,并且文档化,那么我们可以直接调用.导出了未文档化,那么我们就要声明.什么叫文档化和未文档化呢?大家来看一个函数: UCHAR *PsGetProcessImageFileName(IN PEPROCESS Process); 文档化:就是假设函数导出了,并且在

C语言 Windows.h 库的常用函数

Windows.h是计算机中的一个头文件.Windows.h是一个最重要的头文件,它包含了其他Windows头文件,这些头文件的某些也包含了其他头文件.这些头文件定义了Windows的所有资料型态.函数调用.资料结构和常数识别字,它们是Windows文件中的一个重要部分.C/C++ 程序在源文件前面写 #include <windows.h>即可. FindWindow函数 该函数可以通过窗口类名或者窗口标题名来查找特定窗口句柄,返回值是窗口的句柄(在Windows中,句柄是一个系统内部数据结

Windows Socket API 使用经验

本文是我在进行MS-Windows.HP-Unix网络编程的实践过程中总结出来的一些经验,仅供大家参考.本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API. 一.WSAStartup函数    int WSAStartup(      WORD wVersionRequested,        LPWSADATA lpWSAData      );    使用Socket的程序在使用Socket之前必须调用WSAStartup函数.该函数的第一个参数指明

走进windows编程的世界-----消息处理函数(3)

二 定时器消息 1 定时器消息 WM_TIMER   按照定时器设置时间段,自动向窗口发送一个定时器消息WM_TIMER. 优先级比较低.   定时器精度比较低,毫秒级别.消息产生时间也精度比较低.    2 消息和函数   2.1 WM_TIMER  - 消息ID    wParam: 定时器的ID    lParam: 定时器的处理函数 2.2 SetTimer  - 设置一个定时器 UINT SetTimer( HWND hWnd, //窗口的句柄,可以为NULL UINT nIDEven

掉坑日志:Windows Native API与DPI缩放

高DPI显示器越来越普及,软件自然也要适应这个变化,最近实习的时候也遇到了一个关于DPI缩放的问题.因为内部框架的一个控件有BUG,会导致内容的显示出问题,后来实在没办法改成了用Windows Native API来自己定义字体,但是这一写就出问题了,本来在内部开发机100%放缩下好好的,一跑到我自己的WIN10,在2K屏放上缩放125%就字体就显示不正常了(字体变得过大). Window Vista以后的系统可以直接来个SetProcessDpiAwareness来控制程序的DPI问题,但是这

Windows录音API学习笔记--转

Windows录音API学习笔记 结构体和函数信息  结构体 WAVEINCAPS 该结构描述了一个波形音频输入设备的能力. typedef struct { WORD      wMid; 用于波形音频输入设备的设备驱动程序制造商标识符. WORD      wPid; 声音输入设备的产品识别码. MMVERSION vDriverVersion; 用于波形音频输入设备的设备驱动程序的版本号.高位字节是主版本号,低字节是次版本号. CHAR      szPname[MAXPNAMELEN];

走进windows编程的世界-----消息处理函数(2)

一 WM_PAINT消息 1 WM_PAINT的产生   由于窗口的互相覆盖等,产生需要绘制的区域,那么会产生WM_PAINT消息.   一般情况下,不直接发送WM_PAINT消息,通过API声明需要绘制区域,来产生WM_PAINT消息.   例如,可以使用InvalidateRect声明一个需要重新绘制的区域.    2 WM_PAINT的注意点    2.1 如果一个消息队列中,有多个WM_PAINT消息,只有最后一个WM_PAINT消息会被处理.    2.2 WM_PAINT消息处理中,