欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[翻译]使用OllyDbg从零开始Cracking 第四章-汇编指令

[翻译]使用OllyDbg从零开始Cracking 第四章-汇编指令

今年过年在家里休假的时候,无意中看到一篇帖子【使用OllyDbg从零开始Cracking 传说有40多章,为什么没有了?】,里面提到了俄罗斯人写的一系列OD破解的教程。然后我在坛子里面搜了一下这篇教程,发现有个叫BGCoder的大哥翻译了前3章,后面的章节没有继续翻译了,很遗憾。仔细阅读了一下,教程写的非常好。这么好的教程不翻译完可惜了,我本想根据BGCoder提供的链接下载剩余的教程,我擦,发现几年过去了,链接已经失效了。郁闷,可是我还是不死心,将教程的俄文名称在谷歌中搜索了一下,发现有个老毛子的论坛有这篇文章,然后我就进入论坛,但是只发现一篇,没有全集,然后就在论坛里面找哇找,皇天不负有心人,终于找到了教程的合集。
     前几天在坛子里看留言的时候发现BGCoder说了,这系列文章不是俄罗斯人写的,而是一个西班牙人写的,俄罗斯人也是翻译的。,然后BGCoder就感觉很郁闷,发现翻译的不是原版,就没有心情翻译了之类的云云...呵呵,也许是这样BGCoder就不想翻译了吧。
     然后我找到了原版作者的网站,发现果然有西班牙语的这系列教材,并且还有1600多篇高阶教程(近几年软件的脱壳和破解的教程)。这58篇教程是位高阶教程打基础的,貌似作者觉得新手直接看高阶教程,难以理解,所以才写了这58篇基础教程。作者想的真周到。结果我看了看西班牙语版,谷歌翻译了一下,我擦,翻译的文不对题。擦,还是翻译老毛子版的吧,容易翻译一些。
     在百度空间也可以搜索到4-7章的翻译,我看了一下,地址如下:http://hi.baidu.com/hardcom/item/f7ce0d126b8b78e05f53b1b8这几篇文章作者就是直接扔到翻译器中,然后可能直接复制粘贴了,简直难以阅读,本来想从第8章开始翻译,现在看来还是得接着BGCoder的从第4章开始翻译,因为能力有限,所以翻译有的地方可能不准确,请见谅.
     以上就是我想翻译的缘由。
下面是第四章的内容:
                                             第4章-汇编指令
之前的章节主要是理论知识,现在我们要在OllyDbg中实践一下,为后面打开基础。
OllyDbg中几乎所有的标志我都有考虑,如果你遇到了我没有给出的指令,你可以查阅更加全面的汇编指南。
NOP(无操作)
运行这条指令不会对寄存器,内存以及堆栈造成任何影响,英文单词的意思是”无操作”,也就是说,它没有特殊的用途。例如,你用一个短指令来替换一个长指令的话,如果处理器没有错误,多余的空间将会被NOP填充。
适当数目的nop指令可以将其他指令完全替换掉。
下面使用OllyDbg重新载入CrueHead'а(CrackMe的作者)的CrackMe。

我们可以看到反汇编的源代码,如上图第一条指令是PUSH 0,占两个字节,在这条指令上面单击鼠标右键选择Assemble.

或者直接使用快捷键-空格键,在弹出窗口的编辑框中输入NOP。

写入NOP指令后单击Assemble按钮。

这里我们可以看到OD设计的非常智能,考虑到PUSH指令占两个字节,OD会使用两条NOP指令进行替换,而不是使用一条NOP进行替换。
现在,在原来PUSH 0的地方显示的两条NOP指令,单击F7,指令一条NOP指令,可以看到,这里只改变了EIP(保存了下一条要执行指令的地址)寄存器的值,并没有影响到其他寄存器,堆栈或者标志位。
现在我们需要在数据窗口查看这两个字节,它们的内存地址分别是401000和401001。

在数据窗口中,鼠标右键选择-“Go to”-“Expression”,输入你需要转到的地址。

这里我们需要输入401000。

