屏幕上的文字大都是由gdi32.dll的以下几个函数显示的:TextOutA、TextOutW、ExtTextOutA、ExtTextOutW。实现屏幕抓词的关键就是截获对这些函数的调用,得到程序发给它们的参数。

  我的方法有以下三个步骤:

  一、得到鼠标的当前位置

  通过SetWindowsHookEx实现。

  二、向鼠标下的窗口发重画消息,让它调用系统函数重画

  通过WindowFromPoint,ScreenToClient,InvalidateRect 实现。

  三、截获对系统函数的调用,取得参数(以TextOutA为例)

   1.仿照TextOutA作成自己的函数MyTextOutA,与TextOutA有相同参数和返回
值,放在系统钩子所在
的DLL里。

   SysFunc1=(DWORD)GetProcAddress(GetModuleHandle( "gdi32.dll"),"TextO
utA");

   BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR l
pszString,int cbString)

   { //输出lpszString的处理

return ((FARPROC)SysFunc1)(hdc,nXStart,nYStart,lpszString,cbString);}

   2.由于系统鼠标钩子已经完成注入其它GUI进程的工作,我们不需要为注入再
做工作。

  如果你知道所有系统钩子的函数必须要在动态库里,就不会对“注入”感到
奇怪。当进程隐式或显式
调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址
空间里(以下简称“地址空
间”)。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的
堆栈(见图1)。

  图1 DLL映射到虚拟地址空间中

  对系统钩子来说,系统自动将包含“钩子回调函数”的DLL映射到受钩子函数
影响的所有进程的地址
空间中,即将这个DLL注入了那些进程。

   3.当包含钩子的DLL注入其它进程后,寻找映射到这个进程虚拟内存里的各个
模块(EXE和DLL)的
基地址。EXE和DLL被映射到虚拟内存空间的什么地方是由它们的基地址决定的。
它们的基地址是在链接
时由链接器决定的。当你新建一个Win32工程时,VC++链接器使用缺省的基地址
0x00400000。可以通
过链接器的BASE选项改变模块的基地址。EXE通常被映射到虚拟内存的0x0040000
0处,DLL也随之有
不同的基地址,通常被映射到不同进程的相同的虚拟地址空间处。

  如何知道EXE和DLL被映射到哪里了呢?

  在Win32中,HMODULE和HINSTANCE是相同的。它们就是相应模块被装入进程的
虚拟内存空间的
基地址。比如:

   HMODULE hmodule=GetModuleHandle(″gdi32.dll″);

  返回的模块句柄强制转换为指针后,就是gdi32.dll被装入的基地址。

  关于如何找到虚拟内存空间映射了哪些DLL?我用如下方式实现:

while(VirtualQuery (base, &mbi, sizeof (mbi))〉0)

{ if(mbi.Type==MEM—IMAGE)

ChangeFuncEntry((DWORD)mbi.BaseAddress,1);

base=(DWORD)mbi.BaseAddress+mbi.RegionSize; }

   4.得到模块的基地址后,根据PE文件的格式穷举这个模块的IMAGE—IMPORT—
DESCRIPTOR数组,
看是否引入了gdi32.dll。如是,则穷举IMAGE—THUNK—DATA数组,看是否引入了
TextOutA函数。

   5.如果找到,将其替换为相应的自己的函数。

  系统将EXE和DLL原封不动映射到虚拟内存空间中,它们在内存中的结构与磁
盘上的静态文件结构
是一样的。即PE (Portable Executable) 文件格式。

  所有对给定API函数的调用总是通过可执行文件的同一个地方转移。那就是一
个模块(可以是EXE或
DLL)的输入地址表(import address table)。那里有所有本模块调用的其它DLL的
函数名及地址。对其它DLL
的函数调用实际上只是跳转到输入地址表,由输入地址表再跳转到DLL真正的函数
入口。例如:

  图2 对MessageBox()的调用跳转到输入地址表,从输入地址表再跳转到Mess
ageBox函数

   IMAGE—IMPORT—DESCRIPTOR和IMAGE—THUNK—DATA分别对应于DLL和函数。
它们是PE
文件的输入地址表的格式(数据结构参见winnt.h)。

   BOOL ChangeFuncEntry(HMODULE hmodule)

   { PIMAGE—DOS—HEADER pDOSHeader;

   PIMAGE—NT—HEADERS pNTHeader;

   PIMAGE—IMPORT—DESCRIPTOR pImportDesc;

/get system functions and my functions′entry/

   pSysFunc1=(DWORD)GetProcAddress(GetModuleHandle(″gdi32.dll″),″T
extOutA″);

   pMyFunc1= (DWORD)GetProcAddress(GetModuleHandle(″hookdll.dll″),″
MyTextOutA″);

pDOSHeader=(PIMAGE—DOS—HEADER)hmodule;

   if (IsBadReadPtr(hmodule, sizeof(PIMAGE—NT—HEADERS)))

   return FALSE;

   if (pDOSHeader-〉e—magic != IMAGE—DOS—SIGNATURE)

   return FALSE;

   pNTHeader=(PIMAGE—NT—HEADERS)((DWORD)pDOSHeader+(DWORD)pDOSHead
er-〉e—
lfanew);

   if (pNTHeader-)Signature != IMAGE—NT—SIGNATURE)

   return FALSE;

   pImportDesc = (PIMAGE—IMPORT—DESCRIPTOR)((DWORD)hmodule+(DWORD)
pNTHeader
-)OptionalHeader.DataDirectory

   [IMAGE—DIRECTORY—ENTRY—IMPORT].VirtualAddress);

   if (pImportDesc == (PIMAGE—IMPORT—DESCRIPTOR)pNTHeader)

