假设我有一些函数(即哈希函数),该函数根据输入种子和一些预先计算的哈希值生成值,这些值存储在二进制文件中。有什么可能的方法:


防止字符串数据转储
防止哈希算法被反向工程一段时间

我知道对反向器完全隐藏算法的目标是不可能的,但是什么技术会为此增加努力呢?

我对此主题的看法:


使用以下方法保护哈希字符串加密宏

使用某种混淆/多形/变质引擎之王来模糊目标函数,以防止算法轻松恢复(可能导致AV误报,但一般不会造成伤害)
大致生成每个程序副本的新哈希函数和值(有点难以实现和维护)

也许任何人都有更好的概念可以满足我的目标,请在此处发布。

PS请不要建议使用封隔器/保护器。误报AV不会花费夫妇函数算法的费用。

UPD

这是用C ++编写的字符串保护的实现。我不知道此解决方案是否对其他人有用,但是值得一提。

#1 楼

我将以线性方式在内存中没有关键字符串开始

相反,每个字符都可以偏移一些偏移量,例如将表中的所有字符串混合在一起。例如,如果将0123456789像这样存储在内存中,则会得到x

下一个级别更好,请在运行时创建偏移表,它将随机将线性字符串偏移映射为非线性偏移。这很难解码,因为它将所有字符串混合在一起(如果它们是哈希值),那么不加步进几乎不可能解码(不知道它的粗略用法)。

以下示例如何使用它以及输出看起来如何:

|0x1xx2xxx3xxxx4|
|xxxxx5xxxxxx6xx|
|xxxx7xxxxxxxx8x|
|xxxxxxxx9xxxxxx|


结果是这样的:



左边是解码后的文本picdec.txt,右边是编码后的文本picenc.txt,这就是字符串在内存中的外观。

除此之外,您还可以添加任何类型的加密(不仅对文本本身,而且也可以偏移表格...)

评论


感谢您分享您的技术!您能否扩大答案的第二部分?如果我正确的话,所有值都存储在大缓冲区中,并且应用程序的每个副本都会收到其自己的rng代码副本和种子。因此,实现了完整的动态字符串/双字生成,并且没有逐步执行所有rng的方法就无法进行静态计算吗?

–见
15年5月4日在21:39

@seeya更新了我的答案,请参见示例(使用我的图像到ASCII Art转换作为源输入来强调位置/图案失真)

– Spektre
15年5月5日在8:28



#2 楼

有多种方法可以实现您所说的内容。通常,更健壮的技术在提供更高级别的保护的同时,也会给创建软件的程序员带来更多负担。因此,按难度递增的顺序,有一些想法:

非连续存储数据

最简单的方法是不简单地连续存储数据。也就是说,将数据存储在单独的块中,并在需要时在运行时重新组装它们。结合所有其他技术,通常建议将值在内存中保留的时间尽可能短。

混淆存储的数据

有混淆数据的许多方法。一种简单的方法是简单地对某个固定常数进行XOR。一种更复杂的方法是对数据进行加密,但是除非您有某种安全的方法来存储加密密钥,否则实际上可能无法提供那么多的安全性。一种可能性是使用整个程序的加密哈希(减去受保护的数据)作为加密密钥。这将在很大程度上防止二进制文件的更改以及提供一种非显而易见的方式来存储密钥。

在运行时重新计算数据

如果可以避免存储数据根本上,我们消除了能够从静态分析中得出该问题的问题。如果出于性能原因要预先计算某些数据的哈希,请考虑在程序启动时而不是在编译时执行。或者,如果数据是固定的,请考虑编写在编译时可能包含的多态生成器。也就是说,编写一个程序,该程序需要一个固定的常数并生成代码,该代码在运行时会产生该值,而不会明确包含该值。然后将生成的代码与您的程序链接。由于多态生成器将是构建过程的一部分,而不是运行时的一部分,因此触发A / V警告的可能性要小得多。

这个想法的概念证明

我用C ++写了一个小程序来更充分地演示这种技术。这是程序:


#include <iostream>
#include <cstdlib>
#include <random>

int main(int argc, char *argv[])
{
    std::random_device rd;
    std::uniform_int_distribution<> r{-32768,32767};

    for (int i=1; i < argc; ++i) {
        int y = std::atoi(argv[i]);
        int x;
        for (x=r(rd); x==0; x= r(rd));  // make sure x!=0
        int m = r(rd);
        int b = y-m*x;
        std::cout << "int generate" << i << "(int x) { return x * " << m << " + " << b << "; }\n";
        std::cout << "\tassert(" << y << " == generate" << i << "(" << x << "));\n";

    }
}


如何工作

这是一个非常简单的程序它接受一系列整数作为输入,并为每个整数创建一个线性函数。例如,使用以下命令行:

./linear 39181 3802830 938833 -41418699


程序生成以下输出:

int generate1(int x) { return x * -5646 + 182450149; }
    assert(39181 == generate1(32308));
int generate2(int x) { return x * -14922 + 10696794; }
    assert(3802830 == generate2(462));
int generate3(int x) { return x * -15424 + -320805807; }
    assert(938833 == generate3(-20860));
int generate4(int x) { return x * -8144 + -127093579; }
    assert(-41418699 == generate4(-10520));


assert只是用于文档和测试。在实际使用中,如果要在代码中重新创建常量39181,则可以使用generate1(32308)。如果我们将行重新排列成一个完整的程序,我们将得到:


int generate1(int x) { return x * -5646 + 182450149; }
int generate2(int x) { return x * -14922 + 10696794; }
int generate3(int x) { return x * -15424 + -320805807; }
int generate4(int x) { return x * -8144 + -127093579; }
#include <cassert>
int main() {
    assert(39181 == generate1(32308));
    assert(3802830 == generate2(462));
    assert(938833 == generate3(-20860));
    assert(-41418699 == generate4(-10520));
}


显然,您可以将它们相乘或连接如果您需要更长的数字或字符串,而我选择的随机函数值范围内的线性函数完全是任意的。随意替换和尝试。

远程获取数据

根据环境,可能可以远程存储数据,然后通过HTTPS之类的方法安全地获取数据。何时和需要。请注意,这样做也可能意味着即使是普通的网络中断或配置错误的防火墙也会使您的软件无法运行,但是您可以决定是否可以接受此目的。

评论


对我来说,第三种方法听起来非常有趣且很有前途。您是否可以使用更多可能的实现细节来扩展它,或者如果存在的话,甚至可以使用该技术的现成解决方案?

–见
15年5月4日在21:44

我已经用代码和示例运行更新了我的答案,展示了它如何工作。

–爱德华
15年5月5日在16:06

谢谢!我希望我也能接受你的回答。无论如何,请接受我的投票。

–见
2015年5月6日7:55

:) 没问题!这是一个有趣的问题。

–爱德华
2015年5月6日13:14