鼠标屏幕取词技术的原理和实现

合集下载

鼠标 原理

鼠标 原理

鼠标原理
鼠标是一种用于在计算机上控制光标位置和操作的输入设备。

它的工作原理是基于鼠标底部的球体或光学传感器捕捉表面上的运动,然后将这些运动转化为计算机可以理解的信号。

在鼠标底部,通常有两个旋转的滚轮和一个轨迹球或光电传感器。

轨迹球通过感知手指的移动来检测鼠标的运动。

当手指在轨迹球上滚动时,它会带动内部的滚轮旋转,通过编码器将这种旋转转化为数字信号。

这个数字信号将发送给计算机,然后计算机会根据信号的变化来移动光标的位置。

光电传感器是使用类似摄像机的技术来检测鼠标的运动。

它通常由一个红色LED和一个相机组成。

当鼠标在表面上移动时,红色LED发出光线,然后相机记录下光线的反射。

通过比较
连续的图像帧,计算机可以计算出光标的移动方向和速度。

除了检测运动外,鼠标上的按钮也是非常重要的。

鼠标通常有左键、右键和滚轮按钮。

这些按钮的原理是基于按下和松开的电子控制。

当我们按下鼠标按钮时,按钮触点会触发电流流动,然后鼠标的电路板会将这个触发事件转化为计算机可以理解的指令。

总的来说,鼠标的原理是通过检测和转化手指或表面上的运动以及按钮的状态来实现对计算机的控制。

shakemouse 原理

shakemouse 原理

shakemouse 原理shakemouse原理解析一、引言在现代科技发展的背景下,计算机已经成为人们生活和工作中不可或缺的一部分。

而作为计算机的输入设备,鼠标的发展也越来越重要。

其中,shakemouse(晃动鼠标)作为一种新型的鼠标操作方式,吸引了众多用户的关注。

本文将围绕shakemouse原理展开详细解析。

二、shakemouse原理shakemouse原理是基于鼠标的运动感应技术,通过晃动鼠标来实现不同的操作。

它利用了鼠标内部的加速度计和陀螺仪等传感器,通过对鼠标的晃动进行识别和解析,从而实现特定功能的触发。

1. 加速度计的作用加速度计是鼠标内部的一个重要传感器,用于测量鼠标在三个维度上的加速度变化。

当用户晃动鼠标时,加速度计会感知到鼠标的加速度变化,并将这些数据传输给计算机进行处理。

2. 陀螺仪的作用陀螺仪是鼠标内部的另一个重要传感器,用于测量鼠标的旋转角度和方向。

通过陀螺仪的数据,计算机可以判断用户晃动鼠标的方向和力度,从而进行相应的操作。

3. 数据解析与触发当鼠标晃动时,加速度计和陀螺仪会实时采集数据,并将这些数据传输给计算机。

计算机通过对这些数据进行解析和处理,判断用户的意图,并触发特定的功能。

例如,用户可以通过晃动鼠标来切换窗口、调整音量、放大缩小画面等。

三、shakemouse的优势相比传统的鼠标操作方式,shakemouse具有一些独特的优势。

1. 操作简便shakemouse的操作方式简单直观,用户只需通过晃动鼠标即可完成特定功能的触发,无需进行复杂的键盘操作或鼠标点击。

这种简单的操作方式很大程度上提高了用户的使用体验。

2. 功能丰富shakemouse可以实现多种功能的触发,用户可以根据自己的需求进行个性化设置。

例如,在游戏中,晃动鼠标可以实现特殊技能的释放;在办公软件中,晃动鼠标可以实现快速切换工具等。

3. 提高工作效率shakemouse的操作方式更加高效快捷,用户无需频繁切换鼠标和键盘,只需通过晃动鼠标即可完成各种操作。

API HOOK 金山词霸取词功能原理

