【问题标题】:Changing a number defined in a C++(C) program without compiling the source again更改在 C++(C) 程序中定义的数字,而无需再次编译源代码
【发布时间】:2017-11-02 00:20:03
【问题描述】:

假设我有一个打印数字的简单程序:

#include <iostream>
int unique_id = 112233;
int main()
{
    std::cout << unique_id;
    return 0;
}

然后我将它编译成a.exe 之类的东西。现在我想创建另一个打开a.exe 并将unique_id 更改为其他内容的应用程序。有可能吗?
由于某些限制,我不会将参数传递给程序。

顾名思义,我想使用unique_id 来唯一标识我的程序正在运行的位置。但我不想为 1000 个客户编译我的程序 1000 次。我知道我可以使用硬盘序列号,但在虚拟机中,这个序列号可能会被省略。我知道我可以使用 CPU 序列号,但我在 S.O 帖子中读到此序列号已被弃用。我知道我也可以使用 MAC 地址 :),但是可以轻松更改该地址。所以我决定将唯一 ID 放在 exe 文件本身中。

【问题讨论】:

  • “有可能吗?”没有。
  • 有可能,但超出了语言的范围。
  • 编译时常量不要使用全部大写的标识符,这是反模式。
  • 如果您知道已编译二进制文件的结构并让您的程序编辑此结构,则可能。相当脆弱。
  • 你为什么问?请激发你的问题....实际常数是什么,它的目的是什么?

标签: c++ windows


【解决方案1】:

考虑到您添加到问题中的动机,您可以简单地让 exe 从 .txt 文件中读取 id,然后为每个客户提供一个带有 exe 的不同 .txt 文件。

或者,等效地,您可以创建一个具有返回 id 的函数的 DLL(或您的平台的等效项),并且只为每个客户重新编译 DLL。

