zoukankan      html  css  js  c++  java
  • 逆向入门——汇编基础

    0x00 IA32处理器体系结构

    微机的基本结构

    指令执行周期

    当指令使用了内存操作数时还需要两个额外的步骤:取操作数和存储输出操作数。
    机器指令的执行;

    1. 取指令
    2. 解码
    3. 执行

    操作模式

    保护模式:处理器的基本模式。
    虚拟8086模式:多任务环境中执行是地址模式的软件。
    实地址模式:用于运行那些需要直接访问系统内存和硬件设备的MS-DOS程序。
    系统管理模式:实现电源管理和系统安全等功能的机制。

    基本执行环境

    基本寄存器

    寄存器中数据在内存中存放数据遵循高高低低的原则

    8个通用寄存器

    EAX EBX ECX EDX
    EBP ESP ESI EDI

    6个段寄存器

    一个处理器状态标志寄存器(EFLAGS)和一个指令指针(EIP)寄存器。

    ESP:栈地址寄存器 任意时刻指向栈顶元素
    EBP:扩展帧指针寄存器 指向堆栈上的函数参数和局部变量
    EIP:指令指针寄存器
    EIP寄存器不能作为MOV指令的⽬标操作数

    EFLAGS:由控制CPU的操作或反映CPU某些运算的结果的独立二进制位构成

    状态标志

    Name
    CF 进位标志 进位或借位时CF=1
    AC 辅助进位标志 低4位进位或借位时A=1
    PF 奇偶标志 偶数P=1
    ZF 零标志 结果为0则Z=1
    SF 符号标志 S = 符号位值(补码时0=正,1=负)
    OF 溢出标志 运算结果超界时O=1
    DF Direction Flag
    IF Intertupt Flag
    TF Trace Flag

    内存管理

    实地址模式

    可以寻址1MB内存 0~FFFFF

    20位线性地址

    linear address or abssolute address is 20 bits,range from 0 to FFFFF
    用段-偏移地址表示

    • CS:16位代码段
    • DS:16位数据段
    • SS:16位堆栈段
    • ES,FS,GS可指向其他数据段

    保护模式

    可以寻址4GB内存 0~FFFFFFFF
    段寄存器指向段描述符表,操作系统使用段描述符表定位程序使用的段的位置。

    0x01 汇编语言基础

    补码的表示法

    • 正数的补码:与源码相同
    • 负数的补码:反码加1

    寻址方式


    基本元素

    16进制数第一个是字母时要在前面加0

    指令

    一条汇编指令包括4个部分:

    • 标号(可选)
    • 助记符
    • 操作数
    • 注释(可选)

    INVOKE

    相当于call,调用函数或过程

    伪指令

    伪指令课用于定义变量、宏以及过程,可用于执行命名段以及执行其他与汇编器相关任务。
    .data? :指明未初始化的数据段

    NOP指令

    占用一个字节的存储,什么也不做。

    程序模板

    TITLE Program Template
    ; 程序描述:
    ; 作者:
    ; 创建日期:
    ; 修改:
    ; 日期:  修改者:
    INCLUDE Irvine32.inc
    .data
    	;(在此插入变量)
    .code
    main PROC
    	;(在此插入可执行代码)
    	exit
    main ENDP
    	;(在此插入其他子程序)
    END main
    

    汇编-链接-执行


    定义数据

    字符常量/字符串常量

    • 以单引号或双引号括起来的单个/一串字符
    • 存储为对应字符的ASCII码
    后缀 含义
    d 十进制
    b 二进制
    q 八进制
    h 十六进制

    数据定义语句

    初始值可以用?表示不确定,可以是表达式。
    可以指定多个初始值,用逗号隔开,变量名代表第一个初始值的偏移。

    DUP可以为多个数据项分配存储空间。
    V1 BYTE 10 dup (0)V1占用10个字节空间,初值均为0

    符号常量

    等号伪指令:名字=表达式

    计算数组和字符串大小:
    list BYTE 10, 20, 30
    ListSize = ($ - list)
    
    list word 10,20,30,40
    ListSize = ($-list)/2
    
    myString_len = ($ - myString)
    
    EQU和TEXTEQU伪指令:

    将符号名和整数表达式,文本联系起来。

    name EQU expression
    name EQU symbol
    name EQU <text>
    
    rowSize = 5
    count TEXTEQU %(rowSize * 5)
    move TEXTEQU <mov>
    setupAL TEXTEQU <move al, count> 
    

    setupAL将被汇编成mov al, 10

    0x02 数据传送,寻址,算术运算

    小尾(小端)顺序

    intel处理器使用小端顺序存储,最低字节存储在最低地址单元
    Val DWORD 12345678h

    数据传送指令

    操作数类型

    • 立即操作数(immediate)
    • 寄存器操作数(register)
    • 内存操作数(memory)

    MOV指令
    MOV destination, source

    • 两个操作数尺寸必须一致
    • 不能同时为内存操作数
    • 目的操作数不能是CS, EIP,IP
    • 立即数不能直接送至段寄存器

    MOVZX
    复制较小值至较大值中。
    低八位原样复制,高八位补0扩展,仅适用于无符号整数。
    MOVSX
    低八位原样复制,高八位补F扩展,仅适用于有符号整数。
    LAHF/SAHF
    LAHF将标志局存起EFLAGS的低8位复制到AH寄存器,SAHF是将AH复制到EFLAGS
    XCHG指令
    交换两个操作数的内容。
    XCHG reg, reg
    XCHG reg, mem
    XCHG mem, reg

    算数指令

    名称 作用 影响标志位
    INC 加1 AF OF PF SF ZF 不影响CF
    DEC 减1 AF OF PF SF ZF 不影响CF
    ADD 相加 CF ZF SF OF AF PF
    SUB 相减 CF ZF SF OF AF PF
    NEG 取相反数 CF ZF SF OF AF PF

    加减法影响标志位

    INC和DEC不会影响CF标志位

    NEG影响的标志位和ADD SUB一样

    名称 作用
    CF进位位 无符号数是无溢出
    OF溢出位 有符号数有无溢出
    ZF零标位 判断结果是否为0
    SF符号位 结果正负
    PF奇偶标志 最低有效字节内1的个数是否为偶数
    AC辅助进位标志 最低有效字节的第三位向高位进位

    加减法算术运算指令的操作数自身不区分有无符号数,程序通过判断不同的标志位来实现对有符号数和无符号数的处理。

    和数据相关的操作符和伪指令

    名称 作用
    OFFSET 取偏移地址
    ALIGN 设置对齐值
    PTR 重载默认尺寸
    TYPE 返回单个元素大小
    LENGTHOF 计算数组中元素的数目
    SIZEOF 返回LENGTHOF*TYPE
    LABEL 插入一个标号并赋予尺寸

    加逗号可以多行定义

    LABEL不会分配存储空间

    JMP和LOOP

    JMP

    无条件转移与条件转移

    JMP 目的地址
    功能:接着从目的地址开始执行指令

    • 目的地址一般为标号
    • 通常在当前过程内跳转

    LOOP

    LOOP 目的地址
    功能:将ecx的值减1,接着与0比较,如果不等于0,就执行目的地址开始的指令,如果等于0 ,则不跳转,接着执行紧跟在LOOP指令后的指令

    • 通常,ecx里的值就是循环次数。但如果初值为0,因是先减1再判断是否等于0,所以,实际实际循环次数就是 1 00 00 00 00 H
    • LOOPD也使用ecx控制循环,LOOPW使用cx控制循环。
    • 实模式下,使用的是cx作为控制循环的寄存器

    实例

    数组求和:

    INCLUDE irvine32.inc
    .data 
      vb1 byte  1 , 2 , 3 
    .code
      main proc
          mov esi  , offset vb1
          mov ecx , lengthof vb1
          mov al    , 0 
       L1:
          add  al   , [ esi ]
          add  esi , type vb1
          loop L1
          exit
      main endp
    end main 
    

    复制字符串:

    INCLUDE irvine32.inc
    .data 
      s1  byte  "source string",0
      s2  byte sizeof s1 dup(0) 
    .code
      main proc
          mov esi , 0
          mov ecx , sizeof s1
       L1:
          mov al , s1[ esi ]
          mov s2[esi] , al
          inc esi  
          loop L1
          exit
       main endp
    End main 
    

    寻址方式总结

    操作数寻址方式

    数据寻址的基本方式:

    1. 立即寻址
    2. 寄存器寻址
    3. 存储器寻址

    存储器寻址有六种类型

    用寄存器作为指针并操纵寄存器的值。操作数使用间接寻址则叫间接操作数。

    0x03 过程

    堆栈操作

    运行时栈

    运行时栈是CPU直接管理的内存数组,使用到两个寄存器:SS和ESP

    • 保护模式下,SS是段选择子,应用程序不应该修改它
    • ESP是指向栈的特定位置的一个32位偏移值
    • 一般不会直接修改ESP,而是通过使用CALL,RET,PUSH,POP等指令,由这些指令间接操作ESP。
    • ESP指向最后压入到栈的数据
    • 实模式下,使用的SS和SP

    PUSH
    PUSH r/m16
    PUSH r/m32
    PUSH imm32

    压栈,将操作数放入堆栈中:

    1. 将ESP减4
    2. 将要压入的32位值拷贝到ESP指向的内存。

    对于32位操作数,ESP减4,存到栈中的内容为双字;对于16位操作数,ESP减2,存到栈中的内容为字
    POP
    POP r/m16
    POP r/m32

    出栈,从堆栈中取出操作数放到指令中的操作数中

    1. 将ESP所指向内存中的内容取出放到操作数中
    2. 将ESP加4

    对于32位操作数,是先从栈中拷贝双字到操作数中,然后ESP加4;对于16位操作数,是先从栈中拷贝字到操作数中,然后ESP加2。

    PUSHFD 把32位标志寄存器压入堆栈
    POPFD 从堆栈中弹出32位值到标志寄存器中
    两指令无操作数
    实模式下标志寄存器是16位的,入栈出栈指令分别是PUSHF,POPF。
    PUSHAD 把八个32位通用寄存器按序全部压入堆栈
    POPAD是以上序反序从堆栈中依次弹出值到八个32位通用寄存器中

    过程定义

    PROC

    main proc
    ...
    main endp
    

    一般过程需要返回指令ret,起始过程需要调ExitProcess

    CALL和RET

    call 过程名
    将EIP压栈(即当前CALL指令的下一条指令的地址),然后将过程名所在地址赋给EIP(相当于跳转到过程名所在的代码处)
    RET
    RET指令是从栈中取出32位地址,赋给EIP。

    使用寄存器传递过程参数

    .data
    dArray DD  1, 2 , 3
    dSum  DD ?
    .code
    Main proc
           mov ebx , offset dArray
           mov ecx , lengthof dArray
           call SumOf
           mov dSum, eax
           exit
    Main endp
    SumOf proc
        push ebx
        push ecx
        mov eax , 0 
    L2: add eax , [ebx]
        add ebx , 4
        loop L2
        pop ecx
        pop ebx     
        ret
    SumOf endp
    End main
    

    0x04 条件处理

    布尔和比较指令

    名称 作用
    AND
    OR
    XOR 异或
    NOT
    TEST 与,不改变目的操作数只改变标志位
    BT,BTC,BTR,BTS 求补/清零/置位
    尺寸相同
    AND, OR,XOR总是清除溢出标志和进位标志(CF和OF)
    NOT不影响任何标志位

    实例

    小写转大写:

    同一字母的大写字母和小写字母的ASCII码的区别只在第5位不同,其他各位相同,小写字母第5位为1,大写字母第5位为0
    如要把小写转大写,则可将小写的ASCII码与11011111B相与

    .data
    aName byte “Abraham” , 0
    nameSize=($-aName)-1
    .code
    Main proc
         mov ecx , nameSize
         mov esi , 0
    L1:AND  aName[esi] , 11011111B
         inc esi
         loop L1
    Main endp
    End Main
    
    将0-9之间的整数转换为对应数字符号的ASCII码
    .data
    aNum byte 1,3,2,0
    numSize=($-aNum)-1
    .code
    Main proc
         mov ecx , numSize
         mov esi , 0
    L1:OR  aNum[esi] , 110000B
         inc esi
         loop L1
         exit
    Main endp
    End Main
    

    CMP
    功能:对两个操作数作相减运算,不修改操作数,但会影响标志位。会修改OF、SF、ZF、CF、AF、PF。

    设置和清除单个CPU状态标志

    条件跳转

    基于特定标志位

    为真时跳转 为假时跳转 相关标志位
    JZ JNZ ZF
    JC JNC CF
    JO JNO OF
    JS JNS SF
    JP JNP PF

    基于相等比较

    助记符 描述
    JE 相等跳转 同JZ
    JNE 不相等跳转 同JNZ
    JCXZ CX=0跳转
    JECXZ ECX=0跳转

    基于无符号数比较

    助记符 描述
    JA 大于跳转
    JB 小于跳转
    JAE 大于等于
    JBE 小于等于
    JNA 不大于
    JNB 不小于
    JNBE 同JA
    JNAE 同JB

    基于有符号数比较

    助记符 描述
    JG 大于跳转
    JL 小于跳转
    JGE 大于等于
    JLE 小于等于
    JNG 不大于
    JNL 不小于
    JNLE 同JG
    JNGE 同JL

    实例

    将最小有符号数存到AX:

         Mov ax,v1
         Cmp ax,v2
         JL   L1
         mov ax,v2
    L1:cmp ax,v3
         JL    L2
         mov ax, v3
    L2:
    

    数组的顺序查找
    查找第一个非0值

    INCLUDE Irvine32.inc
    
    .data
    intArray SWORD 0,0,0,0,5,20,35,-12,66,4,0
    noneMsg BYTE "A non-zero value was not found", 0
    .code
    main PROC
    	mov		ebx, OFFSET intArray
    	mov		ecx, LENGTHOF intArray
    L1: cmp		WORD PTR [ebx], 0
    	jne		found
    	add		ebx, 2
    	loop	L1
    	jmp		notFound
    found:
    	movsx	eax, WORD PTR[ebx]
    	call	WriteInt
    	jmp		quit
    notFound:
    	mov		edx, OFFSET noneMsg
    	call	WriteString
    quit:
    	call	Crlf
    	exit
    main ENDP
    END main
    

    条件循环指令

    指令 循环条件
    LOOPZ ECX>0 && ZF=1
    LOOPE ECX>0 && ZF=1
    LOOPNZ ECX>0 && ZF=0
    LOOPNE ECX>0 && ZF=0
    LOOPE和LOOPZ不影响任何状态标志
    .data 
    Array        SWORD    -3,-6,-1,-10,10,30,40,5
    Sentinel   SWORD    0
    .code
    ; …
        mov esi , offset array
        mov ecx , lengthof array
    L1:test word ptr [esi],8000h
         pushfd            ; pushfd不修改标志位
         add esi , type array
         popfd
         loopnz   L1       ; 注意:loopnz不修改标志位
         jnz quit
         sub  esi , type array
    Quit:
    

    0x05 整数算术指令

    移位和循环移位

    指令 含义
    SHL 逻辑左移
    SHR 逻辑右移
    SAL 算术左移
    SAR 算术右移
    ROL 循环左移
    ROR 循环右移
    RCL 带进位的循环左移
    RCR 带进位的循环右移
    SHLD 双精度左移
    SHRD 双精度右移

    逻辑移位和算术移位

    SHL/SAL
    SHL 目的操作数, 移位位数
    功能:对目的操作数执行左移操作,最低位补0,移出的最高位送入进位标志CF,原来的进位位将丢失。SHL和SAL功能完全一样。

    左移的SHL和SAL是等价的。算术移位不改变符号位,逻辑移位可能改变符号位
    SHR
    SHR 目的操作数, 移位位数
    功能:将目的操作数逻辑右移,左边空出的位添0,右边最低位被移出,复制到CF位中
    SHR可以实现无符号数的快速除法

    SAR
    有符号数的快速除法,右移过程中最高位保持不变

    ROL/ROR/RCL/RCR
    移出的位又送回另一端

    SHLD/SHRD

    应用

    BinToAsc PROC  uses eax ebx ecx  esi
     ;将EAX中的数转换成二进制ASCII码存到ESI指向的数组中
      Add esi , 31
      Mov ecx ,32
    Nxt:
        Mov bl,  al
        And bl , 1
        Add bl , 30H
        Mov [esi],bl
        Shr  eax,1
        Dec esi
      Loop nxt
      Ret
    BinToAsc ENDP
    

    乘法和除法指令

    助记符 描述
    MUL 无符号乘法
    IMUL 有符号乘法
    DIV 无符号除法
    IDIV 有符号除法
    应用
    Mov al, 30h
    Mov bl, 4h
    Mul  bl   ;AX =0C0H,CF=0
    
    
    Mov ax , 2000h
    Mov bx ,100h
    Mul  bx   ;DX:AX=0020 0000h,CF=1
    
    Mov al, -4
    Mov bl, 4
    IMUL  bl             ;AX=0FFF0H,CF=0
    
    Mov ax, 30h
    Mov bx, 4h
    IMul bx            ;DX:AX =0C0H,CF=0
    
    Mov al, 48
    Mov bl, 4
    IMUL  bl             ;AX=00C0H(即十进制的192),CF=1
    
    任意进制的码制转换
    .data 
    ASCIICHAR BYTE  '0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX'
    .code
    ToASC PROC  uses eax ebx  ecx  esi  
    ;将EAX中的数按BL中指定的进制数,转换成ASCII字符串放到ESI指向的数组中
       mov ecx , 0   ;
       mov cl , bl      ; movzx  ecx, bl
       add esi , 31
    nxt_ta:
       mov edx , 0 
       div   ecx
       mov bl,ASCIICHAR[edx]
       mov [esi],bl
       dec esi
       cmp eax , 0
       jnz nxt_ta	; jne
       ret 	
    ToASC ENDP
    

    0x06 高级过程

    stack frame

    给子过程传递参数的两种基本方式

    1. 通过寄存器传递
    • 执行效率高
    • 代码可能显得混乱
    • 寄存器数量有限
    mov esi , offset array
    mov ecx,lengthof array
    mov ebx , type array
    call DumpMem
    
    1. 通过堆栈传递
    • 方式灵活通用
    • 效率偏低
    push offset array
    push lengthof array
    push type array
    call DumpMem2
    

    使用堆栈传递参数时压入了两类参数:

    • 值参数(变量或常量的值)
    • 引用/指针参数(变量的地址)
    实例

    传递值

    .data 
    val1 dword 5
    val2 dword 6
    .code
    push val2
    push val1
    call AddTwo
    

    AddTwo(val1,val2)

    传递引用

    .data 
    val1 dword 5
    val2 dword 6
    .code
    push offset val2
    push offset val1
    call AddTwo
    

    AddTwo(&val1,&val2)

    重点:参数访问
    .data 
    Val1 dword 5
    Val2 dword 6
    .code
    Push val2
    Push val1
    Call AddTwo
    …
    AddTwo proc
      push ebp
      Mov  ebp , esp
      mov  eax , [ebp + 12]     ;取得val2
      add  eax ,  [ebp + 8]       ;加上val1
      pop   ebp
      ret
    AddTwo endp
    

    堆栈清理

    因为在调用子过程前,给堆栈压入了一些内容,在子过程返回时,必须调整堆栈指针。

    • 在调用完子过程后通过加法指令改变ESP值
    • 通过 RET imm 指令的形式
      add方法:
    .data 
    Val1 dword 5
    Val2 dword 6
    .code
    Push  val2
    Push  val1
    Call    AddTwo
    Add    esp , 8
    

    ret方法,在子过程中调用:

    .data 
    Val1 dword 5
    Val2 dword 6
    .code
    Push val2
    Push val1
    Call AddTwo
    AddTwo proc
      push ebp
      mov ebp,esp
      mov eax,[ebp+12]
      add eax,[ebp+8]
      pop ebp
      ret  8
    AddTwo endp
    

    采用uses操作符保存寄存器,则要注意uses指令是将寄存器的压栈指令放在子过程的开始,即在堆栈帧里push ebp语句之前,这时,参数偏移地址计算将会受到影响

    0x07 字符串和数组

    CLD 清除方向标志
    STD设置方向标志

    MOVSB,MOVSW,MOVSD

    指令 功能 ESI和EDI修改量
    MOVSB 复制字节 1
    MOVSW 复制字 2
    MOVSD 复制双字 4

    复制双字数组

    .data 
      source dword 20 dup(0ffh)
      target  dword 20 dup(?)
    .code
      ; … 
      cld
      mov ecx , lengthof source
      mov esi , offset source
      mov edi , offset target
      rep movsd    ;将source开始的20个双字复制到target中
      ; …
    

    CMPSB,CMPSW,CMPSD

    指令 操作
    CMPSB 比较字节
    CMPSW 比较字
    CMPSD 比较双字

    单个比较

    .data 
      source dword 1234h
      target  dword  5678h
    .code
      ; …
      mov esi , offset source
      mov edi , offset target
      cmpsd   ;比较双字
      ja L1     ;如果source>targe跳转至L1 
      jmp L2  ;如果source<=target跳转至L2,本例即是
      ; ….
    

    字符串比较

    .data
    CmpsTestSource byte "ABCDE"
    CmpsTestTarget  byte "AB   "
    .code
    CMPSTEST proc
      cld
      mov  esi , offset CmpsTestSource
      mov  edi , offset CmpsTestTarget
      mov  ecx, lengthof CmpsTestSource  ;最多比较次数,此例为5
      repe  cmpsb ; 比较到第三个字母时,因两者不等,重复不再继续,但当前串
                             ; 操作执行完,esi和edi还会增加。所以,最后,esi和edi会指向
                             ; 第四个字母的位置。
      ret
    CMPSTEST endp
    

    SCASB,SCASW,SCASD

    将AL的值与EDI指向的内存内容相比较(相当于cmp AL , [edi]),即相当于是做查找操作,通常会跟重复前缀

    • 如果使用repe前缀,则将查找到EDI开始的内存中第一个不等于AL时中止重复;
    • 如果使用repne前缀,则将查找到EDI开始的内存中第一个等于AL时中止重复;
    • 当然,如果ecx减到0,也会结束查找
      SCASW是用AX作字查找,SCASD是用EAX作双字查找

    扫描一个匹配字符

    .data 
      alpha byte “ABCDEFGH”,0
    .code
      mov edi , offset alpha
      mov al , ‘F’
      mov ecx , lengthof alpha
      cld
      repne scasb   ;不相等则重复,即找到第一个相等的
      jnz quit    ; 如果这个条件满足,表示是找完整个ecx长度,也没有找到
      dec edi   ;回减一,让edi指向找到第一个相等的位置
      …
    Quit:
    

    STOSB,STOSW,STOSD

    把AL/AX/EAX的内容存储在EDI指向的内存单元中,同时EDI的值根据方向标志的值增加和减少。
    Stosb是存储AL,stosw存储AX,stosd存储EAX 使用rep前缀可以对一段内存进行填充

    LODSB,LODSW,LODSD

    将从esi指向的内存内容取出存到累加器中,同时,修改esi的值。
    lodsb是取出一个字节存到AL中,lodsw是取出一个字存到AX中,lodsd是取出一个双字存到EAX中。
    该指令一般不会跟重复前缀

    串操作指令对标志位的影响

    cmpsscas指令会对标志位有影响,影响效果如同CMP指令。
    movs,lods,stos不会影响标志位。

  • 相关阅读:
    mock.js 模拟数据
    pa
    观察者模式
    WebSocket
    Nginx官方文档学习
    Java中文乱码解决
    Jersey+Spring+Maven(转)
    App架构经验总结(转)
    JSONP跨域的原理解析(转)
    mongoDB学习
  • 原文地址:https://www.cnblogs.com/twosmi1e/p/13326235.html
Copyright © 2011-2022 走看看