分享
 
 
 

在Win32中截获Windows系统调用

王朝system·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

1 前言

Windows 9x系统对文件没有提供保护,任何用户均可删除某一文件,这在多用户的计算机上容易造成文件破坏,因此一个Windows 9x系统下的文件保护器是个完善Windows 9x文件系统的好注意。要完成这种文件保护器,首先必须截获任一进程对系统文件函数如DeleteFile()等的调用,从而才能在用户删除文件时进行身份确认。另外,某些软件如在线词典的动态翻译软件和屏幕动态汉化软件需要获得任一窗口的文本输出。要想做到这一点,同样必须首先截获任一进程对系统输出函数如TextOut()、DrawText()等的调用。而Windows下的Win32进程是各自独立的,Windows系统也未如DOS那样提供截获系统调用的标准功能,这导致截获Windows的系统调用存在以下两个障碍:

1.如何进入其它进程的地址空间;

2.如何截获系统函数调用;

现有的某些软件如中文之星和网络金典均已越过了这两个障碍,但它们是如何做到这一点的却不清楚,因此,要想在自己的程序中实现同它们类似的功能还必须自己找方法。本文给出一个利用全局钩子和动态连接机制实现截获Windows系统调用的方法。其中,全局钩子属于标准的Win32编程,而动态连接机制所用的PE文件格式是公开的,在Winnt.h中有详细定义,且内容复杂,篇幅有限,均不细说,有兴趣的同志可以参考有关资料。

2 Win32系统DLL函数的地址及其调用

如下语句:

TextOut(hdc,10,10,text,lstrlen(text));

经编译后为:

call word ptr [004162b8]

004162b8很接近EXE的装载基址00400000(即HINSTANCE),该处值为84f902e0,对应的内存显然属于系统区(>2GB),查看84f902e0处为:

68 29 74 f7 bf e9 06 57 00 3b

即:

push bff77429h

jmp 相对此处偏移3b005706

可以计算出TextOutA()(即TextOut()的ANSI版)的代码地址为:

84f902e0+5+5+3b005706=bff959f0

其中,两个5是(push指令+操作数)的长度和(jmp指令+操作数)的长度。

而在运行时通过调用GetProcAddress()返回的地址同上述情况类似,之所以说类似而不说相同是因为当进程处于调试状态时两者有些不同。对于TextOutA(),处于调试时:

GetProcAddress(GetModuleHandle("GDI32"),"TextOutA")=>844e48a0

该处的值为:

68 4c 25 f2 bf e9 46 11 ab 3b

即指令:

push bff2254ch

jmp 相对此处偏移3bab1146

由此计算得到TextOutA()的代码地址为:

844e48a0+5+5+3bab1146=bff959f0

同前面的计算结果一致。而当进程处于一般运行状态时,上述的84f902e0和844e48a0两个地址值将是同一个值。

以上试验表明:系统DLL起始处有一个跳转表,存放有jmp 指令,所有对该DLL的函数调用均转化为对这个跳转表的调用,而调用DLL函数的进程前部(从HINSTANCE所指之处开始)某个地方有一个单元,进程建立时装载器(Loader)将被调用的DLL函数的跳转表地址填入该处,完成动态连接。系统DLL函数的调用过程如图所示:

*.EXE *.DLL

ANSI版TextOut()调用

call word ptr [......]

地址存放单元

push......

jmp......

装 ∶

入 TextOutA()的代码

对某一个系统DLL函数的调用,如果我们找到它的跳转表地址存放单元,将我们自己的函数地址写入,则其它进程调用对应的系统DLL函数时将进入我们安置的代码。明白了这个过程,可以很容易地想到模仿装载器重新填入地址。Win32的PE文件格式是为动态连接而设计的,因而具有我们想知道的、即定位存放被调用的系统DLL函数跳转表地址的单元所需的信息。

3 源程序与说明

本程序演示如何截获其它进程对GDI32.DLL中的TextOutA()的调用,主体是个含WH_MOUSE型全局钩子的MouseHook.DLL,源码由一个MouseHook.C文件和一个MouseHook.DEF文件组成。另外提供一个启动程序源码Try.c表明使用方法。

3.1 程序说明

