【问题标题】:C++ How To Read First Couple Bytes Of Function? (32 bit Machine)C ++如何读取函数的前几个字节? (32位机)
【发布时间】:2018-02-28 05:49:34
【问题描述】:

假设我有一个这样的函数(完全随机,例如,我只是在 30 秒内写出来)

bool exampleAuthetnication(char *a, char *b)
{
    bool didAuthenticate = false;
    if(strcmp(a, b) == 0)
    {
        didAuthenticate = true;
    }

    if(didAuthenticate)
    {
        return true;
    }
    else
    {
        stopExecutable();
        return false;
    }
}

如何读取这个函数的前几个字节?

我想出了这个

int functionByteArray[10];
for (int i = 0; i < 10; i++)
{
    functionByteArray[i] = *(int*)(((int)&exampleAuthetnication) + (0x04 * i));
}

其背后的逻辑是我们获取函数的内存地址(在本例中为exampleAuthetnication()),然后我们转换为 int 指针,然后取消引用以获取我们尝试读取的当前字节行的值,然后存储在functionByteArray,但它似乎无法正常工作。我究竟做错了什么?我正在努力实现的目标可能吗?

【问题讨论】:

  • "但它似乎无法正常工作" 为什么?结果是什么,你期待什么结果?
  • @tkausl 结果似乎只是一些随机数,它们不符合在 IDA 中查看函数的基础
  • 这可能是因为您将 32 位读取为 LittleEndian,而 IDA 将数据显示为字节。
  • @tkausl 也是,即使我更改了此函数的前几行(exampleAuthetnication),存储在 functionByteArray 中的字节也不会改变?
  • 你没有解释为什么要获取代码的前几个字节,以及你想在哪台机器和操作系统(和 ABI)上这样做

标签: c++ security assembly memory tampering


【解决方案1】:

理论上(根据 C++11 标准),您甚至不能将函数指针转换为数据指针(在 Harvard architectures 上,代码和数据位于不同的内存和不同的地址空间中)。一些操作系统或处理器也可能禁止读取可执行文件code segments(请阅读关于NX bit)。

实际上,在运行某些操作系统(如 Linux 或 Windows)的 x86-64(或 32 位 x86)上,函数代码是 bytes 的序列,可以不对齐,位于 ( common) 其进程的虚拟地址空间。所以你至少应该有char functionByteArray[40];,你可以使用&lt;string&gt;中的std::memcpy并做一些

std::memcpy(functionByteArray, (char*)&exampleAuthetnication,
            sizeof(functionByteArray));

最后你的代码是错误的,因为 -on x86-64 特别是 - int 与指针的大小不同(所以 (int)&amp;exampleAuthetnication 正在丢失地址的高位字节)。你至少应该使用intptr_t。并且int 比代码有更强的对齐约束。

顺便说一句,您也可以要求编译器显示生成的汇编代码。使用GCC 编译您的exampleAhtetnication 使用g++ -O -fverbose-asm -S 的C++ 代码并查看生成的.s 文件。

请注意,C++ 编译器可能会optimize 从代码段中“删除”某些函数(例如,因为该函数已在各处内联),或者将函数代码分成几部分,或者将其放入 exampleAhtetnication 代码“内部”另一个函数...

【讨论】:

  • 如果我没有转换为(int),我将无法编译并且会出现错误,这就是我这样做的原因。不过谢谢你的回复!
  • 我可能错了,但我认为如果您使用函数的地址,它将使用非内联变体。
  • 是的。但编译器可能仍会优化以拆分其代码,或在 another 内联 exampleAhtetnication 的函数内发出跳转。
  • @BasileStarynkevitch:是的,某些编译器可能会这样做,但 gcc 不会。如果你获取一个函数的地址,即使编译单元/程序不需要它,你也会得到一个独立的定义。
  • @MSalters:如果函数是staticinline,则编译器可以选择不发出独立定义。如果不是,我认为只有链接时优化可以放弃它,但如果在优化之后需要指向函数的有效指针,它就无法做到。 (但我不会称其为“链接器”。)
【解决方案2】:

C++ 源代码不是计算机要执行的指令列表;它是描述程序含义的语句的集合

您的编译器会解释这些语句并生成可以在我们的物理现实中实际执行的实际指令序列(通过汇编阶段)。

用于执行此操作的语言不提供任何工具来检查构成已编译程序的字节。通过未定义行为的魔力,您所有尝试转换函数指针等可能会随机给您一些相似的数据,但结果就是:未定义。

如果您想检查已编译的可执行文件,请从程序外部进行。例如,您可以使用十六进制编辑器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 2017-06-04
    • 2015-12-11
    相关资源
    最近更新 更多