#disassemble

相关学习资源

二进制安全相关工具和教程站点

内容体系

  • 基本概念
  • IA-32处理器体系结构
  • 汇编语言基础
  • 数据传送、寻址和算术运算
  • 过程
  • 条件处理
  • 整数算术指令
  • 高级过程
  • 字符串和数组
  • 结构和宏
  • 32位Windows变成
  • 高级语言结构
  • 16位MS-DOS程序设计
  • 磁盘基础知识
  • BIOS程序设计
  • 高级MS-DOS程序设计
  • 高级主题

汇编和二进制基础知识

  • CPU指令的基本单位

    • 1 byte = 8 bit 0~155
    • 1 word = 2 byte 0~65535
    • 1 double word = 2 word 0~4294967295
    • 1 kilobyte = 1024 byte
    • 1 megabyte = 1024 kbyte
  • 总线

    • 数据总线
    • 控制总线
    • 地址总线
  • 地址空间

    • 32位寄存器可以使用0-2^32-1,可对4GB内存进行寻址
  • 寻址方式

    • 段基址*10H+段内偏移地址,形成20位地址
    • 段基址是16的倍数,长度最大不超过64K
  • 寄存器(80386处理器中的寄存器分为8组,每组宽度为32位)

    • 通用寄存器
      • EAX: 累加器 在乘法和除法指令中被自动使用
        • AX(低16位) AH(高8位) AL(低8位)
      • EBX: 基址寄存器
        • BX BH BL
      • ECX: 计数器 循环计数器
        • CX CH CL
      • EDX: 数据寄存器
        • DX DH DL
      • ESI: 源变址寄存器
        • SI
      • EDI: 目的变址寄存器
        • DI
      • EBP: 扩展基址指针寄存器 由高级语言用来引用函数参数和局部变量
        • BP
      • ESP: 栈指针寄存器
        • SP
    • 段寄存器
      • CS:代码段(Code Segment)
      • DS:数据段(Data Segment)
      • ES:附加数据段(Extra Segment)
      • SS:堆栈段(Stack Segment)
      • FS:附加段
      • GS 附加段
    • 指令寄存器
      • EIP 指令指针寄存器,低16位为IP(8086),它存储的是下一条要执行指令的地址。
    • 标志寄存器
      • IOPL(I/O Privilege Level): I/O特权级字段,宽度为2bit,它指定了I/O指令的特权级。如果当前的特权级别在数值上小于或等于IOPL,那么I/O指令可执行。否则,将发生一个保护性异常。
      • NT(Nested Task): 控制中断返回指令IRET,它宽度为1位。NT=0,用堆栈中保存的值恢复EFLAGS,CS和EIP从而实现中断返回;NT=1,则通过任务切换实现中断返回。
      • RF(Restart Flag): 重启标志,它的宽度是1位。它主要控制是否接受调试故障。RF=0接受,RF=1忽略。如果你的程序每一条指令都被成功执行,那么RF会被清0。而当接受到一个非调试故障时,处理器置RF=1。
      • VM(Virtual Machine): 虚拟8086模式(用软件来模拟8086的模式,所以也称虚拟机)。VM=0,处理器工作在一般的保护模式下;VM=1,工作在V8086模式下。
      • PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成
        • CF(Carry Flag): 进位标志位,由CLC,STC两标志位来控制,在无符号算数运算的结果无法容纳于目的操作数中时被设置
        • PF(Parity Flag): 奇偶标志位
        • AF(Assistant Flag): 辅助进位标志位
        • ZF(Zero Flag): 零标志位
        • SF(Singal Flag): 符号标志位
        • IF(Interrupt Flag): 中断允许标志位,由CLI,STI两条指令来控制
        • DF(Direction Flag): 向量标志位,由CLD,STD两条指令来控制
        • OF(Overflow Flag): 溢出标志位
    • 系统地址寄存器
    • 控制寄存器
    • 调试寄存器
    • 测试寄存器

PSW寄存器详解

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
标志位 说明
OF(Overflow Flag)溢出标志 溢出时为1,否则置0
SF(Sign Flag)符号标志 结果为负时置1,否则置0
ZF(Zero Flag)零标志 运算结果为0时ZF位置1,否则置0
CF(Carry Flag)进位标志 进位时置1,否则置0
AF(Auxiliary carry Flag) 辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0
PF(Parity Flag)奇偶标志 结果操作数中1的个数为偶数时置1,否则置0
DF(Direction Flag)方向标志 在串处理指令中控制信息的方向
if(Interrupt Flag)中断标志
TF(Trap Flag)陷阱标志

