ARTeam 教程

作者: Shub-Nigurrath
译者: aal from bbs.pediy.com

请访问: http://cracking.accessroot.com | http://forum.accessroot.com

新手教程第 #4 篇: 脱壳与补丁, 一个稍复杂点的例子 v.1.1


相关信息 Gabri3l发起的新手系列教程的继续,解释了aspack的压缩程序,IAT细节和Olly的自解压功能.
目标 Super Video Joiner 1.8.0
下载地址 http://www.witcobber.com/  或者
http:/www.intechhosting.com/~access/ARTeam/tools/superjoiner_180.exe
工具 OllyDbg 1.10 (HideDebug和OllyDump插件), Import Reconstructor 1.6, Diablo2oo2 Universal Patcher
保护方式 AsPack 2.12,被破坏的输入表, 序列保护
难度 新手
类别 补丁
作者(们) Shub-Nigurrath 2005年二月
软件要求 Windows XP, Firefox 1.0 或更高(以获得更好的阅读效果和打印效果)


1. 介绍


这篇教程是Gabri3l的三篇系列教程的继续,您可以在我们的网站上找到它们.我将假设您已经读过这些教程了,他在前面讲述的一些东西您已经了解.我所指的尤其是最后一篇,也就是第三篇教程,那篇文章中讨论了一个由AsPack 2.11保护的程序.

这篇教程中我将(滥)用另外一个由AsPack保护的程序,但这个程序却并不能用Gabri3l在他的第三篇文章中提到的方法脱壳.这次的目标软件是Super Video Joiner,一个用来将不同几个的视频合并成一个文件的小程序,也可以是DivXs.

我们不久就会看到,这个目标程序有一个不同的输入地址表(ImportAddressTable,IAT)格式,这需要我们使用Import Reconstructor的高级一点的用法.我还会演示如何使用Olly自动脱那些带自解压程序的壳(比如那些用aspack, upx或者类似的程序但不是asprotect, armadillo等程序压缩的程序).而一旦脱了壳,对脱壳后的程序进行补丁就是非常简单的了,我将只用几步将其讲述清楚.

最后我将集中在如何创建一个小补丁上,希望这补丁会在将来的版本中继续有效:Gabri3l展示了如何使用Dup(Diablo2oo2Universal Patcher)的偏移量补丁(Offset Patch),而我要讲述的的是如何使用搜索与替换(Search and Replace)这一页的功能...

好了,系好安全带(出发!) ;-)



2. 程序的脱壳


理解程序所使用的压缩手段
一如平常,任何破解进程的首要一步是理解我们所面对的是什么东西!先打开PEiD看看它怎么说.

顺便提一下,PEiD不会总是100%正确,偶尔也会犯错误.因此它的作者加入了允许使用外部签名数据库的功能,这就是在它的安装目录下的叫做userrdb.txt外部文件.

该文件包含了PEiD内部当前不包含的额外签名.这些签名通常都会在各处的论坛中发布出来,甚至我们的论坛上也有,你可以花一些时间来收集这些签名,这样会提高你的PEiD的准确度.PEiD只安装一个没有任何意义的示例userdfb.txt文件,所以通常你需要四处找一些真正的签名.

 

话说回来,这次是个简单的情况,AsPack很容易就被PEiD识别了出来.你大概早就知道了AsPack是一个程序压缩壳,而不是保护壳,所以它的主要目的是减小整个exe文件的大小而不是保护它.它的主要特征是可以"re-entrant",也就是说解压代码将程序解压到内存中后它不会留下任何跟踪代码,在程序看来就好像什么也没有发生一样(同样见Gabri3l的教程).这样就有了Gabri3l在他的教程中所推荐的方法,也就是在第一个PUSHAD指令后的堆栈中的Dword设置一个硬件断点.

另一方面,像AsProtect这样的保护壳(与AsPack同一作者),不仅仅只对可执行代码作那么几件事而且是"非re-entrant"的;另外当程序在内存解压后要执行时,其代码已经被大面积的被修改(它的IAT被修改是为了使其不容易被重建,它的指令被修改是为了使其不容易被跟踪出程序的流程等等).

