Я пытаюсь написать исполняемый загрузчик ELF для Linux x86-64, аналогично this , который был реализован на ARM. У продвинутого класса ОС Криса Россбаха есть лаборатория, которая делает в основном то, что я хочу.Моя цель состоит в том, чтобы загрузить простой (статически связанный) двоичный файл типа "привет мир" в память моего процесса и запустить его без execve
ing.Я успешно mmap
запустил файл ELF, настроил стек и перешел к точке входа ELF (_start
).
// put ELF file into memory. This is just one line of a complex
// for() loop that loads the binary from a file.
mmap((void*)program_header.p_vaddr, program_header.p_memsz, map, MAP_PRIVATE|MAP_FIXED, elffd, program_header.p_offset);
newstack = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); // Map a page for the stack
if((long)newstack < 0) {
fprintf(stderr, "ERROR: mmap returned error when allocating stack, %s\n", strerror(errno));
exit(1);
}
topstack = (unsigned long*)((unsigned char*)newstack+4096); // Top of new stack
*((unsigned long*)topstack-1) = 0; // Set up the stack
*((unsigned long*)topstack-2) = 0; // with argc, argv[], etc.
*((unsigned long*)topstack-3) = 0;
*((unsigned long*)topstack-4) = argv[1];
*((unsigned long*)topstack-5) = 1;
asm("mov %0,%%rsp\n" // Install new stack pointer
"xor %%rax, %%rax\n" // Zero registers
"xor %%rbx, %%rbx\n"
"xor %%rcx, %%rcx\n"
"xor %%rdx, %%rdx\n"
"xor %%rsi, %%rsi\n"
"xor %%rdi, %%rdi\n"
"xor %%r8, %%r8\n"
"xor %%r9, %%r9\n"
"xor %%r10, %%r10\n"
"xor %%r11, %%r11\n"
"xor %%r12, %%r12\n"
"xor %%r13, %%r13\n"
"xor %%r14, %%r14\n"
:
: "r"(topstack-5)
:"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14");
asm("push %%rax\n"
"pop %%rax\n"
:
:
: "rax");
asm("mov %0,%%rax\n" // Jump to the entry point of the loaded ELF file
"jmp *%%rax\n"
:
: "r"(jump_target)
: );
Затем я перешагиваю этот код в gdb
.Я вставил несколько первых инструкций кода запуска ниже.Все отлично работает до первой инструкции push
(помеченной).push
вызывает segfault.
0x60026000 xor %ebp,%ebp
0x60026002 mov %rdx,%r9
0x60026005 pop %rsi
0x60026006 mov %rsp,%rdx
0x60026009 and $0xfffffffffffffff0,%rsp
0x6002600d * push %rax
0x6002600e push %rsp
0x6002600f mov $0x605f4990,%r8
Я пытался:
- Использование стека из исходного процесса.
mmap
с новымstack (как в вышеприведенном коде): (1) и (2) оба вызывают segfaults. push
ing и pop
вход в / из стека перед jmp
входом в загруженный файл ELF,Это не вызывает segfault. - Изменение флагов защиты для стека во втором
mmap
на PROT_READ | PROT_WRITE | PROT_EXEC
.Это не имеет значения.
Я подозреваю, что, возможно, это как-то связано с дескрипторами сегментов (может быть?).Кажется, что код из загружаемого файла ELF не имеет доступа для записи в сегмент стека, где бы он ни находился.Я не пытался изменить дескриптор сегмента для недавно загруженного двоичного файла или изменить регистры архитектурного сегмента.Это необходимо?Кто-нибудь знает, как это исправить?