变速齿轮研究

ce中文带变速版 ce中文带变速版本,纯净版,解压即可使用。无病毒木马,可用于修改进程的内存,反汇编等。是个不错的游戏修改器,也算个黑客工具。 立即下载
    提起“变速齿轮”(以下简称“齿轮”)这个软件,大家应该都知道吧,该软件号称
是全球第一款能改变游戏速度的程序。我起初用时觉得很神奇,久而久之就不禁思考其实现原理了,但苦于个人水平有限,始终不得其解,成了长驻于脑中挥散不去的大问号。
   偶然一天在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反汇编,看看它的输出函数:
     ?ghwnd@@3pauhwnd__@@a
     ?gnhotkey1@@3ka
     ?gnhotkey2@@3ka
     ?gnhotkey3@@3ka
     ?gnhotkey4@@3ka
     ?nhook@@3ha
     ?sethook@@yahpauhwnd__@@@z
     ?unhook@@yahxz
    看函数名好象该dll只是安装钩子捕获变速热键的,与我的研究目的没太大的关系, 跳过去!
    再看看变速齿轮.exe的导入函数,timegettim、gettickcount等时间相关的函数都
在里面。嘿,还有createfilemappinga和mapviewoffileex,看来“齿轮”是用这两个函
数创建映射文件的。以下列出几个关键的导入函数:
     hook.?gnhotkey1@@3ka
     hook.?gnhotkey2@@3ka
     hook.?gnhotkey3@@3ka
     hook.?gnhotkey4@@3ka
     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函数

变速齿轮(可以加快也可以减慢程序速度) 变速齿轮是一个神奇的改变游戏速度的程序(可以加快也可以减慢游戏速度)。所以如果你觉得你的反应速度够快,不需要降低速度的话,那么就在那些磨蹭的游戏中加快速度吧。程序的使用非常简单,你要做的唯一事情是移动滑杆设定你想要的速度。 立即下载

相关推荐

VC写的系统变速齿轮加速源码

1)我的电脑-》右键-》属性 -》硬件-》设备管理器-》计算机-》打开-》更新驱动设备-》选advanced驱动 重启电脑安装后; 2)再用VC6.0调试编译代码,打开游戏然后试试; 3)注意驱动文件sys的加载

“变速齿轮”再研究

                             “变速齿轮”再研究       作者        BBBKOM        关键词     变速齿轮  调用门  RING0     提起“变速齿轮”(以下简称“齿轮”)这个软件,大家应该都知道吧,该软件号称 是全球第一款能改变游戏速度的程序。我起初用时觉得很神奇,久而久之就不禁思考其实现原理了,但苦于个人水平有限,始终不得其解,成了长

BBBKOM的专栏 2720

变速齿轮 易语言源码 变速齿轮源码 变速器源码

变速齿轮 易语言源码 变速齿轮源码 变速器源码 可以用于网络游戏

深入游戏变速底层原理以及内核变速的实现

前言 当你接触了一款叫做“变速齿轮”的软件,你可以玩上一整个星期。因为它实在是太酷炫了,无论网页(Flash、HTML5)、还是小软件,又或者是单机游戏,他总能给你惊喜,唯独网游不能!那么我们今天就来探究一下变速原理,并且我们试试看,能不能写出一个更牛逼的变速软件。 我们本章节内容大致如下图: 变速原理探究 变速齿轮(GearNT 0.46) 原理 我们拿个经典的变速软件开刀,...

yaoyutian的博客 1万+

易语言 草帽变速齿轮 源码

易语言 草帽变速齿轮 源码

易语言草帽变速齿轮源码-易语言

易语言草帽变速齿轮源码

变速齿轮原理研究

打开Intel64 手册卷1,查看指令集:处理器在每个时钟周期内单调递增时钟周期计数器MSR,并在处理器重置时将其归零,而rdtsc指令从时钟周期计数器MSR中读取数据,存入EDX:EAX中,如果CPU的主频是1GHz,那么时钟周期的持续时间就是1纳秒,所以这个API能精确到微秒也是可以理解的,类似的还有rdtscp指令,它还额外返回处理器的id到ECX中。另外的,这种方法只能欺骗自己的机器,对于需要联网的程序,强行改变客户端运行的速度可能会导致异常。当标志设置时,仅能在特权级别0(内核态)下执行。

