Windows挂钩的简单使用

原贴地址:http://blog.sina.com.cn/s/blog_56dee71a010007h1.html

 

最近手机上的短信存储器快满了,应该删除一些短信以留出一些空间,但是有好多短信是各个MM发过来的,舍不得就这么删除了,想导出到电脑里面保存起来。万一哪天MM成了我女朋友了,有机会的时候可以给她看看,说明我是这么珍惜跟她相关的点点滴滴。^_^于是用数据线把手机连接到电脑上,打开EasyGPRS软件,读取手机中的短信到列表窗口中。但是可惜的是EasyGPRS软件没有提供导出短信内容的功能,于是只好自己想办法了。当然最简单的方法是,把各条短信的接收时间,对方号码,内容等信息在电脑上输入一遍,保存到文件中。但是近200条短信,我可以没有那个耐心。我当然得想跟简单快捷的方法。我想到的方法就是,写一个程序,把EasyGPRS已经从手机中读取的短信内容,从列表窗口复制并保存到一个文件中。想到了就做!原本以为比较简单的东西,没想到也花了我6,7个小时时间,当然是晚上下班后和早晨上班前的空余时间。:)
   先对照程序界面说说其基本功能:


 
    点击并拖动查找工具图标到EasyGPRS软件界面上的短信列表窗口上,释放图标,程序会把短信列表的内容复制到自己的列表窗口中;在窗口中央的格式文本框中输入导出的格式;点击【导出】按钮,程序就按照指定的格式(当然只是比较简单的格式定义)将短信内容输出到下面的文本框中,并复制到剪贴板上;这时打开一个文本编辑软键,如记事本,执行粘贴命令后保存就可以了。复制短信内容并执行导出后的界面如下(事关个人隐私,我不得不做了点简单的处理^_^):
 
 
    写这个程序的难点,和本文的重点就在于,怎么把另一个程序(进程)中一个列表控件中内容,复制到自己的程序(进程)中?我采用了Windows挂钩的实现方式。下面就依编程的顺序进行讲述。
    要将另一个进程中的列表控件的内容复制到自己的进程中,当然首先得找到这个列表控件了,方法比较简单,按下鼠标左键,拖动窗口上的查找工具图标到目标列表窗口上,释放鼠标键就可以了。从程序实现上来讲,在查找工具图标上按下鼠标左键时,开始鼠标捕获;移动鼠标时,需要在当前鼠标位置下的窗口边框上绘制一个矩形框,表示当前的目标窗口,方法是使用 WindowFromPoint()函数取得鼠标光标下的窗口句柄,使用GetWindowRect()取得其相对于屏幕坐标系的矩形,使用GetDC(NULL)取得屏幕设备描述表,使用Rectangle()在目标窗口上绘制外边框;释放鼠标键时,停止鼠标捕捉。
   找到列表控件窗口后,就可以向它发送消息取得其列数,行数和各行数据了,我刚开始时就是这么做的。但是,发送消息取得列数和行数是正确的,却不能取得各行的数据。这是为什么呢?原来,Windows编程中,允许跨越进程边界向另一个进程中的窗口发送消息(跨进程发送消息当然也是跨线程发送消息,处理方式同在线程内部发送消息的处理方式有一些不同,详细情况可以参考《Windows核心编程》第26章《窗口消息》)。但是,跨进程边界发送的消息不能带有指针类型的参数,因为 指针不能跨越进程边界。在Windows系统中,各个进程是相互隔离的,有自己的独立地址空间,一个进程中有效的指针,传递到另一个进程中不一定有效;即使有效,它也是指向另一个进程的地址空间中的某个位置了,负责处理消息的目标窗口(线程)会从那个位置取得输入参数,或者将处理结果存放到那里去,发送消息的进程无法正确地传递消息参数,或者利用指针取得消息处理时返回的结果。跨进程传递指针需要特殊的处理,例如使用COM+,或者Java RMI等分布式处理系统。
    由于需要一个指针作为其参数,所以不能用普通的SendMessage()函数向另一个进程中的列表控件发送消息,取得控件的某个项目的内容。那么如何解决这个问题,取得另一个进程中的列表控件的内容呢?我想到了前段时间学习的《Windows核心编程》一书中讲到过的高级DLL操作技术,是否可以用某种DLL技术解决这个问题呢?赶紧找出书来,翻开第22章《插入DLL和挂接API》,再次阅读第3节《使用Windows挂钩来插入DLL》,我就找到解决办法了。作者在这一节介绍了使用windows挂钩在某进程中插入DLL的方法,并给出了实际的应用示例,即用Windows挂钩向资源管理器进程Explorer.exe插入DLL,来实现保存和恢复桌面图标位置的功能。我就稍微变通一下,使用Windows挂钩向EasyGPRS进程插入DLL,来实现取得EasyGPRS进程中的短信列表窗口内容的功能。
    首先简单介绍以下设置Windows挂钩的函数SetWindowsHookEx:
 