让Ollydbg为我们解压程序
好了,我们现在可以打开OllyDbg解压程序了.作为练习,用Gabri3l第三篇教程中描述的方法自己跟踪一下,看看你是不是已经理解的够清楚了. 完成了么?? 好,继续往下读..

在这篇文章里我要用另一种方法,这种方法已经OllyDbg在中实现了,可是却很少有人知道或用到.

进入OllyDbg的选项然后设成下图这样:

OllyDbg有个自解压功能模块(Self Extractors,SFX),它所完成的和Gabri3l向你讲述的方法相似(实际上更复杂些).正如我所说的,这个技巧对所有的"re-entrant"(原来的程序可以重获控制权)的程序压缩壳都有效,或者说的更广泛些,它对所有的在内存中自解压的可执行程序都有效.Ollydbg能够为你做所有的工作并且停在正确的地方!

像上面那样设置好了之后就可以将目标载入Ollydbg了(这一次的目标程序是videojoiner.exe). 没必要关掉Olly再打开,只要重新载入就可以了,也就是说如果已经打开了的话再按一下CTRL-F2就可以了.

通常Ollydbg都会停在AsPack解压代码的第一条指令上(如下),是一条PUSHAD指令

004F8001 > 60 PUSHAD
004F8002 E8 03000000 CALL videojoi.004F800A
004F8007 - E9 EB045D45 JMP 45AC84F7


而这次Ollydbg一反常态开始深入地分析程序,尤其是开始跟踪程序(注意界面的左下角,你会看到一条信息"跟踪SFX"("Tracing SFX..")). 让Ollydbg这样一直忙下去,等到这个过程停下来你就到达了Ollydbg自认为是解压过程的最后的目的地.

你可以看到,Olly会在状态栏中告诉你它所作的工作的一个统计数据:

如果你还没看到,按一下CTRL-A重新分析程序,这样就会清楚地看到我们已经到了程序的真正入口处(OEP).

关于Ollydbg需要注意一点:Ollydbg会存储(存在UDD文件中)刚才找到的真正入口(OEP),这样下次我们启动该程序的时候就会立刻找到OEP, Ollydbg会报告SFX入口点(也就是OEP)已经"得到了":

可以看到,我们所停的地方与我们手动获得的地方是一样的!所以,就省去了一些工作!

脱壳进程以及修复输入地址表(IAT)
现在我们就可以像平常一样脱壳这个程序了.往下读之前试试Gabri3l在第三篇文章中教你的方法,你会发现ImpRec无法从运行的进程中找到任何的有效输入函数;这次像Gabri3l说的那样操作是不行了. 为什么?好吧, 我们马上揭开谜底.

实现用OllyDump将程序脱壳,不要选中重建输入表复选框Rebuild Import,注意一下OEP. 这里就是: AF0A0.

我把脱了壳的文件起名, dump.exe.

现在打开Import Reconstructor 1.6,从进程列表中打开你正在调试的程序. 我会做得简单些,如果你已经对这个程序很熟悉了,那么下面这个图就足以说明一切了,图中的数字是所要进行的操作步骤.

一旦按下了"IAT Autosearch"按钮,ImpRec就会弹出这个对话框:

估计你已经知道了,这就是ImpRec认为IAT所在的地方(IAT有一个固定的结构所以很容易被ImpRec识别出来). 进行到图中的第3步和第4步你就会得出结论:这次IAT是不可能用这种方法重建的!

我们需要指导ImpRec以另一种方式在进程的内存空间中自己找到API函数!

 

来看看怎么做..
 

右击界面上的空白处会有这样一个菜单:

选择 "Get API Calls" 我们看到了一个这样的对话框:


