01.赋值传送。
mov al,0Ah B0 0A
mov cl,0Ah B1 0A
mov dl,0Ah B2 0A
mov bl,0Ah B3 0A
mov ah,0Ah B4 0A
mov ch,0Ah B5 0A
mov dh,0Ah B6 0A
mov bh,0Ah B7 0A
前四个只改变X1=0A,对X2~X4不会改动;后四个只改动X2不会改动X1、X3、X4。
mov eax,0Ah B8 0A 00 00 00
mov ecx,0Ah B9 0A 00 00 00
mov edx,0Ah BA 0A 00 00 00
mov ebx,0Ah BB 0A 00 00 00
mov esp,0Ah BC 0A 00 00 00
mov ebp,0Ah BD 0A 00 00 00
mov esi,0Ah BE 0A 00 00 00
mov edi,0Ah BF 0A 00 00 00
这个改变整数X1 X2 X3 X4=0A 00 00 00
mov ax,0Ah 66 B8 0A 00
mov cx,0Ah 66 B9 0A 00
mov dx,0Ah 66 BA 0A 00
mov bx,0Ah 66 BB 0A 00
mov sp,0Ah 66 BC 0A 00
mov bp,0Ah 66 BD 0A 00
mov si,0Ah 66 BE 0A 00
mov di,0Ah 66 BF 0A 00
这个改变为0A 00 X3 X4,
四字节我们叫dword,两个字节叫word,单字节叫byte。
若一个dword指令其编码前加上66那就是变dword为word,对数的操作时相应的也就由原来的XX XX XX XX变XX XX。
同样跨段操作也是在指令编码前加上一个字节,比如加上65就是去gs段了。
接着讲一个同类指令集,指令集分四部分:无偏移,小偏移,大偏移,寄存器。
也可能一个指令集是没有寄存器这部分的,那就只有三个部分。
首先,无偏移。
mov byte ptr [eax],0Ah C6 00 0A
mov byte ptr [ecx],0Ah C6 01 0A
mov byte ptr [edx],0Ah C6 02 0A
mov byte ptr [ebx],0Ah C6 03 0A
mov byte ptr [esi],0Ah C6 06 0A
mov byte ptr [edi],0Ah C6 07 0A
[]内无数值,只有寄存器就是无偏移。
上面就是说单字节(byte)[地址]=10。
这里可能有人发现了,咋没有esp和ebp呢?
别闹笑话写出了mov byte ptr [ebp],0Ah这样的东西。
那有人可能说mov byte ptr [esp],0Ah有没有?
答案是有的,但不是C6 04 0A就是指[esp]=10。
C6 05 0A这原本属于ebp的位置,但是它不是mov byte ptr [ebp],0Ah,
而是变化为:C6 05 XX XX XX XX 0A ,
即mov large byte ptr ds:XXXXXXXXh,0Ah,表示地址[XXXXXXXX]=10。
当然这指令显示嘛,有的软件会不同,但是编码和指单字节[XXXXXXXX]=10这是绝对的。
C6 04原本esp的地盘,用来干嘛了?用于指令扩展了,扩展成C6 04 XX 0A 了,XX=00~FF。
当XX=00,eax+eax,即mov byte ptr [eax+eax],0Ah ;
当XX=FF,edi+edi*8,即mov byte ptr [edi+edi*8],0Ah 。
00~FF间就是其他组合,其实就是两个寄存器之间的累加。
两个寄存器可以同为一个但不能出现第三个寄存器。
在这扩展指令中,ebp和esp又是特例。
在这形如A+B*n的样式里面,A不能是ebp,B不能是esp。
你别写出[ebp+edi*8],[eax+esp*4]这样的东西,写了那就笑话了。
A是ebp时就跟前面ebp一样用地址代替了,即[XXXXXXXXh+edi*8]。
B是esp时直接当没有,[eax+esp*4]就是[eax]。
例子1,mov byte ptr ds:XXXXXXXXh[edx*2],0Ah C6 04 55 XX XX XX XX 0A,表示单字节[XXXXXXXXh+edx*2]=10。
例子2,C6 04 24 0A,mov byte ptr [esp],0Ah,原本是[esp+esp],但你写成这样那就是错误。
这里你应该也知道了:[esp+ebp]是合法的,[ebp+esp]是非法的。
这里也回答了我前面的问题,是否有mov byte ptr [esp],0Ah?
这里有人问了,在FF~0这里面,会出现[esp+esp][esp+esp*2][esp+esp*4][esp+esp*8]共四个。
esp在后省略么?那用哪个?基本是第一个。
这里一共7个基本指令+256个扩展指令共263个指令。
合起来可以记作:
mov byte ptr [寄存器],常数h;mov byte ptr [双寄存器累加],常数h,记住ebp、esp的特殊化。
其次,小偏移。
mov byte ptr [eax+XXh,0Ah C6 40 XX 0A
mov byte ptr [ecx+XXh],0Ah C6 41 XX 0A
mov byte ptr [edx+XXh],0Ah C6 42 XX 0A
mov byte ptr [ebx+XXh],0Ah C6 43 XX 0A
mov byte ptr [ebp+XXh],0Ah C6 45 XX 0A
mov byte ptr [esi+XXh],0Ah C6 46 XX 0A
mov byte ptr [edi+XXh],0Ah C6 47 XX 0A
表示单字节[变量+XX]=10.
这里看过前面的,一眼就发现,ebp不特殊了,嗯,没错,ebp不特殊了。
小偏移、大偏移时ebp都没有特殊化。
有前面铺垫,这里说起来就简单了。
记法:mov byte ptr [寄存器+偏移],常数h。
扩展指令:mov byte ptr [双寄存器累加+偏移],常数h,esp跟前面一样特殊。
扩展时编码都是在原来基础上加一个字节作00~FF,这里即C6 44 YY XX 0A ,YY位置拓展,XX小偏移量。
[ebp+ecx*2]在前面说了是非法的,但是这里ebp不特殊了,[ebp+ecx*2+XXh]这是合法的。
因为[ebp]不存在,我们恰恰要用到这个东西了,咋办呢?很简单,在这里就有了。
[ebp]不存在,但是[ebp+0]存在啊,所以看到[ebp+0]你不要说这编程的是不是**啊,+0还加个屁啊,
那是因为你不知道原因,知道了,你会大呼,啊,原来如此啊。
[ebp+XXh]就是堆栈的表示法,当然也有的编程会用[esp+XXh],在这里也就是扩展指令下的C6 44 24 XX 0A。
但是要记住,[esp+XXh]时XX越大越靠近底部,[ebp+XXh]XX越小越靠近底部。
毕竟它们本身就是顶部和底部的区别。
我们修改时候,先看程序的堆栈的顶部到了什么位置,然后堆栈就可以随心所欲的操作了。
比如这个函数到了[ebp+4C],然后[ebp+50][ebp+54]等等就可以随意让你使用了,
用来作数值中转或者保存暂时用不到的数据但后面要用的,这是不二之选。
关于函数堆栈到了哪,别的软件我不熟悉,但是ida就清清楚楚明明白白的了,至于自己去确定我怕搞错。
再个,大偏移。
大偏移就更没有好说的了,小嘛大嘛,这就表示了它们的关系。
所以你可以把小偏移和大偏移合并成一个项目就是有偏移,但记住有偏移时有XXh和XXXXXXXXh两种。
最后,寄存器。这个是单独对寄存器的操作。
mov al,0Ah C6 C0 0A
mov cl,0Ah C6 C1 0A
mov dl,0Ah C6 C2 0A
mov bl,0Ah C6 C3 0A
mov ah,0Ah C6 C4 0A
mov ch,0Ah C6 C5 0A
mov dh,0Ah C6 C6 0A
mov bh,0Ah C6 C7 0A
是不是感觉熟悉?嗯,没错,这个跟前面其实就是一码子事。
这指令我是从来不用的,因为它有三个字节,我修改游戏都会选用能达到目的但字节数最少的方法。
因为修改一个游戏没必要大动干戈去扩充文件,假如游戏函数间的空隙也就是c3后的90或者CC足够的话那还好说,比如Hero3,但是很多游戏哪会给你象Hero3这样的发挥空间?你跳转?你往哪去跳?
简单啊,大不了我不改exe,我改内存算了,那我无话可说。
复杂吧?我扩充文件,先不说技术行不行,行那也不是123就搞定的。
例如mov eax,0Ah B8 0A 00 00 00,当数值不需要很大时我都是xor eax,eax;mov al,XX这样;来的。
即33 C0 B0 01,少用了一个字节,有的数据根本不会用上>127的,完全可以代替。
有人说一个字节能干什么?
简单举个例子,D1 E0,这个是shl eax,1,它改不了啊。
D1 E0这是个整体,你不能动,它又表示了*2,你想改你没数据改。
那附近若恰恰有个B8 64 00 00 00这样的mov赋值的话,这就修改就来了。
xor eax,eax;mov al,XX,33 C0 B0 01 90。
然后把90窜到D1 E0那里,imul eax 2即6B C0 02,替代原来的90 D1 E0(nop shl eax,1).
数据2出来了,想怎么改就咋改了。
再比如25 FF 00 00 00即and eax,0XFF,
这个修改游戏的基本不陌生,目的就是把原来的X1 X2 X3 X4这样的四字节数改变成X1 00 00 00。
知道效果找替代程序嘛,简单啊,movzx eax,al,这一样是把X1 X2 X3 X4变X1 00 00 00
movzx eax,al是0F B6 C0,又是多了两个nop 90 90。
+1很多游戏都是inc eax(40),又或者甚至inc eax(FF C0),这两个指令同指令编码不同效果一样的。
inc附近有and eax,0XFF,窜一下再90 90 40改add eax,1(83 C0 01),来了,数据1出来了。
类似的还有很多很多,前提是你要对指令熟悉,知道怎么用能效果一样但指令码最少。
这里详细介绍了指令集,后面讲到指令集时就都是跟这里讲的是一个模式的。
我详细讲的这个集合你可以称字节赋值或byte赋值。
有字节byte,那自然有双字赋值(dword),单字赋值(word).
mov dword ptr [eax],0Ah C7 00 0A 00 00 00 ,~,mov eax,0Ah C7 C0 0A 00 00 00 等
mov word ptr [eax],0Ah 66 C7 00 0A 00 ,~,mov ax,0Ah 66 C7 C0 0A 00 等
使用的时候跟游戏一致就好了,别你游戏内存用的XX XX表示一个属性,你来个mov dword...,
然后到游戏一看,嗯,咋回事?我只改了一个属性啊,怎么其他也变了!!
又或者用错,进入游戏,等到你对修改处有操作时,碰,冒出错误。
修改时切记一致性,操作对象是字节你就得用字节的指令~~~~~~
比如一些老游戏很多用的是字,如三国英杰传等,你去用dword指令,那不出错才怪呢。