API HOOK 金山词霸取词功能原理
不固定,所以需要从指令流里面动态析出它的地址。
3.4 动态生成JMP指令
动态生成的JMP指令占用5个字节,保存在一个数组里面。以生成跳转到TextOut的替
代函数myTextOut的JMP指令为例:
BYTE ins[5]; //保存JMP指令
符串:
. 代码拦截:Windows以DLL方式提供系统服务,可以方便地获取Windows字符输出API的地址,修改其入口代码,拦截应用程序对它们的调用。
. 鼠标HOOK:安装WH-MOUSEPROC类型的全局鼠标HOOK过程,监视鼠标在整个屏幕上的移动。
数入口的代码,再调用TextOut()执行正常的字符输出,接下来SpyCode()在被拦截函数
入口再次放入JMP指令,最后返回调用进程。
3.3 获取被拦截API的动态链接地址
TextOut和ExtTextOut的地址可以通过GetProcAddress取得,而TextOut16W和ExtTextOut16W既未公 开于文档,也没有用字符串或序号从gdi.exe中引出,无法使用GetProcAddress取得它们的地址。下面讨论如何解决这个问题。
. 获取GDI代码段的基地址和界限;
. 用当前DS复制一个新的段选择子,该选择子的描述符具有可读写的属性;
. 将复制得到的选择子的基地址和界限设置为GDI代码段的基地址和界限;
. 将复制得到的选择子的基地址和界限设置为GDI代码段的基地址和界限;
不同的索引来从该表格里面取目的地址。ExtTextOutW和TextOutW分别使用索引B2H和30
H,取表中相应元素作为ExtTextOut16W和TextOut16W的地址。取得这个数组的起始位置

灵格斯翻译家在多语言市场的应用

灵格斯翻译家在多语言市场的应用

灵格斯翻译家在多语言市场的应用以前我给大家分享过一个我个人用了7-8年的词典,叫灵格斯词霸,功能很强大,支持多语种的翻译(当然这个需要大家下载相应语种的词典),同时也支持全文翻译(主要以谷歌翻译为主,有道、必应和雅虎翻译为辅)。

个人认为谷歌翻译的准确度还是比较高的,所以首选谷歌翻译。

自从多语言市场功能开发以来,灵格斯强大的翻译功能就显现出来了。

目前只有日语和西语这两个语种的市场。

下面就灵格斯翻译家在多语言市场中发布产品的应用,简单介绍下我自己的几个建议1. 毕竟词典翻译属于机器翻译,没有人工翻译那么准确和灵活,大段的翻译容易出现错译和误译,所以为了提供翻译的准确度,建议单句翻译;2. 日语和汉语属于汉藏语系,在语法上和文化起源上有很多相似的地方,所以在日语翻译时,建议选择日汉互译,准确度能高点;3. 西语和英语属于印欧语系,语法和单词上有很多相同或者相似的地方,所以在翻译西语时,建议翻译西语时用英文。

4. 灵格斯的官网上也有好多专业的词典,可以根据自己行业下载专业的词典。

Note:以前我写的灵格斯分享帖:/bbs/read-htm-tid-596645-fid-49.html 好萝卜分享帖:/bbs/read-htm-tid-714977-fid-47.html 好萝卜就灵格斯词霸的使用讲述的很详细,而且很全面,大家可以仔细看看。

如果对西语学习比较感兴趣的,推荐大家一个西语学习的网站。

西语学习网站/~bknelson/SLC/index.phpLingoes支持多达80种语言互查互译,包括英、法、德、意、俄、中、日、韩、西、葡、阿拉伯语及更多正好是阿里网站的多语言项目得力小帮手!超过80 种语言互查互译超过42 种语言全文翻译“Ctrl + 鼠标右键”屏幕取词,多国语言即指即译:支持英语、法语、德语、俄语、西班牙语、中文、日语、韩语网络释义,网罗普通词典里无法收录的各类新词汇开放式的词库管理:可以根据自已的需要下载安装词库,并自由设定它们的使用和排列方式。

鼠标取词

鼠标取词

鼠标取词原理初探其实这是个老话题了,自从这项技术面世以来就已经被讨论了无数遍。

事实上,这个看起来很牛逼的东西其实不是想象中的那么难,方法也有多种多样,而且,有了先前各位大牛的研究成果,实现这项技术变得相对容易很多。

最主要的是,编写的过程中能让人学到很多知识。

好了,话不多说,直接进入主题。

在Windows平台下,输出文字基本上都是靠TextOut,ExtTextOut这两个API实现。

也有DrawText这样的函数,但最终也是调用了ExtTextOut(TextOut其实就是ExtTextOut的特殊版本)。

在Windows 9x下是如此,Windows NT下也是,不过就是有了ANSI和Unicode两个版本的区别。

