因此,在通过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,它不应该真正影响其他任何东西。