反跟蹤技術

m78星️發表於2021-01-05

1、Anti-Debug
1.MeltICE子型別
型別:檢測SoftICE、TRW2000
平臺:Windows9x、Windows NT
原理:用CreateFileA( )或_lopen( )函式試圖獲得SoftICE的驅動程式"\.\SICE"(Windows9X版本)、"\.\SIWDEBUG"、"\.\NTICE"(Windows NT版本)、"\.\SIWVID"等的控制程式碼,如果成功則說明SoftICE駐留在記憶體中。
2.VWIN32_Int41Dispatch子型別
型別:檢測SoftICE
平臺:Windows9x
原理:VWIN32.VxD(其VxD ID為0x002A)提供一個名為VWIN32_Int41Dispatch的VxD service(其service ID為0x002A),系統核心使用此服務來與系統級偵錯程式如WinDBG、SoftICE等進行通訊。其中0x4F號子功能是用來查詢偵錯程式是否已經駐留記憶體並能否處理保護模式程式,如果是的話則偵錯程式應返回0xF386。
3.給SoftICE傳送命令
型別:檢測SoftICE
平臺:Windows9x、Windows NT
原理:通過除錯中斷int 3給SoftICE傳送命令讓其執行,其中SI和DI暫存器中放的分別是固定值0x4647(“FG”)和0x4A4D(“JM”)。AX中存放的是子功能號,值為0x0911則表示讓SoftICE執行命令,此時DX指向一個命令字串如"HBOOT"等。AX還可以為其它子功能號,比如讓SoftICE修改斷點設定等。
4、BoundsChecker後門
型別:檢測SoftICE
平臺:Windows9x、Windows NT
原理:這是SoftICE為BoundsChecker留的一個公開的介面,入口引數EBP = 0x4243484B(即"BCHK"),AL =4,如果SoftICE在記憶體中則應返回AL = 0。
這種方法一般也要結合SEH?(結構異常處理)來實現,否則當SoftICE不存在時就會引起非法操作。
5.ICECream子型別
型別:檢測SoftICE、TRW2000
平臺:Windows9x
原理:偵錯程式駐留後修改INT 1和INT 3的入口,指向它自己的處理程式,所以入口高位偏移與其他中斷不同。其他所有中斷入口高位偏移都相同。
6.INT 68h子型別
型別:檢測SoftICE
平臺:Windows9x
原理:
MOV AH, 43h
INT 68h
CMP AX, 0F386h ;檢測此處是否被偵錯程式設定0F386h
JZ SoftICE_is_here
7.搜尋特徵串
型別:檢測SoftICE
平臺:Windows9x
原理:通過在記憶體中搜尋SoftICE的特徵串來發現SoftICE,這一般要結合SEH一起使用,以防止引起記憶體保護出錯而使得程式被終止。這種方法在DOS下是可行的。由於Windows95之後的作業系統中的每個ring 3程式的地址空間是獨立的,使得這種方法受到限制。比如在記憶體中搜尋"WINICE.BR"。
8.IsDebuggerPresent子型別
型別:檢測SoftICE
平臺:Windows NT
原理:呼叫kernel32.dll輸出的函式IsDebuggerPresent()來檢測是否有偵錯程式存在。這個函式只能檢查使用Debug API來跟蹤程式的偵錯程式,無法檢測SoftICE之類的系統級偵錯程式。
2、Anti-靜態分析
1.死迴圈語句
型別:對付W32Dasm
平臺:Windows9x 、Windows NT
原理:下面是故意在程式中插入的一個死迴圈,可能會使W32Dasm的某些版本停止響應:
0401000 JMP 00401005
……
00401005 JMP 00401000

對策:W32Dasm進入死迴圈後,用Bpx hmempcy設斷,來到死迴圈程式碼處,將其跳出死迴圈,或用IDA來反彙編。
2.利用花指令
花指令是對付靜態分析的重要手段。以下是一段彙編源程式:
start_:
xor eax,1
add eax,2
jmp label1
label1: xor eax,3
add eax,4
xor eax,5
end start_
此時把源程式進行編譯,然後用W32Dasm進行反彙編,得到的反彙編結果完全正常。接著我們將上述源程式作如下修改:
start_:
xor eax,1
add eax,2
jnz label1 ;注意這裡,用兩句條件跳轉代替了:jmp label1
jz label1
db 0E8h ;注意這個無用的位元組和源程式的區別
label1: xor eax,3
add eax,4
xor eax,5
end start_
再把源程式進行編譯,然

