3
家电维修班,手机维修班,电脑维修班,电工班,焊工班,液晶电视维修班,电动工具维修班、电动车摩托车维修班、网络营销培训、网站设计培训、淘宝培训---全国招生 家电维修班,手机维修班,电脑维修班,电工班,焊工班,液晶电视维修班,电动工具维修班、电动车摩托车维修班、网络营销培训、网站设计培训、淘宝培训---全国招生
阳光电影院· 地区招生平台· 知识库 · 维修资料查询 ·您想咨询招生情况,请联系我啊·您想咨询招生情况,请联系我啊·
当前位置:湖南阳光电子技术学校文章资讯招生解答

湖南阳光电子技术学校 简介

学校地址:湖南省 长沙市 雨花区 车站南路红花坡路口
来校路线:长沙火车站售票厅后坪,乘135路公交车到“红花坡站”,即到.
学校电话:13807313137,13308461099
免费电话:13807313137
值班手机:(0)13807313137 杨老师
开课时间:我校常年面向全国招生,月月开班。每月1号,16号开学。
招生范围:凡年满15岁的公民,不限年龄,性别,地区。都欢迎来我校学习。
食宿问题:学校免费住宿,免费提供床上用品。食堂就餐,费用自理,约12元/天。
学校官网:www.hnygpx.net www.410014.com
专业介绍:欢迎查看我校专业课程!
收费标准:欢迎查看我校收费标准!
在线 Q Q:您想咨询招生情况,请联系我啊·您想咨询招生情况,请联系我啊
网上报名:欢迎您报读湖南阳光电子学校!


“变速齿轮”再研究

减小字体 增大字体 作者:佚名  来源:本站整理  发布时间:2010-07-19 02:03:02
提起“变速齿轮”(以下简称“齿轮”)这个软件,大家应该都知道吧,该软件号称
是全球第一款能改变游戏速度的程序。我起初用时觉得很神奇,久而久之就不禁思考其实现原理了,但苦于个人水平有限,始终不得其解,成了长驻于脑中挥散不去的大问号。

  偶然一天在BBS上看到了一篇名为《“变速齿轮”研究手记》(以下简称《手记》)的文章,我如获至宝,耐着性子把文章看完了,但之后还是有很多地方不解,不过还是有了比较模糊的认识:原来齿轮是通过截获游戏程序对时间相关函数的调用并修改返回结果实现的呀。

  为了彻彻底底地弄清齿轮的原理,我这次打算豁出去了。考虑到《手记》的作者从是研究的“齿轮”的反汇编代码的,那我也照样从反汇编代码开始。不过自认为汇编功底不够,又从图书馆借了几本关于Windows底层机制和386汇编的书,在经过差不多两周的“修行”之后,自我感觉有点好啦,哈哈,我也有点要迫不及待地把“齿轮”大卸八块了!

  在动手之前,我又把《手记》看了一遍,这次可就清楚多了:通过调用门跳到Ring0级代码段,修改各系统时间相关函数的前8个字节为jmp指令,转跳到“齿轮”映射到2G之上的代码,达到截获对各系统时间相关函数的调用的目的。但同时我的疑惑也更明确了:
   1.“齿轮”怎样建立指向自己映射到2G以上内存的代码的调用门描述符的;
   2.“齿轮”怎样将自己的代码映射到2G以上线性地址的;
   3.映射到2G之上的代码是怎样做到在代码基址更改的情况仍能正确运行的

  带着这样的疑问,我正式开始了对“齿轮”反汇编代码的分析。工具嘛,不用说当
然是Softice for Windows98、W32Dasm,OK,出发啦!

  我的“齿轮”版本是0.221 for win98和winme的,内含有两个文件(变速齿轮.exe
和Hook.dll)。先看看Hook.dll里面有些什么,用W32Dasm将Hook.dll反汇编,看看它的输出函数:

   __@@A">?ghWnd@@3PAUHWND__@@A
   ?gnHotKey1@@3KA
   ?gnHotKey2@@3KA
   ?gnHotKey3@@3KA
   ?gnHotKey4@@3KA
   ?nHook@@3HA
   __@@@Z">?SetHook@@YAHPAUHWND__@@@Z
   ?UnHook@@YAHXZ

  看函数名好象该dll只是安装钩子捕获变速热键的,与我的研究目的没太大的关系, 跳过去!
  再看看变速齿轮.exe的导入函数,timeGetTim、GetTickCount等时间相关的函数都
