鼠标屏幕取词技术windows编程实现教程及详细源代码Word文档下载推荐.docx
《鼠标屏幕取词技术windows编程实现教程及详细源代码Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《鼠标屏幕取词技术windows编程实现教程及详细源代码Word文档下载推荐.docx(10页珍藏版)》请在冰豆网上搜索。
exe和dll被映射到虚拟内存空间的什么地方是由它们的基地址决定的。
它们的基地址是在链接时由链接器决定的。
当你新建一个win32工程时,vc++链接器使用缺省的基地址0x00400000。
可以通过链接器的base选项改变模块的基地址。
exe通常被映射到虚拟内存的0x00400000处,dll也随之有不同的基地址,通常被映射到不同进程
的相同的虚拟地址空间处。
系统将exe和dll原封不动映射到虚拟内存空间中,它们在内存中的结构与磁盘上的静态文件结构是一样的。
即pe(portableexecutable)文件格式。
我们得到了进程模块的基地址以后,就可以根据pe文件的格式穷举这个模块的image_import_descriptor数组,看看进程空间中是否引入了我们需要截获的函数所在的动态链接库,比如需要截获“textouta”,就必须检查“gdi32.dll”是否被引入了。
说到这里,我们有必要介绍一下pe文件的格式,如右图,这是pe文件格式的大致框图,最前面是文件头,我们不必理会,从pefileoptionalheader后面开始,就是文件中各个段的说明,说明后面才是真正的段数据,而实际上我们关心的只有一个段,那就是“.idata”段,这个段中包含了所有的引入函数信息,还有iat(importaddresstable)的rva(relativevirtualaddress)地址。
说到这里,截获windowsapi的整个原理就要真相大白了。
实际上所有进程对给定的api函数的调用总是通过pe文件的一个地方来转移的,这就是一个该模块(可以是exe或dll)的“.idata”段中的iat输入地址表(importaddresstable)。
在那里有所有本模块调用的其它dll的函数名及地址。
对其它dll的函数调用实际上只是跳转到输入地址表,由输入地址表再跳转到dll真正的函数入口。
具体来说,我们将通过image_import_descriptor数组来访问“.idata”段中引入的dll的信息,然后通过image_thunk_data数组来针对一个被引入的dll访问该dll中被引入的每个函数的信息,找到我们需要截获的函数的跳转地址,然后改成我们自己的函数的地址……具体的做法在后面的关键代码中会有详细的讲解。
讲了这么多原理,现在让我们回到“鼠标屏幕取词”的专题上来。
除了api函数的截获,要实现“鼠标屏幕取词”,还需要做一些其它的工作,简单的说来,可以把一个完整的取词过程归纳成以下几个步骤:
1.安装鼠标钩子,通过钩子函数获得鼠标消息。
使用到的api函数:
setwindowshookex
2.得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画窗口。
windowfrompoint,screentoclient,invalidaterect
3.截获对系统函数的调用,取得参数,也就是我们要取的词。
对于大多数的windows应用程序来说,如果要取词,我们需要截获的是“gdi32.dll”中的“textouta”函数。
我们先仿照textouta函数写一个自己的mytextouta函数,如:
boolwinapimytextouta(hdchdc,intnxstart,intnystart,lpcstrlpszstring,intcbstring)
{
//这里进行输出lpszstring的处理
//然后调用正版的textouta函数
}
把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的hookimportfunction函数来截获进程
对textouta函数的调用,跳转到我们的mytextouta函数,完成对输出字符串的捕捉。
hookimportfunction的
用法:
hookfuncdeschd;
procporigfuns;
hd.szfunc="
textouta"
;
hd.pproc=(proc)mytextouta;
hookimportfunction(afxgetinstancehandle(),"
gdi32.dll"
&
hd,porigfuns);
下面给出了hookimportfunction的源代码,相信详尽的注释一定不会让您觉得理解截获到底是怎么实现的
很难,ok,let’sgo:
/////////////////////////////////////////////begin///////////////////////////////////////////////////////////////
#include<
crtdbg.h>
//这里定义了一个产生指针的宏
#definemakeptr(cast,ptr,addvalue)(cast)((dword)(ptr)+(dword)(addvalue))
//定义了hookfuncdesc结构,我们用这个结构作为参数传给hookimportfunction函数
typedefstructtag_hookfuncdesc
lpcstrszfunc;
//thenameofthefunctiontohook.
procpproc;
//theproceduretoblastin.
}hookfuncdesc,*lphookfuncdesc;
//这个函数监测当前系统是否是windownt
boolisnt();
//这个函数得到hmodule--即我们需要截获的函数所在的dll模块的引入描述符(importdescriptor)
pimage_import_descriptorgetnamedimportdescriptor(hmodulehmodule,lpcstrszimportmodule);
//我们的主函数
boolhookimportfunction(hmodulehmodule,lpcstrszimportmodule,
lphookfuncdescpahookfunc,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'
);
isbadcodeptr(pahookfunc.pproc));
#endif
if((szimportmodule==null)||(isbadreadptr(pahookfunc,sizeof(hookfuncdesc))))
_assert(false);
setlasterrorex(error_invalid_parameter,sle_error);
returnfalse;
//////////////////////////////////////////////////////////////////////////////
//监测当前模块是否是在2gb虚拟内存空间之上
//这部分的地址内存是属于win32进程共享的
if(!
isnt()&
&
((dword)hmodule>
=0x80000000))
setlasterrorex(error_invalid_handle,sle_error);
//清零
if(paorigfuncs)memset(paorigfuncs,null,sizeof(proc));
//调用getnamedimportdescriptor()函数,来得到hmodule--即我们需要
//截获的函数所在的dll模块的引入描述符(importdescriptor)
pimage_import_descriptorpimportdesc=getnamedimportdescriptor(hmodule,szimportmodule);
if(pimportdesc==null)
//若为空,则模块未被当前进程所引入
//从dll模块中得到原始的thunk信息,因为pimportdesc->
firstthunk数组中的原始信息已经
//在应用程序引入该dll时覆盖上了所有的引入信息,所以我们需要通过取得pimportdesc->
originalfirstthunk
//指针来访问引入函数名等信息
pimage_thunk_dataporigthunk=makeptr(pimage_thunk_data,hmodule,
pimportdesc->
originalfirstthunk);
//从pimportdesc->
firstthunk得到image_thunk_data数组的指针,由于这里在dll被引入时已经填充了
//所有的引入信息,所以真正的截获实际上正是在这里进行的
pimage_thunk_dataprealthunk=makeptr(pimage_thunk_data,hmodule,pimportdesc->
firstthunk);
//穷举image_thunk_data数组,寻找我们需要截获的函数,这是最关键的部分!
while(porigthunk->
u1.function)
//只寻找那些按函数名而不是序号引入的函数
if(image_ordinal_flag!
=(porigthunk->
u1.ordinal&
image_ordinal_flag))
//得到引入函数的函数名
pimage_import_by_namepbyname=makeptr(pimage_import_by_name,hmodule,
porigthunk->
u1.addressofdata);
//如果函数名以null开始,跳过,继续下一个函数
if('
==pbyname->
name[0])
continue;
//bdohook用来检查是否截获成功
boolbdohook=false;
//检查是否当前函数是我们需要截获的函数
if((pahookfunc.szfunc[0]==pbyname->
name[0])&
(strcmpi(pahookfunc.szfunc,(char*)pbyname->
name)==0))
//找到了!
if(pahookfunc.pproc)
bdohook=true;
if(bdohook)
//我们已经找到了所要截获的函数,那么就开始动手吧
//首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取
memory_basic_informationmbi_thunk;
virtualquery(prealthunk,&
mbi_thunk,sizeof(memory_basic_information));
_assert(virtualprotect(mbi_thunk.baseaddress,mbi_thunk.regionsize,
page_readwrite,&
mbi_thunk.protect));
//保存我们所要截获的函数的正确跳转地址
if(paorigfuncs)
paorigfuncs=(proc)prealthunk->
u1.function;
//将image_thunk_data数组中的函数跳转地址改写为我们自己的函数地址!
//以后所有进程对这个系统函数的所有调用都将成为对我们自己编写的函数的调用
prealthunk->
u1.function=(pdword)pahookfunc.pproc;
//操作完毕!
将这一块虚拟内存改回原来的保护状态
dworddwoldprotect;
mbi_thunk.protect,&
dwoldprotect));
setlasterror(error_success);
returntrue;
//访问image_thunk_data数组中的下一个元素
porigthunk++;
prealthunk++;
//getnamedimportdescriptor函数的实现
pimage_import_descriptorgetnamedimportdescriptor(hmodulehmodule,lpcstrszimportmodule)
//检测参数
_assert(hmodule);
if((szimportmodule==null)||(hmodule==null))
returnnull;
//得到dos文件头
pimage_dos_headerpdosheader=(pimage_dos_header)hmodule;
//检测是否mz文件头
if(isbadreadptr(pdosheader,sizeof(image_dos_header))||
(pdosheader->
e_magic!
=image_dos_signature))
//取得pe文件头
pimage_nt_headerspntheader=makeptr(pimage_nt_headers,pdosheader,pdosheader->
e_lfanew);
//检测是否pe映像文件
if(isbadreadptr(pntheader,sizeof(image_nt_headers))||
(pntheader->
signature!
=image_nt_signature))
//检查pe文件的引入段(即.idatasection)
if(pntheader->
optionalheader.datadirectory[image_directory_entry_import].virtualaddress==0)
//得到引入段(即.idatasection)的指针
pimage_import_descriptorpimportdesc=makeptr(pimage_import_descriptor,pdosheader,
pntheader->
optionalheader.datadirectory[image_directory_entry_import].virtualaddress);
//穷举pimage_import_descriptor数组寻找我们需要截获的函数所在的模块
while(pimportdesc->
name)
pstrszcurrmod=makeptr(pstr,pdosheader,pimportdesc->
name);
if(stricmp(szcurrmod,szimportmodule)==0)
break;
//找到!
中断循环
//下一个元素
pimportdesc++;
//如果没有找到,说明我们寻找的模块没有被当前的进程所引入!
if(pimportdesc->
name==null)
//返回函数所找到的模块描述符(importdescriptor)
returnpimportdesc;
//isnt()函数的实现
boolisnt()
osversioninfostosvi;
memset(&
stosvi,null,sizeof(osversioninfo));
stosvi.dwosversioninfosize=sizeof(osversioninfo);
boolbret=getversionex(&
stosvi);
_assert(true==bret);
if(false==bret)returnfalse;
return(ver_platform_win32_nt==stosvi.dwplatformid);
///////////////////////////////////////////////end//////////////////////////////////////////////////////////////////////