qq_43148856的博客 1677

变速齿轮主程序源码.e

这是变速齿轮源码,喜欢的可以下载哦。源码开发不易请善待,仅供学习,禁止商用,这个是最简单的编程登入器,希望能帮到你们

“变速齿轮”研究手记

注意:如果你看了本文,对我们这个软件有兴趣,请到我们的主页www.vrbrothers.com下载。注:为节省篇幅,本文对一些计算机术语直接使用而没有作详细的解释,读者若有不熟悉之处,建议参考清华大学出版社出版,周明德编著的《微型计算机系统原理及应用》一书中关于8253/8254定时器和x86保护模式的相应章节。            “变速齿轮”研究手记                     

rchu的专栏 2218

达梦DCA2023年10月培训及考试总结

本次培训共分三天,培训内容不单单是DCA的单机安装、实例创建、库表操作等,培训内容是把关系型数据库的理论及DM8的产品结合起来讲,很实用。具体的考试则是需要个人大量的线下练习,考试总体而言都是培训讲过的内容,只要用心听,好好练习,哪怕是没有计算机基础的童鞋也能考过。提示:以下是本篇文章正文内容,下面案例可供参考总体而言,本次考试不是很难,用心准备,考前认真听讲,考后多次练习,基本都能通过。

qinjuao的博客 2320

企业级防火墙实战:用深信服AF8.0打造安全办公网络(含流控/僵尸防护配置)

本文详细介绍了如何利用深信服AF8.0防火墙构建企业级安全办公网络,涵盖设备初始化、多接入方式配置、三维一体安全防护体系(包括应用控制、僵尸防护和智能流控)以及典型问题排查。通过实战案例和配置模板,帮助IT管理员高效部署和优化网络安全管理,提升企业网络安全防护能力。

weixin_28716443的博客 280

VC “变速齿轮”再研究

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站                              “变速齿轮”再研究       作者        BBBK

1401

Mac 不小心断开移动硬盘导致磁盘无法读取和加载(顺利解决!)

Mac移动硬盘不正确断开连接导致无法读取加载的解决方法,亲测有效

Robin_Pi的博客 1万+

变速齿轮”再研究-----(转帖)

 提起“变速齿轮”(以下简称“齿轮”)这个软件,大家应该都知道吧,该软件号称是全球第一款能改变游戏速度的程序。我起初用时觉得很神奇,久而久之就不禁思考其实现原理了,但苦于个人水平有限,始终不得其解,成了长驻于脑中挥散不去的大问号。  偶然一天在BBS上看到了一篇名为《“变速齿轮”研究手记》(以下简称《手记》)的文章,我如获至宝,耐着性子把文章看完了,但之后还是有很多地方不解,不过还是有了比

lxh111222的专栏 2348

告别玄学:用Multisim或LTspice仿真带你直观理解PFC的三种工作模式(CCM/DCM/CrM)

本文通过Multisim和LTspice仿真工具,详细解析了PFC(功率因数校正)的三种工作模式(CCM、DCM、CrM)的本质差异。从波形捕捉到参数优化,提供了实用的仿真技巧和工程实践策略,帮助工程师直观理解并优化PFC电路设计,提升THD和功率因数。

weixin_30363285的博客 395

3D点云目标检测Complex-YOLO(训练篇)(一)———KITTI数据集预处理与制作

KITTI dataset Download dataset KITTI 3D Object Detection Evaluation 2017 link 下载四个部分,共41.4GB 解压后为四部分内容(相机校准矩阵calib、RGB图像image_2、标签label_2、点云数据velodyne) 对应的testing和training数据。其中,training数据为7481张(图片和点云对应的场景),testing数据 7518张(无label_2数据)。 Data Preprocess

“知识最大的敌人不是无知,而是错觉” 7150

