【问题标题】:Can I be sure that the binary code of the functions will be copied sequentially?我可以确定函数的二进制代码将按顺序复制吗?
【发布时间】:2020-11-24 14:05:50
【问题描述】:

对不起,如果这个问题已经存在,因为我希望使用这种方法,但我只是不知道它是如何调用的。所以,我的目的是从内存中执行函数序列,为此我复制了第一个和最后一个函数的大小。

这是我的第一次尝试:

source.cpp

void func1(int var1, int var2)
{
  func2();
  func3();
  //etc.
}
void func2(...){...}
void func3(...){...}
void funcn(){return 123;}//last func as border, I do not use it

//////////////////////////////////////////////////

main.cpp

#include"source.cpp"

long long size= (long long)funcn-(long long)func1;// i got size of binary code of this funcs;
// and then i can memcpy it to file or smth else and execute by adress of first

首先它工作正常,但在更新我的功能后它崩溃了。大小已变为负数。 然后我试图将它附加到内存中:

source.cpp

extern void(*pfunc1)(int, int);
extern void(*pfuncn)();

void(*pfunc1)(int , int) = &func1;
void(*funcn)() = &funcn;

static void __declspec(noinline) func1(int var1, int var2)
{
 //the same impl
}
static void __declspec(noinline) func2(...){...}
static void __declspec(noinline) func3(...){...}
static void __declspec(noinline) funcn(...){retunr 123;}

//////////////////////////////////
main.cpp
#include"source.cpp"

long long size= (long long) pfuncn - (long long) pfunc1;
//same impl

这在我第一次更新后有效,但后来我不得不再次更新它,现在这给了我错误的尺寸。大小接近 900 多个字节。我改变了一些函数,大小变成了 350+ 字节,我没有改变那么多。 我禁用了优化和内联优化。

所以我的问题是如何确保我的 func1 的地址比最后一个 funcn 少,以及什么会改变它们在内存中的位置。感谢您的关注。

【问题讨论】:

  • 无法保证 func1 到 funcn 在内存中是相邻的,更不用说它们存在于最终的二进制文件中。无论如何,这似乎是一个XY problem。你需要这个做什么?
  • 未定义行为中不相关对象之间的指针运算。您必须使用 c 样式转换的事实应该让您暂停,因为他们不关心语言规则。
  • 如果有理由不使用函数指针数组?
  • C 和 C++ 都不能保证指向函数的指针实际上以任何形式包含函数代码所在的地址(它可能是函数句柄的地址或其他结构化数据访问函数)或者一个函数的代码在一个连续的内存段中,更不用说不同的函数是连续的了。不要写这样的代码。无论你想要完成什么,这都是错误的方式。
  • @zwol 例如将代码从闪存复制到 SRAM。为什么?为了最大限度地减少中断延迟,实现引导加载程序(当您需要擦除闪存时)和许多其他用例。不是每个人都以 Linux 或操作系统为中心

标签: c++ c reverse-engineering


【解决方案1】:
// and then i can memcpy it to file or smth else and execute by adress of first

将其复制到内存中,然后在分配的内存中调用它,然后按分配地址调用。

这需要说明:

您不能将代码从一个位置复制到另一个位置并希望它能够正常工作。

  1. 无法保证调用函数所需的所有代码 位于一个连续的块中。
  2. 无法保证函数指针实际上指向 所需代码的开头。
  3. 无法保证您可以有效地写入可执行内存。对于操作系统来说,你看起来很像病毒。
  4. 无法保证代码是可重定位的(在移动到不同位置后能够工作)。为此,它只需要使用相对地址

简而言之:除非你有超出标准 C++ 范围的支持工具,否则别想了。

【讨论】:

  • In short: don't even think about it. 不是事实。非常常用于嵌入式开发。例如,将代码从 FLASH 复制到 SRAM 并从那里执行。所以程序员应该考虑一下并知道如何归档它
【解决方案2】:

仅限 GCC 家族!

您可以强制编译器将整个函数放在单独的部分。然后就可以知道函数所在的内存区域了。

int __attribute__((section(".foosection"))) foo()
{
    /* some code here */
}

在链接脚本中需要添加的.text

 .text :
  {
 
      /* ... */

    __foosection_start = .;
     *(*foosection)
     *(.foosection*) 
     __foosection_end = .;

     /* .... */

在你想知道或使用的地方

extern unsigned char __foosection_start[];
extern unsigned char __foosection_end[];

void printfoo()
{
    printf("foosection start: %p, foosection end: %p\n ", (void *)__foosection_start, (void *)__foosection_end);
}

【讨论】:

    【解决方案3】:

    这可能是不可能的,因为你没有提到一个要求,但为什么不使用函数指针数组呢?

    std::function<void()> funcs[] = {
        func2,
        func3,
        [](){ /* and an inline lambda, because why not */ },
    };
    
    // Call them in sequence like so: 
    for (auto& func: funcs) {
       func();
    }
    

    【讨论】:

    • 我将通过分配内存在其他项目中使用这个函数的memcpy,并从分配地址调用第一个函数。所以这个指针数组是错误的。我应该改变我的问题以避免误解?谢谢
    猜你喜欢
    • 2012-01-19
    • 1970-01-01
    • 2018-04-25
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    相关资源
    最近更新 更多