在里面。嘿,还有CreateFileMappingA和MapViewOfFileEx,看来“齿轮”是用这两个函
数创建映射文件的。以下列出几个关键的导入函数:

   Hook.?gnHotKey1@@3KA
   Hook.?gnHotKey2@@3KA
   Hook.?gnHotKey3@@3KA
   Hook.?gnHotKey4@@3KA
   __@@@Z">Hook.?SetHook@@YAHPAUHWND__@@@Z
   KERNEL32.CreateFileMappingA
   KERNEL32.GetModuleFileNameA
   KERNEL32.GetModuleHandleA
   KERNEL32.GetTickCount
   KERNEL32.MapViewOfFileEx
   KERNEL32.QueryPerformanceCounte
   USER32.KillTimer
   USER32.SendMessageA
   USER32.SetTimer
   WINMM.timeGetTime
   WINMM.timeSetEvent

  既然“齿轮”截获了timeGetTime,那我就跟踪timeGetTime函数的执行情况。

  我先写了个Win32 APP (以下简称APP),当左击客户区时会调用timeGetTime并将返回的结果输出至客户区。运行这个程序,打开“齿轮”,改变当前速度。

  Ctrl + D 呼出Softice,bpx timeGetTime ,退出,再左击APP客户区,Softice跳出。哈,果然timeGetTime函数的首指令成了jmp 8xxx 002A ,好F8继续执行,进入了“ 齿轮”映射到2G线性地址之上的代码。一路F8下去,发现接着“齿轮”把timeGetTime 首指令恢复,并再次调用timeGetTime,这样就得到了timeGetTime的正确结果,保存结果。“齿轮”再把timeGetTime首指令又改为jmp 8xxx 002A 。接下来都猜得到“齿轮”要干什么了!没错,将得到的返回值修改后返回至调用timeGetTime的程序APP。

  我仔细分析了一下,“齿轮”修改返回值的公式如下:
           倍数*(返回值-第一次调用timeGetTime的返回值)
修改后的返回值=---------------------------------------------------+上一次修改后的返回值
                          100000
  公式中“上次修改后的返回值”是自己猜测的未经证实,仅供参考。

  代码分析已经进行一部分了,可我之前的疑问仍未解决,“齿轮”是怎么将代码映
射的?又是怎么得到修改代码的权限的?

  既然“齿轮”中调用了CreateFileMappingA,我想其安装调用门,映射代码的初始化部分应该就在调用该函数代码的附近。好,沿着这个思路,呼出Softice,在CreateF ileMappingA处设置断点,将“齿轮”关闭后再运行。Softice跳出,停在了CreateFile MappingA处,F11回到“齿轮”的代码。看到了“齿轮”调用CreateFileMappingA的形式如下:

  CreateFileMappingA(FF,0,4,0,10000,0);
  可见“齿轮”创建了长度为0x10000的映射文件,继续,“齿轮”接着又调用MapViewOfFileEx,调用形式如下:
  MapViewOfFileEx(EDX,2,0,0,0,EAX);
  //EDX为CreateFileMappingA返回的映射文件句柄
  //EAX为申请映射代码的基址,第一次调用时EAX为0x8000 0000

  这里就是关键了,“齿轮”要将映射文件映射至基址为0x8000 0000 的内存空间中,可并不见得Windows就真的允许其映射呀?果然,“齿轮”在在调用之后判断返回值是否有效,无效则将上次申请的基址加上0x1000,再次调用MapViewOfFileEx,一直循环到成功为止,再将返回的地址保存。

  接下来“齿轮”将原“齿轮”exe中的截获API的代码逐字节拷贝到映射区域去。至此,“齿轮”已经将关键代码映射到2G以上线性地址中了。

  我再F8,哈哈,和熟悉的SGDT指令打了个照面。“齿轮”保存全局描述符表线性基 址,再用SLDT指令保存局部描述符表索引,计算出LDT基址。接着呢“齿轮”在局部描述表中创建了一个特权等级为0的代码段指向需要利用Ring0特权修改代码的“齿轮”自己的代码,并把局部描述表中索引为2的调用门指向的地址改为“齿轮”映射到高于2G的代码。

  然后“齿轮”依次调用各时间相关的API,保存其返回值留做计算返回时结果用。
