📄 chap6-2-4.htm.primary
字号:
+----< byte string (or file) 字节串,(或是文件)<br>
|<br>
v 3 2 1 0 byte 字节<br>
| +---+---+---+---+<br>
XOR---<| | | | | Register 记存器<br>
| +---+---+---+---+<br>
| |<br>
| XOR<br>
| ^<br>
v +---+---|---+---+<br>
| | | | | | Precomputed table 值表(用来进行操作)<br>
| +---+---+---+---+<br>
+--->-: : : : :<br>
+---+---+---+---+<br>
| | | | |<br>
+---+---+---+---+<br>
(figure 2)</p>
<p>'reflected' direct Table 算法:</p>
<p> 由于这里有这样一个与之相对应的'反射'算法,事情显得复杂了.一个反射的值/记存器<br>
就是将它的每一位以此串的中心位为标准对调形成的.例如:0111011001就是1001101110<br>
的反射串. <br>
他们提出'反射'是因为UART(一种操作IO的芯片)发送每一个字节时是先发最没用的0位,<br>
最后再发最有意义的第七位.这与正常的位置是相逆的. <br>
除了信息串不做反射以外,在进行下一步操作前,要将其于的数据都做反射处理.所以在<br>
计算值表时,位向右移,且poly也是作过反射处理的.当然,在计算CRC时,记存器也要向右<br>
移,而且值表也必须是反射过的. </p>
<p> byte string (or file) -->---+<br>
| 1. 表中每一个入口都是反射的.<br>
byte 3 2 1 0 V 2. 初始化记存器也是反射的.<br>
+---+---+---+---+ | 3. 但是byte string中的数据不是反射的,<br>
| | | | |>---XOR 因为其他的都做过反射处理了. <br>
+---+---+---+---+ |<br>
| |<br>
XOR V<br>
^ |<br>
+---+---|---+---+ |<br>
| | | | | | 值表<br>
+---+---+---+---+ |<br>
: : : : : <---+<br>
+---+---+---+---+<br>
| | | | |<br>
+---+---+---+---+<br>
(figure 3)</p>
<p>我们的算法如下:<br>
1. 将记存器向右移动一个字节.<br>
2. 将刚移出的哪个字节与byte string中的新字节做XOR运算,<br>
得出一个指向值表table[0..255]的索引<br>
3. 将索引所指的表值与记存器做XOR运算.<br>
4. 如数据没有全部处理完,则跳到步骤1.</p>
<p><br>
下面是这个算法的简单的可执行汇编源码:</p>
<p>完整的CRC-32标准所包含的内容:<br>
Name : "CRC-32"<br>
Width : 32<br>
Poly : 04C11DB7<br>
Initial value : FFFFFFFF<br>
Reflected : True<br>
XOR out with : FFFFFFFF</p>
<p>作为对你好奇心的奖励, 这里是CRC-16标准: :)<br>
Name : "CRC-16"<br>
Width : 16<br>
Poly : 8005<br>
Initial value : 0000<br>
Reflected : True<br>
XOR out with : 0000</p>
<p>'XOR out with' 是为了最终得到CRC而用来与记存器最后结果做XOR运算的值.<br>
假如你想了解一些关于'reversed'逆向CRC poly的话,请看我的参考文章.<br>
<br>
我是在16位DOS模式下用的32位编码,因此你会在这个程序中看到很多32位与16位混合<br>
的编码...当然这是很容易转换成纯32位编码的.注意这个程序是经过完整测试并且能够<br>
正常运行的.下面的Java 和 C 代码都是由这个汇编代码而来的. <br>
底下的这段程序就是用来计算CRC-32 table的:</p>
<p> xor ebx, ebx ;ebx=0, 将被用做一个指针.<br>
InitTableLoop:<br>
xor eax, eax ;eax=0 为计算新的entry.<br>
mov al, bl ;al<-bl</p>
<p> ;生成入口.<br>
xor cx, cx<br>
entryLoop:<br>
test eax, 1<br>
jz no_topbit<br>
shr eax, 1<br>
xor eax, poly<br>
jmp entrygoon<br>
no_topbit:<br>
shr eax, 1<br>
entrygoon:<br>
inc cx<br>
test cx, 8<br>
jz entryLoop</p>
<p> mov dword ptr[ebx*4 + crctable], eax<br>
inc bx<br>
test bx, 256<br>
jz InitTableLoop</p>
<p>注释: - crctable 是一个包含256个dword的数组.<br>
- 由于使用反射算法,EAX被向右移.<br>
- 因此最低的8位被处理了.</p>
<p> 用Java和C写的代码如下(int is 32 bit):</p>
<p>for (int bx=0; bx<256; bx++){<br>
int eax=0;<br>
eax=eax&0xFFFFFF00+bx&0xFF; // 就是 'mov al,bl' 指令<br>
for (int cx=0; cx<8; cx++){<br>
if (eax&&0x1) {<br>
eax>>=1;<br>
eax^=poly;<br>
}<br>
else eax>>=1;<br>
}<br>
crctable[bx]=eax;<br>
}</p>
<p>下面的汇编代码是用来计算CRC-32的:</p>
<p> computeLoop:<br>
xor ebx, ebx<br>
xor al, [si]<br>
mov bl, al<br>
shr eax, 8<br>
xor eax, dword ptr[4*ebx+crctable]<br>
inc si<br>
loop computeLoop</p>
<p> xor eax, 0FFFFFFFFh</p>
<p>注释: - ds:si 指向将要被处理的byte string信息流.<br>
- cx 信息流的长度.<br>
- eax 是当前的CRC.<br>
- crctable是用来计算CRC的值表.<br>
- 此例中记存器的初始值为: FFFFFFFF.<br>
- 要将中间值与FFFFFFFFh做XOR才能得到CRC</p>
<p>下面是Java和C写的代码:</p>
<p> for (int cx=0; cx>=8;<br>
eax^=crcTable[ebx];<br>
}<br>
eax^=0xFFFFFFFF;</p>
<p> 现在我们已经完成了本文的第一部分:CRC原理部分,所以如果你希望能够对CRC做更深<br>
的研究,那么我建议你去读在本文最后给出连接上的资料,我读了.好了,终于到了本文最<br>
有意思的部分,CRC的逆向分析!</p>
<p><br>
------------------------------------------------------------------------------------<br>
第二部分 CRC的逆向分析:</p>
<p> 我遇到了很多障碍,当我思考如何破解CRC时.我试图使用一些特殊顺序的字节使CRC无效.<br>
但我没有做到...后来我意识到这种方法是行不同的,因为CRC内建了一些处理过程,无论你<br>
改变任何位它都不会出问题,真正的CRC就是在不断变化的,总是在变化的.找一些CRC程序,<br>
你可以自己尝试一下. <br>
现在我知道我只能'纠正'在CRC后面的那些我想改变的字节.所以我要构造一个字节序列,<br>
它可以将CRC转化成任何我想要的样子! </p>
<p>具体实现这个想法</p>
<p>一个字节串? 01234567890123456789012345678901234567890123456789012<br>
You want to change from ^ this byte to ^ this one.</p>
<p>就是位置9->26.<br>
同时我们需要额外的4个字节用来在最后恢复原始字节串.</p>
<p> 当你计算CRC-32时,从0-8都没有问题,直到第9位,修补过的字节串会使CRC发生根本的改变.<br>
即使当走过了第26位,以后的字节都没有改变,你也不可能在得到原始的CRC了,不可能了!你读<br>
过后面的段落时就会明白为什么.间而言之,当你修改一个字节串时,要保证CRC不变. </p>
<p>1. 计算并保存从1~9位的CRC.<br>
2. 继续计算直到第27位还有额外的4字节并保存结果.<br>
3. 用1的值来计算新的字节串和额外4字节的CRC(对应patch后的新的CRC值),并将之保存.<br>
4. 现在我们得到了一个新的CRC,但是我们希望将它还原成原先的CRC,所以我们用逆向算法<br>
来计算那额外的4字节.</p>
<p>1~3就是实际的情况,下面你将学到最关键的部分4.</p>
<p><br>
'反转'CRC-16</p>
<p> 我想,先来介绍计算逆CRC-16对于你来说会简单些.好的,我们现在处在一个恰当的位置,<br>
在以修改代码后面,就是你想将CRC还原的地方.我们知道原始的CRC(是在patch代码之前计<br>
算出来的)还有这个当前的记存器值.现在我们的目的就是计算可以改变当前记存器值到原<br>
始记存器值的两个字节.首先,我们用正常的方法计算这两个未知字节的CRC.我们设他们为<br>
X,Y.设记存器为a1,a0,只有0不能用来作为变量(00).:)在来看一下我们的CRC算法,figure<br>
3,更好的理解下面我要做的.</p>
<p>好,我们开始:</p>
<p>用这两字节串'X Y' 字节是从左边开始被处理的.<br>
记存器现在是a1 a0.<br>
用'+'来表示XOR运算(和第一部分中用的一样)</p>
<p>处理第一个字节, X:<br>
a0+X 这是顶部字节的计算结果 (1)<br>
b1 b0 这是(1)在表中索引对象.<br>
00 a1 向右移动记存器.<br>
00+b1 a1+b0 上面两行对应位做XOR运算.</p>
<p>现在记存器为: (b1) (a1+b0)</p>
<p>处理第二个字, Y:<br>
(a1+b0)+Y 此轮顶部字节的计算结果(2)<br>
c1 c0 这是(2)在表中的索引对象.<br>
00 b1 向右移动记存器.<br>
00+c1 b1+c0 上面两行对应位做XOR运算.</p>
<p>最后记存器就是: (c1) (b1+c0)</p>
<p>我用一点不同的方法来表示:</p>
<p>a0 + X =(1) 在表中指向b1 b0.<br>
a1 + b0 + Y =(2) 在表中指向c1 c0.<br>
b1 + c0=d0 记存器中新的低位字节.<br>
c1=d1 记存器中新的高位字节.<br>
(1) (2)</p>
<p>Wow! 请大家暂时记住上面的信息:)<br>
别着急, 下面给出一个有具体值的例子.<br>
<br>
如果你想要的记存器的值是d1 d0(是原始的CRC),而且你知道在变换之前的记存器的值<br>
(a1 a0)...那么你将要送如什么样的2个字节进记存器来做CRC计算呢? <br>
好了,现在我们的工作应该从幕后走到台前来了.d0一定是bi+c0,并且d1一定是c1...<br>
但是这到底是怎么回事,我听到你这样问了,你能知道b1和c0的值吗???你还记得哪个值表<br>
吗?你只需要在表中查找c0 c1这个字的值就可以了因为你知道c1.所以你需要编写一个查<br>
找程序.假如你找到了这个值,一定要记住这个值的索引,因为这就是找出未知的两个顶部<br>
字节,举例来说:(1)和(2)! <br>
所以,现在你找到了c1 c0,那么如何来得到b1 b0呢?如果b1+c0=d0那么b1=d0+c0!如前所<br>
述,现在你用哪个查找程序在表中查b1 b0的值.现在我们得到了所有计算X和Y所需要的值.<br>
Cool huh? <br>
a1+b0+Y=(2) so Y=a1+b0+(2)<br>
a0+X=(1) so X=a0+(1)</p>
<p><br>
实例.</p>
<p>让我们来看看这个具体值的例子:<br>
-register before: (a1=)DE (a0=)AD<br>
-wanted register: (d1=)12 (d0=)34<br>
在附录的CRC-16的表中查找以12开头值的入口.这里入口38h的值为12C0.试这找一找是否还<br>
有以12开头的值的入口.你不可能在找到的,因为我们计算每一中顶部字节组合而得的值的<br>
入口,一共是256个值,记住!<br>
现在我们知道(2)=38,c1=12,c0=C0,所以b1=C0+34=F4,现在查找以F4开头的b1的入口.这里<br>
入口4Fh的值是F441.<br>
我们还知道 (1)=4F,b1=F4,b0=41,现在所有我们需要的都已经清楚了,接下来我们计算X,Y.</p>
<p>Y=a1+b0+(2)=DE+41+38=A7<br>
X=a0+(1) =AD+4F =E2</p>
<p>结论:将CRC 记存器的值从 DEAD 变为 1234 我们需要这两个字节 E2 A7 (以此顺序).</p>
<p> 你看,破解CRC校验你需要反向计算,还有要记住的就是计算过程中的值.当你在用汇编编写<br>
查找表程序时,要注意intel在小模式中是反向存储值的.现在你可能已经明白如何去破解这个<br>
CRC-16了...下面介绍如何在CRC-32中实现.</p>
<p><br>
破解 CRC-32</p>
<p>现在我们来看CRC-32,和CRC-16是一样容易的(可能一样的不容易你认为).这里你操作的对象<br>
是4个字节的而不是2字节的.继续向下看,将它与上面CRC-16版本做对比.</p>
<p>设4字节串 X Y Z W , 从左边开始处理.<br>
设记存器为 a3 a2 a1 a0<br>
注意a3是MSB,而a0是LSB</p>
<p>处理第一个字节, X:<br>
a0+X 这是顶部字节的计算结果(1).<br>
b3 b2 b1 b0 这是(1)在表中索引对象序列.<br>
00 a3 a2 a1 右移记存器.<br>
00+b3 a3+b2 a2+b1 a1+b0 上面两行对应位做XOR运算.</p>
<p>现在记存器是: (b3) (a3+b2) (a2+b1) (a1+b0)</p>
<p>Processing second byte, Y:<br>
(a1+b0)+Y 这是顶部字节的计算结果(2).<br>
c3 c2 c1 c0 这是(2)在表中索引对象序列.<br>
00 b3 a3+b2 a2+b1 右移记存器.<br>
00+c3 b3+c2 a3+b2+c1 a2+b1+c0 上面两行对应位做XOR运算.</p>
<p>现在记存器是: (c3) (b3+c2) (a3+b2+c1) (a2+b1+c0)</p>
<p>Processing third byte, Z:<br>
(a2+b1+c0)+Z 这是顶部字节的计算结果(3).<br>
d3 d2 d1 d0 这是(3)在表中索引对象序列.<br>
00 c3 b3+c2 a3+b2+c1 右移记存器.<br>
00+d3 c3+d2 b3+c2+d1 a3+b2+c1+d0 上面两行对应位做XOR运算.</p>
<p>现在记存器是: (d3) (c3+d2) (b3+c2+d1) (a3+b2+c1+d0)</p>
<p>Processing fourth byte, W:<br>
(a3+b2+c1+d0)+W 这是顶部字节的计算结果(4).<br>
e3 e2 e1 e0 这是(4)在表中索引对象序列.<br>
00 d3 c3+d2 b3+c2+d1 右移记存器.<br>
00+e3 d3+e2 c3+d2+e1 b3+c2+d1+e0 上面两行对应位做XOR运算.</p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -