试图制作我自己的加载器,但无法实现数据部分
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, 60
和 mov 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不取rbx
、rcx
和rdx
中的参数,而是rdi
、[=24=中的参数] 和 rdx
.
出于学习目的,我正在尝试实现自己的二进制加载程序,但无法找出数据段。
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, 60
和 mov 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不取rbx
、rcx
和rdx
中的参数,而是rdi
、[=24=中的参数] 和 rdx
.