ASM指令做题记录

pwn.college 做题记录:

传送门

set-multiple-register

点击查看代码

memory read

初探地址。

.intel_syntax noprefix
mov  rax, [0x404000]

memory write

题目要求我们把寄存器 rax 里的值存到 0x404000 地址里。

.intel_syntax noprefix
mov  [0x404000], rax

memory_increment

一开始写的是

add [0x404000], 0x1337

发现报错,原因是

x86_64 汇编中,直接对内存地址使用 add 指令时,必须明确指定内存操作数的 "字节大小"

字节(byte)

双字(dword,\(4\) 字节

四字(qword,\(8\) 字节)

使用 rax 寄存器,说明是在 x86_64 环境下。

数据默认是 \(64\) 位( \(8\) 字节,对应 qword)

在内存地址前添加 qword ptr 明确大小

.intel_syntax noprefix
mov  rax, [0x404000]
add qword ptr [0x404000], 0x1337

byte-access

题目要求我们将 0x404000 地址处的字节(1 字节)数据存入 rax 寄存器。

mov al, [0x404000]

memory—size-access

考察几个操作:

  • mov al, [A] => 将地址 A 处的最低有效字节移入 rax 寄存器
  • mov ax, [A] => 将地址 A 处的最低有效字移入 rax 寄存器
  • mov eax, [A] => 将地址 A 处的最低有效双字移入 rax 寄存器
  • mov rax, [A] => 将地址 A 处的完整四字移入 rax 寄存器
mov al, [0x404000]
mov bx, [0x404000]
mov ecx, [0x404000]
mov rdx, [0x404000]

little-end-write

这题开始讲小端序。

x86 架构采用小端序存储,即 即低位字节存储在低内存地址,高位字节存储在高内存地址。

例如:

[0x1330] = 0x00000000deadc0de

[0x1330] = 0xde (最低字节)

[0x1331] = 0xc0

[0x1332] = 0xad

[0x1333] = 0xde

[0x1334] = 0x00

[0x1335] = 0x00

[0x1336] = 0x00

[0x1337] = 0x00 (最高字节)

这题需要我们把一个大常量写入寄存器指向的地址。

x86_64 结构的 mov 指令没法直接将超长立即数写入内存地址,需要找个寄存器做中转,先把常量存入寄存器,再将寄存器的值写入目标内存地址。

落实到这个题,我们需要:

1.将 0xdeadbeef00001337 写入 rdi 寄存器所指向的内存地址(即 [rdi]);
2.将 0xc0ffee0000 写入 rsi 寄存器所指向的内存地址(即 [rsi])。

采用中转法,先把大常数存到寄存器 rax 里,再把 rax 里的值转给对应内存地址。

mov rax, 0xdeadbeef00001337
mov [rdi], rax

mov rax, 0xc0ffee0000
mov [rsi], rax

memory_sum

内存是线性存储的。

我们可以通过偏移量访问相邻内存数据。

假设我们访问 0x1337 地址处的四字数据:

[0x1337] = 0x00000000deadbeef

内存的实际存储形式是按字节排列的(遵循小端序):

[0x1337] = 0xef (第 0 字节)(第一个字节)

[0x1337 + 1] = 0xbe (第 1 字节)(第二个字节)

[0x1337 + 2] = 0xad (第 2 字节)

...

[0x1337 + 7] = 0x00 (第 7 字节)(第八个字节)

来看下题目问题:

  • 1.从 rdi 寄存器存储的地址处,读取两个连续的四字数据(即 [rdi] 和 [rdi+8],因为一个四字占 8 字节,连续存储的下一个四字偏移量为 8);
  • 2.计算这两个四字数据的总和;
  • 3.将计算得到的总和存入 rsi 寄存器存储的地址处。
mov rax, [rdi]
add rax, [rdi+8] 
mov [rsi], rax 

stack-subtraction

这题开始讲栈操作。

跟我之前学的栈不太一样的一点,这里的栈是一块可动态扩展和收缩的内存区域。

栈指针寄存器为 rsp

其他没啥好说的。

push rax ----> 把 rax 里的值压入栈顶

pop rax -----> 弹出栈顶并存入 rax 
mov rax, 0
pop rax
sub rax, rdi
push rax

swap-stack-values

栈特性:先进后出。

要求我们只能使用入栈出栈指令。

初始状态:rdi = 2,rsi = 5
目标状态:rdi = 5,rsi = 2

push rsi    
push rdi    
pop rsi     
pop rdi 

average-stack-values

寄存器 rsp 始终保存栈顶的内存地址,也就是最后一个入栈值的内存地址。

可以通过 [rsp] 直接访问栈顶数据(无需 pop)。

来看题目要求,在禁用 pop 指令的情况下:

1.计算栈中存储的 4 个连续四字数据 的平均值;
2.将计算得到的平均值压入栈中。

需要考虑下偏移量。

栈中 4 个四字的存储遵循 “后进先出” 规则,且每个四字(Quad Word)占 8 字节。

假设 4 个数据按「A→B→C→D」的顺序入栈(A 先入栈,D 最后入栈,D位于栈顶)

则它们的内存地址偏移如下:

D   rsp+0 相对rsp偏移0x0 , 偏移量为0可以省略
C   rsp+8 偏移8字节。
B   rsp+16 偏移16字节
A   rsp+24 偏移24字节

除法需要用到 div 指令 和 rax 寄存器。

mov rax, 0
add rax, [rsp]
add rax, [rsp+8]
add rax, [rsp+16]
add rax, [rsp+24]
mov rbx, 4 
div rbx
push rax 

abolute-jump

终于到了控制流操作。

专用寄存器 rip (指令指针寄存器) 始终指向即将执行的下一条指令。

jmp 为跳转指令。 call 为调用指令。 cmp 为比较指令。

这题主要讲跳转操作,跳转分为两种:

  • 无条件跳转(Unconditional jumps):始终触发跳转,不依赖于之前指令的执行结果

  • 条件跳转(Conditional jumps):仅当满足特定条件(由前序指令结果决定)时才触发跳转。

内存地址不但能存储数据,还能存储指令。

所有跳转指令均按 “目标地址指定方式” 分为三类:

  • 相对跳转(Relative jumps):相对于 “下一条指令” 的地址向前(+)或向后(-)偏移跳转;
  • 绝对跳转(Absolute jumps):跳转到一个固定的、明确的内存地址;
  • 间接跳转(Indirect jumps):跳转到 “寄存器中存储的内存地址”(目标地址由寄存器间接指定)。

这题考察我们绝对跳转,也就是跳转到固定地址。

实现方式:先将目标地址加载到某个通用寄存器(如 rax、rbx 等),再通过 “跳转至该寄存器” 完成绝对跳转。

jmp 寄存器

例如这题

mov rax, 0x403000
jmp rax 

relative-jump

这题考相对跳转,即相对于"下一条指令"的地址向前(+)或向后(-)偏移跳转。

实现可以用空指令 NOP 实现。

NOP(空指令),只占一字节。执行时不做任何操作,仅推进指令指针(轮空

看一下题目要求:

  • 1.代码的第一条指令必须是 jmp 指令;
  • 2.该 jmp 是相对跳转,跳转目标为 “距离当前 jmp 指令所在位置 0x51 字节” 的地址;
  • 3.在相对跳转所指向的代码位置,执行指令:将 rax 寄存器的值设为 0x1。
posted @ 2025-11-14 16:33  int_Hello_world  阅读(6)  评论(0)    收藏  举报