“齿轮”又依次调用映射到高于2G的代码修改各API的首指令。到了这里,“齿轮”的初始化部分就结束了,只等着还蒙在鼓里的游戏上钩啦,哈哈!

  结束代码只不过是作些恢复工作罢了,仅仅是初始化代码的逆过程,所以就不再赘述(其实是我自己懒得看了,^_^!).
  至此,我对“齿轮”的加速原理已有大致的了解,深刻感受到“齿轮”代码的精巧, 所以觉得有必要将"齿轮"中所运用到的一些技巧作一个总结:

1.基址无关代码的编写
  姑且以上面一句话作标题,^_^。看了“齿轮”的初始化代码,知道其映射代码的基址差不多是随机的,那么“齿轮”是怎么保证映射后的代码能正常运行的呢?如果 代码是完全顺序执行的倒没什么问题,但如果要调用自己映射代码中的子程序呢?呵呵,就只有运行时计算出子程序的入口地址并调用了,不过还是要先得到映射代码所在的地址才行。“齿轮”简单地用两条指令就得到当前正在执行的指令的地址,具体如下(地址为假设的):

  0:0  call 5
  0:5  pop esi

  现在esi中的值就是5了,哈哈!

  这里的call用的是近调用,整条指令为E800000000,即为调用下一条指令.所进行的操作只不过是把下一条指令的地址入栈而已.再pop将返回地址(即pop指令本身的地址)取出.

2.修改调用门,生成jmp指令,修改代码
  这些都是高度依赖于CPU的操作,技巧性也很强,主要是钻了操作系统的漏洞。比如“齿轮”就是用SGDT,SLDT获得全局和局部描述符表基址来安装调用门,通过访问调用门来获取RING0权限作一些平时不为系统所允许的操作;而CIH病毒是用SIDT获得中断描述符表基址安装中断门然后出发软中断获取RING0权限的,原理都是一样的。这些在水木上讨论过很多遍,大家都很熟悉,所以也就不敢班门弄斧,写到此为止。

3.64K代码编写
  由调用CreateFileMappingA函数参数可知“齿轮”只映射10000(64K)大小的区域,所以其映射在2G之上的代码和数据决不能大于64K。我想作者之所以选择64K为映射区域的大小,可能是与调用子程序或数据时容易计算地址有关。在映射代码的任意一处得到当前指令地址之后将其低16位置0即可得到映射代码的基地址,再加上子程序入口或数据的偏移即可求得其绝对地址。
 
我的评论:

  一句话:佩服“齿轮”的作者王荣先生。

  “齿轮”的代码表现他对windows运行机制的深刻理解以及深厚的汇编功底还有丰富的想象力。对我来说“齿轮”仿佛就是一件精美的艺术品,每个细处都很值得玩味一 番,所以我才在看过“齿轮”代码之后有了把我的分析过程用笔写下来的冲动。但同时 我又不得不承认“齿轮”的功能的实现是依靠其高度技巧化的代码实现的,换句话说就 是这种的方法局限性实在是太大了。不就是截获API嘛,用的着这么麻烦吗?

  为了证实自己的想法,我在Codeguru上直接找了个HOOK API 的代码,该代码是通过安装WH_CBT类型全局钩子在所有被插入DLL的进程中修改进程PE映像的输入节达到截获API的(这种方法在《windows核心编程》中有详细说明)。把代码稍做修改,就能工作了(在星际争霸下试过,可以改变游戏速度)。尽管只在98下试过,但我觉得肯定也能在2000下用,因为代码中只用了一两句汇编指令,而且整个程序都是在RING3下运行的,没有作出什么出轨的举动。当然这种方法也有缺点,就是对用Loadlibrary加载WINMM.dll再用GetProcAddress获取timeGetTime地址的API调用不起作用(原因在《windows核心编程》中有说明)。

  我打算在将测试用程序稍稍完善后再公布源代码,届时欢迎大家下载。
 