当启动程序Try.EXE调用SetMouseHook()后,Windows系统将MouseHook.DLL映射入每一个有鼠标消息传入的进程地址空间。映射时将用DLL_PROCESS_ATTACH作为参数fdwReason的值调用DllEntryPoint(),DllEntryPoint()调用ModifyCall()搜索该进程对TextOutA()的调用并将其替换为调用MyTextOut()。这样当该进程调用GDI32.DLL的TextOutA()时实际调用的却是MouseHook.DLL的MyTextOut()。

ModifyCall()利用进程的HINSTANCE(也即HMODULE,对于Win32而言它们是一回事,即装载基址)找到DOS文件头结构IMAGE_DOS_HEADER,再利用IMAGE_DOS_HEADER中的e_lfanew成员找到Win32的IMAGE_NT_HEADERS结构,该结构含有动态连接所需的信息。IMAGE_NT_HEADERS中的OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]含有DLL函数引入表的RVA(相对虚拟地址)和大小。搜索该表所指向的DLL引入函数地址,值与GetProcAddress()返回值相同的单元就是对应的DLL函数地址存放单元,将MyTextOut()地址写入即可。详细情形请参阅有关PE文件格式的说明和Winnt.h中的定义。

当启动程序调用UnMouseHook()时,过程与之类似,只是此时是为了卸下WH_MOUSE全局钩子并恢复原来对TextOutA()的调用。

此处设置WH_MOUSE全局钩子的目的只是利用全局钩子的特性将MouseHook.DLL“挤进”其它进程的地址空间,因此钩子过程MouseProc()很简单,只是传递一下消息而已。

两个#pragma data_seg()编译器指令是为了定义一个名为.MouseHook的数据段(更确切地说是数据节),该数据段在MouseHook.DEF中被说明为共享,之所以如此是因为各个进程空间中的MouseProc()需要该钩子的句柄hMouseHook,而hMouseHook只在启动程序Try.EXE调用SetMouseHook()时得到一次,因此只能放在共享内存中,否则编程将变得复杂起来。至于每个进程中被替换下来的TextOutA()地址,是属于单个进程空间的,故放在局部数据中,Windows系统会为每次映射使用不同的内存。实际上,TextOutA()的引入地址在所有的进程中都是相同的,这是因为为了页面管理的简单和进程切换的效率,对每个进程Windows 9x将系统DLL映射在同一地址上,但这不是Windows对外保证的,而只是权宜之计,以后可能改变,且Windows NT的情况也可能不同。

MyTextOut()将截获的TextOutA()的参数lpText(即输出字符串)改变以后才输出,从而可以看到截获是否成功。之所以改变两个字符而不是简单的一个,是因为只改变一个字符将导致汉字输出乱码。

MyTextOut()源码中唯一的一条汇编语句__asm sub esp,14h是所有源码中最难写的语句。如果没有这条指令,MyTextOut()将无法正确返回到进程调用TextOutA()处的下一条指令上,出现的“意外”情况是:进程调用TextOutA()的最后一个参数、即输出字符串长度参数将作为返回地址从堆栈中弹出,从而使EIP为一个很小的值,程序进入Windows系统用作指针检查的低端内存,导致非法内存访问。在调试过程中发现导致这种现象的原因是MyTextOut()在临返回前使用了add esp,14h来清除并不需要清除的堆栈,从而破坏了堆栈。显然,原因在于函数调用说明使编译器产生了“错误”的堆栈管理代码,我不知道如何改正这一点,只好使用__asm sub esp,14h强行使堆栈指针指向“正确”的返回地址。有知晓个中奥妙的同志请与作者联系,多多指教。

以下程序在Windows 98、Microsoft Visual Studio 97中调试通过,由于编程中并未使用Windows 9x的特性,且程序依靠的PE文件格式在Windows 9x和Wiundows NT中是通用的,因此上述方法在Windows NT可能也是可行的,只是我并未验证(我没有装Windows NT的机器)。另外,某些方面的情况由于编译器和操作系统不同可能会有所不同(如编译器生成的指令),我的叙述会因此而偏颇甚至错误,在此先做个提醒,也欢迎来意见改进编程。

3.2 MouseHook.DLL源码

3.2.1 MouseHook.DEF文件

SECTIONS

.MouseHook READ WRITE SHARED

EXPORTS

SetMouseHook

UnMouseHook

MyTextOut

3.2.2 MouseHook.C文件

#include<windows.h>

