迷失领域吧 关注:931贴子:19,395
  • 6回复贴,共1

X86指令介绍!

只看楼主收藏回复

首先介绍寄存器。
段寄存器:es cs ss ds fs gs,这个可以自己百度去看介绍。
我只说一点:cs首址,ds数据。
push cs、push ds,这都没问题,
但是pop ds可以,pos cs是不存在的,笑话,从首址寄存器弹出那还怎么执行程序。
浮点寄存器st st(1) st(2) st(2) st(3) st(4) st(5) st(6) st(7)
数据寄存器:eax ecx edx ebx esp ebp esi edi。
它们都能储存16制(X1 X2 X3 X4)四个字节,十进制就反过来转化。
它们具体的定义我觉得没必要去深究,你把它们分三类就好了。
能读取X1、 X2、X1 X2和整个数的寄存器:eax ecx edx ebx。
al 、cl、dl、bl读取X1,ah、bh、ch、dh读取X2,ax、cx、dx、bx读取X1 X2,本身读取全数。
能读取X1 X2和整个数的寄存器:esp ebp esi edi。
sp、bp、si、di读取X1 X2,本身读取全数。
不能随便使用的寄存器:esp ebp。
ebp指向堆栈的底部,esp指向堆栈的顶部,
比如执行了push指令那么一般最好跟随add esp 4,执行两个就add esp 8。
打个比方,堆栈就像我们砌房子,ebp是地基,我们层数增加,那么esp也就跟着增加了。
砌房子地基不好动,但是ebp毕竟指向的数据,熟悉了多少还是能用的,但不熟悉你不单独使用就好了。
于是三类就是(eax ecx edx ebx)(esi edi)(esp ebp)。
在一个程序段(也叫函数)里面,首部有push 寄存器,尾部有pop 寄存器;
比如edi在首部有push,尾部有pop,在中间没看到edi,那么这edi你可以放心大胆的使用。
一般指令有个“,”号,你插入的位置后面紧接着有某个寄存器在逗号前,这也是你可以使用的寄存器。
还是不够用?那再继续说,从你插入位置开始往后看,找到有寄存器在逗号前的,然后再看这个寄存器在这中间有没有出现在逗号后,出现了就表示你不能用,没出现那照样可以使用。
这样继续扩大范围你可以找到所有你能使用的寄存器。
假如你熟悉指令,那还可以挪出寄存器来使用。
当然你熟悉堆栈的用法就不会有没寄存器可用的情况。