红色突出显示的是刚刚修改过的字节。前两个是90,然后E8,FF和04,00,00。这是一个Call指令的所有剩余字节。
OD可以撤销我们修改的指令吗?
呵呵,当然啦。
在数据窗口或者反汇编窗口中,鼠标拖选中两个字节。

然后单击鼠标右键,选择“UNDO SELECTION”。

这样就恢复了原来的PUSH指令。

在数据窗口中的话,你就可以看到它原始的字节了。

以上是关于NOP指令的所有内容。
堆栈相关指令的说明
我们之前说过,堆栈就像一个信箱一样,越顶部的信越先被取出来。
PUSH 
PUSH指令-将操作数压入堆栈中。我们可以看到,CrueHead'а(CrackMe的作者)的CrackMe的第一条指令就是PUSH指令。

“PUSH 0”指令将把0存入到堆栈的顶部,此时并没有压入堆栈,指令执行后,我们看看堆栈如何变化。堆栈的地址在你的机器上可能会有所不同,但效果是一样的。

堆栈地址在我的机器上是12FFC4,可能与你机器上的不同,因为堆栈每次可能放置在不同的位置,其初始内容也可能有所不同,即这里的7C816D4F和你的也可能不同。按F7,将0压入堆栈,堆栈顶部的就是0了。

按下F7,堆栈顶部我们可以看到加入了0。下面的12FFC4中仍然是7C816D4F,堆栈中的其他值并没有改变。
主要的变化就是堆栈的顶部变成了12FFC0(这是PUSH 0指令执行的结果),新压入的数据总是在堆栈的顶部,并不会改变下面的数据。
这里也可以看到,ESP的值变成了12FFC0。

当然PUSH指令不仅仅可以压入数值:
PUSH EAX的话,那么堆栈的顶部将保存EAX的值。
同样的适用于其他的寄存器,你也可以压入特定内存地址中的值。
PUSH [401008]
注意,这和下面这条指令的解释是不同的。
PUSH 401008
(没有方括号)
如果你使用的“PUSH 401008”,那么堆栈中将被放置401008。

执行以后,我们可以看到下面的结果:

如果换成是“PUSH [401008]”

[401008]表示401008这个内存单元中存储的内容,这个时候我们得去数据窗口中查看它的值是多少。
在数据窗口中鼠标右键单击-Go to-Expression,输入401008,可以看到:

这四个字节是CA 20 40 00。按F7执行这条PUSH执行。

堆栈中我们可以看到在数据窗口中颠倒过来的值,即,它们被倒序放置。
读/写的内容在内存中倒序放置时处理器的特点之一。要怪你只能怪处理器的厂商了,嘿嘿。
通常情况下,没有用方框号括起来的只是一个数字。
现在我们知道,OD中“PUSH [401000]”的意思了。

PUSH DWORD PTR DS:[401008]
像这样的,除非有明确规定,否则OD都是认为你要操作的是4个字节的内存,也就是DWORD。其他格式会在其他指令中予以说明。
POP 
POP指令是出栈:它会取出堆栈顶部的第一个字母或者第一个值,然后存放到指定的目标地址内存单元中。例如,POP EAX从栈顶中取出第一个值存放到EAX中,随后的一个值随即变成栈顶。
我们还是看到CrueHead'а的CrackMe的开始的语句:

将第一条指令替换成“POP EAX”,注意在第一行,按空格键:


以下是执行此操作,堆栈变化情况的说明:

ESP中存放的是12FFC4,它存放的是堆栈的顶部的内存地址。

我们可以看到EAX的值是0(我这里的情况)。
按F7键。

我们可以看到,原来堆栈顶部的值消失了,现在ESP为12FFC8。

原本在堆栈顶部的7C816D4F现在到了EAX中。
同样地,如果用户是“POP ECX”,那么上面的值将会到ECX寄存器中或者你指定其他寄存器中。
我们这里就研究了入栈和出栈指令。
PUSHAD 
PUSHAD指令把所有通用寄存器的内容按一定顺序压入到堆栈中,PUSHAD也就相当于’PUSH EAX,PUSH ECX,PUSH EDX,PUSH EBX,PUSH ESP,PUSH EBP,PUSH ESI, PUSH EDI’。
让我们来看看。
再次载入CrueHead'а的CrackMe,调用右键菜单来修改指令为PUSHAD。


