静态分析高级技术
原文链接:https://hvnt3r.top/2019/03/静态分析高级技术/
知识点
本章内容为静态分析高级技术,知识点内容较多,很多知识点都是与x86体系结构下的指令等相关知识。
**微指令|机器码:**微指令层又称为固件,微指令只能在特定的电路上执行,其通常由更高的机器码层翻译而来,提供了访问硬件的接口;机器码层由操作码组成,操作吗是一些十六进制的数字,机器码一般由多条微指令实现,用于告诉底层硬件如何执行实际的代码。
**逆向工程:**逆向工程是将程序的二进制文件作为输入,由反汇编软件将其输出为汇编语言再进行分析的过程,汇编语言是一类语言的统称,例如x86、x64、SPARC、PowerPC、MIPS、ARM等。
内存:
一个程序的内存可以分为一下四个主要部分
+------------------------+
低内存地址| 栈 | 用于函数的局部变量及参数,以及控制程序执行流
+------------------------+
| 堆 | 堆是为程序执行期间需要的动态内存准备的
+------------------------+
| |
| 代码 | 包含在执行程序代码时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位值的方式引用,应用示例如下:
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 |
============ ============
**通用寄存器:**通用寄存器一般用于存储数据或者地址,但是一些寄存器并不通用,如乘法和除法指令只使用EAX
和EDX
,而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
,调用函数的流程一般如下:
- 使用push将参数压入栈中
- 使用call memory_location来调用函数,当前指令地址被压入栈中
- 分配空间给局部变量,同时基地址EBP也被压入栈中
- 函数工作
- 恢复栈,调整ESP来释放局部变量占用的空间,从栈中弹出EBP
- 函数通过ret指令返回,从栈中弹出先前存储的EIP中的值给EIP,程序从原来调用的地方继续执行
- 调整栈,以移除之前压入的参数
**汇编指令:**常见的汇编指令数量较多,可从网上参阅。
**C语言主函数和主函数参数:**一个标准的C程序的主函数通常有两个参数:
int main(int argc, char ** argv)
参数argc
和argv
在运行时决定。其中参数argc
是一个整数,说明了命令行中参数的个数。参数argv
是一个字符串数据指针,指向了所有命令行参数。
课后练习
本章无课后练习。