试图制作我自己的加载器,但无法实现数据部分

Attempting to make my own loader, but cannot implement the data section

出于学习目的,我正在尝试实现自己的二进制加载程序,但无法找出数据段。

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

这是我正在尝试的汇编程序 运行。我用 nasm -f elf64 test.s -o test.o && ld test.o -o test.bin

编译

我的装载机是这样的:

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
}

但是当我 运行 它时,什么也没有打印出来。

我在 GDB 下试过 运行ning 它,程序在退出系统调用后立即退出,mov rax, 60mov rdi, 0,所以我知道系统调用部分有效。我认为问题出在 hello world 程序中 helloworld 的地址。 GDB 说它仍在地址 0x402000 下,这可能不是虚拟内存下的同一地址。令人惊讶的是,测试函数在 0x401000 和 objdump 中,但在 运行 GDB 时完全不同,它确实被调用了。有人知道如何实施吗?

我不确定这会有多大帮助,但我运行正在使用英特尔下的 x64 Linux。

nasm -f elf64 test.s -o test.o
ld test.o -o test.bin

不幸的是,我没有 NASM,但是如果我使用 GNU 汇编程序而不是 NASM,上面的行会生成一个 position-dependent 文件。

这意味着 phdr.p_vaddr 没有指定一个相对于变量 exec 的值,但是 phdr.p_vaddr 指定了一个不能更改的绝对地址。

假设符号 helloworld 位于数据段的开头,指令 mov rcx, helloworld 将简单地将值 phdr.p_vaddr 加载到寄存器 rcx - 并且不是值 exec + phdr.p_vaddr.

但是,由于地址 phdr.p_vaddr 可能已被使用,您不能简单地将代码加载到那里!

如果您想从已经 运行 的程序中加载代码,唯一的可能是 so-called 可以加载到内存中不同地址的“位置无关代码”...

顺便说一句:

64-bit x86 Linux不取rbxrcxrdx中的参数,而是rdi、[=24=中的参数] 和 rdx.