• x80386汇编


    EAX寄存器:EAX称为累加器,常用于算数运算、布尔操作、逻辑操作、返回函数结果等。
    EBX寄存器:EBX称为基址寄存器,常用于存档内存地址。
    ECX寄存器:ECX称为计数寄存器,常用于存放循环语句的循环次数,字符串操作中也常用。
    EDX寄存器:称为数据寄存器,常常和EAX一起使用。

    5、变址寄存器

    变址寄存器
    顾名思义,变址的含义是内存地址会变动的,也就是说变址寄存器中存放在变动的内存地址。80386架构中有两个变址寄存器,分别是ESI和EDI。
    ESI和EDI寄存器:
    ESI:ESI称为源变址寄存器,通常存放要处理的数据的内存地址。
    EDI:EDI称为目的变址寄存器,通常存放处理后的数据的内存地址。
    ESI和EDI寄存器的用途
    ESI和EDI常用来配合使用完成数据的赋值操作,下面是一个ESI和EDI配合使用的例子。
    Rep movs dword ptr [edi],dword ptr [esi]
    上面的指令把ESI所指向的内存地址中的内容复制到EDI所指向的内存中,数据的长度在ECX寄存器中指定。

    6、指针寄存器

    指针寄存器
    80386的指针寄存器有基址寄存器EBP,堆栈指针寄存器ESP和指令指针寄存器EIP。只需要了解基址寄存器EBP和堆栈指针寄存器ESP即可,指令指针寄存器EIP总是指向下一条要执行的指令的地址,一般情况下无需修改EIP。

    EBP和ESP寄存器
    EBP:EBP称为基址寄存器,可作为通用寄存器用于存放操作数,常用来代替堆栈指针访问堆栈中的数据。
    ESP:ESP称为堆栈指针寄存器,不可作为通用寄存器使用,ESP存放当前堆栈栈顶的地址,一般情况下,ESP和EBP联合使用来访问函数中的参数和局部变量。

    EBP和ESP寄存器的用途:
    EBP和ESP常配合使用完成堆栈的访问,下面是一段常见的堆栈访问指令。
    Push ebp
    Mov ebp,esp
    Sub esp,78
    Push esi
    Push edi
    Cmp dword ptr [ebp+8],0


    7、标志寄存器
    标志寄存器EFLAGS一共有32位,在这32位中大部分是保留和给编写操作系统的人用的,一般情况下只需知道32位的低16位中的8位即可,图1-3列出了标志寄存器EFLAGS中需要了解的8个位的位置。
    * OF(Overflow Flag):溢出标志,溢出时为1,否则置0。
    * DF (Direction Flag):方向标志,在串处理指令中控制信息的方向。
    * IF (Interrupt Flag) :中断标志
    * AF (Auxiliary carry Flag) :辅助进位标志,有进位时置1,否则置0。
    * ZF (Zero Flag) :零标志,运算结构为0时ZF位位置1,否则置0。

    * SF (Sign Flag):符号标志,结果为负时置1,否则置0。
    * CF (Carry Flag): 进位标志,进位时置1,否则置0。
    * PF (Parity Flag): 奇偶标志。结果操作数中1的个数为偶数时置1,否则置0。

    EFLAGS寄存器的用途
    正如上面所说EFLAGS是实现条件判断和逻辑判断的一种机制,在汇编语言中一般不直接访问EFLAGS寄存器,而是通过指令的操作隐含访问EFLAGS寄存器,下面是一个利用EFLAGS寄存器的例子。

    Cmp dword ptr [ebp+8],0 ;影响标志CF、ZF、SF、OF、AF和PF
    Jz 00405898 ; 如果ZF等于1,则跳转到00405898

    8、寻址方式及作用

    立即寻址
    示例:
    Mov eax,56H
    作用:通常用于赋值。
    直接寻址
    示例:
    Mov eax,[12558878H]
    作用:通常用于处理变量。
    寄存器寻址
    示例
    Mov eax,[edi]
    作用:地址在寄存器中。
    (4)寄存器相对寻址
    示例
    Mov EAX,[EDI+32H]
    作用:常用于访问数组和结构。
    基址加变址寻址
    示例
    Mov EAX,[EBP+ESI]
    作用:常用于访问数组
    相对基址加变址寻址。
    示例
    MOV EAX,[EBX+EDI-10H]
    作用:常用于访问结构。

    9、数据结构,以及80386寻址方式

    一般高级语言中的数据结构和寻址方式有一定的关系,熟悉这些关系对逆向分析反汇编指令有很大的帮助。表1-1所示为高级语言中的数据结构和80386寻址方式的关系。


    10、学习80386指令-Intel格式和AT&T格式的指令格式。

    80386指令
    观察一下Intel 的80x86 CPU文档手册会发现,Intel的80x86 CPU支持几百条指令,如此多的指令莫说深入研究,连入门恐怕也非易事。
    幸运的是,在这几百条的指令中,常用的也不过几十条而已,Intel 的80x86 CPU之所以支持几百条指令,原因在于Intel 的80x86 CPU为了保持向下兼容的问题,所以从过去到现在的所有指令都包含在CPU里面,例如有8086/8088、80186、80286和80386等。

    Intel格式和AT&T格式(&在英语里读and)
    编写IA-32架构的汇编语言常见有两种格式,一种是Intel格式,另一种是AT&T格式。
    Intel 格式的指令格式。
    指令名称 目标操作数DST,源操作数SRC
    示例代码:
    Mov eax,[edx] //将内存地址为EDX的数据放入EAX寄存器
    Xchg eax,edi //交换EAX和EDI寄存器的值
    Add eax,ebx // 将EAX和EBX相加,结构放回EAX中
    Shl eax,4 //将EAX逻辑左移4位。
    AT&T格式的指令格式。
    指令名称 源操作数SRC,目标操作数DST
    示例代码:
    Mov (%EDX),%EAX //将内存地址为EDX的数据放入EAX寄存器
    XCHG %EDI,%EAX //交换EAX和EDI寄存器的值
    ADD %EBX,%EAX //将EAX和EBX相加,结构放回EAX中
    SHL $4,%EAX //将EAX逻辑左移4位。
    本教程中讲解的汇编语言统一采用Intel 格式的汇编格式。

    11、学习数据传送指令MOV、XCHG效果。

    80386的数据传送指令是为了实现CPU和内存、输入/输出端口之间的数据传送。

    MOV:称为数值传送指令,格式是“MOV DST,SRC”。
    MOV指令将源操作数SRC传送到目的操作数DST中,
    传送的数据格式可以是8字节、16字节和32字节。

    示例代码:
    MOV EAX,56 //将56H立即数传送到EAX寄存器
    MOV ESI,DWROD PTR [EAX*2+1] //将内存地址为EAX*2+1处的4字节数据传送到ESI寄存器。
    MOV AH,BYTE PTR [ESI*2+EAX] //将内存地址为ESI*2+EAX处的8位数据传送到AH寄存器。
    MOV DWORD PTR [ESP+36],EBX //将EBX寄存器的值以4字节传送到堆栈地址为ESP+36所指向的地方。


    XCHG:称为交换指令,XCHG实现寄存器间和内存间的数据交换。
    格式是“XCHG DST,SRC”。XCHG指令交换SRC和DST之间的数据,
    交换的数据可以是8字节、16字节和32字节,其中SRC和DST必须格式相同。

    示例代码:
    XCHG EAX,EDX //将EDX寄存器的值和EAX寄存器的值交换
    XCHG [ESP-55],EDI //将EDI寄存器的值和堆栈地址为[esp-55]处的值交换。
    XCHG BH,BL //将BL寄存器和BH寄存器的值交换。

    12、学习数据传送指令PUSH、POP效果。
    PUSH和POP:称为压入堆栈指令和弹出堆栈指令,格式是“PUSH SRC(源操作数)”和“POP DST(目的操作数)”。
    PUSH指令和POP指令是匹配出现的,上面的代码有多少个PUSH下面的代码就有多少个POP,否则堆栈就会不平衡。
    PUSH指令将源操作数SRC压入堆栈,同时ESP-4,而POP恰恰相反,POP指令从堆栈的顶部弹出4字节的数值然后放入DST。在32位的Windows操作系统上,PUSH和POP指令的操作是以4字节为单位的。
    PUSH和POP指令常用于向函数传递参数。

    示例代码:

    PUSH EAX //将EAX寄存器的值以4字节压入堆栈,同时ESP-4
    PUSH DWORD PTR [12FF8589] //将内存地址为12FF8589所指向的值以4字节压入堆栈,同时ESP-4
    -------------------------
    POP DWORD PTR [12FF8589] //将堆栈顶部的4字节弹出到内存地址为12FF8589所指地方,同时ESP+4
    POP EAX //将堆栈顶部的4字节弹出到EAX寄存器,同时ESP+4

    13、学习地址传送指令 LEA效果。

    80x86有3条地址传送指令,分别是LEA,LDS和LES。其实LDS和LES指令和段寄存器有关,在32位的Windows操作系统上,一般的程序员都不需要管理段寄存器,所以相对而言,LDS和LES寄存器使用得比较少,一般情况下常见的只有LEA指令。
    LEA:称为地址传送指令,格式是“LEA DST,ADDR”。LEA指令将ADDR地址加载到DST,其中ADDR可以是内存,也可以是寄存器,而DST必须是一个通用寄存器。
    LEA指令相当于C语言中的“&”and操作符,需要注意的是LEA和MOV是不同的,前者传送的是地址,后者传送的是操作数。

    示例代码:

    LEA EAX,[12345678]
    MOV EAX,[12345678]
    //指令执行后EAX寄存器的值为12345678H,而MOV EAX,[12345678] 指令执行后EAX寄存器的值为内存地址12345678指向的那个数值。

    还有一点需要注意LEA指令可用于算法运算。
    示例代码:
    LEA ECX,[ECX+EAX*4] //ECX=ECX+EAX*4

    14、学习算数运算指令ADD SUB效果。

    80x86提供了8条加减法指令:ADD、ADC、SUB、SBB、INC、DEC、CMP和NEG,4条乘除法指令:MUL、IMUL、DIV和IDIV。

    常用的加减法指令。
    1.ADD:称为加法指令,格式是“add OPER1,OPER2”。(操作数(operand))
    ADD指令将OPER1+OPER2结果存放在OPER1中。

    示例代码:
    ADD EAX,ESI //将EAX寄存器的值加上ESI寄存器的值,得出的结果保存在EAX寄存器中。
    ADD EBX,DWORD PTR [12345678] //将EBX寄存器的值加上内存地址为12345678所指的4字节值,得出的结构保存在EBX寄存器中,其中DWORD PTR的意思是显示说明按多少字节来操作,DWORD是DOUBLE WORD的缩写,也就是两个WORD的意思。

    不同的平台和编译器中,DWORD占用的字节数不同,在32位的Windows中一个WORD占用16字节空间,DWORD占用32字节空间,读者可以在32位的Windows平台上使用Visual C++编译器编写C语言Printf("%d",sizeof(DWORD))来验证。
    在汇编语言中常用的还有WORD PTR和BYTE PTR,表示的意思分别是按WORD来操作和按BYTE来操作。

    SUB:称为减法指令,格式是“SUB OPER1,OPER2”。
    SUB 指令将OPER1-OPER2结果存放在OPER1中。

    示例代码:
    SUB ECX,4H //将ECX寄存器的值减去4H,得出的结果保存在EAX寄存器中。
    SUB BYTE PTR[EAX],CH //将内存地址为EAX所指向的数据按字节为单位和CH寄存器相减,得出的结果按字节为单位保存在EAX所指向的地方。

    15、学习算数运算指令INC DEC效果。

    INC:称为加1指令,格式是“INC OPER”。
    INC指令将操作数OPER加1,得出的结果保存在OPER中。

    示例代码:
    INC EAX //将EAX寄存器的值加1,得出的结果存放在原来的地方。
    INC WORD PTR [EBX+2] //将内存地址为EBX+2的数据按WORD为单位加1,得出的结果存放在原来的地方。

    DEC:称为减1指令,格式是“EDC OPER”。
    DEC指令将操作数OPER减1,得出的结果保存在OPER中。

    示例代码:
    DEC EDX //将EDX寄存器的值减1,得出的结果存放在原来的地方
    DEC DWORD PTR [EBP+36] //将堆栈地址为EBP+36的数据按DWORD为单位减1,得出的结果存放在原来的地方。

    16、学习算数运算指令CMP NEG效果。

    CMP:称为比较指令,格式是“CMP OPER1,OPER2”.
    CMP指令将OPER1减去OPER2,得出的结果不保存,只是相应地设置寄存器EFLAGS的CF、PF、ZF、AF、SF和OF。也就是说可以通过测试寄存器EFLAGS相关的标志的值得知CMP指令执行后的结果。


    示例代码:
    CMP EAX,56H //将EAX寄存器的值减去56H,得出的结果不保存,并且设置寄存器EFLAGS相关的标志位。
    CMP EDX,DWORD PTR [ECX+2] //将EDX寄存器的值以DWORD为单位减去内存地址为ECX+2所指向的数据,得出的结果不保存,并且设置寄存器EFLAGS相关的标志位。

    NEG:称为取补指令,格式是“NEG OPER”。
    NEG指令将OPER操作数取反,简而言之就是将零减去OPER操作数,得出的结果保存在OPER自身中。
    在计算机的CPU中没有减法的机制,减法是用加法实现的,例如:100-55这个操作,CPU实际执行的是100+(-55),而-55相当于是求55的相反数,求相反数的时候NEG指令正好派上用场了。

    示例代码:
    NEG EAX //将EAX寄存器的值取反,得出的结果保存在EAX中
    NEG WORD PTR [12345678] //将内存地址为12345678所指向的数据以WORD为单位取反,得出的结果以WORD为单位,保存在内存地址为12345678

    17、学习算数运算指令 MUL IMUL效果。

    MUL:称为无符号乘法指令,格式是“MUL OPER”。
    MUL指令隐含了一个参加运算的操作数EAX寄存器,MUL指令将EAX寄存器的值乘以OPER,得出的结果保存在EAX寄存器中。如果结果超过32位,则高32位使用EDX寄存器保存,EAX寄存器则保存低32位。

    示例代码:
    MUL EDX //将EAX寄存器的值乘以EDX寄存器的值,得出的结果保存在EAX寄存器中。
    MUL BYTE PTR [EDI] //将EAX寄存器的值乘以以BYTE为单位内存地址为EDI所指向的数据,得出的结果保存在EAX寄存器中。

    IMUL称为有符号乘法指令,原理和操作可以参考MUL指令,IMUL和MUL的区别是IMUL将参与运算的操作数当成有符号数来处理。

    18、学习算数运算指令 DIV IDIV效果。

    DIV:称为除法指令,格式是“DIV OPER”。
    DIV 指令将64位(EDX和EAX)或32位(EAX)的值除以OPER,得出的商保存在EAX寄存器中,而余数则保存在EDX寄存器中,由OPER操作数决定按多少字节操作。

    示例代码:
    DIV ECX //将EAX寄存器的值按4字节为单位除以ECX寄存器的值,得出的结果商保存在EAX寄存器中,余数保存在EDX寄存器中。
    DIV WORD PTR [ESP+36] //将EAX寄存器的值按WORD为单位除以堆栈地址为ESP+36所指向的数据,得出的结果商保存在EAX寄存器中,余数保存在EDX寄存器中。

    IDIV:称为有符号除法指令,原理和操作可以参考DIV指令,IDIV和DIV的区别是IDIV将参与运算的操作数当成有符号数来处理。


    mov eax,1000
    cwd
    div ecx

    1000(H)/7= 4096/7 =585.1

    CWD是汇编语言中的字扩展指令,它的功能是将一个字型变量扩展为双字型变量,即Change Word to Double word。

    19、学习逻辑运算指令OR AND NOT效果。

    80x86提供了OR、AND、NOT/XOR和TEST这5条逻辑运算指令,逻辑运算指令是数据加密/解密的基础,所以应该熟练掌握它们的用法。

    OR:称为或操作指令,格式是“OR OPER1,OPER2”。OR指令将OPER1操作数和OPER2操作数进行或运算,得出的结果保存在OPER1中。
    OR指令主要用于维持某个二进制的某些位的值不变,而另一些位设置为1的情况。把不需要改变的位用0进行或运算,把要设置为1的位用1进行或运算即可。
    示例代码:
    OR EAX,80008000H //将EAX寄存器和立即数80008000H进行或运算,实际上是将EAX寄存器的31位和15位置1
    OR AH,BH //将AH寄存器或BH寄存器进行或运算。

    EAX= 1C1EA4
    OR EAX,80008000H
    得到:eax=801C9EA4

    AH=1 BH=2
    1 二进制 0001
    Or 2 二进制 0010
    0011(也就是10进制3)
    因为第一位是1,第二位是0,而第二个数的第一位是0第二位是1
    根据 or计算原理.1 or 0=1,0 or 0=0,0 or 1=1,1 or 1=1的原理得到的就是0011

    AND:称为与操作指令,格式是“AND OPER1,OPER2”。AND指令将OPER1操作数和OPER2操作数进行与运算,得出的结果保存在OPER1中。
    AND指令主要用于维持某个二进制数的某些位的值不变,而另一些位设置为0的情况。把不需要改变的位用1进行与运算,把要设置为0的位用0进行与运算即可。
    示例代码:
    AND CH,80H //将CH寄存器的值和80H进行与运算,实际上是将CH寄存器的第7位保存不变,其余位置0.
    AND DWORD PTR [EAX],80008000H //将内存地址为EAX所指向的数据按DWORD为单位与80008000H进行与运算,实际上是将内存地址为EAX指向的4字节数据的第31位和15位置保存不变,其余位置0.

    AND CH,80H
    CH=1 二进制 00000001
    80 二进制 10000000
    And 00000000
    根据 and计算原理 0 and 1 =0 ,0 and 0=0,1 and 1=1的原理得到的就是00000000

    AND DWORD PTR [EAX],80008000H
    EAX=001C1EA4
    001C1EC8 二进制_00000000000111000001111011001000
    080008000 二进制10000000000000001000000000000000
    And 00000000000000000000000000000000 (十六进制就是00000000)
    根据 and计算原理 0 and 1 =0 ,0 and 0=0,1 and 1=1的原理得到的就是00000000000000000000000000000000

    NOT:称为取反指令,格式是“NOT OPER”。NOT 指令将OPER操作数取反。注意NOT和NEG不同,NOT指令是按位取反,NEG是求补,意即将0减去操作数。
    例如:
    15H的二进制为 0 0 0 1 0 1 0 1 B
    NEG 15H 的二进制为 1 1 1 0 1 0 1 1 B
    NOT 15H 的二进制为 1 1 1 0 1 0 1 0 B

    根据 NOT计算原理 0 not 1, 1 not 0 得 1 1 1 0 1 0 1 0

    NOT ECX // ECX=1 执行后就是用FFFFFFFF-1=FFFFFFFE

    20、学习逻辑运算指令XOR TEST效果。

    XOR:称为异或操作指令,格式是“XOR OPER1,OPER2”。XOR指令将OPER1操作数和OPER2操作数进行异或运算,得出的结果保存在OPER1中。
    XOR指令主要用于维持某个二进制数的某些位的值不变,而某些位取反的情况。把不需要改变的位用0进行异或运算,把需要取反的位用1进行异或运算即可。

    示例代码:
    XOR EAX,FFFF0000H //将EAX寄存器的值和立即数FFFF0000H进行异或运算,实际上将EAX寄存器的值的高16位取反,低16位保存不变。
    XOR AH,F0H //将AH寄存器的值和立即数F0F0H进行异或运算,实际上是将AH寄存器的值的第15位和7位取反,其余位保持不变。

    EAX=001C1EA4
    001C1EA4_0000000000111000001111010100100
    FFFF0000 _11111111111111110000000000000000
    Xor _11111111111000110001111010100100 (十六进制:FFE31EA4)
    根据 XOR 计算原理:0 xor 0 =0, 1 xor 1 =0,1 xor 0 =1 得到:FFE31EA4

    XOR AH,F0H
    AH=50_01010000
    F0_11110000
    XOR 10100000 (A0十六进制)
    根据 XOR 计算原理:0 xor 0 =0, 1 xor 1 =0,1 xor 0 =1 得到:A0


    TEST:称为测试指令,格式是“TEST OPER1,OPER2”。TEST指令将OPER1操作数和OPER2操作数进行与运算,不保存结果,只设置标志寄存器EFLAGS相应的标志位的值。
    TEST指令常用于测试一个二进制位的某些位是否为1,但不改变源操作数的情况。

    示例代码:
    TEST EAX,F0000000H //将EAX寄存器的值和立即数F00000000H进行与运算,实际上是测试EAX寄存器的第31、30、29、28位是否为1,并且设置标志寄存器EFLAGS相应的标志位的值。


    TEST EAX,F0000000H
    EAX=001C1EA4
    001C1EA4_ 000000000000000111000001111010100100
    F00000000_111100000000000000000000000000000000
    And 000000000000000000000000000000000000

    根据 and计算原理 0 and 1 =0 ,0 and 0=0,1 and 1=1的原理得到的就是 0

    21、学习普通移位指令SAL SAR SHL SHR效果。

    80x86有4条普通移位指令和4条循环移位指令,它们都隐含地使用CF寄存器参与运算。
    普通移位指令。
    SAL算术左移指令、SAR算术右移指令、SHL逻辑左移指令和SHR逻辑右移指令。

    这4条普通移位指令的格式都是一样的:普通移位指令名称 OPER1,OPER2,其中OPER1可以是寄存器或内存,OPER2代表的是移位的位数。其中SAL指令和SHL指令指向结果是一样的。

    对于有符号和无符号数而言,SAL算术左移指令和SHL逻辑左移指令每移动一位相当于乘以2.而SAR算术右移指令和SHR逻辑右移指令有点不同。对于有符号和无符号而言,SAR算术右移指令每移动一位相当于除以2,而SHR逻辑右移指令不管操作数是有符号数还是无符号数,每向右移动一位,左边都是用0填充,所以当操作数是无符号数的时候,SHR逻辑右移指令每移动一位才等于除以2.

    示例代码:
    SAL EAX,2 //将EAX寄存器的值向左移动2位,得出的结果保存在EAX寄存器中,相当于EAX=EAX*4
    SAR DWORD PTR DS:[ESI],4 //将内存地址为ESI所指向的数据按DWORD为单位右移4位,相当于将内存地址为ESI所指向的数据按DWORD为单位的数据除以2*2*2*2=16(十进制)
    SHL DWORD PTR [EBP+2H],2 //将堆栈地址为EBP+2H所指向的数据按DWORD为单位左移2位,相当于将内存地址为EBP+2H所指向的数据按DWORD为单位的数据乘以4.
    SHR EDI,4 //将EDI寄存器的值逻辑右移ECX位。EDI值除以2*2*2*2=16(十进制)

    22、学习循环移位指令ROL ROR RCL RCR效果。

    循环移位指令
    ROL左循环移位指令、ROR右循环移位指令、RCL带进位左循环移位指令和RCR带进位右循环移位指令。

    这4条循环移位指令的格式都是一样的:循环移位指令名称 OPER1,OPER2,其中OPER1可以是寄存器或内存,OPER2要么是CL寄存器要么是1,代表移动的次数,如果要移的次数多于1次,则需要把移位次数存放在CL寄存器中。

    ROL、ROR和RCL、RCR的区别是前者没有将标志寄存器EFLAGS的CF进位标志包含参与循环移位,后者则把CF进位标志包含参与循环移位。

    示例代码:

    ROL AL,1 //将EAX寄存器的值向左移动一位,被移出的位送到CF,同时将被移出的位放到最低位。
    ROR EAX,CL //将EAX寄存器的值向右移动CL位,被移出的位送到CF,同时将被移出的位放到最高位。
    RCL EAX,1 //将EAX寄存器的值向左移动1位,被移出位送到CF,同时将CF之前的值放到最低位。
    RCR EAX,CL //将EAX寄存器的值向右移动CL位,被移出位送到CF,同时将CF之前的值放到最高位。

    ROL AL,1
    ROR EAX,CL
    把82H转成二进制数10000010(B)
    循环左移1位后变成:00000101(B),换算成十六进制数便是05(H)
    循环右移1位后变成:01000001(B),换算成十六进制数便是41(H)


    RCL AL,1
    RCR AL,CL
    首先把82H转换成二进制数10000010B
    带进位循环左移1位后变成:00000100B,CF=1换算成十六进制数便是04H
    带进位循环右移1位后变成:11000001B,CF=0 换算成十六进制数便是C1H

    23、学习条件转移指令 JMP JNZ JE 等效果。

    条件转移指令
    程序的结构可以分为3大部分,分别是顺序结构、分支结构、循环结构。像高级语言一样,在高级语言里有if-else条件分支、do-while循环、for 循环和goto语句来改变程序的执行流程。在汇编语言中没有高级语言里的if-else条件分支、do-while循环和for循环,汇编语言通过提供条件转移指令来实现程序执行流程的改变。

    在汇编语言中条件转移指令分为无条件转移指令和条件转移指令。
    无条件转移指令JMP
    JMP指令格式是:JMP OPER,其中OPER是目的地址。
    示例代码:
    JMP EAX //跳转到EAX寄存器指示的4字节地址。
    JMP DWORD PTR DS:[ESI+2] //跳转到内存地址为ESI+2指示的2字节地址。

    条件转移指令。
    汇编的条件转移指令非常多,通常可以分为3大部分:无符号数的条件转移指令、有符号数的条件转移指令和算术条件转移指令。

    条件转移指令格式是:条件转移指令名称 OPER,其中OPER是目的地址。
    无符号数的条件转移指令名称、转移条件、转移说明如表1-2表示。
    表1-2 无符号数的条件转移说明表

    指令名称
    转移条件
    转移说明
    JA/JNBE
    CF=0 且 ZF=0
    结果低于等于转移,或者高于转移
    JAE/JNB
    CF=0
    结果不低于转移,或者高于转移
    JB/JNAE
    CF=1
    结果低于转移,或者不高于等于转移
    JBE/JNA
    CF=1
    结果低于等于转移,或者不高于转移


    有符号数的条件转移指令名称、转移条件、转移说明如表1-3所示。
    表1-3 有符号数的条件转移说明表
    指令名称
    转移条件
    转移说明
    JG/JNLE
    ZF=0 且SF=OF
    不小于等于转移,或者大于转移
    JGE/JNL
    SF=OF
    不小于转移,或者大于等于转移
    JL/JNGE
    SF≠OF
    小于转移,或者大于等于转移
    JLE/JNG
    ZF=1 且 F≠OF
    小于等于转移,或者不大于转移


    算术条件转移指令名称、转移条件、转移说明如表1-4所示。
    表1-4 算术条件转移说明表
    指令名称
    转移条件
    转移说明
    JZ/JE
    ZF=1
    等于0转移,或者相等转移
    JNZ/JNE
    ZF=0
    不等于0转移,或者不相等转移
    JS
    SF=1
    为负转移
    JNS
    SF=0
    为正则转移
    JO
    OF=1
    溢出转移
    JNO
    OF=0
    不溢出转移
    JC
    CF=1
    进位标志被置转移
    JNC
    CF=0
    进位标志被清转移
    JP/JPE
    PF=1
    偶数转移
    JNP/JPO
    PF=0
    奇数转移


    24、学习函数(子程序)调用指令 CALL RET效果。

    函数调用指令CALL
    在高级语言中,对函数再熟悉不过了,一个程序的功能可以认为是一组函数互相调用的结果,函数是计算并且返回某些值的一段代码。

    在汇编语言中,
    使用CALL指令和RET指令或者CALL指令和ADD ESP,OPER指令
    实现函数的调用与函数返回。

    CALL指令的格式是:CALL OPER,其中OPER是函数地址(子程序地址)
    CALL指令首先将ESP堆栈指针寄存器的值减4,然后将EIP程序指令计数器的值压入堆栈,最后计算函数地址,将当前EIP程序指令计数器的值设置为函数地址。
    例如:调用下面的C语言函数,计算机会执行如下操作:
    PUSH a[i]
    Print("%d",a[i]) : PUSH OFFSET String "%d"
    CALL print
    Add esp,8

    在高级语言中调用函数时不需要编程人员管理堆栈和恢复函数调用前的环境,因为高级语言的编译器在编译源代码的时候已经做好了。

    在汇编语言中,编程人员使用CALL调用完函数后需要使用RET指令或ADD ESP指令恢复函数调用前的坏境,以便调用完函数后程序能继续正常执行。

    RET函数返回指令的格式是:RET OPER,其中OPER是需要从堆栈中弹出的字节数。

    RET指令首先将堆栈中弹出4字节数据到EIP,然后ESP=ESP+2,最后根据OPER的值修改ESP堆栈指针的值ESP=ESP+OPER。
    对于遵从Cdecl调用约定(后面会讲到)的那些函数而言,函数调用完后不是使用RET指令,而是由调用者使用ADD ESP,OPER从堆栈中弹出OPER字节数据来清理堆栈。

    如何判断CALL有多少个参数?
    1.看CALL前面的有几个PUSH,假设有3个,就是有3个参数
    2.进入CALL,看CALL尾部retn指令 是几,假设是:retn 4 说明该CALL有1个参数,假设retn 0c 说明有3个参数,0c(十六进制)=12(10进制),之所以4为一个参数,是因为1个PUSH 占用4字节。

    如果平衡堆栈?
    根据上面的方法可以判断出CALL有几个参数,
    平衡堆栈就是用add esp,参数个数*4再转换成16进制
    假设:有5个参数,5*4=20(十进制) =转换成十六进制(14) 也就是: add esp,14
    Push 1
    Push 2
    Push 3
    Push 4
    Push 5
    Call 00401000
    Add esp,14

    用于:CALL调用后能达到效果,但是游戏奔溃。

    25、学习函数(子程序)调用约定-3种常用的调用约定。

    在早期的编程语言中具有代表性的有C语言、Pascal、Basic、不辛的是这些具有代表性的编程语言各自使用不同的函数参数传递方式和由谁负责清除传递参数时使用的堆栈,俗称堆栈平衡方式,从而导致了有几种调用约定。
    调用约定是规定函数参数传递的顺序和堆栈平衡的方式。常见的函数调用约定有Pascal调用约定、Cdecl调用约定和StdCall调用约定。
    使用Delphi、编写的程序都遵从Pascal调用约定,C/C++/JAVA编写的程序都遵从Cdecl调用约定,Windows的API调用遵从的是StdCall调用约定。

    调用约定的参数传递顺序
    假设有一高级语言函数Message(parameter1,paramerer2,parameter3),按照Pascal调用约定、Cdecl调用约定和StdCall调用约定的参数传递顺序如表1-5所示。
    表1-5 常见的函数调用约定

    Pascal调用约定
    Cdecl调用约定
    StdCall调用约定
    PUSH parameter1
    PUSH parameter3
    PUSH parameter3
    PUSH parameter2
    PUSH parameter2
    PUSH parameter2
    PUSH parameter3
    PUSH parameter1
    PUSH parameter1
    CALL Message
    CALL Message
    CALL Message

    ADD ESP,0CH

    参数从左到右传递,由被调用的函数清理堆栈
    参数从右到左传递,调用函数清理堆栈
    参数从右到左传递,由被调用的函数清理堆栈。

    26.如何对汇编设立不变OD汇编死码与二进制死码。

    为什么要设立?

    因为游戏更新基址,较大的偏移会变化,为了方便,
    所以设立OD死码,方便快速获取游戏更新后的相关数据。
    =======================
    什么是OD汇编死码与OD二进制死码?

    OD汇编死码指的就是OD反汇编中游戏更新后也不变的汇编。
    OD二进制死码指的就是OD反汇编中游戏更新后也不变的二进制。
    =======================

    如何设立OD汇编死码?

    条件:最多8条汇编,因为OD里ctrl+s搜的时候最多只能8条

    哪些汇编不能用于OD死码:call 内存地址
    jz/jnz/je(等跳转语句) 内存地址
    mov eax,[ecx+0abc] 偏移abc比较大的不可以。
    mov eax,[esp+30] 汇编里有ESP堆栈寄存器的不可以,后面跟的偏移+30会变化

    举例:
    LEA EAX,DWORD PTR SS:[ESP+10]
    LEA ECX,DWORD PTR SS:[ESP+40]
    LEA EDX,DWORD PTR SS:[ESP+4]
    PUSH ESI
    PUSH EDI
    PUSH EAX


    =======================
    如何设立OD二进制死码?

    根据上面不能用于死码的情况来设立二进制死码:

    具体设立是:OD中选中一块汇编,右键-二进制-二进制复制。

    快捷键:ctrl+b

    举例:
    0077B832 |. 56 PUSH ESI
    0077B833 |. 57 PUSH EDI
    0077B834 |. 50 PUSH EAX
    0077B835 |. 8B8424 D0000000 MOV EAX,DWORD PTR SS:[ESP+D0]
    0077B83C |. 51 PUSH ECX
    0077B83D |. 52 PUSH EDX
    0077B83E |. 50 PUSH EAX
    0077B83F |. E8 0C9B1200 CALL elementc.008A5350


    56 57 50 8B 84 24 D0 00 00 00 51 52 50 E8 0C 9B 12 00

    56 57 50 8B 84 24 ?? 00 00 00 51 52 50 E8 ?? ?? ?? ??

    =======================

    汇编指令英文全称

    1.通用数据传送指令
    MOV----> move
    MOV dest,src ;dest←src
    MOV指令把一个字节或字的操作数从源地址src传送至目的地址dest。
    MOVSX---->extended move with sign data
    MOVZX---->extended move with zero data
    PUSH---->push
    POP---->pop
    进栈出栈指令
    PUSHA---->push all
    POPA---->pop all
    PUSHAD---->push all data
    POPAD---->pop all data
    BSWAP---->byte swap
    XCHG---->exchange
    交换指令用来将源操作数和目的操作数内容交换,操作数可以是字、也可以是字节,可以在通用寄

    存器与通用寄存器或存储器之间对换数据,但不能在存储器与存储器之间对换数据。
    mov ax,1234h ;ax=1234h
    mov bx,5678h ;bx=5678h
    xchg ax,bx ;ax=5678h,bx=1234h
    xchg ah,al ;ax=7856h
    CMPXCHG---->compare and change
    XADD---->exchange and add
    XLAT---->translate
    换码指令用于将BX指定的缓冲区中、AL指定的位移处的数据取出赋给AL。
    2.输入输出端口传送指令
    IN---->input
    OUT---->output
    3.目的地址传送指令
    LEA---->load effective addres
    有效地址传送指令
    mov bx,0400h
    mov si,3ch
    lea bx,[bx+si+0f62h] ;BX=139EH
    这里BX得到的是主存单元的有效地址,不是物理地址,也不是该单元的内容。
    LDS---->load DS
    LES---->load ES
    LFS---->load FS
    LGS---->load GS
    LSS---->load SS
    4.标志传送指令
    LAHF---->load AH from flag
    SAHF---->save AH to flag
    PUSHF---->push flag
    POPF---->pop flag
    PUSHD---->push dflag
    POPD---->pop dflag
    二、算术运算指令
    ADD---->add
    加法指令 mov al,0fbh ;al=0fbh
    add al,07h ;al=02h
    ADC---->add with carry
    INC---->increase 1
    AAA---->ascii add with adjust
    DAA---->decimal add with adjust
    SUB---->substract
    SBB---->substract with borrow
    DEC---->decrease 1
    NEC---->negative
    CMP---->compare
    AAS---->ascii adjust on substract
    DAS---->decimal adjust on substract
    MUL---->multiplication
    IMUL---->integer multiplication
    AAM---->ascii adjust on multiplication
    DIV---->divide
    IDIV---->integer divide
    AAD---->ascii adjust on divide
    CBW---->change byte to word
    CWD---->change word to double word
    CWDE---->change word to double word with sign to EAX
    CDQ---->change double word to quadrate word

    三、逻辑运算指令
    AND---->and
    or---->or
    XOR---->xor
    NOT---->not
    TEST---->test
    SHL---->shift left
    SAL---->arithmatic shift left
    SHR---->shift right
    SAR---->arithmatic shift right
    ROL---->rotate left
    ROR---->rotate right
    RCL---->rotate left with carry
    RCR---->rotate right with carry
    四、串指令
    MOVS---->move string
    CMPS---->compare string
    SCAS---->scan string
    LODS---->load string
    STOS---->store string
    REP---->repeat
    REPE---->repeat when equal
    REPZ---->repeat when zero flag
    REPNE---->repeat when not equal
    REPNZ---->repeat when zero flag
    REPC---->repeat when carry flag
    REPNC---->repeat when not carry flag
    五、程序转移指令
    1>无条件转移指令(长转移)
    JMP---->jump
    CALL---->call
    RET---->return
    RETF---->return far
    2>条件转移指令(短转移,-128到+127的距离内)
    JA--->jump when above
    JAE---->jump when above or equal
    JNB---->jump when not below
    JB---->jump when below
    JNAE---->jump when not above or equal
    JBE---->jump when below or equal
    JNA---->jump when not above
    JG---->jump when greater
    JNLE---->jump when not less or equal
    JGE---->jump when greater or equal
    JNL---->jump when not less
    JL---->jump when less
    JNGE---->jump when not greater or equal
    JLE---->jump when less or equal
    JNG---->jump when not greater
    JE---->jump when equal
    JZ---->jump when has zero flag
    JNE---->jump when not equal
    JNZ---->jump when not has zero flag
    JC---->jump when has carry flag
    JNC---->jump when not has carry flag
    JNO---->jump when not has overflow flag
    JNP---->jump when not has parity flag
    JPO---->jump when parity flag is odd
    JNS---->jump when not has sign flag
    JO---->jump when has overflow flag
    JP---->jump when has parity flag
    JPE---->jump when parity flag is even
    JS---->jump when has sign flag
    3>循环控制指令(短转移)
    LOOP---->loop
    LOOPE---->loop equal
    LOOPZ---->loop zero
    LOOPNE---->loop not equal
    LOOPNZ---->loop not zero
    JCXZ---->jump when CX is zero
    JECXZ---->jump when ECX is zero
    4>中断指令
    INT---->interrupt
    INTO---->overflow interrupt
    IRET---->interrupt return
    5>处理器控制指令
    HLT---->halt
    WAIT---->wait
    ESC---->escape
    LOCK---->lock
    NOP---->no operation
    STC---->set carry
    CLC---->clear carry
    CMC---->carry make change
    STD---->set direction
    CLD---->clear direction
    STI---->set interrupt
    CLI---->clear interrupt
    六、伪指令
    DW---->definw word
    PROC---->procedure
    ENDP---->end of procedure
    SEGMENT---->segment
    ASSUME---->assume
    ENDS---->end segment
    END---->end

  • 相关阅读:
    4.关于QT中的QFile文件操作,QBuffer,Label上加入QPixmap,QByteArray和QString之间的差别,QTextStream和QDataStream的差别,QT内存映射(
    PlSql加入数据库链接
    UserScan的处理流程分析
    第八十八题(金山笔试题,字符串移动)
    4Sum_leetCode
    LeetCode Add Binary
    Hibernate or JPA Annotation中BLOB、CLOB注解写法
    配置Nginx防止直接用IP訪问Webserver
    Java集合系列之TreeMap源代码分析
    使用Application Loader上传APP流程解读[APP公布]
  • 原文地址:https://www.cnblogs.com/Zidon/p/4662446.html
Copyright © 2020-2023  润新知