若能截获这些API,转到自己的函数里,便能得到输出的字符串,实现取词功能。

______________________________________________________________BOOL WINAPI MyExtTextOutA( HDC hdc, int x, int y, UINT options, CONST RECT * lprect, LPCSTR lpString, UINT c, CONST INT * lpDx){// lpString便是所要的字符串(ANSI),在此进行处理// 然后调用正版的API输出字符return ::ExtTextOutA(hdc,x,y,options,lprect,lpString,c,lpDx);}看到这里,聪明的你肯定已经想到了,这不就是要实现一个API钩子么。

确实是这样,鼠标取词的核心技术就是API钩子。

事实上,有了API钩子,你可以干很多事情(比如拦截WinSock的sendto / recvfrom函数来实现数据包的截取等)。

实现API钩子主要有两种方法,一种是修改exe文件在内存中各个模块的导入函数表,另一种则是经典的内嵌汇编代码,采用JMP XXXX跳转到你自己的函数。

鼠标什么原理

鼠标什么原理

鼠标什么原理
鼠标是一种用于操作计算机的输入设备。

它的原理是利用感光器或感应器来追踪用户在平面上移动时所产生的输入信号。

一种常见的鼠标原理是光学原理。

这种鼠标内部有一个光电传感器,当鼠标在表面上移动时,传感器会捕捉到由光源反射回来的光线。

根据这些光线的变化,鼠标可以计算出用户的移动方向和速度。

具体来说,传感器会在鼠标底部投射出红光或红光激光,并通过计算机处理这个光线的变化来确定鼠标的位置。

另一种常见的鼠标原理是机械原理。

这种鼠标内部有一个小球,当鼠标在表面上移动时,球会滚动并改变鼠标内部的位置。

然后,通过计算机内部的编码器来测量小球的滚动方向和速度。

这种原理的鼠标需要定期清洁小球以确保准确性。

还有一种较新的鼠标原理是激光原理。

这种鼠标使用激光来追踪用户在表面上的移动。

激光鼠标通常比光学鼠标更准确,因为它使用更高分辨率的光电传感器来捕捉细微的移动。

无论鼠标使用哪种原理,它们都能够通过与计算机连接,将用户在平面上的移动转化为相应的光标或指针的移动。

这种输入设备在现代计算机使用中起到非常重要的作用,使用户能够方便地进行各种操作。

Windows操作系统中屏幕取词技术的研究

Windows操作系统中屏幕取词技术的研究
主要步 骤如 下 :
1 挂 EA I 子 ; ) P 钩
收 稿 日期 :05 1一 1 2 0— O 2
作者简介 : 童
强( 98 16一
)男 , , 湖北黄石人 , 讲师 , 工程师 , 硕士 , 主要研究方 向为多媒 体应 用技术 、 b应用技术等 。 We

7 ・ 5
维普资讯
2 得到鼠标当前位置 , ) 并设法让鼠标下的窗口刷新 ; 3 在 A I 子 函数 中截 获字 符 串 , 根 据 鼠标 位 置 计 算 出 鼠标 下 的 字 符 ; 调 用 原 型 函数 完 成 ) P钩 并 并 刷 新工作 。 为 了随 时知道 鼠标 的位 置 , 要 安装一 个 鼠标 钩子 以便 随时 得 到 鼠标 的屏 幕 坐 标 。这 里用 到 了 还 两 种钩 子 , 鼠标 钩 子和 前 面提 到 的 A I 子 。 即 P钩
维普资讯
第2 6卷
第 2期
湖北师范学 院学报 ( 自然科学版 )
Jun f u e N r l nvrti c ) l ma U sy ul e
Vo. 6 12 No 2, 0 6 . 20
Widw 操 作 系 统 中 屏 幕 取 词 技 术 的 研 究 no s
童 强
( 湖北 师 范 学院 计 算机科 学 系, 湖北 黄石 4 50 ) 30 2
摘要 : 分析 Wid w 操作 系统 中屏幕取词技术的原理和 实现方法 , nos 并对其 中涉及到 的钩 子 函数的使 用, P AI
2 挂 接 A I 子 截 获 A I 数 P钩 P参
如前所述 , 要获得系统 A I P 的输出参数 , 关键就是对几个有关文本输出的 A I P 函数进行挂接。实

