CTF竞赛权威指南Pwn篇-汇编基础

CPU结构和指令集

CPU是名称为中央处理单元,简称处理器,主要的作用是从内存中读取指令,然后解码和执行。
CPU架构就是CPU内部设计的结构,是一堆硬件组成,用于实现指令集所规定的操作

指令集包含了一系列的操作码(opcode),以及特定的CPU执行的基本命令。如果想要设计CPU,就得先解决使用什么样的指令集,然后设计硬件的电路。根据现在指令集的特征,可以分为两类:CSSCRISC

由于指令集是一堆二进制数据,不方便阅读和理解,于是就有了汇编语言(Assembly language),汇编语言就是类似人类语言的方式对执行集进行描述,每个汇编指定都会对应的机械执行。再往后就有了C/C++等高级语言

指令集架构

  • CISC: 复杂指令集计算机,代表的是x86处理器
  • RISC: 精简指令集计算机,典型代表是ARM处理器

需要注意的一点是RISC可以完全使用寄存器来传递参数,而CISC只能使用栈,或者是栈和寄存器结合起来使用

x86/x64汇编基础

我们主要以了解x86的汇编语法来了解汇编语言
x86汇编主要的语法有两种风格: AT&TIntel风格

通常在Linux中见到的AT&T的风格比较多,常见的GCC、GDB、objdump都是使用的AT&T风格,他们两个之间语法有一些简单的区别,我们主要了解Intel风格

有很多细节的不同,AT&T的特征是寄存器和数字的前面通常会加入% $的符号,Intel没有,十六进制使用的是0xIntel使用的是后方计入h

寄存器

寄存器从8位到16位,在到32位以及64为处理器,寄存器的名称有一些变化。

您想要一个从8位到64位寄存器名称变化的表格。以下是一个简单的表格,显示了不同位数寄存器的常见名称。请注意,这只是一个基本的示例,实际的寄存器名称可能因处理器架构、设计或特定应用而有所不同。

位数 寄存器名称示例
8位 AL, AH, BL, BH, CL, CH, DL, DH
16位 AX, BX, CX, DX, SP, BP, SI, DI
32位 EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI
64位 RAX, RBX, RCX, RDX, RSP, RBP, RSI, RDI

在64为模式下,操作书的默认大小仍然是32位

整数常量
如果给如一个1234不添加如何说明,它就是一个十进制整数,也可以是八进制或者十六进制数据
因为十六禁止含有ABCDEF,需要需要再数值的前方加入0,在后方加入h表示,例如1234就是0x1234h]

浮点数常量
也称实数常亮,浮点数中至少包含一个整数和一个小数点,例如:"1."、"+2.3"、"-3.14159"、"26.E53"

字符串常量
字符串常亮通常使用单引号和双引号包裹起来

汇编常用语句

MOV EAX, 0       # 将0保存在EAX寄存器中

.data
  testArray BYTE 99h, 98h, 97h, 96h
.code
  MOV al, testArray        ; al = 99h
  MOV bl, [testArray+1]    ; bl = 98h
  MOV cl, [testArray+2]    ; cl = 97h

INCDEC分别为操作数加1和操作数减1

ADDSUB为加和减

.data
  testData  DWORD 10000h
  testData2 DWORD 20000h
.code
  MOV EAX, testDATA      ; EXA=10000h
  ADD EAX, testDATA2     ; EAX=30000h    
.data
  testData  DWORD 20000h
  testData2 DWORD 10000h
.code
  MOV EAX, testDATA      ; EXA=20000h
  SUB EAX, testDATA2     ; EAX=10000h  

跳转指令一般是JMPLOOP,通常分为有条件和无条件跳转

栈与函数调用

栈是计算机中最重要、最基础的数据结构之一,它采用先入后出的数据结构,我们可以把他想象成一个薯片桶,先放入薯片桶的薯片总是最后一个被拿出。

在编译完成的二进制程序中,栈的空间总是有限的。通常来说,编译器会默认分配足够程序自身使用的栈空间,在Linux中,可以使用ulimit -a查看或更改当前系统默认的栈大小。

栈空间是计算机内存中一段确定的内存区域,也有一些指针指向相应的内存地址,在x86结构中这个指针位于ESP寄存器,在x64平台上为RSP寄存器。

在计算机底层,栈主要的几个用途是:

  1. 存储局部变量
  2. 执行CALl执行调用函数时,保存函数地址以便函数结束时正确返回
  3. 传递函数参数

操作栈使用的指令是PUSH和POP即入栈和出栈。PUSH指令在栈中加入4(32位)或8(64位),所以对应的指针就需要减去4或者8个字节,原因是栈采用的是高地址往低地址的顺序。

以下是一个表示栈结构的表格,其中高地址位于表格的上方。请注意,栈是从高地址向低地址增长的,所以新压入的数据项(即最近的操作)将位于表格的底部。

地址(高 → 低) 数据内容
地址A 数据X
地址A-8 数据Y
地址A-16 数据Z
... ...
地址A-n*8 数据N

在这个表格中,地址A 是栈的初始栈顶地址(即栈的最高地址)。每次执行 push 操作时,栈指针会减少8(在64位系统中),并且一个新的数据项(如数据Y、数据Z等)会被压入栈中。这些新的数据项将占据比初始栈顶地址更低的内存位置。

栈的底部(即最低地址)是栈的增长方向,新数据总是从栈底向上压入。因此,在表格中,新数据(如数据N)将位于最下方,而旧数据(如数据X)则位于上方。

比较难以理解,后续慢慢研究。

posted @ 2024-04-28 12:14  Junglezt  阅读(49)  评论(0编辑  收藏  举报