:00401000 83F001
:00401003 83C002
:00401006 7503
:00401008 7401
:0040100A E883F00383
:0040100F C00483F0
xor eax, 00000001
add eax, 00000002
jne 0040100B
je 0040100B
call 83440092
rol byte ptr [ebx+4*eax], F0
結果令人很吃驚,會發現W32Dasm反彙編的結果和事先寫的彙編指令不一樣,從反彙編的結果中已經無法理解程式的"真實"的功能了,W32Dasm給出了一個意想不到的答案。 這是因為上述改動是為了在W32Dasm的反彙編工作中做點手腳,從而使得它犯下錯誤。那麼W32Dasm為什麼會因此而犯下這樣的錯誤呢?
不同的機器指令包含的位元組數並不相同,有的是單位元組指令,有的是多位元組指令。對於多位元組指令來說,反彙編軟體需要確定指令的第一個位元組的起始位置,也就是操作碼的位置,這樣才能正確地反彙編這條指令,否則它就可能反彙編成另外一條指令了。 如果在程式中加入一些無用的位元組來干擾反彙編軟體的判斷,從而使得它錯誤地確定指令的起始位置,那麼也就達到了干擾W32Dasm反彙編工作的目的。
通過前面的介紹,知道由於"無用的位元組"干擾了W32Dasm對指令起始位置的判斷,從而導致反彙編的錯誤結果,所以如果能讓W32Dasm正確地識別出指令起始位置,也就達到了去除花指令的目的了。比如可以把那些無用的位元組都替換成單位元組指令,最常見的一種替換方法是把無用的位元組替換成 NOP 指令,即十六進位制數 90。
3、CRC簡介
CRC原理及其逆向破解方法:
介紹:
這篇短文包含CRC原理介紹和其逆向分析方法,很多程式設計師和破解者不是很清楚瞭解
CRC的工作原理,而且幾乎沒人知道如何逆向分析它的方法,事實上它是非常有用的.
首先,這篇教程教你一般如何計算CRC,你可以將它用在資料程式碼保護中.第二,主要是
介紹如何逆向分析CRC-32,你可以以此來分析程式中的CRC保護(象反病毒編碼).當然
有很多有效的工具用來對付CRC,但我懷疑它是否會說明原理.
我要告訴你,這篇短文裡中應用了很多數學知識,這不會影響一些人,而且會被一般的
程式設計師與逆向分析者很好理解.為什麼?那麼如果你不知道數學是如何被應用在CRC中,
我建議你可以停止繼續學習了.所以我假定你們(讀者)都是具備二進位制算術知識的.
第一部分:CRC 介紹,CRC是什麼和計算CRC的方法.

