章节大纲

  • 我们提供了一个示例 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 寄存器中。这就是如何获取字符串地址的方法。

    请完成以下任务:

    •  编译并运行代码,验证是否可以获得一个 shell。使用 -g 选项启用调试信息,因为我们将对代码进行调试。
      // 针对 amd64
      $ nasm -g -f elf64 -o mysh64.o mysh64.s
      $ ld --omagic -o mysh64 mysh64.o
      
      // 针对 arm64
      $ as  -g -o mysh64.o mysh64.s
      $ ld --omagic -o mysh64 mysh64.o
      注意,在运行链接器程序 ld 时,我们需要使用 --omagic 选项。默认情况下,代码段是不可写的。当此程序运行时,它需要修改存储在代码区域的数据,如果代码段不可写,程序将崩溃。 这在实际攻击中不是问题,因为实际攻击中,代码通常被注入到可写的数据段(例如栈或堆)中。通常我们不会将 shellcode 作为独立程序运行。
    • 使用 gdb 调试程序,展示程序如何获取 shell 字符串 /bin/sh 的地址
    • 解释程序如何构造 argv[] 数组,并展示哪些代码行设置了 argv[0] 和 argv[1] 的值
    • 解释第 🅰 行和 🅱 行的真正含义。
     
    以下是一些可能对本实验有用的 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