Linux AMD64 从复制的程序集调用 C 库函数
Linux AMD64 call C library functions from copied assembly
如何从 memcpy 汇编函数调用 C 库函数?
我正在制作一个示例测试代码,如何在 Linux、AMD64 到 运行 上分配和更改内存保护从 C 任意生成的代码。
我所做的是在我的主程序(用 C 编写)旁边编译一个小的 GAS 汇编函数,然后在 运行 时间内将汇编二进制 blob 复制到片段可执行内存中并跳入其中。
这部分工作正常。
但是我如果从复制的程序集 blob 调用 C 库 puts() 会导致错误的函数地址导致段错误?!我该如何解决?
汇编代码 blob:
.text
.global _print_hello_size
.global _print_hello
.type _print_hello,@function
_print_hello:
push %rbp
mov %rsp, %rbp
# puts("Hello World\n")
mov $_message, %rdi
call puts # <-- SEGFAULT
pop %rbp
ret
procend: # mark end address of the _print_hello code
.section .rodata
_message:
.asciz "Hello, world\n"
_print_hello_size:
.long procend - _print_hello
然后在 C main() 中我做(伪代码):
// Import assembler function and its size
extern "C" void _print_hello(void);
extern "C" const long _print_hello_size;
int main() {
// Use special function that allocates Read-Write-Executable memory
void * memexec = MallocExecutableMemory(1024);
// Copy the binary asm blob, memexec is aligned to at least 16-bytes
memcpy(memexec, (void*)_print_hello, _print_hello_size);
void (*jmpfunc)(void) = (void (*)(void))memexec;
jmpfunc(); // Works, jumps into copied assembly func
return 0;
}
稍后,如果这甚至可能,甚至不会编译 asm blob,而只是将示例程序编码在 unsigned char execblob[] = { 0xCC,0xCC,0xC3,..} 并将其复制到可执行区域。这段代码探索如何从 C 开始生成 asm。
也许你可以
push %rbp
mov %rsp, %rbp
# puts("Hello World\n")
mov $_message, %rdi
mov $puts, %eax
call %eax
pop %rbp
ret
从而迫使 call
成为绝对的。问题是汇编程序是否不会出于自己的目的对其进行优化。
不可能从 memcpy 代码块 调用任何 C 标准库函数(或任何链接函数),就好像链接器决定 puts 函数入口点在其他任何地方一样与最初复制的代码块相比,它只是格式错误。第一个答案停止工作第二个二进制 blob 与程序的其余部分不同步。
唯一的解决办法是在 运行 时修改二进制 blob,并像 C 链接器那样将当前实际函数地址(从 C 程序)分配给 blob。
如何从 memcpy 汇编函数调用 C 库函数?
我正在制作一个示例测试代码,如何在 Linux、AMD64 到 运行 上分配和更改内存保护从 C 任意生成的代码。 我所做的是在我的主程序(用 C 编写)旁边编译一个小的 GAS 汇编函数,然后在 运行 时间内将汇编二进制 blob 复制到片段可执行内存中并跳入其中。 这部分工作正常。
但是我如果从复制的程序集 blob 调用 C 库 puts() 会导致错误的函数地址导致段错误?!我该如何解决?
汇编代码 blob:
.text
.global _print_hello_size
.global _print_hello
.type _print_hello,@function
_print_hello:
push %rbp
mov %rsp, %rbp
# puts("Hello World\n")
mov $_message, %rdi
call puts # <-- SEGFAULT
pop %rbp
ret
procend: # mark end address of the _print_hello code
.section .rodata
_message:
.asciz "Hello, world\n"
_print_hello_size:
.long procend - _print_hello
然后在 C main() 中我做(伪代码):
// Import assembler function and its size
extern "C" void _print_hello(void);
extern "C" const long _print_hello_size;
int main() {
// Use special function that allocates Read-Write-Executable memory
void * memexec = MallocExecutableMemory(1024);
// Copy the binary asm blob, memexec is aligned to at least 16-bytes
memcpy(memexec, (void*)_print_hello, _print_hello_size);
void (*jmpfunc)(void) = (void (*)(void))memexec;
jmpfunc(); // Works, jumps into copied assembly func
return 0;
}
稍后,如果这甚至可能,甚至不会编译 asm blob,而只是将示例程序编码在 unsigned char execblob[] = { 0xCC,0xCC,0xC3,..} 并将其复制到可执行区域。这段代码探索如何从 C 开始生成 asm。
也许你可以
push %rbp
mov %rsp, %rbp
# puts("Hello World\n")
mov $_message, %rdi
mov $puts, %eax
call %eax
pop %rbp
ret
从而迫使 call
成为绝对的。问题是汇编程序是否不会出于自己的目的对其进行优化。
不可能从 memcpy 代码块 调用任何 C 标准库函数(或任何链接函数),就好像链接器决定 puts 函数入口点在其他任何地方一样与最初复制的代码块相比,它只是格式错误。第一个答案停止工作第二个二进制 blob 与程序的其余部分不同步。
唯一的解决办法是在 运行 时修改二进制 blob,并像 C 链接器那样将当前实际函数地址(从 C 程序)分配给 blob。