关于钩子函数,讨论者皆有报酬,300大洋。(300分)

  • 主题发起人 主题发起人 xjxk2000
  • 开始时间 开始时间
X

xjxk2000

Unregistered / Unconfirmed
GUEST, unregistred user!
在程序中想实现屏幕取词功能,听说要用到安装鼠标钩子,通过钩子函数获得鼠标消息。<br>在下系一名名符其实的菜鸟,一提到什么使用“钩子函数”就感觉一头雾水,不知所云,<br>但路总是要走的,很想真正弄清楚这个问题,但不知从何学起,请诸位大侠指点。<br>在下谢谢先!<br>一个喝求answer的迷茫菜鸟......<br>
 
先找本基础的书看看,DELPHI5开发人员指南上就有一部分<br>
 
是啊!看书!
 
關注,我也想學
 
msdn上有!
 
看大富翁老帖+葵花宝典,也差不多了。<br>但还是应该系统的看看书。
 
hubdog的葵花宝典.
 
我不喜欢钩子,呵呵,光要跟踪鼠标,不需要钩子的:),很早以前看过这部分,现在忘了<br>:)
 
对了在电脑报2000或者是在2001年合订本上的有过两篇文章一篇是关于介绍程序中实现屏幕取词功能<br>的不过好像没有提什么钩子函数?具体因为时间太长我也记不大清了。另外一篇是关于介绍钩子函数的<br>,不过不是什么实现屏幕取词而是关于什么病毒的。不好意思我也记不大清了,如果电脑报上<br>没有你再去赛迪网去查查看看电脑教育报上有没有?我只能这样帮你了!
 