迴圈冗餘碼 CRC
我們都知道CRC.甚至你沒有印象,但當你想到那些來自諸如RAR,ZIP等壓縮軟體發給你
由於錯誤連線和其他一些意外原因導致的檔案錯誤的惱人的訊息時,你就會知道.CRC是塊
資料的計算值,比如對每一個檔案進行壓縮.在一個解壓縮過程中,程式會從新計算解壓檔案
的CRC值,並且將之與從檔案中讀取的CRC值進行比對,如果值相同,那麼正確.在CRC-32中,
會有1/2^32的可能性發生對確認資料更改的校驗錯誤.
很多人認為CRC就是迴圈冗餘校驗,假如CRC真的就是迴圈冗餘校驗,那麼很多人都錯用了
這個術語.你不能說"這個程式的CRC是12345678".人們也常說某一個程式有CRC校驗,而不
說是 “迴圈冗餘校驗” 校驗.結論:CRC 代表迴圈冗餘碼,而不是迴圈冗餘校驗.
計算是如何完成的呢?好,主要的想法就是將一個檔案看成一個被一些數字分割的很長的
位字串,這裡會有一個餘數—CRC!你總會有一個餘數(可以是0),它至多比除數小一.
(9/3=3 餘數=0 ; (9+2)/3=3 餘數=2)
(或者它本身就包含一個除數在其中).
在這裡CRC計算方法與除法有一點點區別,除法就是將被減數重複的減去除數X次,然後留下
餘數.如果你希望得到原值,那麼你就要把除數乘上X次,然後加上餘數.
CRC計算使用特殊的減法與加法完成的.也就是一種新的"演算法".計算中每一位計算的進位值
被"遺忘"了.
看如下兩個例子,1是普通減法,2和3是特殊的.
-+
(1) 1101 (2) 1010 1010 (3) 0+0=0 0-0=0
1010- 1111+ 1111- 0+1=1 *0-1=1
---- ---- ---- 1+0=1 1-0=1
0011 0101 0101 *1+1=0 1-1=0
在(1)中,右數第二列可以看成是0-1=-1,因此要從高位借1,就變成(10+0)-1=1.(這就象普通
的’by-paper’十進位制減法).特例(2,3)中,1+1會有正常的結果10,‘1’是計算後的進位.這個值
被忽略了.特殊情況0-1應該有正常結果’-1’就要退到下一位.這個值也被忽略了.假如你對程式設計
有一定了解,這就象,XOR 操作或者更好.
現在來看一個除法的例子:
在普通演算法中:
1001/1111000\1101 13 9/120\13
1001 - 09 -|
---- – |
1100 30 |
1001 - 27 -


0110 3 -> 餘數
0000 -

1100
1001 -

011 -> 3, 餘數
在CRC演算法中:
1001/1111000\1110 9/120\14 餘數為 6
1001 -

1100
1001 -

1010
1001 -

0110
0000 -

110 -> 餘數
(例 3)
這個除法的商並不重要,也沒必要去記住,因為他們僅僅是一組無關緊要的位串.真正
重要的是餘數!它就是這個值,可以說比原檔案還重要的值,他就是基本的CRC.

過度到真正的CRC碼計算.
進行一個CRC計算我們需要選則一個除數,從現在起我們稱之為"poly".寬度W就是最高位
的位置,所以這個poly 1001的W 是3,而不是4.注意最高位總是1,當你選定一個寬度,那麼你只
需要選擇低W各位的值.
假如我們想計算一個位串的CRC碼,我們想確定每一個位都被處理過,因此,我們要在目標
後用W32Dasm進行反彙編,來看一下反彙編後的結果
位串後面加上W個0位.在此例中,我們假設位串為1111.請仔細分析下面一個例子:
Poly = 10011, 寬度 W=4
位串 Bitstring
Bitstring + W zeros = 110101101 + 0000
10011/1101011010000\110000101 (我們不關心此運算的商)
10011|||||||| -
-----||||||||
10011|||||||
10011||||||| -
-----|||||||
00001||||||
00000|||||| -
-----||||||
00010|||||
00000||||| -
-----|||||
00101||||
00000|||| -
-----||||
01010|||
00000||| -
-----|||
10100||
10011|| -
-----||
01110|
00000| -
-----|
11100
10011 -

1111 -> 餘數 -> the CRC!
(例 4)
重要兩點宣告如下:
1.只有當Bitstring的最高位為1,我們才將它與poly做XOR運算,否則我們只是將
Bitstring左移一位.
2.XOR運算的結果就是被操作位串bitstring與低W位進行XOR運算,因為最高位總為0.
演算法設計:
你們都應知道基於位運算的演算法是非常慢的而且效率低下.但如果將計算放在每一位元組上
進行,那麼效率將大大提高.不過我們只能接受poly的寬度是8的倍數(一個位元組;).可以形
象的看成這樣一個寬度為32的poly(W=32):
3 2 1 0 byte
±–±--±–±--+
Pop! <–| | | | |<-- bitstring with W zero bits added, in this case 32
±–±--±–±--+
1<— 32 bits —> this is the poly, 4*8 bits
(figure 1)
這是一個你用來存放暫時CRC結果的記存器,現在我稱它為CRC記存器或者記存器.你從右
至左移動位串,當從左邊移出的位是1,則整個記存器被與poly的低W位進行XOR運算.(此例
中為32).事實上,我們精確的完成了上面除法所做的事情.

