适合CE初学者观看
目录
1. 介绍
2. 寄存器
2.a 32位寄存器 2.b 16位寄存器
3. 指令
3.a JMP 3.b MOV
3.c Push/Pop + The Stack
3.d alloc/label/registersymbol 3.e Call and Ret 3.f 其他
4. Array of Bytes 5. 结尾
6. 人员名单/致谢词
额外内容: 写一个脚本
1. 介绍
'哟,我猜你在读这个是因为下面两件事中的一个。
1) 你正在试图学习自动汇编 (我不会叫你菜鸟,因为每个人都是从哪里开始的,对吧 =) ) or
2) 你想测试你的自动汇编知识 (作为扩展)。
那么,如果你是前者,那就慢慢的体会好每个部分,并且在继续进行前确认自己已经明白了
这章。
Dark Byte wrote:
大多数人都认为AA很难,其实它可容易了。
来自CE作者自己的话
如果是后者,那我不会给你提任何的建议,即使我想帮忙。如果你发现有什么错了,或者含糊或者认为我可以做的更好,请告诉我。我一直处于自我学习的状态! 等等!别问,我知道你在想什么。
You wrote:
为什么我非要听一个还在学习的人的话?
好,我来告诉你,我的朋友。即使我仍然在学习,我了解AA,并且我认为与你分享知识是一件很棒的事。 =)
Edit: 这个是很久以前写的,但是现在我学到了很多,并且已经重新检查了。
现在,让我们投身入奇幻的电脑世界
2. 寄存器
这些也许你已经在一些脚本中看到过,它们被非常广泛的使用。有两种寄存器被使用,接下来来进行讲解。
---------------
2.a 32 Bit
---------------
首先,我将解释每个寄存器是如何得到它们的名字的,这会帮助你记住它们哪个是哪个。首先,以E开头 (如果你注意了下面,你会发现所有的寄存器都是以E开头的) 它告诉你这个寄存器是32位寄存器。而A,B,C,D的含义你看完描述就能明显得体会到了。像SI, DI, BP,SP,IP也是一样。在 EAX, EBX, ECX, EDX后面的X,他简单的表示已经没有更多的字母了。有点像一个 NOP 命令 (之后你将读到)。如果你注意了,你会发现每个32位寄存器都是3个字母。
EAX: 累加器(Acculmulator register)。能够用来当存储器
EBX: 从前, 它是个基础寄存器,但现在只是个闲着的存储器 ECX: 计数器(Counting register)。也能用来当存储器
EDX: 数据寄存器(Data register)。 跟之前三个一样,能用来当存储器。
ESI: 源址变址寄存器(SourceIndex register)。 是字符串形式的指针变量,但你现在还不用担心那部分。 能够用来当存储器。
EDI: 目的变址寄存器(DestinyIndex register)。又一次,能够当作存储器,并且是个字符串形式的指针变量, 但别担心。
EBP: 机制指针寄存器(Base Pointer register)。 是用来临时存储ESP, 当然也可以像常规的存储器那样使用。
ESP: 原址指针寄存器(Source Pointer register)。它在堆栈里指向寄存器和地址(这个内容待会再说)。
EIP: 指令指针寄存器(Instruction Pointer register)。 错误的使用会使你正在试图修改的程序崩溃。
---------------
2.b 16 Bit
---------------
16位寄存器和32位寄存器很相似,他们间有两个区别。一是,32位寄存器名字是三个字母,而16位寄存器是两个字母。还有一件事就是16位寄存器比32位寄存器多但别担心。16位寄存器我们一般都用不上。
AX: 参照 EAX BX: 参照EBX CX: 参照ECX DX: 参照EDX SI: 参照ESI DI: 参照EDI BP: 参照EBP SP: 参照ESP IP: 参照EIP
---------------
关于寄存器的内容还很多。 如果你想学习更多关于寄存器的只是, 那就去拜Google大神吧。 对于绝大多数的学习者来说求知欲都是很重要的。
3. 命令
当今,什么语言没有它自己的函数和命令呢? 与英文相比,命令像个单词,而操作代码像个句子。操作代码并不难,比如:
Code:
jmp 00123EAA
关于操作代码你应该知道两点。
首先, 在操作码里,一般都会有个地址或者寄存器,以及一个显而易见的命令。地址是Hex形式的,是Hexadecimal的缩写。Hexadecimal是16进制数。 就如同10进制那样。
按照这种思路想一想。如同上面提到的那样,我们一般使用十进制。这就意味着我们在一个列里不能有“10”, 因为它占了两个地方。 而16进制,10至15都可以放在一个列里头。但是你会发现,“10”并没有出现在列里头。
先冷静, 这是因为在Hex进制里,10是用A表示的,而11是用B,直到15是F。再往后,它就是10,然后11,直到1F, 如果超过了20(十进制)就得从15(十六进制)往后算。有个简单的转换方法就是用操作系统自带的计算器,这里不赘述了。
还有,每一个地址都有它的操作码和字节码。这个字节数就是代表操作码是什么, 每一个命令都一一对应已经定义好的字节码。我知道那些常用函数的字节码是什么,如果你想知道那些字节码的话,你还是指望别人吧,推荐去拜Google大神 =)
最后,想注释很方便。 要注释的话, 就把\"//\"放在命令后边或某块空处,然后打上你想打的。如果你不打 \"//\" ,那电脑就会以为你在打命令呢。
现在,让我们来了解每个命令都是干啥的。
3.a JMP
JMP 命令是最常使用的命令之一 (就如同 MOV 那样, 接下来会讲)。 也许你在操作码和脚本里看到的\"JMP\" 不像\"MOV\" 那么多,那是因为 JMP 命令有很多变种。下面就是列表。
JMP: 始终跳转(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: 无符号不小于则跳转(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\"或者JG都是条件跳转。 条件跳转都如同它们名字所描述的那样,会在条件成立的情况下跳转。通常,都会有CMP或者其他的比较函数在上面,这个待会再提。以上大抵就是JMP的故事。
3.b MOV
前文已经提过, MOV 命令是最被广泛实用的命令,因为它是那么给力。 下面就是MOV的例子: Code:
mov eax,ebx
这个的意思是 \"把ebx里存的地址放到eax里\"。注意在两个寄存器之间还有个逗号,而不是空格。 最开始这可能有些令人费解,但是它真的很容易,下面来好好解释一下。
\"Mov\"是\"move\"的意思。 寄存器自身表示\"这个寄存器里存的地址\"。 基本上来说, \"把ebx里存的地址放到eax的地址里面去\", 这会把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 [ebx],eax //把(值0100200A) 存到ebx的值里面 pop eax //然后让eax出栈,这个也待会讲
解释这个是为了MOV函数。继续...
3.c Push and Pop + The Stack
你已经看了上面的Push和Pop 命令的用法。等等!我是尤里!我能读你的思想!
You wrote:
那个push和pop都是干啥的?还有啥是stack?
好,你读了前文, 在\"Push\"之后,我是这么注释的。 Code:
//Push eax onto the stack (让eax入栈)
这个基本上就是把eax存到堆栈里。而pop eax 基本上就是把eax从堆栈中清除。CEF的Skyone写了一个非常非常好的关于堆栈的解释,我甚至无法容忍自己不让你像我那样获得对此学习的快乐;)
Skyone wrote:
堆栈是用来存放可变的空白的值。它就如同: 你有用来做作业的一张纸, 但是之后你的朋友打电话叫你给Phil打电话,他的电话是 555-6405。 你慌了,没地方记啊,所以你把它写在了作业本上了。 之后你撂下电话, 就可以把Phil's电话写在电话薄上了。 在写完号码之后, 你就把号码从作业本用橡皮擦掉了, 然后打电话给Phil,并且继续做作业。
把值push to stack (入栈)就是 \"在作业本上写下Phil的电话号码\" 而pop就是 \"把电话号码录入到电话薄上, 然后再从作业本上擦掉。\"
3.d Alloc/Label/RegisterSymbol
我一般喜欢用两种方法来区分Auto Assembler脚本
1) 简单的地址改变
如同下面的简单代码:
Code:
[enable] 00ABC123: mov eax,ebx [disable] 00ABC123: mov ebx,eax
这是在改变地址00ABC123里面的操作码。
但是也有非常复杂的脚本,就如同dICE 或 pID00的脚本。 (我不会在这里介绍, 因为那真的没什么必要去学)
在非常复杂的脚本当中,你会在最顶上看到某些函数,(注意,我并没有经常使用)在一个复杂的脚本当中通常会出现\"alloc\",\"label\"函数,有时还会出现\"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的label,那你做得不错。
注意所有的labels(跳转标签)都在我的脚本中定义了,否则他们不会编译。
Alloc
现在开始讲解\"alloc\"函数。 它是用来按照你的设想来分配X量的内存的函数 (hence,alloc,allocate)。 这些内存不是那些用过的, 而是那些你在运行程序中可以覆盖的没有被使用的内存。 就如同我说的,它会分配X量的内存。 X是个字节数的变量。1024bytes, 或者1 kb(kilobyte) 一般就足够你用了。现在我们了解了alloc函数,让我来演示下如何来使用它,并利用之前所学到的一切。(摸丸:按照尺寸划分一块内存,并给它起个名字)
Code:
alloc(AutoAssembler,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\"。 它可以添加你Cheat 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 counterparts。
有两种使用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 Continue je SayWhat
Continue:
cmp eax,[WooHoo] [DISABLE]
现在,你会注意到,Disable部分什么也没有,不过一会就不会了!;)
你在Disable部分中所想达到的就是:撤销你在Enable部分中所做的。 如何撤销内存分配和注册中的标记。现在,我感觉有个问题要来了
You wrote: 你是怎么办的?
简单来说,我的朋友。通过dealloc和 unregister symbol 函数! 哦,等等又有问题要来了。
\"那label怎么办?\" 你问?
嗯,没必要去撤销label。 =) 这意味着在Disable部分中,你大概要从开始那段中扔掉一半的代码! =)
那我们为什么不把学的加进脚本里呢?
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(WooHoo)
.还有个问题。
You wrote:
为什么你不用那个\"1024\"了?
因为计算机知道你分配的AutoAssembler是大小为1024 bytes 的 (我们就把它当个例子试一试吧),你只是需要deallocAutoAssembler,它就知道应该取消分配AutoAssembler的全部1024 bytes了。(很抱歉,解释的有些迷糊)
3.e Call and Ret
Call 函数和JMP 函数很相似。 唯一的不同是Call有个返回到之前代码的功能。这个简单例子利用了下面的两个函数。
Code:
mov [0100579C],10 //把地址的值改成10 cmp [0100579C],0 //把它和0进行比较
call NameGoesHere //调用或者跳转到 NameGoesHere jmp 01002FF5
NameGoesHere:
dec [0100579C] //地址的值减1 cmp [0100579C],0 //跟0进行比较
jne NameGoesHere //如果不想等就返回到
//NameGoesHere的开始,否则 继续 ret //返回到之前,刚好是调用(call)函数之后
3.f Others
现在,很显然,在Auto Assembler还有很多很多其他的函数,不然它就是个很局限的语言。 我会继续尽我所能解释更多的函数。 (哦,老兄...) ```` Nop
`````
Nop: 这个我之前提过。 它意味着 \"无操作\", 大体上它可以抵消它之前的代码。 ````` 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/Or/Xor `````
好,我不知道如何清晰的说明“异或(xor)”在英文中的含义 –很多网站都只是用几行字这么描述的...
Them wrote:
我估计你明白这些逻辑操作符的行为
如果谁能给我用英文为XOR做个定义我将感激不尽。即使如此,我还是不需要帮助就能解释它们是如何在汇编中工作的。 ;)
首先,它们都有着类似于add、sub命令一样的语法,如下。
Code: and eax,ebx
每个eax 和 ebx 都可能是任何东西。哦不对, 他不可能是 \"mymom\"。 -。-;
回到脚本。来处理这个,(对,我知道我们都不是电脑...) 我们先要给两个寄存器值。就把12给eax,把27给ebx吧。下面,我们把它换成二进制的—那个零和一的语言。 =O 有问题吗?
You wrote:
怎么把十进制转成二进制啊?
我的傻朋友啊, 你怎么把你的好友计算器给忘了,用十进制把数输入,然后再点二进制啊! 有些事情需要注意,计算器不允许输入小数,它会简单的四舍五入下然后再转成二进制。 我搞出了:
Code:
EAX = 1100 & EBX = 11011
好,还能回想起小学时做的数学填空题吗?让我们在这儿做下。
Code:
and eax,ebx
EAX = 1100 EBX = 11011
------------
现在,ADD(与)方法是如果都是1则是1,否则就是0。那就让我们来做出这道题吧。
Code:
and eax,ebx
EAX = 01100 (加个0更轻松) 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 = 11011 -----------------
好,XOR函数是如果两个数相同就得0,不同就得1。
Code:
xor eax,ebx
EAX = 01100 EBX = 11011 ----------------- 10111
我们把它转换成23,而23就是储存在EAX里的值。顺便一提,无论当我们写的是脚本、代码还是别的什么,你都不需要考虑EAX里面装的什么BlahBlahBlah的东西, 我之所以这么做是让你知道它是如何工作的。 `````
好,这就是你所有需要知道的命令,(伙计,这可真长...) 让我们开始下一章节吧...
4. Array of Bytes
好,想象一下。你已经有了你的代码你的脚本你的地址,你准备开始hack。万事俱备,只欠……。哦不! 补丁! 但别慌。现在你不用担心等待别人发布新的内存地址!
You wrote:
我慌了?没印象啊...
你没慌?!
哦,你还不知道该怎么做
好,接下来让我来告诉你,我的朋友
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上方中间的搜索部分中选择下拉框中的值类型,并选择字节数组。
3) 现在,键入或者从记事本里把AOB码粘贴到值的框里。
4) 先扫描,然后地址就应该出现了。如果没有一个地址出现,那就把AOB码弄短点 (从后面删掉一组值),然后再扫描。(摸丸:这里还应该勾选上“同时扫描只读内存”)
好,你已经得到了地址... 现在你要拿它做什么呢? 把它转换成你想要的脚本—就让我们用 v.38版的Maplestory上帝模式脚本做例子吧。
Code: [ENABLE] 6803ec: je 0068133e [DISABLE] 6803ec:
jne 0068133e
既然你有了新地址,那就在Enable和Disable块中删除\"6803EC\", 并且粘贴上你的新地址。 要确认别在那儿把冒号给忘了。 嗯,这就是AOB(Array of Bytes)...
5. Conclusion
...还有很多关于自动汇编的教程! 嘿等等, 我感觉到一个问题要来?
You wrote:
真的就这么简单吗? 就这样? 没有更多的窍门或者其他类似的了吗?
没,就这样! 又一次,我应该引用CE作者的话...
\"大多数人都认为ASM很难,但事实上,它非常简单。\"~Dark Byte himself 即使如此,在此之后我还有一个关于写脚本的小教程,请稍等, 兄弟! 你没必要读\"Writinga Script\" 教程的,那只是个额外的部分,虽然我强烈推荐。
6. Credits/Acknowledgements
在给出人员表之前,有很多需要感谢的人。
首先, 首要的,Wizet,他制作了Maplestory所以我才能去hacking。 ; )
第二, Dark Byte,他创建了我们今天hack的基础,并且为Cheat Engine制造了教程。我们都有个开始!
第三, Sponge和Labyrnth帮我做脚本。
第四, 所有教程的作者,我学到了很多知识。 谢谢你们!
人员名单
Scrbly's Tutorial onCodeInjection and AutoAssembler
Idogear's post on Basic Assembly
TheSorc3r3r's tutorial on Beginning Assembly Language
Skyone's Tutorial on BasicAssembly
M3KillU's tutorial on Array ofBytes
Renkokuken's Post on Register Sets
Vrunk's Tutorial on Allocated Memory
Dark Byte's Guide on BasicAssembler
Dark Byte's Guide on theregistersymbol/unregistersymbol functions
Zhoul's \"There's alwaysmore than 1 way to skin a cat\"
嘿, Dark Byte's \"Auto assemblerscripts in cheat tables\"
------------------------------ 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
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 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 函数在你的程序中分配一块未使用内存。因为这是未使用的,程序不会访问这里,所以,你要让程序访问它。
当你干预这些地址时,你需要注意一些至关重要的事情。第一, 你必须用与源码相同总量的字节。 如何知道一个操作码字节的数量呢? 简单,看在地址和操作码中间,那儿应该有一堆两个一组两个一组的字符,他们被称为Array of Bytes。
nop 函数对于这种情况非常有帮助。就如同我之前解释的那样,它的含义是“无操作”。并且它只用一个字节。这意味着,如果你用不了所有的字节位置,那你就可以用nop把它填满。
当填字节时你可能会遇到三种情况。我已经描述了第一种你的代码比源码少的情况。剩下两种情况的一个是,你的代码所占的字节数和源码字节数相等,一切刚刚好。
但最后一种情况有些令人困惑,那就是你的代码的字节比源码的多。现在,你的代码的字节会把下面的代码字节覆盖,直到字节的位置够用。
为什么我们不玩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的值传入eax
mov [0100579C],eax //把存有ChiliDog值的eax存入时间
pop eax //让eax出栈,因为我们已经用完了 jmp ReturnHere //跳转到ReturnHere, 待会解释
[disable]
dealloc(WhatNowMinesweeper) //取消分配内存 dealloc(ChiliDog)
unregistersymbol(ChiliDog) //反注册标识
01002FF5: //这是我修改来跳入我的代码的地址 inc [0100579C] //这是源操作码用以防止崩溃
现在,最最先要做的。我要告诉你\"ReturnHere\"是什么。 如你所知,一个被告知要执行一个操作码的程序进程,他们会自动移动到下一个操作码,然后继续。 这就如同一个无限的轮回。这样,如果我们弄了条死路,即代码之后已经没有代码了,那这个程序几乎就会崩溃。因此, 我们要让它执行过我们的代码之后再返回到调用跳转之后的位置上,这样代码就可以继续了—并且我们也不会崩溃! =D
关于写脚本还有很多内容。 请自由的提问并且评论你是否喜欢。 =)
因篇幅问题不能全部显示,请点此查看更多更全内容