HHOOK SetWindowsHookEx( int idHook,

                        HOOKPROC lpfn,

                        HINSTANCE hMod,

                        DWORD dwThreadId );

 参数的简单说明:
     idHook 挂钩的种类,在我这个程序里面使用的是 WH_GETMESSAGE标志,表示安装一个监测投递到目标线程的消息的挂钩
     lpfn  挂钩函数,一个应用程序定义的回调函数,当挂钩截取到消息时,系统将调用此函数
     hMod  包含挂钩函数的DLL模块句柄
     dwThreadId  要安装挂钩的目标线程
   Windows挂钩函数必须是包含在Dll中的。因为挂钩截取到消息时,系统使用目标线程调用挂钩函数,而安装挂钩的线程可能和目标线程不在同一个进程中,不共享同一个地址空间。这样,目标线程没办法执行位于另一个进程中的挂钩函数,因为即使知道挂钩函数的入口地址,函数的可执行代码也是在另一个进程中的。Windows要求挂钩函数位于Dll中,这样,因为Dll可以被映射到不同的进程中。安装挂钩的时候,Windows可以将原本已经映射到源进程的Dll,再映射到目标进程中。这样,位于目标进程中的目标线程就可以调用挂钩函数了。需要注意的是:挂钩函数在两个进程中的入口地址很可能是不一样的,因为包含它的Dll的在不同进程中被映射到的地址很可能是不一样的。
     安装挂钩函数后,系统在截取到相应类型的消息时,就会利用目标线程调用挂钩函数,这时候就可以直接用SendMessage()函数向目标列表控件发送消息,取得其某一行的内容了。但是问题是,取得的内容是在目标进程中的,还是无法传递到源进程中。我对这个问题的解决方法是在Dll中设置一个带共享属性的节,把取得的内容放到共享节中。这样目标线程取得内容后,源进程也可以通过位于共享节的变量得到内容。注意:这里共享节是位于Dll中的,而Dll是被同时映射到两个进程中的。
    最后一个问题是:源线程和目标线程需要以某种方式来进行通信和同步它们的操作。源线程必须告诉目标线程,要取得哪个列表窗口的哪一行的内容;目标线程了解了这个信息之后,执行挂钩函数,取得列表项内容,之后源线程才能利用共享节定义的变量,将列表项内容复制到自己的列表中。我采用的通信方式是线程消息和事件。
    写了这么多,才发现自己的讲述比较乱,好像没太多条理,这里简单总结一下。我要解决的问题是,把另一个程序中的一个列表窗口的内容,复制自己的程序中。因为跨进程发送消息时,消息参数不能含有指针,所以不能简单地用SendMessage()发送相关消息给另一个程序中的列表窗口,来取得其内容。最终对这个问题的解决涉及到3个方面:
1 利用Windows挂钩机制,将含有挂钩函数的dll插入到目标进程。目标线程在调用挂钩函数时,可以简单地用SendMessage()函数取得列表项内容。
2 目标线程取得列表项内容后,如何传回源线程。我采用的方法是利用dll中的共享数据节。
3 对于目标线程和源线程的通信和同步,我采用了线程消息和事件的机制。
 
    现在该看具体的代码了。这是含有挂钩函数的dll程序代码:
  1    #include <windows.h>