移動前記存器值為:10110100
當從右邊移入4位時,左邊的高4位將被移出,此例中1011將被移出,而1101被移入.
情況如下:
當前8位CRC記存器 : 01001101
剛剛被移出的高4位 : 1011
我們用此poly : 101011100, 寬度 W=8
現在我們用如前介紹的方法來計算記存器的新值.
頂部 記存器


1011 01001101 高四位和當前記存器值
1010 11100 + (*1) Poly 放在頂部最高位進行XOR運算 (因為那裡是1)

0001 10101101 運算結果
現在我們仍有一位1在高4位:
0001 10101101 上一步結果
1 01011100+ (*2) Poly 放在頂部的最低位進行XOR運算 (因為那裡是1)

0000 11110001 第二步運算結果
^^^^
現在頂部所有位均為0,所以我們不需要在與poly進行XOR運算
你可以得到相同的結果如果你先將(*1)與(*2)做XOR然後將結果與記存器值做XOR.
這就是標準XOR運算的特性:
(a XOR b) XOR c = a XOR (b XOR c) 由此,推出如下的運算順序也是正確的.
1010 11100 poly (*1) 放在頂部最高位
1 01011100+ polys (*2) 放在頂部最低位

1011 10111100 (*3) XOR運算結果
The result (*3) 將(*3)與記存器的值做XOR運算
1011 10111100
1011 01001101+ 如右:

0000 11110001
你看到了嗎?得到一樣的結果!現在(*3)變的重要了,因為頂部為1010則(3)的值總是等於
10111100(當然是在一定的條件之下)這意味著你可以預先計算出任意頂部位結合的XOR值.
注意,頂部結果總是0,這就是組合XOR操作導致的結果.(翻譯不準確,保留原文)
現在我們回到figure 1,對每一個頂部位元組的值都做移出操作,我們可以預先計算出一個值.
此例中,它將是一個包含256個double word(32 bit)雙字的表.
(翻譯不準確,保留原文)
用偽語言表示我們的演算法如下:
While (byte string is not exhausted)
Begin
Top = top_byte of register ;
Register = Register shifted 8 bits left ORred with a new byte from string ;
Register = Register XORred by value from precomputedTable at position Top ;
End
direct table演算法:
上面提到的演算法可以被優化.位元組串中的位元組在被用到之前沒有必要經過整個記村器.用
這個新的演算法,我們可以直接用一個位元組去XOR一個位元組串通過將此位元組移出記存器.結果
指向預先計算的表中的一個值,這個值是用來被記存器的值做XOR運算的.
我不十分確切的知道為什麼這會得到同樣的結果(這需要了解XOR運算的特性),但是這又
極為便利,因為你無須在你的位元組串後填充0位元組/位.(如果你知道原理,請告訴我:)
讓我們來實現這個演算法:
±—< byte string (or file) 位元組串,(或是檔案)
|
v 3 2 1 0 byte 位元組
| ±–±--±–±--+
XOR—<| | | | | Register 記存器
| ±–±--±–±--+
| |
| XOR
| ^
v ±–±--|—±–+
| | | | | | Precomputed table 值表(用來進行操作)
| ±–±--±–±--+
±–>-: : : : :
±–±--±–±--+
| | | | |
±–±--±–±--+
(figure 2)
‘reflected’ direct Table 演算法:
由於這裡有這樣一個與之相對應的’反射’演算法,事情顯得複雜了.一個反射的值/記存器
就是將它的每一位以此串的中心位為標準對調形成的.例如:0111011001就是1001101110
的反射串.
他們提出’反射’是因為UART(一種操作IO的晶片)傳送每一個位元組時是先發最沒用的0位,
最後再發最有意義的第七位.這與正常的位置是相逆的.
除了資訊串不做反射以外,在進行下一步操作前,要將其於的資料都做反射處理.所以在
計算值表時,位向右移,且poly也是作過反射處理的.當然,在計算CRC時,記存器也要向右
移,而且值表也必須byte string (or file) -->—+
| 1. 表中每一個入口都是反射的.
byte 3 2 1 0 V 2. 初始化記存器也是反射的.
±–±--±–±--+ | 3. 但是byte string中的資料不是反射的,
| | | | |>—XOR 因為其他的都做過反射處理了.
±–±--±–±--+ |
| |
XOR V
^ |
±–±--|—±–+ |
| | | | | | 值表
±–±--±–±--+ |
: : : : : <—+
±–±--±–±--+
| | | | |
±–±--±–±--+
(figure 3)
我們的演算法如下:

  1. 將記存器向右移動一個位元組.
  2. 將剛移出的哪個位元組與byte string中的新位元組做XOR運算,
    得出一個指向值表table[0…255]的索引
  3. 將索引所指的表值與記存器做XOR運算.
  4. 如資料沒有全部處理完,則跳到步驟1.

