屏幕抓词的技术实现

粥糨干饭稀

粥糨干饭稀

2016-02-19 15:18

下面是个简单易学的屏幕抓词的技术实现教程,图老师小编详细图解介绍包你轻松学会,喜欢的朋友赶紧get起来吧!

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

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

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

  一、得到鼠标的当前位置

  通过SetWindowsHookEx实现。

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

  通过WindowFromPoint,ScreenToClient,InvalidateRect 实现。

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

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

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

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

  BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,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通常被映射到虚拟内存的0x00400000处,DLL也随之有不同的基地址,通常被映射到不同进程的相同的虚拟地址空间处。

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

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

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

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

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

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

  { if(mbi.Type==MEMIMAGE)

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

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

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

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

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

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

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

  

IMAGEIMPORTDESCRIPTOR和IMAGETHUNKDATA分别对应于DLL和函数。它们是PE文件的输入地址表的格式(数据结构参见winnt.h)。
  BOOL ChangeFuncEntry(HMODULE hmodule)
  { PIMAGEDOSHEADER pDOSHeader;
  PIMAGENTHEADERS pNTHeader;
  PIMAGEIMPORTDESCRIPTOR pImportDesc;
/ get system functions and my functions′entry /
  pSysFunc1=(DWORD)GetProcAddress(GetModuleHandle(〃gdi32.dll〃),〃TextOutA〃);
  pMyFunc1= (DWORD)GetProcAddress(GetModuleHandle(〃hookdll.dll〃),〃MyTextOutA〃);
pDOSHeader=(PIMAGEDOSHEADER)hmodule;
  if (IsBadReadPtr(hmodule, sizeof(PIMAGENTHEADERS)))
   return FALSE;
  if (pDOSHeader-〉emagic != IMAGEDOSSIGNATURE)
   return FALSE;
  pNTHeader=(PIMAGENTHEADERS)((DWORD)pDOSHeader+(DWORD)pDOSHeader-〉elfanew);
  if (pNTHeader-〉Signature != IMAGENTSIGNATURE)
   return FALSE;
  pImportDesc = (PIMAGEIMPORTDESCRIPTOR)((DWORD)hmodule+(DWORD)pNTHeader-〉OptionalHeader.DataDirectory
   [IMAGEDIRECTORYENTRYIMPORT].VirtualAddress);
  if (pImportDesc == (PIMAGEIMPORTDESCRIPTOR)pNTHeader)
return FALSE;
  while (pImportDesc-〉Name)
  { PIMAGETHUNKDATA pThunk;
  strcpy(buffer,(char )((DWORD)hmodule+(DWORD)pImportDesc-〉Name));
CharLower(buffer);
if(strcmp(buffer,"gdi32.dll"))
{ pImportDesc++;
continue;
}else
{ pThunk=(PIMAGETHUNKDATA)((DWORD)hmodule+(DWORD)pImportDesc-〉FirstThunk);
while (pThunk-〉u1.Function)
{ if ((pThunk-〉u1.Function) == pSysFunc1)
{ VirtualProtect((LPVOID)(&pThunk-〉u1.Function),
   sizeof(DWORD),PAGEEXECUTEREADWRITE, &dwProtect);
   (pThunk-〉u1.Function)=pMyFunc1;
   VirtualProtect((LPVOID)(&pThunk-〉u1.Function), sizeof(DWORD),dwProtect,&temp); }
pThunk++; } return 1;}}}

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

展开更多 50%)
分享

猜你喜欢

屏幕抓词的技术实现

编程语言 网络编程
屏幕抓词的技术实现

java实现屏幕取色

编程语言 网络编程
java实现屏幕取色

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

DreamweaverASP实现分页技术

Web开发
DreamweaverASP实现分页技术

android屏幕全屏的实现代码

编程语言 网络编程
android屏幕全屏的实现代码

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

用Delphi实现远程屏幕抓取

Delphi
用Delphi实现远程屏幕抓取

Ubuntu中如何实现屏幕切分的方法

服务器
Ubuntu中如何实现屏幕切分的方法

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

在Windows Media Player中刻录CD或DVD

在Windows Media Player中刻录CD或DVD

JavaScript中无符号右移运算符

JavaScript中无符号右移运算符
下拉加载更多内容 ↓