我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Windows 9x屏幕取词的实现方法是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Windows 9x屏幕取词的实现方法是什么

Windows 9x屏幕取词的实现方法是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

有关屏幕取词

"鼠标屏幕取词"技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在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中被引入的每个函数的信息,找到我们需要截获的函数的跳转地址,然后改成我们自己的函数的地址……

废话不说了,下面提供一个屏幕取词具体的实现方法。

1. 安装鼠标钩子,通过钩子函数获得鼠标消息。

使用到的api函数:setwindowshookex

2. 得到鼠标的当前位置,向鼠标下的窗口发重画消息,让它调用系统函数重画窗口。

使用到的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;    // 这个函数监测当前系统是否是windownt   bool 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 _debug    if (paorigfuncs) _assert(!isbadwriteptr(paorigfuncs, sizeof(proc)));    _assert(pahookfunc.szfunc);   _assert(*pahookfunc.szfunc != \0 );    _assert(!isbadcodeptr(pahookfunc.pproc));    #endif    if ((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].virtualaddress == 0)   return null;   // 得到引入段(即 .idata section)的指针   pimage_import_descriptor pimportdesc = makeptr(pimage_import_descriptor, pdosheader,   pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress);   // 穷举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);   }

关于Windows 9x屏幕取词的实现方法是什么问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Windows 9x屏幕取词的实现方法是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Windows 9x屏幕取词的实现方法是什么

Windows 9x屏幕取词的实现方法是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。有关屏幕取词"鼠标屏幕取词"技术是在电子字典中得到广泛地应用的,如四通利方和金山词
2023-06-17

Win10屏幕截图的方法是什么

这篇文章主要介绍“Win10屏幕截图的方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Win10屏幕截图的方法是什么”文章能帮助大家解决问题。操作如下:1.点击Win10的开始菜单,在搜索里
2023-06-27

Android判断屏幕是横屏或是竖屏的简单实现方法

本文所述为一个Android的常用技巧代码,主要用于判断手机屏幕是横向或是竖向的,在判断屏幕水平或垂直后你可以对程序做出相应的响应,该实例代码只是判断是否为竖屏,若判断正确返回true,否则返回false。 具体的程序代码如下:packag
2022-06-06

ubuntu双屏幕设置的方法是什么

在Ubuntu上设置双屏幕可以通过以下步骤进行:1. 连接第二个显示器到计算机上。2. 打开“Settings”(设置)应用程序。3. 在左侧导航栏中选择“Devices”(设备)选项卡。4. 点击“Displays”(显示器)选项卡。5.
2023-09-09

android获取屏幕高度和宽度的实现方法

本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
android获取屏幕高度和宽度的实现方法
2022-06-06

Windows7切换屏幕的快捷方法是什么

在桌面上空白处右键一下,看看出现了什么?点屏幕分辨率:      屏幕分辨率   现在可以设www.cppcns.com置了:     对于电脑上接着双屏的朋友,现在 Windpythonows 7 中切换编程客栈屏幕也很简便www.cpp
2023-05-25

Flex全屏的实现方法是什么

今天就跟大家聊聊有关Flex全屏的实现方法是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Flex全屏方法介绍最近在做工作流的流程设计工具相关的内容,web应用使用了比较标准的分
2023-06-17

python打印日志到屏幕的方法是什么

在Python中,可以使用内置的logging模块来打印日志到屏幕。以下是一个简单的示例:import logging# 设置日志级别为DEBUGlogging.basicConfig(level=logging.DEBUG)# 打印不
python打印日志到屏幕的方法是什么
2024-03-12

屏幕录制的方法是什么_win7怎么进行屏幕录制图文教程

win7怎么进行屏幕录制图文教程:1、电脑左下角位置点击开始菜单其中选择运行,打开快捷方式为win+r组合键,如下图所示;2、打开运行窗口,在窗口中输入cmd并进入管理员界面;
2023-06-05

Linux系统调整屏幕亮度的方法是什么

今天就跟大家聊聊有关Linux系统调整屏幕亮度的方法是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。对于程序员来说熬夜就是是经常的使用,那么Linux系统如何如何调整屏幕亮度呢?
2023-06-28

Java捕获当前屏幕图像的方法是什么

这篇文章主要讲解了“Java捕获当前屏幕图像的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java捕获当前屏幕图像的方法是什么”吧!import java.awt.*; impo
2023-06-03

Android应用中实现截取手机屏幕的方法有哪些

这篇文章给大家介绍Android应用中实现截取手机屏幕的方法有哪些,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。方法1:首先想到的思路是利用SDK提供的View.getDrawingCache()方法: public
2023-05-31

Win10设置电脑屏幕常亮的方法是什么

这篇文章主要介绍“Win10设置电脑屏幕常亮的方法是什么”,在日常操作中,相信很多人在Win10设置电脑屏幕常亮的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Win10设置电脑屏幕常亮的方法是什么
2023-06-27

Android判断NavigationBar是否显示的方法(获取屏幕真实的高度)

有些时候,我们需要知道当前手机上是否显示了NavigationBar,也就是屏幕底部的虚拟按键。 比如截屏的时候,要获取屏幕的高度,必须包括NavigationBar的高度。 试过网上的多种方法,但是对那种可以通过手势来显示/隐藏的Navi
2022-06-06

Android中通过view方式获取当前Activity的屏幕截图实现方法

此方法是通过view的方式获取当前activity的屏幕截图,并不是framebuffer的方式,所以有一定的局限性。但是这种方法相对简单,容易理解。 首先通过下面的函数获取Bitmap格式的屏幕截图:代码如下: public Bitmap
2022-06-06

调节苹果Mac电脑屏幕亮度的方法是什么

这篇文章给大家介绍调节苹果Mac电脑屏幕亮度的方法是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。具体方法:1、其实仔细看苹果Mac笔记本键盘,或者iMac附带的苹果蓝牙键盘您应该能够找到答案,F1上面标有亮度的小
2023-06-05

实现Flex全屏的三大方法分别是什么

实现Flex全屏的三大方法分别是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Flex全屏方法介绍最近在做工作流的流程设计工具相关的内容,web应用使用了比较标准的分布
2023-06-17

windows系统蓝屏代码及相应的解决方法是什么

本篇文章为大家展示了windows系统蓝屏代码及相应的解决方法是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。  偶尔我们会碰到电脑出现蓝屏代码的情形,蓝屏代码的情形有多种,不同的蓝屏代码,相应
2023-06-04

java实现webservice的方法是什么

Java实现WebService的方法主要有以下几种:1. 使用JAX-WS(Java API for XML Web Services):JAX-WS是Java EE中的一部分,它提供了一种简单的方式来创建和部署基于SOAP(Simple
2023-09-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录