在点击OK之前, 你知道程序将要干什么么?它将要在从0000000到FFFFFFF的内存中扫描所有的 CALL [X] 指令. 现在我们仅对其中的一个调用手动的实现一下这个过程,来理解你按下ok后ImpRec所要做的工作.
现在保持ImpRec原样不动,回来往下读读看这里发生了什么.

理解ImpRec将要做的工作
切换到正在台后运行的OllyDbg,此时它应该是停在了进程的OEP处.

下面就应该是你看到的:

004AF0A0 /. 55 PUSH EBP ; Real entry point of SFX code
004AF0A1 |. 8BEC MOV EBP,ESP
004AF0A3 |. B9 0B000000 MOV ECX,0B
004AF0A8 |> 6A 00 /PUSH 0
004AF0AA |. 6A 00 |PUSH 0
004AF0AC |. 49 |DEC ECX
004AF0AD |.^ 75 F9 \JNZ SHORT videojoi.004AF0A8
004AF0AF |. 53 PUSH EBX
004AF0B0 |. 56 PUSH ESI
004AF0B1 |. B8 B8ED4A00 MOV EAX,videojoi.004AEDB8
004AF0B6 |. E8 6573F5FF CALL videojoi.00406420
004AF0BB |. 8B35 6C204B00 MOV ESI,DWORD PTR DS:[4B206C] ; videojoi.004B3C14
004AF0C1 |. 33C0 XOR EAX,EAX
004AF0C3 |. 55 PUSH EBP
004AF0C4 |. 68 28F54A00 PUSH videojoi.004AF528
004AF0C9 |. 64:FF30 PUSH DWORD PTR FS:[EAX]
004AF0CC |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
004AF0CF |. 6A 00 PUSH 0 ; /Title = NULL
004AF0D1 |. 68 38F54A00 PUSH videojoi.004AF538 ; |Class = "Super Video Joiner"
004AF0D6 |. E8 B97BF5FF CALL videojoi.00406C94 ; \FindWindowA
004AF0DB |. 85C0 TEST EAX,EAX
004AF0DD |. 76 14 JBE SHORT videojoi.004AF0F3


去看看附近代码某一个系统调用是如何实现的.比如说我跟随的这个函数, FindWindowA, 它在004AF0D6调用.

看一下这个调用: 是这个样子的CALL videojoi.00406C94,此处00406C94是调用的子程序的地址. 在这个地址我们看到:

00406C94 $- FF25 04474B00 JMP DWORD PTR DS:[4B4704] ; user32.FindWindowA

00406C94的JMP直接跳到了user32的FindWindowA的开始地址. 另外你如果在00406C94附近看看就会发现, 这里有许多的JMP跳向真正的系统调用地址...

让我们做个总结: 有一个对某地址(004AF0D6)的调用,这个调用包含了跳向另一个地址(004B4704)的JMP,所以我们可以将第一个调用地址看作一个指向真正调用地址的指针,在汇编中这就是一个间接调用.

它的参数是这样的:

CALL [address_containing_the_real_addr_to_call] //注,方括号中即为"包含真正调用地址指针"

或者干脆用ImpRec的表示方法就是, CALL [X]..ImpRec现在要你做什么?选中"Get CALL [X] Scheme"...

比如说当寄存器中包含要调用的子程序的地址时,对应的汇编指令就会是这个模样:CALL [EAX], 假设EAX是这个寄存器.


让我们歇歇来更深入的看一下这个特别的参数, 首先,了解一下编译器是如何生成输入函数的这种调用的,以及为什么要这样做,我觉得会很有意义. 仔细想想输入API函数的调用是什么样子的,我们会想到两种情况:一种效率高的办法和一种效率不高的办法. 好一点的情况,输入API函数的调用应该是这样的:

CALL DWORD PTR [0x00405030]

如果你不熟悉x86汇编语言,我告诉你这是一个函数指针的调用.不管0x405030处DWORD大小的值是什么,CALL指令都会将程序的控制权转移到那里. 在前面的例子里, 0x405030这个地址就在输入地址表IAT中.
效率比较低的API函数调用形式会是这样的:
 