我的感谢:
  在我彻底弄清“齿轮”的代码之后,已经是第三天的上午了,无奈自己才疏学浅,全不像《手记》的作者只花了一个晚上就弄清楚,我可是花了一个上午、两个下午、两个晚上才结束了战斗,实在是惭愧呀。

  自己之所以能自得其乐地坚持了两天多,是与寝室兄弟小强的支持分不开的。穷 困潦倒的我在这几天不知道总共抽了他多少支烟,无以为报,只有在这里说一声谢谢了!另外还要感谢sunlie非常地阅读本文,指出了原文中的错误并提出了非常宝贵的意见!

   后要说的就是个人水平有限,文中难免出现错误,欢迎大家讨论!^_^

附A:
  使用工具:Softice for Windows98,W32Dasm,VisualC++ 6.0
  操作系统:Window98 2nd
  分析目标:变速齿轮 for 98me 版本:0.221
  参考书籍或文章:
    80x86汇编语言程序设计教程   杨季文等编著  清华大学出版社
    windows剖析--初始化篇及内核篇        清华大学出版社
    虚拟设备驱动程序开发
    intel 32位系统软件编程
    80x86指令参考手册
    《“变速齿轮”研究手记》

附B:
  “齿轮”关键代码完全注释
  一、初始化部分(从"齿轮"调用CreateFileMappingA函数开始分析)
          0167:00401B0E PUSH   00
          0167:00401B10 PUSH   00010000
          0167:00401B15 PUSH   00
          0167:00401B17 PUSH   04
          0167:00401B19 PUSH   00
          0167:00401B1B PUSH   FF
          0167:00401B1D CALL   [KERNEL32!CreateFileMappingA]
  ;调用CreateFileMappingA
  ;调用形式如右:CreateFileMappingA(FF,0,4,0,10000,0)
          0167:00401B23 MOV    ECX,[EBP-30]
          0167:00401B26 MOV    [ECX+00000368],EAX
          0167:00401B2C MOV    DWORD PTR [EBP-14],80000000
          0167:00401B33 JMP    00401B41
          0167:00401B35 MOV    EDX,[EBP-14]
          0167:00401B38 ADD    EDX,00010000
  ;申请基址加0x10000
          0167:00401B3E MOV    [EBP-14],EDX
          0167:00401B41 MOV    EAX,[EBP-14]
          0167:00401B44 PUSH   EAX   ;映射文件基址
          0167:00401B45 PUSH   00    ;映射的字节数
          0167:00401B47 PUSH   00    ;文件偏移低32位
          0167:00401B49 PUSH   00    ;文件偏移高32位
          0167:00401B4B PUSH   02    ;访问模式
          0167:00401B4D MOV    ECX,[EBP-30]
          0167:00401B50 MOV    EDX,[ECX+00000368]
          0167:00401B56 PUSH   EDX
  ;CreateFileMappingA返回的映射文件句柄
          0167:00401B57 CALL   [KERNEL32!MapViewOfFileEx]
  ; 调用形式如右:MapViewOfFileEx(EDX,2,0,0,0,EAX)
          0167:00401B5D MOV    ECX,[EBP-30]
  ;[EBP-30]为即将映射到2G之上
          0167:00401B60 MOV    [ECX+0000036C],EAX
  ; 的代码的数据域的起始地址
          0167:00401B66 MOV    EDX,[EBP-30]
          0167:00401B69 CMP    DWORD PTR [EDX+0000036C],00
  ;检查MapViewOfFileEx
          0167:00401B70 JZ     00401B74
         ;返回值,若为0则继续调
          0167:00401B72 JMP    00401B76  ;调用MapViewOfFileEx
          0167:00401B74 JMP    00401B35  ;直至成功为止
          0167:00401B76 MOV    EAX,[EBP-30]
          0167:00401B79 MOV    ECX,[EAX+0000036C]
          0167:00401B7F MOV    [EBP-08],ECX
  ;映射文件起始地址存入[EBP-08]
          0167:00401B82 CALL   [WINMM!timeGetTime]
          0167:00401B88 MOV    [EBP-14],EAX
  ;将初次调用timeGetTime
         0167:00401BA0 MOV    ECX,[EBP-08]
  ;的返回值保存到[EBP-14]
         0167:00401BA3 MOV    EDX,[EBP-14]
  ;以及映射文件基址+FF30处
         0167:00401BA6 MOV    [ECX+0000FF30],EDX
 ...省略的代码类似的保存调用初次GetTickCount,QueryPerformanceCounter的返回值
 
         0167:00401BED MOV    DWORD PTR [EBP-14],00000000
         0167:00401BF4 MOV    EDX,[EBP-30]
         0167:00401BF7 MOV    EAX,[EDX+0000036C]
         0167:00401BFD MOV    ECX,[EBP-14]
         0167:00401C00 MOV    BYTE PTR [ECX+EAX+0000F000],9A
  ;9a为远调用的指令码
         0167:00401C08 MOV    EDX,[EBP-14]
         0167:00401C0B ADD    EDX,01
         0167:00401C0E MOV    [EBP-14],EDX
         0167:00401C11 MOV    EAX,[EBP-14]
         0167:00401C14 ADD    EAX,04
         0167:00401C17 MOV    [EBP-14],EAX
         0167:00401C1A MOV    ECX,[EBP-30]
         0167:00401C1D MOV    EDX,[ECX+0000036C]
         0167:00401C23 MOV    EAX,[EBP-14]
         0167:00401C26 MOV    BYTE PTR [EAX+EDX+0000F000],14
  ;14为调用门描述符的索引
         0167:00401C2E MOV    ECX,[EBP-14]
         0167:00401C31 ADD    ECX,01
         0167:00401C34 MOV    [EBP-14],ECX
         0167:00401C37 MOV    EDX,[EBP-30]
         0167:00401C3A MOV    EAX,[EDX+0000036C]
         0167:00401C40 MOV    ECX,[EBP-14]
         0167:00401C43 MOV    BYTE PTR [ECX+EAX+0000F000],00
  ;CALL指令其他部分
         0167:00401C4B MOV    EDX,[EBP-14]
         0167:00401C4E ADD    EDX,01
         0167:00401C51 MOV    [EBP-14],EDX
         0167:00401C54 MOV    EAX,[EBP-30]
         0167:00401C57 MOV    ECX,[EAX+0000036C]
         0167:00401C5D MOV    EDX,[EBP-14]
         0167:00401C60 MOV    BYTE PTR [EDX+ECX+0000F000],C2
         0167:00401C68 MOV    EAX,[EBP-14]
         0167:00401C6B ADD    EAX,01
         0167:00401C6E MOV    [EBP-14],EAX
         0167:00401C71 MOV    ECX,[EBP-30]
         0167:00401C74 MOV    EDX,[ECX+0000036C]
         0167:00401C7A MOV    EAX,[EBP-14]
         0167:00401C7D MOV    BYTE PTR [EAX+EDX+0000F000],00
         0167:00401C85 MOV    ECX,[EBP-14]
         0167:00401C88 ADD    ECX,01
         0167:00401C8B MOV    [EBP-14],ECX
         0167:00401C8E MOV    EDX,[EBP-30]
         0167:00401C91 MOV    EAX,[EDX+0000036C]
         0167:00401C97 MOV    ECX,[EBP-14]
         0167:00401C9A MOV    BYTE PTR [ECX+EAX+0000F000],00
         0167:00401CA2 MOV    EDX,[EBP-14]
  ;以上代码为在映射代码偏移F000处写入指令CALL 0014:0000
         0167:00401CA5 ADD    EDX,01
  ;指令 A91400C20000共6个字节
         0167:00401CA8 MOV    [EBP-14],EDX ;
         0167:00401CAB MOV    ESI,0040213B
  ;要复制的代码的起始地址
         0167:00401CB0 MOV    EDI,[EBP-08]
  ;要复制代码的目标地址(映射区域中)
         0167:00401CB3 MOV    ECX,00402688
  ;402688为要复制的代码的末地址
         0167:00401CB8 SUB    ECX,ESI
         0167:00401CBA REPZ   MOVSB ;将代码全部复制到映射区域
         0167:00401CBC SGDT   FWORD PTR [EBP-1C] ;这句开始就很关键了
         0167:00401CC0 LEA    EAX,[EBP-001C]
         0167:00401CC6 MOV    EAX,[EAX+02]    ;取GDT线性基址
         0167:00401CC9 XOR    EBX,EBX
         0167:00401CCB SLDT   BX         ;取LDT在GDT中的偏移
         0167:00401CCE AND    BX,-08
         0167:00401CD2 ADD    EAX,EBX
         0167:00401CD4 MOV    ECX,[EAX+02]
         0167:00401CD7 SHL    ECX,08
         0167:00401CDA MOV    CL,[EAX+07]
         0167:00401CDD ROR    ECX,08       ;以上计算出LDT线性基址
         0167:00401CE0 MOV    [EBP-0C],ECX    ;保存
         0167:00401CE3 MOV    EAX,[EBP-30]
         0167:00401CE6 MOV    ECX,[EBP-0C]
         0167:00401CE9 MOV    [EAX+00000370],ECX
         0167:00401CEF MOV    EDX,[EBP-30]
         0167:00401CF2 MOV    EAX,[EDX+0000036C]
         0167:00401CF8 MOV    ECX,[EBP-0C]
         0167:00401CFB MOV    [EAX+0000FE00],ECX
  ;将LDT线性基址保存至映射代码中
         0167:00401D01 MOV    AX,CS
  ;得到当前代码段描述符号
         0167:00401D04 AND    AX,FFF8
         0167:00401D08 MOV    [EBP-10],AX
         0167:00401D0C MOV    EDX,[EBP-10]
         0167:00401D0F AND    EDX,0000FFFF
  ;EDX为代码段描述符在LDT中的偏移量
         0167:00401D15 MOV    EAX,[EBP-30]
         0167:00401D18 MOV  ECX,[EAX+00000370] ;ECX此时为LDT线性基址
         0167:00401D1E MOV    EAX,[EBP-30]
         0167:00401D21 MOV   EAX,[EAX+00000370] 
