鼠标屏幕取词技术的原理和实现
鼠标 原理
鼠标原理
鼠标是一种用于在计算机上控制光标位置和操作的输入设备。
它的工作原理是基于鼠标底部的球体或光学传感器捕捉表面上的运动,然后将这些运动转化为计算机可以理解的信号。
在鼠标底部,通常有两个旋转的滚轮和一个轨迹球或光电传感器。
轨迹球通过感知手指的移动来检测鼠标的运动。
当手指在轨迹球上滚动时,它会带动内部的滚轮旋转,通过编码器将这种旋转转化为数字信号。
这个数字信号将发送给计算机,然后计算机会根据信号的变化来移动光标的位置。
光电传感器是使用类似摄像机的技术来检测鼠标的运动。
它通常由一个红色LED和一个相机组成。
当鼠标在表面上移动时,红色LED发出光线,然后相机记录下光线的反射。
通过比较
连续的图像帧,计算机可以计算出光标的移动方向和速度。
除了检测运动外,鼠标上的按钮也是非常重要的。
鼠标通常有左键、右键和滚轮按钮。
这些按钮的原理是基于按下和松开的电子控制。
当我们按下鼠标按钮时,按钮触点会触发电流流动,然后鼠标的电路板会将这个触发事件转化为计算机可以理解的指令。
总的来说,鼠标的原理是通过检测和转化手指或表面上的运动以及按钮的状态来实现对计算机的控制。
shakemouse 原理
shakemouse 原理shakemouse原理解析一、引言在现代科技发展的背景下,计算机已经成为人们生活和工作中不可或缺的一部分。
而作为计算机的输入设备,鼠标的发展也越来越重要。
其中,shakemouse(晃动鼠标)作为一种新型的鼠标操作方式,吸引了众多用户的关注。
本文将围绕shakemouse原理展开详细解析。
二、shakemouse原理shakemouse原理是基于鼠标的运动感应技术,通过晃动鼠标来实现不同的操作。
它利用了鼠标内部的加速度计和陀螺仪等传感器,通过对鼠标的晃动进行识别和解析,从而实现特定功能的触发。
1. 加速度计的作用加速度计是鼠标内部的一个重要传感器,用于测量鼠标在三个维度上的加速度变化。
当用户晃动鼠标时,加速度计会感知到鼠标的加速度变化,并将这些数据传输给计算机进行处理。
2. 陀螺仪的作用陀螺仪是鼠标内部的另一个重要传感器,用于测量鼠标的旋转角度和方向。
通过陀螺仪的数据,计算机可以判断用户晃动鼠标的方向和力度,从而进行相应的操作。
3. 数据解析与触发当鼠标晃动时,加速度计和陀螺仪会实时采集数据,并将这些数据传输给计算机。
计算机通过对这些数据进行解析和处理,判断用户的意图,并触发特定的功能。
例如,用户可以通过晃动鼠标来切换窗口、调整音量、放大缩小画面等。
三、shakemouse的优势相比传统的鼠标操作方式,shakemouse具有一些独特的优势。
1. 操作简便shakemouse的操作方式简单直观,用户只需通过晃动鼠标即可完成特定功能的触发,无需进行复杂的键盘操作或鼠标点击。
这种简单的操作方式很大程度上提高了用户的使用体验。
2. 功能丰富shakemouse可以实现多种功能的触发,用户可以根据自己的需求进行个性化设置。
例如,在游戏中,晃动鼠标可以实现特殊技能的释放;在办公软件中,晃动鼠标可以实现快速切换工具等。
3. 提高工作效率shakemouse的操作方式更加高效快捷,用户无需频繁切换鼠标和键盘,只需通过晃动鼠标即可完成各种操作。
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操作系统中屏幕取词技术的研究
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.鼠标取词翻译的定义和作用2.鼠标取词翻译的操作方法3.鼠标取词翻译的优缺点4.鼠标取词翻译的应用场景5.未来发展趋势和展望正文在当今全球化时代,语言交流成为了人们日常工作、学习和生活中不可或缺的一部分。
为了更好地促进不同语言之间的沟通,翻译工具应运而生。
其中,鼠标取词翻译作为一种实用便捷的翻译方式,逐渐受到用户的青睐。
本文将对鼠标取词翻译的定义、操作方法、优缺点、应用场景以及未来发展趋势进行详细介绍。
一、鼠标取词翻译的定义和作用鼠标取词翻译是指用户在阅读一篇文章或文档时,只需用鼠标点击所要翻译的词汇,翻译工具即可快速将该词汇翻译成其他语言。
这种翻译方式有效地解决了用户在阅读过程中遇到的生词、难词等问题,提高了阅读效率。
二、鼠标取词翻译的操作方法鼠标取词翻译的操作方法非常简单。
首先,用户需要在电脑上安装一款鼠标取词翻译插件或者浏览器扩展。
安装完成后,用户在阅读文档时,只需将鼠标悬停在需要翻译的词汇上,插件或扩展就会自动识别并翻译该词汇。
部分翻译插件还可以实现整句翻译、语音翻译等功能,满足用户多样化的需求。
三、鼠标取词翻译的优缺点鼠标取词翻译具有操作简便、实时翻译、准确性高等优点。
然而,它也存在一些不足之处。
首先,由于不同翻译工具的词库和算法不同,翻译结果可能存在差异。
其次,鼠标取词翻译无法翻译句子结构和语境,对于一些复杂的语句,翻译效果可能不尽如人意。
四、鼠标取词翻译的应用场景鼠标取词翻译广泛应用于学术研究、商务交流、跨语言教育等领域。
特别是在涉及多语言的学术论文阅读、国际贸易合同签订等场景中,鼠标取词翻译能够为用户提供极大的便利。
五、未来发展趋势和展望随着人工智能技术的不断发展,未来鼠标取词翻译将更加智能化、精确化。
例如,结合语义分析、情感分析等功能,提供更符合语境的翻译结果。
同时,随着跨语言交流的需求不断增长,鼠标取词翻译的市场前景也将更加广阔。
总之,鼠标取词翻译作为一种实用便捷的翻译工具,已经深入到我们日常生活的方方面面。
金山词霸的使用
•
•
选择取词语言 ,选取词霸翻译的语言
固定取词条,锁定窗口在当前位置,避免浮动窗口因鼠标的移 动而消失
二.词典查询
1.查词典:即打开金山词霸的主界面,在输入框中输入单词 (如develop),按Enter即可进行查询。
输入框
2.模糊查询:当您忘记单词的完整拼写时,可采用 “*”、 “?”等通配符进行模糊查词。 (1) 如果你忘记一个单词中的某个字母可以用?来代替进行 查询,此时目录栏会列出所有符合条件的单词,如图:
五.全文检索
在浩如烟海的词库内容中可以飞速查找要检索的单词的位 置,便于找到相关内容(支持输入多个单词组合进行检索)。点 击图中标记的按钮,在出现的图标 中选择即可。
目录栏将显示 解释、例句等 涉及到 “develop”的单 词列表,右边 显示区会显示 列表中单词的 详细解释。
六.工具的使用
打开生词本
Ctrl + Alt + F1 Ctrl + Alt + A 打开或关闭屏幕取词开关 将当前所取的词加进用户词典。
Ctrl + Alt + S
Ctrl + Alt + C Ctrl + F12
跳转到当前所取的词的解释窗口。
将当前取词窗口的文本拷贝到剪贴板。 朗读选中的文字。
Ctrl + Tab 切换词霸左侧“索引”“向导”“全文检 索”“附录”标签的内容。
双击“学”后
4.生词本与词霸的灵活切换
词霸取词可以放入生词本中,点击取词条上的加入生词本按
钮,可将所取单词加入到生词本中,便于今后的学习。同时 在生词本中也可以快速切换到词霸的查询界面。方法很简单, 在生词本的浏览窗口中双击列表中要查询单词即可快速打开 词霸解释该单词。
双击选中单词 原理
"双击选中单词"是计算机用户界面中的一个常见功能,它允许用户通过快速连续的双击来选中屏幕上的一个单词。
这个功能的原理基于以下几个方面:
1. 鼠标或触摸板输入:
当用户第一次点击鼠标或触摸板时,计算机理解为此用户想选择单词的一部分。
第二次点击发生在第一次点击之后不久,且位置大致相同,计算机将其解释为用户想要选中整个单词。
2. 点击间隔时间:
双击通常要求两次点击之间有一定的时间间隔,以确保用户不是偶然的双击。
这个时间间隔是程序预设的,如果两次点击太快,计算机可能只会将其识别为一次单击。
3. 点击位置的检测:
计算机还会检测两次点击的位置是否接近,如果是,则认为是选中单词的行为;如果位置相差较大,则可能不会被识别为双击选中单词。
4. 操作系统和应用软件的协同工作:
操作系统和应用软件共同定义了双击选中单词的行为和规则。
例如,在文本编辑器中,双击选中单词是一个系统级的或应用程序级的特性。
5. 用户界面规范:
不同操作系统和应用程序可能会有不同的双击行为,但是通常都遵循一定的用户界面规范,以保持用户的操作习惯和一致性。
实现双击选中单词的功能,需要在操作系统或应用程序的编程接口中实现相应的监听器和事件处理逻辑。
在不同的编程环境中,这可能涉及到不同的编程语言和框架。
例如,在Windows操作系统中,可以使用Windows API来检测鼠标事件并实现双击选中单词的功能。
Lingoes使用教程
开始使用Lingoes 是一款专业的词典与文本翻译工具,但同时也是非常简明易用,将复杂的功能包含在简洁的用户界面设计中,用户只要打开Lingoes窗口,输入单词,然后按下“Enter”键,软件就会自动地帮你进行查找,并返回正确的翻译结果。
Lingoes另一项创新的技术就是“屏幕取词”功能,用户只需将鼠标移动到屏幕中的任何有单词的位置,按下“Ctrl + 鼠标右键”,Lingoes就能智能地识别出该单词,即时弹出迷你窗口显示翻译结果,你甚至还可以听到单词的读音。
这一切都是自动的,也不会干扰到你当前正在进行的工作。
对于普通的用户,无需任何学习,就可以凭直观的操作来使用Lingoes,如果你希望更进一步地了解Lingoes的工作原理,对软件进行更高级的个性化设定,请继续阅读下面对各个功能的详细使用说明。
词典Lingoes采用了软件与词典分离的开放式架构,软件本身作为一个提供查询本地或联机词典的功能平台,由用户根据自已的需要下载词典安装使用。
Lingoes有二种类型的词典, 为Lingoes提供了丰富的内容l 本地词典:词典数据安装在本地硬盘上,可以随时使用。
l 联机词典:是由在线词典服务商提供的查询服务,需要连接网络才能使用。
Lingoes所有的功能都是围绕着词典来进行,在“词典管理”窗口中,用户可以查看词典的属性,安装、删除词典,调整词典的排列顺序,启用或禁用词典等。
用户还可以根据不同的用途,分将词典加入到“索引组”、“取词组”中,为索引提示和取词功能提供支持。
1. 词典安装列表:所有已安装的词典都会显示在词典安装列表中,并且在Lingoes主窗口中查询单词的详细解释时,将会对词典安装列表中的词典进行查询。
2. 索引组:其中的词典用于输入单词时,提供索引提示功能。
3. 取词组:其中的词典用于在取词时查询单词的解释,在迷你窗口中查询单词时亦使用该组中的词典。
查询单词词典查询功能作为Lingoes最核心的功能,具有索引提示、查找词条和词组、单词变形识别、相关词匹配等专业查询技能。
屏幕取词翻译
屏幕取词翻译
随着互联网的发展,世界各地的人们日益依赖于网络的交流,而不同的语言正是网络交流过程中常见的障碍。
受到这些障碍的限制,很多人选择使用机器翻译,帮助他们浏览大量外文内容,以帮助他们更好地理解内容。
而屏幕取词翻译技术就是其中最值得关注的之一。
屏幕取词翻译就是将屏幕上某一字或一句话拿出来,然后将其翻译成目标语言,从而使人们可以更好地理解所阅读的内容。
屏幕取词翻译技术广泛应用于浏览器、搜索引擎、电子书、视频等场景,可以大大提高人们的阅读体验,减轻他们由于语言障碍带来的理解困难。
屏幕取词翻译技术的实现原理主要分为以下几个步骤:
首先,程序会自动获取屏幕上某一字或一句话,并将其发送到服务器。
接着,服务器会通过智能语言处理引擎,将源语言转换为目标语言。
最后,处理完毕的目标语言将会被发送到客户端,然后客户端就可以通过屏幕取词翻译技术将其显示出来。
当前,屏幕取词翻译技术的发展比较迅速。
百度翻译、腾讯翻译、有道翻译等都推出了屏幕取词翻译功能,使用上也比较方便,而且翻译结果也不错。
从而使得更多的人可以利用它们浏览大量外文内容,提高自己的阅读体验。
同时,屏幕取词翻译技术还有一些不足之处,比如翻译准确性不够、翻译效率低等。
不过,随着语言识别、自然语言处理技术的发展,
屏幕取词翻译技术也会越来越完善,从而帮助更多的人理解外语内容。
总的来说,屏幕取词翻译技术给大家带来了极大方便,帮助我们解决了语言障碍带来的困扰,让我们可以更好地欣赏外语文化。
从屏幕抓词的技术实现
从屏幕抓词的技术实现周天舒1999/6/22前言:现在的即时翻译软件种类很多,使用方法也各有千秋,但它们大都有一个共同的特点:鼠标指到哪儿,就翻译它下面的单词。
这大大地方便了用户,但是从一个编程人员的角度来看就不那么轻松了。
因为没有一个方便的函数类似”GetWordUnderMouse()”可以得到鼠标下面的单词,那么这些软件是怎么做的呢?经常在BBS和mailing list里看到和我同样困惑的问着相同问题的网友们。
经过痛苦地研究后,我找到了一种实现的方法,现拿出来和大家共享。
注:这个程序是为NT定做的,只能在NT下运行。
技术概述:屏幕上的大多数文字都是由gdi32.dll的以下几个函数显示的:TextOuA,TextOutW,ExtTextOutA,ExtTextOutW。
象user32.dll中的DrawTextA,DrawTextW都是调用GDI32.DLL的这几个函数实现的。
其实大家都知道实现屏幕抓词的关键是如何截获对这几个函数的调用。
我从易到难简要描述一下实现抓词需要做的几件事:一、得到鼠标的当前位置。
二、向鼠标下的窗口发重画消息,让它调用系统函数重画。
三、截获对系统函数的调用,得到发给系统函数的参数。
下面我对每一条逐个详细描述。
实现步骤相关技术的详细描述:一、得到鼠标的当前位置只要装入一个WH_MOUSE类型的系统钩子,就可以截获所有的鼠标消息。
SetWindowsHookEx(WH_MOUSE, //钩子类型(HOOKPROC)MouseProc, //回调函数GetModuleHandle("hookdll.dll"), //我的动态库0); //标明是系统钩子在回调函数里:if ( wParam == WM_MOUSEMOVE ) {lpMouseHookStruct = (LPMOUSEHOOKSTRUCT)lParam;MousePoint=lpMouseHookStruct->pt; //这就是鼠标的当前位置}二、向鼠标下的窗口发重画消息,让它调用系统函数重画。
屏幕抓词原理及其实现方法
第26卷 第4期2008年12月 贵 州 科 学GU IZHO U SC IENC E Vol.26,No .4Dec .2008收稿日期作者简介冀肖榆(%),男,5级研究生李丹宁,贵州科学院副院长,硕士生导师屏幕抓词原理及其实现方法冀肖榆1,赵廷义2,李丹宁2(1.贵州大学 电子技术与信息技术学院,贵州 贵阳 550025;2.贵州科学院,贵州 贵阳 550001)摘 要 本文介绍了W indo ws 2000/XP 下屏幕抓词的原理,对其如何获得鼠标位置的字符串,从代码拦截、鼠标HOOK 、屏幕刷新等方面进行了讨论.最后分析了通过本方法取词的局限性.关键词:屏幕取词;Hook;代码拦截中图分类号 TP391.1 文献标识码 A 文章编号 1003-6563(2008)04-0045-04THE P R INC IPL E AN D I M PL E M ENTAT I O N O F GRASP INGWO RDSJI Xiao -yu 1,ZHAO Ting -yi 2,L I D an -ning 2(1.College of Electroni c Sc ience and Infor m ati on T echn ology,Guizhou U nive rsity,Guiyang 550025;2.GuizhouAcademy of Sc iences,G uiyang 50001)ABSTRAC T This article intr oduced the princ i p l e of gras p ing words in W indo ws 2000/XP 1W e discussed theaspects of code intercepti on,mouse HOOK,and screen refurbis hing on h ow t o ge t charac ter a round t he mouse .F i nall y,we analyzed the li m itations of the gra s p i ng word based on the me th od .KEY W O RD S gras p ing word;H OOK ;AP I interception1 概述屏幕抓词的关键是如何获得鼠标位置的字符串,W indow s 的动态链接和消息响应机制为之提供了实现途径[3].概括地说,主要通过下面的几个步骤来取得屏幕上鼠标位置的字符串:代码拦截:W indows 以DLL 方式提供系统服务,可以方便地获取W indows 字符输出A P I 的地址,修改其入口代码,拦截应用程序对它们的调用.鼠标HOOK :安装WH _MOUSE 类型的全局鼠标HO O K 过程,监视鼠标在整个屏幕上的移动.屏幕刷新:使鼠标周围一块区域无效,并强制鼠标位置的窗口刷新屏幕输出.窗口过程响应WM_P A I N T 消息,调用ExtText Out 等字符输出AP I 更新无效区域里面的字符串.这些调用被我们截获,从堆栈里取得窗口过程传给字符I 的参数,如字符串地址、长度、输出坐标、D 、裁剪区等信息:2008-04-22:1982200..AP H C .2 原理分析在W indows2000/XP 系统中,屏幕上的大多数文字都是由gdi32.dll 和user32.dll 中的几个函数[1]显示的:Text O ut A ,Text Out W ,ExtText Out A ,ExtText Out W ,D ra wText W ,D ra wText A.其调用关系如图1所示.图1 字符输出F i g .1 C ha ra c te r o ut p ut 从上图可以看出,无论是通过那个字符输出函数,最终应用程序都会通过G D I 32.dll 的ExtText Out A 和ExtText O ut W 把字符输出到屏幕上.其中ExtText O ut A 输出ANS I 格式的字符,ExtText O ut W 输出U N I CODE 格式的字符;因此,只要拦截2个函数:ExtText Out A 和ExtText Out W ,就能截获应用程序的所有字符串输出.3 实现本文将用Delphi 实现在W indows2000/XP 系统下的屏幕抓词,以下是实现步骤及关键的伪码.图2 屏幕抓词实现F i g .2 R ea li z i ng t he g ra sp i ng wo rd s3.1 拦截字符输出AP I3 代码拦截的基本思路为了实现对x T xO x T xO W 等I 的拦截,需要在函数入口放入一条动态生成的“M <替代函数>”指令,M 的操作数是我们提供的一个拦截替代函数的地址[]当该I 被调用时,M 指令首64贵 州 科 学 26卷 .1.1E t e t ut A /E t e t ut A P J P J P 2.AP J P先执行,跳转到替代函数.替代函数负责从堆栈中获取参数,计算字符串坐标,分出鼠标位置的单词等工作.执行完成后,替代函数再调用原来的被拦截函数,完成正常的字符输出,然后返回.图2(a )表明了应用程序对Text Out 的正常调用流程,图2(b )是系统Text Out 被拦截后的流程.3.1.2 构造拦截替代函数拦截替代函数插入被拦截AP I 的流程中执行,需要有相同的参数和返回值原型.以对Text O utExt A 的拦截为例,下面是替代函数myText O utExt A 的D el phi 关键代码:Boolean m yExtText Out (H DC hdc,integer x,y,LPSTR l p str,integer cbstr ){ DoS py (hDC ,x ,y,lpstr,cbstr ); //保存参数,作抓词的所有工作 Restor eCode ();//恢复Text Out 入口原来的指令 Text Out (hDC ,x,y,l p str,cbstr );//调用原来的AP I SpyCode ();//再次放入JMP 指令 return TRUE;}函数首先调用DoSpy ()来作抓词的具体工作,然后R est oreCode ()函数恢复被拦截函数入口的代码,再调用Text O ut ()执行正常的字符输出,接下来S pyCode ()在被拦截函数入口再次放入JMP 指令,最后返回调用进程.3.1.3 获取被拦截AP I 的动态链接地址ExtText O ut W /ExtText Out A 的地址可以通过G e tPr oc Addr e ss 取得,以myExtText Out 为例的De l phi 伪码如下:Va r pSysFunc:Pointer; //系统函数的地址pSysFunc :=G e tPr ocAddr e ss (Get M oduleHandle (’gdi .dll ’),’ExtText O ut A ’);3.1.4 动态生成J MP 指令动态生成的J MP 指令占用5个字节,保存在一个TCode5记录内.以生成跳转到m yExtText Out A 的替代函数m yExtText Out 的JMP 指令为例的De l phi 关键代码如下:Va r pThunkFunc: Pointer ; //替换函数的地址 code B ak: TCode5;//系统函数的代码的前5个字节 codeThunk: T Code5;//替换函数的代码的5个字节pT hunkFunc :=GetP r oc Address (hI nstance,’m yExtText Out A ’);c ode B ak.siJmp :=P B yte (pSysFunc)^;c ode B ak.dw Addr :=P D WORD (D WORD (pSysFunc)+1)^;c odeThunk.si Jmp :=ShortInt (S _E9);c odeThunk.dw Addr :=D WORD (pThunkFunc )-D WORD (pSysFunc )-5;3.1.5 修改被拦截函数的入口修改ExtText Out W /ExtText Out A 的入口以便系统调用这两个函数的时候能重定向到自己的代码m yExt 2Text Out W /m yExtText Out A,可以通过W ritePr ocess Me mory 进行修改,该AP I 可以把指定长度的代码拷贝到指定进程地址空间中[2].关键代码如下:W ritePr oce ss Memor y (hPr oc,pSysFunc,@codeThunk,5);上面的语句表示把codeThunk 中前五位代码(即替换函数m yExtText Out 的入口地址)拷贝到pSysFunc 进程空间(即系统函数x T xO 的入口地址)中,即完成对x T xO 等I 的拦截修改3 使用OO K 监视鼠标移动安装一个W _MOUS 类型的全局OOK,就可以截获所有的鼠标消息[]每当有鼠标事件产生,OOK74 4期 冀肖榆,等:屏幕抓词原理及其实现方法E t e t ut E t e t ut AP ..2H H E H 4.H84贵 州 科 学 26卷 过程被调用,它判断出鼠标移动了后,就向主窗口发送消息,通知主窗口鼠标位置发生了变化.该全局H OOK 过程需要放入DLL里面.只要装入一个WH_MOUSE类型的系统钩子,关键代码如下:Set W indow s HookEx( W H_MOUSE, //钩子类型 (HOOK PROC)MouseP r oc,//回调函数 Get ModuleHandle("hookdll.dll"),//我的动态库0);//标明是系统钩子,即拦截所有消息在回调函数里:if(wPara m=WM_MOUSE MOVE)then beginl pMouseHookStruct:=(LP MO USEHOOKSTRUCT)lPara m;MousePoint:=l pMouseHookStruct->p t; //这就是鼠标的当前位置End;3.3 刷新屏幕输出根据鼠标位置(更确切说是需求取词的位置)构造一个矩形区域,然后调用I nva lidateR ect使得该区域无效,强制目标窗口刷新屏幕输出.在响应WM_P A I N T系统消息中,目标窗口对字符输出A PI(ExtText Out W/ ExtText O ut A)的调用将被我们截获和处理,完成一次抓词过程.如下是关键代码:Va r r ect:TR ECT; //使之无效的矩形区域ScreenToC lient(hwnd,&MousePoint);//转换坐标rec t.left:=MousePoint.x;//根据要取词的位置构造无效矩形区域rec t.t op:=MousePoint.y;rec t.right:=MousePoint.x+1;rec t.bott om:=MousePoint.y+1;I nvalidateR ect(hwnd,@r ect,F ALSE);//使之无效4 局限性分析安装钩子的抓词方式是目前最流行的方式,这种技术是基于应用程序调用W indows系统ExtText Out输出字符的原理,若是应用程序有自己特定的字符输出机制,不通过ExtText Out输出,则钩子方式是无法截获缓冲区的字符的.典型的应用就是Acr obat R eader.由于上述的局限性,可以考虑把OCR技术应用到屏幕抓词中,即直接读取屏幕一块区域为图片并进行文字识别,但效率问题一直未能得到比较好的解决.参考文献[1] John Ayres.Del p hi W in32核心AP I参考[M].北京:中国电力出版社,2004.[2] R I CHT ER J.W indo w s核心编程[M].北京:北京机械工业出版社,2000,299~309.[3] 陈小辉.钩子技术在信息窥探中的使用[J].计算机与现代化,2006,9:97~99.[4] 熊志勇.利用Hook技术实现屏幕热区[J].电脑编程技巧与维护,2002,12:17~18.。
Delphi屏幕取词
Delphi屏幕取词屏幕取词(Delphi)(1)“ 屏幕取词”的实现//-----------------------------------------------------------------1用SetWindowsHookEx()安装鼠标钩子MouseProc;2在屏幕上移动鼠标时,系统就会调用鼠标钩子MouseProc;3进入MouseProc,获得鼠标的坐标(x,y),设置对T extOut()、ExtTextOut()等的跟踪程序,用invalidateRect()告诉系统该点(x,y)“失效”;4系统发出WM_PAINT消息,指示该点(x,y)处的应用程序重绘“失效”的区域。
5负责绘制该点()的应用程序在受到 WM_PAINT 消息后,就有机会调用TextOut()、 ExtTextOut()等函数。
6调用的函数被拦截进入跟踪程序:设置好了的跟踪程序截获了该次调用,从应用程序的堆栈中取出该点(x,y)“文字”的指针;7从应用程序的数据段中将“文字”指针的内容取出,即完成了一次“屏幕抓字”;8退出跟踪程序,返回到鼠标钩子MouseProc;9在MouseProc中解除对TextOut() ExtTextOut()的跟踪;10退出MouseProc鼠标钩子程序,控制权交给系统。
11在屏幕上移动鼠标,开始下一次“屏幕抓字”,返回步骤2。
//-----------------------------------------------------------------Dll工程.GetWordDll.dpr//-----------------------------------------------------------------------------------library GetWordDll;usesWindows,SysUtils,Classes,UnitHookDll in 'UnitHookDll.pas',UnitNt2000Hook in 'UnitNt2000Hook.pas',UnitHookType in 'UnitHookType.pas';exportsStartHook,StopHook,// MouseWndProc,{以下导出列表都是必须的,不能少,因为程序要取其地址}NewBeginPaint,NewCreateCompatibleDC,NewTextOutA,NewTextOutW,NewExtTextOutA,NewExtTextOutW,NewDrawTextA,NewDrawTextW;beginend.UnitHookType.pasunit UnitHookType;interfaceuses windows, messages;constMaxStringLen = 100;WM_MOUSEPT = WM_USER + 1138; MappingFileName = 'GetWord32 for 9x NT 2000'; fBeginPaint=0;fGetWindowDC=1;fGetDC=2;fCreateCompatibleDC=3;fTextOutA=4;fTextOutW=5;fExtTextOutA=6;fExtTextOutW=7;fDrawTextA=8;fDrawTextW=9;typePPointer = ^Pointer;TShareMem = packed recordhProcWnd: HWND; {主应用窗口句柄}hHookWnd: HWND; {鼠标所在窗口}pMouse: TPoint; {鼠标信息}DCMouse,DCCompatible: HDC;fTimerID: integer;fStrMouseQueue: array[0..MaxStringLen] of Char; {鼠标信息串} nTimePassed: integer; {鼠标停留的时间}bCanSpyNow: Boolean; {开始取词}Text: array[0..MaxStringLen] of Char; {字符串}end;PShareMem = ^TShareMem;implementationend.UnitNt2000Hook.pas//-----------------------------------------------------------------------------------unit UnitNt2000Hook;interfaceuses classes, Windows,SysUtils, messages,dialogs;typeTImportCode = packed recordJumpInstruction: Word;AddressOfPointerT oFunction: PPointer;end;PImportCode = ^TImportCode;PImage_Import_Entry = ^Image_Import_Entry;Image_Import_Entry = recordCharacteristics: DWORD;TimeDateStamp: DWORD;MajorVersion: Word;MinorVersion: Word;Name: DWORD;LookupTable: DWORD;end;TLongJmp = packed recordJmpCode: ShortInt; {指令,用$E9来代替系统的指令}FuncAddr: DWORD; {函数地址}end;THookClass = classprivateTrap:boolean; {调用方式:True陷阱式,False改引入表式}hProcess: Cardinal; {进程句柄,只用于陷阱式}AlreadyHook:boolean; {是否已安装Hook,只用于陷阱式}AllowChange:boolean; {是否允许安装、卸载Hook,只用于改引入表式}Oldcode: array[0..4]of byte; {系统函数原来的前5个字节}Newcode: TLongJmp; {将要写在系统函数的前5个字节}privatepublicOldFunction,NewFunction:Pointer;{被截函数、自定义函数}constructor Create(IsTrap:boolean;OldFun,NewFun:pointer);constructor Destroy;procedure Restore;procedure Change;publishedend;implementation{取函数的实际地址。
鼠标唤醒显示屏的工作原理
鼠标唤醒显示屏的工作原理
鼠标唤醒显示屏的工作原理一般是通过以下步骤:
1、鼠标状态检测:鼠标通常会有一个内置的传感器,可以感知鼠标是否移动或点击。
当鼠标被移动或者点击时,传感器会发送信号。
2、鼠标信号传输:鼠标传感器发送的信号会经过鼠标的电路进行处理,并通过鼠标上的线缆或无线信号传输途径将信号传输给计算机。
3、计算机接收信号并处理:计算机接收到鼠标传输的信号后,操作系统会解读信号,并根据信号的类型作出相应的动作。
例如,如果信号是鼠标移动的信号,操作系统会根据鼠标移动的方向和速度改变光标的位置。
4、驱动显示屏:根据操作系统的指令,显示屏的驱动电路会根据鼠标的信号进行调整,以控制显示屏的亮度、色彩等参数,并将图像信号发送到显示屏的屏幕上。
5、显示屏显示图像:显示屏接收到来自驱动电路的图像信号后,会根据信号进行屏幕的像素点点亮或白化操作,以显示出相应的图像内容。
- 1 -。
鼠标的原理
鼠标的原理鼠标的原理主要是通过光电传感器来实现的。
鼠标底部通常有一个小的红光或红色的光电传感器,这个传感器可以感知鼠标在桌面上的移动情况。
当我们移动鼠标时,光电传感器会感知到桌面上的纹理变化,然后通过内部的电路将这些变化转换成计算机可以识别的信号,最终传输给计算机,从而实现鼠标在屏幕上的移动。
除了光电传感器,鼠标还有一个滚轮和若干个按键。
滚轮主要用于上下滚动页面或调整音量大小,它的原理是通过内部的编码器来感知滚轮的旋转方向和速度,然后将这些信息转换成计算机可以识别的信号。
按键的原理也是类似的,通过按下按键来改变电路的连接状态,从而发送相应的信号给计算机。
在鼠标的工作原理中,最关键的部分是光电传感器。
光电传感器可以分为两种类型,一种是机械式光电传感器,另一种是固态光电传感器。
机械式光电传感器通过机械结构来感知鼠标在桌面上的移动情况,而固态光电传感器则是通过固态元件来实现的。
固态光电传感器由于没有机械结构,所以寿命更长,精度更高,而且不容易受到灰尘和杂物的影响,所以现在大部分的鼠标都采用固态光电传感器。
除了光电传感器,鼠标的工作原理还与鼠标的底部有关。
鼠标通常使用橡胶球或光栅来感知桌面的纹理变化,然后将这些变化转换成信号。
橡胶球鼠标由于机械结构复杂,所以不太常见了,而光栅鼠标则是通过内部的光栅结构来感知桌面的纹理变化,从而实现鼠标在屏幕上的移动。
总的来说,鼠标的原理是通过光电传感器来感知鼠标在桌面上的移动情况,然后通过内部的电路将这些变化转换成计算机可以识别的信号,最终传输给计算机,从而实现鼠标在屏幕上的移动。
光电传感器的类型和鼠标底部的结构对鼠标的性能和精度有着重要的影响。
希望通过本文的介绍,可以让大家更好地了解鼠标的工作原理,从而更好地使用和维护鼠标。
操作系统中鼠标操作原理
操作系统HID接口
而在创建具体的设备节点之前,Event Handler层需要先注册一类设备的输入事件处理函数及相关 接口 static int __init mousedev_init(void) { //注册输入事件处理、连接、断开、设备(文件)操作函数 input_register_handler(&mousedev_handler); //创建输入设备节点 devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), S_IFCHR|S_IRUGO|S_IWUSR, "input/mice"); } static struct input_handler mousedev_handler = { .event = mousedev_event,//REL(相对坐标)、按键、ABS(绝对坐标)、SYNC等事件处 理 .connect = mousedev_connect, .disconnect = mousedev_disconnect, .fops = &mousedev_fops,//鼠标打开、poll、读写等操作 .minor = MOUSEDEV_MINOR_BASE, .name = "mousedeቤተ መጻሕፍቲ ባይዱ", .id_table = mousedev_ids, };
HID协议简介
下面仅对这3个HID设备类特定描述符中的HID描 述符进行说明。 HID描述符关联于接口描述符,因而如果一个设 备只有一个接口描述符,则无论它有几个端点描 述符,HID设备只有一个HID描述符。HID设备描 述符主要描述HID规范的版本号、HID通信所使 用的额外描述B HID类可采用的通信管道 所有的HID设备通过USB的控制管道(默认管道 ,即端点0)和中断管道与主机通信。 USB HID规范定义的HID设备可用端点包含三种 管道: 1、控制(端点0)( 必选) 传输USB描述符、类请求 代码以及供查询的消息数据等 2、中断输入 (必选) 传输从设备到主机的输入数 据 3、中断输出 (可选 )传输从主机到设备的输出数 据
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,希望这篇文章对大家能有所帮助。