2    #include <CommCtrl.h>
3    #include <shlwapi.h>
4   
5    #define UM_GET_COL_INFO   (WM_USER + 0x1000)
6    #define UM_GET_ITEM_INFO  (WM_USER + 0x2000)
7   
8    #pragma comment(lib,"shlwapi.lib")
9    #pragma comment(lib,"comctl32.lib")
10  
11   #pragma data_seg("Shared")
12   HHOOK    g_hhook=NULL;
13   extern "C" __declspec(dllexport) LVCOLUMN g_colinfo={0};
14   extern "C" __declspec(dllexport) LVITEM   g_iteminfo={0};
15   extern "C" __declspec(dllexport) TCHAR    g_szText[500]={0};
16   #pragma data_seg()
17  
18   #pragma comment(linker,"/section:Shared,rws")
19  
20   static HINSTANCE g_hDll;
21  
22   BOOL WINAPI DllMain(HINSTANCE hInst,DWORD fdwReason,PVOID fImpLoad)
23   {
24      switch(fdwReason)
25      {
26      case DLL_PROCESS_ATTACH:
27          g_hDll = hInst;
28          break;
29      }
30      return TRUE;
31   }
32  
33   static LRESULT WINAPI  GetMsgProc(int ncode,WPARAM wParam,LPARAM lParam)
34   {
35      MSG* pMsg;
36      pMsg = (MSG*)lParam;
37      /* 取得列(ListView头部)信息 */
38      if (UM_GET_COL_INFO == pMsg->message)
39      {
40          HWND hList = (HWND)(pMsg->wParam);
41          HANDLE hEvent;
42  
43          ZeroMemory(g_szText,sizeof(g_szText));
44          g_colinfo.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
45          g_colinfo.fmt  = LVCFMT_CENTER;
46          g_colinfo.pszText = g_szText;
47          g_colinfo.cchTextMax = 500;
48     
49          ListView_GetColumn(hList,pMsg->lParam,&g_colinfo);
50  
51          hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,"MsgProcessed");
52          SetEvent(hEvent);
53          CloseHandle(hEvent);
54      }
55      /* 取得数据项信息 */
56      else if (UM_GET_ITEM_INFO == pMsg->message)
57      {
58          HWND hList = (HWND)(pMsg->wParam);
59          HANDLE hEvent;
60  
61          ZeroMemory(g_szText,sizeof(g_szText));
62          g_iteminfo.mask = LVIF_TEXT;
63  
64          g_iteminfo.iItem = HIWORD(pMsg->lParam);
65          g_iteminfo.iSubItem = LOWORD(pMsg->lParam);
66  
67          g_iteminfo.pszText = g_szText;
68          g_iteminfo.cchTextMax = 500;
69         
70          ListView_GetItem(hList,&g_iteminfo);
71         
72          hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,"MsgProcessed");
73          SetEvent(hEvent);
74          CloseHandle(hEvent);
75      }
76      return CallNextHookEx(g_hhook,ncode,wParam,lParam);
77   }
78  
79   extern "C"  __declspec(dllexport) BOOL WINAPI SetMyHook(DWORD dwThreadId)
80   {
81      if (0 != dwThreadId)
82          g_hhook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,g_hDll,dwThreadId);
83      else
84          UnhookWindowsHookEx(g_hhook);
85      return TRUE;
86   }
87  
对代码说明如下:
1 第6,7行定义了两个自定义消息,源线程给目标线程发送这两个消息,分别表示要求取得列表控件的列信息或者某个项目的信息。消息的WPARAM参数是列表控件的窗口句柄,LPARAM参数是列序号或者项目序号(高16位是项目序号,低16位是子项目序号)
2 第9,10行定义了dll需要额外链接的库。这样用预编译指令在源文件里面写,就不需要改工程属性了,比较方便。
3 第12至17行在Dll中定义了一个名为Shared的节。注意节中定义的变量必须初始化,否则变量不是被放在指定的节,而是放在默认的未初始化全局变量节。(编译时编译器会给出警告信息)。节中的后三个变量加了extern "C" __declspec(dllexport)修饰,其中__declspec(dllexport)表示需要导出指定的变量,extern "C"表示以C语言方式导出,即不给修饰名,这样即使Dll是用C++语言编写的,C语言编写的客户端程序也可以调用它。第19行定义了Shared节的属性为rws。r = readable,w=writeable,s=shared,即在多个进程间共享本节。
共享节的意义是:Dll被多次映射时,共享节中的变量只有一份,而不是通常的对每个映射dll的进程有一份,变量在多个进程间共享,类似于C++类中的静态成员变量被类的多个实例共享一样。
4 第34行的GetMsgProc()是挂钩函数,它将被目标线程调用。此函数对6,7行定义的消息进行处理,取得列表的列信息或者项目信息,放到共享节定义的变量中;然后打开用于进程间(源进程和目标进程)通信的事件,设置事件。注意:这里可以安全地对事件句柄调用CloseHandle()函数,因为事件的引用计数是2,不仅目标进程引用了它,创建这个事件的源进程也引用了它。这里调用CloseHandle()使事件引用计数减1,变成1,系统内核的事件对象并不会被销毁。
5 第80行的SetMyHook()函数供源进程调用以设置或者取消挂钩。函数比较简单,只是简单地调用一个API函数。不导出这个函数,直接在源进程中进行调用也是可以的,只是我在源进程采用了简单的隐含链接方式,而不是动态加载方式,这样做要方便一点(不导出这个函数时安装挂钩的方法是,调用GetModuleHandle("dll模块名")取得dll模块句柄,然后在调用SetWindowsHookEx时传递这个模块句柄)。
6 代码中导出函数和变量用 extern "C" __declspec(dllexport)修饰,显得比较累赘,更通常采用的方法是添加一个头文件,在其中添加以下代码
#ifdef MYDLL
#define MYAPI extern "C __declspec(dllexport)
#else
#define MYAPI extern "C" __declspec(dllimport)
#endif
这样定义后,编译dll和客户端程序时就可以使用同一个头文件。这也是各种相关书籍介绍的方法。各种相关书籍上一般还会介绍使用def文件的方法,这个更简便一些。我这里只是一个简单的dll,所以没有采用这两种方法。
 
   下面该介绍客户端程序代码了。由于代码比较多,这里只给出与本文主题相关的,主要部分的代码如下:
