是否可以仅查看原始函数的输入和输出来构建等效函数?

逆向工程 职能 散列函数
2021-07-03 06:12:57

想象一下,您正在对软件进行逆向工程。该软件使用了一个经过混淆和加密的库。该库包含一个函数,让我们调用它secret_function这个函数是一个纯函数(即它没有任何副作用,当使用相同的参数调用时,它总是返回相同的输出)。

假设我可以用secret_function我想要的任何参数调用我想要的次数,但我无法查看实现,是否可以用另一种语言(例如 python)构建等效函数,只分析输入和输出值?

这是一个示例实现secret_function

int secret_function(int a, int b) {
    if (a == 234) {
        return b*2 - a;
    }
    return a*b;
}

我想到的一种存档方法是使用每个可能的参数调用该函数(在示例中为 2^32 * 2^32,假设为 32 位整数)并存储所有这些,以根据参数返回它们,就像一个巨大的查找表。但这似乎不是很有效,如果可能的话。

更新:您可以假设该函数使用固定大小的参数。所以没有字符串或可变长度数组。

4个回答

您的问题似乎与 Sibyl 的目标(https://github.com/cea-sec/Sibyl)有关。它尝试根据函数的副作用(返回值、内存写入等)来识别已知函数。当然,你需要一种数据库来“学习”这个功能!

如果您拥有所有可能的输入和所有预期的输出,并且它们与加密/压缩数据无法区分,那么您可以找到比仅拥有大型查找表更有效的存储机制。即使是简单的遗传算法也可以很快让您“使用 a * b,除非 a == 234”(我实际上已经为此类问题专门制作了一个求解器,尽管在更一般的数学公式情况下)。最后,这是一个相当普通的优化问题,您需要在存储空间、计算和准备时间之间取得平衡,以获得正确的结果。更复杂的算法可能需要更长的时间来解决,

但无论如何,要确定,您必须尝试所有可能的输入。对于几个整数来说这很容易(虽然肯定很费力),但对于像字符串这样的东西很快就站不住脚了。

除非您按照您的建议尝试所有输入可能性,否则您只能获得该函数的近似值。这基本上是机器学习领域的基本问题之一,所以我会这样看,而不是尝试为 2^32 * 2^32 值生成查找表。

您显然应该小心,您不能 100% 保证该函数是等效的,并且还请记住,在特定领域,如何计算输出与输出本身一样重要。以加密函数为例:具有相同的输出但暴露信息(由于内存泄漏、功耗峰值等)进行侧信道攻击意味着“等效”函数实际上比原始函数差得多(以至于它可能不会是合适的替代品)。

这个问题本质上描述了曲线拟合相结合序列分析领域

如果您能够对模型需要适用的秘密函数输入做出一些假设,您可以使用它来指导您选择算法以生成新值以尝试作为函数的输入。

如果您能够对函数特征做出一些假设,您可以使用它来指导您选择函数以适应秘密函数的输出,这将决定当您将其置于输入下时结果函数的行为方式还没试过。

即使是给出的“简单”示例,在这些领域中也可能有多种不同的解释。例如:

  • 如果您不能对函数做出任何假设,并且您的模型必须准确再现正确的值,则您别无选择,只能评估所有 2^64 种可能性。如果您正确地猜测了一个可以使用正确参数重现每个值的函数,那么您不必在执行过程中将它们全部存储起来。
  • 如果您知道只有一个值a会改变函数,并且它是 的两个线性函数之一,a并且b取决于该值,那么您平均需要 2^31 次试验才能找到魔术a值,从而显着缩小问题。
  • 如果您不需要精确复制,那么您可以从关于可接受哪些错误的价值判断开始推理。例如,一个在 2^-32 次完全错误的函数可能是完全可以接受的,所以如果你知道特殊情况不大于这个,你可以只选择几个随机值(几乎肯定不是意外选择a = 234)和求解线性方程组。
  • 您可能不知道该函数具有线性部分,但知道它并不比其他函数复杂。这个更复杂函数的参数,当拟合到秘密函数的输出时,将产生一个函数,该函数与从函数获得的值的线性行为相匹配,但不一定保证对于未经测试的值是线性行为;因此,您选择拟合的任何函数的可能行为必须努力匹配在您的假设下可能被认为合理的行为范围。

这些都是很大的领域,并且有很多选项可供您利用,从而受益于您的问题的具体情况。