中石油 软件工程课程设计 在线考试

中石油 软件工程课程设计 在线考试

2009软件工程设计实验软件项目开发题目和完成内容要求【本文主要对此课程的授课目的、内容、授课形式和考核条件进行了叙述,并提供给学生一些可选题目,供学生选择完成。

学生也可根据文中提供的选题评分依据自拟自己喜欢的题目。

】鲁强中国石油大学计算机系1.课程目的在完成软件工程课程后,需要应用软件工程开发方法从需求分析、体系结构设计、详细设计、测试等相关环节来实践软件系统开发过程。

本课程提供了相关完成相关环节报告的模版,需要学生在完成相关软件题目开发过程中,按照软件工程学到的方法,在各个阶段撰写相关内容。

2.课程内容2.1.课程要求开发题目将按照高中低三个档次来进行布置,每个题目的起评分依照项目难度的不同分别为90、85和80。

如完成基本题目要求的功能为以上分数,如缺少部分功能将减少5~10,如不能完成(缺少大部分功能)将减少20分,如提供比较完备的功能将在此基础上增加5~10分。

提交的作业需包含以下内容:1.选择以下题目或自拟一个题目,并提交与此题目对应的可执行代码和源代码。

(20~30分)2.提交四个文档,即产品需求规格说明书、体系结构设计说明书、模块设计说明书、测试用例说明书(70~80分,以论文来替代此部分报告,将给零分)3.将完成的文档以压缩包的格式上传,不能上传多个doc、docx文档,以免造成文件的丢失。

2.2.开发题目及其验收内容2.2.1.P2P分布式存储●难度高●实现内容使用Java下JXTA或自己设计P2P协议完成多个客户机下的资源共享。

此系统具有以下功能,每个用户能够配置自己的硬盘空间来供全网络的用户使用,每个用户能够看到全网络下唯一的文件视图(即能够看到唯一文件目录,此文件目录下存储着全网络的共享文件),用户能够在此文件视图下创建文件目录、上传文件和下载文件。

其中上传文件指的是将本地文件上传到P2P文件存储系统中,下载文件指的是将P2P文件存储系统中的文件内容下载到本地机。

2.2.2.工作流引擎的设计●难度高●实现内容查阅工作流资料和分析现有的开源工作流引擎软件,设计一套简单的工作流描述语言,并对此语言建立解释和运行引擎,以支持工作流系统的开发和运行。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

鼠标屏幕取词技术的原理和实现鼠标屏幕取词技术的原理和实现“鼠标屏幕取词”技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在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函数:setwindowshookex2( 得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画窗口。

使用到的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",&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;// 这个函数监测当前系统是否是windowntbool 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 _debugif (paorigfuncs) _assert(!isbadwriteptr(paorigfuncs, sizeof(proc)));_assert(pahookfunc.szfunc);_assert(*pahookfunc.szfunc != '\0');_assert(!isbadcodeptr(pahookfunc.pproc));#endifif ((szimportmodule == null) || (isbadreadptr(pahookfunc,sizeof(hookfuncdesc)))){_assert(false);setlasterrorex(error_invalid_parameter, sle_error);return false;}//////////////////////////////////////////////////////////////////// //////////// 监测当前模块是否是在2gb虚拟内存空间之上// 这部分的地址内存是属于win32进程共享的if (!isnt() && ((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 &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]) &&(strcmpi(pahookfunc.szfunc, (char*)pbyname->name) == 0)){// 找到了!if (pahookfunc.pproc)bdohook = true;}if (bdohook){// 我们已经找到了所要截获的函数,那么就开始动手吧// 首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取memory_basic_information mbi_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;// 操作完毕!将这一块虚拟内存改回原来的保护状态dword dwoldprotect;_assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, mbi_thunk.protect, &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].virtualaddre ss ==0)return null;// 得到引入段(即 .idata section)的指针pimage_import_descriptor pimportdesc =makeptr(pimage_import_descriptor, pdosheader,pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddre ss);// 穷举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(&stosvi, null, sizeof(osversioninfo));stosvi.dwosversioninfosize = sizeof(osversioninfo);bool bret = getversionex(&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,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,希望这篇文章对大家能有所帮助。

相关文档
最新文档