【讨论】:

    【解决方案2】:

    一般情况下,如果不重新编译,您将无法更改任何内容。

    在实践中,在非常有限的情况下,您可能会修补您的二进制文件。这主要是特定于处理器(以及特定于可执行格式和特定​​于 ABI)的,并且较少依赖于您的特定操作系统版本(例如,如果它适用于 Windows 9,它也适用于 Windows 10)。

    (但是,我不知道也从未使用过 Windows;我只使用 Linux;您应该根据您的操作系统调整我的答案)

    所以在某些情况你可能会对你的二进制可执行文件进行逆向工程。如果你有 C 源代码,你可以让你的编译器发出汇编代码(例如,用gcc -O -fverbose-asm -SGCC 编译)。然后您可以反汇编您的可执行文件,并使用二进制或十六进制编辑器更改包含该常量的机器代码。

    这并不总是有效,因为机器指令(及其大小)可能取决于常量的大小(位大小)。

    举个简单的例子,在 C 语言中,对于 GCC 7,在 Linux/x86-64 上,考虑以下 C 文件:

     /// A, B, C are preprocessor symbols defined as integers
     int f(int x) { 
       if (x > 0)
         return A*x + B;
       return C;
     }
    

    如果我用 gcc -fverbose-asm -S -O -DA=12751 -DB=32 -DC=11 e.c 编译它,我会得到:

        .type   f, @function
    f:
    .LFB0:
        .cfi_startproc
    # e.c:3:   if (x > 0)
        testl   %edi, %edi  # x
        jle .L3 #,
    # e.c:4:     return A * x + B;
        imull   $12751, %edi, %edi  #, x, tmp90
        leal    32(%rdi), %eax  #, <retval>
        ret
    .L3:
    # e.c:5:   return C;
        movl    $11, %eax   #, <retval>
    # e.c:6: }
        ret
        .cfi_endproc
    .LFE0:
        .size   f, .-f
    

    但如果我这样做 gcc -S -O -fverbose-asm -DA=12753 -DB=32 -DC=10 e.c 我会得到

        .type   f, @function
    f:
    .LFB0:
        .cfi_startproc
    # e.c:3:   if (x > 0)
        testl   %edi, %edi  # x
        jle .L3 #,
    # e.c:4:     return A * x + B;
        imull   $12753, %edi, %edi  #, x, tmp90
        leal    32(%rdi), %eax  #, <retval>
        ret
    .L3:
    # e.c:5:   return C;
        movl    $10, %eax   #, <retval>
    # e.c:6: }
        ret
    

    确实,在上述情况下,我可以修补二进制文件(我需要在机器代码中找到 1275111 常量;在这种情况下可行但很乏味)。


    现在,让我们试试 A 是 2 的小幂,比如 16,C 是 0,所以 gcc -S -O -fverbose-asm -DA=16 -DB=32 -DC=0 e.c:

    f:
    .LFB0:
        .cfi_startproc
    # e.c:4:     return A * x + B;
        leal    2(%rdi), %eax   #, tmp90
        sall    $4, %eax    #, tmp93
        testl   %edi, %edi  # x
        movl    $0, %edx    #, tmp92
        cmovle  %edx, %eax  # tmp93,, tmp92, <retval>
    # e.c:6: }
        ret
    

    由于编译器优化,代码发生了显着变化。打补丁不容易。

    重要提示

    只要付出足够的努力、金钱和时间(想想类似 NSA 的能力),很多事情都是可能的。

    如果您的目标是混淆二进制文件中的某些数据(例如某些密码),您可能会对其进行加密以使黑客的生活更加困难(但不要天真,NSA 将能够得到它)。记住座右铭:有No Silver Bullet;看起来这就是你的目标,但不要太天真(顺便说一句,围绕你的软件的法律保护,例如许可证,更重要;所以你需要一个律师来写一个好的 EULA)。

    如果您的目标是调整一些性能关键的代码,您可以使用metaprogrammingpartial evaluation 技术。我喜欢做的一个做法是在运行时生成一些临时 C(或 C++)代码(更适合您的特定情况和数据),将临时 C 或 C++ 代码编译为一些 plugin,然后 @ 987654326@ 那个临时插件(在 Linux 上使用 dlopendlsym;在 Windows 上你需要 LoadLibrary 但我让你了解细节和后果)。您可以使用 JIT compiling 库,例如 libgccjit,而不是在运行时生成 C 或 C++ 代码。如果您喜欢这种技术,如果您的管理层允许,请考虑使用更好的编程语言(例如带有 SBCL 的 Common Lisp)。

    但我不想为 1000 个客户编译我的程序 1000 次

    这让我很惊讶。编译一个只包含常量的简单(短)C 文件很快,链接时间也很快。我会考虑为每个客户重新编译。

    顺便说一句,我觉得 你太天真了。最重要的保护不是二进制文件中的技术,而是法律保护(您需要一份好的合同,所以要找一个好的律师并支付费用)。

    相反,您是否考虑过让您的产品成为免费软件?许多公司都在这样做(并通过许可的其他方式赚钱,例如支持)。


    注意。有很多现有的license managers。你考虑过购买和使用吗?另请注意,公司有很大的动机来避免作弊,而那些愿意窃取您的软件的人无论如何都可以这样做。您将通过致力于软件质量来销售更多产品,而不是花精力在使客户烦恼的徒劳的“保护”措施上,增加您的logistics 和分发和维护成本,并加强对客户发现的错误的调试。

    【讨论】:

    • 加上常量可能会被完全编译出来。
    • 不鼓励这种事情,但是如果您将行更改为const volatile int unique_id = 112233,程序将在每次使用时加载 unique_id 并防止它被优化掉。这种事情很常见,如果通常通过嵌入式编程中的指针来实现的话。
    【解决方案3】:

    不,更改const 变量的行为是未定义。所以你不能用标准的 C 或 C++ 做到这一点。

    最好的办法是采用内联汇编解决方案;但请注意,UNIQUE_ID 可能会被完全编译出来(C 和 C++ 都不是 反射性 语言)。为了增加UNIQUE_ID 被保留的概率,删除const 限定符并可能引入volatile

    我个人会在命令行上将UNIQUE_ID 传递给您的程序。

    起点:https://msdn.microsoft.com/en-us/library/fabdxz08.aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      相关资源
      最近更新 更多