执行shellcode后ELF文件崩溃

逆向工程 艾达 x86 小精灵 外壳代码
2021-07-01 20:35:03

因此,在通过e_entry字段更改入口点后,我设法在将控制权返回到原始入口点之前执行了我的 shellcode。这是我如何做到的:

 // write string and jump to OEP, patch address at 23
    unsigned char shellcode[] = "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\xeb"
                      "\x16\x5e\xb0\x01\x40\xb7\x01\xb2\x09\x0f"
                      "\x05\x48\xb8\x41\x41\x41\x41\x41\x41\x41"
                      "\xff\xe0\xe8\xe5\xff\xff\xff\x68\x69\x6a"
                      "\x61\x63\x6b\x65\x64\x0a";

因此,一旦我解析了 ELF 标头,我就会修补 shellcode:

uint64_t oep = ehdr->e_entry;
memcpy(&opcode[23], &oep, 8);

一切正常,shellcode 执行,然后执行恢复到它应该的位置。问题是在目标的主函数完成后它会出现段错误(为了简单起见,我只是制作了一个打印字符串的程序)。

所以我用 gdb 和 IDA 来看看发生了什么。只是为了清楚起见,执行流程如下:

  • 执行从shellcode开始
  • shellcode 跳转到 _start
  • _start 调用 libc_start_main
  • main 执行并返回 libc_start_main

现在,在主要返回后,IDA 显示以下内容:

libc_start_main:
 ext:000000000040244B                 lea     rax, [rsp+108h+var_98]
.text:0000000000402450                 mov     fs:300h, rax
.text:0000000000402459                 mov     rdx, cs:environ
.text:0000000000402460                 mov     edi, [rsp+108h+var_FC]
.text:0000000000402464                 mov     rsi, [rsp+108h+var_F8]
.text:0000000000402469                 mov     rax, [rsp+108h+mainaddr]
.text:000000000040246E                 call    rax             ; jump to main
.text:0000000000402470                 mov     edi, eax
.text:0000000000402472
.text:0000000000402472 loc_402472:                             ; CODE XREF: __libc_start_main+4AF↓j
.text:0000000000402472                 call    exit
.text:0000000000402477 ; -----------------------------------------------------------

退出函数结束调用run_exit_handlers这似乎是真正的罪魁祸首,因为它的故障有以下说明:

mov     rdx, [rax+18h]
mov     rdi, [rax+20h]
mov     qword ptr [rax+10h], 0
mov     esi, ebp
ror     rdx, 11h
xor     rdx, fs:30h
call    rdx             ; faults
jmp     loc_40823A

由于某种原因,rdx 的值为 9,这是不允许的,因此会导致分段错误。

我搜索了有关run_exit_handlers 的信息,但没有找到任何有意义的信息。

所以我的问题是:为什么会发生这种情况?我的 shellcode 所做的只是写一个字符串并跳转到 OEP,它不应该真正影响其他任何东西。

1个回答

你的问题是你的shellcode为了系统调用RDX通过设置dl使用您没有将此变量设置回它的初始值,不幸的是,这被传递到它被视为指向将作为终结器执行的函数的指针的地方。这就是为什么会出现段错误的原因,因为 CPU 尝试从 address 执行代码9sys_write0_start0x9

您应该在执行 shellcode 之前保存寄存器并在返回入口点之前恢复它们。但是为了这个例子,如果你在跳回之前清除 (r)dx 它将起作用。

修改 shellcode,添加\x48\x31\xd2forxor rdx,rdx和更改偏移量shellcode[10]shellcode[37]调整其中的其他字节(手动修改它们 - 如果您有源代码 - 您可以在代码中进行并编译)

unsigned char shellcode[] = 
              "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\xeb"
              "\x19\x5e\xb0\x01\x40\xb7\x01\xb2\x09\x0f"
              "\x05\x48\xb8\x41\x41\x41\x41\x41\x41\x41\xff\x48\x31\xd2"               
              "\xff\xe0\xe8\xe2\xff\xff\xff\x68\x69\x6a"
              "\x61\x63\x6b\x65\x64\x0a"; 

多一点解释为什么RDX这里有问题。在你的 shellcode 完成它的工作后,它会_start像开头那样执行这两行:

  00400a30 31  ed          XOR       EBP ,EBP
  00400a32 49  89  d1      MOV       R9 ,RDX

所以无论是在RDX将被分配给R9. 后来__libc_start_main调用了它,如果你检查它的签名(例如这里),你可以看到它有 7 个参数。它们通过寄存器传递,根据 Linux ABI,第 6 个作为R9寄存器传递,第 6 个参数_libc_start_main是终结器的回调。这就是为什么值在RDX传递到R9并最终作为回调地址很重要的原因。