CTF竞赛权威指南Pwn篇-汇编基础
CPU结构和指令集
CPU是名称为中央处理单元,简称处理器,主要的作用是从内存中读取指令,然后解码和执行。
CPU架构就是CPU内部设计的结构,是一堆硬件组成,用于实现指令集
所规定的操作
指令集包含了一系列的操作码(opcode),以及特定的CPU执行的基本命令。如果想要设计CPU,就得先解决使用什么样的指令集,然后设计硬件的电路。根据现在指令集的特征,可以分为两类:CSSC和RISC
由于指令集是一堆二进制数据,不方便阅读和理解,于是就有了汇编语言(Assembly language)
,汇编语言就是类似人类语言的方式对执行集进行描述,每个汇编指定都会对应的机械执行。再往后就有了C/C++等高级语言
指令集架构
- CISC: 复杂指令集计算机,代表的是x86处理器
- RISC: 精简指令集计算机,典型代表是ARM处理器
需要注意的一点是RISC可以完全使用寄存器来传递参数,而CISC只能使用栈,或者是栈和寄存器结合起来使用
x86/x64汇编基础
我们主要以了解x86的汇编语法来了解汇编语言
x86汇编主要的语法有两种风格: AT&T
和Intel
风格
通常在Linux
中见到的AT&T
的风格比较多,常见的GCC、GDB、objdump
都是使用的AT&T
风格,他们两个之间语法有一些简单的区别,我们主要了解Intel风格
有很多细节的不同,AT&T
的特征是寄存器和数字的前面通常会加入% $
的符号,Intel
没有,十六进制使用的是0x
而Intel
使用的是后方计入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
INC
和DEC
分别为操作数加1和操作数减1
ADD
和SUB
为加和减
.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
跳转指令一般是JMP
和LOOP
,通常分为有条件和无条件跳转
栈与函数调用
栈是计算机中最重要、最基础的数据结构之一,它采用先入后出的数据结构,我们可以把他想象成一个薯片桶,先放入薯片桶的薯片总是最后一个被拿出。
在编译完成的二进制程序中,栈的空间总是有限的。通常来说,编译器会默认分配足够程序自身使用的栈空间,在Linux中,可以使用ulimit -a
查看或更改当前系统默认的栈大小。
栈空间是计算机内存中一段确定的内存区域,也有一些指针指向相应的内存地址,在x86结构中这个指针位于ESP
寄存器,在x64平台上为RSP
寄存器。
在计算机底层,栈主要的几个用途是:
- 存储局部变量
- 执行CALl执行调用函数时,保存函数地址以便函数结束时正确返回
- 传递函数参数
操作栈使用的指令是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)则位于上方。
比较难以理解,后续慢慢研究。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程