1    #include <shlwapi.h>
2    #pragma comment(lib,"shlwapi.lib")
3   
4    #pragma comment(lib,"MsgHookDll.lib")
5   
6    extern "C" __declspec(dllimport) BOOL WINAPI SetMyHook(DWORD);
7    extern "C" __declspec(dllimport) LVCOLUMN g_colinfo;
8    extern "C" __declspec(dllimport) LVITEM   g_iteminfo;
9    extern "C" __declspec(dllimport) TCHAR    g_szText[500];
10  
11   #define UM_GET_COL_INFO   (WM_USER + 0x1000)
12   #define UM_GET_ITEM_INFO  (WM_USER + 0x2000)
13  
14   void CExportSMSDlg::CopyDataFromList()
15   {
16       if(NULL == m_hDestWnd) return;
17  
18       HWND hList,hHeader;
19       int  x,y,rows,cols;
20       DWORD dwThreadId;
21       HANDLE hEvent;
22  
23       //取得行列数,清空客户端程序列表内容的代码省略
24  
25       hEvent = CreateEvent(NULL,FALSE,FALSE,"MsgProcessed");
26       dwThreadId = GetWindowThreadProcessId(m_hDestWnd,NULL);
27       SetMyHook(dwThreadId);
28      
29       // 复制ListView头部信息的代码省略
30  
31       //复制各行数据的代码类似于取得列表
32       for(y = 0; y < rows; y++)
33           for(x = 0; x < cols; x++)
34           {
35               PostThreadMessage(dwThreadId,UM_GET_ITEM_INFO,(WPARAM)m_hDestWnd,MAKELPARAM(x,y));
36               WaitForSingleObject(hEvent,INFINITE);
37              
38  
39               g_iteminfo.pszText = g_szText;
40               g_iteminfo.mask  |= LVIF_STATE;
41               g_iteminfo.state |= LVIS_SELECTED;
42               if (0 == x)
43                   ListView_InsertItem(hList,&g_iteminfo);
44               else
45                   ListView_SetItem(hList,&g_iteminfo);
46               ListView_EnsureVisible(hList,y,FALSE);
47               ProcessMessage();
48           }
49  
50       CloseHandle(hEvent);
51       SetMyHook(0);
52   }

