变速齿轮工作原理解密

相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PS:昨天回家搭车时听了About变速齿轮的工作原理的探讨,感觉有点奇怪,于是上网查了下,貌似最有说服力就是这篇,关于利用驱动通过Ring0进行访问I/O,我感觉采用这种方法的可能性不大,因为没下载变速齿轮,无法得知他是否采用这种方法。(用了呢种方案出错率非一般的大,一错了,随时死机。。。)

---------------------------------我是传说中的分割线-------------------------------------
变速齿轮工作原理解密

绝大部分游戏里面即时都会用到使用的定时器,变速齿轮之所欲可以给游戏加速,就是改变了定时器的性质。
有两种办法可以改变系统定时器。

1)API代码注入,通过注入自己的代码,使得API跳转到自己的代码处运行

计时API函数有GETTICKCOUNT和TIMEGETTIME,它是windows系统函数,如果可以找到他们的未知,修改成对自己有利的形式,就达到了加速的目的。

下面以GETTICKCOUNT为例进行分析:原本的GETTICKCOUNT汇编:

kernel32!gettickcountmovgs,[bffcaea18]
moveax,gs:[00000000]
subedx,edx
movgs,dx
ret

变速齿轮修改后的GETTICKCOUNT汇编:
kernel32!gettickcount
这里是关键-->jmp840500d9(840500d9并不是绝对的)
add[eax],al
add[ecx+00000000],ah
subedx,edx
movgs,dx
ret

可以看出变速齿轮修改了gettickcount的代码,当游戏和程序使用gettickcount时就会自动跳转到840500d9处执行。

再看?40500d9处的代码汇编:

840500d9:CLI
pushebp
movebp,esp
pushebx
pushec
pushedx
pushesi
pushedi
call840500e7
840500e7:popedi
xordi,di
movesi,edi
addesi,00402051
subesi,00401f0b
pushesi
calledi
call84050101
84050101:popedi
xordi,di
call[edi+0000fef0]
call84050110
84050110:subeax,[edi+0000ff30]
muldword,ptr[edi+0000ff30]
movebx,00100000
divebx
addeax,[edi+0000fe20]
pusheax
moveax,00402072
subeax,00401f08
addeax,edi
pusheax
calledi
popeax
popedi
popesi
popedx
popecx
popebx
popebp
sil
ret

以上正是变速齿轮变速的核心所在。(GETTICKCOUNT返回的是EAX的值你可以对EAX进行跟踪)

下面说一下变速齿轮挂接API的方法:首先变速齿轮在MMF区(WIN9X/ME)申请一块内存,把上面的代码从程序中移到该内存。使用修改描述符的方法从应用程序级跳到核心级,修改GETTICKCOUNT开头的代码使之指向申请的内存的首地址实现挂接。

这是普遍采用的一种方式。

2)还有一种方式。从驱动级修改。

在win95/98时代,我们是可以直接操作IO端口的,winNT以后,包括2000,XP系统,当我们试图操作IO端口,改变硬件设置的时候,系统会拒绝或者引起冲突。事实上在386以后的CPU上,用户程序都是运行在保护模式下,用户对IO端口对设备的访问都要经过操作系统的管理和监督。用户程序是无法直接对设备操作的。



那么在window下,要访问设备怎么办?windows提供了2中办法:

第一,通过修改EFLAGS寄存器中的特权级别(IOPL)实现

第二,任务状态段(TSS)中的IO许可映射BIt位

WindowsNT下,只使用了2种等级的特权级,0特权级和3特权级。用户程序运行在3特权级,设备驱动和系统内核运行在0特权级。只有系统内核程序和设备驱动才被允许直接操作IO端口。所有的用户程序在访问IO端口前,都需要得到设备驱动的准入,低信任级别的程序是无法访问的。

IObit映射就是为了使得那些低信任级别的程序能够访问设备IO而设置的。

在NT系统下,直接操作IO端口就有两种方式,一是,编写设备驱动,运行在0特权级直接操作IO设备端口。另外一种可行的办法就是修改IObit映射。

我们建议采用第一种办法,通过工作在0特权级的驱动访问IO端口。

这里有写好的设备驱动,PortTalk.sys,并提供了对外的接口,下面是用c语言写的,通过这个驱动对IO访问的一个例子:

#include

#include

#include

void__cdeclmain(void)


{

unsignedcharvalue;

printf("IoExampleforPortTalkV2.0\nCopyright2001CraigPeacock\\n");

OpenPortTalk();

outportb(0x378,0xFF);

value=inportb(0x378);


printf("Valuereturned=0x%02X\n",value);

outp(0x378,0xAA);

value=inp(0x378);

printf("Valuereturned=0x%02X\n",value);

ClosePortTalk();

}

变速齿轮原理 (转)
通过调用门跳到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指令参考手册
《“变速齿轮”研究手记》


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函数

附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线性基址



相关文档
最新文档