CALL 0x0040100C
........
0x0040100C:
JMP       DWORD PTR [0x00405030]
这种情况下, CALL指令将控制权交给一个stub. 由这个stub跳转到0x405030处所存储的地址. 同样,需要注意0x405030是输入地址表IAT中的一项 . 简而言之, 效率低的方式调用API函数多使用5个字节,并且因为这个多余的跳转执行是也会占用更多的时间.

那你可能会疑问怎么这种方式既然效率低却还要用呢? 没有很好的解释. 由于编译器本身的问题,它并不能区分同一模块中的API输入函数和普通函数的调用. 这样, 编译器就生成了这种形式的调用
 

CALL XXXXXXXX
此处 XXXXXXXX 是由连接器填充的实际的代码地址. 注意到上面这个调用指令没有借用函数指针, 而是使用了实际的代码地址. 为了保持全局内前后的环境平衡, 连接器需要一大堆的代码来替换 XXXXXXXX. 最简单的实现方法就是使用一个对JMP stub的调用,这就是前面你所见到的.

JMP stub是从哪里来的? 很让人吃惊, 它来自输入函数所在地输入函数库. 如果你研究一下输入函数库,研究一下与输入的API函数名相关联的代码,你会发现它正是一个上面那样的JMP stub. 这就意味着,在默认的情况下,不进行任何外来的干预,API输入函数的调用将会使用效率低的形式.

按这个逻辑,下个问题就是:如何得到优化的方式呢? 答案就在于你提供给编译器的一个暗示. __declspec(dllimport) 告诉编译器该函数在另外一个DLL中,这样编译器就会生成下面的指令
 

CALL DWORD PTR [XXXXXXXX]
而不是这样的指令:
 
CALL XXXXXXXX
另外, 编译器会生成一些信息,告诉连接器指令的函数指针部分解析成一个叫做 __imp_functionname(__imp_函数名)的符号. 比如, 如果你要调用MyFunction, 符号名称就应该是__imp_MyFunction. 看一下输入函数库的内部, 就会发现普通的符号名前都附加有__imp__前缀.这个__imp__符号直接解析为输入函数表IAT项目,而不解析为JMP stub.

既然这样,在平时你应该怎么做? 如果你在写输出函数并且为它们提供了一个.H文件,别忘了对函数使用__declspec(dllimport)修饰符号:
 

__declspec(dllimport) void Foo(void);
到Windows系统头文件里看看, 就会发现他们对Windows API同样使用__declspec(dllimport). 看到这一点并不容易, 但是如果你在WINNT.H中搜一下WinBase.H文件中使用的DECLSPEC_IMPORT宏的定义, 你就会发现 __declspec(dllimport) 是如何附加到系统API函数声明前面的.


结束脱壳过程
终于是时间结束我们的脱壳过程并修复出一个正常工作的脱壳文件来准备打补丁了, 对吧?

按下前面ImpRec对话框的 OK 后, 程序就开始工作了,最后的结果应该是下面这样的:

按下按钮就会发现许多的无效函数: 这是所有ImpRec无法将其与任何系统调用联系起来的调用.

没问题,很明显这没有出乎我们的意料: 并不是所有CALL [X]形式的调用都会指向系统调用,其中的一些肯定是程序的内部调用,所以ImpRec无法识别它们也就不足为奇了.


将这些函数去除掉只需在其中一项上右击然后选择"剪切指针数据"("Cut Chunk(s)" )(见前面图中的右键菜单).


再次按下"显示无效函数"("Show Invalid"),这次应该没有什么无效项了.

ImpRec的日志窗口显示 "Congratulation!" 不再有无效指针了, 可问题是:这能行么?:-)

 

 


按下"修复抓取文件(Fix Dump)" 马上我们就会看到答案了 ;-)

很明显答案是YES, 程序正常工作了!
 



3. 给程序打补丁

现在我们已经得到了一个正常工作的脱壳文件,可以痛痛快快地给它打补丁了.