说明如下:
1 第4行隐含链接含有钩子函数的dll;第6到第9行声明dll导入函数和变量
2 第11,12行定义两种自定义消息类型
3 第25行创建用于源进程和目标进程通信的事件对象
3 第26,27行取得目标窗口线程ID,调用导出函数SetMyHook安装挂钩
4 第35行,客户端发送消息给目标线程,要求取得某行的数据
5 第36行,客户端等待目标线程取得列表指定行数据,放入dll共享节的相关变量中之后,触发事件。关于这一点上文已有说明。关于事件对象采用全局命名空间,一个命名的事件可以在系统中的所有进程间共享这一点,我就不用详细解释了。
6 第39行是不是有点多此一举呢? 钩子函数中已经执行了g_iteminfo.pszText = g_szText;从列表中取得某项内容,这里还有必要写这一句吗?这一句不多余,而且还很重要。Dll映射到两个进程中的起始地址很可能不一样,则g_szText这个数组类型的全局变量在两个进程中的逻辑地址是不一样的。目标进程中执行g_iteminfo.pszText = g_szText;使g_iteminfo.pszText的值等于目标进程中g_szText的地址,这个地址大多数时候是不等于源进程中的g_szText地址值的。源进程如果不再次执行这个语句,则下面的43,45行代码企图在一个很可能只相对于目标进程有效的地址(g_szText的地址)处取得项目的文本信息,这很可能造成拒绝访问异常。关于dll被映射到两个进程中的不同地址处,造成g_szText在两个进程中的逻辑地址不一样这一点,可以参看操作系统原理类教材对地址重定位的讲解。
7 第40,41行设置新添加的项目为被选中状态
8 第47行是一个自定义函数,用于处理消息队列中积压的消息,其代码如下:
static void ProcessMessage()
{
    MSG msg;
    while(PeekMessage(&msg,0,0,0,PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
9 第50行关闭事件对象句柄是很好的习惯
10 卸载挂钩,请参看上面给出的dll源代码
 
   最后说明两点:
1 这里给出的取得列表控件的内容是通用的。虽然我是用它来取得EasyGPRS软件的短信列表框的内容,但是它完全也可以取得其他各种列表框的内容。(本文提到的列表框是指ListView控件)
2 本文提到的dll和客户程序的源代码,可以到我的网络硬盘上下载。地址: http://www.800disk.com/,用户名:yaozijian110,密码:123456(这个帐号只具有下载文件的权限)
 
    这篇文章终于写完了。这才发现自己的表达能力,对内容的组织能力还是有所欠缺的,以后还要多锻炼才是。
---------------------------------------
谢谢你提供的程序,我参考了你的钩子监控指定的程序成功。

我发现我出错的地方在dwThreadId = GetWindowThreadProcessId(m_hDestWnd,NULL);

我试过用    DWORD dwThreadId=0;
    GetWindowThreadProcessId(m_hDestWnd, &dwThreadId);

 hook指定程序就没反应。而用dwThreadId = GetWindowThreadProcessId(m_hDestWnd,NULL);
则成功hook到。这是为什么,请指教!
----------------------------------------
这是MSDN中这个函数的声明:
   DWORD GetWindowThreadProcessId(HWND hWnd,
                 LPDWORD lpdwProcessId);
   函数的第二个参数用于返回窗口所在进程的ID,返回值是创建窗口的线程ID。
   你这样调用GetWindowThreadProcessId(m_hDestWnd, &dwThreadId);取得的是进程ID,而不是线程ID,所以安装挂钩失败。
---------------------------------------------
×××××××××××××××××××××××××××××××××××××××××××××××
***********************************************************************
×××××××××××××××××××××××××××××××××××××××××××××××
其他地方引用:
以下内容我在你的另外一个类似问题中已经回答, 这里将其再重复一遍, 同时作一些补充!

以下全部手打, 自认为写的很认真,很详细, 呵呵, 如果还有不明白随时欢迎
提出来!

首先说一句, 能够调用WIN32API的编程语言很多, 例如汇编, Fortran, VB, C, Java, 甚至脚本语言matlab, ruby等, 不过原理都是一样的, 这里就以C / C++ 来为例吧
-------------------
SetWindowsHookEx一般都是写在你要使用Hook的地方, 在进一步解释之前先简略说明一下Hook相关情况
-------------------
钩子有局部和远程两种类型, 与钩子相关的函数有:
1. 建立钩子:SetWindowsHookEx, 其四个参数分别为钩子类型, 钩子函数地址, 钩子函数所在DLL的实例句柄,安装钩子后想监控的线程的ID号, 返回参数为钩子句柄

2. UnhookWindowsHookEx, 参数只有一个,为要卸载的钩子句柄

3. 钩子函数(名称任意), 三个参数, 具体意义与钩子类型有关

这里以一个例子说明一下:比如你想写一个程序, 当鼠标移到哪里时就在主程序中显示鼠标所在窗口的名称
--------------------
(1)如果鼠标只是局限在窗口内, 那么以上1, 2, 3三个函数均写在运行的主程序中, 比如函数1可以写在按下某个按钮的消息响应函数中,函数 2 写在松开按扭的消息响应函数中,函数 3 只要不写在别的函数中就行, 因为它本生就是一个要定义的独立函数

(2)如果鼠标可以在屏幕任意位置移动, 那么以上函数1, 2位置同(1), 但函数3要写在一个另外写的DLL里, 因为此时安装的是全局钩子, 为了达到获取窗口名称的目的, 在DLL里可能还要做一些其他工作,比如设置共享段, 关于这些这里不细说了

------------------------

关于钩子,以上只是简单说了一下, 不过, 使用钩子确实也很简单, 因为主要的工作还是在钩子函数里
------------------------
------------------------
------------------------
关于钩子的使用其实真的很简单, 应该说 WIN32编程 其实真的很简单, 更进一步说, 不用动脑筋写算法只用熟练语法的编程 都很简单, 这里当然也包括钩子的应用啦 :)
说到这里忍不住发表一点题外话, 编程多年, 走过很多弯路, 体会很多, 关于编程, 其实学问很大, 小到学语法, 大到算法, 应用, 架构等等, 作为一门计算机科学, 与物理, 数学等有关系的分枝还有很多, 还有与电子相关的专业等, 内容庞杂

因此, 如果你真想学点东西, 不要局限在写一个有特殊用途的桌面程序,也不要整天沉浸在各种语言,语法中, 最主要的是不要贪多! 明确自己学编程到底想做什么, 是写软件, 开发游戏, 还是搞网络, 还是做算法, 还是解决理工,数学, 电子问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二、API Hook的原理 这里的API既包括传统的Win32 APIs,也包括任何Module输出的函数调用。熟悉PE文件格 式的朋友都知道,PE文件将对外部Module输出函数的调用信息保存在输入表中,即.idata段。 下面首先介绍本段的结构。 输入表首先以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始。每个被PE文件隐式链接 进来的DLL都有一个IID.在这个数组中的最后一个单元是NULL,可以由此计算出该数组的项数。 例如,某个PE文件从两个DLL中引入函数,就存在两个IID结构来描述这些DLL文件,并在两个 IID结构的最后由一个内容全为0的IID结构作为结束。几个结构定义如下: IMAGE_IMPORT_DESCRIPTOR struct union{ DWORD Characteristics; ;00h DWORD OriginalFirstThunk; }; TimeDateStamp DWORD ;04h ForwarderChain DWORD ;08h Name DWORD ;0Ch FirstThunk DWORD ;10h IMAGE_IMPROT_DESCRIPTOR ends typedef struct _IMAGE_THUNK_DATA{ union{ PBYTE ForwarderString; PDWORD Functions; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; }u1; } IMAGE_IMPORT_BY_NAME结构保存一个输入函数的相关信息: IMAGE_IMPORT_BY_NAME struct Hint WORD ? ;本函数在其所驻留DLL的输出表中的序号 Name BYTE ? ;输入函数的函数名,以NULL结尾的ASCII字符串 IMAGE_IMPORT_BY_NAME ends OriginalFirstThunk(Characteristics):这是一个IMAGE_THUNK_DATA数组的RVA(相对于PE文件 起始处)。其中每个指针都指向IMAGE_IMPORT_BY_NAME结构。 TimeDateStamp:一个32位的时间标志,可以忽略。 ForwarderChain:正向链接索引,一般为0。当程序引用一个DLL中的API,而这个API又引用别的 DLL的API时使用。 NameLL名字的指针。是个以00结尾的ASCII字符的RVA地址,如"KERNEL32.DLL"。 FirstThunk:通常也是一个IMAGE_THUNK_DATA数组的RVA。如果不是一个指针,它就是该功能在 DLL中的序号。 OriginalFirstThunk与FirstThunk指向两个本质相同的数组IMAGE_THUNK_DATA,但名称不同, 分别是输入名称表(Import Name Table,INT)和输入地址表(Import Address Table,IAT)。 IMAGE_THUNK_DATA结构是个双字,在不同时刻有不同的含义,当双字最高位为1时,表示函数以 序号输入,低位就是函数序号。当双字最高位为0时,表示函数以字符串类型的函数名 方式输入,这时它是指向IMAGE_IMPORT_BY_NAME结构的RVA。 三个结构关系如下图: IMAGE_IMPORT_DESCRIPTOR INT IMAGE_IMPORT_BY_NAME IAT -------------------- /-->---------------- ---------- ---------------- |01| 函数1 ||02| 函数2 || n| ... |"USER32.dll" | |--------------------| | | FirstThunk |---------------------------------------------------------------/ -------------------- 在PE文件中对DLL输出函数的调用,主要以这种形式出现: call dword ptr[xxxxxxxx] 或 jmp [xxxxxxxx] 其中地址xxxxxxxx就是IAT中一个IMAGE_THUNK_DATA结构的地址,[xxxxxxxx]取值为IMAGE_THUNK_DATA 的值,即IMAGE_IMPORT_BY_NAME的地址。在操作系统加载PE文件的过程中,通过IID中的Name加载相应 的DLL,然后根据INT或IAT所指向的IMAGE_IMPORT_BY_NAME中的输入函数信息,在DLL中确定函数地址, 然后将函数地址写到IAT中,此时IAT将不再指向IMAGE_IMPORT_BY_NAME数组。这样[xxxxxxxx]取到的 就是真正的API地址。 从以上分析可以看出,要拦截API的调用,可以通过改写IAT来实现,将自己函数的地址写到IAT中, 达到拦截目的。 另外一种方法的原理更简单,也更直接。我们不是要拦截吗,先在内存中定位要拦截的API的地址, 然后改写代码的前几个字节为 jmp xxxxxxxx,其中xxxxxxxx为我们的API的地址。这样对欲拦截API的 调用实际上就跳转到了咱们的API调用去了,完成了拦截。不拦截时,再改写回来就是了。 这都是自己从网上辛辛苦苦找来的,真的很好啊

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值