Windows游戏加速外挂-变速齿轮 学习笔记-【第一篇】

找到两篇文章,是比较流行的方法,接下来记录一下收获: 第一篇文章: “变速齿轮”研究手记 转自:http://www.newasp.net/tech/58262.html 注意:如果你看了本文,对我们这个软件有兴趣,请到我们的主页www.vrbrothers.com下载。 注:为节省篇幅,本文对一些计算机术语直接使用而没有作详细的解释,读者若有不熟悉之处,建议参考清华大学

duhaomin的专栏 1万+

【前端HTML生成条形码——MQ】

本文将展示前端HTML如何根据用户输入内容生成对应的条形码,附HTML和JS代码。代码如下(示例):

weixin_42588676的博客 1955

向往这样的生活

我想还是先看看这种生活是一种什么样的生活吧。 变速齿轮”研究手记回到主页     也许是我孤陋寡闻吧,说出来不怕您笑话,对于“变速齿轮”这样著名的软件,我一直到五天前,也就是2001年2月28号才第一次听说。我有几个同学很喜欢玩图形MUD,整天见了面就在一起切磋“泥”技。我对MUD本身并没有多大兴趣,但是那天早上偶尔听

hungrytiger1987的专栏 873

ORB-SLAM2算法部署验证、原理代码和各种改进方法汇总

本文主要汇总个人学习使用ORB-SLAM2算法中参考的相关博客和代码汇总,以及在ORB-SLAM2算法基础上各个改进方法的汇总。

重点记录机器人和自动驾驶相关领域的技术,尤其是计算机视觉,视觉SLAM等,不仅有前言文献或必备算法的学习,也会有实际应用的点滴经验 742

Python爬虫实战:使用Scrapy+Playwright高效爬取全球石油价格历史数据

python复制下载# 产品信息# 日期信息# 价格数据price = scrapy.Field() # 收盘价open = scrapy.Field() # 开盘价high = scrapy.Field() # 最高价low = scrapy.Field() # 最低价volume = scrapy.Field() # 交易量change_percent = scrapy.Field() # 涨跌幅# 元数据。

2201_76125261的博客 1020

ok-ww 实操教程:用一个自动化程序跑完鸣潮的刷本、日常与资源收集

深夜十一点,合上电脑前勾好任务队列,屏幕里的角色还会继续清完声骸之渊。这种"设置好就走人"的体验,来自 ok-ww——一个靠图像识别驱动的《鸣潮》自动化助手。 ## ok-ww 是怎么识别画面并操作角色的 整套逻辑很直白:ok-ww 抓取游戏屏幕,把技能图标、小地图标记、按钮位置这些界面元素与内置模板比对,再通过 Windows 接口模拟键盘和鼠标输入,驱动角色移动、切人、放技能。它不读内存、

gitblog_00691的博客 462

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

炉石插件 HsMod 快速上手:变速齿轮、皮肤修改与战棋辅助

HsMod 是一款基于 BepInEx 的炉石插件,用本地注入的方式给游戏加上一整套自定义能力:炉石变速齿轮、炉石皮肤修改、炉石帧率解锁,以及炉石酒馆战棋的对手 MMR 与会话统计。它遵循 AGPL-3.0 开源协议,功能默认关闭、需手动启用。这篇上手文带你从安装到进阶配置走一遍。 ## 它能干什么 一句话定位:HsMod 不改游戏本体,靠 BepInEx 5 在运行时挂钩方法,把原本藏在开发

gitblog_00908的博客 402

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

变速齿轮的原理

变速齿轮通过修改API函数GETTICKCOUNT和TIMEGETTIME骗过了游戏和程序的定时器导致游戏和程序速度看起来被改变。下面以GETTICKCOUNT为例进行分析:原本的GETTICKCOUNT汇编:kernel32!gettickcount     mov gs,[bffcaea18]                                    mov eax,gs:

rwjgpget的专栏 4564

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

上一篇: 用GDB调试程序
下一篇: 庆祝一下
codeshell
博客等级 码龄22年 9粉丝 33原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值