Fork me on GitHub

静态分析高级技术


知识点

本章内容为静态分析高级技术,知识点内容较多,很多知识点都是与x86体系结构下的指令等相关知识。

微指令|机器码:微指令层又称为固件,微指令只能在特定的电路上执行,其通常由更高的机器码层翻译而来,提供了访问硬件的接口;机器码层由操作码组成,操作吗是一些十六进制的数字,机器码一般由多条微指令实现,用于告诉底层硬件如何执行实际的代码。

逆向工程:逆向工程是将程序的二进制文件作为输入,由反汇编软件将其输出为汇编语言再进行分析的过程,汇编语言是一类语言的统称,例如x86、x64、SPARC、PowerPC、MIPS、ARM等。

内存:

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

1
2
3
4
5
6
7
8
9
10
11
		+------------------------+
低内存地址| 栈 | 用于函数的局部变量及参数,以及控制程序执行流
+------------------------+
| 堆 | 堆是为程序执行期间需要的动态内存准备的
+------------------------+
| |
| 代码 | 包含在执行程序代码时CPU执行的指令
| |
+------------------------+
高内存地址| 数据 | 程序运行时的静态值,程序运行时不会发生变化
+------------------------+

指令:指令时汇编程序的构成块,格式如下:

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

操作码|字节序:每条指令使用操作码opcode告诉CPU执行什么样的操作,反汇编器将操作码翻译为人类易读的指令,如下对比:

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

大端序|小端序:x86架构使用小端字节序。数据的字节序指的是在一个数据项中,最高位(大端)还是最低位(小端)被排在第一位,一些恶意代码必须在网络通信时改变字节序,因为网络数据使用大端序,而x86程序使用小端序,再大端序下,网络地址127.0.0.1被表示为0x7f000001,而在小端序下表示为0x1000007f

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

  • 立即数:操作数是一个固定的值。
  • 寄存器:操作数指向寄存器。
  • 内存地址:操作数指向一个内存地址,一般由方括号内包含值、寄存器或方程式组成,如[eax]

寄存器:寄存器是可以被CPU使用的少量数据存储器,访问速度极快,寄存器分为以下四类:

  • 通用寄存器,在CPU执行期间使用
  • 段寄存器,用于定位内存节
  • 状态标志,用于做出决定
  • 指令指针,用于定位下一条要执行的指令

在x86环境下,这些寄存器的大小都为32位,有四个写寄存器可以以8位值的方式引用,应用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
                              EAX
32bits ================================================
|1010 1001 1101 1100 1000 0001 1111 0101|
| A 9 D C 8 1 F 5 |
================================================

AX
16bits ========================
|1000 0001 1111 0101|
| 8 1 F 5 |
========================

AH AL
8bits ============ ============
|1000 0001| |1111 0101|
| 8 1 | | F 5 |
============ ============

通用寄存器:通用寄存器一般用于存储数据或者地址,但是一些寄存器并不通用,如乘法和除法指令只使用EAXEDX,而EAX一般也会作为存储程序返回值的寄存器。

标志寄存器:EFLAGES是一个标志寄存器,在x86架构中,他是32位的,每一位是一个标志,在运行时每一位要不么置位,要么清零,常见的标志如下:

  • ZF:当一个运算结果等于0时,ZF为1,否则为0
  • CF:当一个运算的结果相对于目标操作数太大或太小时,CF为1,否则为0
  • SF:当一个运算的结果为负数,SF为1,否则为0,且当运算结果的最高位为1,SF也为1
  • TF:TF用于调试,当TF为1时,x86处理器每次只执行一条指令

EIP:EIP寄存器保存了程序将要执行的下一条指令的地址。、

NOP指令:nop指令即空指令,实际上此指令是xchg eax的一个伪名。

栈:用于函数的内存,局部变量,流程控制结构等存储在栈中,栈智能用于短期存储,其主要用途是管理函数调用之间的数据交换。不同的编译器对这种管理方法实现的方式有所不同,但是大部分常见约定都使用相对EBP的地址来引用局部变量和参数。

函数调用:在函数调用时,最常见的调用方式是cdecl,调用函数的流程一般如下:

  1. 使用push将参数压入栈中
  2. 使用call memory_location来调用函数,当前指令地址被压入栈中
  3. 分配空间给局部变量,同时基地址EBP也被压入栈中
  4. 函数工作
  5. 恢复栈,调整ESP来释放局部变量占用的空间,从栈中弹出EBP
  6. 函数通过ret指令返回,从栈中弹出先前存储的EIP中的值给EIP,程序从原来调用的地方继续执行
  7. 调整栈,以移除之前压入的参数

汇编指令:常见的汇编指令数量较多,可从网上参阅。

C语言主函数和主函数参数:一个标准的C程序的主函数通常有两个参数:

1
int main(int argc, char ** argv)

参数argcargv在运行时决定。其中参数argc是一个整数,说明了命令行中参数的个数。参数argv是一个字符串数据指针,指向了所有命令行参数。

课后练习

本章无课后练习。


本章结束🎊

您的支持是我最大的动力🍉