Мне удалось пропатчить точку входа в файл 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 расширены нулями .
EDIT : Массив шелл-кода выглядит следующим образом:
_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
Но после исправления инструкции jmp с 0x47fe8d старшие байты адреса становятся равными нулю:
\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 неправильно кодирует инструкцию в 000000000047FEA C, кажется, что файл был успешно исправлен, символ _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
В результате вызывается исходная основная функция, все кажется в порядке.
При дальнейшем осмотре я обнаружил, что инструкция по 000000000047FEAE является виновником, хотя я не совсем понимаю, почему. Это инструкция call , которую я использовал для вывода sh адреса строки в стек.
Почему я получаю ошибку сегментации?