【问题标题】:How could i define a function at run-time in c我如何在 c 中在运行时定义一个函数
【发布时间】:2020-06-02 08:19:11
【问题描述】:

我正在尝试在 arm cpu(cortex a72) 中使用 c 语言在运行时定义和调用一个函数。为此,我实现了如下代码:

#include <stdio.h>
#include <sys/mman.h>

char* ibuf;
int   pbuf = 0;
#define ADD_BYTE(val) do{ibuf[pbuf] = val; pbuf++;} while(0)
void (*routine)(void);

void MakeRoutineSimpleFunc(void)
{
    //nop
    ADD_BYTE(0x00);
    ADD_BYTE(0xf0);
    ADD_BYTE(0x20);
    ADD_BYTE(0xe3);

    //bx lr
    ADD_BYTE(0x1e);
    ADD_BYTE(0xff);
    ADD_BYTE(0x2f);
    ADD_BYTE(0xe1);
}

int main(void)
{
    ibuf = (char*)mmap(NULL, 4 * 1024, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    MakeRoutineSimpleFunc();
    routine = (void(*)())(ibuf);
    routine();
}

如您所见,在上面的代码中,首先我分配了一个可执行内存区域并将其地址分配给 ibuf,然后我在 ibuf 中放入了一些简单的指令(“nop”和“bx lr”,这意味着在 arm 中返回) 然后我尝试通过函数指针调用这个函数。

但是当我想通过函数指针调用函数时,出现“分段错误”错误。顺便说一句,当我尝试使用 GDB 调试器程序运行应用程序时成功运行,没有任何错误。

我在上面的代码中遗漏了什么导致“分段错误”?

我想补充一点,当我在编译时将上述指令(“nop”和“bx lr”表示在 arm 中返回)添加到如下函数时,函数可以正常工作。

void f2(void)
{
    __asm__ volatile (".byte 0x00, 0xf0, 0x20, 0xe3");
    __asm__ volatile (".byte 0x1e, 0xff, 0x2f, 0xe1");
}

编辑1: 为了检查运行时函数的有效性,我用 ghidra 反汇编器删除了 f2 prolog 和 epilogue,所以 f2 的汇编代码是这样的:

**************************************************************
FUNCTION                     
**************************************************************
undefined FUN_0000083c()
undefined         r0:1           <RETURN>
undefined4        Stack[-0x4]:4  local_4
FUN_0000083c                                    XREF[1]: FUN_00000868:000008a4(c)  
        0000083c 00 f0 20 e3     nop
        00000840 00 f0 20 e3     nop
        00000844 00 f0 20 e3     nop
        00000848 00 f0 20 e3     nop
        0000084c 00 f0 20 e3     nop
        00000850 00 f0 20 e3     nop
        00000854 1e ff 2f e1     bx         lr
        00000858 00 f0 20 e3     nop
        0000085c 00 f0 20 e3     nop
        00000860 00 f0 20 e3     nop
        00000864 00 f0 20 e3     nop

而且它又可以正常工作了。

编辑2: 我想添加一些可能有助于解决问题的东西,正如我在汇编程序中看到的那样,编译器使用“blx r3”指令调用“例程”函数,而它使用“bl'symbol name'”调用其他函数。据我所知,blx 可以将处理器状态从 ARM 更改为 Thumb,反之亦然。这点会导致问题吗?

编辑3: main函数的反汇编如下:

**************************************************************
FUNCTION
**************************************************************
int __stdcall main(void)
int               r0:4           <RETURN>
undefined4        Stack[-0xc]:4  local_c                                 XREF[1]:     00010d44(W)  
undefined4        Stack[-0x10]:4 local_10                                XREF[1]:     00010d4c(W)  
                             main                                            XREF[4]:     Entry Point(*), 
                                                                                          _start:00010394(*), 000103a8(*), 
                                                                                          .debug_frame::000000a0(*)  
        00010d34 00 48 2d e9     stmdb      sp!,{ r11 lr }
        00010d38 04 b0 8d e2     add        r11,sp,#0x4
        00010d3c 08 d0 4d e2     sub        sp,sp,#0x8
        00010d40 00 30 a0 e3     mov        r3,#0x0
        00010d44 04 30 8d e5     str        r3,[sp,#local_c]
        00010d48 00 30 e0 e3     mvn        r3,#0x0
        00010d4c 00 30 8d e5     str        r3,[sp,#0x0]=>local_10
        00010d50 22 30 a0 e3     mov        r3,#0x22
        00010d54 07 20 a0 e3     mov        r2,#0x7
        00010d58 01 1a a0 e3     mov        r1,#0x1000
        00010d5c 00 00 a0 e3     mov        r0,#0x0
        00010d60 7d fd ff eb     bl         mmap                                         
        00010d64 00 20 a0 e1     cpy        r2,r0
        00010d68 50 30 9f e5     ldr        r3,[->ibuf]                                      
        00010d6c 00 20 83 e5     str        r2,[r3,#0x0]=>ibuf
        00010d70 48 30 9f e5     ldr        r3,[->ibuf]
        00010d74 00 30 93 e5     ldr        r3,[r3,#0x0]=>ibuf
        00010d78 03 10 a0 e1     cpy        r1,r3
        00010d7c 40 00 9f e5     ldr        r0=>s_ibuf:_%x_00010e40,[PTR_s_ibuf:_%x_00010d
        00010d80 69 fd ff eb     bl         printf
        00010d84 ae fe ff eb     bl         MakeRoutineSimpleFunc
        00010d88 30 30 9f e5     ldr        r3,[->ibuf]
        00010d8c 00 30 93 e5     ldr        r3,[r3,#0x0]=>ibuf
        00010d90 03 20 a0 e1     cpy        r2,r3
        00010d94 2c 30 9f e5     ldr        r3,[->routine]
        00010d98 00 20 83 e5     str        r2,[r3,#0x0]=>routine
        00010d9c 24 30 9f e5     ldr        r3,[->routine]
        00010da0 00 30 93 e5     ldr        r3,[r3,#0x0]=>routine
        00010da4 33 ff 2f e1     blx        r3
        00010da8 1c 00 9f e5     ldr        r0=>DAT_00010e4c,
        00010dac 61 fd ff eb     bl         puts
        00010db0 00 30 a0 e3     mov        r3,#0x0
        00010db4 03 00 a0 e1     cpy        r0,r3
        00010db8 04 d0 4b e2     sub        sp,r11,#0x4
        00010dbc 00 88 bd e8     ldmia      sp!,{ r11 pc }

如您所见,在地址“00010da4”处使用“blx r3”指令调用的例程。我还打印了ibuf的地址,它是“0xb6ff8000”。

【问题讨论】:

  • 如何确保 ibuf 数组是字对齐的?
  • 你在调用函数时是在 arm 还是 thumb 模式,你的目标代码是 arm 还是 thumb?
  • @old_timer 感谢您的评论,当我使用“mmap”功能分配内存页面时,ibuf 的前 12 位将为零(对于 4KB 页面),并且肯定 ibuf 可以被 4 整除并且是字​​对齐的.
  • @old_timer 我对 arm 不是很熟悉,但是我在反汇编器中看到每条指令在 main 函数中占用 4 个字节,我们可以得出结论,我们处于 arm 模式吗?

标签: c function arm function-pointers


【解决方案1】:

我认为,您可以直接在字符串“二进制代码”中输入操作码,然后使用((void*)STRING)() 执行代码。但是,您可能还想了解 gcc 如何实现蹦床,因为这是 gcc 生成代码的方式,这些代码在堆栈上创建代码并在那里跳转执行。

【讨论】:

  • 感谢您的回复,只是有一点我没有告诉您。我需要在程序的特定地址中定义功能。使用我的代码,如果我将特定地址发送到 mmap 而不是 NULL,我可以做到这一点(如果我可以解决“分段错误”问题 :))。但我无法想象我怎么能用你的例子做到这一点。
  • @alirezasadeghpour 您需要非常了解目标汇编程序,并且不要使用使用绝对值但使用相对值等的指令。此外,为了能够从您的代码返回,您需要知道编译器将如何实现,知道在哪里可以找到返回地址。
  • @alirezasadeghpour 将汇编程序与 C 混合使用是非常特定于实现的,您需要非常积极地学习给定实现的内部结构,以便能够混合这些东西。
  • 堆栈和返回很好,编译器将其作为函数调用,因此一切都会到位。这取决于正在使用什么指令来调用(在这种情况下,它应该是 bx 或 blx,因为它无法在链接时确定)、代码的对齐方式和权限。 25% 的成功几率基于对齐,但 mmap 可能会一直返回对齐。 OP 已经完成了 99% 的工作,如果有什么问题也没有做太多
猜你喜欢
  • 2011-06-24
  • 1970-01-01
  • 2016-11-30
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 2017-02-06
  • 2014-12-04
  • 1970-01-01
相关资源
最近更新 更多