我们提供了一个示例 shellcode (mysh64.s)。以下代码针对 amd64 架构。代码也可以在 Labsetup 文件夹中找到。如果你在 Apple 机器上完成本实验,可以在 arm 子文件夹中找到针对 arm64 的示例代码。
section .text
global _start
_start:
BITS 64
jmp short two
one:
pop rbx
mov [rbx+8], rbx ; 将 rbx 存储到内存地址 rbx + 8
mov rax, 0x00 ; rax = 0
mov [rbx+16], rax ; 将 rax 存储到内存地址 rbx + 16
mov rdi, rbx ; rdi = rbx 🅰
lea rsi, [rbx+8] ; rsi = rbx +8 🅱
mov rdx, 0x00 ; rdx = 0
mov rax, 59 ; rax = 59
syscall
two:
call one
db '/bin/sh', 0 ; 命令字符串(以零终止) 🅲
db 'AAAAAAAA' ; argv[0] 的占位符
db 'BBBBBBBB' ; argv[1] 的占位符
上述代码首先跳转到 two 位置的指令,而该指令又跳转到 one 位置,但这次使用了 call 指令。此指令用于函数调用,即在跳转到目标位置之前,它将下一条指令的地址(即返回地址)保存到栈顶,这样函数返回时可以返回到 call 指令之后的指令。
在这个例子中,call 指令之后的“指令”实际上不是一条指令,而是存储了一个字符串。call 指令会将其地址(即字符串的地址)压入栈中,作为函数的返回地址。当我们跳转到 one 位置的函数后,栈顶存储的是返回地址。因此,pop rbx 指令实际上获取了位置 🅲 的字符串地址,并将其保存到 rbx 寄存器中。这就是如何获取字符串地址的方法。
请完成以下任务:
以下是一些可能对本实验有用的 gdb 命令。如果需要了解其他 gdb 命令,可以在 gdb 内部键入 help 获取命令类别名称列表。键入 help 加上类别名称,可以获取该类别中的命令列表。
$ gdb mysh64
help -- 打印帮助信息
break one -- 在 "one" 处设置断点
run -- 启动被调试程序。
step -- 单步执行程序直到到达不同的源代码行。
print $rbx -- 打印 rbx 寄存器的值
x/40bx <addr> -- 打印内存地址 <addr> 的内容(40 字节)
x/40bx $rsp -- 打印栈顶 40 字节的内容
x/5gx $rsp -- 打印栈顶 5 个双字(8 字节)的内容
quit -- 退出 gdb