当前位置:  -> 首页 -> 屏幕取词程序实现方案

上一篇 | 下一篇
屏幕取词程序实现方案
作者:洛羽叶  点击率:9842  发布时间:2012-05-09

 

概述:在Windows操作系统下,窗口上的字都是由TextOutA、TextOutW、ExtTextOutA、ExtTextOutw四个Api函数输出的,这四个函数都存在于Windows的核心库Gdi32.dll中。

由此看来,我们要实现屏幕取词就要从这四个函数入手,首先得到鼠标下方的程序名称,然后使鼠标下方区域的屏幕实效,此时系统就会调用这四个函数进行重画,我们就可以截获函数的参数,得到失效区域的文字,将文字进行处理。

在此过程中用导的主要函数:

得到窗口名:WindowFromPoint、ScreenToClient、InvalidateRect。

截获Api函数:SetWindowsHookEx、UnhookWindowsHookEx、CallNextHookEx、VirtualQuery、GetProcAddress、VirtualProtect。

下面我们将对实现的重要环节及相关知识做进一步的讲解:

  1. 由鼠标位置得到窗口名

由鼠标位置得到鼠标下方窗口的句柄可使用Api函数WindowFromPoint来实现:

HWND HWnd;           //定义句柄变量

POINT MousePoint;       //定义存放鼠标位置的变量

//由鼠标回调函数的参数得到鼠标位置

HWnd=WindowFormPoint(MousePoint);//由鼠标位置得到窗口的句柄

  1. 调用系统Api进行文字输出

在Windows系统中,所有的窗口都是通过一些系统函数画出来的,文字也是。在窗口区域无效时,系统就要调用相应函数进行重画。我们可以借助于Api函数使鼠标所在区域无效,系统将重新调用输出文字函数进行文字的再次输出。

InvalidateRect(hwnd,&rect,FALSE); //调用函数InvalidateRect使rect包含的矩形区域无效

  1. 鼠标钩子的应用

钩子是Windows系统消息处理机制的一个要点,主要分鼠标钩子、键盘钩子、外壳钩子、日志钩子等几种,通过在子例程里设置钩子函数,用来监视和截获Windows消息,从而在消息到达目的以前进行截获根据用户的要求做出相应的处理。

鼠标钩子用来截获鼠标消息,键盘钩子用来截获键盘消息,外壳钩子用来截获启动和关闭应用程序的消息,日志钩子用来监视和记录输入事件。

钩子也可分为线程专用钩子和全局钩子两种,线程专用钩子用来监视当前线程的消息,全局钩子用来监视系统全部线程的消息。要监视所有线程的消息,钩子函数必须被包含在动态连接库(Dll)中,这样才能被所有的应用程序所调用。

Windows提供Api函数SetWindowsHookEx来建立一个Hook,通过这个函数可以将一个程序放入Hook链中来监视系统的消息。

SetwindowsHookEx (idHook:Integer,       //钩子类型

                  lpfn:TFNHookProc,   //回调函数

                  hmod: HINST,       //包含钩子函数的动态库地址

                  dwThreadId:DWORD   //钩子的监控线程

                 )

HHOOK hMouseHook;  //定义鼠标钩子变量

hMouseHook =SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseProc, \

GetModuleHandle("hookdll.dll"),0); //设置鼠标钩子

  1. 替换系统Api函数

1.仿照系统Api函数TextOutA、TextOutW、ExtTextOutA、ExtTextOutW做四个自定义的函数MyTextOutA、MyTextOutW、MyExtTextOutA、MyExtTextOutW,和鼠标钩子放在同一个动态连接库中,参数和原函数保持一致。

//仿照TextOutA定义自己的函数

BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,

                         int cbString)

//仿照TextOutW定义自己的函数

BOOL WINAPI MyTextOutW(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,

                          int cbString)

//仿照ExtTextOutA定义自己的函数

BOOL WINAPI MyExtTextOutA(HDC hdc, int nXStart, int nYStart, UINT fuOptions,

                            const RECT FAR *lprc, LPCSTR lpszString,

                            UINT cbString,int FAR *lpDx)

//仿照ExtTextOutW定义自己的函数

BOOL WINAPI MyExtTextOutW(HDC hdc, int nXStart, int nYStart, UINT fuOptions,

                            const RECT FAR *lprc, LPCSTR lpszString,

                            UINT cbString,int FAR *lpDx)

2.当包含函数的动态连接库注入到其他进程时,寻找映射该进程的虚拟内存地址中各模块的基地址。

EXE和DLL被映射到虚拟内存空间的什么地方是由它们的基地址决定的。它们的基地址是在链接时由链接器决定的。当你新建一个Win32工程时,VC++链接器使用缺省的基地址0x00400000。你如果跟踪进WinMain的时候,hInstance值总是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)                      //是EXE或DLL的映射

        …

        //进行地址转换,将原Api函数地址传给自定义函数

        …

        base=(DWORD)mbi.BaseAddress+mbi.RegionSize; //继续

    }

3.得到基地址后,根据PE文件的格式穷举这个模块的IMAGE_IMPORT_DESCRIPTOR数组,

看是否引入了Gdi32.Dll,如是,穷举IMAGE_THUNK_DATA数组,看是否引入了

TextOuA、TextOutW、ExtTextOutA、ExtTextOutW等4个函数,如果找到其中之一,将其替换为相应的自己的函数。

 

标签: API
引用地址:本站原创
   站点首页      技术人生      旅途足迹      我要留言      友情链接      关于站长   
[本站统计]
在线人数:0
今日访问:227
总访问量:1475098
Copyright 2006-2022 EasyWeb 1.6 订阅 All Rights Reserved
粤ICP备08028977号-1
www.luoriver.com
Created by WWH in 2006