谁有实现屏幕取词的代码?火急,谢谢(100分)

  • 主题发起人 主题发起人 良驹
  • 开始时间 开始时间

良驹

Unregistered / Unconfirmed
GUEST, unregistred user!
谁有实现屏幕取词的代码?火急,谢谢
我也是个初学者的拉
 
看看这个
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1123349
 
这是一个屏幕取词的DLL代码:
library dll_HookMouse;
uses
SysUtils,
Windows,
Classes,
Messages,
Math,
Dialogs,
U_Def in 'U_Def.pas';
{$R *.RES}
var
hMouseHook : HHOOK;
SpyInstalled : Boolean;
fTimerID : Cardinal;
pShMem : PShareMem;
hMappingFile : THandle;
function InstallSpy:Boolean;
forward;
function UnWiseSpy:Boolean;
forward;
function fExtTextOutA(theDC :HDC;
nXStart, nYStart :integer;
toOptions : Lon
gint;
rect : PRect;
lpStr :PAnsiChar;
nCount :Longint;
Dx: PInteger):BOOL;
stdcall;
var
dwBytes, dwCallingProc : DWORD;
pOldExtTextOut : _ExtTextOutA;
hModuleGDI : THandle;
poOri, poDC, poText, poMouse : TPoint;
Size : TSize;
begin
UnWiseSpy;
GetWindowThreadProcessID(pShMem^.hHookWnd, @dwCallingProc);
try
if pShMem^.bCanSpyNow and (dwCallingProc <> pShMem^.hProc) then
begin
dwBytes := Min(nCount, MaxStringLen);
CopyMemory(@(pShMem^.fStrExtTextOutA), lpStr, dwBytes);
//Get lpStr Content
//The following codes for get the right text
GetDCOrgEx(theDC, poOri);
// 取得本窗口设备相关坐标原点的全局逻辑坐标
poDC.x := nXStart;
poDC.y := nYStart;
//
LPToDP(theDC, poDC, 1);
//全局逻辑坐标转化为设备相关坐标
GetCursorPos(poMouse);
poText.x := poDC.x + poOri.x;
poText.y := poDC.y + poOri.y;
if (GetTextAlign(theDC) and TA_UPDATECP) <> 0 then
begin
GetCurrentPositionEx(theDC, @poOri);
poText.x := poText.x + poOri.x;
poText.y := poText.y + poOri.y;
end;
GetTextExtentPoint(theDC, lpStr, nCount, Size);
// 取得要输出的字符串的实际显示大小
if (poMouse.x >= poText.x) and (poMouse.x <= poText.x + Size.cx) and
(poMouse.y >= poText.y) and (poMouse.y <= poText.y + Size.cy) then
begin
pShMem^.bCanSpyNow := False;
pShMem^.nTimePassed := -1;
end;
pShMem^.fStrExtTextOutA[dwBytes] := Chr(0);
FlushViewOfFile(pShMem, 0);
if dwCallingProc <> pShMem^.hProc then
PostMessage(pShMem^.hProcWnd, WM_MOUSEPT, 2, 2);
end;
if (dwCallingProc = pShMem^.hProc) or pShMem^.bHookExtTextOutA then
begin
hModuleGDI := GetModuleHandle(PChar('GDI32'));
@pOldExtTextOut := GetProcAddress(hModuleGDI, PChar('ExtTextOutA'));
Result := pOldExtTextOut(theDC, nXStart, nYStart, toOptions, rect, lpS
tr, nCount, Dx);
end else
Result := True;
except
Result := False;
end;
SpyInstalled := True;
InstallSpy;
end;
function UnWiseSpy:Boolean;
var
dwBytesWritten, dwOldProtect : DWORD;
pOldExtTextOut : _ExtTextOutA;
hModuleGDI : THandle;
begin
hModuleGDI := GetModuleHandle(PChar('GDI32'));
@pOldExtTextOut := GetProcAddress(hModuleGDI, PChar('ExtTextOutA'));
if not VirtualProtect(@pOldExtTextOut, SizeOf(TLongJump), PAGE_EXECUTE_REA
DWRITE, @dwOldProtect) then
begin
Result := False;
Exit;
end;
if not WriteProcessMemory(GetCurrentProcess, @pOldExtTextOut, @pShMem^.pOl
dExtTextOutA, SizeOf(TLongJump), dwBytesWritten) then
begin
Result := False;
Exit;
end;
if not VirtualProtect(@pOldExtTextOut, SizeOf(TLongJump), dwOldProtect, @d
wBytesWritten) then
begin
Result := False;
Exit;
end;
Result := True;
end;
function InstallSpy:Boolean;
var
dwBytesWritten, dwOldProtect : DWORD;
ljHere : TLongJump;
pOldExtTextOut : _ExtTextOutA;
hModuleGDI : THandle;
begin
hModuleGDI := GetModuleHandle(PChar('GDI32'));
@pOldExtTextOut := GetProcAddress(hModuleGDI, PChar('ExtTextOutA'));
if not VirtualProtect(@pOldExtTextOut, SizeOf(TLongJump), PAGE_EXECUTE_REA
DWRITE, @dwOldProtect) then
begin
Result := False;
Exit;
end;
ljHere.JmpOp := CodeJump;
ljHere.Addr := Pointer( Cardinal(@fExtTextOutA) - Cardinal(@pOldExtTextOut
) - SizeOf(TLongJump) );
if not WriteProcessMemory(GetCurrentProcess, @pOldExtTextOut, @ljHere, Siz
eOf(TLongJump), dwBytesWritten) then
begin
Result := False;
Exit;
end;
if not VirtualProtect(@pOldExtTextOut, SizeOf(TLongJump), dwOldProtect, @d
wBytesWritten) then
begin
Result := False;
Exit;
end;
Result := True;
end;
function MouseHookProc(nCode : integer;
wPar : WParam;
lPar : LParam) : lRes
ult;
stdcall;
var
pMouseInf : TMouseHookStruct;
begin
if (not SpyInstalled) and pShMem^.bHookExtTextOutA then
InstallSpy;
if SpyInstalled and (not pShMem^.bHookExtTextOutA) then
begin
UnwiseSpy;
SpyInstalled := False;
end;
pShMem^.nTimePassed := 0 ;
if (nCode >= 0) and (wPar = WM_MOUSEMOVE) then
begin
pMouseInf := (PMouseHookStruct(lPar))^;
if (pShMem^.pMouse.x <> pMouseInf.pt.x) or
(pShMem^.pMouse.y <> pMouseInf.pt.y) then
begin
if nCode = HC_NOREMOVE then
pShMem^.fStrMouseQueue := 'Not removed from the queue'
else
//then
HC_ACTION
pShMem^.fStrMouseQueue := 'Removed from the queue';
pShMem^.pMouse := pMouseInf.pt;
pShMem^.hHookWnd := pMouseInf.hwnd;
PostMessage(pShMem^.hProcWnd, WM_MOUSEPT, 1, 1);
//1 indicates mouse m
essage
end;
end;
FlushViewOfFile(pShMem, 0);
Result := CallNextHookEx(hMouseHook, nCode, wPar, lPar);
end;
procedure fOnTimer(theWnd : HWND;
msg, idTimer : Cardinal;
dwTime : DWORD);f
ar pascal;
//CallBack Type
begin
if pShMem^.nTimePassed = -1 then
Exit;
pShMem^.nTimePassed := pShMem^.nTimePassed + 1;
if pShMem^.nTimePassed > 21 then
begin
pShMem^.nTimePassed := 21;
FlushViewOfFile(pShMem, 0);
Exit;
end;
if pShMem^.nTimePassed > 20 then
begin
pShMem^.bCanSpyNow := True;
FlushViewOfFile(pShMem, 0);
SetWindowPos(pShMem^.hWndPseudo, HWND_TOPMOST, pShMem^.pMouse.x, pShMem^
.pMouse.y, 1, 8, SWP_NOACTIVATE or SWP_SHOWWINDOW);
ShowWindow(pShMem^.hWndPseudo , SW_HIDE);
end;
end;
function MouseWndProc(theWnd : HWND;
theMess : Cardinal;
wPar : wParam;
lPar
: lParam): LResult;stdcall;
begin
case theMess of
WM_CLOSE :
begin
DestroyWindow(theWnd);
PostQuitMessage(0);
end;
else
begin
Result := DefWindowProc(theWnd, theMess, wPar, lPar);
Exit;
end;
end;
Result := 0;
end;
function InstallMouseHook(hInst : LongWord):Boolean;
begin
hMouseHook := SetWindowsHookEx(WH_MOUSE,
MouseHookProc,
GetModuleHandle(PChar('dll_HookMouse')),
0);
if hMouseHook = 0 then
begin
Result := False;
Exit;
end;
pShMem^.hWndPseudo := CreateWindowEx(WS_EX_TOPMOST or WS_EX_TOOLWINDOW,
'ZL_MOUSE_WND_PSEUDO',
'ZL_MOUSE_WND_PSEUDO',
WS_CLIPSIBLINGS or WS_POPUP ,
0, 0, 1, 8,
0, 0,
hInst,
nil);
ShowWindow(pShMem^.hWndPseudo, SW_HIDE);
UpdateWindow(pShMem^.hWndPseudo);
fTimerID := SetTimer(0, 0, 10, @fOnTimer);
FlushViewOfFile(pShMem, 0);
Result := True;
end;
function UnWiseMouseHook:Boolean;
begin
KillTimer(0, fTimerID);
DestroyWindow(pShMem^.hWndPseudo);
if SpyInstalled then
UnWiseSpy;
pShMem^.bHookExtTextOutA := False;
FlushViewOfFile(pShMem, 0);
Result := UnHookWindowsHookEx(hMouseHook);
end;
procedure DllEntry(nReason : integer);
begin
case nReason Of
DLL_PROCESS_ATTACH:
begin
hMappingFile := CreateFileMapping($FFFFFFFF,
nil,
PAGE_READWRITE,
0,
SizeOf(TShareMem),
PChar(MappingFileName));
if hMappingFile<>0 then
//if h..=0 , the work isdo
ne by OS
begin
pShMem := PShareMem( MapViewOfFile(hMappingFile,
FILE_MAP_WRITE,
0, //hi_order offset where mapp
ing begin
s
0, //lo_order offset where mapp
ing begin
s
0) );
//Size of the mapping
if pShMem = nil then
begin
CloseHandle(hMappingFile);
ShowMessage('Cannot create the Share Memory Block!');
end;
end else
ShowMessage('Cannot create the Share Memory Block!');
end;
DLL_PROCESS_DETACH:
begin
UnwiseSpy;
UnMapViewOfFile(pShMem);
CloseHandle(hMappingFile);
end;
else
;
end;
end;
exports
MouseWndProc,
InstallMouseHook,
UnWiseMouseHook;
begin
DllProc := @DllEntry;
DllEntry(DLL_PROCESS_ATTACH);
end.
-------------- ----------------
下面是dll的接口文件:
unit U_Def;
interface
uses
Messages, Windows;
const
WM_MOUSEPT = WM_USER + 1000 + Ord('M') + Ord('P') + Ord('T');
MappingFileName = 'Mapping File By Raphael';
MaxStringLen = 50;
CodeJump = $E9909090;
type
PInt = ^integer;
_ExtTextOutA = function (theDC :HDC;
nXStart, nYStart :integer;
toOptions
: Longint;
rect : PRect;
lpStr :PAnsiChar;
nCount :integer;
Dx : PInteger)
:BOOL;
stdcall;
_PExtTextOutA = ^_ExtTextOutA;
TLongJump = packed record
JmpOp : Cardinal;
Addr : Pointer;
end;
TShareMem = packed record
hProcWnd : HWND;
//The main window of the program
hHookWnd : HWND;
//The window currently being hooked
hWndPseudo : HWND;
//The pseudo window used to repaint the other
window
hProc : THandle;
//The process ID of the main program
pMouse : TPoint;
//the mouse position
fStrMouseQueue : array [0..MaxStringLen] of Char;
//mouse info
nTimePassed : integer;
//the time passed since last time's mousemove
bCanSpyNow : Boolean;
bHookExtTextOutA : Boolean;
pOldExtTextOutA : TLongJump;
fStrExtTextOutA : array [0..MaxStringLen] of Char;
end;
PShareMem = ^TShareMem;
implementation
end.
--------------------
下面是主窗体文件:
unit U_MouseHook;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, U_Def;
type
TF_MouseHook = class(TForm)
Label1: TLabel;
e_MouseInfo: TEdit;
btn_HookMouse: TButton;
Label2: TLabel;
e_ExtTextOutA: TEdit;
btn_HookExtTextOutA: TButton;
procedure btn_HookMouseClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure btn_HookExtTextOutAClick(Sender: TObject);
private
fWndClosed, fbMouseHookInstalled : Boolean;
hMapObj : THandle;
pShMem : PShareMem;
procedure getMouseInfo(var theMess:TMessage);
message WM_MOUSEPT;
public
{ Public declarations }
end;
function InstallMouseHook(hInst : LongWord) : Boolean;
external 'dll_HookMouse.dll';
function UnWiseMouseHook : Boolean;
external 'dll_HookMouse.dll';
function MouseWndProc(theWnd : HWND;
theMess : Cardinal;
wPar : wParam;
lPar : lParam): LResult;
stdcall;
external 'dll_HookMouse.dll';
var
F_MouseHook: TF_MouseHook;
implementation
{$R *.DFM}
procedure TF_MouseHook.btn_HookMouseClick(Sender: TObject);
begin
if not fbMouseHookInstalled then
begin
fbMouseHookInstalled := InstallMouseHook(hInstance);
if fbMouseHookInstalled then
begin
btn_HookMouse.Caption := 'Stop!';
btn_HookExtTextOutA.Enabled := True;
end else
ShowMessage('Cannot hook mouse!');
end else
begin
fbMouseHookInstalled := not UnWiseMouseHook;
if not fbMouseHookInstalled then
begin
btn_HookMouse.Caption := 'Hook Mouse';
btn_HookExtTextOutA.Enabled := False;
btn_HookExtTextOutA.Caption := 'Hook ExtTextOutA';
pShMem^.bHookExtTextOutA := False;
FlushViewOfFile(pShMem, 0);
end else
ShowMessage('Cannot unhook mouse!');
end;
end;
procedure TF_MouseHook.getMouseInfo(var theMess : TMessage);
begin
if fWndClosed then
Exit;
if theMess.LParam = 1 then
//Get the Mouse info to display
e_MouseInfo.Text := 'X:' + IntToStr(pShMem^.pMouse.x) + ' ' +
'Y:' + IntToStr(pShMem^.pMouse.y) + ' ' +
'HWND:0x' + IntToHex(pShMem^.hHookWnd, 8) + ' ' +
pShMem^.fStrMouseQueue
else
if theMess.LParam = 2 then
//Get the ExtTextOutA display
e_ExtTextOutA.Text := pShMem^.fStrExtTextOutA;
end;