汇编指令

  • 整数

    • d 十进制
    • b 二进制
    • q/o 八进制
    • h 十六进制
    • r 编码实数
    • t 十进制(可选)
    • y 二进制(可选)
  • 整数表达式优先级

      1. ()
      1. +,- 单目加减
      1. *,/
      1. MOD 取模
      1. +,- 加减
  • 数据传送

    • 算术运算/逻辑运算
    • 移位
    • 控制转移
    • 串操作
    • 高级语言支持
    • 条件字节设定
    • 位操作
    • 处理器控制
    • 保护方式
  • 通用数据传送

    • 累加器专用传送
    • 地址传送
    • 标志传送
  • 伪指令

    • .data 标识包含变量的区域
    • .code 标识程序中包含指令的区域
    • func PROC PROC标识了过程的开始,func可以是任何名字
  • 代码标号

    1
    2
    3
    target:
    mov ax,bx
    jmp target
  • 数据标号

    1
    first BYTE 10
  • 注释

    1
    2
    3
    4
        ;单行注释
    COMMENT ;
    多行注释
    ;
1
2
3
4
5
6
7
8
9
mov        赋值
add 两个值相加
sub 相减
mul 相乘
inc 加1
dec 减1
neg 求相反数(转换成对应的二进制补码,补码可通过将目的操作数的所有数据位取反加1)
jmp 跳转到一个新位置
call 调用一个过程
1
2
3
4
5
6
7
8
9
10
11
12
byte    8位无符号整数
sbyte 8位有符号整数
word 16位无符号整数
sword 16位有符号整数
dword 32位无符号整数
sdword 64位有符号整数
fword 48位整数
qword 64位整数
tbyte 80位整数
real4 32位IEEE短实数
real8 64位IEEE长实数
real10 80位IEEE拓展精度实数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.data
list BYTE 10,20,30,40

;在内存中的形态:
; 0000:10
; 0001:20
; 0002:30
; 0004:50

list BYTE 10,20,30,40
BYTE 50,60,70,80
BYTE 81,82,83,84

;定义字符串
str1 BYTE 'This is a test', 0 ;
str2 BYTE "This is a test ",0dh,0ah, ;CRLF
BYTE "This is a test ",0dh,0ah,0 ;

dup
BYTE 20 DUP(0) ;20字节,全部用0填充
BYTE 4 DUP("stack") ;20字节,"STACKSTACKSTACKSTACK"

word1 WORD 65535 ;最大无符号字
word2 WORD -32768 ;最小无符号字
word3 WORD ? ;未初始化的字

push 压栈
pop 出栈
MOV 赋值
MOVZX 零拓展传送
MOVSX 符号拓展传送

1
2
3
4
5
MOV DL,90H        ;DL=90H
MOVSX AX,DL ;AX=FF90H
MOVZX AX,DL ;AX=0090H
MOVSX ESI,DL ;ESI=FFFFFF90H
MOVZX ESI,DL ;ESI=00000090H

CBW 将字节数据扩展成字,符号位扩展到AH中
CWD 将字数据扩展成双字,符号位放到DX中

1
2
3
MOV AL,70H    ;
CBW ;AX=0070(2byte,1word)
CWD ;DX=0000,AX=0070(4byte,2word)

XCHG 交换两个操作数的数据,支持8位、16位、32位
PUSH 入栈,支持立即数入栈
PUSHA 将8个16位通用寄存器全部入栈,顺序为AX,CX,DX,BX,SP,BP,SI,DI,然后SP指针寄存减16,内容为PUSHA指令执行前的内容
PUSHAD 将8个32位通用寄存器全部入栈,顺序为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,然后ESP的内容是执行指令PUSHAD之前的内容
POP 出栈
POPA 8个16位通用寄存器全部出栈,堆栈指针寄存器不是堆栈中弹出的内容,而是加16之后得到
POPAD 8个32位通用寄存器全部出栈,ESP的内容是执行指令PUSHAD之前的内容
LEA 取有效地址

1
2
3
MOV EAX, 11111111h
MOV EBX, 11111111h
LEA ECX, [EAX+EBX] ;ECX = 22222222h