这是我当前寄存器组的情况。

按下F7键,看看现在堆栈的情况:

看到所有寄存器的值都被压入堆栈了。12FFC4存放的是之前堆栈顶部的值,然后上面就是0(PUSH EAX),12FFBC存放的是ECX的内容,接下来是EDX寄存器的内容,以此类推。
POPAD 
该指令与PUSHAD正好相反,它从堆栈中取值,并将它们放到相应的寄存器中。POPAD等价于“POP EDI,POP ESI,POP ESP,POP ESP,POP EBX,POP EDX,POP ECX,POP EAX”。
正如前面的例子,我们将第一条指令修改为POPAD:

由于堆栈中保存的是之前寄存器组的值,执行POPAD其返回原来的状态。

因为之前执行了PUSHAD,所以恢复的寄存器及其值和之前相同。

PUSHAD-POPAD指令经常被使用,例如:某个时刻你需要保存所有寄存器的内容,然后修改寄存器的值,或者进行堆栈的相关操作,然后使用POPAD恢复它们原来的状态。
也有以下的用法:
PUSHA 等价于 'PUSH AX, CX, DX, BX, SP, BP, SI, DI'。
POPA 等价于 'pop DI, SI, BP, SP, BX, DX, CX, AX'。
PUSHA,POPA和PUSHAD,POPAD就像姐妹一样,只不过它们在16位程序中使用,所以我们不感兴趣,OllyDbg是一个32位程序的调试器。
赋值指令的说明
MOV 
该指令将第二个操作数赋值给第一个操作数,例如:
MOV EAX, EBX
EBX值赋值给EAX。继续用OD载入CrueHead'а的CrackMe。

两个寄存器的值是不一样的,我们只是看寄存器:

在我的机器上,EAX为0,EBX为7FFD7000。这些初始值有可能与你机器上的不一样,我们只是看赋值的过程,按F7键,EBX的值就赋值给了EAX。

明白了吗?
MOV指令操作数有很多可以选择,例如:
MOV AL, CL
这条指令时将CL的值赋值给AL。在OD中写上这句。

寄存器:

请记住,AL-是EAX的最后两位数字以及CL-是ECX的最后两位数字。按F7

可以看到只是将B0赋值给AL了,并不改变EAX和ECX的其他内容,即EAX的最后两位数字。
也可以给内存单元赋值,反之亦然一个寄存器的内容。

上图这种情况下,将要给EAX给的值是405000这个内存单元中的值,正如前面提到的,DWORD意味着你必须移动4个字节。如果该指令给出了一个不存在的内存单元可能会导致错误。我们可以用OD很容易的检查出来。
在数据窗口中右键单击选择Go to-Expression转到405000处.


可以看到内存中的内容是-00100000。由于内存中内容是倒序存放的,那么EAX中将被装入00001000,按下F7键,看看会发生什么。

这里是1000,是从内存中读取出来的。现在,如果我们想写一个值到这个地址:
MOV DWORD PTR DS:[400500],EAX
写入该指令。

在数据窗口中查看405000:

然后,按F7键发生异常:

注意异常的描述,是由于我们要写入400500这个内存地址导致的内存访问异常。
如果移动的是4个字节使用DWORD,移动两个字节的话使用WORD。
例如:
MOV AX,WORD PTR DS:[405008]
将405008内存单元中的两个字节赋值给AX。这种情况下,我们不能用EAX,这里移动的只有两个字节,所以你必须使用一个16位的寄存器。
在数据窗口中查看405008.

按F7赋值给AX的只有两个字节。如下:

MOVSX (带符号扩展的传送指令)
第二个操作数可能一个寄存器也可能是内存单元,第一个操作数的位数比第二个操作数多,第二个操作数的符号位填充第一个操作数剩余部分。 下面是一个例子。
这里我们还是在OD中载入CrueHead'а的CrackMe。

这里我不会说太多,因为我想在座各位自己来计算这个操作数求值,嘿嘿。
在OD中,反汇编窗口和数据窗口中间有一个解释窗口。

这里我们可以看到,解释窗口向我们展示了我们操作数里面存放的值。我这里,BX存放的是F000。

