【问题标题】:How to compute checksum of some binary function (or basic block) after compilation?编译后如何计算某些二进制函数(或基本块)的校验和?
【发布时间】:2017-07-19 19:23:28
【问题描述】:

我想编写一些简单的防篡改机制,在运行时计算某个函数(或基本块)的校验和,并将其与固定的预计算值进行比较。如果校验和匹配,那么一切都很好;否则程序终止。

我可以在运行时轻松计算校验和,但我不知道如何计算固定值进行比较。

我想我必须在编译后对二进制文件进行一些后期处理,因为在编译时不可能知道校验和(对吗?)。

但是如何在二进制代码中找到与我的函数(或基本块)相对应的代码点?我可以使用一些十六进制编辑器手动执行此操作,但我想自动化该过程。预先计算校验和后,我需要修改二进制文件以包含它 - 但这再次需要能够在二进制文件中找到正确的位置。

对解决方案有什么建议吗?

【问题讨论】:

  • 在我看来你会过得很糟糕。在 C++ 中编译函数的方法不止一种,因此没有预定义的方法来预计算期望值。也许解释型语言更适合您的任务?
  • 谢谢,但这就是为什么我建议进行一些后期处理。
  • @Shuzheng 你到底想达到什么目的?也许有比在可执行/共享库中这样做更好的方法。
  • 我想实现防篡改机制。

标签: c++ security binary x86 obfuscation


【解决方案1】:

例如,您可以获得指向函数的指针并将其重新解释为 uint8_t 数组。如果您不希望编译器在某个循环中使用该函数的内联副本,您也可以禁用特定函数的内联。

uint8_t* pMain = (uint8_t*)&main;

然后你必须尝试估计函数长度并计算校验和。

【讨论】:

  • 但是,要执行此操作,您必须在编译前修改源代码。
【解决方案2】:

如果您的代码由多个文件组成,您可以遵循以下机制:

  1. 使用您希望将来检查的功能编译文件

  2. 定位在编译文件(很可能是 .o、.obj)函数位置 - 例如使用 objdump 或类似工具 - 它必须显示人类可读的文本以及编译代码的十六进制代码

示例:

objdump a.o -d --insn-width=10

  40064b:       48 89 e5                        mov    %rsp,%rbp
  40064e:       ff d0                           callq  *%rax
  400650:       5d                              pop    %rbp
  400651:       e9 7a ff ff ff                  jmpq   4005d0 <register_tm_clones>

0000000000400656 <calculate>:
  400656:       55                              push   %rbp
  400657:       48 89 e5                        mov    %rsp,%rbp
  40065a:       89 7d ec                        mov    %edi,-0x14(%rbp)
  40065d:       48 89 75 e0                     mov    %rsi,-0x20(%rbp)
  400661:       8b 05 f9 09 20 00               mov    0x2009f9(%rip),%eax        # 601060 <magicCRC1>
  400667:       89 c2                           mov    %eax,%edx
  400669:       48 8b 45 e0                     mov    -0x20(%rbp),%rax
  40066d:       89 10                           mov    %edx,(%rax)
  40066f:       48 8b 45 e0                     mov    -0x20(%rbp),%rax
  400673:       48 83 c0 04                     add    $0x4,%rax
  400677:       c7 00 01 00 00 00               movl   $0x1,(%rax)
  40067d:       c7 45 fc 00 00 00 00            movl   $0x0,-0x4(%rbp)
  400684:       eb 72                           jmp    4006f8 <calculate+0xa2>
  400686:       83 7d fc 01                     cmpl   $0x1,-0x4(%rbp)
  40068a:       75 1c                           jne    4006a8 <calculate+0x52>
  40068c:       8b 45 fc                        mov    -0x4(%rbp),%eax
  40068f:       48 98                           cltq   

所以如果是 objdump 函数名称将被 包围 - 上面的计算函数示例

  1. 编写脚本来解析它——地址和反汇编代码之间需要十六进制代码

  2. 计算您的 CRC、哈希或任何您需要的东西

  3. 存储 CRC 和块长度,以便稍后在运行时将其解码到一个文件中,该文件也将被编译 - 当然,您需要知道映射哪些变量/常量保存哪个函数 CRC

    李>
  4. 链接所有文件

如果有单个文件可用(完整的可执行文件),您可以:

  1. 在源代码中定义常量/静态来保存 CRC
  2. 如上所述进行 CRC 计算
  3. 在二进制文件中(在 .data 或 .rodata 部分)找到您的 var 位置并更新它
  4. 您很可能也需要修复可执行 CRC!

【讨论】:

  • 谢谢@Anty。你能推荐任何预制的解析器吗? “可执行 CRC”是指可执行文件本身的校验和,对吗?
猜你喜欢
  • 1970-01-01
  • 2012-08-27
  • 2012-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-11-22
  • 1970-01-01
  • 2021-10-02
  • 2011-02-04
相关资源
最近更新 更多