LDS 装入指针,目的寄存器必须是16位或32位的通用寄存器,操作数必须是内存单元,不能是立即数

1
2
LDS EAX, [1000H];将偏移地址为1000,1001h这两个字节单元的内容送给DS,将1002,1003,1004,1005这四个字节单元的内容送往EAX
LDS AX,[1000H];这表明将偏移地址为1000,1001H这两个字节单元的内容送给段寄存器DS,将偏移地址1002,1003H两个字节单元的内容送往EAX

LES 同LDS,不过段寄存器是ES
LFS 同LDS,不过段寄存器是FS
LGS 同LDS,不过段寄存器是GS
LSS 同LDS,不过段寄存器是SS

LAHF 将标志寄存器的低8位送至AH中,包括SF,ZF,ZF,PF,CF。
SAHF 与LAHF的过程恰好相反
PUSHF 将标志寄存器的EFLAGS低16位内容入栈
PUSHFD 将标志寄存器EFLAGS的内容入栈
POPF 将栈顶的一个字弹出,并将它送到标志寄存器EFLAGS的低16位
POPFD 将栈顶的两个字弹出,并将它送到标志寄存器EFLAGS

OFFSET 取当前内存的地址

1
offset Array

EBP EBP指向栈底
ESP 指向栈区域的栈顶位置
EIP 指向下一个将会被执行的指令

EAX = [AX]+[AH+AL]

db: 声明 1 byte(字节)变量
dw: 声明 2 byte(字节)变量 = 1 word
dd: 声明 4 byte(字节)变量 = 2 word = 1 double word

1
2
3
4
mov eax ebx
mov eax 333h
mov eax [ebx]
mov eax [ebx+66h]

和C语言的对比:

a = b[0x66] => mov eax [ebx+66h]

  • 数据交换

    1
    2
    3
    4
    5
    lea edx, b            ;将edx指向b
    mov [edx], ebx ;赋值a = b
    mov ebx, a ;赋值ebx = a
    mov b,ebx ;赋值a = b
    mov ecx, offset a ;将ecx指向a
  • 条件跳转:
    cmp 比较结果
    jx 检查条件x,有大于、小于、等于三个状态

je 相等时跳转
jne 不相等时跳转
jb、ja 比较无符号数然后决定是否跳转
jl、jg 比较有符号数然后决定是否跳转
jbe 在一个无符号数小于或者等于另一个无符号数时发生跳转
jmp 无条件跳转

1
2
3
4
5
cmp EAX,EBX        ;比较EAX和EBX
jz xxx ;如果相等就跳转到xxx
cafebabe
cmp [ECX], EDX ;比较*ECX和EDX的值
JAE yyy ;如果*ECX>=EDX,就跳转到yyy
  • 函数调用:
    1
    2
    3
    push offset, LibName    ;将字符串偏移量压入堆栈
    call LoadLibrary ;进行函数调用
    mov h,EAX ;将EAX中的值传给变量h

PE、ELF基础知识

  • PE文件结构:
    .text 二进制代码
    .data 初始化的数据块
    .idata 动态链接库等外来函数与文件
    .rsrc 资源

文件偏移地址 = 虚拟内存地址-装载基址-节偏移
= RVA - 节偏移

  • 文件幻数
    Windows PE File
    1
    4d 5a                            MZ
    Jpeg Image File
    1
    ff d8 ff e0 00 10 4a 46 49 46    JFIF
    Java .class file
    1
    ca fe ba be

