Linux AMD64 从复制的程序集中调用 C 库函数

     2023-02-16     64

关键词:

【中文标题】Linux AMD64 从复制的程序集中调用 C 库函数【英文标题】:Linux AMD64 call C library functions from copied assembly 【发布时间】:2015-05-30 17:44:14 【问题描述】:

如何从 memcpy 的汇编函数调用 C 库函数?

我正在制作一个示例测试代码,如何在 Linux、AMD64 上分配和更改内存保护以运行从 C 中任意生成的代码。 我所做的是在我的主程序(用 C 编写)旁边编译一个小的 GAS 汇编函数,然后在运行时将汇编二进制 blob 复制到一块可执行内存中并跳转到其中。 这部分工作正常。

但是如果我从复制的程序集 blob 中调用 C 库 puts() 会由于函数地址错误而导致段错误?!我该如何解决?

汇编代码块:

       .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。

【问题讨论】:

能否也提供已编译程序集 blob 的 objdump? blob 期望 puts 位于相对于代码的特定位置,因此如果您移动代码,则不会到达正确的位置。根据具体情况,您可以从 C 端传入指向 puts 的指针。 首先,我怀疑你的意思是reinterpret_cast。其次,我确实认为reinterpret_cast 是一种 C++ 主义,在 C 中无效——但我以前错了。 哎呀,我尝试从 C++ 简化代码,所以 reinterpret_cast 是错误的。这是 objdump -d pastebin.com/bUgjL1SK 【参考方案1】:

也许你可以这样做

push %rbp
mov %rsp, %rbp
# puts("Hello World\n")
mov $_message, %rdi
mov $puts, %eax
call %eax
pop %rbp
ret

从而迫使call 成为一个绝对的。问题是汇编器是否不会出于自己的目的对其进行优化。

【讨论】:

谢谢! mov $puts, *%rax 工作!当链接器随机打乱电话时,我快疯了.. 对不起,我忘了你是在 64 位机器上。是的,链接器有时会让我们的时间变得比我们想要的更有趣。【参考方案2】:

不可能从 memcpy-ed 代码块调用任何 C 标准库函数(或任何链接),就好像链接器决定 puts 函数入口点在它原来的位置之外的任何地方复制的代码块只是格式错误。第一个答案停止工作,第二个二进制 blob 与程序的其余部分不同步。

唯一的解决方法是在运行时修改二进制 blob,并像 C 链接器那样将当前的实际函数地址(来自 C 程序)分配给 blob。

【讨论】:

这是因为 AMD64 使用 RIP 相对地址来处理所谓的(在这种情况下具有讽刺意味)与位置无关的代码。关键是,一个库可以在任何内存地址加载,所以如果没有 RIP-relative,一个想要调用同一个库中的其他函数的函数要么需要在每个调用站点,在动态链接时进行地址修复。 (即对于每个进程启动)。 32 位 x86 必须这样做,这就是为 AMD64 引入 RIP-relative 的原因。

linux / amd64 C与C ++上的abi差异

】linux/amd64C与C++上的abi差异【英文标题】:abidifferenceonlinux/amd64CvsC++【发布时间】:2017-12-1422:33:41【问题描述】:我有带有此类API的C库:#ifdef__cplusplusextern"C"#endifstructFoovoid*p;intlen;;structFoof(void*opaque,intparam);voidfoo_free(structFoo* 查看详情

为啥在 x64 程序集中将复制变量地址移动到寄存器?

】为啥在x64程序集中将复制变量地址移动到寄存器?【英文标题】:Whydoesmovecopyvariableaddresstoregister,inx64assembly?为什么在x64程序集中将复制变量地址移动到寄存器?【发布时间】:2014-01-0821:48:14【问题描述】:我使用AT&T语法在... 查看详情

从反射生成的程序集中调用 lambda

】从反射生成的程序集中调用lambda【英文标题】:CallingalambdafromReflection-generatedassembly【发布时间】:2013-03-2017:28:28【问题描述】:我正在使用Reflection.Emit创建一个程序集,我希望它调用一个特殊的回调。这是代码的简化版本:pu... 查看详情

如何从 gcc 内联 arm7 程序集中调用 c++ 成员函数

】如何从gcc内联arm7程序集中调用c++成员函数【英文标题】:Howtocallac++memberfunctionfromgccinlinearm7assembly【发布时间】:2012-08-0422:04:20【问题描述】:我有以下在x86上工作的代码,我需要将其转换为gcc/arm7内联汇编。似乎替换堆栈只... 查看详情

linux下用c语言写出复制黏贴文件

复制文件命令是cp这个要怎么去写,一条命令写成两段,操作的时候分别执行吗???c语言调用cp命令和参与systeam"cp--h";不过我觉得用shell脚本来工作效率好点儿参考技术A首先源文件和目的文件作为参数传入main(argc.argv[])创建目... 查看详情