用DELPHI编制Windows95下的钩子函数<br><br>&nbsp;<br><br>  Windows消息管理机构提供了能使应用程序访问控制消息流μ<br>'c4所谓的钩子(HOOK)机制。钩子有多种,分别用于捕获某一特定类型或某一范围的消息。如:键盘消息,鼠标消息等。我们这里仅以键盘钩子的使用为例,讨论在DELPHI下怎样编写DLL程序和怎样在自己的程序中安装使用键盘钩子函数,并讨论了不同程序使用同一DLL文件时怎样共享数据。<br>一、 钩子过滤函数的编写说明<br>由于钩子过滤函数必须在独立的模块中,也就是说我们必须首先生成一个DLL框架,然后再在其中加入钩子函数代码以及其他相关函数代码。我们这里以键盘钩子过滤函数的编写为例来说明。具体步骤如下:<br>1、先生成一个DLL筐2架<br>2、编写自己的键盘钩子过滤函数<br>钩子过滤函数必须是回调函数,其函数的 4?稳缦拢o<br>function KeyHookProc( <br>iCode:Integer;<br>wParam:WPARAM;<br>lParam:LPARAM ) : LRESULT; stdcall ;export ;<br>在生成的DLL框架中加入自己的键盘钩子处理函数处理键盘消息。<br>代码如下:…<br>if(iCode&gt;=0) then begin<br>Result:=0; //初始化返回值 <br>// 在这里加入自己的代码<br>end else <br>begin<br>Result:=CallNextHook(hOldKeyHook,iCode,wParam,lParam);<br>// hOldKeyHook是保存的原键盘过滤函数 5刂·<br>end; <br>3、 安装键盘钩子过滤函数<br>为安装一个钩子筥fd滤函数应调用SetWindowsHookEx函数(适用于Windows3.0的SetWindowsHook钩子安装函数现在已经废弃不用)。该函数的原形如下:<br>HHOOK SetWindowsHookEx(<br>int idHook, &nbsp; &nbsp;// 安装的筥b3子类型<br>HOOKPROC lpfn, &nbsp; &nbsp;// 钩子过滤籂f数地址<br>HINSTANCE hMod, &nbsp; &nbsp; // 任务句柄<br>DWORD dwThreadId &nbsp; &nbsp; // 钩子用于的目的<br>);<br>需要说明的是:蚠a8常应该调用MakeProcInstance函数以获取一个输出函数的前导码的入口地址,再将此地址作为SetWindowsHookEx的第二个参数lpfn。但由于Delphi提供了"灵巧调用(smart callback)",使得MakeProcInstance可以省去,而直接将钩子过滤函数名用作入口地址。<br>这样当应用程序觃c3GetMessage或PeekMessage函数从消息队列中读消息或有按键消息(WM_KEYDOWN或WM_KEYUP)要处理时,系统就要调用钩子过滤函数KeyHookProc处理键盘消息。<br>4、 卸载钩子过滤函数。<br>当钩子函数不再需要时,应调用UnHookWindowsHookProc卸载安装的钩子以释放系统资源。<br>完整的程序清单如下?ba<br>Library KEYHOOK;<br>uses Windows;<br>const BUFFER_SIZE=16*1024;<br>const HOOK_MEM_FILENAME='SAMPLE KEY_HOOK_MEM_FILE';<br>const HOOK_MUTEX_NAME ='SAMPLE KEY_HOOK_MUTEX_NAME';<br>type<br>TShared=record<br>Keys : array[0..BUFFER_SIZE] of Char;<br>KeyCount : Integer;<br>end;<br>PShared=^TShared;<br>var<br>MemFile,HookMutex : THandle;<br>hOldKeyHook : HHook;<br>ProcSaveExit : Pointer;<br>Shared : PShared;<br><br>//键盘钩子过滤函数<br>function KeyHookProc(iCode: Integer; wParam: WPARAM ; lParam: LPARAM):LRESULT<br>; stdcall; export;<br>const KeyPressMask = $80000000;<br>begin<br>if iCode &lt; 0 then<br>Result := CallNextHookEx(hOldKeyHook, iCode, wParam, lParam)<br>else begin<br>if ((lParam and KeyPressMask)= 0) then // 键按下<br>begin<br>Shared^.Keys[Shared^.KeyCount]:=Char(wParam and $00ff);<br>Inc(Shared^.KeyCount);<br>if Shared^.KeyCount&gt;=BUFFER_SIZE-1 then Shared^.KeyCount:=0;<br>end;<br>iCode:=-1;<br>Result := CallNextHookEx(hOldKeyHook, iCode, wParam, lParam);<br>end;<br>end;<br><br>// 设置钩子过滤函数<br>function EnableKeyHook : BOOL ; export;<br>begin<br>Shared^.KeyCount:=0; //初始化键盘指针<br>if hOldKeyHook=0 then begin<br>hOldKeyHook := SetWindowsHookEx(WH_KEYBOARD,<br>KeyHookProc,<br>HInstance,<br>0);<br>end;<br>Result := (hOldKeyHook &lt;&gt; 0);<br>end;<br><br>//撤消钩子过滤函数<br>function DisableKeyHook: BOOL ; export;<br>begin<br>if hOldKeyHook&lt;&gt; 0 then<br>begin<br>UnHookWindowsHookEx(hOldKeyHook); // 解除 Keyboard Hook<br>hOldKeyHook:= 0;<br>Shared^.KeyCount:=0;<br>end;<br>Result := (hOldKeyHook = 0);<br>end;<br><br>//取得键盘缓冲区中击键的个数<br>function GetKeyCount :Integer ; export;<br>begin<br>Result:=Shared^.KeyCount;<br>end;<br><br>//取得键盘缓冲区的键<br>function GetKey(index:Integer) : Char ; export;<br>begin<br>Result:=Shared^.Keys[index];<br>end;<br><br>//清空键盘缓冲区<br>procedure ClearKeyString ; export;<br>begin<br>Shared^.KeyCount:=0;<br>end;<br><br>//DLL的退出处理过程<br>procedure KeyHookExit; far;<br>begin<br>if hOldKeyHook &lt;&gt; 0 then DisableKeyHook;<br>UnMapViewOfFile(Shared); // 释放内存映象文件<br>CloseHandle(MemFile); // 关闭映象文件<br>ExitProc := ProcSaveExit;<br>end;<br><br>exports // 定义输出函数<br>EnableKeyHook,<br>DisableKeyHook,<br>GetKeyCount,<br>ClearKeyString,<br>GetKey;<br><br>begin<br>// DLL 初始化部分<br>HookMutex:=CreateMutex(nil,True,HOOK_MUTEX_NAME);<br>// 通过建立内存映象文件以共享内存<br>MemFile:=OpenFileMapping(FILE_MAP_WRITE,False,<br>HOOK_MEM_FILENAME);<br>if MemFile=0 then<br>MemFile:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,<br>SizeOf(TShared) ,HOOK_MEM_FILENAME);<br>Shared:=MapViewOfFile(MemFile,File_MAP_WRITE,0,0,0);<br>ReleaseMutex(HookMutex);<br>CloseHandle(HookMutex);<br>ProcSaveExit := ExitProc; // 保存DLL的ExitProc<br>ExitProc := @KeyHookExit; // 设置DLL新的ExitProc<br>end.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 源代码结束<br><br>二、 在自己的程序中使用编制好的键盘钩子过滤函数。<br>钩子函数编制好后,使用起来其实很简单:首先调用SetWindowsHookEx安装自己的钩子过滤函数,同时保存原先的钩子过滤函数地址。这时钩子函数就开始起作用了,它将按照你的要求处理键盘消息。程序运行完毕或不再需要监视键盘消息时,调用UnHookWindowsHookProc函数卸载所安装的钩子函数,同时恢复原来的钩子过滤函数地址。<br>下面就是使用在以上编制的钩子函数的例子:<br>unit Unit1;<br>interface<br>uses<br>Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,<br>StdCtrls, ExtCtrls;<br>type<br>TForm1 = class(TForm)<br>Memo1: TMemo;<br>Panel1: TPanel;<br>bSetHook: TButton;<br>bCancelHook: TButton;<br>bReadKeys: TButton;<br>bClearKeys: TButton;<br>Panel2: TPanel;<br>procedure bSetHookClick(Sender: TObject);<br>procedure bCancelHookClick(Sender: TObject);<br>procedure bReadKeysClick(Sender: TObject);<br>procedure bClearKeysClick(Sender: TObject);<br>end;<br>var Form1: TForm1;<br><br>implementation<br>{$R *.DFM}<br>function EnableKeyHook : BOOL ; external 'KEYHOOK.DLL';<br>function DisableKeyHook : BOOL ; external 'KEYHOOK.DLL';<br>function GetKeyCount : Integer ; external 'KEYHOOK.DLL';<br>function GetKey(idx:Integer) : Char ; external 'KEYHOOK.DLL';<br>procedure ClearKeyString ; external 'KEYHOOK.DLL';<br><br>procedure TForm1.bSetHookClick(Sender: TObject); // 设置键盘钩 7ó<br>begin<br>EnableKeyHook;<br>bSetHook.Enabled :=False;<br>bCancelHook.Enabled:=True;<br>bReadKeys.Enabled :=True;<br>bClearKeys.Enabled :=True;<br>Panel2.Caption:=' 键盘钩子已经设置';<br>end;<br><br>procedure TForm1.bCancelHookClick(Sender: TObject); // 卸载键盘钩子<br>begin<br>DisableKeyHook;<br>bSetHook.Enabled :=True;<br>bCancelHook.Enabled:=False;<br>bReadKeys.Enabled :=False;<br>bClearKeys.Enabled :=False;<br>Panel2.Caption:=' 键盘钩子没有设置';<br>end;<br><br>procedure TForm1.bReadKeysClick(Sender: TObject); // 取得击键的历史记录<br>var i:Integer;<br>begin<br>Memo1.Lines.Clear; // 在Memo1中显示击键历史记录<br>for i:=0 to GetKeyCount-1 do<br>Memo1.Text:=Memo1.Text+GetKey(i);<br>end;<br><br>procedure TForm1.bClearKeysClick(Sender: TObject); // 清除击键历史记录<br>begin<br>Memo1.Clear;<br>ClearKeyString;<br>end;<br><br>end.<br>// 源代码结束<br><br>三、 Windows95下DLL中实现共享内存<br>  在上面的钩子函数所在的DLL文件中,需要使用共享内存,即,所有击键的记录存储在同一个数据段中。为什么要这样做呢?这是因为Windows95的DLL调用方法与Windows3.X的方法不同。每个进(线)程在登录某动态连接库时都会为该动态连接库传入一个新的实例句柄(即DLL数据段的句柄)。这使得DLL各个实例之间互不干扰,但是这对那些所有DLL实例共享一组变量带来一些困难。为了解决这个问题,我们在这儿通过建立内存映射文件的方法来解决。即使用Windows的OpenFileMapping、CreateFileMapping和<br>MapViewOfFile三个函数来实现。使用方法如下:<br>…<br>MemFile是THandle类型,Shared是指针类型,HOOK_MEM_FILENAME是一常量串<br>…<br>MemFile:=OpenFileMapping(FILE_MAP_WRITE,False,<br>HOOK_MEM_FILENAME); //打开内存映射文件<br>if MemFile=0 then //打开失败则衉c2建内存映射文件<br>MemFile:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,<br>SizeOf(TShared) ,HOOK_MEM_FILENAME);<br>//映射文件到变量<br>Shared:=MapViewOfFile(MemFile,File_MAP_WRITE,0,0,0);<br><br>到此为止,你已经知道用Delphi编制钩子函数有多么容易。最后不得不提醒大家:钩子函数虽然功能比较强,但如果使用不当将会严重影响系统的效率,所以要尽量避免使用系统钩子。非要使用不可时也应该格外小心,应使之尽可能小地影响系统的运行。<br>[全文完] <br>
 