return FALSE;

   while (pImportDesc-)Name)

   { PIMAGE—THUNK—DATA pThunk;

   strcpy(buffer,(char)((DWORD)hmodule+(DWORD)pImportDesc-)Name))
;

CharLower(buffer);

if(strcmp(buffer,"gdi32.dll"))

{ pImportDesc++;

continue;

}else

{ pThunk=(PIMAGE—THUNK—DATA)((DWORD)hmodule+(DWORD)pImportDesc-)Fi
rstThunk);

while (pThunk-)u1.Function)

{ if ((pThunk-)u1.Function) == pSysFunc1)

{ VirtualProtect((LPVOID)(&pThunk-)u1.Function),

   sizeof(DWORD),PAGE—EXECUTE—READWRITE, &dwProtect);

   (pThunk-)u1.Function)=pMyFunc1;

   VirtualProtect((LPVOID)(&pThunk-)u1.Function), sizeof(DWORD),dw
Protect,&temp); }

pThunk++; } return 1;}}}

  替换了输入地址表中TextOutA的入口为MyTextOutA后,截获系统函数调用的
主要部分已经完成,当
一个被注入进程调用TextOutA时,其实调用的是MyTextOutA,只需在MyTextOutA
中显示传进来的字符
串,再交给TextOutA处理即可。

转载于:https://www.cnblogs.com/hcmfys/archive/2008/11/17/1335296.html

VC++屏幕抓词的技术实现相关推荐

  1. 关于金山词霸的屏幕取词3

    本文只对与几个关键性技术的实现细节进行讨论,其它的编程细节,请参考源程序. 32位到16位的形式替换 32位代码与16位代码的数据交换 动态修改Windows内核 1. 32bit到16bit的形式替 ...

  2. [IE9] 开发IE9上的屏幕取词功能

    "屏幕取词"是词典软件(如:金山词霸)里面一个必要功能. 在IE9中,由于使用Direct2D接口取代GDI做页面渲染,原来基于GDI接口的屏幕取词技术将无法继续正常工作. 因此, ...

  3. 金山词霸”屏幕取词技术揭密

    金山词霸"屏幕取词技术揭密                               "金山词霸"屏幕取词技术揭密(讨论稿)                       ...

  4. 屏幕取词技术实现原理与关键源码

    转自: https://www.cnblogs.com/seacryfly/archive/2012/01/08/2316511.html 虽然屏幕取词技术早已经不是什么秘密,以至于除了汉化工具.翻译 ...

  5. 金山词霸”屏幕取词技术揭密(讨论稿)

    金山词霸"屏幕取词技术揭密(讨论稿) 主题 屏幕取词技术系列讲座(一) 作者 亦东 很多人对这个问题感兴趣. 原因是这项技术让人感觉很神奇,也很有商业价值. 现在词典市场金山词霸占了绝对优势 ...

  6. Linux 下的屏幕取词技术

    Linux 下的屏幕取词技术 作者: 于明俭 屏幕取词即当鼠标在应用软件所显示中/英文的地方滑过, 则有一小窗口 出现在离单词附近, 上面出现鼠标下面单词的解释. 屏幕取词以前一直是 MS Windo ...

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

    鼠标屏幕取词技术的原理和实现 "鼠标屏幕取词"技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在WINDOWS系统中实现却是非常复杂的,总的来 ...

  8. 鼠标屏幕取词原理 (VC++)

    "鼠标屏幕取词"技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在windows系统中实现却是非常复杂的,总的来说有两种实现方式:  第一种: ...

  9. “鼠标屏幕取词”技术

    "鼠标屏幕取词"技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在WINDOWS系统中实现却是非常复杂的,总的来说有两种实现方式: 第一种:采 ...

最新文章

  1. 分布式事务 GTS 的价值和原理浅析
  2. 到底什么时候该使用MQ
  3. python抓包教程_Python爬虫入门技能:教你通过 Fiddler 进行手机抓包!
  4. 关于百度地图api测距显示NaN的解决方案
  5. python及numpy,pandas易混淆的点
  6. RT-Thread下的串口驱动程序分析
  7. kmp算法字符串匹配C语言实现
  8. 如何用禅道写出一份让开发膜拜的测试用例?超神级测试用例库
  9. labelme制作数据集:将标注好的json文件,转换成类似VOCdevkit数据集中的SegmentationClass(标注技巧)
  10. 自定义django表单
  11. PHP两文件嵌套循环引用,php的循环与引用的一个坑,php循环引用_PHP教程
  12. leetcode 225. Implement Stack using Queuesk
  13. 2021年杭州行政区划调整方案与远景轨道线网概念图
  14. 三折线弹塑性滞回模型matlab,考虑屈曲的钢筋滞回模型
  15. LCD1602液晶显示屏使用方法
  16. 图像修复序列——混合稀疏表示(Hybrid Sparse Representations)模型
  17. 【LTspice】004 Voltage Source 参数配置
  18. 算法设计与分析(整理)
  19. unity urp内置lit材质源码解析(上)
  20. BS架构调用TSC TTP-244 Pro条码打印机的常见问题及解决方案

热门文章

  1. JSP的学习二(请求转发与 重定向)
  2. 虚幻4 远处的贴图模糊处理
  3. 【神经网络与深度学习】【C/C++】比较OpenBLAS,Intel MKL和Eigen的矩阵相乘性能
  4. Tomcat7安装配置 for Ubuntu
  5. Oracle数据库的认证方法、用户管理、权限管理和角色管理等
  6. java对象关系映射ROM
  7. 替换k个字符后最长重复子串
  8. DeepMind详解新WaveNet:比原来快千倍,语音更自然 | 附论文
  9. 微信发布2017国庆长假出游情况大数据报告
  10. Flash中如何使用滤镜