基本工具

  • PE Tools 分析Windows进程和可执行文件
  • PEiD 识别构建PE文件所使用的编译器
  • nm 检查中间目标文件(拓展名为.o的文件),可以显示符号、函数等信息
    动态链接二进制文件,未定义的符号在C语言共享库中定义
  • ldd 链接器(静态链接、动态链接)
    静态链接:链接器将应用程序的目标文件和所需的库文件组合起来,可执行文件更大
    动态链接:不需要复制库,方便维护,可执行文件更小
    1
    2
    gcc -o test test.c
    gcc -o test test.c --static
    编译好之后可以通过file参数查看类型
    1
    2
    ldd ./test    
    #查看库依赖(mac:otool -L windows:dumpbin)
    `objdump 节头部 程序每节的摘要信息
    专用头部 程序内存分布信息,还有运行时加载器所需的其他信息
    调试信息 程序中的调试信息
    符号信息 类似nm的方式转储符号表信息
    反汇编代码清单 对文件中标记为代码的部分执行线性扫描反汇编
  • dumpbin 从PE文件提取符号、导入函数名、导出函数名和反汇编代码
  • c++filt nm test | grep demo |c++filt
    跟C++函数重载有关的操作,显示重载函数的不同形态
  • strings 提取文件中的字符串内容 strings test
    strings -t 可显示字符串文件的偏移量信息
    strings -e 可搜索更广泛的字符

反汇编器:

1
2
3
msfpayload linux/x86/shell_findport CPORT=4444 R >test
ls -l test
ndisasm -u test

分析网络数据包中可能包含shellcode的计算机网络攻击时,可以采用流式反汇编器来反汇编数据包中包含shellcode的部分

栈帧: 程序运行时栈中分配的内存块,专门用于特定的函数调用。
因为每个递归函数调用都有自己的栈帧,使得递归成为可能。

  • 函数调用步骤:
    1.调用方将被调用方所需的参数放到该函数所采用的调用约定制定的位置
    2.调用方将控制权转交给被调用方
    3.如有必要,为被调用的函数配置栈指针
    4.被调用方为局部变量分配空间
    5.被调用函数执行操作
    6.函数完成操作后,为局部变量保留的栈空间被释放。(逆向执行第4步中的操作)
    7.还原寄存器值
    8.被调用函数将控制权返还给调用方
    9.删除栈中的参数

  • 调用约定: 通过调用函数将函数参数存入栈中,调用函数必须存储被调用函数所需的参数。
    调用约定指定调用方放置函数所需参数的具体位置。
    调用约定可能要求将参数放置在制定的寄存器、程序栈或者寄存器和栈中。
    c调用规定。
    cdecl调用约定规定:
    调用方按从右到左的顺序将函数参数放入栈中。
    在被调用的函数完成操作时,调用方负责从栈中清除参数。


ida基本知识

ida目录结构

  • cfg 配置文件
  • idc 包含内置脚本语言IDC所需的核心文件
  • ids 包含一些符号文件
  • loaders 在加载过程中用于识别和解析PE或ELF等一直格式的IDA拓展
  • plugins 包含专门为IDA提供的附加功能
  • procs 包含已安装的IDA版本所支持的处理器模块
  • sig 包含IDA在各种模式匹配操作中利用的现有代码的签名
  • til 包含一些类型库信息

ida基本操作

  • HKEY_CURRENT_USER\Software\Hex-Rays\IDA IDA注册表项

ida数据库文件

要分析的文件为main.exe
会在目录中生成

  • main.id0 二叉树形式的数据库
  • main.id1 包含描述每个程序字节的标记
  • main.nam 包含于IDA的Names窗口中显示的给定程序位置有关的索引信息
  • main.til til文件用于存储与一个给定数据库的本地类型定义有关的信息
  • main.idb ida数据库文件

IDA创建数据库

从磁盘加载文件,解析文件头信息,创建包含代码或数据的程序块
编译器识别:IDA尝试确定用于创建输入文件的编译器,如果能确定,就可以扫描该编译器使用的样板代码序列
函数参数和局部变量识别:
数据类型信息:

ida桌面

  • IDA View-A
  • Hex View-A Hex View可以右键编辑并提交
  • Hex View-B
  • String Window
  • Names Window F:常规函数
  • L:库函数,通过签名匹配算法来识别
  • I:导入的名称,通常为共享库导入的函数名称。
  • C:命名代码
  • D:数据。已命名数据的位置通常表示全局变量。
  • A:字符串数据。
  • sub_xxxx:地址xxxx处的子例程
  • loc_xxxx:地址xxxx出的e指令
  • byte_xxx:位置xxx处的8位数据
  • word_xxx:位置xxx处的16位数据
  • dword_xx:位置xx处的32位数据
  • unk_xxxx:位置xxxx处的大小未知的数据

ida热键

  • IDA>View:
  • View>Open Subviews 恢复需要的窗口
  • ESC 后退(跳转到上一个)
  • Ctrl+Enter 前进(跳转到下一个)
  • G (GoTo) Jump To Address

加深理解-汇编指令、长度和十六进制的关系

地址 指令 16进制 指令长度
.text:00401010 push ebp 55 1byte
.text:00401011 mov ebp, esp 8B EC 2byte
.text:00401013 sub esp, 44h 83 EC 44 3byte
.text:00401016 push ebx 53 1byte
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×