扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在本页阅读全文(共2页)
第二部分 CRC的逆向分析:
我遇到了很多障碍,当我思考如何破解CRC时.我试图使用一些特殊顺序的字节使CRC无效.
但我没有做到...后来我意识到这种方法是行不同的,因为CRC内建了一些处理过程,无论你
改变任何位它都不会出问题,真正的CRC就是在不断变化的,总是在变化的.找一些CRC程序,
你可以自己尝试一下.
现在我知道我只能'纠正'在CRC后面的那些我想改变的字节.所以我要构造一个字节序列,
它可以将CRC转化成任何我想要的样子!
具体实现这个想法
一个字节串? 01234567890123456789012345678901234567890123456789012
You want to change from ^ this byte to ^ this one.
就是位置9->26.
同时我们需要额外的4个字节用来在最后恢复原始字节串.
当你计算CRC-32时,从0-8都没有问题,直到第9位,修补过的字节串会使CRC发生根本的改变.
即使当走过了第26位,以后的字节都没有改变,你也不可能在得到原始的CRC了,不可能了!你读
过后面的段落时就会明白为什么.间而言之,当你修改一个字节串时,要保证CRC不变.
1. 计算并保存从1~9位的CRC.
2. 继续计算直到第27位还有额外的4字节并保存结果.
3. 用1的值来计算新的字节串和额外4字节的CRC(对应patch后的新的CRC值),并将之保存.
4. 现在我们得到了一个新的CRC,但是我们希望将它还原成原先的CRC,所以我们用逆向算法
来计算那额外的4字节.
1~3就是实际的情况,下面你将学到最关键的部分4.
'反转'CRC-16
我想,先来介绍计算逆CRC-16对于你来说会简单些.好的,我们现在处在一个恰当的位置,
在以修改代码后面,就是你想将CRC还原的地方.我们知道原始的CRC(是在patch代码之前计
算出来的)还有这个当前的记存器值.现在我们的目的就是计算可以改变当前记存器值到原
始记存器值的两个字节.首先,我们用正常的方法计算这两个未知字节的CRC.我们设他们为
X,Y.设记存器为a1,a0,只有0不能用来作为变量(00).:)在来看一下我们的CRC算法,figure
3,更好的理解下面我要做的.
好,我们开始:
用这两字节串'X Y' 字节是从左边开始被处理的.
记存器现在是a1 a0.
用'+'来表示XOR运算(和第一部分中用的一样)
处理第一个字节, X:
a0+X 这是顶部字节的计算结果 (1)
b1 b0 这是(1)在表中索引对象.
00 a1 向右移动记存器.
00+b1 a1+b0 上面两行对应位做XOR运算.
现在记存器为: (b1) (a1+b0)
处理第二个字, Y:
(a1+b0)+Y 此轮顶部字节的计算结果(2)
c1 c0 这是(2)在表中的索引对象.
00 b1 向右移动记存器.
00+c1 b1+c0 上面两行对应位做XOR运算.
最后记存器就是: (c1) (b1+c0)
我用一点不同的方法来表示:
a0 + X =(1) 在表中指向b1 b0.
a1 + b0 + Y =(2) 在表中指向c1 c0.
b1 + c0=d0 记存器中新的低位字节.
c1=d1 记存器中新的高位字节.
(1) (2)
Wow! 请大家暂时记住上面的信息:)
别着急, 下面给出一个有具体值的例子.
如果你想要的记存器的值是d1 d0(是原始的CRC),而且你知道在变换之前的记存器的值
(a1 a0)...那么你将要送如什么样的2个字节进记存器来做CRC计算呢?
好了,现在我们的工作应该从幕后走到台前来了.d0一定是bi+c0,并且d1一定是c1...
但是这到底是怎么回事,我听到你这样问了,你能知道b1和c0的值吗???你还记得哪个值表
吗?你只需要在表中查找c0 c1这个字的值就可以了因为你知道c1.所以你需要编写一个查
找程序.假如你找到了这个值,一定要记住这个值的索引,因为这就是找出未知的两个顶部
字节,举例来说:(1)和(2)!
所以,现在你找到了c1 c0,那么如何来得到b1 b0呢?如果b1+c0=d0那么b1=d0+c0!如前所
述,现在你用哪个查找程序在表中查b1 b0的值.现在我们得到了所有计算X和Y所需要的值.
Cool huh?
a1+b0+Y=(2) so Y=a1+b0+(2)
a0+X=(1) so X=a0+(1)
实例.
让我们来看看这个具体值的例子:
-register before: (a1=)DE (a0=)AD
-wanted register: (d1=)12 (d0=)34
在附录的CRC-16的表中查找以12开头值的入口.这里入口38h的值为12C0.试这找一找是否还
有以12开头的值的入口.你不可能在找到的,因为我们计算每一中顶部字节组合而得的值的
入口,一共是256个值,记住!
现在我们知道(2)=38,c1=12,c0=C0,所以b1=C0+34=F4,现在查找以F4开头的b1的入口.这里
入口4Fh的值是F441.
我们还知道 (1)=4F,b1=F4,b0=41,现在所有我们需要的都已经清楚了,接下来我们计算X,Y.
Y=a1+b0+(2)=DE+41+38=A7
X=a0+(1) =AD+4F =E2
结论:将CRC 记存器的值从 DEAD 变为 1234 我们需要这两个字节 E2 A7 (以此顺序).
你看,破解CRC校验你需要反向计算,还有要记住的就是计算过程中的值.当你在用汇编编写
查找表程序时,要注意intel在小模式中是反向存储值的.现在你可能已经明白如何去破解这个
CRC-16了...下面介绍如何在CRC-32中实现.
破解 CRC-32
现在我们来看CRC-32,和CRC-16是一样容易的(可能一样的不容易你认为).这里你操作的对象
是4个字节的而不是2字节的.继续向下看,将它与上面CRC-16版本做对比.
设4字节串 X Y Z W , 从左边开始处理.
设记存器为 a3 a2 a1 a0
注意a3是MSB,而a0是LSB
处理第一个字节, X:
a0+X 这是顶部字节的计算结果(1).
b3 b2 b1 b0 这是(1)在表中索引对象序列.
00 a3 a2 a1 右移记存器.
00+b3 a3+b2 a2+b1 a1+b0 上面两行对应位做XOR运算.
现在记存器是: (b3) (a3+b2) (a2+b1) (a1+b0)
Processing second byte, Y:
(a1+b0)+Y 这是顶部字节的计算结果(2).
c3 c2 c1 c0 这是(2)在表中索引对象序列.
00 b3 a3+b2 a2+b1 右移记存器.
00+c3 b3+c2 a3+b2+c1 a2+b1+c0 上面两行对应位做XOR运算.
现在记存器是: (c3) (b3+c2) (a3+b2+c1) (a2+b1+c0)
Processing third byte, Z:
(a2+b1+c0)+Z 这是顶部字节的计算结果(3).
d3 d2 d1 d0 这是(3)在表中索引对象序列.
00 c3 b3+c2 a3+b2+c1 右移记存器.
00+d3 c3+d2 b3+c2+d1 a3+b2+c1+d0 上面两行对应位做XOR运算.
现在记存器是: (d3) (c3+d2) (b3+c2+d1) (a3+b2+c1+d0)
Processing fourth byte, W:
(a3+b2+c1+d0)+W 这是顶部字节的计算结果(4).
e3 e2 e1 e0 这是(4)在表中索引对象序列.
00 d3 c3+d2 b3+c2+d1 右移记存器.
00+e3 d3+e2 c3+d2+e1 b3+c2+d1+e0 上面两行对应位做XOR运算.
最后,记存器为: (e3) (d3+e2) (c3+d2+e1) (b3+c2+d1+e0)
我用一个不同一点的方法来表示:
a0 + X =(1) 在表中指向 b3 b2 b1 b0
a1 + b0 + Y =(2) 在表中指向 c3 c2 c1 c0
a2 + b1 + c0 + Z =(3) 在表中指向 d3 d2 d1 d0
a3 + b2 + c1 + d0 + W =(4) 在表中指向 e4 e3 e2 e1
b3 + c2 + d1 + e0 =f0
c3 + d2 + e1 =f1
d3 + e2 =f2
e3 =f3
(1) (2) (3) (4)
(figure 4)
这里是用的与CRC-16同样的方法来实现的,我会给出一个具体值的例子.查找用附录中
CRC-32的值表.
Take for CRC register before, a3 a2 a1 a0 -> AB CD EF 66
Take for CRC register after, f3 f2 f1 f0 -> 56 33 14 78 (wanted value)
我们开始:
First byte of entries entry value
e3=f3 =56 -> 35h=(4) 56B3C423 for e3 e2 e1 e0
d3=f2+e2 =33+B3 =E6 -> 4Fh=(3) E6635C01 for d3 d2 d1 d0
c3=f1+e1+d2 =14+C4+63 =B3 -> F8h=(2) B3667A2E for c3 c2 c1 c0
b3=f0+e0+d1+c2=78+23+5C+66=61 -> DEh=(1) 616BFFD3 for b3 b2 b1 b0
Now we have all needed values, then
X=(1)+ a0= DE+66=B8
Y=(2)+ b0+a1= F8+D3+EF=C4
Z=(3)+ c0+b1+a2= 4F+2E+FF+CD=53
W=(4)+d0+c1+b2+a3=35+01+7A+6B+AB=8E
(final computation)
CRC-32的破解算法
假如你考虑手动计算这个可以还原CRC记存器的字节序列,那么这将很难变成一个
简洁的算法.
看看下面这个最后计算的附加版本:
Position
X =(1) + a0 0
Y =(2) + b0 + a1 1
Z =(3) + c0 + b1 + a2 2
W =(4) + d0 + c1 + b2 + a3 3
f0= e0 + d1 + c2 + b3 4
f1= e1 + d2 + c3 5
f2= e2 + d3 6
f3= e3 7
(figure 5)
它就等同于figure 4,只不过是一些值/字节被交换了.这种方法可以帮助我们构造一个
简洁的算法.这里我们用一个8字节的缓冲区,0-3位我们放置a0到a3,4-7位我们放置f0到
f3.象以前一样,我们用这个已知值e3(由figure 5中得知)在表中查出(e3 e2 e1 e0),并且
象图5(figure 5)中所示,将它们放到第4位(position 4),我们马上得到了d3的值.因为f2=
e2+d3,所以f2+e2=d3.又因为(4)已知(入口值),我们照样把它也放到位置3.然后在用d3查表
得到(d3 d2 d1 d0),同上也将他们放到图中所述位置.同样,由于有f1+e1+d2=c3在位置5上.
我们继续做直到将b3 b2 b1 b0放到位置1,对了,就是它! Et voila!
此时,缓冲区的第3-第0字节中已经包含全部元素,用来计算X~W!
算法总结如下:
1.对于这个8字节的缓冲区,0~3字节放入a0...a3(CRC记存器起始值),4~7字节放入f0...f3
(目标记存器的值).
2.取出位置7的已知值,查表得到相应值.
3.将查出值放如图5相应位置,其实就是做XOR运算.(为了直观,可以拟定此图)
4.将入口字节放入图中.也是做XOR运算.
5.继续做2,3两步3次,同时每次降低1个位置 position 5 to 4, 4 to 3 and so on.
算法的实现:
现在是时候给出代码了.下面就是用汇编写成的可执行的CRC-32算法(用其他语言也一样
简单,对于其他的CRC-32标准也一样).注意在汇编中(计算机里)双字在读写操作中顺序都是
反着的.就是逆向顺序.
crcBefore dd (?)
wantedCrc dd (?)
buffer db 8 dup (?)
mov eax, dword ptr[crcBefore] ;/*
mov dword ptr[buffer], eax
mov eax, dword ptr[wantedCrc] ;Step 1
mov dword ptr[buffer+4], eax ;*/
mov di, 4
computeReverseLoop:
mov al, byte ptr[buffer+di+3] ;/*
call GetTableEntry ;Step 2 */
xor dword ptr[buffer+di], eax ;Step 3
xor byte ptr[buffer+di-1], bl ;Step 4
dec di ;/*
jnz computeReverseLoop ;Step 5 */
Notes:
-Registers eax, di bx are used
Implementation of GetTableEntry
crctable dd 256 dup (?) ;should be defined globally somewhere &initialized of course
mov bx, offset crctable-1
getTableEntryLoop:
add bx, 4 ;points to (crctable-1)+k*4 (k:1..256)
cmp [bx], al ;must always find the value somewhere
jne getTableEntryLoop
sub bx, 3
mov eax, [bx]
sub bx, offset crctable
shr bx, 2
ret
On return eax contains a table entry, bx contains the entry number.
SEH技术
结构化异常处理
当某一线程发生异常时,程序的控制权会立即进入Ring0异常处理程序,这是属于操作系统的部分,
如果发生的异常是如页异常之类的异常,Ring0处理程序可以处理完它后重新回到程序中执行,而被中断
过的进程可能根本就不知道发生过异常。
但事情并不总是如此,有时进程会发生一些始料不及的异常,例如访问不存在的内存,被0除等,
这些异常Ring0处理程序不知该如何处理它,而进程本身也可能想自己处理这些情况,这是就要用到结构化异常处理(SEH)。在C/C++中也有异常处理的语句如_try,_catch等,这些语句的实现也与SEH紧密联
系。
当系统遇到一个它不知道如何处理的异常时,它就查找异常处理链表,注意每个线程都有它自己的异
常处理链表。异常链表以FS:[0]所指向的位置为链表头。
异常处理开始时,系统把一些与当前线程和与异常有关的内容传给链头所指向的处理程序;处理程序
由用户编写或编译器生成,它的返回值可以是告诉系统:异常处理以完成,可以继续执行程序,或未处理
异常,可由链表的下一个处理程序处理等,可以一次传递下去。
下面给出一个例子:
-------------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include kernel32.inc
include user32.inc
include windows.inc
includelib kernel32.lib
includelib user32.lib
.data
szCaption db "SEH",0
szTextSEH db "SEH 程序正在运行",0
szText db "SEH 程序没有运行",0
.code
start:
lea eax,[esp-4*2]
xchg fs:[0],eax ;这一行编译错误,哪位大虾指点一下正确格式
mov ebx,offset SEH
push ebx
push eax
mov esi,0
mov eax,[esi]
invoke MessageBox,0,offset szText,offset szCaption,MB_OK
jmp Exit
SEH:
invoke MessageBox,0,offset szTextSEH,offset szCaption,MB_OK
Exit:
invoke ExitProcess,0
end start
end
-------------------------------------------------------------------------------------------
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者