1楼2017-11-17 14:16回复
    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指令,那不出错才怪呢。


    2楼2017-11-17 18:03
    回复
      02.传递byte,即传递一个字节。
      传递byte有16个指令集,分别对应al、cl、dl、bl,ah、bh、ch、dh。
      前四个跟后四个效果略有不同,这个在前面说了它们的区别了。
      你一定问了,你这说了al、cl、dl、bl,ah、bh、ch、dh,这不就是八个么?咋来的16个呢?
      传递是双向的,你传给我,我传给你,这不就是16个了?
      16个集分成八组,就跟夫妻配对一样吧。
      mov [eax],al,88 00 跟mov al,[eax] 8A 00 配对,即单字节[eax]=al、al=单字节[eax]。
      我们可以把前者称为写入,后者称为读取。
      物品再从集中随意举例:
      mov [ecx+eax],al跟mov al,[ecx+eax]。
      mov [esi+edx*2+1Ah],al跟mov al,[esi+edx*2+1Ah]。
      mov [ebp+0C],al 跟mov al ,[ebp+0C]
      mov cl,al 跟mov al,cl。
      只要[]符合前面我讲的,那都是可以的。
      可能有人还对[ecx+eax],ecx+eax这样的不清楚。后者表示数据,把数据放到[]内就是地址了。
      03.传递dword:
      传递dword也有16个指令集,分别对应eax ecx edx ebx esp ebp esi edi。
      跟前面一样,区别是前面是对XX来操作,这个是对XX XX XX XX来操作。
      04传递word:
      传递word也有16个指令集,分别对应ax cx dx bx sp bp si di,这个是对XX XX来操作。
      三者唯一的区别就是对象不同,因XX、XX XX 、XX XX XX XX的不同使用相应的数据传递指令。
      数据传递就上面四类常用,你会问还有其他没?
      回答是有!但是你基本用不上,比如mov word ptr [eax], es跟mov es,word ptr [eax]。
      所以你没必要去知道了。


      4楼2017-11-17 19:54
      回复
        我们在进行数据传递时会遇上这样一种情况:比如传递前是XX XX即word,但是我又要[eax+ecx]这样来用它,
        你会说我先mov ax,word []然后直接[eax+edi]=这样不就好了么?
        这个就要说数据传递的弊端了,比如我eax前面使用时存入了01 02 03 04,我要传递的word是55 55
        执行mov ax,word []后eax=55 55 03 04,你用[eax+edi]那么eax参与进去的不是55 55,而是55 55 03 04。
        有的人说有[ax+di]不就好了?哈哈,这是没有的。
        这类处理方式有的游戏的编程者就是用类似and,eax,0XFFFF0000,把后面的高位给清零再说。
        为了方便,于是说到下面两种指令:movzs和movsx。
        它们的目的都是传递的同时寄存器的高位清零,但它们有区别:
        movsx是连带传递前那数据的符号位,除了符号位其他高位清零。
        movzx是不管一切统统清零,除了需要符号一般都用这个啦。
        它们都有16个指令集,8个是把XX传递成XX 00 00 00,8个是把XX XX传递成XX XX 00 00。
        05.零扩展传递movzx:
        movzx eax,byte ptr [ecx],传递单字节[ecx](XX)给eax,传递的同时把原来eax的高位清零,即eax=XX 00 00 00
        movzx eax,cl把ecx(X1 X2 X3 X4)的第一个字节cl(X1)扩展成X1 00 00 00再传递给eax。
        其他八个集使用的接受对象是其他寄存器而已,比如movzx ecx,al。
        指令的逗号前后就象打架,逗号前是挨打对象,逗号后是打人的。
        movzx eax,cx把ecx(X1 X2 X3 X4)的X1 X2给eax,形成eax=X1 X2 00 00.
        movzx eax,word ptr [ecx],双字节XX XX给eax生成eax=XX XX 00 00
        06.符号传递movsx,这个不说了,movzx多传了符号而已,一样16个指令组。
        07.movaps和movups等传递指令,我们用不上,对xmm0~xmm7操作,随着计算机技术发展出现xmm8 xmm9 xmm10等,AVX指令甚至有的指令可以三四个操作数了。


        5楼2017-11-17 21:05
        回复
          还有别的传送指令没?有。
          因为eax是用的最多的,eax就给了6个福利指令,这六个指令在对应的指令组中都能找到效果一样的指令。
          mov large ds:XXXXXXXXh,al A2 XX XX XX XX 跟mov al,large ds:XXXXXXXXh A0 XX XX XX XX
          mov large ds:XXXXXXXXh,eax A3 XX XX XX XX 跟mov eax,large ds:XXXXXXXXh A1 XX XX XX XX
          mov large ds:XXXXXXXXh,ax 66 A3 XX XX XX XX 跟mov ax,large ds:1Ah 66 A1 XX XX XX XX
          效果:在地址[XXXXXXXX]和eax间进行字节、双字、字的相互传递。
          mov large ds:XXXXXXXXh,eax 89 05 XX XX XX XX 表示双字传递[XXXXXXXX]=eax
          在福利指令里面是不是有个一样的?说一样是指令一样,但是码不同,但这不妨碍我们把他们等同起来。
          有很多指令一样但码不同的,还可能意义也不一样,但那些跟我们没关系,对我们来说它们就是一样的。
          比如无偏移中ebp特殊:mov ds:XXXXXXXXh[ecx],eax 89 04 0D 1A 00 00 00
          他表示双字传递:[XXXXXXXX+ecx]=eax。
          你把它反过来:[ecx+XXXXXXXX]=eax,这不就是可以用大偏移里面的基本指令么?
          mov [ecx+XXXXXXXXh],eax 89 81 1A 00 00 00
          []内+前+后意义不同,但那跟我们有什么关系?
          eax的一些福利指令用的是相当频繁的,也是应该去熟悉的。
          mov [ecx+ebx*4+XXXXXXXXh],eax这样的指令我们不知道不熟悉,那很简单,我先运算。
          ebx=ebx*4
          ecx=ecx+ebx
          ecx=ecx+XXXXXXXXh
          [ecx]=eax
          拐弯抹角你还是能达到目的,虽然这弯弯拐的有点远了,但至少你目的达到了。
          mov large ds:XXXXXXXXh,ecx的指令我不用,
          mov eax,ecx
          mov large ds:XXXXXXXXh,eax
          知道eax福利指令和几个基本的寄存器间传递指令,一样实现目的。
          所以我推荐eax福利指令全部记住,包括它的编码。


          6楼2017-11-18 13:39
          回复
            08.lea、les、lds等指令
            这是一类特殊的指令,也是很常用的指令,特别是lea,基本上,没有不用lea的游戏exe。
            lea指令只有八个集,每个集对应一个寄存器,并且八个集没有第四部分。
            你别写出lea eax,ecx出来,lea只有一个形式lea 寄存器,[***]。
            指令组中无偏移的lea有些都基本没用,比如
            lea eax,large ds:XXXXXXXXh,这就是把[XXXXXXXX]这原本应该表示的地址的[]内的东西传递给eax。
            即eax=XXXXXXXX,这个有什么用?直接mov eax,XXXXXXXX就好了。
            lea eax,[ecx+eax]这是eax=ecx+eax,这有什么用?我们用后面说的eax=eax+ecx的加法指令不一样啊。
            但lea eax,[edi+ebx]这样的东西就有用了,效果:eax=edi+ebx。
            你不知道这个指令,那就edi=edi+ebx,eax=edi。
            这你也达到了目的,但是,你觉得一个指令爽还是两个爽呢?
            象上面那样涉及三个寄存器的lea指令可以说是这个指令常用的一个原因。
            在00~FF表示[eax+eax]~[edi+edi*8]时,[]内+前都是单寄存器,
            但是后面有*2、*4、*8这样本属累加的乘法效果,这是这个指令经常被用到的另一个原因。
            lea ecx,[eax+eax*2]
            lea edx,[eax+ecx*4]
            lea ecx,[ecx+edx*2]
            lea eax,[ecx+edx*8]
            执行这四个指令后,你知道现在的eax数据是原来eax数据的多少倍了?来算一算吧
            ecx=3*eax
            edx=eax+(3*eax)*4=eax*13
            ecx=3*eax+(eax*13)*2=eax*29
            eax=eax*29+(eax*13)*8=133*eax
            使用三个寄存器进行lea可以得到任何一个倍数。
            这也是我们修改游戏时明明在游戏中是1000的数据,自己也在exe这里找到了某位置就是这个1000数据的来源,可就是没看到那里有个1000这样的数据。
            有偏移数时,[eax+eax+XX]~[edi+edi*8+XX]。
            lea eax,[edi+ebx*4+xxxxxxxxh]这样的指令,你可以拐弯抹角:
            eax=edi
            ebx=ebx*4
            eax=eax+ebx
            eax=eax+xxxxxxxx
            四条指令一样把这个lea指令的效果做出来了,但是有捷径走又何必呢?
            les、lds等我们很少用,但还是知道它们的意思最好,说不定某个时候碰到。
            les edi,[]。[]=X1 X2 X3 X4。执行les等指令后会把这个数分尸,前两个给了edi,后两个给了寄存器es。


            7楼2017-11-18 14:36
            回复
              游戏修改m嘛,你了解了原来程序作的事情那就可以随意修改了。
              比如我当初修改远程攻击的时候,什么两次射击,不就是把射击函数再用一次而已么?
              那我这样:
              edi=0
              if(edi>5){射击函数。if(目标生物生命值<0)(EB跳出。)edi+1。}
              这就是多次射击了,edi=0,射击。edi=1射击。edi=2射击。edi=3射击。edi=4射击。
              当edi=5时,不满作条件了,循环终止。
              当生物被射死,循环终止。
              只要你明白程序原本效果,作出修改就简单多了。
              比如半随机,到底游戏是如何实现半随机的呢?
              我这样来修改石碑的经验值。
              [ebp+40]=1000,[ebp+44]=1300,[ebp+48]=1700,[ebp+4C]=2500,[ebp+50]=3600,等等
              这是什么意思,这[ebp+3C]是原本程序到达的堆栈(我没去看原程序,这个是我随意弄的),
              我又在堆栈上增加了数据,这里我可以增加10个数据。
              读取英雄的等级,除以10,加上1,把这个数存入edi。(我为什么用edi,因为Heros3很少用edi的)
              这时edi就是等级/10+1,其实就是给等级设定档次的,1 2 3等等这些档次。
              当然你也可以直接对等级开方,不知道开方大不了自己写个函数来实现开方。
              我举几个例子你琢磨一下就知道怎么写开方函数了。
              开方15:1<15 2<7 4≮3则a=4,[15/4+4]/2=[3+4]/2=3<4换a=3,[15/3+3]/2=4≮3则3就是15的开方值。
              开方80:1<80 2<40 4<20 8<10 16≮5则a=16,[80/16+16]/2= 10<16换a=10 [80/10+10]/2=9<10换a=9 [80/9+9]/2=8<9换a=8 [80/8+8]/2=9≮8,那么8就是80的开方值。
              大概10几年前我记得我不知道开方就这样自己写的,篇幅记得不大的。
              回到原来,然后以edi为基础随机,就是取随机数edi=[0,edi),
              这个很多游戏都有这个函数,实在不知道也可以自己写。
              然后eax=[ebp+edi*4+40],把eax替代原来石碑经验值1000。
              这实现什么效果了呢?
              当人物20级时,edi=3,随机后edi就是0~2。
              随机得edi=0时,eax=[ebp+0*4+40]=[ebp+40]=1000,那就是得到1000经验值。
              随机得edi=1时,eax=[ebp+1*4+40]=[ebp+44]=1300,那就是得到1300经验值。
              随机得edi=2时,eax=[ebp+2*4+40]=[ebp+48]=1700,那就是得到1700经验值。
              也就是说石碑在你Lv20去访问时他是1000、1300、1700三个数里面的某一个。
              再说另一类修改,edi=等级/10,我不对edi加随机,直接使用eax=[ebp+edi*4+40],我先不去替代1000。
              而对这个eax加1来随机一次,那eax=[0,eax],就是说eax是0~eax中的某一个数。
              这时候把eax再替代原本的1000,这又是什么效果呢?
              等级=10时,eax==[ebp+1*4+40]=[ebp+44]=1300,+1再随机后,eax是0~1300中的数。
              等级=20时,eax==[ebp+2*4+40]=[ebp+48]=1700,+1再随机后,eax是0~1700中的数。
              这就说你等级越高,随机的基数越高,你Lv10你再怎么读档你也得不到1500经验值。
              但是你Lv20时候你人品好你甚至可以得到1700的经验值。


              8楼2017-11-19 14:53
              回复