在程序集中查找数组

问题描述:

Here是一种混淆技术,其中条件跳转由跳转表替换。每个数组只包含一个有效的函数指针,它们是基于crc值调用的。我只保留了函数指针在程序集中查找数组

#include <stdio.h> 
#include <inttypes.h> 

typedef void (*crc_check_fn)(uint32_t *); 

static void crc_nib2 (uint32_t *crc) { printf("OK\n"); } 

crc_check_fn b1[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, crc_nib2, 0, 0, 0 }; 

int main(){ 
     uint32_t crc = 0xFFF7FB7C; 
     int index = crc & 0x0F; 
     (*b1[index])(&crc);  
} 

的单个阵列我有几个问题:

  1. 可这阵位于二进制的组装?我不太了解装配,所以检查它我无法告诉。

  2. 我假设上一个问题的答案是肯定的,因为原来的帖子建议初始化b1就像{ ..., crc_nib2-8, crc_nib2, crc_ni2+8, ... };。这是否安全?难道不可能说这些是无效指针吗?不应该是更好的选择来实现大量真正的虚拟功能吗?

  3. 编译与gcc -S

    B1: ...
    .quad 0
    .quad crc_nib2 .quad 0
    ...

上运行objdump -d二进制产生一个没有上述行的程序集。为什么组件不同?我没有删除符号。

+0

关于'objdump -d',分割在段上的elf,.text(带代码),'.rodata'常量等等,'objdump -d'仅反汇编'.text'段,而你的数据可能在另一部分。 – fghj

  1. 这个数组可以位于二进制程序集吗?我不太了解装配,所以检查它我无法说清楚。

是的,也许。有了GCC,我可以把它放在程序集中。可以想象它将被放置在BSS(零初始化存储器)中,然后在main之前初始化以包含单个非空指针。

  1. 我认为答案对前一个问题是肯定的,因为原帖建议initializng b1{ ..., crc_nib2-8, crc_nib2, crc_ni2+8, ... };。这是否安全?难道不可能告诉 这些是无效的指针吗? 不应该是一个更好的选择实现大量真正的虚拟功能吗?这一切
开始=“2”>

首先实际上不是很安全。所有需要了解的信息都是可用的。如果我们知道这已经完成了,可以很容易地找出crc_nib2是一个有效的函数指针,其他的都是。还有人可以得出结论,crc_ni2+8可能不是一个有效的指针。这是通过知道函数的开始看起来非常相似并且可以在某种程度的准确度下自动识别而完成的。如果有符号表可用,它变得更简单。

如果你打算让可执行真正防篡改你必须 肯定你不能用代码检查可执行文件的完整性篡改。如果检查是在可执行文件本身完成的,那么你可能会毫无意义(如果你可以在一个地方修改)可以修改检查以使它们在别处修改后仍能通过。

  1. 编译与gcc -S

    B1: ...
    .quad 0
    .quad crc_nib2 .quad 0
    ...

开始=>

是的,我o,但问题是什么?

请注意,objdump -d只是对可执行文件的可执行部分中的所有内容进行反汇编,并跳过其余部分。这是因为通常其他(数据)部分中的数据类型,所以没有太多的“反汇编”干预,只有对这些数据执行hexdump才有意义。

这个数组可以位于二进制程序集吗?我不太了解装配,所以检查它我无法说清楚。

不太确定什么“组合二进制”应该是什么意思。就像任何其他初始化的静态存储持续时间变量一样,它将分配到程序的.data部分。

这是否安全?难道不可能说这些是无效指针吗?不应该是更好的选择来实现大量真正的虚拟功能吗?

这些指针必须是指向有效函数的指针,否则程序将崩溃并烧毁。同样,如果你调用指向地址0的函数指针,你的程序就会崩溃并烧毁。所以不,它不安全,要么同样是不安全的。事实上,唯一不会导致崩溃的解决方案是实施虚拟功能。总之,我真的不明白你为什么需要一个函数指针数组。只需使用一个普通的查找表,一个整数数组,并将CRC与这些数据进行比较。

+0

在比较crc和普通查找表之后,我是不是最终会遇到一个非常容易绕过的条件跳转? – robert

+0

@ franz1“PC安全性”方面是另外一个可以肯定的地方,如果PC的东西是你正在使用的东西。但是你必须从写一个稳定的程序开始,这很有道理。如果它一直崩溃,你的程序就不安全了...... – Lundin