我已经设法修补了ELF文件的入口点,并使其指向其他位置,并在返回原始入口点之前执行了一段代码。以下是我尝试跳回到OEP的方式:
mov rax, 0x4141414141414141 ( 48 b8 41 41 41 41 41 41 41 41 )
jmp rax (ff e0)
我有一个包含这些操作码的数组,我在解析ELF标头以获取入口点后立即对其进行修补:
uint64_t oep = ehdr->e_entry;
memcpy(&opcode[23], &oep, 8);
但是入口点总是类似于:0x47fe8d,这使数组的其余部分无效,因为操作码期望的是8个字节的地址而没有零。我尝试通过符号扩展地址来代替它,例如:0xffffffff47fe8d,但是它不起作用。由于x86地址为zero-extended,因此这似乎是正常现象。
EDIT:shellcode数组看起来像这样:
_start:
xor rax, rax
xor rax, rax
xor rsi, rsi
jmp get_str
shellcode:
pop rsi
mov al, 1
mov dil, 1
mov dl, 9
syscall ; writes a string
mov rax, 0x4141414141414141 ; patched with the EP
jmp rax
get_str:
call shellcode
db "strings!", 0xa
// write syscall + jmp OEP (mov rax, addr, jmp rax). patch 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";
我制作了一个函数,该函数在修补该阵列之前将其打印出来。外观如下:
\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
但是用0x47fe8d修补jmp指令后,地址的高字节变为零:
\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\xeb\x16\x5e\xb0\x01\x40\xb7\x01\xb2\x09\x0f\x05\x48\xb8\x20\x1b\x40
并且由于某种原因导致分割错误。我使用IDA搜索修补文件的入口点,这就是我发现的内容:
LOAD:000000000047FE8D start: ; DATA XREF: LOAD:0000000000400018↑o
LOAD:000000000047FE8D xor rax, rax
LOAD:000000000047FE90 xor rdi, rdi
LOAD:000000000047FE93 xor rsi, rsi
LOAD:000000000047FE96
LOAD:000000000047FE96 loc_47FE96: ; CODE XREF: LOAD:000000000047FEAC↓j
LOAD:000000000047FE96 jmp short loc_47FEAE
LOAD:000000000047FE98 ; ---------------------------------------------------------------------------
LOAD:000000000047FE98 pop rsi
LOAD:000000000047FE99 mov al, 1
LOAD:000000000047FE9B mov dil, 1
LOAD:000000000047FE9E mov dl, 9
LOAD:000000000047FEA0 syscall ; $!
LOAD:000000000047FEA2 mov rax, offset _start
LOAD:000000000047FEAC loopne loc_47FE96
LOAD:000000000047FEAE
LOAD:000000000047FEAE loc_47FEAE: ; CODE XREF: LOAD:loc_47FE96↑j
LOAD:000000000047FEAE in eax, 0FFh ; $!
LOAD:000000000047FEAE ; ---------------------------------------------------------------------------
LOAD:000000000047FEB0 dq 6B63616A6968FFFFh
LOAD:000000000047FEB8 db 65h, 64h, 0Ah
LOAD:000000000047FEB8 LOAD ends
因此,尽管IDA在000000000047FEAC]处错误地编码了指令,但似乎文件已被成功修补,_start符号仍会导致以下路径:
public _start _start proc near endbr64 xor ebp, ebp mov r9, rdx ; rtld_fini pop rsi ; argc mov rdx, rsp ; ubp_av and rsp, 0FFFFFFFFFFFFFFF0h push rax push rsp ; stack_end mov r8, offset __libc_csu_fini ; fini mov rcx, offset __libc_csu_init ; init mov rdi, offset main ; main db 67h call __libc_start_main hlt _start endp
这最终调用了原始的main函数,似乎一切正常。
进一步检查后,我发现000000000047FEAE]处的指令是罪魁祸首,尽管我不太清楚为什么。这是我用来将字符串的地址压入堆栈的call
指令。为什么会出现细分错误?
我已经设法修补了ELF文件的入口点,并使其指向其他位置,并在返回原始入口点之前执行了一段代码。以下是我尝试跳的方式...
IDA没解码错,您的机器代码的十六进制字符串版本是错误的
;一个\x41
字节短,因此mov r64, imm64
消耗下一个FF
字节作为其立即数的一部分,而不是jmp
的操作码。这就是为什么它在0e e8
loopne`处解码。