前言
这个模块是关于汇编语言编程的,要求设置寄存器rdi的值为0x1337。在x86-64架构的Linux系统中,rdi是第一个参数寄存器。
为了将0x1337这个值放入rdi寄存器,你可以使用以下的汇编代码:
assembly mov $0x1337, %rdi
这条指令将立即数0x1337移动到rdi寄存器中。
如果你需要将这条指令转换为字节码形式(即机器码),你可以使用任何支持x86-64汇编的汇编器,如GNU Assembler (GAS)。以下是如何使用GAS将上述汇编指令转换为字节码的步骤:
- 创建一个名为set_rdi.s的汇编源文件,其中包含以下内容:
assembly section .text global _start _start: mov $0x1337, %rdi ; 添加退出代码,以便程序能够正常退出 mov $60, %rax ; 系统调用号 (sys_exit) xor %rdi, %rdi ; 退出状态码 0 syscall
- 使用GAS将汇编源文件编译为机器码:
bash nasm -f elf64 set_rdi.s
- 链接生成的目标文件以创建可执行文件:
bash ld -m elf_x86_64 -o set_rdi set_rdi.o
现在,set_rdi可执行文件包含了将0x1337设置到rdi寄存器的机器码。你可以使用objdump工具来查看这些机器码:
bash objdump -d ./set_rdi
这将显示反汇编的输出,其中包含了mov $0x1337, %rdi指令的机器码表示。注意,实际的机器码可能会因为不同的系统和编译器选项而有所不同。
1. 寄存器赋值: level 1~level 2
from pwn import * context.update(arch="amd64") io = process("/challenge/run") io.write(asm(""" mov rdi,0x1337 # Write your assembly code here """)) io.interactive() # print(io.readallS())
mov rax, 0x1337 mov r12, 0xCAFED00D1337BEEF mov rsp, 0x31337
2. 加减乘除(模):level 3~level 6
add rdi,0x331337
imul rdi,rsi # rdi = rdi*rsi ; imul 有符号乘法,mul无符号乘法; # 单操作数 imul xxx / mul xxx ==> rax*xxx,放在rax,作用于有符号乘数结果可能有溢出 add rdi,rdx mov rax,rdi
# div 只有一个操作数。需要先把被除数放入rax寄存器,然后使用 div 除以 除数寄存器,商保存rax rax,rdi div rsi # rax = rdi/rsi
xor rdx,rdx # 先清空rdx内容 mov rax ,rdi # 运算 rax➗rsi div rsi mov rax,rdx # div 运算余数放在 rdx寄存器
3. 通用数据寄存器高低位:level 7 ~ level 8
通用数据寄存器:rax,rbx,rcx,rdx
以rax寄存器为例:
MSB 【high】 LSB 【low】
±---------------------------------------+
| rax | 64 bit
±-------------------±------------------+
** | eax | 32 bit**
** ±--------±--------+**
** | ax | 16 bit**
** ±—±—+**
** | ah | al | 8 bit each**
** ±—±—+**
mov ah, 0x42
仅使用 mov 指令完成模运算
mov rcx,rdi mov al,cl # 注意mov前后要对等,mov rax,cl 会报错 mov rcx,rsi mov bx,cx
4. 逻辑运算:level 9 ~ level 11
# reg2的数据表示移动位数【这里可以是立即数,如:shl rdi,24】 shl reg1, reg2 <=> Shift reg1 left by the amount in reg2 shr reg1, reg2 <=> Shift reg1 right by the amount in reg2
rdi = | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | Set rax to the value of B4 # 将 rdi 先左移 3*8=24位得到: # | B4 | B3 | B2 | B1 | B0 | 0 | 0 | 0 | # 然后右移 7*8 = 56 位得到: # | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B4 | # 最后赋值给 rax即可 shl rdi,24 shr rdi,56 mov rax ,rdi
and rdi,rsi # 不使用 mov指令,实现:rax = rdi and rsi xor rax,rax or rax,rdi
Implement the following logic: if x is even then # x 是 偶数,y = 1;反之 y=0 y = 1 else y = 0 where: x = rdi y = rax
and rdi,0x1 # 取 rdi最低位:0/1 (偶/奇) xor rdi,1 # 反转 -----> 1/0 xor rax,rax # 清空 rax or rax,rdi # 赋值 rax
5. 内存<–>寄存器操作 :level 12~level 14
In x86 we can access the thing at a memory location, called dereferencing, like so: mov rax, [some_address] <=> Moves the thing at 'some_address' into rax This also works with things in registers: mov rax, [rdi] <=> Moves the thing stored at the address of what rdi holds to rax This works the same for writing: mov [rax], rdi <=> Moves rdi to the address of what rax holds. So if rax was 0xdeadbeef, then rdi would get stored at the address 0xdeadbeef: [0xdeadbeef] = rdi Note: memory is linear, and in x86, it goes from 0 - 0xffffffffffffffff (yes, huge).
#取内存地址的值赋给rax寄存器: mov rax,[0x404000] # mov rax, qword ptr [0x404000] # 将寄存器值写入内存地址: mov [0x404000],rax # mov qword ptr [0x404000],rax # 将指定地址内容赋值给 rax,接着该地址内容+0x1337 mov rax,[0x404000] add rax,0x1337 mov [0x404000],rax sub rax,0x1337 # [或者借助另外寄存器]: mov rax,[0x404000] mov rbx,rax add rbx,0x1337 mov [0x404000],rbx
6. 内存操作:level 15~level 16
Recall the following: The breakdown of the names of memory sizes: Quad Word = 8 Bytes = 64 bits Double Word = 4 bytes = 32 bits Word = 2 bytes = 16 bits Byte = 1 byte = 8 bits In x86_64, you can access each of these sizes when dereferencing an address, just like using bigger or smaller register accesses: mov al, [address] <=> moves the least significant byte from address to rax mov ax, [address] <=> moves the least significant word from address to rax mov eax, [address] <=> moves the least significant double word from address to rax mov rax, [address] <=> moves the full quad word from address to rax
mov al, byte [0x403fff] # mov al, byte ptr [0x404000] # 如果地址前面加上 byte等,汇编代码地址需要-1,执行时候会自动向后加一位开始赋值。 # 常规: mov al, [0x404000] mov bx , [0x404000] mov ecx ,[0x404000] mov rdx , [0x404000] # 加上byte...之后地址需要依此变形 mov al, byte [0x403fff] mov bx , word [0x403ffe] mov ecx ,[0x404000] mov rdx , [0x404000] mov al, byte [0x403fff] mov bx , word [0x403ffe] mov ecx ,dword [0x403ffc] mov rdx ,qword [0x403ff8] # 结果 ---------------- CODE ---------------- 0x400000: mov al, byte ptr [0x404000] 0x400007: mov bx, word ptr [0x404000] 0x40000f: mov ecx, dword ptr [0x404000] 0x400016: mov rdx, qword ptr [0x404000] --------------------------------------
7. 寄存器地址赋值:level 17~level 18
Set [rdi] = 0xdeadbeef00001337 Set [rsi] = 0xc0ffee0000 mov rax,0xdeadbeef00001337 mov [rdi],rax mov rax, 0xc0ffee0000 mov [rsi],rax
[0x404190] = 0xd5729 [0x404198] = 0xe37d2 rdi = 0x404190 # task:取出 rdi 与 rdi+8 执向地址数据,求和存入 rsi 指向地址 rsi = 0x4047a8 mov rax, [rdi+8] mov rbx,[rdi] add rbx,rax mov [rsi],rbx
8. 栈调用:level 19~level 21
pop rax sub rax,rdi push rax # 或者 mov rax,[rsp] sub rax,rdi mov [rsp],rax
push rdi push rsi pop rdi pop rsi
mov rax, [rsp] add rax,[rsp+8] add rax,[rsp+16] add rax,[rsp+24] mov rdi,4 div rdi push rax ---------------- CODE ---------------- 0x400000: mov rax, qword ptr [rsp] 0x400004: add rax, qword ptr [rsp + 8] 0x400009: add rax, qword ptr [rsp + 0x10] 0x40000e: add rax, qword ptr [rsp + 0x18] 0x400013: mov rdi, 4 0x40001a: div rdi 0x40001d: push rax -------------------------------------- # 下面汇编不能得到正确结果,可能栈没有对齐 mov rax,[rsp] mov rbx,[rsp+8] mov rcx,[rsp+16] mov rdx,[rsp+24] mov rdi,4 add rax,rbx add rax,rcx add rax,rdx div rdi push rax ---------------- CODE ---------------- 0x400000: mov rax, qword ptr [rsp] 0x400004: mov rbx, qword ptr [rsp + 8] 0x400009: mov rcx, qword ptr [rsp + 0x10] 0x40000e: mov rdx, qword ptr [rsp + 0x18] 0x400013: mov rdi, 4 0x40001a: add rax, rbx 0x40001d: add rax, rcx 0x400020: add rax, rdx 0x400023: div rdi 0x400026: push rax --------------------------------------
然而,还有另一种更有趣的除法方法。
除法使用shr
我们知道,字节中的每一位都是某个数字的 2 次方。
±--------------------------------------------------------------+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| (2^7) | (2^6) | (2^5) | (2^4) | (2^3) | (2^2) | (2^1) | (2^0) |
±--------------------------------------------------------------+
上面字节的值是 1x(2^7),等于 128。
如果我们向右移动 2 位,我们会得到以下结果。
±--------------------------------------------------------------+
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| (2^7) | (2^6) | (2^5) | (2^4) | (2^3) | (2^2) | (2^1) | (2^0) |
±--------------------------------------------------------------+
字节的值现在是 1x(2^5),即 32。因此,我们基本上在不使用指令的情况下将数字除以 4。div
现在,我们只需要对存储的总和做同样的事情来找到平均值。rax
shr rax, 2
接下来,我们必须使用指令将平均值复制到堆栈上。push
push rax
堆栈将如下所示:
±------------------------+
RSP+0x20 | Quad Word A | <------ rbp
±------------------------+
RSP+0x18 | Quad Word B |
±------------------------+
RSP+0x10 | Quad Word C |
±------------------------+
RSP+0x08 | Quad Word D |
±------------------------+
RSP | Average | <------ rsp
±------------------------+