先研究一下程序的保护方式:程序有三个限制,不能合并两个以上的文件,退出时有个消息框,不能输入任意的注册码. 所有这些限制都有一个明显的错误消息框, 而这正是用Olly逆向时很重要的条件.

从这一系列前面的新手教程以及ARTeam教程里, 你应该早知道了错误消息框是发现需要打补丁的代码的好地方, 也应该学会了如何在OllyDbg中搜索参考字符串.

用这种方法我发现了程序中需要补丁的三个地方. 我只是将代码简单的列了出来,并没有做什么注释,因为它们实在是太简单了 (只是修改一些跳转指令). 但我还是列出了我找到这些地方所用的参考字符串.

补丁1

原来的代码:
004A7123 |. /75 14 JNZ SHORT videojoi.004A7139
004A7125 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A712D |. |74 0A JE SHORT videojoi.004A7139
004A712F |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 |. |75 12 JNZ SHORT videojoi.004A714B
004A7139 |> \BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

修改后的代码:
004A7123 . 90 NOP
004A7124 . 90 NOP
004A7125 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A . 8338 00 CMP DWORD PTR DS:[EAX],0
004A712D . 90 NOP
004A712E . 90 NOP
004A712F . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 . EB 12 JMP SHORT videojoi.004A714B
004A7139 . BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

补丁2

原来的代码:
004A8CE2 |. /75 18 JNZ SHORT videojoi.004A8CFC
004A8CE4 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A8CE9 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A8CEC |. |74 0E JE SHORT videojoi.004A8CFC
004A8CEE |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A8CF3 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A8CF6 |. |0F85 52020000 JNZ videojoi.004A8F4E
004A8CFC |> \8D45 F0 LEA EAX,[LOCAL.4]
004A8CFF |. BA 28954A00 MOV EDX,videojoi.004A9528 ; ASCII "This trial version only can join less than 2 video files at a time,this limit only can be eliminated when it is registered!"
004A8D04 |. E8 E3B4F5FF CALL videojoi.004041EC

修改后的代码:
004A8CE2 . 90 NOP
004A8CE3 . 90 NOP
004A8CE4 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A8CE9 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A8CEC . 90 NOP
004A8CED . 90 NOP
004A8CEE . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A8CF3 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A8CF6 . E9 53020000 JMP videojoi.004A8F4E
004A8CFB . 90 NOP
004A8CFC . 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
004A8CFF . BA 28954A00 MOV EDX,videojoi.004A9528 ; ASCII "This trial version only can join less than 2 video files at a time,this limit only can be eliminated when it is registered!"
004A8D04 . E8 E3B4F5FF CALL videojoi.004041EC

补丁3

原来的代码:
004AB0D4 . /75 12 JNZ SHORT videojoi.004AB0E8
004AB0D6 . |833D FC3C4B00 00 CMP DWORD PTR DS:[4B3CFC],0
004AB0DD . |74 09 JE SHORT videojoi.004AB0E8
004AB0DF . |833D 003D4B00 00 CMP DWORD PTR DS:[4B3D00],0
004AB0E6 . |75 0A JNZ SHORT videojoi.004AB0F2
004AB0E8 > \B8 FCB04A00 MOV EAX,videojoi.004AB0FC ; ASCII "This is a unregistered Version, Don't forget to register it."
004AB0ED . E8 327EF8FF CALL videojoi.00432F24

修改后的代码:
004AB0D4 . 90 NOP
004AB0D5 . 90 NOP
004AB0D6 . 833D FC3C4B00 00 CMP DWORD PTR DS:[4B3CFC],0
004AB0DD . 90 NOP
004AB0DE . 90 NOP
004AB0DF . 833D 003D4B00 00 CMP DWORD PTR DS:[4B3D00],0
004AB0E6 . EB 0A JMP SHORT videojoi.004AB0F2
004AB0E8 . B8 FCB04A00 MOV EAX,videojoi.004AB0FC ; ASCII "This is a unregistered Version, Don't forget to register it."
004AB0ED . E8 327EF8FF CALL videojoi.00432F24