procedure TF_MouseHook.FormCreate(Sender: TObject);
var
hModuleGDI : THandle;
wc : TWndClass;
begin
hMapObj := OpenFileMapping(FILE_MAP_WRITE, //Get full access of the mapping file
False, //Not inheritable
LPCTSTR(MappingFileName));
//Name of the mapping file
if hMapObj = 0 then
begin
ShowMessage('Cannot locate the Share Memory Block!');
Halt;
end;
pShMem := PShareMem( MapViewOfFile(hMapObj,
FILE_MAP_WRITE,
0, //hi_order offset where mapping begin
s
0, //lo_order offset where mapping begin
s
0) );
//Size of the mapping
if pShMem = nil then
begin
ShowMessage('Map File Mapping Failed! Error '+ IntToStr(GetLastError));
CloseHandle(hMapObj);
Halt;
end;
FillChar(pShMem^, SizeOf(TShareMem), 0);
hModuleGDI := GetModuleHandle(PChar('GDI32'));
if hModuleGDI = 0 then
begin
ShowMessage('Cannot get module GDI32! Error ' + IntToStr(GetLastError));
UnmapViewOfFile(pShMem);
CloseHandle(hMapObj);
Halt;
end;
CopyMemory(@pShMem^.pOldExtTextOutA, GetProcAddress(hModuleGDI, PChar('ExtTextOutA')), SizeOf(TLongJump));
pShMem^.hProcWnd := Self.Handle;
GetWindowThreadProcessID(Self.Handle, @pShMem^.hProc);
pShMem^.bHookExtTextOutA := False;
pShMem^.bCanSpyNow := False;
fbMouseHookInstalled := False;
FlushViewOfFile(pShMem, 0);
wc.style := 0;
wc.lpfnWndProc := TFNWndProc(@MouseWndProc);
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
wc.hInstance := HInstance;
wc.hIcon := 0 ;
wc.hCursor := 0 ;
wc.hbrBackground := 0 ;
wc.lpszMenuName := nil;
wc.lpszClassName := 'ZL_MOUSE_WND_PSEUDO';
// register the class for the main window
Windows.RegisterClass(wc);
fWndClosed := False;
end;

