VC实现屏幕截词功能的方法详解

   2015-07-12 0
核心提示:这篇文章主要介绍了VC实现屏幕截词功能的方法详解,对于深入的理解windows程序运行原理很有帮助,需要的朋友可以参考下

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

实现的方法有以下三个步骤:

一、得到鼠标的当前位置
通过SetWindowsHookEx实现。

二、向鼠标下的窗口发重画消息,让它调用系统函数重画
通过WindowFromPoint,ScreenToClient,InvalidateRect 实现。

三、截获对系统函数的调用,取得参数(以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成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

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==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真正的函数入口。例如:

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

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"),"TextOutA");
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)pDOSHeader->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->FirstThunk);
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),dwProtect,&temp);
}
pThunk++; 
} 
return 1;
}
}
}

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

 
标签: VC 屏幕 方法
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • asp.net mvc多条件+分页查询解决方案
    


            
asp.net mvc多条件+分页查询解决方案
    asp.net mvc多条件+分页查询解决方案
    http://www.cnblogs.com/nickppa/p/3232535.html开发环境vs2010css:bootstrapjs:jquery    bootstrap paginator原先只是想做个mvc的分页,但是一般的数据展现都需要检索条件,而且是多个条件,所以就变成了MVC多条件+分页查询因为美工不是很好,所以用的是
    02-09
  • 教程一:用ASP.NET MVC创建一个TaskList应用程序
    教程一:用ASP.NET MVC创建一个TaskList应用程
    原文地址:http://www.asp.net/learn/mvc/tutorial-01-cs.aspx本篇教程目的是让你了解创建一个ASP.NET程序是“怎么样子的”。在这篇教程里,我会从头到尾快速地创建一整个ASP.NETMVC程序。我会告诉你如何创建一个简单的TaskList程序。如果你跟ASP或者APS.NET
    02-09
  • [C#]使用 AltCover 获得代码覆盖率 - E2E Test 和 Unit Test
    [C#]使用 AltCover 获得代码覆盖率 - E2E Test
    背景在 CI/CD 流程当中,测试是 CI 中很重要的部分。跟开发人员关系最大的就是单元测试,单元测试编写完成之后,我们可以使用 IDE 或者 dot cover 等工具获得单元测试对于业务代码的覆盖率。不过我们需要一个独立的 CLI 工具,这样我们才能够在 Jenkins 的 CI
  • Delphi XE2  使用自带vclstyle换肤
    Delphi XE2 使用自带vclstyle换肤
    听说 Delphi XE2  里自带了vclstyle,很兴奋,想试试,因为以前delphi7中总是用vclstyle的第三方控件,这次终于可以不用了安装了,摸索了半天,不知道怎么用,查自带的帮助吧,还真不好找!后来发现,在这里(有图有真相):1、Project- Options - Applicati
    02-09
  • Delphi VCL 在TRichEdit控件中设置WordWrap属性
    最近在使用RAD XE7时,在WIN10上开发一个类似记事本的工具,使用的是TRichEdit控件,在实现自动换行功能时,发现设置WordWrap后无法自动换行,经过检查发现和滚动条ScrollBars属性有关系,只要设置水平滚动条( RichEdit1.ScrollBars := TScrollStyle.ssBoth
    02-09
  • VC,BCB,C#,Delphi,Java的委托方案 vcbcbcbcb
    委托(delegate)有委托接收方和委托发出方两个实例。委托接收方需要有一个函数实现,然后“拜托”别人调用。委托发出方需要有一个“服务员”接受委托接收方的“委托”请求,并记录调用入口,在非面向对象的时候用的是函数指针,现在面向对象,需要保存this后调
    02-09
  • VC调用Delphi制作的动态链接库如何互相传递字符
    1、VC to DelphiDelphi源程序:library ExportChartData; uses   SysUtils,   Classes; {$R *.res} const   DLL_VER      : Word = $0001; function GetVer: Word; begin   Result := DLL_VER; end; procedure SayHello(a
    02-09
  • (转) Lua使用心得一 LUA和VC整合
    这几天研究了一下lua,主要关注的是lua和vc之间的整合,把代码都写好放在VC宿主程序里,然后在lua里调用宿主程序的这些代码(或者叫接口、组件,随便你怎么叫),希望能用脚本来控制主程序的行为。这实际上也是一种把业务分离,用脚本控制的架构,可能有些人
    02-09
  • 用VC编译lua源码,生成lua语言的解释器和编译器
    用VC编译lua源码,生成lua语言的解释器和编译器1.去网址下载源码 http://www.lua.org/download.html 2.装一个VC++,我用的是VC6.0 3.接下来我们开始编译源码,我们需要编译:一个静态库一个动态库一个lua语言解释器一个lua编译器建立一个工静态库工程打开VC--
    02-09
  • vc++ & matlab 换行符号
    在C/C++语言中,可能我们要书写的一个字符串太长了,放在一行上影响代码的可读性。这时我们就需要多行书写了。字符串多行书写有两种规则:在字符串换行处加一个反斜杠’\’,下一行前不能有空格或者Tab键;使用双引号。 程序示例:/* *  Introduction: *
    02-09
点击排行