Estoy tratando de implementar mi propio cargador binario con fines de aprendizaje, pero no puedo descifrar el segmento de datos.
section .data helloworld db "hello world", 10 section .text global _start test: ;just for testing ret _start: call test mov rax, 1 mov rbx, 1 mov rcx, helloworld mov rdx, 11 syscall mov rax, 60 mov rdi, 0 syscall Este es mi programa de ensamblaje que estoy tratando de ejecutar. Compilé con nasm -f elf64 test.s -o test.o && ld test.o -o test.bin
Mi cargador se ve así:
int main(int argc, char** argv) { char* bin = argv[1]; struct ElfLib lib = read_elf(bin); //just reading the elf library into the default structures (Elf64_Ehdr, Elf64_Phdr, etc...) unsigned char* exec = mmap(NULL, DEFAULT_MEM_SIZ, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); //allocating the virtual memory memset(exec, 0, DEFAULT_MEM_SIZ); for (int i = 0; i < lib.elf_header.e_phnum; i++) { Elf64_Phdr phdr = lib.program_headers[i]; fseek(lib.execfile, phdr.p_offset, SEEK_SET); switch (phdr.p_type) { case PT_LOAD: { //load the memory at the file offset into the virtual address of exec fread(exec + phdr.p_vaddr, sizeof(unsigned char), phdr.p_memsz, lib.execfile); break; } } int flags = PROT_NONE; #define HASFLAG(flag) if (phdr.p_flags & flag) flags|=flag HASFLAG(PROT_EXEC); //execute flag on HASFLAG(PROT_WRITE); //write flag on HASFLAG(PROT_READ); //read flag on mprotect(exec + phdr.p_vaddr, phdr.p_memsz, flags); } void (*ex)() = (void*)(exec + lib.elf_header.e_entry); ex(); //call the _start function in the virtual memory }Pero cuando lo ejecuto, no se imprime nada.
Intenté ejecutarlo bajo GDB, y el programa se cierra rápidamente después de la llamada al sistema de salida, con mov rax, 60 y mov rdi, 0 , así que sé que la parte de la llamada al sistema funciona. Creo que el problema está en la dirección de helloworld en el programa hello world. GDB dice que todavía está bajo la dirección 0x402000, que probablemente no sea la misma dirección bajo la memoria virtual. Sorprendentemente, la función de prueba está en 0x401000 con objdump, pero en una completamente diferente cuando se ejecuta con GDB, que es llamado. ¿Alguien tiene una idea sobre cómo implementar esto?
No estoy seguro de cuánto ayudará esto, pero estoy usando x64 Linux bajo Intel.
nasm -f elf64 test.s -o test.o ld test.o -o test.bin
Desafortunadamente, no tengo NASM, pero si uso el ensamblador GNU en lugar de NASM, las líneas anteriores dan como resultado un archivo dependiente de la posición.
Esto significa que phdr.p_vaddr no especifica un valor relativo a la variable exec , pero phdr.p_vaddr especifica una dirección absoluta que no debe cambiarse.
Suponiendo que el símbolo helloworld se encuentra al comienzo del segmento de datos, la instrucción mov rcx, helloworld simplemente cargará el valor phdr.p_vaddr en el registro rcx , y no el valor exec + phdr.p_vaddr .
Sin embargo, debido a que la dirección phdr.p_vaddr ya puede estar en uso, ¡no puede simplemente cargar su código allí!
La única posibilidad que tiene si desea cargar código de un programa que ya se está ejecutando es el llamado "código independiente de posición" que se puede cargar en diferentes direcciones en la memoria...
De paso:
Linux x86 de 64 bits no toma los parámetros en rbx , rcx y rdx , sino en rdi , rsi y rdx .