procedure TF_MouseHook.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if fbMouseHookInstalled then
UnWiseMouseHook;
UnMapViewOfFile(pShMem);
CloseHandle(hMapObj);
Windows.UnRegisterClass('ZL_MOUSE_WND_PSEUDO', hInstance);
fWndClosed := True;
end;
procedure TF_MouseHook.btn_HookExtTextOutAClick(Sender: TObject);
begin
if pShMem^.bHookExtTextOutA then
btn_HookExtTextOutA.Caption := 'Hook ExtTextOutA'
else
btn_HookExtTextOutA.Caption := 'Stop!';
pShMem^.bHookExtTextOutA := not pShMem^.bHookExtTextOutA;
FlushViewOfFile(pShMem, 0);
end;
//RaiseLastWin32Error can be used to create a GetLastError
//Message
end.
 
这是一篇文章:
鼠标屏幕取词技术的原理和实现
白瑜
“鼠标屏幕取词”技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在windows系统中实现却是非常复杂的,总的来说有两种实现方式:
第一种:采用截获对部分gdi的api调用来实现,如textout,textouta等。
第二种:对每个设备上下文(dc)做一分copy,并跟踪所有修改上下文(dc)的操作。
第二种方法更强大,但兼容性不好,而第一种方法使用的截获windowsapi的调用,这项技术的强大可能远远超出了您的想象,毫不夸张的说,利用windowsapi拦截技术,你可以改造整个操作系统,事实上很多外挂式windows中文平台就是这么实现的!而这项技术也正是这篇文章的主题。
截windowsapi的调用,具体的说来也可以分为两种方法:
第一种方法通过直接改写winapi 在内存中的映像,嵌入汇编代码,使之被调用时跳转到指定的地址运行来截获;第二种方法则改写iat(import address table 输入地址表),重定向winapi函数的调用来实现对winapi的截获。
第一种方法的实现较为繁琐,而且在win95、98下面更有难度,这是因为虽然微软说win16的api只是为了兼容性才保留下来,程序员应该尽可能地调用32位的api,实际上根本就不是这样!win 9x内部的大部分32位api经过变换调用了同名的16位api,也就是说我们需要在拦截的函数中嵌入16位汇编代码!
我们将要介绍的是第二种拦截方法,这种方法在win95、98和nt下面运行都比较稳定,兼容性较好。由于需要用到关于windows虚拟内存的管理、打破进程边界墙、向应用程序的进程空间中注入代码、pe(portable executable)文件格式和iat(输入地址表)等较底层的知识,所以我们先对涉及到的这些知识大概地做一个介绍,最后会给出拦截部分的关键代码。
先说windows虚拟内存的管理。windows9x给每一个进程分配了4gb的地址空间,对于nt来说,这个数字是2gb,系统保留了2gb到 4gb之间的地址空间禁止进程访问,而在win9x中,2gb到4gb这部分虚拟地址空间实际上是由所有的win32进程所共享的,这部分地址空间加载了共享win32 dll、内存映射文件和vxd、内存管理器和文件系统码,win9x中这部分对于每一个进程都是可见的,这也是win9x操作系统不够健壮的原因。win9x中为16位操作系统保留了0到4mb的地址空间,而在4mb到2gb之间也就是win32进程私有的地址空间,由于 每个进程的地址空间都是相对独立的,也就是说,如果程序想截获其它进程中的api调用,就必须打破进程边界墙,向其它的进程中注入截获api调用的代码,这项工作我们交给钩子函数(setwindowshookex)来完成,关于如何创建一个包含系统钩子的动态链接库,《电脑高手杂志》在第?期已经有过专题介绍了,这里就不赘述了。所有系统钩子的函数必须要在动态库里,这样的话,当进程隐式或显式调用一个动态库里的函数时,系统会把这个动态库映射到这个进程的虚拟地址空间里,这使得dll成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈,也就是说动态链接库中的代码被钩子函数注入了其它gui进程的地址空间(非gui进程,钩子函数就无能为力了),
当包含钩子的dll注入其它进程后,就可以取得映射到这个进程虚拟内存里的各个模块(exe和dll)的基地址,如:
hmodule hmodule=getmodulehandle(“mypro.exe”);
在mfc程序中,我们可以用afxgetinstancehandle()函数来得到模块的基地址。exe和dll被映射到虚拟内存空间的什么地方是由它们的基地址决定的。它们的基地址是在链接时由链接器决定的。当你新建一个win32工程时,vc++链接器使用缺省的基地址0x00400000。可以通过链接器的base选项改变模块的基地址。exe通常被映射到虚拟内存的0x00400000处,dll也随之有不同的基地址,通常被映射到不同进程
的相同的虚拟地址空间处。
系统将exe和dll原封不动映射到虚拟内存空间中,它们在内存中的结构与磁盘上的静态文件结构是一样的。即pe (portable executable) 文件格式。我们得到了进程模块的基地址以后,就可以根据pe文件的格式穷举这个模块的image_import_descriptor数组,看看进程空间中是否引入了我们需要截获的函数所在的动态链接库,比如需要截获“textouta”,就必须检查“gdi32.dll”是否被引入了。说到这里,我们有必要介绍一下pe文件的格式,如右图,这是pe文件格式的大致框图,最前面是文件头,我们不必理会,从pe file optional header后面开始,就是文件中各个段的说明,说明后面才是真正的段数据,而实际上我们关心的只有一个段,那就是“.idata”段,这个段中包含了所有的引入函数信息,还有iat(import address table)的rva(relative virtual address)地址。
说到这里,截获windowsapi的整个原理就要真相大白了。实际上所有进程对给定的api函数的调用总是通过pe文件的一个地方来转移的,这就是一个该模块(可以是exe或dll)的“.idata”段中的iat输入地址表(import address table)。在那里有所有本模块调用的其它dll的函数名及地址。对其它dll的函数调用实际上只是跳转到输入地址表,由输入地址表再跳转到dll真正的函数入口。
具体来说,我们将通过image_import_descriptor数组来访问“.idata”段中引入的dll的信息,然后通过image_thunk_data数组来针对一个被引入的dll访问该dll中被引入的每个函数的信息,找到我们需要截获的函数的跳转地址,然后改成我们自己的函数的地址……具体的做法在后面的关键代码中会有详细的讲解。
讲了这么多原理,现在让我们回到“鼠标屏幕取词”的专题上来。除了api函数的截获,要实现“鼠标屏幕取词”,还需要做一些其它的工作,简单的说来,可以把一个完整的取词过程归纳成以下几个步骤:
1. 安装鼠标钩子,通过钩子函数获得鼠标消息。
使用到的api函数:setwindowshookex
2. 得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画窗口。
使用到的api函数:windowfrompoint,screentoclient,invalidaterect
3. 截获对系统函数的调用,取得参数,也就是我们要取的词。
对于大多数的windows应用程序来说,如果要取词,我们需要截获的是“gdi32.dll”中的“textouta”函数。
我们先仿照textouta函数写一个自己的mytextouta函数,如:
bool winapi mytextouta(hdc hdc, int nxstart, int nystart, lpcstr lpszstring,int cbstring)
{
// 这里进行输出lpszstring的处理
// 然后调用正版的textouta函数
}
把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的hookimportfunction函数来截获进程
对textouta函数的调用,跳转到我们的mytextouta函数,完成对输出字符串的捕捉。hookimportfunction的
用法:
hookfuncdesc hd;
proc porigfuns;
hd.szfunc="textouta";
hd.pproc=(proc)mytextouta;
hookimportfunction (afxgetinstancehandle(),"gdi32.dll",&amp;hd,porigfuns);
下面给出了hookimportfunction的源代码,相信详尽的注释一定不会让您觉得理解截获到底是怎么实现的
很难,ok,let’s go:
///////////////////////////////////////////// begin
///////////////////////////////////////////////////////////////
#include <crtdbg.h>
// 这里定义了一个产生指针的宏
#define makeptr(cast, ptr, addvalue) (cast)((dword)(ptr)+(dword)(addvalue))
// 定义了hookfuncdesc结构,我们用这个结构作为参数传给hookimportfunction函数
typedef struct tag_hookfuncdesc
{
lpcstr szfunc;
// the name of the function to hook.
proc pproc;
// the procedure to blast in.
} hookfuncdesc , * lphookfuncdesc;
// 这个函数监测当前系统是否是windownt
bool isnt();
// 这个函数得到hmodule -- 即我们需要截获的函数所在的dll模块的引入描述符(import descriptor)
pimage_import_descriptor getnamedimportdescriptor(hmodule hmodule, lpcstr szimportmodule);
// 我们的主函数
bool hookimportfunction(hmodule hmodule, lpcstr szimportmodule,
lphookfuncdesc pahookfunc, proc* paorigfuncs)
{
/////////////////////// 下面的代码检测参数的有效性 ////////////////////////////
_assert(szimportmodule);
_assert(!isbadreadptr(pahookfunc, sizeof(hookfuncdesc)));
#ifdef _debug
if (paorigfuncs) _assert(!isbadwriteptr(paorigfuncs, sizeof(proc)));
_assert(pahookfunc.szfunc);
_assert(*pahookfunc.szfunc != '/0');
_assert(!isbadcodeptr(pahookfunc.pproc));
#endif
if ((szimportmodule == null) || (isbadreadptr(pahookfunc, sizeof(hookfuncdesc))))
{
_assert(false);
setlasterrorex(error_invalid_parameter, sle_error);
return false;
}
//////////////////////////////////////////////////////////////////////////////
// 监测当前模块是否是在2gb虚拟内存空间之上
// 这部分的地址内存是属于win32进程共享的
if (!isnt() &amp;&amp;
((dword)hmodule >= 0x80000000))
{
_assert(false);
setlasterrorex(error_invalid_handle, sle_error);
return false;
}
// 清零
if (paorigfuncs) memset(paorigfuncs, null, sizeof(proc));

// 调用getnamedimportdescriptor()函数,来得到hmodule -- 即我们需要
// 截获的函数所在的dll模块的引入描述符(import descriptor)
pimage_import_descriptor pimportdesc = getnamedimportdescriptor(hmodule, szimportmodule);
if (pimportdesc == null)
return false;
// 若为空,则模块未被当前进程所引入
// 从dll模块中得到原始的thunk信息,因为pimportdesc->firstthunk数组中的原始信息已经
// 在应用程序引入该dll时覆盖上了所有的引入信息,所以我们需要通过取得pimportdesc->originalfirstthunk
// 指针来访问引入函数名等信息
pimage_thunk_data porigthunk = makeptr(pimage_thunk_data, hmodule,
pimportdesc->originalfirstthunk);
// 从pimportdesc->firstthunk得到image_thunk_data数组的指针,由于这里在dll被引入时已经填充了
// 所有的引入信息,所以真正的截获实际上正是在这里进行的
pimage_thunk_data prealthunk = makeptr(pimage_thunk_data, hmodule, pimportdesc->firstthunk);
// 穷举image_thunk_data数组,寻找我们需要截获的函数,这是最关键的部分!
while (porigthunk->u1.function)
{
// 只寻找那些按函数名而不是序号引入的函数
if (image_ordinal_flag != (porigthunk->u1.ordinal &amp;
image_ordinal_flag))
{
// 得到引入函数的函数名
pimage_import_by_name pbyname = makeptr(pimage_import_by_name, hmodule,
porigthunk->u1.addressofdata);
// 如果函数名以null开始,跳过,继续下一个函数
if ('/0' == pbyname->name[0])
continue;
// bdohook用来检查是否截获成功
bool bdohook = false;
// 检查是否当前函数是我们需要截获的函数
if ((pahookfunc.szfunc[0] == pbyname->name[0]) &amp;&amp;
(strcmpi(pahookfunc.szfunc, (char*)pbyname->name) == 0))
{
// 找到了!
if (pahookfunc.pproc)
bdohook = true;
}
if (bdohook)
{
// 我们已经找到了所要截获的函数,那么就开始动手吧
// 首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取
memory_basic_information mbi_thunk;
virtualquery(prealthunk, &amp;mbi_thunk, sizeof(memory_basic_information));
_assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize,
page_readwrite, &amp;mbi_thunk.protect));
// 保存我们所要截获的函数的正确跳转地址
if (paorigfuncs)
paorigfuncs = (proc)prealthunk->u1.function;
// 将image_thunk_data数组中的函数跳转地址改写为我们自己的函数地址!
// 以后所有进程对这个系统函数的所有调用都将成为对我们自己编写的函数的调用
prealthunk->u1.function = (pdword)pahookfunc.pproc;
// 操作完毕!将这一块虚拟内存改回原来的保护状态
dword dwoldprotect;
_assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize,
mbi_thunk.protect, &amp;dwoldprotect));
setlasterror(error_success);
return true;
}
}
// 访问image_thunk_data数组中的下一个元素
porigthunk++;
prealthunk++;
}
return true;
}
// getnamedimportdescriptor函数的实现
pimage_import_descriptor getnamedimportdescriptor(hmodule hmodule, lpcstr szimportmodule)
{
// 检测参数
_assert(szimportmodule);
_assert(hmodule);
if ((szimportmodule == null) || (hmodule == null))
{
_assert(false);
setlasterrorex(error_invalid_parameter, sle_error);
return null;
}
// 得到dos文件头
pimage_dos_header pdosheader = (pimage_dos_header) hmodule;
// 检测是否mz文件头
if (isbadreadptr(pdosheader, sizeof(image_dos_header)) ||
(pdosheader->e_magic != image_dos_signature))
{
_assert(false);
setlasterrorex(error_invalid_parameter, sle_error);
return null;
}
// 取得pe文件头
pimage_nt_headers pntheader = makeptr(pimage_nt_headers, pdosheader, pdosheader->e_lfanew);
// 检测是否pe映像文件
if (isbadreadptr(pntheader, sizeof(image_nt_headers)) ||
(pntheader->signature != image_nt_signature))
{
_assert(false);
setlasterrorex(error_invalid_parameter, sle_error);
return null;
}
// 检查pe文件的引入段(即 .idata section)
if (pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress == 0)
return null;
// 得到引入段(即 .idata section)的指针
pimage_import_descriptor pimportdesc = makeptr(pimage_import_descriptor, pdosheader,
pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress);
// 穷举pimage_import_descriptor数组寻找我们需要截获的函数所在的模块
while (pimportdesc->name)
{
pstr szcurrmod = makeptr(pstr, pdosheader, pimportdesc->name);
if (stricmp(szcurrmod, szimportmodule) == 0)
break;
// 找到!中断循环
// 下一个元素
pimportdesc++;
}
// 如果没有找到,说明我们寻找的模块没有被当前的进程所引入!
if (pimportdesc->name == null)
return null;
// 返回函数所找到的模块描述符(import descriptor)
return pimportdesc;
}
// isnt()函数的实现
bool isnt()
{
osversioninfo stosvi;
memset(&amp;stosvi, null, sizeof(osversioninfo));
stosvi.dwosversioninfosize = sizeof(osversioninfo);
bool bret = getversionex(&amp;stosvi);
_assert(true == bret);
if (false == bret) return false;
return (ver_platform_win32_nt == stosvi.dwplatformid);
}
/////////////////////////////////////////////// end //////////////////////////////////////////////////////////////////////
不知道在这篇文章问世之前,有多少朋友尝试过去实现“鼠标屏幕取词”这项充满了挑战的技术,也只有尝试过的朋友才能体会到其间的不易,尤其在探索api函数的截获时,手头的几篇资料没有一篇是涉及到关键代码的,重要的地方都是一笔代过,msdn更是显得苍白而无力,也不知道除了image_import_descriptor和image_thunk_data,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,希望这篇文章对大家能有所帮助。
 
谢谢大家的支持!!!
 
文章是不错,但是,能不能以后给断下行啊。
 
屏幕取词delphi实现 作者 甘化新
http://cozo.diy.163.com/FreeDict.zip
屏幕取词源程序(值得收藏,可延伸制作出很多特效的呀!) [推荐]
文件大小: 316 KB
应用平台: Win98/NT/Win2000/
更新日期: 2002-5-22 19:53:55
软件性质: 网友源代码
下载次数: 174 次
下载地址: http://epro2000.myetang.com/source/screen.zip
其它链接: http://wolfsoft.nugoo.com/example/file/screen.zip
简介: 屏幕取词的源程序,比较不错.
 
屏幕取词的完整解决方案见我的《delphi深入windows核心编程》一书,
解决了IE、win98下的高技术难题,支持windows98/2000/xp,
我的主页http://wenjinshan.yeah.net
 
http://www.chinabcb.com/bbs/
 
后退
顶部