(转载).鼠标屏幕取词原理

上一篇 / 下一篇  2008-02-20 18:16:44 / 个人分类:笔记

]W8W#lGFh0发表:2005-10-26 9:39:48 出处:你的博客网(yourblog.org) 作者:独孤九剑
:` pR/M+f5j3c0email: mikaiyue@sina.com
s?2F8us}]@0日期:6/21/2001 10:21:49 AM51Testing软件测试网}2Ms qSL.@
51Testing软件测试网%Ja _wx,s

51Testing软件测试网 ngN7k$q gud"q,[

51Testing软件测试网%b~jP+S1a7O#n
“鼠标屏幕取词”技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在windows系统中实现却是非常复杂的,总的来说有两种实现方式:51Testing软件测试网xr%j$qAm[9e ~
第一种:采用截获对部分gdi的api调用来实现,如textout,textouta等。51Testing软件测试网#T;t,L1M`y+P|;{g
第二种:对每个设备上下文(dc)做一分copy,并跟踪所有修改上下文(dc)的操作。
b_FUz%nI0第二种方法更强大,但兼容性不好,而第一种方法使用的截获windowsapi的调用,这项技术的强大可能远远超出了您的想象,毫不夸张的说,利用windowsapi拦截技术,你可以改造整个操作系统,事实上很多外挂式windows中文平台就是这么实现的!而这项技术也正是这篇文章的主题。51Testing软件测试网w0[;WrZ:J!G
截windowsapi的调用,具体的说来也可以分为两种方法:
Z U2^4R{$DI0第一种方法通过直接改写winapi 在内存中的映像,嵌入汇编代码,使之被调用时跳转到指定的地址运行来截获;第二种方法则改写iat(import address table 输入地址表),重定向winapi函数的调用来实现对winapi的截获。
f:ZUf)[+r2b-Z0第一种方法的实现较为繁琐,而且在win95、98下面更有难度,这是因为虽然微软说win16的api只是为了兼容性才保留下来,程序员应该尽可能地调用32位的api,实际上根本就不是这样!win 9x内部的大部分32位api经过变换调用了同名的16位api,也就是说我们需要在拦截的函数中嵌入16位汇编代码!51Testing软件测试网)`;L*XWE.kD
我们将要介绍的是第二种拦截方法,这种方法在win95、98和nt下面运行都比较稳定,兼容性较好。由于需要用到关于windows虚拟内存的管理、打破进程边界墙、向应用程序的进程空间中注入代码、pe(portable executable)文件格式和iat(输入地址表)等较底层的知识,所以我们先对涉及到的这些知识大概地做一个介绍,最后会给出拦截部分的关键代码。51Testing软件测试网es0ZQp(oq$MF,Kr
先说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进程,钩子函数就无能为力了),51Testing软件测试网8m W:GX#IrcC&V
当包含钩子的dll注入其它进程后,就可以取得映射到这个进程虚拟内存里的各个模块(exe和dll)的基地址,如:
5TcGv u/h0hmodule hmodule=getmodulehandle(“mypro.exe”);51Testing软件测试网Q|m J kB
在mfc程序中,我们可以用afxgetinstancehandle()函数来得到模块的基地址。exe和dll被映射到虚拟内存空间的什么地方是由它们的基地址决定的。它们的基地址是在链接时由链接器决定的。当你新建一个win32工程时,vc++链接器使用缺省的基地址0x00400000。可以通过链接器的base选项改变模块的基地址。exe通常被映射到虚拟内存的0x00400000处,dll也随之有不同的基地址,通常被映射到不同进程51Testing软件测试网k$ca{C7zZ}(J
的相同的虚拟地址空间处。51Testing软件测试网+b l{"\NG
系统将exe和dll原封不动映射到虚拟内存空间中,它们在内存中的结构与磁盘上的静态文件结构是一样的。即pe (portable executable) 文件格式。我们得到了进程模块的基地址以后,就可以根据pe文件的格式穷举这个模块的image_import_descrīptor数组,看看进程空间中是否引入了我们需要截获的函数所在的动态链接库,比如需要截获“textouta”,就必须检查“gdi32.dll”是否被引入了。说到这里,我们有必要介绍一下pe文件的格式,如右图,这是pe文件格式的大致框图,最前面是文件头,我们不必理会,从pe file optional header后面开始,就是文件中各个段的说明,说明后面才是真正的段数据,而实际上我们关心的只有一个段,那就是“.idata”段,这个段中包含了所有的引入函数信息,还有iat(import address table)的rva(relative virtual address)地址。51Testing软件测试网]U7l@7X+e4i3m
说到这里,截获windowsapi的整个原理就要真相大白了。实际上所有进程对给定的api函数的调用总是通过pe文件的一个地方来转移的,这就是一个该模块(可以是exe或dll)的“.idata”段中的iat输入地址表(import address table)。在那里有所有本模块调用的其它dll的函数名及地址。对其它dll的函数调用实际上只是跳转到输入地址表,由输入地址表再跳转到dll真正的函数入口。
DRW s"x,ot1n]051Testing软件测试网/G*?3bO$Z.~S
具体来说,我们将通过image_import_descrīptor数组来访问“.idata”段中引入的dll的信息,然后通过image_thunk_data数组来针对一个被引入的dll访问该dll中被引入的每个函数的信息,找到我们需要截获的函数的跳转地址,然后改成我们自己的函数的地址……具体的做法在后面的关键代码中会有详细的讲解。51Testing软件测试网\ZS1FY_6Z1|
讲了这么多原理,现在让我们回到“鼠标屏幕取词”的专题上来。除了api函数的截获,要实现“鼠标屏幕取词”,还需要做一些其它的工作,简单的说来,可以把一个完整的取词过程归纳成以下几个步骤:51Testing软件测试网K%T6OO5k]N
1. 安装鼠标钩子,通过钩子函数获得鼠标消息。
$MykO I F ElGR0使用到的api函数:setwindowshookex
'mJB Yb Nk02. 得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画窗口。
V b[c*lup5a^1s%f0使用到的api函数:windowfrompoint,screentoclient,invalidaterect51Testing软件测试网9v8PEH,Fw$Z
3. 截获对系统函数的调用,取得参数,也就是我们要取的词。
JR?/e?u%p!D"b0对于大多数的windows应用程序来说,如果要取词,我们需要截获的是“gdi32.dll”中的“textouta”函数。51Testing软件测试网'Bw8G{s0^7X,L4X
我们先仿照textouta函数写一个自己的mytextouta函数,如:
y%?1R i ^`J9[0bool winapi mytextouta(hdc hdc, int nxstart, int nystart, lpcstr lpszstring,int cbstring)51Testing软件测试网&K7?? sP@ c
{
dr,?'N7Ul.w0// 这里进行输出lpszstring的处理51Testing软件测试网V.C(_ p p I
// 然后调用正版的textouta函数51Testing软件测试网 Fi xV.~Oc&G3E(et
}
:Q.i%U|d0把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的hookimportfunction函数来截获进程
{iK6JiGS(K U`z0对textouta函数的调用,跳转到我们的mytextouta函数,完成对输出字符串的捕捉。hookimportfunction的
(l[ }Z%K_0用法:51Testing软件测试网C!|D]Ei
hookfuncdesc hd;51Testing软件测试网-t v`W:j,e4M
proc porigfuns;51Testing软件测试网"dX(^0KQcp
hd.szfunc="textouta";
Q#[LI3@3jUt0hd.pproc=(proc)mytextouta;
7}U3vd5h^0hookimportfunction (afxgetinstancehandle(),"gdi32.dll",&hd,porigfuns);
'j!w ex ZBJ |jP0下面给出了hookimportfunction的源代码,相信详尽的注释一定不会让您觉得理解截获到底是怎么实现的
mHt7atU&KBb[ z0很难,ok,let’s go:51Testing软件测试网^3lHbm

^Zb5pW'q)D0///////////////////////////////////////////// begin ///////////////////////////////////////////////////////////////51Testing软件测试网IP(ac$to DK
#include <crtdbg.h>
[ ?@i-d.c|051Testing软件测试网#zaa3\%F*P#Ig$z
// 这里定义了一个产生指针的宏
0g0I0u/~2XSM3r~F;]0#define makeptr(cast, ptr, addvalue) (cast)((dword)(ptr)+(dword)(addvalue))51Testing软件测试网(AC;m*en:U#O B]

lNn8z q!z0// 定义了hookfuncdesc结构,我们用这个结构作为参数传给hookimportfunction函数51Testing软件测试网:L*y#{8I!@ur Q
typedef struct tag_hookfuncdesc
9@$F"cI ?W^ {0{
F`g9UZ P0lpcstr szfunc; // the name of the function to hook.
"s!@kO fD0proc pproc; // the procedure to blast in.
)?1G^-R"s,t k_0} hookfuncdesc , * lphookfuncdesc;51Testing软件测试网Phb(CeG,A
51Testing软件测试网L YJ Yq%V+fN p
// 这个函数监测当前系统是否是windownt
\ R+p:wj7D0bool isnt();51Testing软件测试网sp w@4u
51Testing软件测试网 E.L~w9ZWi
// 这个函数得到hmodule -- 即我们需要截获的函数所在的dll模块的引入描述符(import descrīptor)51Testing软件测试网Wp)F F/Fpi6D(bN:T
pimage_import_descrīptor getnamedimportdescrīptor(hmodule hmodule, lpcstr szimportmodule);
"qy ZT0o2w8ae051Testing软件测试网K G G5\0?)R
// 我们的主函数51Testing软件测试网:NLO,[?
bool hookimportfunction(hmodule hmodule, lpcstr szimportmodule,
ac_D;p;le [ J n'`0lphookfuncdesc pahookfunc, proc* paorigfuncs)51Testing软件测试网 g5vS5l%VE
{
,w&mC:y5O|#`+T0/////////////////////// 下面的代码检测参数的有效性 ////////////////////////////51Testing软件测试网 n0y ]!Y(`$qL!t n
_assert(szimportmodule);
/e"I4CN(P.u0_assert(!isbadreadptr(pahookfunc, sizeof(hookfuncdesc)));51Testing软件测试网Q@q.D-~Z
#ifdef _debug
v^S ~ B8^:D0if (paorigfuncs) _assert(!isbadwriteptr(paorigfuncs, sizeof(proc)));
9X9Wo&@ Wsf.d0_assert(pahookfunc.szfunc);
L|[U v0_assert(*pahookfunc.szfunc != ’\0’);51Testing软件测试网$^K Om@b|4sO
_assert(!isbadcodeptr(pahookfunc.pproc));51Testing软件测试网6pD)b*KjP)BS
#endif51Testing软件测试网i P}Q`4M'e9o
if ((szimportmodule == null) || (isbadreadptr(pahookfunc, sizeof(hookfuncdesc))))51Testing软件测试网8gyj Q$m"`x
{
a*h'y1lu#c)B0_assert(false);51Testing软件测试网NY/V(mMd] {cT&z
setlasterrorex(error_invalid_parameter, sle_error);
J h OZ dkg0return false;
XF,Aw f*b w ]0}51Testing软件测试网5pU7?-F1a1W#R.vK
//////////////////////////////////////////////////////////////////////////////51Testing软件测试网 Xo{*r z1SR]7_ `9N
51Testing软件测试网_#mt&Vrw`
// 监测当前模块是否是在2gb虚拟内存空间之上51Testing软件测试网` E:qP)G
// 这部分的地址内存是属于win32进程共享的
3alb)z xq^'W+t0if (!isnt() && ((dword)hmodule >= 0x80000000))51Testing软件测试网le3AJ4ZQ7u'T6}:]
{
X1M~:\8\/v zp0_assert(false);51Testing软件测试网S4Cy.r&jp:t8?
setlasterrorex(error_invalid_handle, sle_error);
;D)Cv*|2W^:Fp6Fz$j0return false;
/L6zf7~fU6P H sk ]0}
ujd#]@~Pp0// 清零51Testing软件测试网'p2`4dki ^
if (paorigfuncs) memset(paorigfuncs, null, sizeof(proc));
v'@([rY a0
oY@0f"p!vK#a0// 调用getnamedimportdescrīptor()函数,来得到hmodule -- 即我们需要51Testing软件测试网S,inl'R6Jt
// 截获的函数所在的dll模块的引入描述符(import descrīptor)51Testing软件测试网B:T/Fv mW
pimage_import_descrīptor pimportdesc = getnamedimportdescrīptor(hmodule, szimportmodule);51Testing软件测试网7H7W5?yii
if (pimportdesc == null)
%M-w$g&Y5YR%f ?3@`0return false; // 若为空,则模块未被当前进程所引入51Testing软件测试网 Lx,Lu7oR\
51Testing软件测试网B(@c1Uk@9m2m
// 从dll模块中得到原始的thunk信息,因为pimportdesc->firstthunk数组中的原始信息已经
l L4wm"]0V?0// 在应用程序引入该dll时覆盖上了所有的引入信息,所以我们需要通过取得pimportdesc->originalfirstthunk51Testing软件测试网8s)B*RE6F7f @I5^-M0o
// 指针来访问引入函数名等信息51Testing软件测试网b^Z7S%r8P5M
pimage_thunk_data porigthunk = makeptr(pimage_thunk_data, hmodule,
4ly#If^+iRW0pimportdesc->originalfirstthunk);
(aja9wk9]0{051Testing软件测试网2]*~$egM
// 从pimportdesc->firstthunk得到image_thunk_data数组的指针,由于这里在dll被引入时已经填充了51Testing软件测试网)v/F B^5B9l%n
// 所有的引入信息,所以真正的截获实际上正是在这里进行的
"K,W5OJ:DIw M f0pimage_thunk_data prealthunk = makeptr(pimage_thunk_data, hmodule, pimportdesc->firstthunk);
R*I G W}0
&h?7R6R)M0// 穷举image_thunk_data数组,寻找我们需要截获的函数,这是最关键的部分!
&@.nF$eg:^(bY~i0while (porigthunk->u1.function)51Testing软件测试网)\{)|@4B6v h#|
{
0FZ j!mc"x0// 只寻找那些按函数名而不是序号引入的函数
E kR W6`P/P0if (image_ordinal_flag != (porigthunk->u1.ordinal & image_ordinal_flag))51Testing软件测试网al2G7V5A8e
{
&{B0mA(C3\0// 得到引入函数的函数名51Testing软件测试网xX\Y9J5sY3b
pimage_import_by_name pbyname = makeptr(pimage_import_by_name, hmodule,
id`Yk i8u m2zk0porigthunk->u1.addressofdata);
}]dXRI{y0
&b} ]dQ a0// 如果函数名以null开始,跳过,继续下一个函数51Testing软件测试网+DjS A~-`
if (’\0’ == pbyname->name[0])
Qi6Gjr9fv.b0continue;51Testing软件测试网9^ v{W;x"Q[%Q

N*[;F.K4P iaw m.jT\0// bdohook用来检查是否截获成功
jg$D{u9g'b-v4c0bool bdohook = false;51Testing软件测试网(X-G&RXO$A:i(I

9K@$q ~c#Fr0// 检查是否当前函数是我们需要截获的函数
8y\oZK;^0if ((pahookfunc.szfunc[0] == pbyname->name[0]) &&51Testing软件测试网hS{9Fy
(strcmpi(pahookfunc.szfunc, (char*)pbyname->name) == 0))51Testing软件测试网#zTpw*vo4n+xg
{51Testing软件测试网2P1]N9?s
// 找到了!
{Ry5q[2AQNu0if (pahookfunc.pproc)
5K.}RTX.h(N0bdohook = true;
)@D#~6l*I Z;T C*t9za0}
+N L$ae;PI0if (bdohook)
U#^i;x v)C0{51Testing软件测试网!i6On7Rb
// 我们已经找到了所要截获的函数,那么就开始动手吧51Testing软件测试网k t Evxt [
// 首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取51Testing软件测试网8R f6Gu%J {
memory_basic_information mbi_thunk;
p!N:xFG:?m0virtualquery(prealthunk, &mbi_thunk, sizeof(memory_basic_information));
;Q!b^7^7S@Xk0_assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize,51Testing软件测试网Jh5Qu/aD}%i
page_readwrite, &mbi_thunk.protect));
3P4]2l@%H9[!xc9Nb051Testing软件测试网(`d G?5C9U;iy rcK3K
// 保存我们所要截获的函数的正确跳转地址51Testing软件测试网6CT*gX M$k*wG o|\
if (paorigfuncs)51Testing软件测试网wy6x9J|_
paorigfuncs = (proc)prealthunk->u1.function;51Testing软件测试网w8Mj4pr~+K
51Testing软件测试网k?/LB m `
// 将image_thunk_data数组中的函数跳转地址改写为我们自己的函数地址!51Testing软件测试网v&v4WX1kH _2L!fH
// 以后所有进程对这个系统函数的所有调用都将成为对我们自己编写的函数的调用
0F)gde!a*c,DA4~e0prealthunk->u1.function = (pdword)pahookfunc.pproc;51Testing软件测试网HY'|UW

~9c h&R&R'qb NNR0// 操作完毕!将这一块虚拟内存改回原来的保护状态
f5L epJfx0dword dwoldprotect;
3q f#G5c_K wF G0_assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize,
%y2at4a [w \&R,|0mbi_thunk.protect, &dwoldprotect));
a,K"e ](u q.HD4Ol0setlasterror(error_success);51Testing软件测试网xI)h5TO
return true;51Testing软件测试网x#hiKY][ `
}
U#ar$^H9j0}
,Q5I;UWe-j0// 访问image_thunk_data数组中的下一个元素
l)oi*BS1c-Y0porigthunk++;51Testing软件测试网J*R2r-rx&X
prealthunk++;
*y `sK/kL)`0}
&}k y6k [#o|0return true;
v*I&Sp.m,f0}51Testing软件测试网s9g aYp~P.]
51Testing软件测试网2R+q ?`*Y q S
// getnamedimportdescrīptor函数的实现51Testing软件测试网D*I&?O?-EA w/SwQ)g
pimage_import_descrīptor getnamedimportdescrīptor(hmodule hmodule, lpcstr szimportmodule)51Testing软件测试网D-F p:Y!bh
{51Testing软件测试网|*N)k\2}Xc
// 检测参数51Testing软件测试网Ef5h*t#p+rl v%F
_assert(szimportmodule);
il#[eW+`}0_assert(hmodule);51Testing软件测试网2@rc1MKUL N:[
if ((szimportmodule == null) || (hmodule == null))51Testing软件测试网 ~ Z&qm([R
{51Testing软件测试网X/V.n&l.iH]{ h
_assert(false);51Testing软件测试网tyO,|jMy _g
setlasterrorex(error_invalid_parameter, sle_error);
.s x"o UqBCY Y@'Z{0return null;
*l`-lM'S)Y8Ar+z0}
8}4G7s6s*ttA051Testing软件测试网"{8q"E'a9u&Z
// 得到dos文件头
Z p8yfWoX6VH!FZ v0pimage_dos_header pdosheader = (pimage_dos_header) hmodule;
@K'Pfz\r~/e0
m0z0_SAp[}7|0// 检测是否mz文件头
%?'H3a7t'j n0if (isbadreadptr(pdosheader, sizeof(image_dos_header)) ||
E[`~ G0(pdosheader->e_magic != image_dos_signature))
,?,M#X^Vp0{51Testing软件测试网(M^+^"n(G9e5C H
_assert(false);
n k-LPE3vO0setlasterrorex(error_invalid_parameter, sle_error);
es^.F1|/y3K?J0return null;
#X2ihls-N?*b0}
'wz M T%];b'opu0
8a`!KgL8H0// 取得pe文件头
9n0I6J9NO H\F r ~ M0pimage_nt_headers pntheader = makeptr(pimage_nt_headers, pdosheader, pdosheader->e_lfanew);
*u+`e-P/~"f0
Um2v`%m7[0// 检测是否pe映像文件51Testing软件测试网@l&Rll%g#s
if (isbadreadptr(pntheader, sizeof(image_nt_headers)) ||
f!EL'b t1xM0(pntheader->signature != image_nt_signature))51Testing软件测试网!T w'y i_0? ZPG
{51Testing软件测试网o2m n4]%rys Ye6t
_assert(false);
.aP:d g!pyP0setlasterrorex(error_invalid_parameter, sle_error);
N7R"e'pk V1Ao`I B0return null;51Testing软件测试网H+]Eb?J
}51Testing软件测试网orj3lJ!I
51Testing软件测试网sR4G&Q:[6K7D8}V
// 检查pe文件的引入段(即 .idata section)
;{1u:ty-p&g%x"H0if (pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress == 0)51Testing软件测试网5E;a7}.t}n Kd#A,n1[
return null;51Testing软件测试网,o%n{&Q/m4`!Q
51Testing软件测试网M ^P!c$@(A'w
// 得到引入段(即 .idata section)的指针51Testing软件测试网$]K)NI{/^+A*b r
pimage_import_descrīptor pimportdesc = makeptr(pimage_import_descrīptor, pdosheader,51Testing软件测试网9n^` _"L&|V
pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress);51Testing软件测试网:kd)o z&r,{'BC
51Testing软件测试网0qmh^*WBlj-w
// 穷举pimage_import_descrīptor数组寻找我们需要截获的函数所在的模块
~-iV)x&j4N ToA-s#x0while (pimportdesc->name)
+B `p3`T0{51Testing软件测试网6dv bH H
pstr szcurrmod = makeptr(pstr, pdosheader, pimportdesc->name);
F z\,L@3@I0if (stricmp(szcurrmod, szimportmodule) == 0)51Testing软件测试网LI3k0Y'}Xp
break; // 找到!中断循环51Testing软件测试网+X~$[%XB9Ex~m
// 下一个元素
ee A0U.F&UY#v0pimportdesc++;51Testing软件测试网D7H4Eq:@g
}51Testing软件测试网1aW QXPq

$`*Sc$~ F y8K%sw0// 如果没有找到,说明我们寻找的模块没有被当前的进程所引入!51Testing软件测试网5I%|bIWjM
if (pimportdesc->name == null)
a,baRX9U0return null;
J:@(Z.i OpQ,h051Testing软件测试网%F l xOn?+A0{NIb
// 返回函数所找到的模块描述符(import descrīptor)51Testing软件测试网9~l4[{(}
return pimportdesc;
|do3G;b^cp+[0}51Testing软件测试网B0M!OVF-aO{%|

GES sf V0// isnt()函数的实现51Testing软件测试网C/P1~#R_
bool isnt()
9VNe-o?:\/y/k0{51Testing软件测试网-g0r5z7?,c*n*[$sn
osversioninfo stosvi;
7j"}+Tt$BD;U&d0memset(&stosvi, null, sizeof(osversioninfo));
$M_n4vmyb r0stosvi.dwosversioninfosize = sizeof(osversioninfo);
K l.^-L-O [W#I7K,N JW:d0bool bret = getversionex(&stosvi);
$g C Q X5hU&y-Is0_assert(true == bret);
)B-lR yb;~;F_0if (false == bret) return false;
v1j/y&QAOg.p s-T0return (ver_platform_win32_nt == stosvi.dwplatformid);
,UfQs;Xx0}
NTyI3Z s0/////////////////////////////////////////////// end //////////////////////////////////////////////////////////////////////51Testing软件测试网-|VA;B3O At4Rf

/Wh1D5{IhU0不知道在这篇文章问世之前,有多少朋友尝试过去实现“鼠标屏幕取词”这项充满了挑战的技术,也只有尝试过的朋友才能体会到其间的不易,尤其在探索api函数的截获时,手头的几篇资料没有一篇是涉及到关键代码的,重要的地方都是一笔代过,msdn更是显得苍白而无力,也不知道除了image_import_descrīptor和image_thunk_data,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,希望这篇文章对大家能有所帮助。

2nPC5h&_.v U0

TAG: 笔记

 

评分:0

我来说两句

日历

« 2023-10-10  
1234567
891011121314
15161718192021
22232425262728
293031    

数据统计

  • 访问量: 31021
  • 日志数: 33
  • 图片数: 3
  • 文件数: 8
  • 建立时间: 2007-10-10
  • 更新时间: 2011-06-28

RSS订阅

Open Toolbar