Section outline

  • 为了直接控制 Shellcode 中要使用的指令,编写 Shellcode 的最佳方式是使用汇编语言。在本任务中,我们将使用一个示例程序来熟悉开发环境。你的任务是完成整个开发过程:编译并运行示例代码,然后从二进制文件中提取机器代码。

    不同计算机架构的汇编语言有所不同。在本任务中,示例代码 hello.s 针对的是 amd64(64 位)架构。代码包含在 Labsetup 文件夹中。使用 Apple 芯片的学生可以在 Labsetup/arm 文件夹中找到适用于 arm 架构的示例代码。

    ;; 一个 amd64 架构的汇编程序示例 hello.s
    global _start
    
    section .text
    
    _start:
      mov rdi, 1        ; 标准输出
      mov rsi, msg      ; 消息的地址
      mov rdx, 15       ; 消息的长度
      mov rax, 1        ; write() 系统调用的编号
      syscall           ; 调用 write(1, msg, 15)
    
      mov rdi, 0        ;
      mov rax, 60       ; exit() 系统调用的编号
      syscall           ; 调用 exit(0)
    
    section .rodata
      msg: db "Hello, world!", 10
      • 我们使用 nasm 编译上述汇编代码,它是 Intel x86 和 x64 架构的汇编和反汇编工具。对于 arm64 架构,对应的工具是 as。选项 -f elf64 表示我们希望将代码编译为 64 位 ELF 二进制格式。可执行和可链接格式(ELF)是一个常见的可执行文件、目标代码和共享库的标准文件格式。对于 32 位汇编代码,应使用 elf32。
        // 针对 amd64
        $ nasm -f elf64 hello.s -o hello.o  
        
        // 针对 arm64
        $ as  -o hello.o hello.s
      • 获得目标代码 hello.o 后,如果希望生成可执行二进制文件,可以运行链接器程序 ld,这是编译的最后一步。完成此步骤后,我们获得最终的可执行代码 hello。运行它时,会打印出 "Hello, world!"。

        // 适用于 amd64 和 arm64
        $ ld hello.o -o hello
        $ ./hello  
        Hello, world!
      • 在大多数攻击中,我们只需要 Shellcode 的机器代码,而不需要包含其他数据的可执行文件。从技术上讲,只有机器代码才被称为 Shellcode。因此,我们需要从可执行文件或目标文件中提取机器代码。有多种方法可以实现这一点。一种方法是使用 objdump 命令反汇编可执行文件或目标文件。
         
        对于 amd64,汇编代码有两种常见的语法模式:AT&T 语法模式和 Intel 语法模式。默认情况下,objdump 使用 AT&T 模式。以下示例中,我们使用 -Mintel 选项生成 Intel 模式的汇编代码。

        $ objdump -Mintel -d hello.o
        hello.o:     file format elf64-x86-64
        
        Disassembly of section .text:
        
        0000000000000000 <_start>:
           0: bf 01 00 00 00        mov    edi,0x1
           5: 48 be 00 00 00 00 00  movabs rsi,0x0
           c: 00 00 00
           f: ba 0f 00 00 00        mov    edx,0xf
          14: b8 01 00 00 00        mov    eax,0x1
          19: 0f 05                 syscall
          1b: bf 00 00 00 00        mov    edi,0x0
          20: b8 3c 00 00 00        mov    eax,0x3c
          25: 0f 05                 syscall
         
        在上述输出中,冒号后的数字是机器代码。你还可以使用 xxd 命令打印二进制文件的内容,应该能从输出中找到 Shellcode 的机器代码。

        $ xxd -p -c 20 hello.o
        7f454c4602010100000000000000000001003e00
        ...                     ⇩ 机器代码从这里开始
        000000001800000000000000bf0100000048be00
        00000000000000ba0f000000b8010000000f05bf
        00000000b83c0000000f05000000000000000000
        ...