【发布时间】:2019-07-05 14:25:08
【问题描述】:
我想从 C++ 调用 ARM/ARM64 ASM 代码。 ASM 代码包含系统调用和对外部函数的重定位。 这里的ARM架构不是那么重要,我只是想从概念上了解如何解决我的问题。
我有以下在共享库中调用的 ASM 系统调用(来自objdump -d 的输出):
198: d28009e8 mov x8, #0x4f // #79
19c: d4000001 svc #0x0
1a0: b140041f cmn x0, #0x1, lsl #12
1a4: da809400 cneg x0, x0, hi
1a8: 54000008 b.hi 0 <__set_errno_internal>
1ac: d65f03c0 ret
这段代码调用fstatat64 syscall 并通过外部__set_errno_internal 函数设置errno。
readelf -r 显示 __set_errno_internal 函数的以下重定位:
00000000000001a8 R_AARCH64_CONDBR19 __set_errno_internal
我想从 C++ 中调用这段代码,所以我将它转换为缓冲区:
unsigned char machine_code[] __attribute__((section(".text"))) =
"\xe8\x09\x80\xd2"
"\x01\x00\x00\xd4"
"\x1f\x04\x40\xb1"
"\x00\x94\x80\xda"
"\x08\x00\x00\x54" // Here we have mentioned relocation
"\xc0\x03\x5f\xd6";
编辑:重要细节 - 我选择使用缓冲区(不是内联汇编等),因为我想在此缓冲区上运行额外的处理(例如字符串文字上的解密函数作为软件保护机制,但这在这里并不重要)被评估为机器码。
之后,可以将缓冲区强制转换为函数并直接调用以执行机器代码。显然搬迁有问题,它不是自动修复的,我必须手动修复它。但是在运行时我不能这样做,因为.text 部分是只读和可执行的。
虽然我几乎可以完全控制源代码,但我不能关闭堆栈保护和其他功能以使该部分可写(不要问为什么)。因此,似乎应该在链接阶段以某种方式执行重定位修复。据我所知,共享库在链接器修复重定位后包含相对偏移量(用于类似的外部函数调用),二进制 *.so 文件应包含正确的偏移量(无需运行时重定位工作),因此修复 machine_code链接期间的缓冲区应该是可能的。
我正在使用手动构建的 Clang 7 编译器,并且我可以完全控制 LLVM 通行证,所以我认为也许可以编写某种在链接时执行的 LLVM 通行证。虽然看起来 ld 最终被调用了,所以 LLVM 通行证在这里可能无济于事(这里不是专家)。
不同的想法也将不胜感激。 如您所见,问题非常复杂。也许你有一些方向/想法如何解决这个问题?谢谢!
【问题讨论】:
-
为什么不能在函数返回后在 C 端完成
__set_errno_internal?或者,您可以将函数地址作为参数传递。 -
好的,我得考虑一下。目前我有很多用 ASM 编写的系统调用包装器,所以我认为如果不需要对它们进行任何更改,那就太好了。但是我必须先测试一下,看看它是否有效。
-
为什么要把它写成缓冲区而不使用内联汇编?而不是采用操作码(十六进制数字),而是采用汇编器文本并将其转换为内联汇编器宏。您的正常构建过程将按照正常方式链接它。请参阅:GCC pre-process as assembler。
-
@Jester 根据您的想法,我通过删除
cmn、cneg和b.hi指令来删除重定位。有什么好处 - 这是对 ASM 系统调用包装器的一个非常简单的修改。这种方式通常返回 0(或在其他返回句柄的系统调用的情况下为正值)或负errno值(在错误的情况下)返回。之后,如果从汇编代码返回负值,我可以围绕它编写一个 C++ 包装器来手动设置errno。谢谢! -
您可以对任何代码运行加密。只需使用链接器或属性并放入输入部分(.text.encrypt)。您可以在本节的开头/结尾定义变量并运行在线/离线加密/解密。来自storing CRC in elf 的一些想法可以用于加密。不过,我看不到您将在哪里获得可以阻止攻击者的解密密钥;但它相当于数组,对重定位没有任何限制。
标签: c++ assembly linker arm llvm