;EAX此时为LDT线性基址     

         0167:00401D27 MOV    ESI,[EDX+ECX]
         0167:00401D2A MOV    [EAX+08],ESI
         0167:00401D2D MOV    ECX,[EDX+ECX+04]
  ;以上将当前代码段描述符复制到
         0167:00401D31 MOV    [EAX+0C],ECX  ;LDT第1项
         0167:00401D34 MOV    EDX,[EBP-30]
         0167:00401D37 MOV    EAX,[EDX+00000370]
         0167:00401D3D MOV    CL,[EAX+0D]
         0167:00401D40 AND    CL,9F
         0167:00401D43 MOV    EDX,[EBP-30]
         0167:00401D46 MOV    EAX,[EDX+00000370]
         0167:00401D4C MOV    [EAX+0D],CL
  ;以上修改LDT第1项的DPL为0,则当由调用门转到该段代码时即获得RING0权限
         0167:00401D4F MOV    EAX,[EBP-0C]
         0167:00401D52 ADD    EAX,10    ;获得LDT中索引为2的调用门地址
         0167:00401D55 MOV    EBX,0040213B
         0167:00401D5A MOV    [EAX],EBX
         0167:00401D5C MOV    [EAX+04],EBX
         0167:00401D5F MOV    WORD PTR [EAX+02],000C
         0167:00401D65 MOV    WORD PTR [EAX+04],EC00 ;调用门修改完毕
         0167:00401D6B MOV    ECX,[EBP-08]
         0167:00401D6E MOV    EDX,[WINMM!timeGetTime]
         0167:00401D74 MOV    [ECX+0000FEE0]

