Shellcode 的主要目的是运行一个 shell 程序,例如 /bin/sh。在 Ubuntu 操作系统中,这可以通过调用 execve() 系统调用来实现。
execve("/bin/sh", argv[], 0)
我们需要为该系统调用传递三个参数:在 amd64 架构中,它们通过 rdi, rsi, rdx 寄存器传递。在 arm64 架构中,它们通过 x0, x1, x2 寄存器传递。伪代码如下所示:
// 针对 amd64 架构
Let rdi = address of the "/bin/sh" string
Let rsi = address of the argv[] array
Let rdx = 0
Let rax = 59 // 59 是 execve 的系统调用号
syscall // 调用 execve()
// 针对 arm64 架构
Let x0 = address of the "/bin/sh" string
Let x1 = address of the argv[] array
Let x2 = 0
Let x8 = 221 // 221 是 execve 的系统调用号
svc 0x1337 // 调用 execve()
编写 shellcode 的主要挑战是如何获取 "/bin/sh" 字符串的地址以及 argv[] 数组的地址。通常有两种典型的方法:
- 方法 1:将字符串和数组存储在代码段中,然后使用指向代码段的 PC 寄存器获取它们的地址。本任务中我们关注这种方法。
- 方法 2:在栈上动态构造字符串和数组,然后使用栈指针寄存器获取它们的地址。我们将在下一任务中关注这种方法。