同时可以看到,EAX的值为0,所以这些东西总是可以帮助我们理解OD要执行的指令(我希望你已经掌握了其中的概念和寻址方式,嘿嘿)。
按F7键。

看到AX,BX中存放的是F000,并且EAX剩余部分填充为了FFFF,因为F000是一个负的16位数字。如果BX存放1234,EAX将等于00001234,即左边的字节将会被0填充,因为1234是一个正的16位数字。
16位数和32位数的正数和负数的概念是一样的,只不过16位数的范围是0000到FFFF。0000到7FFF是正数,8000到FFFF是负数。
如果我们把BX修改为7FFF,把EAX修改为0,然后执行这条指令会发生什么呢。

AX被赋值为了7FFF,其余部分被填充为0了-因为7FFF是正数。我们还可以把BX修改为8000(负)。

按F7键。

AX被赋值为了8000,剩余的部分为FFFF,因为8000是负数。
MOVZX (带0扩展的传送指令)
MOVZX类似于前面的语句,但是这种情况下,剩余的部分不根据第二个操作数的正负来进行填充。我们这里不提供范例,因为和上面是相似的,剩余的部分总是被填充为0。
LEA (取地址指令)
类似于MOV指令, 但是第一个操作数是一个通用寄存器,并且第二个操作数是一个内存单元。当计算的时候要依赖于之前的结果的话,那么这个指令就非常有用。
我们在OD中写入以下指令:

在这种情况下,有括号,但不需要获取ECX+38指向内存的值,只需要计算ECX+38的值即可。我这里,ECX的值为
12FFB0。

在这个例子中,LEA指令就计算ECX + 38的值,然后将计算的结果赋值给EAX。
解释窗口中显示了两个操作数。

它表示,该操作数是12FFE8,也就是ECX+38的值,并且EAX的值为0。
按F7键。

指定的地址被存放到了EAX中,因为完成的是赋值操作,所以我们会认为操作数是内存单元中的值,但是实际上
操作数仅仅是内存单元的地址,而不是里面的内容。
XCHG (交换 寄存器/内存单元 和 寄存器) 
该指令交换两个操作数的值,例如:
XCHG EAX,ECX
EAX的值将被存放到ECX中。反之亦然。我们在OD中来验证这一点。

在我的机器上,EAX的值为0,ECX的值为12FFB0。

按F7键,看到他们交换了数值。

你也可以使用这个指令来交换寄存器和内存单元的值,本节之前有提到。

按下F7键:

这个例子我们在MOV指令使用过,这里我们对该内存地址没有写权限。
好了,这就是常用指令的第一部分,是非常有用的以及有趣的,我给出的例子能够体现这一点。在接下来的部分,我们将继续研究指令。
这么多图片,传的我手的麻了,神马时候坛子的编辑功能才能改变呀,忘了吃饭,饿死我了,吃饭去了,闪人...
下面是DOC版以及本章的附件。
使用OllyDbg从零开始Cracking 第四章.doc
ollydbg01-Crackme.7z

上传的附件 图片1.png
图片2.png
图片3.png
图片4.png
图片5.png
图片6.png
图片7.png
图片8.png
图片9.png
图片10.png
图片11.png
图片12.png
图片13.png
图片14.png
图片15.png
图片16.png
图片17.png
图片18.png
图片19.png
图片20.png
图片21.png
图片22.png
图片23.png
图片24.png
图片25.png
图片26.png
图片27.png
图片28.png
图片29.png
图片30.png
图片31.png
图片32.png
图片33.png
图片34.png
图片35.png
图片36.png
图片37.png
图片38.png
图片39.png
图片40.png
图片41.png
图片42.png
图片43.png
图片44.png
图片45.png
图片46.png
图片47.png
图片48.png
图片49.png
图片50.png
图片51.png
图片52.png
图片53.png
图片54.png
图片55.png
图片56.png
图片57.png
图片58.png
图片59.png
图片60.png
图片61.png
图片62.png
图片63.png
图片64.png
图片65.png
图片66.png
使用OllyDbg从零开始Cracking 第四章.doc
ollydbg01-Crackme.7z

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功