完成了么! 希望你能理解我只是简单粘到这里的代码, (这样做我也很抱歉,但是尽量缩短教程的长度也很重要).在Ollydbg保存程序所作的修改并起个名字比如dump.patched.exe, 然后运行.

程序正常工作了,没有任何限制,没有恼人的对话框,并且允许合并两个以上的文件..
 



4. 制作补丁

这次我们还是用Diablo2oo2 Universal Patcher制作补丁, 因为它有个独特的功能就是它能够给加壳的程序制作补丁,更重要的是它支持 AsPack. 最后的补丁应该能够直接用于原始的安装后的文件,而不需要先脱壳再减小发布时的体积.

还有就是我们这次要使用不同的方法制作补丁, 搜索与替换("Search and Replace"). .

搜索与替换("Search and Replace"), 总体构思
这种补丁方法是搜索目标可执行文件中的某个特定字节格式,并且当找到时用相应的新字节格式进行替换. 这个操作会不顾一切的将所有的指定的字节格式替换. 所以如果不使用一个精心设计的字节格式会潜在很大的危险.
而另一方面,如果字节格式选用的足够好就很可能能够再次用于同一程序的未来版本中: 程序的不同版本之间有所变化的通常只是程序中跳转指令,调用指令,传送指令(JMPs, CALLs和MOVs)等指令的相对偏移,而代码不变(因为我们所修改的只是通常不会发生变化的注册子程序部分, 而变化了的软件功能的改变我们是不会动的). 读到本文的每个人再破解同一程序的不同版本时都应该意识到这一点!

这就是 DuP 的搜索与替换("Search and Replace")表:

1中输入原来的字节格式,在2中输入新的格式, 点击3中的添加(Add)按钮,然后是4中的检查"搜索字节"事件(Check occourrence of "Search Bytes") . 最后应该在5中能见到添加的结果

还得提一下, 通常说来如果字节格式不唯一,DuP允许选择需要修改的事件数量,即使用事件(Occourrence)框.

选择字节格式
继续之前我需要澄清一件事: 每个汇编操作都有它的操作码(一个十六进制数表示的CPU指令), 然后是它的操作数. Ollydbg 为用户将它们分开了, 所以指令会像下面这样:

A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]

它有一个操作码 A1 (代表MOV EAX, DWORD PTR DS:[]), 操作数 081E4B00 (4B1E08逆序).

考虑到这点, 我们可以继续深入了, 来理解处理方法. 同样是为了简洁,我只列出本文第三部分中的第一处修改. 我只完整的说明如何一步步完成此处修改,对另外几处我只给出最后结果,你可以自己试一下手.

原来的代码:
004A7123 |. /75 14 JNZ SHORT videojoi.004A7139
004A7125 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A712D |. |74 0A JE SHORT videojoi.004A7139
004A712F |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 |. |75 12 JNZ SHORT videojoi.004A714B
004A7139 |> \BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

字节形式是这样的:

75,14,A1,08,1E,4B,00,83,38,00,74,0A,A1,9C,1E,4B,00,83,38,00,75,12,BA,00,72,4A,00

黄色部分代表操作数.
现在自己想想,这个程序的不同版本之间可能变化的值是什么? 答案是: 偏移地址, 字符串的地址和传送指令MOV的地址...

好,那么将这些可能变化的东西从字节格式中去掉,替换成两个星号(*)

75,**,A1,**,**,**,**,83,38,**,74,0A,A1,**,**,**,**,83,38,**,75,**,BA,**,**,**,**

再往深处想想这些操作数,你会发现我们可以将其中的一些留下,比如第一个操作数,是14, 它是一个相对跳转目的地的偏移量(意即向前14个字节),我可以断定即使软件的下一个版本中代码位置发生了变化,这一部分也是作为一个整体而变化位置的,而这14个字节的相对偏移量很小,所以跳转的目的地应该还会在这个相对位置上. 004A712A处的指令操作数是 00, 也可以肯定在未来的版本中不会发生变化.