;EDX;保存timeGetTime入口地址
    ...省略部分依次保存GetTickCount,GetMessageTime,timeSetEvent,SetTimer,
       timeGetSystemTime,QueryPerformanceCounter入口地址
         0167:00401DD2 MOV    ECX,[EBP-08]
         0167:00401DD5 MOV    EAX,[WINMM!timeGetTime]
         0167:00401DDA MOV    EBX,[EAX]
         0167:00401DDC MOV    [ECX+0000FE40],EBX
         0167:00401DE2 MOV    EBX,[EAX+04]
         0167:00401DE5 MOV    [ECX+0000FE44],EBX
                  ;保存timeGetTime函数前8个字节指令
   ...省略部分依次保存GetTickCount,GetMessageTime,timeSetEvent,timeGetSystemTime , QueryPerformanceCounter前8个字节指令
         0167:00401E6D MOV    BYTE PTR [ECX+0000FE90],E9
         0167:00401E74 MOV    EAX,00402165
         0167:00401E79 SUB    EAX,0040213B
       ;EAX为截获代码在映射代码中的偏移
         0167:00401E7E ADD    EAX,ECX  ;计算出截获代码的线性入口地址
         0167:00401E80 SUB    EAX,[WINMM!timeGetTime]
         0167:00401E86 SUB    EAX,05   ;JMP指令总长5个字节
         0167:00401E89 MOV    [ECX+0000FE91],EAX
       ;计算生成从timeGetTime跳到截获代码的JMP指令并保存
 
    ...省略部分依次计算并生成GetTickCount,GetMessageTime,timeSetEvent,    timeGetSystemTime , QueryPerformanceCounter跳到截获代码的JMP指令并保存
 
         0167:00401F58 CLI  ;关闭中断,谨防修改代码时发生意外
         0167:00401F59 MOV    EAX,004021F3     ;
         0167:00401F5E SUB    EAX,0040213B;计算子程序在映射代码中的偏移
         0167:00401F63 ADD    EAX,[EBP-08]     ;EAX=8xxx 00B8
         0167:00401F66 PUSH   EAX  ;传入参数EAX为修改timeGetTime代码的
                              ;子程序入口地址
         0167:00401F67 MOV    EAX,[EBP-08]     ;调用8xxx 0000
         0167:00401F6A CALL   EAX    ;返回时timeGetTime首指令被更改
 
      ...省略部分依次修改GetTickCount,GetMessageTime,timeSetEvent,
       timeGetSystemTime , QueryPerformanceCounter函数的首指令
 
         0167:00401FF  SETI   ;设置中断,初始化代码结束
  二、截获时间函数部分(以timeGetTime为例子,代码以跟踪顺序列出)
      timeGetTime
             JMP 832A 002A
      ;这是timeGetTime被修改后的首指令
         0167:832A 002A     CLI
      ;此时[esp]=40BF2C,即游戏程序中调用timeGetTime函数的下一条指令
      ...(6个)各寄存器分别入栈 且MOV EBP,ESP
         0167:832A 0033     CALL  832A 0038
      ;将当前EIP入栈(即下一条指令的地址)
         0167:832A 0038     POP  EDI    ;取出当前指令地址
                     XOR  DI  , DI
                     MOV  ESI , EDI
     ;将64K内存首地址赋给ESI
     ;此时ESI=EDI=832A 0000
                     ADD  ESI , 0040 2102
                     SUB  ESI , 0040 213B ;求出映射代码首地址
                     PUSH  ESI
         0167:832A 004B     CALL  EDI    ;ESI为传进的参数
                      ;返回时已经将timeGetTime代码还原
         0167:832A 004D     CALL  832A 0052  ;
         0167:832A 0052     POP  EDI
                     XOR  DI ,DI    ;故技重施
                     CALL  [EDI + 0000FEED];调用原timeGetTime函数
                     SUB  EAX,[EDI + 0000 FF30]
     ;减去第一次调用timeGetTime的结果
                     MUL  DWORD PTR [EDI+0000 FE30]
     ;乘以用户所指定的倍数
                     MOV  EBX ,00100000
                     DIV  EBX
     ;除以常数100000
                     ADD  EAX ,[EDI+ 0000FE20]
                     MOV  EAX,004021F3
                     SUB  EAX,0040213B
                     ADD  EAX,EDI
     ;以上指令为修改timeGetTime函数返回值
                     PUSH  EAX
     ;EAX为传进的参数
                     CALL  EDI
     ;返回时又将timeGetTime首指令换成JMP
     ...恢复各寄存器的值,EAX中为修改后的返回值
                     RET ;此时[ESP]=40BF2C,执行RET将返回到游戏中去
     ;
         0167:832A 0000     CALL  832A 0005
         0167:832A 0005     POP  EDI
                     XOR  DI ,DI      ;老套了撒^_^
                     MOV  ESI ,[EDI+0000 FE00]
     ;此地址保存着LDT的线性基址
                     MOV  EAX,[ESP+04]
                     MOV  [ESI +10],AX
                     SHR  EAX,10
                     MOV  [ESI+16],AX
     ;以上代码将LDT中索引为2的调用门描述符的偏移改为传入的参数
     ...
                     MOV  EAX,0000 0F00
                     CALL  EAX
     ;调用子程序修改timeGetTime代码
     0167:832A 0027        , ; RET  4
     ;弹出参数,返回
     ;
         0167:832A F000     CALL  0014:00000000
                     RET  0
     ;
         000C:832A 0097     CALL  832A 009C
         000C:832A 009C     POP  EDI
                     MOV  EAX,[EDI+0000 FE40]
                     MOV  EBX,[EDI+0000 FEE0]
                     MOV  [EBX],EAX
                     MOV  EAX,[EDI+0000 FE44]
                     MOV  [EBX+04],EAX
                     RETF
     注:EDI+0000 FE40起前8个字节为原timeGetTime函数的指令
       EDI+0000 FEE0保存着timeGetTime函数的入口地址
       以上即恢复timeGetTime前8个字节的代码
     ;
         000C:832A 00B8     CALL  832A 00BD
         000C:832A 00BD     POP  EDI
                     XOR  DI ,DI
     ...
                     MOV  EAX,[EDI+0000 FE90]
                     MOV  EBX,[EDI+0000 FEE0]
                     MOV  [EBX],EAX
                     MOV  EAX,[EDI+0000FE94]
                     MOV  [EBX+04],EAX
                     RETF

     注:EDI+0000 FE90 起前8个字节保存着JMP 832A 002A 指令
       是由“齿轮”初始化部分代码计算出来的,以上代码将JMP 832A 002A
       写入timeGetTime函数
  湖南省阳光电子技术学校常年开设:手机维修培训、家电维修培训、电工培训、电脑维修培训、焊工培训--面向全国火爆招生!网址:http://www.hnygpx.com 报名电话:13807313137)。安置就业。考试合格颁发全国通用权威证书。采用我校多年来独创的“模块教学法”,理论与实践相结合、原理+图纸+机器三位一体的教学模式,半天理论,半天实践,通俗易懂,确保无任何基础者也能全面掌握维修技能、成为同行业中的佼佼者。工作(一期不会,免费学会为止)。