感谢皮皮007。<br>还有要发言的吗?还想听听别人的声音。<br>如没有,我准备收场了。
 
&gt;&gt;&gt;&gt;以下转添:<br>----------这是*.dll中的单元---------------<br>unit HookProc;<br><br><br>interface<br><br>uses windows,messages,sysutils;<br><br>const<br>&nbsp; HTName:array[1..13] of pchar=(<br>&nbsp; 'CALLWNDPROC','CALLWNDPROCRET','CBT','DEBUG','GETMESSAGE','JOURNALPLAYBACK',<br>&nbsp; 'JOURNALRECORD','KEYBOARD','MOUSE','MSGFILTER','SHELL','SYSMSGFILTER','FOREGROUNDIDLE'<br>&nbsp; );<br><br><br>function CallWndProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function CallWndRetProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function CBTProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function DebugProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function GetMsgProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function JournalPlaybackProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function JournalRecordProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function KeyboardProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function MouseProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function MessageProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function ShellProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function SysMsgProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>function ForegroundIdleProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br><br>implementation<br><br>procedure SaveInfo(k:integer;str:string);stdcall;<br>var<br>&nbsp; f:textfile;<br>&nbsp; WorkPath:string;<br>begin<br>&nbsp; WorkPath:=ExtractFilePath(ParamStr(0));<br>&nbsp; assignfile(f,WorkPath+'Records.txt');<br>&nbsp; if fileexists(WorkPath+'Records.txt')=false then rewrite(f)<br>&nbsp; else append(f);<br>&nbsp; //if strcomp(pchar(str),pchar('#13#10'))=0 then writeln(f,'')<br>&nbsp; //else write(f,str);<br>&nbsp; writeln(f,HTName[k]+'----'+str);<br>&nbsp; closefile(f);<br>end;<br><br><br><br>function CallWndProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>var<br>&nbsp; pcs:TCWPSTRUCT;<br>begin<br>&nbsp; pcs:=TCWPSTRUCT(PCWPSTRUCT(lParam)^);<br>&nbsp; if nCode&gt;=0 then<br>&nbsp; begin<br>&nbsp; &nbsp; if pcs.message=wm_lbuttonup then<br>&nbsp; &nbsp; SaveInfo(1,format('hwnd=%x',[pcs.hwnd]));<br>&nbsp; end;<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function CallWndRetProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function CBTProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function DebugProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function GetMsgProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>var<br>&nbsp; pcs:TMSG;<br>begin<br>&nbsp; pcs:=TMSG(PMSG(lParam)^);<br>&nbsp; if nCode&gt;=0 then<br>&nbsp; begin<br>&nbsp; &nbsp; if pcs.message=wm_lbuttonup then<br>&nbsp; &nbsp; SaveInfo(5,format('hwnd=%x',[pcs.hwnd]));<br>&nbsp; end;<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function JournalPlaybackProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function JournalRecordProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function KeyboardProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function MouseProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function MessageProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function ShellProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function SysMsgProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br>//<br>function ForegroundIdleProc(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>begin<br>&nbsp; Result:=CallNextHookEx(0,nCode,wParam,lParam);<br>end;<br><br><br>end.<br><br><br><br>--------这是*.dll主程序------------------<br>library DemoHook;<br><br>uses<br>&nbsp; windows,messages,sysutils,<br>&nbsp; HookProc in 'HookProc.pas';<br><br>{$r *.res}<br><br>const<br><br>&nbsp; HookMemFileName='DllHookMemFile.DTA';<br>&nbsp; HTName:array[1..13] of pchar=(<br>&nbsp; 'CALLWNDPROC','CALLWNDPROCRET','CBT','DEBUG','GETMESSAGE','JOURNALPLAYBACK',<br>&nbsp; 'JOURNALRECORD','KEYBOARD','MOUSE','MSGFILTER','SHELL','SYSMSGFILTER','FOREGROUNDIDLE'<br>&nbsp; );<br><br>type<br>&nbsp; THookProc = function(nCode:integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;<br>&nbsp; PShared=^TShared;<br>&nbsp; THook = record<br>&nbsp; &nbsp; HookHand:HHook;<br>&nbsp; &nbsp; HookType:integer;<br>&nbsp; &nbsp; HookProc:THookProc;<br>&nbsp; end;<br>&nbsp; TShared = record<br>&nbsp; &nbsp; Hook:array [0..16] of THook;<br>&nbsp; &nbsp; Father,Self:integer;<br>&nbsp; &nbsp; Count:integer;<br>&nbsp; &nbsp; hinst:integer;<br>&nbsp; end;<br>&nbsp; TWin = record<br>&nbsp; &nbsp; Msg:TMsg;<br>&nbsp; &nbsp; wClass:TWndClass;<br>&nbsp; &nbsp; hMain:integer;<br>&nbsp; end;<br>var<br>&nbsp; MemFile:THandle;<br>&nbsp; Shared:PShared;<br>&nbsp; Win:TWin;<br>&nbsp; wmhook:integer;<br><br>procedure SaveInfo(k:integer;str:string);stdcall;<br>var<br>&nbsp; f:textfile;<br>&nbsp; WorkPath:string;<br>begin<br>&nbsp; WorkPath:=ExtractFilePath(ParamStr(0));<br>&nbsp; assignfile(f,WorkPath+'Records.txt');<br>&nbsp; if fileexists(WorkPath+'Records.txt')=false then rewrite(f)<br>&nbsp; else append(f);<br>&nbsp; //if strcomp(pchar(str),pchar('#13#10'))=0 then writeln(f,'')<br>&nbsp; //else write(f,str);<br>&nbsp; writeln(f,HTName[k]+'----'+str);<br>&nbsp; closefile(f);<br>end;<br><br><br>procedure InitHookData;<br>var k:integer;<br>begin<br>&nbsp; with Shared^ do<br>&nbsp; begin<br>&nbsp; &nbsp; for k:=0 to 14 do Hook[k].HookHand:=0;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[0].HookType:=WH_CALLWNDPROC;<br>&nbsp; &nbsp; Hook[0].HookProc:=@CallWndProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[1].HookType:=WH_CALLWNDPROCRET;<br>&nbsp; &nbsp; Hook[1].HookProc:=@CallWndRetProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[2].HookType:=WH_CBT;<br>&nbsp; &nbsp; Hook[2].HookProc:=@CBTProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[3].HookType:=WH_DEBUG;<br>&nbsp; &nbsp; Hook[3].HookProc:=@DebugProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[4].HookType:=WH_GETMESSAGE;<br>&nbsp; &nbsp; Hook[4].HookProc:=@GetMsgProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[5].HookType:=WH_JOURNALPLAYBACK;<br>&nbsp; &nbsp; Hook[5].HookProc:=@JournalPlaybackProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[6].HookType:=WH_JOURNALRECORD;<br>&nbsp; &nbsp; Hook[6].HookProc:=@JournalRecordProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[7].HookType:=WH_KEYBOARD;<br>&nbsp; &nbsp; Hook[7].HookProc:=@KeyboardProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[8].HookType:=WH_MOUSE;<br>&nbsp; &nbsp; Hook[8].HookProc:=@MouseProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[9].HookType:=WH_MSGFILTER;<br>&nbsp; &nbsp; Hook[9].HookProc:=@MessageProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[10].HookType:=WH_SHELL ;<br>&nbsp; &nbsp; Hook[10].HookProc:=@ShellProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[11].HookType:=WH_SYSMSGFILTER;<br>&nbsp; &nbsp; Hook[11].HookProc:=@SysMsgProc;<br>&nbsp; &nbsp; //<br>&nbsp; &nbsp; Hook[12].HookType:=WH_FOREGROUNDIDLE;<br>&nbsp; &nbsp; Hook[12].HookProc:=@ForegroundIdleProc;<br><br>&nbsp; end;<br>end;<br><br>function SetHook(fSet:boolean;HookId:integer):bool;stdcall;<br>begin<br>&nbsp; with shared^ do<br>&nbsp; if fSet=true then<br>&nbsp; begin<br>&nbsp; &nbsp; if Hook[HookId].HookHand=0 then<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; Hook[HookId].HookHand:=SetWindowsHookEx(Hook[HookId].HookType,Hook[HookId].HookProc,hinstance,0);<br>&nbsp; &nbsp; &nbsp; if Hook[HookId].HookHand&lt;&gt;0 then Result:=true<br>&nbsp; &nbsp; &nbsp; else Result:=false;<br>&nbsp; &nbsp; end else Result:=true;<br>&nbsp; end else<br>&nbsp; begin<br>&nbsp; &nbsp; if Hook[HookId].HookHand&lt;&gt;0 then<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; if UnhookWindowsHookEx(Hook[HookId].HookHand)=true then<br>&nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; Hook[HookId].HookHand:=0;<br>&nbsp; &nbsp; &nbsp; &nbsp; Result:=true;<br>&nbsp; &nbsp; &nbsp; end else Result:=false;<br>&nbsp; &nbsp; end else Result:=true;<br>&nbsp; end;<br>end;<br><br>procedure Extro;<br>begin<br>&nbsp; UnmapViewOfFile(Shared);<br>&nbsp; CloseHandle(MemFile);<br>end;<br><br><br>function WindowProc(hWnd,Msg,wParam,lParam:longint):LRESULT; stdcall;<br>var k:integer;<br>begin<br>&nbsp; Result:=DefWindowProc(hWnd,Msg,wParam,lParam);<br>&nbsp; case Msg of<br>&nbsp; wm_destroy:<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; for k:=0 to 12 do SetHook(False,k);<br>&nbsp; &nbsp; &nbsp; postmessage(findwindow('WinHook',nil),wm_destroy,0,0);<br>&nbsp; &nbsp; &nbsp; ExitThread(0);<br>&nbsp; &nbsp; end;<br>&nbsp; end;<br>&nbsp; if msg=wmhook then<br>&nbsp; begin<br>&nbsp; &nbsp; if wparam&gt;0 then<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; if sethook(true,wparam-1)=true then postmessage(findwindow('WinHook',nil),wmhook,wparam,0);<br>&nbsp; &nbsp; end else<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; if sethook(false,-wparam-1)=true then postmessage(findwindow('WinHook',nil),wmhook,wparam,0);<br>&nbsp; &nbsp; end;<br>&nbsp; end;<br>end;<br><br>procedure run;stdcall;<br>//var k:integer;<br>begin<br>&nbsp; win.wClass.lpfnWndProc:= &nbsp;@WindowProc;<br>&nbsp; win.wClass.hInstance:= &nbsp; &nbsp;hInstance;<br>&nbsp; win.wClass.lpszClassName:='WideHook';<br>&nbsp; RegisterClass(win.wClass);<br>&nbsp; win.hmain:=CreateWindowEx(ws_ex_toolwindow,win.wClass.lpszClassName,'WideHook',WS_CAPTION,0,0,1,1,0,0,hInstance,nil);<br>&nbsp; FillChar(Shared^,SizeOf(TShared),0);<br>&nbsp; shared^.self:=win.hmain;<br>&nbsp; shared^.hinst:=hinstance;<br>&nbsp; InitHookData;<br>&nbsp; wmhook:=registerwindowmessage(pchar('wm_hook'));<br>&nbsp; while(GetMessage(win.Msg,win.hmain,0,0))do<br>&nbsp; begin<br>&nbsp; &nbsp; TranslateMessage(win.Msg);<br>&nbsp; &nbsp; DispatchMessage(win.Msg);<br>&nbsp; end;<br>end;<br><br>procedure DllEntryPoint(fdwReason:DWORD);<br>begin<br>&nbsp; case fdwReason of<br>&nbsp; DLL_PROCESS_DETACH:<br>&nbsp; &nbsp; Extro;<br>&nbsp; end;<br>end;<br><br>exports run;<br><br>begin<br>&nbsp; //建立内存映象文件,用来保存全局变量<br>&nbsp; MemFile:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,SizeOf(TShared),HookMemFileName);<br>&nbsp; Shared:=MapViewOfFile(MemFile,FILE_MAP_WRITE,0,0,0);<br>&nbsp; DLLProc:=@DllEntryPoint;<br>end.<br><br>====================================================================================<br>procedure Search(Strings:TStrings); <br>implementation <br><br>{$R *.dfm} <br>procedure Search(Strings:TStrings); <br>var <br>&nbsp; Snap:THandle; <br>&nbsp; RB:Boolean; <br>&nbsp; PE:TProcessEntry32; <br>begin <br>&nbsp; if Strings=nil then <br>&nbsp; &nbsp; Exit; <br>&nbsp; snap:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); <br>&nbsp; if snap = -1 then Exit; <br>&nbsp; try <br>&nbsp; &nbsp; PE.dwSize:=SizeOf(TProcessEntry32); <br>&nbsp; &nbsp; RB:=Process32First(snap,PE); <br>&nbsp; &nbsp; while RB do <br>&nbsp; &nbsp; begin <br>&nbsp; &nbsp; &nbsp; Strings.AddObject(PE.szExeFile,Pointer(PE.th32ProcessID)); <br>&nbsp; &nbsp; &nbsp; PE.dwSize:=SizeOf(TProcessEntry32); <br>&nbsp; &nbsp; &nbsp; RB:=Process32Next(snap,PE); <br>&nbsp; &nbsp; end; <br>&nbsp; finally <br>&nbsp; &nbsp; CloseHandle(snap); <br>&nbsp; end; <br>end; <br>procedure TForm1.BitBtn1Click(Sender: TObject); <br>begin <br>&nbsp; ListBox1.Items.Clear; <br>&nbsp; Search(ListBox1.Items); <br>end; <br><br>procedure TForm1.Button1Click(Sender: TObject); <br>var <br>&nbsp; H:THandle; <br>&nbsp; R:Cardinal; <br>begin <br>&nbsp; H:=OpenProcess(PROCESS_TERMINATE,True,LongInt(ListBox1.Items.Objects[ListBox1.ItemIndex])); <br>&nbsp; R:=0; <br>&nbsp; TerminateProcess(H,R); <br>&nbsp; CloseHandle(H); <br>end; <br><br>end. <br>关键的: <br>procedure TForm1.Button1Click(Sender: TObject); <br>var <br>&nbsp; H:THandle; <br>&nbsp; R:Cardinal; <br>begin <br>&nbsp; H:=OpenProcess(PROCESS_TERMINATE,True,LongInt(ListBox1.Items.Objects[ListBox1.ItemIndex])); <br>&nbsp; R:=0; <br>&nbsp; TerminateProcess(H,R); <br>&nbsp; CloseHandle(H); <br>end; <br>其中ListBox1.Items.Objects[ListBox1.ItemIndex]是在遍历进程时存放的ProcessID
 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 鼠标屏幕取词技术的原理和实现 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 白瑜<br><br>&nbsp; &nbsp;“鼠标屏幕取词”技术是在电子字典中得到广泛地应用的,如四通利方和金山<br>词霸等软件,这个技术看似简单,其实在windows系统中实现却是非常复杂的,总<br>的来说有两种实现方式:<br>&nbsp; &nbsp; 第一种:采用截获对部分gdi的api调用来实现,如textout,textouta等。<br>&nbsp; &nbsp; 第二种:对每个设备上下文(dc)做一分copy,并跟踪所有修改上下文(dc)的<br>操作。 &nbsp; &nbsp; &nbsp;<br>&nbsp; &nbsp; 第二种方法更强大,但兼容性不好,而第一种方法使用的截获windowsapi的<br>调用,这项技术的强大可能远远超出了您的想象,毫不夸张的说,利用windowsapi<br>拦截技术,你可以改造整个操作系统,事实上很多外挂式windows中文平台就是<br>这么实现的!而这项技术也正是这篇文章的主题。<br>&nbsp; &nbsp; 截windowsapi的调用,具体的说来也可以分为两种方法:<br>&nbsp; &nbsp; 第一种方法通过直接改写winapi 在内存中的映像,嵌入汇编代码,使之被<br>调用时跳转到指定的地址运行来截获;第二种方法则改写iat(import address table<br>&nbsp;输入地址表),重定向winapi函数的调用来实现对winapi的截获。<br>&nbsp; &nbsp; 第一种方法的实现较为繁琐,而且在win95、98下面更有难度,这是因为虽<br>然微软说win16的api只是为了兼容性才保留下来,程序员应该尽可能地调用32位<br>的api,实际上根本就不是这样!win 9x内部的大部分32位api经过变换调用了同<br>名的16位api,也就是说我们需要在拦截的函数中嵌入16位汇编代码!<br>&nbsp; &nbsp; 我们将要介绍的是第二种拦截方法,这种方法在win95、98和nt下面运行都<br>比较稳定,兼容性较好。由于需要用到关于windows虚拟内存的管理、打破进程<br>边界墙、向应用程序的进程空间中注入代码、pe(portable executable)文件<br>格式和iat(输入地址表)等较底层的知识,所以我们先对涉及到的这些知识大<br>概地做一个介绍,最后会给出拦截部分的关键代码。<br>&nbsp; &nbsp; 先说windows虚拟内存的管理。windows9x给每一个进程分配了4gb的地址空<br>间,对于nt来说,这个数字是2gb,系统保留了2gb到 4gb之间的地址空间禁止进<br>程访问,而在win9x中,2gb到4gb这部分虚拟地址空间实际上是由所有的win32进<br>程所共享的,这部分地址空间加载了共享win32 dll、内存映射文件和vxd、内存<br>管理器和文件系统码,win9x中这部分对于每一个进程都是可见的,这也是win9x<br>操作系统不够健壮的原因。win9x中为16位操作系统保留了0到4mb的地址空间,<br>而在4mb到2gb之间也就是win32进程私有的地址空间,由于 每个进程的地址空间<br>都是相对独立的,也就是说,如果程序想截获其它进程中的api调用,就必须打<br>破进程边界墙,向其它的进程中注入截获api调用的代码,这项工作我们交给钩<br>子函数(setwindowshookex)来完成,关于如何创建一个包含系统钩子的动态链<br>接库,《电脑高手杂志》在第?期已经有过专题介绍了,这里就不赘述了。所有<br>系统钩子的函数必须要在动态库里,这样的话,当进程隐式或显式调用一个动态<br>库里的函数时,系统会把这个动态库映射到这个进程的虚拟地址空间里,这使得<br>dll成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈,也就是<br>说动态链接库中的代码被钩子函数注入了其它gui进程的地址空间(非gui进程,<br>钩子函数就无能为力了),当包含钩子的dll注入其它进程后,就可以取得映射<br>到这个进程虚拟内存里的各个模块(exe和dll)的基地址,如:<br>hmodule hmodule=getmodulehandle(“mypro.exe”);<br>在mfc程序中,我们可以用afxgetinstancehandle()函数来得到模块的基地址。<br>exe和dll被映射到虚拟内存空间的什么地方是由它们的基地址决定的。它们的基<br>地址是在链接时由链接器决定的。当你新建一个win32工程时,vc++链接器使<br>用缺省的基地址0x00400000。可以通过链接器的base选项改变模块的基地址。<br>exe通常被映射到虚拟内存的0x00400000处,dll也随之有不同的基地址,通常被<br>映射到不同进程的相同的虚拟地址空间处。<br>&nbsp; 系统将exe和dll原封不动映射到虚拟内存空间中,它们在内存中的结构与磁盘<br>上的静态文件结构是一样的。即pe (portable executable) 文件格式。我们得<br>到了进程模块的基地址以后,就可以根据pe文件的格式穷举这个模块的image_import_descriptor<br>数组,看看进程空间中是否引入了我们需要截获的函数所在的动态链接库,比如<br>需要截获“textouta”,就必须检查“gdi32.dll”是否被引入了。说到这里,我<br>们有必要介绍一下pe文件的格式,如右图,这是pe文件格式的大致框图,最前面<br>是文件头,我们不必理会,从pe file optional header后面开始,就是文件中各<br>个段的说明,说明后面才是真正的段数据,而实际上我们关心的只有一个段,那<br>就是“.idata”段,这个段中包含了所有的引入函数信息,还有iat(import address table)<br>的rva(relative virtual address)地址。<br>&nbsp; &nbsp;说到这里,截获windowsapi的整个原理就要真相大白了。实际上所有进程对<br>给定的api函数的调用总是通过pe文件的一个地方来转移的,这就是一个该模块<br>(可以是exe或dll)的“.idata”段中的iat输入地址表(import address table)<br>。在那里有所有本模块调用的其它dll的函数名及地址。对其它dll的函数调用实<br>际上只是跳转到输入地址表,由输入地址表再跳转到dll真正的函数入口。<br><br>具体来说,我们将通过image_import_descriptor数组来访问“.idata”段中引入<br>的dll的信息,然后通过image_thunk_data数组来针对一个被引入的dll访问该<br>dll中被引入的每个函数的信息,找到我们需要截获的函数的跳转地址,然后改<br>成我们自己的函数的地址……具体的做法在后面的关键代码中会有详细的讲解。<br>&nbsp; &nbsp;讲了这么多原理,现在让我们回到“鼠标屏幕取词”的专题上来。除了api函<br>数的截获,要实现“鼠标屏幕取词”,还需要做一些其它的工作,简单的说来,<br>可以把一个完整的取词过程归纳成以下几个步骤:<br>1. 安装鼠标钩子,通过钩子函数获得鼠标消息。<br>&nbsp; 使用到的api函数:setwindowshookex<br>2. 得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画<br>窗口。<br>&nbsp; 使用到的api函数:windowfrompoint,screentoclient,invalidaterect<br>3. 截获对系统函数的调用,取得参数,也就是我们要取的词。<br>&nbsp; &nbsp;对于大多数的windows应用程序来说,如果要取词,我们需要截获的是<br>“gdi32.dll”中的“textouta”函数。<br>我们先仿照textouta函数写一个自己的mytextouta函数,如:<br>bool winapi mytextouta(hdc hdc, int nxstart, int nystart, lpcstr lpszstring,int cbstring)<br>{<br>&nbsp; &nbsp; &nbsp; &nbsp;// 这里进行输出lpszstring的处理<br>&nbsp; &nbsp; &nbsp; // 然后调用正版的textouta函数<br>}<br>&nbsp; &nbsp;把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的<br>hookimportfunction函数来截获进程对textouta函数的调用,跳转到我们的<br>mytextouta函数,完成对输出字符串的捕捉。hookimportfunction的用法:<br><br>&nbsp;hookfuncdesc hd;<br>&nbsp;proc &nbsp; &nbsp; &nbsp; &nbsp; porigfuns;<br>&nbsp;hd.szfunc="textouta";<br>&nbsp;hd.pproc=(proc)mytextouta;<br>&nbsp;hookimportfunction (afxgetinstancehandle(),"gdi32.dll",&amp;hd,porigfuns);<br>下面给出了hookimportfunction的源代码,相信详尽的注释一定不会让您觉得理<br>解截获到底是怎么实现的很难,ok,let’s go:<br><br>///////////////////////////////////////////// begin ///////////////////////////////////////////////////////////////<br>#include &lt;crtdbg.h&gt;<br><br>// 这里定义了一个产生指针的宏<br>#define makeptr(cast, ptr, addvalue) (cast)((dword)(ptr)+(dword)(addvalue))<br><br>// 定义了hookfuncdesc结构,我们用这个结构作为参数传给hookimportfunction函数<br>typedef struct tag_hookfuncdesc<br>{<br>&nbsp; lpcstr szfunc; // the name of the function to hook.<br>&nbsp; proc pproc; &nbsp; &nbsp;// the procedure to blast in.<br>} hookfuncdesc , * lphookfuncdesc;<br><br>// 这个函数监测当前系统是否是windownt<br>bool isnt();<br><br>// 这个函数得到hmodule -- 即我们需要截获的函数所在的dll模块的引入描述符(import descriptor)<br>pimage_import_descriptor getnamedimportdescriptor(hmodule hmodule, lpcstr szimportmodule);<br><br>// 我们的主函数<br>bool hookimportfunction(hmodule hmodule, lpcstr szimportmodule, <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;lphookfuncdesc pahookfunc, proc* paorigfuncs)<br>{<br>/////////////////////// 下面的代码检测参数的有效性 ////////////////////////////<br>&nbsp;_assert(szimportmodule);<br>&nbsp;_assert(!isbadreadptr(pahookfunc, sizeof(hookfuncdesc)));<br>#ifdef _debug<br>&nbsp;if (paorigfuncs) _assert(!isbadwriteptr(paorigfuncs, sizeof(proc)));<br>&nbsp;_assert(pahookfunc.szfunc);<br>&nbsp;_assert(*pahookfunc.szfunc != '/0');<br>&nbsp; &nbsp; &nbsp; &nbsp; _assert(!isbadcodeptr(pahookfunc.pproc));<br>#endif<br>&nbsp;if ((szimportmodule == null) || (isbadreadptr(pahookfunc, sizeof(hookfuncdesc))))<br>&nbsp;{<br>&nbsp; _assert(false);<br>&nbsp; setlasterrorex(error_invalid_parameter, sle_error);<br>&nbsp; return false;<br>&nbsp;}<br>//////////////////////////////////////////////////////////////////////////////<br><br>&nbsp;// 监测当前模块是否是在2gb虚拟内存空间之上<br>&nbsp;// 这部分的地址内存是属于win32进程共享的<br>&nbsp;if (!isnt() &amp;&amp; ((dword)hmodule &gt;= 0x80000000))<br>&nbsp;{<br>&nbsp; _assert(false);<br>&nbsp; setlasterrorex(error_invalid_handle, sle_error);<br>&nbsp; return false;<br>&nbsp;}<br>&nbsp; &nbsp; &nbsp;// 清零<br>&nbsp;if (paorigfuncs) memset(paorigfuncs, null, sizeof(proc)); <br><br>&nbsp;// 调用getnamedimportdescriptor()函数,来得到hmodule -- 即我们需要<br>&nbsp;// 截获的函数所在的dll模块的引入描述符(import descriptor)<br>&nbsp;pimage_import_descriptor pimportdesc = getnamedimportdescriptor(hmodule, szimportmodule);<br>&nbsp;if (pimportdesc == null)<br>&nbsp;return false; // 若为空,则模块未被当前进程所引入<br><br>&nbsp;// &nbsp;从dll模块中得到原始的thunk信息,因为pimportdesc-&gt;firstthunk数组中的原始信息已经<br>&nbsp;// &nbsp;在应用程序引入该dll时覆盖上了所有的引入信息,所以我们需要通过取得pimportdesc-&gt;originalfirstthunk<br>&nbsp;// &nbsp;指针来访问引入函数名等信息<br>&nbsp;pimage_thunk_data porigthunk = makeptr(pimage_thunk_data, hmodule, <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pimportdesc-&gt;originalfirstthunk);<br><br>&nbsp;// &nbsp;从pimportdesc-&gt;firstthunk得到image_thunk_data数组的指针,由于这里在dll被引入时已经填充了<br>&nbsp;// &nbsp;所有的引入信息,所以真正的截获实际上正是在这里进行的<br>&nbsp;pimage_thunk_data prealthunk = makeptr(pimage_thunk_data, hmodule, pimportdesc-&gt;firstthunk);<br><br>&nbsp;// &nbsp;穷举image_thunk_data数组,寻找我们需要截获的函数,这是最关键的部分!<br>&nbsp;while (porigthunk-&gt;u1.function)<br>&nbsp;{<br>&nbsp; // 只寻找那些按函数名而不是序号引入的函数<br>&nbsp; if (image_ordinal_flag != (porigthunk-&gt;u1.ordinal &amp; image_ordinal_flag))<br>&nbsp; {<br>&nbsp; &nbsp;// 得到引入函数的函数名<br>&nbsp; &nbsp;pimage_import_by_name pbyname = makeptr(pimage_import_by_name, hmodule,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;porigthunk-&gt;u1.addressofdata);<br><br>&nbsp; &nbsp;// 如果函数名以null开始,跳过,继续下一个函数 &nbsp; <br>&nbsp; &nbsp;if ('/0' == pbyname-&gt;name[0])<br>&nbsp; &nbsp; continue;<br><br>&nbsp; &nbsp;// bdohook用来检查是否截获成功<br>&nbsp; &nbsp;bool bdohook = false;<br><br>&nbsp; &nbsp;// 检查是否当前函数是我们需要截获的函数<br>&nbsp; &nbsp;if ((pahookfunc.szfunc[0] == pbyname-&gt;name[0]) &amp;&amp;<br>&nbsp; &nbsp; (strcmpi(pahookfunc.szfunc, (char*)pbyname-&gt;name) == 0))<br>&nbsp; &nbsp;{<br>&nbsp; &nbsp; // 找到了!<br>&nbsp; &nbsp; if (pahookfunc.pproc)<br>&nbsp; &nbsp; bdohook = true;<br>&nbsp; &nbsp;}<br>&nbsp; &nbsp;if (bdohook)<br>&nbsp; &nbsp;{<br>&nbsp; &nbsp; // 我们已经找到了所要截获的函数,那么就开始动手吧<br>&nbsp; &nbsp; // 首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取<br>&nbsp; &nbsp; memory_basic_information mbi_thunk;<br>&nbsp; &nbsp; virtualquery(prealthunk, &amp;mbi_thunk, sizeof(memory_basic_information));<br>&nbsp; &nbsp; _assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; page_readwrite, &amp;mbi_thunk.protect));<br><br>&nbsp; &nbsp; // 保存我们所要截获的函数的正确跳转地址<br>&nbsp; &nbsp; if (paorigfuncs)<br>&nbsp; &nbsp; &nbsp; paorigfuncs = (proc)prealthunk-&gt;u1.function;<br><br>&nbsp; &nbsp; // 将image_thunk_data数组中的函数跳转地址改写为我们自己的函数地址!<br>&nbsp; &nbsp; // 以后所有进程对这个系统函数的所有调用都将成为对我们自己编写的函数的调用<br>&nbsp; &nbsp; prealthunk-&gt;u1.function = (pdword)pahookfunc.pproc;<br><br>&nbsp; &nbsp; // 操作完毕!将这一块虚拟内存改回原来的保护状态<br>&nbsp; &nbsp; dword dwoldprotect;<br>&nbsp; &nbsp; _assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mbi_thunk.protect, &amp;dwoldprotect));<br>&nbsp; &nbsp; setlasterror(error_success);<br>&nbsp; &nbsp; return true;<br>&nbsp; &nbsp;}<br>&nbsp; }<br>&nbsp; // 访问image_thunk_data数组中的下一个元素<br>&nbsp; porigthunk++;<br>&nbsp; prealthunk++;<br>&nbsp;}<br>&nbsp;return true;<br>}<br><br>// getnamedimportdescriptor函数的实现<br>pimage_import_descriptor getnamedimportdescriptor(hmodule hmodule, lpcstr szimportmodule)<br>{<br>&nbsp;// 检测参数<br>&nbsp;_assert(szimportmodule);<br>&nbsp;_assert(hmodule);<br>&nbsp;if ((szimportmodule == null) || (hmodule == null))<br>&nbsp;{<br>&nbsp; _assert(false);<br>&nbsp; setlasterrorex(error_invalid_parameter, sle_error);<br>&nbsp; return null;<br>&nbsp;}<br><br>&nbsp;// 得到dos文件头<br>&nbsp;pimage_dos_header pdosheader = (pimage_dos_header) hmodule;<br><br>&nbsp;// 检测是否mz文件头<br>&nbsp;if (isbadreadptr(pdosheader, sizeof(image_dos_header)) || <br>&nbsp; (pdosheader-&gt;e_magic != image_dos_signature))<br>&nbsp;{<br>&nbsp; _assert(false);<br>&nbsp; setlasterrorex(error_invalid_parameter, sle_error);<br>&nbsp; return null;<br>&nbsp;}<br><br>&nbsp;// 取得pe文件头<br>&nbsp;pimage_nt_headers pntheader = makeptr(pimage_nt_headers, pdosheader, pdosheader-&gt;e_lfanew);<br><br>&nbsp;// 检测是否pe映像文件<br>&nbsp;if (isbadreadptr(pntheader, sizeof(image_nt_headers)) || <br>&nbsp; &nbsp;(pntheader-&gt;signature != image_nt_signature))<br>&nbsp;{<br>&nbsp; _assert(false);<br>&nbsp; setlasterrorex(error_invalid_parameter, sle_error);<br>&nbsp; return null;<br>&nbsp;}<br><br>&nbsp;// 检查pe文件的引入段(即 .idata section)<br>&nbsp;if (pntheader-&gt;optionalheader.datadirectory[image_directory_entry_import].virtualaddress == 0)<br>&nbsp; return null;<br><br>&nbsp;// 得到引入段(即 .idata section)的指针<br>&nbsp;pimage_import_descriptor pimportdesc = makeptr(pimage_import_descriptor, pdosheader,<br>&nbsp; pntheader-&gt;optionalheader.datadirectory[image_directory_entry_import].virtualaddress);<br><br>&nbsp;// 穷举pimage_import_descriptor数组寻找我们需要截获的函数所在的模块<br>&nbsp;while (pimportdesc-&gt;name)<br>&nbsp;{<br>&nbsp; pstr szcurrmod = makeptr(pstr, pdosheader, pimportdesc-&gt;name);<br>&nbsp; if (stricmp(szcurrmod, szimportmodule) == 0)<br>&nbsp; &nbsp; &nbsp; break; // 找到!中断循环<br>&nbsp; // 下一个元素<br>&nbsp; pimportdesc++;<br>&nbsp;}<br><br>&nbsp;// 如果没有找到,说明我们寻找的模块没有被当前的进程所引入!<br>&nbsp;if (pimportdesc-&gt;name == null)<br>&nbsp; return null;<br><br>&nbsp;// 返回函数所找到的模块描述符(import descriptor)<br>&nbsp;return pimportdesc;<br>}<br><br>// isnt()函数的实现<br>bool isnt()<br>{<br>&nbsp;osversioninfo stosvi;<br>&nbsp;memset(&amp;stosvi, null, sizeof(osversioninfo));<br>&nbsp;stosvi.dwosversioninfosize = sizeof(osversioninfo);<br>&nbsp;bool bret = getversionex(&amp;stosvi);<br>&nbsp;_assert(true == bret);<br>&nbsp;if (false == bret) return false;<br>&nbsp;return (ver_platform_win32_nt == stosvi.dwplatformid);<br>}<br>/////////////////////////////////////////////// end //////////////////////////////////////////////////////////////////////<br><br>&nbsp; &nbsp;不知道在这篇文章问世之前,有多少朋友尝试过去实现“鼠标屏幕取词”这<br>项充满了挑战的技术,也只有尝试过的朋友才能体会到其间的不易,尤其在探索<br>api函数的截获时,手头的几篇资料没有一篇是涉及到关键代码的,重要的地方<br>都是一笔代过,msdn更是显得苍白而无力,也不知道除了image_import_descriptor<br>和image_thunk_data,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,<br>希望这篇文章对大家能有所帮助。
 
看鼠标回放
 
所谓的钩子(HOOK)机制
 
{屏幕取词}<br>unit UnitE2C;<br><br>interface<br><br>uses<br>&nbsp; Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,<br>&nbsp; StdCtrls;<br><br>type<br>&nbsp; TForm1 = class(TForm)<br>&nbsp; &nbsp; Memo1: TMemo;<br>&nbsp; &nbsp; procedure Memo1MouseDown(Sender: TObject; Button: TMouseButton;<br>&nbsp; &nbsp; &nbsp; Shift: TShiftState; X, Y: Integer);<br>&nbsp; private<br>&nbsp; &nbsp; { Private declarations }<br>&nbsp; public<br>&nbsp; &nbsp; { Public declarations }<br>&nbsp; &nbsp; function IsSeparetor(ch:char):boolean;<br>&nbsp; &nbsp; function GetWord(pos:word):string;<br>&nbsp; end;<br><br>var<br>&nbsp; Form1: TForm1;<br><br>implementation<br><br>{$R *.DFM}<br><br>{ TForm1 }<br><br>function TForm1.GetWord(pos: word): string;<br>var<br>&nbsp; st:string;<br>&nbsp; pos1,pos2:word;<br>&nbsp; i:longint;<br>&nbsp; w:string;<br>begin<br>&nbsp; w:='';<br>&nbsp; pos1:=1;<br>&nbsp; getword:='';<br>&nbsp; st:=Memo1.lines.Text;<br>&nbsp; pos2:=length(st);<br>&nbsp; for i:=pos-1 downto 1 do<br>&nbsp; &nbsp; if IsSeparetor(st) then<br>&nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; pos1:=i+1;<br>&nbsp; &nbsp; &nbsp; &nbsp; break<br>&nbsp; &nbsp; &nbsp; end;<br>&nbsp; for i:=pos to pos2 do<br>&nbsp; &nbsp; if IsSeparetor(st) then<br>&nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; pos2:=i-1;<br>&nbsp; &nbsp; &nbsp; &nbsp; break;<br>&nbsp; &nbsp; &nbsp; end;<br>&nbsp; if pos1&lt;=pos2 then<br>&nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; for i:=pos1 to pos2 do<br>&nbsp; &nbsp; &nbsp; &nbsp; w:=w+st;<br>&nbsp; &nbsp; &nbsp; &nbsp; GetWord:='单词'+w;<br>&nbsp; &nbsp; end;<br>end;<br><br>function TForm1.IsSeparetor(ch: char): boolean;<br>begin<br>&nbsp; IsSeparetor:=false;<br>&nbsp; if ch in [' ',',','.','?',#10,#13] then<br>&nbsp; &nbsp; IsSeparetor:=true<br>end;<br><br>procedure TForm1.Memo1MouseDown(Sender: TObject; Button: TMouseButton;<br>&nbsp; Shift: TShiftState; X, Y: Integer);<br>var<br>&nbsp; lc:longint;<br>&nbsp; CharPos:word;<br>begin<br>&nbsp; lc:=SendMessage(Memo1.Handle,EM_CHARFROMPOS,0,x+(y shl 16));<br>&nbsp; charpos:=word(lc);<br>&nbsp; Memo1.Hint:=GetWord(charpos);<br>end;<br>end.
 
API HOOK、屏幕取词的完整解决方案见我的《delphi深入windows核心编程》一书,<br>解决了IE、win98下的高技术难题,支持windows98/2000/xp,<br>我的主页http://wenjinshan.yeah.net
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部