这样我们就得到了最后要搜索的串:

原来的字节序列:
75,14,A1,**,**,**,**,83,38,00,74,0A,A1,**,**,**,**,83,38,**,75,12,BA,**,**,**,**

总结一点就是: 不要将离你要修改的代码太远的偏移量包括进去, 其余的操作数可以留下.

修改后的代码如下:
004A7123 . 90 NOP
004A7124 . 90 NOP
004A7125 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A . 8338 00 CMP DWORD PTR DS:[EAX],0
004A712D . 90 NOP
004A712E . 90 NOP
004A712F . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 . EB 12 JMP SHORT videojoi.004A714B
004A7139 . BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

这次会得到这样的字串:

修改后的字节序列:
90,90,A1,**,**,**,**,83,38,00,90,90,A1,**,**,**,**,83,38,**,EB,12,BA,**,**,**,**

好, 将它们输入 Dup

Note: 注意: 如果右击编辑区域1 或 2 会出现一个菜单允许你将要搜索的格式粘贴进去,而不用手动输入(CTRL-C不起作用).
//注:可能是我理解错误,或是版本问题,我没发现这一点.

5中选择你所添加的字节格式再按按钮4得到结果:

找到的字节格式的虚拟地址偏移量4A7123是正确的!

 

 

长话短说了,另外两个字节格式是这样的:

修改2:

原来的字节序列:
75,18,A1,**,**,**,**,83,38,00,74,0E,A1,**,**,**,**,83,38,00,0F,85,**,**,**,**,8D,45,**,BA,**,**,**,**

修改后的字节序列:
90,90,A1,**,**,**,**,83,38,00,90,90,A1,**,**,**,**,83,38,00,E9,53,02,00,00,90,8D,45,**,BA,**,**,**,**
 

修改3:

原来的字节序列:
75,**,83,3D,**,**,**,**,00,74,09,83,3D,**,**,**,**,00,75,0A,B8,**,**,**,**

修改后的字节序列:
90,90,83,3D,**,**,**,**,00,90,90,83,3D,**,**,**,**,00,EB,0A,B8,**,**,**,**

我想现在你应该已经能够照葫芦画瓢找到它们了吧.

最后Diablo2oo2 Universal Patcher看起来应该是这个样子:

准备好了要补丁了么, 别忘了选中"目标已被加壳"( "Target is packed" )复选框, 保存工程后生成补丁.

可以测试了!

当然 (应该) 正常运行.
 

 

5. 结论

所得到的经验

我意识到每一次想要把道理讲得基础些就得写一大堆东西,抓一大堆图片. 所以越简单的教程就要花费越长的时间, 不管怎样吧,我些这些东西是希望RCE的新一代们能够茁壮成长.. ^__^

希望你提高了自己使用ollydbg的水平,提高了理解幕后发生的事情的能力.也希望你能够理解即使是AsPack这样的简单玩意仅通过一篇教程说清楚也有些复杂.同样希望你能理解逆向工程大部分时间实际上意味着以开放的头脑和不同的视角来分析事物.

祝得到快乐!


 
6. 致谢

[MAIN TEAM]
| Nilrem | Enforcer | Ferrari | Pompeyfan(ex-member) | MaDMAn_H3rCuL3s | EJ12N | Kruger |
Shub-Nigurrath | Jdog45 | R@adier | Teerayoot | ThunderPwr | Eggi | Bone Enterprise | Gabri3l |

*****************************

Exetools | Woodmann | VCT | TSRh | Sir JMI | | FEUERRADER | Britedream | cl0ud (Mephisto) | Zest | Hobgoblin | Nullz | Everyone I missed & you


(.|.)
 ).( (¯`·._.·[¯¨´*·~-.¸¸,.-~*´¨&8~) Ŝħůβ¬Ňïĝµŕřāŧħ ¨´*·~-.¸¸,.-~*´¨]·._.·´¯)
( v )
 \|/