Tags:“变速齿轮”再研究

作者:“变速齿轮”再研究
以上是“变速齿轮”再研究详细信息,如果您想进一步的了解“变速齿轮”再研究的其他相关等,请参考下方我们为您提供的有关“变速齿轮”再研究的相关资讯,让您获取更多的“变速齿轮”再研究信息。以上“变速齿轮”再研究信息,由湖南阳光电子技术学校提供。

文章评论评论内容只代表网友观点,与本站立场无关!

   评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论

相关文章

网页说明

    “变速齿轮”再研究这条信息,为您提供了中国 新 全的“变速齿轮”再研究信息,欢迎您免费查看“变速齿轮”再研究信息。如果您对“变速齿轮”再研究这条消息有好的意见建议请联系我们,让我们一起进步!
  • 热门关键词:创维(766)  CPU(363)  维修(198)  彩电(188)  机芯(144)  故障(143)  招生(110)  职业中专(105)  技工学校(104)  电脑(98)  开县(91)  学校(88)  手机(87)  BIOS(68)  首选(65)  培训(65)  Windows(63)  笔记本(60)  云阳(58)  Vista(52)  学主板维修哪里 好(50)  学电脑硬件哪里 好(50)  检修(50)  Linux(46)  win7(43)  MTK(43)  海尔(42)  TCL(42)  XP(40)  东芝(40)  主板(39)  三极管(38)  模式(37)  三星(36)  硬盘(36)  液晶电视(35)  工厂(33)  技术(32)  CRT(31)  北京(30)