golang如何交叉编译(代码片段)

...支持交叉编译,即在一个平台上生成另一个平台的可执行程序。方法如下:Mac下编译Linux和Windows64位可执行程序CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.goCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.goLinux下编译Mac和Windows64位可执行程序CGO_ENA... 查看详情

使用寄存器而不是堆栈从 x64 程序集调用 C 函数

】使用寄存器而不是堆栈从x64程序集调用C函数【英文标题】:CallingCfunctionfromx64assemblywithregistersinsteadofstack【发布时间】:2015-04-2012:06:59【问题描述】:Thisanswer让我很困惑。根据standardCcallingconventions,调用C函数的标准方法是将pu... 查看详情

使用 struct 参数从程序集中调用 C++ 函数

】使用struct参数从程序集中调用C++函数【英文标题】:CallingC++functionfromassembly,withstructargument【发布时间】:2013-06-2213:41:41【问题描述】:我遇到了一些意外行为,这可能意味着我不完全理解编译器在做什么。考虑以下人为设计... 查看详情

go-跨平台编译(代码片段)

...行的可执行文件了。Mac下编译Linux和Windows平台64位可执行程序:CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildLinux下编译Mac和Windows平台64位可执行程序:CGO_ENABLED=0GOOS=darwinGOARCH=amd64gobuildCGO_ENABLED=0GOOS=windowsGOARCH=... 查看详情

如何从 C/C++ 程序中找到终端列的数量? [复制]

】如何从C/C++程序中找到终端列的数量?[复制]【英文标题】:HowcanIfindthenumberofterminalcolumnsfromaC/C++program?[duplicate]【发布时间】:2011-10-0304:15:53【问题描述】:可能重复:GettingterminalwidthinC?在Linux和OSX上,我的shell报告$COLUMNS具有... 查看详情

golang交叉编译

...持交叉编译,在一个平台上生成另一个平台的可执行程序。Mac下编译Linux和Windows64位可执行程序CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.goCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.goLinux下编译Mac和Windows64位可执行程... 查看详情

如何从应用程序域中所有加载的程序集中获取所有静态类并使用反射调用静态方法

】如何从应用程序域中所有加载的程序集中获取所有静态类并使用反射调用静态方法【英文标题】:Howtogetallthestaticclassesfromalltheloadedassembliesinanapp-domainandinvokeastaticmethodusingreflection【发布时间】:2015-02-1211:20:52【问题描述】:我... 查看详情

提取全局应用程序集中的dll

...INDOWS\assembly在资源管理器中打开这个路径看到的东西不能复制,右键中也只有“卸载”和“属性”两个菜单。在命令提示符下切换到C:\WINDOWS\assembly目录,使用Copy命令会报找不到文件。从网上找到了一个方法:“Subs... 查看详情

nolibsigar-amd64-linux.soinjava.library.path解决方法

关于sigar的介绍可以参考这边博文:https://www.cnblogs.com/luoruiyuan/p/5603771.html在Linux上运行java程序时出现 nolibsigar-amd64-linux.soinjava.library.path这个错误时只需将两个文件拷到你的工程下的lib包下即可libsigar-amd64-linux.solibsigar-x86-l 查看详情

从 Python 调用 C++ 64 位共享库

】从Python调用C++64位共享库【英文标题】:CallingC++64bitsharedlibraryfromPython【发布时间】:2015-02-2109:38:58【问题描述】:我想使用来自python2.7.8的给定C++64位共享库(linux下的.so文件)中的函数。C++共享库的Theheader有这个功能:EXPORT_C... 查看详情

在现代 amd64 CPU 上进行 memset 的最快方法

...s【发布时间】:2014-03-1220:40:02【问题描述】:我想在amd64程序集中用零填充一个4096字节数组(与4096字节边界对齐)。我正在寻找便携式和单CPU类型的解决方案。我知道repstosq可以解决问题,但是有更快的方法吗?MMX?上证所?... 查看详情

从assembly调用c函数(printf)时的segfault(代码片段)

我在linux上使用NASM编写一个基本的汇编程序,它从C库(printf)调用一个函数。不幸的是,我这样做会导致分段错误。注释掉对printf的调用允许程序无错误地运行。;Buildusingthesecommands:;nasm-felf64-g-Fstabs<filename>.asm;gcc<filename>... 查看详情

如何从另一个模块调用导出的内核模块函数?(代码片段)

...写了三个函数。然后我构建并加载了模块,然后将mycode.h复制到<kernel>/include/linux中。在设备驱动程序中,我有一个#include<linux/mycode.h>并调用这三个函数。但是当我构建驱动程序模块时,我得到三个链接器警告,说明这些... 查看详情