中級例項教程―之瘋狂單詞v1.70
前言:
俺學破解其實也只有不到半個月的時間,之所以敢到這裡班門弄斧寫“中級”教程,主要是發現各種教程大多在重複入門知識,廣大菜鳥缺乏進一步的指導,抱著“俺不挨臭雞蛋誰挨臭雞蛋”的信念,希望能夠給菜鳥同志們深入學習帶來一點點啟發,如此足矣。
一、研究物件簡介:
《瘋狂單詞》是俺收集到的最好的背單詞共享軟體,我想任何想鑽研技術的人都會發現具備讀懂 e文資料的能力是相當重要的,而學好 e文必須過詞彙關,這也是俺要拿《瘋狂單詞》開刀的初衷,目前最新的版本是v1.7x,用google 可以找到很多提供下載的地方,建議下載語音壓縮版,20兆,當然如果頻寬不是問題,110 兆的語音清晰版發音會更好一些。
《瘋狂單詞》需要重新啟動程式來驗證註冊碼,好像是從“utraledit”學來的,這種方式對“爆破一族”有一定的防範能力,因為程式不再侷限於在“請輸入註冊碼”和“恭喜註冊成功”之間對註冊碼進行驗證,它可以在任何地方對註冊碼進行突擊檢查,一旦發現註冊碼非法,隨時取消已註冊狀態。所以即使爆破註冊成功,爆破者總是無法判斷程式中是否還有地方埋有不知道什麼時候會引爆的“地雷”。
《瘋狂單詞》不是用 註冊碼 = f(使用者名稱) 的方式將正確的註冊碼算出來,再與輸入的註冊碼做比較,而是構造了一個隱函式演算法,程式總是判斷 F(註冊碼,使用者名稱)的值是否合法,這樣,正確的註冊碼永遠不會在程式的計算過程中出現,對於掌握了在暫存器和記憶體中找註冊碼這種初級技術的菜鳥有很好的防範作用。
《瘋狂單詞》運用了一些反跟蹤的手段,特別是針對 bpx +靜態地址的斷點和 bpm斷點,比如你跟蹤到了驗證註冊碼的函式的地址:0040c660,下 bpx 0040c660, 會發現下一次中斷時0040c660處的程式碼變成了“INVALID”; 再比如你跟蹤到存放假碼的地址:024ab100,下 bpm 024ab100, 會發現程式會用各種方法反覆將該地址裡的值轉移到其他地址去,甚至會分批幾個位元組幾個位元組地偷運,或者寫進登錄檔然後再讀進另一個地址,等等。
二、總體破解思路:
不管程式如何驗證註冊碼,在驗證之前它總要知道你輸入的假碼是什麼,所以在驗證之前它必然會完成兩個動作:
a)
從註冊介面讀入假碼
b) 對假碼進行預處理,可能只是簡單的格式驗證也可能是做一個變換:變碼 = f(假碼),然後將處理過後的假碼或變碼放到一個記憶體地址,註冊碼驗證函式一定會訪問這個地址。
所以跟蹤假碼就成為了找到驗證函式的一把鑰匙:
bpx
getwindowtexta (或 bpx getdlgitemtexta) do "d esp->8"
中斷後esp存放的是函式執行完畢後的返回地址,因為32位系統中每個地址佔4個位元組空間,所以此時esp->4是傳遞給函式的第
1個引數,也就是呼叫函式前最後一個壓棧的引數的地址,同理esp->8是第二個引數,如此類推。函式getwindowtexta的第二個引數就是函式取得的字串的存放地址,也就是我們要跟蹤的假碼的地址,d
esp->8可以得到這一地址,這個地址不是固定的,假設為xxxxxxxx。
bpmd
xxxxxxxx rw
密切關注程式對這一地址的任何讀寫操作,如果程式將該地址中的假碼複製到另一地址,馬上 bpmd 另一地址,如果發現程式呼叫regsetvalueexa函式將該地址中的假碼寫入登錄檔,記下呼叫之前倒數第二個壓棧的值(第二個引數),假設為aaaa(dword,四位元組),馬上下
bpx regqueryvalueex if *(esp->8)==aaaa do "d esp->14",如果程式從登錄檔將假碼讀入另一地址,則馬上
bpmd 另一地址。這樣你肯定會在假碼的帶領下找到程式的註冊碼驗證函式!
由於程式往往會將
F(假碼,使用者名稱)分解成不止一個二元方程,即:
f1(假碼) = f(使用者名稱)
f2(假碼) = f(使用者名稱)
......
fn(假碼)
= f(使用者名稱)
所以第一次跟蹤到的驗證函式一般只包含大F 演算法的一部分,所以只能初步瞭解該演算法。程式在完成第一次驗證之後可能會告訴你“註冊碼正確,請重新啟動程式完成註冊”,請千萬不要被它迷惑,你必須繼續讓“鑰匙”帶領你去找其他的驗證函式,防止程式“偷偷地”做第二次、第三次甚至第n 次驗證。即使關閉程式也要堅決搞清楚它將鑰匙放到哪裡儲存起來了,防止程式重新執行後再次驗證。在跟蹤假碼時程式可能會反覆對假碼進行變換,每一次變換後都要繼續跟蹤變碼,即使程式將變碼寫入登錄檔,也要看看它會不會再讀出來再做驗證,即使程式什麼事都不做了等著你關閉程式,你也要看看在你點選了“X” 按鈕之後程式有沒有處理OnDestroy 訊息響應函式再次變換或驗證變碼,總之要對假碼及假碼的一切變碼 bpmd 到底!
程式重啟時要採用前面提到的 bpx regqueryvalyeexa 加 bpm 手段繼續跟蹤“鑰匙”,直到你對大F 演算法有了足夠的瞭解,能夠推算出正確的註冊碼甚至寫出序號產生器為止。
三、驗證演算法分析:
跟蹤到第一次驗證函式為0040b880,程式碼分析如下:
:u
0040b880 L 271
001B:0040B880 PUSH FF-----------------------------------------------------
001B:0040B882
PUSH 00467DFB
001B:0040B887 MOV
EAX,FS:[00000000]
001B:0040B88D PUSH EAX
001B:0040B88E
MOV FS:[00000000],ESP 這一段是函式對呼叫環境的
儲存,不用深究
001B:0040B895
SUB ESP,000001A4
001B:0040B89B PUSH
EBX
001B:0040B89C PUSH EBP
001B:0040B89D
PUSH ESI
001B:0040B89E PUSH EDI----------------------------------------------------
001B:0040B89F
MOV ESI,00000008<---------迴圈控制變數,從假碼的第9位開始
驗證
001B:0040B8A4 MOV DWORD PTR [ESP+1C],00000000
001B:0040B8AC
MOV EBX,00000001<---------迴圈控制變數,驗證到第20位時回
到第2位繼續驗證
001B:0040B8B1
PUSH 0049141C-----------------------------------------------
001B:0040B8B6
LEA ECX,[ESP+14]
001B:0040B8BA CALL
00449D4A
001B:0040B8BF MOV ECX,00000064
將[ESP+24]之後的100個dword地址
001B:0040B8C4 MOV
EAX,EBX
賦值為“1”
001B:0040B8C6 LEA EDI,[ESP+24]
將[ESP+24]這一地址記入EDI,這
001B:0040B8CA XOR
EBP,EBP 一地址存放輸入的註冊碼(假碼)
001B:0040B8CC
REPZ STOSD
的各位數字值,是程式在驗證函式
之前做的
001B:0040B8CE MOV
EDI,[ESP+000001C8] 再將從[ESP+28]到[ESP+64]這16
001B:0040B8D5 MOV
EAX,00000008 個dword地址依次賦值為
001B:0040B8DA
MOV ECX,00000005 “1975121885116918”,估計與
001B:0040B8DF
MOV [ESP+40],EAX 作者生日有關,將用來作為加密
001B:0040B8E3
MOV [ESP+44],EAX 演算法的一個密碼錶
001B:0040B8E7
MOV [ESP+60],EAX
001B:0040B8EB LEA
EAX,[EDI+04]
001B:0040B8EE MOV EDX,00000009
001B:0040B8F3
MOV [ESP+30],ECX
001B:0040B8F7 MOV
[ESP+48],ECX
001B:0040B8FB PUSH EAX
注意這期間進行過一次壓棧操作,所
以ESP的值發生過變化,對後面的
001B:0040B8FC
LEA ECX,[ESP+14] [ESP+xx]的地址有所影響
001B:0040B900
MOV [ESP+000001C0],EBP
001B:0040B907 MOV
[ESP+28],EBX
001B:0040B90B MOV [ESP+2C],EDX
001B:0040B90F
MOV DWORD PTR [ESP+30],00000007
001B:0040B917 MOV
[ESP+38],EBX
001B:0040B91B MOV
DWORD PTR [ESP+3C],00000002
001B:0040B923 MOV [ESP+40],EBX
001B:0040B927
MOV [ESP+50],EBX
001B:0040B92B MOV
[ESP+54],EBX
001B:0040B92F MOV DWORD
PTR [ESP+58],00000006----------------------------
001B:0040B937
MOV [ESP+5C],EDX-------------------------------------------
001B:0040B93B
MOV [ESP+60],EBX
001B:0040B93F CALL
0044A0F4
001B:0040B944 LEA EAX,[EDI+08]
001B:0040B947
LEA ECX,[ESP+10]
001B:0040B94B PUSH
EAX
001B:0040B94C CALL 0044A0F4
001B:0040B951
LEA EAX,[EDI+14]
001B:0040B954 LEA
ECX,[ESP+10]
001B:0040B958 PUSH EAX
001B:0040B959
CALL 0044A0F4
001B:0040B95E LEA
EAX,[EDI+0C]
001B:0040B961 LEA ECX,[ESP+10]
這一段是將你輸入的使用者名稱、信箱
001B:0040B965 PUSH
EAX
地址與軟體的名稱、版本號合併成
001B:0040B966 CALL 0044A0F4
為一個目標串,放入[ESP+10],
001B:0040B96B
LEA EAX,[EDI+10] 程式實際上驗證
F(註冊碼,目標串)
001B:0040B96E LEA ECX,[ESP+10]
目標串形如
001B:0040B972 PUSH EAX
yourmail+wordslover+yourname+1.60
001B:0040B973
CALL 0044A0F4
看來作者對這一演算法非常滿意,從
001B:0040B978 LEA EAX,[EDI+1C]
1.6版沿用至今
001B:0040B97B LEA
ECX,[ESP+10]
001B:0040B97F PUSH EAX
001B:0040B980
CALL 0044A0F4
001B:0040B985 LEA
EAX,[EDI+20]
001B:0040B988 LEA ECX,[ESP+10]
001B:0040B98C
PUSH EAX
001B:0040B98D CALL 0044A0F4
001B:0040B992
LEA EAX,[EDI+24]
001B:0040B995 LEA
ECX,[ESP+10]
001B:0040B999 PUSH EAX
001B:0040B99A
CALL 0044A0F4-----------------------------------------------
001B:0040B99F
MOV ECX,[ESP+10]<---------將目標串地址寫入ECX
001B:0040B9A3
XOR EAX,EAX<--------------EAX清零
001B:0040B9A5
MOV ECX,[ECX-08]<---------取目標串的長度
001B:0040B9A8
CMP ECX,EAX<--------------長度為零則跳轉(目標串包含
wordslover1.60,長度怎麼可
能是零?有病!)
001B:0040B9AA JLE 0040B9F1
001B:0040B9AC MOV
EDX,EDI<--------------將假碼數字表地址寫入EDX
001B:0040B9AE MOV
[ESP+18],EAX<---------將[ESP+18]作為從零開始的一個
臨時變數
001B:0040B9B2
MOV EDX,[ESI*4+EDX+30]<---將假碼當前位(ESI)的數字值
寫入EDX
001B:0040B9B6
MOV [ESP+20],EDX<---------將該值寫入[ESP+20]儲存
--------------------------------------------------------------------------------
001B:0040B9BA
MOV EDX,[ESP+10]<---------將目標串地址寫入EDX
001B:0040B9BE
MOV EDI,[ESP+18]<---------將變數[ESP+18]的值寫入EDI
001B:0040B9C2
AND EDI,8000000F<---------變數[ESP+18] 除以16的餘數
001B:0040B9C8
MOV DL,[EDX+EAX]<---------將目標串的當前位的ASC碼寫入DL
001B:0040B9CB
JNS 0040B9D2<-------------若餘數不為負值則跳轉?[ESP+18]
的所有賦值動作都在掌握之內,怎
麼可能大於80000000?有病!
001B:0040B9CD
DEC EDI
001B:0040B9CE OR
EDI,-10
001B:0040B9D1 INC EDI
001B:0040B9D2
MOV EDI,[EDI*4+ESP+24]<---根據變數[ESP+18]的值讀入密碼錶
(19751218...)的相應位的數值寫
入EDI
001B:0040B9D6
MOVSX EDX,DL<---------------將DL中的目標串當前位的ASC碼值寫
入EDX
001B:0040B9D9
IMUL EDI,EDX<--------------當前密碼值與目標串當前ASC碼值的
乘積
001B:0040B9DC
ADD EDI,[ESP+20]<---------再加上當前假碼數值
001B:0040B9E0
ADD EBP,EDI<--------------在EBP中累計這一結果
001B:0040B9E2
MOV EDI,[ESP+18]<---------將變數[ESP+18]的值寫入EDI
001B:0040B9E6
INC EAX<------------------目標串的當前位+1
001B:0040B9E7
ADD EDI,ESI<--------------變數[ESP+18]+假碼的當前位
001B:0040B9E9
CMP EAX,ECX<--------------目標串的當前位是否等於目標串長度
001B:0040B9EB
MOV [ESP+18],EDI<---------更新變數[ESP+18]
001B:0040B9EF
JL 0040B9BA<-------------迴圈直至目標串結束
這一段迴圈是針對假碼的每一位p[i]計算一個值A[i]
= f(p[i],目標串),(其中i=ESI)
設目標串為d,密碼錶為c,則 A[i] = {
int num = 0;
for(int
j=0; j<d.lenth(); j++)
num = num + d[j]*c[j*i%16]+p[i];
return num;
}
--------------------------------------------------------------------------------
001B:0040B9F1
MOV EAX,ESI
001B:0040B9F3 AND
EAX,80000007<---------EAX等於假碼當前位除以8的餘數
001B:0040B9F8 JNS
0040B9FF
001B:0040B9FA DEC EAX
001B:0040B9FB
OR EAX,-08
001B:0040B9FE INC
EAX
001B:0040B9FF JNZ 0040BA61<-------------餘數不為零則跳轉
001B:0040BA01
CMP ESI,13
001B:0040BA04 JNZ
0040BA10<-------------不為19則跳轉,除以8餘0怎麼可能
等於19?有病!
001B:0040BA06 MOV
DWORD PTR [ESP+18],00000000
001B:0040BA0E JMP
0040BA17
--------------------------------------------------------------------------------
001B:0040BA10
LEA ECX,[ESI+01]<---------ECX等於假碼當前位+1
001B:0040BA13
MOV [ESP+18],ECX<---------存到臨時變數[ESP+18]
001B:0040BA17
MOV EDX,[ESP+000001C4]<---字串形式的假碼的地址的指標寫
入EDX
001B:0040BA1E
MOV EAX,ESI<--------------EAX等於假碼當前位
001B:0040BA20
MOV EDI,[EDX]<------------字串形式的假碼的地址寫入EDI
001B:0040BA22
CDQ<----------------------------EDX清零
001B:0040BA23 MOV
ECX,[EDI-08]<---------假碼長度寫入ECX
001B:0040BA26 IDIV
ECX<------------------在EDX裡得到假碼當前位(EAX)除
以假碼長度(ECX)的餘數,還不就
是假碼的當前位?毛病!
001B:0040BA28
MOV AL,[EDI+EDX]<---------將假碼當前位的ASC碼值寫入AL
001B:0040BA2B
MOV EDI,0000000A<---------EDI等於10
001B:0040BA30
MOV [ESP+17],AL<----------將假碼當前位的ASC碼值寫入臨時變
量[ESP+17]
001B:0040BA34
MOV EAX,EBP<--------------當前A值,參見前述
A[i]=f(p[i],目標串)
001B:0040BA36
CDQ<----------------------------EDX清零
001B:0040BA37 IDIV
EDI<------------------在EDX裡得到當前A值(EAX)除以10
的餘數
001B:0040BA39 MOV
EAX,[ESP+18]<---------EAX=假碼當前位+1
001B:0040BA3D MOV
EDI,[ESP+000001C8]<---ESP+1c8+30是數值形式的假碼的地址
001B:0040BA44
SUB DL,[EAX*4+EDI+30]<----將DL裡的餘數減去假碼下一位的ASC
碼值
001B:0040BA48
MOV AL,[ESP+17]<----------AL=假碼當前位的ASC碼值
001B:0040BA4C
ADD DL,AL<----------------餘數-假碼下一位在加上當前位後的
結果
001B:0040BA4E
MOV EAX,ESI
001B:0040BA50 PUSH
EDX
001B:0040BA51 CDQ
001B:0040BA52
IDIV ECX
無用功,老毛病!
001B:0040BA54 MOV ECX,[ESP+000001C8]
001B:0040BA5B PUSH EDX
001B:0040BA5C
CALL 0044A234<-------------將結果替換字串形式假碼的當前位
這一段是將假碼的第9、17位做變換:
p[8]
= p[9] + A[8]%10
p[16] = p[17] + A[16]%10
--------------------------------------------------------------------------------
001B:0040BA61
MOV EAX,ESI
001B:0040BA63 MOV
ECX,00000007
001B:0040BA68 CDQ
001B:0040BA69 IDIV
ECX
001B:0040BA6B TEST EDX,EDX
001B:0040BA6D
JNZ 0040BA96
001B:0040BA6F CMP
ESI,13
001B:0040BA72 JNZ 0040BA78
001B:0040BA74
XOR ECX,ECX
001B:0040BA76 JMP
0040BA7B
001B:0040BA78 LEA ECX,[ESI+01]
001B:0040BA7B
MOV EAX,EBP
001B:0040BA7D MOV
EDI,0000000A
001B:0040BA82 CDQ
001B:0040BA83 IDIV
EDI
001B:0040BA85 MOV EAX,[ESP+000001C8]
001B:0040BA8C
SUB EDX,[ECX*4+EAX+30]
001B:0040BA90 JNZ
0040BADA
累死了!這一段就不祥加註釋了,反正就是驗證第8、15位(i%7==0)的A值必須為零,
否則就JNZ
0040bada,驗證失敗!
--------------------------------------------------------------------------------
001B:0040BA92
INC DWORD PTR [ESP+1C]<---若A值為零則標誌變數[ESP+14]+1
001B:0040BA96
INC ESI<------------------假碼當前位+1
001B:0040BA97
CMP ESI,13
001B:0040BA9A JLE
0040BA9E
001B:0040BA9C MOV ESI,EBX<--------------若當前位為19(從0開始計算,19是
第20位),則當前位回到1
001B:0040BA9E
LEA ECX,[ESP+10]
001B:0040BAA2 MOV
DWORD PTR [ESP+000001BC],FFFFFFFF
001B:0040BAAD CALL
00449CDC
001B:0040BAB2 CMP DWORD PTR
[ESP+1C],14
001B:0040BAB7 JL 0040B8B1<-------------迴圈直至標誌為等於20,相當於把
所有工序重複10遍,因為ESI每從1
到19一次,僅有兩次機會修改標誌位
001B:0040BABD
MOV AL,BL<----------------函式返回值置1,表示驗證成功
001B:0040BABF MOV ECX,[ESP+000001B4]
001B:0040BAC6
POP EDI
001B:0040BAC7 POP
ESI
001B:0040BAC8 POP EBP
001B:0040BAC9 POP
EBX
001B:0040BACA MOV FS:[00000000],ECX
001B:0040BAD1
ADD ESP,000001B0
001B:0040BAD7 RET
0008
001B:0040BADA LEA ECX,[ESP+10]
001B:0040BADE
MOV DWORD PTR [ESP+000001BC],FFFFFFFF
001B:0040BAE9
CALL 00449CDC
001B:0040BAEE XOR
AL,AL<---------------函式返回值置0,表示驗證失敗
001B:0040BAF0 JMP
0040BABF
這一驗證函式僅僅驗證了假碼的兩位,然後又將假碼的另兩位做了10次變換,然後就恭喜你註冊成功!所以必須繼續跟蹤變碼,發現果然在你下達了關閉程式的指令後它在程式視窗從螢幕上消失的同時偷偷又呼叫了一個類似的驗證函式,這次它用同樣的方法驗證變碼的其中一位而將變碼的另六位做了20次變換!重新啟動程式後你會發現它在很多各地方再次進行驗證和變換,經過推理可知如果你輸入的註冊碼的每一位的A值都等於0,而且每一位在變換後都可以保持原值,則不管程式如何變換,該註冊碼永遠都能透過驗證!
所以。。。俺的序號產生器可以誕生了!
結語:
瘋狂單詞構造了一個巧妙的演算法,要求註冊碼滿足:
註冊碼
= F(註冊碼,使用者名稱)
然後他每次驗證註冊碼之後,只有正確的註冊碼可以保持不變,並透過下一次的驗證。
但不足的是該作者的離散數學功底太差,他將大F分解成多個小f時僅僅採用了分段函
數的形勢,函式表示式本身是一樣的,僅僅是作用域不同而已(驗證不同的位),假如能
夠將大F分解成多個函式表示式不同的小f,透過跟蹤小f分析其大F的演算法將是非常困難的!
當然,衷心希望此文不要被瘋狂單詞作者看見,即使看見,也衷心希望他不會馬上瘋狂:)
預告:俺的下一篇教程將是《楚漢棋緣》,據說是很不錯的象棋共享軟體,敬請關注!
相關文章
- 瘋狂單詞的破解方法! (3千字)2000-08-22
- 瘋狂單詞v1.6破解 (5千字)2001-11-02
- 中級例項教程――之《楚漢棋緣V1.36》2015-11-15
- 瘋狂單詞破解實錄(初學者請進!) (9千字)2000-08-24
- 瘋狂的 Vue3 之 Setup2021-04-22Vue
- 單例與單例項之爭2016-02-23單例
- 面向切面程式設計之瘋狂的 Aspects2016-04-07程式設計
- 騰訊“瘋狂”開源!2020-01-22
- 網易瘋狂佇列2017-08-17佇列
- 瘋狂學習——DP!2024-04-12
- 京東正在瘋狂招人。。。2024-09-11
- 瘋狂膜拜!阿里出品 “Spring Security” 王者晉級文件!2021-05-29阿里Spring
- 瘋狂連連看之開發遊戲介面元件2011-07-15開發遊戲元件
- js統計陣列中單詞出現次數程式碼例項2017-04-15JS陣列
- Android逆向之旅--瘋狂兔子無敵跑跑 內購破解教程2018-05-28Android
- 瘋狂連連看之載入介面的圖片2011-07-15
- 英語背單詞專案(資料庫中是4級單詞)2020-12-06資料庫
- 瘋狂的遊戲代言人!2020-03-11遊戲
- 瘋狂的程式設計世界2013-01-16程式設計
- 瘋狂的程式設計師2012-07-08程式設計師
- 超級簡單入門vuex 小例項2018-12-07Vue
- css二級下拉選單程式碼例項2017-04-09CSS
- jQuery 省市級聯選單程式碼例項2017-04-18jQuery
- js省市級聯選單程式碼例項2017-04-15JS
- ORM 例項教程2019-02-18ORM
- Java例項教程2020-10-26Java
- oracle之 單例項監聽修改埠2018-04-10Oracle單例
- 單例項刪除ASM例項2012-11-29單例ASM
- 瘋狂的比特幣–資訊圖2013-11-18比特幣
- 瘋狂android講義目錄2014-03-24Android
- 瘋狂刪除tomcat日誌2024-07-18Tomcat
- 數字馬力正在瘋狂招人。。2024-10-28
- 瘋狂的 Vue3 之 四個函式寫應用2021-04-21Vue函式
- NLP自然語言處理中的hanlp分詞例項2019-02-18自然語言處理HanLP分詞
- select下拉選單多級級聯效果程式碼例項2017-04-04
- js將單詞數字母變換為大寫程式碼例項2017-04-06JS
- Smarty例項教程(2)2009-02-25
- Smarty例項教程(5)2009-02-25