typedef BOOL (*TEXTOUT)(HDC,int,int,LPCTSTR,int);

BOOL WINAPI DllEntryPoint( HINSTANCE,DWORD,LPVOID);

LRESULT CALLBACK MouseProc(int,WPARAM,LPARAM);

DWORD ModifyCall(HINSTANCE,char *,char *,DWORD);

/*引出函数*/

VOID SetMouseHook(VOID);

VOID UnMouseHook(VOID);

BOOL MyTextOut(HDC,int,int,LPCTSTR,int);

/*共享数据*/

#pragma data_seg(".MouseHook")

char DllName[]="MouseHook";

HHOOK hMouseHook=0;

#pragma data_seg()

/*局部数据*/

TEXTOUT OldProc=0;

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)

{

switch(fdwReason)

{

case DLL_PROCESS_ATTACH:

OldProc=(TEXTOUT)GetProcAddress(hinstDLL,"MyTextOut");

OldProc=(TEXTOUT)ModifyCall(GetModuleHandle(0),

"GDI32","TextOutA",(DWORD)OldProc);

break;

case DLL_PROCESS_DETACH:

ModifyCall(GetModuleHandle(0),DllName,"MyTextOut",(DWORD)OldProc);

break;

default:

break;

}

return TRUE;

}

LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)

{

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

}

VOID SetMouseHook()

{

hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseProc,

(HINSTANCE)GetModuleHandle("MouseHook"),(DWORD)NULL);

}

VOID UnMouseHook()

{

UnhookWindowsHookEx(hMouseHook);

}

DWORD ModifyCall(HINSTANCE hInstance,char *DllName,char *Name,DWORD Proc)

{

PIMAGE_DOS_HEADER pDos;

PIMAGE_NT_HEADERS pNT;

PIMAGE_IMPORT_DESCRIPTOR pIm;

PIMAGE_THUNK_DATA pTh;

DWORD dwGet;

dwGet=(DWORD)GetProcAddress(GetModuleHandle(DllName),Name);

if((DWORD)dwGet==(DWORD)NULL) return dwGet;

pDos=(PIMAGE_DOS_HEADER)hInstance;

pNT=(PIMAGE_NT_HEADERS)((DWORD)pDos+(DWORD)(pDos->e_lfanew));

pIm=(PIMAGE_IMPORT_DESCRIPTOR)(pNT->

OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

pIm=(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pIm+(DWORD)hInstance);

while(pIm->FirstThunk)

{

pTh=(PIMAGE_THUNK_DATA)((DWORD)(pIm->FirstThunk)+(DWORD)hInstance);

while(pTh->u1.Function)

{

if((DWORD)(pTh->u1.Function)==dwGet)

pTh->u1.Function=(PDWORD)Proc;

pTh++;

}

pIm++;

}

return dwGet;

}

BOOL MyTextOut(HDC hdc,int x,int y,LPCTSTR lpText,int len)

{

BOOL bRet;

if(len>1)

{

((LPSTR)(lpText))[0]='&';

((LPSTR)(lpText))[1]='&';

}

bRet=(*OldProc)(hdc,x,y,lpText,len);

__asm sub esp,14h

return bRet;

}

3.3 启动程序Try.C

#include<windows.h>

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);

char AppName[]="Try";

extern VOID SetMouseHook(VOID);

extern VOID UnMouseHook(VOID);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,

LPSTR lpCmdLine,int nCmdShow)

{

SetMouseHook();

MessageBox(NULL,"SetMouseHook() Completed!\nUnMouseHook()?",AppName,MB_OK);

UnMouseHook();

return 0;

}

4 结束语

本文给出的利用全局钩子和动态连接机制实现截获Windows系统调用的方法,可以用于编制Windows 9x系统下的文件保护程序以及其它需要监视Windows系统运行的程序,主要依靠标准的Win32编程技术,简单可靠。缺点是当某些进程在进行大量内部处理,导致全局钩子无法很快映射到进程空间时,该方法响应较慢甚至无效。另外,需要找到指导编译器生成正确的堆栈管理代码的方法,否则,靠人工强制恢复堆栈是非常困难的。

参考文献

1 《Microsoft Win32程序员参考大全》(一)、(二) [美] Microsoft Corporation 著

清华大学出版社 1995年4月

2 《The PE File Format》Bernd Luevelsmeyer 著

网络下载文件

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有