zoukankan      html  css  js  c++  java
  • 恶意代码分析实战-x86反汇编速成班

    x86反汇编速成

    x86体系结构

    3种硬件构成:

    • 中央处理器:负责执行代码
    • 内存(RAM):负责存储所有的数据和代码
    • 输入/输出系统(I/O):为硬盘、键盘、显示器等设备提供接口

    内存

    一个程序的内存可以分为以下四个主要的节:

    • 栈:栈用于函数的局部变量和参数,以及控制程序执行流。
    • 堆:堆是为程序执行期间需要的动态内存准备的,用于创建(分配)新的值,以及消除(释放)不再需要的值。
    • 代码:代码节包含了在执行程序任务时CPU所取得的指令。
    • 数据:在程序初始加载时放到这里,程序运行时它们并不发生变化。

    指令格式

    一条指令由一个助记符,以及另个或多个操作数组成。

    助记符 目标操作数 源操作数
    mov ecx 0x42

    操作码和字节序

    每条指令使用操作码告诉CPU程序要执行什么样的操作。反汇编将操作码翻译为人类易读的指令。

    指令 mov ecx, 0x42
    操作码 B9 42 00 00 00

    用0x42000000表示值0x42,是因为x86架构使用小端字节序。数据的字节序是指在一个大数据项中,最高位(大端)还是最低位(小端)被排在第一位。

    在大端字节序下,IP地址127.0.0.1会被表示为0x7F000001,而在(本地内存的)小端字节序下,表示为0x0100007F。

    操作数

    操作数说明指令要使用的数据。有以下三种类型:

    • 立即数:一个固定的值,如0x42
    • 寄存数:指向寄存器,如ecx
    • 内存地址:所在的内存地址,一般由方括号内包含值,寄存器或方程式组成,如[eax]

    寄存器

    寄存器是可以被CPU使用的少量数据存储器,x86处理器中有一组寄存器,可以用于临时存储或者作为工作区。

    • 通用寄存器
    • 段寄存器
    • 状态标志
    • 指令指针
    通用寄存器 段寄存器 标志寄存器 指令指针
    EAX [AX, AH, AL] CS EFLAGS EIP
    EBX [BX, BH, BL] SS
    ECX [CX, CH, CL] DS
    EDX [DX, DH, DL] ES
    EBP [BP] FS
    ESP [SP] GS
    ESI [SI]

    所有通用寄存器的大小都是32位,可以在汇编代码中以32位或16位引用。

    有4个寄存器(EAX,EBX,ECX,EDX)还可以8位值的方式引用,从而使用其最低的8位,或次低的8位。例如AL指向EAX寄存器的最低8位,AH指向它的次底8位。

    32bits 1010 1001 1101 1100 1000 0001 1111 0101
    A 9 D C 8 1 F 5
    AX
    1000 0001 1111 0101
    AH AL
    1000 0001 1111 0101
    8 1 F 5

    通用寄存器

    通用寄存器一般用于存储数据或内存地址,EAX通常存储了一个函数调用的返回值。

    标志寄存器

    EFLAGS寄存器是一个标志寄存器。执行期间,每一位表示要么是置位(值为1),要么是清除(值为0),并由这些值来控制CPU的运算,或者给出某些CPU的运算。重要的标志介绍如下:

    • ZF 当一个运算的结果等于0时,ZF被置位,否则被清除。

    • CF 当一个运算的结果相对于目标操作数太大或太小时,CF被置位。否则被清除。

    • SF 当一个运算的结果为负数,SF被置位:若结果为正数,SF被清除。对算术运算,当运算结果的最高位值为1时,SF也会被置位。

    • TF TF用于调式,当它被置位时,x86处理器每次只执行一条指令。

    EIP,指令指针

    EIP寄存器,又称为指令指针或程序计数器,保存了程序将要执行的下一条指令在内存中的地址。EIP的唯一作用就是告诉处理器接下来要做什么。

    简单指令

    mov 指令

    指令 描述
    mov eax, ebx 将EBX中的内容复制至EAX寄存器
    mov eax,0x42 将立即数0x42复制至EAX寄存器
    mov eax,[0x4037C4] 将内存地址0x4037C4的4个字节复制到EAX寄存器
    mov eax,[ebx] 将EBX寄存器指向的内存地址处4个字节复制到EAX寄存器
    mov eax,[ebx+esi*4] 将ebx+esi*4等式结果指向的内存地址处4个字节复制到EAX

    lea指令

    lea指令用来将一个内存地址赋值给目的操作数。

    lea eax,[ebx+8] // 将EBX+8的值给EAX。
    
    mov eax,[ebx+8] // 加载内存中地址为EBX+8处的数据。
    

    mov和lea是等价指令,但是mov是将从内存中获得的值给eax,lea是将内存地址给eax。

    算术运算

    sub eax,0x10 // EAX寄存器值减去0x10
    add eax,ebx  // 将EBX值加入EAX并将结果保存至EAX
    inc edx      // EDX值递增1
    dec ecx      // ECX值递减1
    mul value    // 将eax乘上value,乘法的结果以64位形式分开保存在EDX和EAX中
                 // EDX存储了高的32位,EAX存储低的32位
    
    div value    // 与mul类似,但运算方向正好相反
    shr // 右移
    shl // 左移
    ror和rol与移位指令类似,但移出的那一位会被填到另一端空出来的位上,即右循环移位(ror)会将最低位循环移到最高位;左循环(rol)则相反。
    

    常用的逻辑和移位算术运算指令

    指令 描述
    xor eax,eax 将EAX寄存器清零
    or eax,0x7575 对EAX值进行与0x7575的or操作
    mov eax,0xA 将EAX寄存器左移两位,这两个指令将导致EAX = 0x28
    shl eax,2 因为1010(0xA的二进制表示)左移两位后为101000(0x28)
    mov bl,0xA 将BL寄存器循环移位移两位,这两条指令将导致BL=10000010,因为1010向右循环移位2位为10000010
    ror bl,2

    NOP指令

    NOP什么事情也不做,当它出现时,直接执行下一条指令。nop指令实际上是xchg eax,eax的一个伪名字。不过EAX与它自身的交换等于什么事都没有做。OPCODE是0x90。

    后入先出的结构,用于对栈支持的寄存器包括ESP和EBP。

    ESP是栈指针,包含了指向栈顶的内存地址。一些东西被压人或弹出堆栈时,这个寄存器的值相应改变。

    EBP是栈基址寄存器,在一个函数中会保持不变,因此程序把它当成定位器,用来确定局部变量和参数的位置。

    栈用于短期存储,经常用于保存局部变量、参数和返回地址。主要用途是管理函数调用之间的数据交换。

    函数调用

    1、call等于 push 参数,push当前指令地址(EIP寄存器中的内容)压入栈中。

    2、分配栈中用于局部变量的空间,EBP(基址指针)也被压入栈中。

    3、函数处理部分

    4、调整ESP来释放局部变量的空间,恢复EBP。

    5、通过调用ret指令返回,指令会从栈中弹出返回地址给EIP,程序会从原来调用的地方继续执行。

    栈的布局

    当数据被压入栈时,push eax,ESP会随之减小4。

    当数据被取出时,pop ebx,ESP会增加4

    pushad、pusha 将所有寄存器都压入栈中

    popad、popa 从栈中弹出所有的寄存器

    • pusha 将16位寄存器压入栈中
    • pushad 将32位寄存器压入栈中

    条件指令

    • test 不会修改其使用的操作数,只设置标志位
    • cmp 设置标志位
    cmp dst,src   ZF  CF
     dst == src   1   0
     dst < src    0   1
     dst > src    0   0
    

    分支指令

    • jmp 无条件跳转指令

    • 部分常见跳转指令

    jz/jnz/je/jg/jge/ja/jae/jl/jle/jl/jle/jb/jbe/jo/js/jecxz
    

    重复指令

    重复指令是一组操作数据缓冲区的指令,常见的缓冲区操作指令:

    movsx/cmpsx/stosx/scasx,其中x可以是b,w或者d。分别表示字节、字和双字
    

    这些指令需要一个前缀,用于对长度超过1的数据做操作。movsb指令本身只会移动一个字节,而不使用ecx寄存器。

    rep               // 循环终止条件 ECX=0
    repe,repz         // 循环终止条件 ECX=0 , ZF = 0
    repne,repnz       // 循环终止条件 ECX=0 ,ZF = 1
    

    示例:

    repe cmsb // EDI和ESI设为两段缓冲区的地址,ECX必须被设为缓冲区长度,当ECX=0或者缓冲区不一致的时候,停止比较
    rep stosb //用于一个给定的值初始化一块缓冲区中的字节。EDI包含了缓冲区地址,AL则包含初始值。通常与xor eax,eax一起使用
    rep movsb // 一般用于复制缓冲区中的字节,ESI需要被设为源缓冲区地址,EDI被设为目的缓冲区地址,ECX则必须为要复制的长度。会逐字节复制,直至ECX=0
    rep scasb // 用于一段数据缓冲区中搜索一个字节,EDI需指向缓冲区地址,AL则包含要找的字节,ECX设为缓冲区长度。当ECX=0或找到该字节时,比较停止
    
  • 相关阅读:
    POJ2054 Color a Tree
    洛谷【P1080】国王游戏
    BZOJ1707:[Usaco2007 Nov]tanning分配防晒霜
    BZOJ5281:[Usaco2018 Open]Talent Show
    浅谈二分
    BZOJ1720:[Usaco2006 Jan]Corral the Cows 奶牛围栏
    POJ2018 Best Cow Fences
    洛谷【P2115】[USACO14MAR]破坏Sabotage
    BZOJ 4244 邮戳拉力赛 (DP)
    UOJ #277 BZOJ 4739 [清华集训2016]定向越野 (计算几何、最短路)
  • 原文地址:https://www.cnblogs.com/17bdw/p/10257313.html
Copyright © 2011-2022 走看看