下面是這個演算法的簡單的可執行彙編原始碼:
完整的CRC-32標準所包含的內容:
Name : “CRC-32”
Width : 32
Poly : 04C11DB7
Initial value : FFFFFFFF
Reflected : True
XOR out with : FFFFFFFF
作為對你好奇心的獎勵, 這裡是CRC-16標準: ?
Name : “CRC-16”
Width : 16
Poly : 8005
Initial value : 0000
Reflected : True
XOR out with : 0000
‘XOR out with’ 是為了最終得到CRC而用來與記存器最後結果做XOR運算的值.
假如你想了解一些關於’reversed’逆向CRC poly的話,請看我的參考文章.
我是在16位DOS模式下用的32位編碼,因此你會在這個程式中看到很多32位與16位混合
的編碼…當然這是很容易轉換成純32位編碼的.注意這個程式是經過完整測試並且能夠
正常執行的.下面的Java 和 C 程式碼都是由這個彙編程式碼而來的.
底下的這段程式就是用來計算CRC-32 table的:
xor ebx, ebx ;ebx=0, 將被用做一個指標.
InitTableLoop:
xor eax, eax ;eax=0 為計算新的entry.
mov al, bl ;al<-bl
;生成入口.
xor cx, cx
entryLoop:
test eax, 1
jz no_topbit
shr eax, 1
xor eax, poly
jmp entrygoon
no_topbit:
shr eax, 1
entrygoon:
inc cx
test cx, 8
jz entryLoop
mov dword ptr[ebx*4 + crctable], eax
inc bx
test bx, 256
jz InitTableLoop
註釋: - crctable 是一個包含256個dword的陣列.

  • 由於使用反射演算法,EAX被向右移.
  • 因此最低的8位被處理了.
    用Java和C寫的程式碼如下(int is 32 bit):
    for (int bx=0; bx<256; bx++){
    int eax=0;
    eax=eax&0xFFFFFF00+bx&0xFF; // 就是 ‘mov al,bl’ 指令
    for (int cx=0; cx<8; cx++){
    if (eax&&0x1) {
    eax>>=1;
    eax^=poly;
    }
    else eax>>=1;
    }
    crctable[bx]=eax;
    }
    下面的彙編程式碼是用來計算CRC-32的:
    computeLoop:
    xor ebx, ebx
    xor al, [si]
    mov bl, al
    shr eax, 8
    xor eax, dword ptr[4*ebx+crctable]
    inc si
    loop computeLoop
    xor eax, 0FFFFFFFFh
    註釋: - ds:si 指向將要被處理的byte string資訊流.
  • cx 資訊流的長度.
  • eax 是當前的CRC.
  • crctable是用來計算CRC的值表.
  • 此例中記存器的初始值為: FFFFFFFF.
  • 要將中間值與FFFFFFFFh做XOR才能得到CRC
    下面是Java和C寫的程式碼:
    for (int cx=0; cx>=8;
    eax^=crcTable[ebx];
    }
    eax^=0xFFFFFFFF;
    現在我們已經完成了本文的第一部分:CRC原理部分,所以如果你希望能夠對CRC做更深
    的研究,那麼我建議你去讀在本文最後給出連線上的資料,我讀了.好了,終於到了本文最
    有意思的部分,CRC的逆向分析!
    第二部分 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 的記存器的值從 ABCDEF66 改變到 56331478 我們需要這樣一個位元組
序列: B8 C4 53 8E
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位元組的緩衝區,03位元組放入a0…a3(CRC記存器起始值),47位元組放入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.

Outtro
.

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

相關文章