《汇编程序说明.doc》由会员分享,可在线阅读,更多相关《汇编程序说明.doc(23页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、内容表- I. 介绍 II. 寄存器 II.a 32位寄存器 II.b 16位寄存器 III. 指令 III.a JMP III.b MOV III.c Push/Pop + The Stack III.d alloc/label/registersymbol III.e Call and Ret III.f 其他 IV. Array of Bytes V. 结尾 VI. 人员名单/致谢词 额外内容: 写一个脚本- I. 介绍 - 哟,我猜你在读这个是因为下面两件事中的一个。 1) 你正在试图学习自动汇编 (我不会叫你菜鸟,因为每个人都是从哪里开始的,对吧 =) ) or 2) 你想测试你的自
2、动汇编知识 (作为扩展)。 那么,如果你是前者,那就慢慢的体会好每个部分,并且在继续进行前确认自己已经明白了这章。DarkByte wrote:大多数人都认为AA很难,其实它可容易了。 来自CE作者自己的话如果是后者,那我不会给你提任何的建议,即使我想帮忙。如果你发现有什么错了,或者含糊或者认为我可以做的更好,请告诉我。我一直处于自我学习的状态! 等等!别问,我知道你在想什么。Youwrote:为什么我非要听一个还在学习的人的话? 好,我来告诉你,我的朋友。即使我仍然在学习,我了解AA,并且我认为与你分享知识是一件很棒的事。 =) Edit: 这个是很久以前写的,但是现在我学到了很多,并且已经
3、重新检查了。现在,让我们投身入奇幻的电脑世界 - II. 寄存器- 这些也许你已经在一些脚本中看到过,它们被非常广泛的使用。有两种寄存器被使用,接下来来进行讲解。- II.a 32 Bit - 首先,我将解释每个寄存器是如何得到它们的名字的,这会帮助你记住它们哪个是哪个。首先,以E开头 (如果你注意了下面,你会发现所有的寄存器都是以E开头的) 它告诉你这个寄存器是32位寄存器。而A,B,C,D的含义你看完描述就能明显得体会到了。像SI, DI, BP,SP,IP也是一样。在 EAX, EBX, ECX, EDX后面的X,他简单的表示已经没有更多的字母了。有点像一个 NOP 命令 (之后你将读到
4、)。如果你注意了,你会发现每个32位寄存器都是3个字母。EAX: 累加器(Acculmulator register)。能够用来当存储器EBX: 从前, 它是个基础寄存器,但现在只是个闲着的存储器ECX: 计数器(Counting register)。也能用来当存储器EDX: 数据寄存器(Data register)。 跟之前三个一样,能用来当存储器。 ESI: 源址变址寄存器(SourceIndex register)。 是字符串形式的指针变量,但你现在还不用担心那部分。 能够用来当存储器。 EDI: 目的变址寄存器(DestinyIndex register)。又一次,能够当作存储器,并且
5、是个字符串形式的指针变量, 但别担心。 EBP: 机制指针寄存器(Base Pointer register)。 是用来临时存储ESP, 当然也可以像常规的存储器那样使用。 ESP: 原址指针寄存器(Source Pointer register)。它在堆栈里指向寄存器和地址(这个内容待会再说)。EIP: 指令指针寄存器(Instruction Pointer register)。 错误的使用会使你正在试图修改的程序崩溃。 -II.b 16 Bit-16位寄存器和32位寄存器很相似,他们间有两个区别。一是,32位寄存器名字是三个字母,而16位寄存器是两个字母。还有一件事就是16位寄存器比32位
6、寄存器多但别担心。16位寄存器我们一般都用不上。AX: 参照 EAXBX: 参照EBXCX: 参照ECXDX: 参照EDXSI: 参照ESIDI: 参照EDIBP: 参照EBPSP: 参照ESPIP: 参照EIP- 关于寄存器的内容还很多。 如果你想学习更多关于寄存器的只是, 那就去拜Google大神吧。 对于绝大多数的学习者来说求知欲都是很重要的。 - III. 命令 - 当今,什么语言没有它自己的函数和命令呢? 与英文相比,命令像个单词,而操作代码像个句子。操作代码并不难,比如: Code:jmp 00123EAA 关于操作代码你应该知道两点。首先, 在操作码里,一般都会有个地址或者寄存器
7、,以及一个显而易见的命令。地址是Hex形式的,是Hexadecimal的缩写。Hexadecimal是16进制数。 就如同10进制那样。 按照这种思路想一想。如同上面提到的那样,我们一般使用十进制。这就意味着我们在一个列里不能有“10”, 因为它占了两个地方。 而16进制,10至15都可以放在一个列里头。但是你会发现,“10”并没有出现在列里头。 先冷静, 这是因为在Hex进制里,10是用A表示的,而11是用B,直到15是F。再往后,它就是10,然后11,直到1F, 如果超过了20(十进制)就得从15(十六进制)往后算。有个简单的转换方法就是用操作系统自带的计算器,这里不赘述了。还有,每一个地
8、址都有它的操作码和字节码。这个字节数就是代表操作码是什么, 每一个命令都一一对应已经定义好的字节码。我知道那些常用函数的字节码是什么,如果你想知道那些字节码的话,你还是指望别人吧,推荐去拜Google大神 =) 最后,想注释很方便。 要注释的话, 就把/放在命令后边或某块空处,然后打上你想打的。如果你不打 / ,那电脑就会以为你在打命令呢。现在,让我们来了解每个命令都是干啥的。- III.a JMP - JMP 命令是最常使用的命令之一 (就如同 MOV 那样, 接下来会讲)。 也许你在操作码和脚本里看到的JMP 不像MOV 那么多,那是因为 JMP 命令有很多变种。下面就是列表。JMP: 始
9、终跳转(Always jump to)JE/JZ: 相等则跳转(Jump to if equal) JNE/JNZ: 不等则跳转(Jump to if not equal)JA: 无符号大于则跳转( Jump to if Above) JG: 有符号大于则跳转(Jump to if Greater) JNA: 无符号不大于则跳转(Jump to if not Above) JNG: 有符号不大于则跳转(Jump to if not Greater) JB: 无符号小于则跳转(Jump to if Below) JL: 有符号小于则跳转(Jump to if Lower) JNB: 无符号不小于
10、则跳转(Jump to if not Below) JNL: 有符号不小于则跳转(Jump to if not Lower) JAE: 无符号大于等于则跳转(Jump to if Above or Equal)JGE: 有符号大于等于则跳转(Jump to if Greater orEqual) JNAE: 无符号不大于等于则跳转(Jump to if not Above orEqual (即 JB) )JNGE: 有符号不大于等于则跳转(Jump to if not greaterthan or Equal (即JL) )现在你大致了解了,应该有些疑惑。 Jump to if greater
11、或者JG都是条件跳转。 条件跳转都如同它们名字所描述的那样,会在条件成立的情况下跳转。通常,都会有CMP或者其他的比较函数在上面,这个待会再提。以上大抵就是JMP的故事。- III.b MOV - 前文已经提过, MOV 命令是最被广泛实用的命令,因为它是那么给力。 下面就是MOV的例子:Code: mov eax,ebx 这个的意思是 把ebx里存的地址放到eax里。注意在两个寄存器之间还有个逗号,而不是空格。 最开始这可能有些令人费解,但是它真的很容易,下面来好好解释一下。Mov是move的意思。 寄存器自身表示这个寄存器里存的地址。 基本上来说, 把ebx里存的地址放到eax的地址里面去
12、, 这会把eax之前的值覆盖并彻底消灭掉。=D你可以通过这个方法复制ebx并把它粘贴到eax里。另一种Mov命令。Code: mov eax,ebx 这个的意思是把ebx里面的值存到eax所存的地址里,很简单, 把寄存器或者地址用那么一括,就表示寄存器或者地址的值了。下面这段代码什么效果都没有。 Code:mov eax,ebx 没效果,上面的代码啥用没有。你不能!你不能直接把一个值存在另一个值里面。你可以间接得先把值存在其他的东西里 ;) 参考下面的代码。Code: push eax /先让eax入栈这个待会解释 mov eax,0100200A /把值0100200A存到eax里 mov
13、ebx,eax /把(值0100200A) 存到ebx的值里面pop eax /然后让eax出栈,这个也待会讲 解释这个是为了MOV函数。继续. - III.c Push and Pop + The Stack - 你已经看了上面的Push和Pop 命令的用法。等等!我是尤里!我能读你的思想!Youwrote: 那个push和pop都是干啥的?还有啥是stack? 好,你读了前文, 在Push之后,我是这么注释的。Code: /Push eax onto the stack (让eax入栈) 这个基本上就是把eax存到堆栈里。而pop eax 基本上就是把eax从堆栈中清除。CEF的Skyon
14、e写了一个非常非常好的关于堆栈的解释,我甚至无法容忍自己不让你像我那样获得对此学习的快乐;) Skyonewrote:堆栈是用来存放可变的空白的值。它就如同: 你有用来做作业的一张纸, 但是之后你的朋友打电话叫你给Phil打电话,他的电话是 555-6405。 你慌了,没地方记啊,所以你把它写在了作业本上了。之后你撂下电话, 就可以把Phils电话写在电话薄上了。 在写完号码之后, 你就把号码从作业本用橡皮擦掉了, 然后打电话给Phil,并且继续做作业。把值push to stack(入栈)就是 在作业本上写下Phil的电话号码 而pop就是 把电话号码录入到电话薄上,然后再从作业本上擦掉。
15、继续. - III.d Alloc/Label/RegisterSymbol - 我一般喜欢用两种方法来区分Auto Assembler脚本 1) 简单的地址改变 如同下面的简单代码: Code: enable 00ABC123: mov eax,ebx disable 00ABC123: mov ebx,eax 这是在改变地址00ABC123里面的操作码。但是也有非常复杂的脚本,就如同dICE 或 pID00的脚本。 (我不会在这里介绍, 因为那真的没什么必要去学) 在非常复杂的脚本当中,你会在最顶上看到某些函数,(注意,我并没有经常使用)在一个复杂的脚本当中通常会出现alloc,label
16、函数,有时还会出现registersymbol。 Label 我认为label函数是三个函数中最有重要的。 他能够允许你用在脚本中使用label定义的变量,如同下面这样。 (摸丸:label用于定义用于跳转的标签)Code:label(Continue) AutoAssembler: mov eax,02 cmp eax,03 jne Continue je SayWhat Continue: /. /. SayWhat: jmp 0 /insta-crash! =D 首先应该注意此脚本的一些问题,这个脚本没有 alloc 函数, 之后我就讲接着,如果你明白为什么程序执行了对应jne的labe
17、l,那你做得不错。注意所有的labels(跳转标签)都在我的脚本中定义了,否则他们不会编译。 Alloc 现在开始讲解alloc函数。 它是用来按照你的设想来分配X量的内存的函数 (hence,alloc,allocate)。 这些内存不是那些用过的, 而是那些你在运行程序中可以覆盖的没有被使用的内存。 就如同我说的,它会分配X量的内存。 X是个字节数的变量。1024bytes, 或者1 kb(kilobyte) 一般就足够你用了。现在我们了解了alloc函数,让我来演示下如何来使用它,并利用之前所学到的一切。(摸丸:按照尺寸划分一块内存,并给它起个名字)Code: alloc(AutoAss
18、embler,1024) alloc(SayWhat,1024) label(Continue) AutoAssembler: mov eax,02 cmp eax,03 jne Continue je SayWhat Continue: /. SayWhat: jmp 0 注意为什么我不这么做 Code: label(AutoAssembler) 或者Code: label(SayWhat) 因为当用alloc划分出内存的时候,他们在Cheat Engine的词典中就自动得已经定义好了,便于我们调用。 RegisterSymbol 最后,函数registersymbol。 它可以添加你Che
19、at Table中你注册的标志。注意,你始终需要去为它分配内存。让我们在我们的脚本中试一试。Code: alloc(AutoAssembler,1024) alloc(SayWhat,1024) alloc(WooHoo,1024) label(Continue) registersymbol(WooHoo) AutoAssembler: mov eax,02 cmp eax,03 jne Continue je SayWhat Continue: cmp eax,WooHoo SayWhat: jmp 0 Counterparts 现在,让我们来继续最后的一小段: the counterpa
20、rts。 有两种使用AutoAssembler script的方法。第一种,你可以注入一些东西。第二种,你可以把它添加到Cheat Table中。如果你决定要把它填到cheat table (当今大多数脚本都是如此), 那你的代码需要enable和disable两部分代码,如下。 Code: ENABLE alloc(AutoAssembler,1024) alloc(SayWhat,1024) alloc(WooHoo,1024) label(Continue) registersymbol(WooHoo) AutoAssembler: mov eax,02 cmp eax,03 jne C
21、ontinue je SayWhat Continue: cmp eax,WooHoo DISABLE 现在,你会注意到,Disable部分什么也没有,不过一会就不会了!;) 你在Disable部分中所想达到的就是:撤销你在Enable部分中所做的。如何撤销内存分配和注册中的标记。现在,我感觉有个问题要来了Youwrote: 你是怎么办的? 简单来说,我的朋友。通过dealloc和 unregister symbol 函数! 哦,等等又有问题要来了。 那label怎么办? 你问? 嗯,没必要去撤销label。 =) 这意味着在Disable部分中,你大概要从开始那段中扔掉一半的代码! =) 那
22、我们为什么不把学的加进脚本里呢?Code:ENABLE alloc(AutoAssembler,1024) alloc(SayWhat,1024) alloc(WooHoo,1024) label(Continue) registersymbol(WooHoo) AutoAssembler: mov eax,02 cmp eax,03 jne Continue je SayWhat Continue: cmp eax,WooHoo DISABLE dealloc(AutoAssembler) dealloc(SayWhat) dealloc(WooHoo) unregistersymbol(W
23、ooHoo) 哦这儿.还有个问题。Youwrote: 为什么你不用那个1024了? 因为计算机知道你分配的AutoAssembler是大小为1024 bytes 的 (我们就把它当个例子试一试吧),你只是需要deallocAutoAssembler,它就知道应该取消分配AutoAssembler的全部1024 bytes了。(很抱歉,解释的有些迷糊) - III.e Call and Ret - Call 函数和JMP 函数很相似。 唯一的不同是Call有个返回到之前代码的功能。这个简单例子利用了下面的两个函数。Code: mov 0100579C,10/把地址的值改成10cmp 010057
24、9C,0 /把它和0进行比较call NameGoesHere/调用或者跳转到 NameGoesHere jmp 01002FF5 NameGoesHere: dec 0100579C /地址的值减1 cmp 0100579C,0 /跟0进行比较jne NameGoesHere/如果不想等就返回到 /NameGoesHere的开始,否则 继续ret /返回到之前,刚好是调用(call)函数之后- III.f Others - 现在,很显然,在Auto Assembler还有很多很多其他的函数,不然它就是个很局限的语言。我会继续尽我所能解释更多的函数。 (哦,老兄.) Nop Nop: 这个我之
25、前提过。 它意味着 无操作, 大体上它可以抵消它之前的代码。 Inc/Dec Inc: 加一。 用于给寄存器或地址加一。 用法如下。 Code:inc eax Dec: 减一,跟Inc似的 Add/Sub Add: 加法。 用法如下。 Code:add eax,02 /给eax加2,并且将结果保存在eax中 Sub: 减法,用法类似Add Lea 这个有点让人迷糊, 但还是继续吧。看下边的脚本。 Code:mov eax,00123ABC lea ebx,eax+DEF 也许你分不清楚,eax+DEF 是一个指针。 LEA把后面被指向的eax+DEF的地址替换给前面被指向的ebx里。 And/
26、Or/Xor 好,我不知道如何清晰的说明“异或(xor)”在英文中的含义 很多网站都只是用几行字这么描述的. Themwrote:我估计你明白这些逻辑操作符的行为如果谁能给我用英文为XOR做个定义我将感激不尽。即使如此,我还是不需要帮助就能解释它们是如何在汇编中工作的。 ;) 首先,它们都有着类似于add、sub命令一样的语法,如下。Code:and eax,ebx 每个eax 和 ebx 都可能是任何东西。哦不对, 他不可能是 mymom。 -。-; 回到脚本。来处理这个,(对,我知道我们都不是电脑.) 我们先要给两个寄存器值。就把12给eax,把27给ebx吧。下面,我们把它换成二进制的那
27、个零和一的语言。 =O 有问题吗?Youwrote:怎么把十进制转成二进制啊?我的傻朋友啊, 你怎么把你的好友计算器给忘了,用十进制把数输入,然后再点二进制啊! 有些事情需要注意,计算器不允许输入小数,它会简单的四舍五入下然后再转成二进制。我搞出了:Code: EAX =1100 & EBX = 11011 好,还能回想起小学时做的数学填空题吗?让我们在这儿做下。Code: and eax,ebx EAX =1100 EBX = 11011 - 现在,ADD(与)方法是如果都是1则是1,否则就是0。那就让我们来做出这道题吧。Code: and eax,ebx EAX = 01100(加个0更轻
28、松) EBX = 11011 - 01000 现在我们把01000改成十进制,我们就会得到8,那个被存在EAX里的值。接下来, 是OR(或) 函数。 现在让我们用相同的方法来轻松一下Code:or eax,ebx EAX = 01100 EBX = 11011 - OR函数的功能与AND函数正相反。都是0,则得0, 否则就得1。 Code:or eax,ebx EAX = 01100 EBX = 11011 - 11111 把11111转成十进制,那会得到31,是EAX里存的值。最后,是XOR(异或)函数。还是用那个方法。Code:xor eax,ebx EAX = 01100 EBX = 1
29、1011 - 好,XOR函数是如果两个数相同就得0,不同就得1。Code:xor eax,ebx EAX = 01100 EBX = 11011 - 10111 我们把它转换成23,而23就是储存在EAX里的值。顺便一提,无论当我们写的是脚本、代码还是别的什么,你都不需要考虑EAX里面装的什么BlahBlahBlah的东西, 我之所以这么做是让你知道它是如何工作的。- IV. Array of Bytes - 好,想象一下。你已经有了你的代码你的脚本你的地址,你准备开始hack。万事俱备,只欠。哦不! 补丁! 但别慌。现在你不用担心等待别人发布新的内存地址! Youwrote:我慌了?没印象啊
30、.你没慌?! 哦,你还不知道该怎么做好,接下来让我来告诉你,我的朋友1) 打开你的CE并附加上正在Hacking的游戏进程。2) 点作弊表右上方的手动添加地址(AddAddress Manually)。 3) 点那个地址(Address)文本框,并且键入你想获得的AOB码的内存地址。4) 点击下拉框,点开类型(Type)并选择“字节数组(Array of Byte)”5) 在 number of bytes 或是Nr. of bytes键入8。 6) 点确认。 =) 好,现在你就得到了与你地址对应的AOB码了,但是打补丁之后怎么办呢?1) 打开你的CE,然后加载你想hack的游戏。2) 在CE
31、上方中间的搜索部分中选择下拉框中的值类型,并选择字节数组。 3) 现在,键入或者从记事本里把AOB码粘贴到值的框里。 4) 先扫描,然后地址就应该出现了。如果没有一个地址出现,那就把AOB码弄短点 (从后面删掉一组值),然后再扫描。(摸丸:这里还应该勾选上“同时扫描只读内存”) 好,你已经得到了地址. 现在你要拿它做什么呢? 把它转换成你想要的脚本就让我们用 v.38版的Maplestory上帝模式脚本做例子吧。Code:ENABLE 6803ec: je 0068133e DISABLE 6803ec: jne 0068133e 既然你有了新地址,那就在Enable和Disable块中删除6
32、803EC, 并且粘贴上你的新地址。 要确认别在那儿把冒号给忘了。 嗯,这就是AOB(Array of Bytes). - V. Conclusion - .还有很多关于自动汇编的教程! 嘿等等, 我感觉到一个问题要来?Youwrote: 真的就这么简单吗? 就这样? 没有更多的窍门或者其他类似的了吗? 没,就这样! 又一次,我应该引用CE作者的话. 大多数人都认为ASM很难,但事实上,它非常简单。Dark Byte himself 即使如此,在此之后我还有一个关于写脚本的小教程,请稍等, 兄弟! 你没必要读Writinga Script 教程的,那只是个额外的部分,虽然我强烈推荐。 - VI
33、. Credits/Acknowledgements - - Bonus. Writing a Script - 既然你读了教程,那也应该很了解了,但你还是想知道更多。 我明白, 就如同我体会过的遗憾。我不认为有任何关于写脚本的教程是我用的上的。 Code:ENABLE alloc(AutoAssembler,1024) alloc(SayWhat,1024) alloc(WooHoo,1024) label(Continue) registersymbol(WooHoo) AutoAssembler: mov eax,02 cmp eax,03 jne Continue je SayWhat
34、 Continue: cmp eax,WooHoo SayWhat: jmp 0 DISABLE dealloc(AutoAssembler) dealloc(SayWhat) dealloc(WooHoo) unregistersymbol(WooHoo) 还记得之前的这个脚本吗? 现在让我们用我们所学的大量内容把它弄完整。Code: ENABLE alloc(AutoAssembler,1024) alloc(SayWhat,1024) alloc(WooHoo,1024) label(Continue) registersymbol(WooHoo) AutoAssembler: mov
35、eax,02 cmp eax,03 jne Continue je SayWhat Continue: cmp eax,WooHoo SayWhat: jmp 0 DISABLE dealloc(AutoAssembler) dealloc(SayWhat) dealloc(WooHoo) unregistersymbol(WooHoo) 对于脚本你需要有些东西, 一个你试图hack的程序的真实内存的地址。 无论如何, 它都不能是一个随机的地址, 或者是一些你不希望它发生但是会导致其发生的地址(崩溃,也许)。这个地址对于这件事很重要。但是为什么我们需要一个地址? 就如同之前我所说的,alloc
36、 函数在你的程序中分配一块未使用内存。因为这是未使用的,程序不会访问这里,所以,你要让程序访问它。 当你干预这些地址时,你需要注意一些至关重要的事情。第一, 你必须用与源码相同总量的字节。 如何知道一个操作码字节的数量呢? 简单,看在地址和操作码中间,那儿应该有一堆两个一组两个一组的字符,他们被称为Array of Bytes。nop 函数对于这种情况非常有帮助。就如同我之前解释的那样,它的含义是“无操作”。并且它只用一个字节。这意味着,如果你用不了所有的字节位置,那你就可以用nop把它填满。 当填字节时你可能会遇到三种情况。我已经描述了第一种你的代码比源码少的情况。剩下两种情况的一个是,你的
37、代码所占的字节数和源码字节数相等,一切刚刚好。但最后一种情况有些令人困惑,那就是你的代码的字节比源码的多。现在,你的代码的字节会把下面的代码字节覆盖,直到字节的位置够用。为什么我们不玩Minesweeper呢? 一段时间之前,我还在学习写脚本的时候,我写了一小段代码(我还是我,但那时我还知道的很少)。Code:enable alloc(WhatNowMinesweeper,256) /分配. alloc(ChiliDog,4) label(ReturnHere)registersymbol(ChiliDog) /注册. ChiliDog: dd 0 /用于参考引用,表示ChiliDog的值是从0开始01002FF5: /这个地址是为MineSweeper写入时间的 jmp WhatNowMinesweeper /我重写了操作码,让它跳转到我的脚本nop /填充了最后的字节 ReturnHere: /这个是干什么的我待会告诉你 WhatNowMinesweeper: /我的实际代码 push eax /保存eax, 我们不这么做就会有问题 mov eax,ChiliDog /把ChiliDog的值传入
限制150内