反转简单消息 + 校验和对(32 字节)

逆向工程 二元分析
2021-06-14 11:01:48

我正在尝试确定大端系统上 32 字节受保护内存部分背后的算法。即使更改了一个位,它也会变得无效,但我可以生成任意数量的有效 32 字节消息。

这里显示了各种示例数据。我相信最后 4 个字节是校验和。所有这些都被算法接受。

00000000000000000000000000000000000000000007478A000101004892B760
F0000000000000000000000000000000000000000002D8DF00010100C9E23610
3065C0000000000000000000000000000000000000006F850001010060EB9F07
FFFFFFFF03FC6BBD000000000000000000000000000000000001010070B88F3A
FFFFFFFF0397E33D340C804138458C5006060968570C214B0001017AE8F416FE

我能够创建一堆几乎没有差异的自定义消息。似乎它不可能是 CRC-32。

FFFFFFFF01671F7E000000000000000000000000000000000001010021E4DE0E (00)
FFFFFFFF01671F84000000000000000000000000000000000001010021EADE08 (01)
FFFFFFFF01671F88000000000000000000000000000000000001010021EEDE04 (02)
FFFFFFFF01671F86000000000000000000000000000000000001010021ECDE06 (03)
FFFFFFFF01671F8C000000000000000000000000000000000001010021F2DE00 (04)
FFFFFFFF01671F8A000000000000000000000000000000000001010021F0DE02 (05)
FFFFFFFF01671F86000000000000000000000000000000000001010021ECDE06 (06)
FFFFFFFF01671F8C000000000000000000000000000000000001010021F2DE00 (07)
FFFFFFFF01671F90000000000000000000000000000000000001010021F6DDFC (08)

所有输入字节都相同,除了在每条消息中增加 1 的一个字节。

是 50 对左右的列表。

基本上,是否有任何分析方法可以应用于一组数据以确定算法的某些属性?我可以生成任意数量的这些消息,并验证它们是否被接受。

编辑:通过移动一位,我注意到 0x04 被添加到两个字节,但减去另一个(F84 -> F88,1EA -> 1EE,E08 -> E04)。经过一些实验(主要是加减不同的值并测试它们),我很幸运,结果是前十四个 16 位字的总和。用 0xFFFF 对总和进行 AND 掩码,该值等于第 15 个字(例如,第一个消息中的 21EA)。然后从 0xFFF2 中减去该值以生成消息中的第 16 个字。

FFFFFFFF01671F84000000000000000000000000000000000001010021EADE08
FFFFFFFF01671F88000000000000000000000000000000000001010021EEDE04
1个回答

显示的一些样本输入仅在第十六个半字节的值上有所不同;让 's(X)' 代表在该半字节处具有值 X 的输入:

s(4): FFFFFFFF01671F840000000000000000000000000000000000010100 21EADE08
s(6): FFFFFFFF01671F860000000000000000000000000000000000010100 21ECDE06
s(8): FFFFFFFF01671F880000000000000000000000000000000000010100 21EEDE04
s(A): FFFFFFFF01671F8A0000000000000000000000000000000000010100 21F0DE02
s(C): FFFFFFFF01671F8C0000000000000000000000000000000000010100 21F2DE00

以类似的方式,让'o(Y)' 代表除了第十六个半字节的值 Y 之外完全由零组成的位串。

现在,让我们将某些输入的 XOR(位差)和相应校验和的 XOR 作为 MSB-first 位串来处理:

s(4) ^ s(6) = o(2), cksum delta: .............**.............***.
s(8) ^ s(A) = o(2), cksum delta: ...........****..............**.
s(8) ^ s(C) = o(4), cksum delta: ...........***...............*..
s(4) ^ s(C) = o(8), cksum delta: ...........**...............*...

校验和差异的最低设置位等于输入不同的位,并且匹配差异正好高出十六位。设置位的突发可能是由于冒泡进位。

为了比较,这里是对应的 CRC32 值之间的异或差异:

s(4) ^ s(6) = o(2), crc32 delta: .*****.*.......***...*..***..*..
s(8) ^ s(A) = o(2), crc32 delta: .*****.*.......***...*..***..*..
s(8) ^ s(C) = o(4), crc32 delta: *.***.**.....*..*****..*...*..*.
s(4) ^ s(C) = o(8), crc32 delta: .***.**.....*..*****..*...*..*.*

变化的结构完全不同,而且要复杂得多。请注意相同位(前两行)不同输入的 CRC 差异,以及接近理论 50% 的差异密度。这是因为 CRC 比简单的经验校验和具有更好的扩散(avalance 效应)。

现在是具有近乎完美扩散的散列图片(杂音散列混合器功能):

s(4) ^ s(6) = o(2), murmur delta: ..*..*.....**.**..*..*....**..**
s(8) ^ s(A) = o(2), murmur delta: .*.*.*.*.*..*..*.*.*.*.**.*.*.*.
s(8) ^ s(C) = o(4), murmur delta: ..****.*****.******.**.**.******
s(4) ^ s(C) = o(8), murmur delta: .***...***.**...*.*....**.*.**.*

通过观察越来越复杂的函数的这些差异,很容易了解校验和的结构:sum、xor、xorshift、一些经典散列。

然后将焦点放在您的目标功能上。观察一些固定输入的输出差异,让单个位翻转在字符串中漫游。观察一系列不同输入的特定固定位置的单位差异的输出差异。让差异位置以 8 位为增量跳跃,观察行为。然后尝试 32 位跃点,观察。如果可能,将初始调查集中在输入的最后 32 位上,因为它们与输出的关系肯定比经过更多迭代的位简单得多......散列函数的结构无法隐藏自己你好久

那是看看异或差异。根据校验和的假设结构,其他实验也是可能的。作为一个真实的例子,几年前我不得不根据(大部分)正确样本的样本批次推导出各种校验码算法(本质上类似于 ISBN 校验码算法)。由于这些算法基于将数字与特定位置相关的权重相乘,因此我搜索了除校验和外仅在一位数字上不同的数字对。通过修正关于总和如何变成校验位的某些假设,这允许我推导出每个数字的权重(1、2、3、4...)并确定总和的正确假设。

在所考虑的情况下,对于我在这里使用的五个样本,校验和的第十六个半字节和最后一个半字节的总和为 12 (0x0C) 肯定是暗示性的。

因此,基本思想是通过对需要推断的函数结构的怀疑来应对输入差异和输出差异。如果具有最小差异的样本对稀缺,事情会变得非常棘手。相反,如果您可以随意生成样本(选择明文攻击),那么事情确实看起来非常好......