【軟件名稱】:
Reglo v3.5a
【下載地址】:
Http://www.basta.com/
【軟件簡介】:
Reglo 是一款電腦屏幕測量工具,其實就是一把尺子,有多種單位可以選擇,用於測量電腦屏幕中圖片或其它.
【軟件限制】:
如果沒有註冊,則只有 30 天的試用期.
【破解聲明】:
我是搞軟件的,知道一款軟件的開發,需要付出很多很多,所以如果大家有點錢的話,還是多支持支持軟件的作者,這樣人家才有乾勁呀.其實,這款軟件對我沒什麼用,只是好奇它的註冊算法而已,如果大家有興趣,不妨在看完了這篇註冊算法分析後,用Reglo 的姊妹軟件試試,看它們的註冊算法是什麼.它的姊妹軟件包括:
AppToService v4.0 一個Windows 控制台,這個使用控制台的軟體可以讓你把常規程序或服務器,運行做Windows 服務.
Buzof v4.0 一款讓你可以消除煩人的系統消息的工具,比如,刪除文件時,系統總會提示是否真的要刪除等信息.
Deletor v4.2 一款可以按照你的要求,進行刪除的工具.例如,刪除滿足一定大小/時間/屬性等條件的文件夾等等.
Filo v4.2 修改文件或文件夾屬性的工具.
Splitty v4.0 文件分割合併工具.
ZMover v7.2 桌面佈局管理軟件,可以讓你指定程序窗口位置/大小/層次等.
【破解工具】:
PEID v0.94,OllyDBG v1.10
【破文作者】:
WSLVIC 電郵:Crk4u@163.com
【破解時間】:
二〇一一年七月二日
【破解過程】:
———————————————————————————————————————————
不必多說,首先是查查殼,用PEID 一看,原來是Microsoft Visual C++ 7.0,也好,省的脫殼了.然後當然是用OD 載入,載入後停在這裡:
00416197 > $ 6A 60 PUSH 60
00416199 . 68 F0284300 PUSH Reglo2.004328F0
0041619E . E8 451A0000 CALL Reglo2.00417BE8
004161A3 . BF 94000000 MOV EDI,94
004161A8 . 8BC7 MOV EAX,EDI
004161AA . E8 C10E0000 CALL Reglo2.00417070
004161AF . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004161B2 . 8BF4 MOV ESI,ESP
004161B4 . 893E MOV DWORD PTR DS:[ESI],EDI
004161B6 . 56 PUSH ESI ; /pVersionInformation
004161B7 . FF15 A8014300 CALL DWORD PTR DS:[<&KERNEL32.GetVersion>; \GetVersionExA
......
典型的VC 入口呀,不急下斷點,先看看註冊窗口是什麼樣的,按F9 運行,在標尺上點擊右鍵選擇"&About Reglo...",在彈出窗口中選擇"&Purchasing Information.. .",終於出現了,原來註冊碼分兩部分,每部分都8 位,總長度是16,也就是十六進制的0x10,好了先看看能不能輸入中文(呵呵,習慣了),輸入"瑤淼"---原來可以輸入中文,當然也可以輸入英文字母和數字,胡亂輸一通,比如"11111111-22222222" 點擊"Ok",彈出對話框"The password you have entered is incorrect." ---現在當然不能註冊,不過記下了這串E 文,點擊"確定" 回到OD 中,用OD 自帶的字符串搜索功能搜索"所有參考文本字符串",很快就搜索完畢了,可惜上面那串E 文,連影子都沒有,再用OD 插件"超級字串參考" 和"中文搜索引擎",也沒找到,暈,只好下對話框斷點了,試試MessageBoxA 吧,在命令條中輸入"bpx messageboxa",然後點擊F9 運行,輸入註冊碼"11111111-22222222",點擊"確定",呵呵,運氣不錯,被OD 斷了下來,斷點停在:
0042B8C7 |. FF15 9C034300 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA (1)
不管它,向上看,直到該段代碼的開始處,
0042B7FB /> /55 PUSH EBP (2)
點擊該行,這時,OD 顯示"跳轉來自0042B924",一看,竟然是這段代碼的最後一行
0042B924 \.^\E9 D2FEFFFF JMP Reglo2.0042B7FB (3)
於是想到應該是從其它某處調用到(1) 之後的某個地方,再執行到(3),然後再跳轉到(2),仔細觀察代碼,發現(1) 之後的0042B904 處
0042B904 |. C2 0C00 RETN 0C
非常可疑,其下一行為
0042B907 |$ 55 PUSH EBP
在該行點擊,OD 顯示"本地調用來自00401698,00406C15,00425097,0042B96A,0042C086,0042C163",到底從這裡的哪個調用來的呢,不忙,先在該處按F2,下一個斷點,按F9 運行程序,胡亂輸入"1111111122222222" 點擊"確定",發現OD 已經斷在了該處,在OD 工具欄中點擊圖標"K" --- 調用堆棧,發現OD 顯示"函數過程Reglo.0042B907,調用來自Reglo.0042B96A",於是在OD 信息窗口的"本地調用來自00401698,00406C15,00425097,0042B96A...",上點右鍵選擇"轉到Call 來自0042B96A",於是OD 立刻來到了0042B96A所在的代碼段,在該段的段頭:
0042B929 /$ B8 6DED4200 MOV EAX,Reglo.0042ED6D
上點擊,發現OD 顯示"本地調用來自004018F9, 00401F57, 0040777D, 00407CB3, 0040887B, 0040A7CC, 0040B31A, 0040B464, 00412AC9, 00412AE0, 004250B4, 004286E2, 0042875A, 0042BF9A",暈,又是一堆,咋辦呢,還是老辦法,F2 斷點→F9 運行→輸入註冊碼→點擊"確定",OD 斷在了0042B929,在點擊圖標"K",OD 顯示"函數過程Reglo.0042B929,調用來自Reglo.0040B464" ,再次轉到0040B464 所在的代碼段,該段的段頭為:
0040B350 /$ 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
下斷點,運行到該行,F8 單步到
0040B3AC |. E8 519A0100 CALL Reglo.00424E02
處時,註冊窗口彈出,呵呵,該行是調用註冊窗口的.繼續F8,直到
0040B3BA |. 8B8424 D80000>MOV EAX,DWORD PTR SS:[ESP+D8]
這時在OD 信息窗口中看到了已經輸入的註冊碼.
哈哈,萬里長征終於走到了第一站!
這點很重要,這是進行下一步調試的關鍵,於是整理一下,刪除除了0040B3BA 處以外的所有其它斷點.註冊碼分析就要開始了.在0040B3BA 下方看到一個Call
0040B3DA |. E8 A17B0000 CALL Reglo.00412F80
F7 跟進去一看,果真是進行註冊碼測試,好,先下個斷點.然後按Ctrl+F2 重新載入,輸入註冊碼時輸入"1111111122222222",OD 斷在了0040B3DA,F7 跟進,便來到了下方,
┌──────────────────────────────────────────────┐
│00412F80 PUSH EBX │
│00412F81 PUSH EBP │
│00412F82 MOV EBX,EAX ; EBX=輸入的註冊碼│
│00412F84 PUSH ESI │
│00412F85 XOR EBP,EBP │
│00412F87 TEST EBX,EBX │
│00412F89 PUSH EDI │
│00412F8A JE Reglo.004130C7 ; 一跳就死│
│00412F90 LEA EDX,DWORD PTR DS:[EAX+1] ; EDX=第二個字符的地址│
│00412F93 /MOV CL,BYTE PTR DS:[EAX] ; CL=第一個字符│
│00412F95 |INC EAX ; 下一個│
│00412F96 |TEST CL,CL ; 測試CL,是否為全零│
│00412F98 \JNZ SHORT Reglo.00412F93 ; 循環測試CL 是否為00 │
│00412F9A SUB EAX,EDX ; EAX=尾字符的地址,EDX=第二個字符的地址│
│00412F9C CMP EAX,10 ; 比較字符串長度是否為16 │
│00412F9F JNZ Reglo.004130C7 ; 不等就跳,一跳就完│
│00412FA5 MOV ESI,EAX ; ESI 指向尾字符│
└──────────────────────────────────────────────┘
這段代碼沒什麼難度,就是測試下輸入的註冊碼的長度,及每個字符是否為00,之後則來到,
┌──────────────────────────────────────────────┐
│00412FA7 /MOVSX EAX,BYTE PTR DS:[ESI+EBX-1] │
│00412FAC |DEC ESI │
│00412FAD |PUSH EAX │
┌──────────────────────────────────────────────┐
│00412FAE |CALL Reglo.00415ACF ; 判斷是否是數字(4)│
└──────────────────────────────────────────────┘
│00412FB3 |ADD ESP,4 │
│00412FB6 |TEST EAX,EAX │
│00412FB8 |JE Reglo.004130C7 ; 這裡一跳就完蛋│
│00412FBE |TEST ESI,ESI │
│00412FC0 \JNZ SHORT Reglo.00412FA7 │
└──────────────────────────────────────────────┘
這是一段循環,中間有個Call,其過程是逐字讀入註冊碼的每一位,然後用Call 進行測試,看來是關鍵測試了,F7 步入,OD 轉到這裡,
┌──────────────────────────────────────────────┐
│00415ACF PUSH EBP │
│00415AD0 MOV EBP,ESP │
│00415AD2 PUSH ECX │
│00415AD3 PUSH EBX │
│00415AD4 MOV EBX,DWORD PTR SS:[EBP+8] ; 地址移動8位│
│00415AD7 CMP EBX,0FF ; 判定是否是ASCII 字符│
│00415ADD /JBE SHORT Reglo.00415B49 ; 小於等於255 時,這裡一定跳,否則必死│
└──────────────────────────────────────────────┘
這段的關鍵是上面的最後兩句,0FF 是255,EBX 中是當前位置註冊碼的ASCII 值(別忘了這段是在循環中),所以這兩句是測試,如果當前位置註冊碼的ASCII 值<= 255 時,發生跳轉,由255 可知,要求輸入字符是"基本拉丁字符集" 和"增補拉丁字符集1" 中的字符,由於當前輸入的測試註冊碼是"1111111122222222",故滿足要求,不過你可以試試輸入註冊碼"!111111122222222",這時Reglo 會彈出對話框"Please enter no more than 8 characters." 註冊就失敗了.然後跳轉到00415B49 處,注意,中間有段代碼00415ADF-00415B47 是處理當字符的ASCII>255 時,進行的操作,我沒跟過去,如果誰有興趣不妨看看Reglo 在這段干了什麼.跳轉到了這裡,
┌──────────────────────────────────────────────┐
│00415B49 \PUSH EBX │
│00415B4A CALL Reglo.00419636 ; 判斷是否是數字:否,則跳出. │
│00415B4F POP ECX │
│00415B50 POP EBX │
│00415B51 LEAVE │
│00415B52 RETN │
└──────────────────────────────────────────────┘
又是一個 Call! 煩哪,還得跟進去,來到,
┌──────────────────────────────────────────────┐
│00419636 /$ CALL Reglo.00419174 ; 別管,給EAX 一個地址│
│0041963B |. MOV EAX,DWORD PTR DS:[EAX+64] │
│0041963E |. CMP EAX,DWORD PTR DS:[43FF84] ; Reglo.0043FF30 │
│00419644 |. JE SHORT Reglo.0041964B ; 必跳│
│00419646 |. CALL Reglo.0041911B │
│0041964B |> CMP DWORD PTR DS:[EAX+28],1 │
│0041964F |. JLE SHORT Reglo.00419661 ; 必跳│
│00419651 |. PUSH 4 │
│00419653 |. PUSH DWORD PTR SS:[ESP+8] │
│00419657 |. PUSH EAX │
│00419658 |. CALL Reglo.004198A3 │
│0041965D |. ADD ESP,0C │
│00419660 |. RETN │
│00419661 |> MOV EAX,DWORD PTR DS:[EAX+48] ; UNICODE " ((((( │
│00419664 |. MOV ECX,DWORD PTR SS:[ESP+4] │
│00419668 |. MOVZX EAX,BYTE PTR DS:[EAX+ECX*2] ; 若ECX 為0-9 的數字,則EAX=84 │
│0041966C |. AND EAX,4 ; EAX=84 And 4=4 │
│0041966F \. RETN │
└──────────────────────────────────────────────┘
怎麼又是一堆Call!而且第一句就是一個Call,F7 進入,發現一堆系統調用,包括GetLastError/TlsGetValue/TlsSetValue/GetCurrentThreadID/SetLastError 等等,它們是乾什麼的呢?為什麼這麼做?到現在也沒搞清楚,不過至少知道這個Call 改變了EAX,也就是給EAX 了一個新地址,在這個Call 後的兩句,可以看到它是在進行地址比較,不管它了,來到00419644 處,這裡一定要跳,之後來到0041964B,與1 進行比較,之後到了0041964F 處跳轉至00419661,關鍵來了,這句給了EAX 一個UniCode 字符串,這是乾啥用的?先不急,看看它的下一句,在OD 的信息窗口看到了SS:[ESP+4] 中放置的其實就是當前位置註冊碼的ASCII 值,並賦給了ECX,ok,再下一句看到了DS:[EAX+ECX *2],這又是乾什麼呢,分析看看:當前EAX 是一個指向UniCode 的地址,ECX 是ASCII 值,所以EAX+ECX*2 必是一個地址,運行到該句OD 顯示"DS:[00432AC6 ]=84",在其上點擊右鍵,選擇"數據窗口中跟隨地址",發現數據窗口中是這樣一串數據"84 00 84 00 84 00 84 00..."---共10 組"84 00",於是終於明白了這段到底是在幹什麼,也就是說從該段的第一個Call 給了EAX 一個地址,通過這個地址加上一定的偏移量,指向一些特定地址,例如00419661 處, EAX=DS:[EAX+48],如果當前位置的字符是0-9 (ASCII 的30-39),那麼從DS:[EAX+48] 開始,偏移30*2 至39*2,都將得到84 這個值,也就是上面的10 組"84 00",那麼為什麼是84 呢,從0041966C 處,可看出84 與4 進行"與" 操作,EAX 將等於4,不等於0,換句話說EAX 就是一個flag,當EAX 非零時,說明匹配成功,當前字符是0-9 的數字,否則EAX=0 說明匹配失敗.其實在UltraEdit 中也可找到這兩個字符串,其中,
UNICODE " ((((( H" 在實偏移31C62H 處
"84 00 84 00 84 00 84 00 84 00 84 00 84 00..." 在實偏移31CC2H 處
誰有興趣的話,不妨算算它們之間的偏移是否是0x30*2 - 0x39*2
至此,萬里長征第二步算完成了,可是只是知道註冊碼由數字組成,卻不知道形成規則是什麼,怎麼辦,只好一路返回,回到00412FB3 處,此處仍在循環中,在00412FC2 處按F4,運行到該行,
┌──────────────────────────────────────────────┐
│00412FC2 MOVSX EDI,BYTE PTR DS:[EBX+F] ; EDI=末字符│
│00412FC6 SUB EDI,30 │
│00412FC9 MOV EAX,EDI ; EAX=末字符的數值│
│00412FCB IMUL EAX,EDI ; EAX=末位數的平方│
│00412FCE CDQ ; CDQ 雙字擴展成四字│
│00412FCF MOV ECX,0A │
│00412FD4 IDIV ECX ; 末位數的平方(EAX)除10,商放EAX,余放EDX │
│00412FD6 MOV AL,BYTE PTR DS:[EDI+EBX] ; AL=第EDI+1 個字符│
│00412FD9 ADD DL,30 ; 加30 │
│00412FDC CMP AL,DL ; │
│00412FDE JNZ Reglo.004130C7 ; 一跳就完│
└──────────────────────────────────────────────┘
這段代碼的關鍵是:
AL 是註冊碼的第EDI+1(EDI=末字符的數值) 個字符,而DL 是註冊碼的最後一個字符的平方,除以10,所得的餘數,由CMP Al,DL 及其下一行的JNZ Reglo.004130C7 可知,註冊碼的最後一位的數值i,決定著註冊碼第i+1 位的值必須為最後一位的數值的平方除10 所得的餘數,即:
最後一位數i 第i+1 位數模型
i=0 第1(i+1) 位為0(0^2 Mod 10) 0xxxxxxx xxxxxxx0
i=1 第2(i+1) 位為1(1^2 Mod 10) x1xxxxxx xxxxxxx1
i=2 第3(i+1) 位為4(2^2 Mod 10) xx4xxxxx xxxxxxx2
i=3 第4(i+1) 位為9(3^2 Mod 10) xxx9xxxx xxxxxxx3
i=4 第5(i+1) 位為6(4^2 Mod 10) xxxx6xxx xxxxxxx4
i=5 第6(i+1) 位為5(5^2 Mod 10) xxxxx5xx xxxxxxx5
i=6 第7(i+1) 位為6(6^2 Mod 10) xxxxxx6x xxxxxxx6
i=7 第8(i+1) 位為9(7^2 Mod 10) xxxxxxx9 xxxxxxx7
i=8 第9(i+1) 位為4(8^2 Mod 10) xxxxxxxx 4xxxxxx8
i=9 第10(i+1) 位為1(9^2 Mod 10) xxxxxxxx x1xxxxx9
由於輸入的測試註冊碼"1111111122222222" 不滿足這個要求,在00412FDE 處,就跳走了.不妨把測試註冊碼改為"1141111122222222",Ctrl+F2 重新運行,終於第一個坎00412FDE 過了,然後代碼來到,
┌──────────────────────────────────────────────┐
│00412FE4 |.LEA EAX,DWORD PTR DS:[EDI+1] ; EAX=末字符數值+1 │
│00412FE7 |.CMP EAX,0F │
│00412FEA |.JL SHORT Reglo.00412FEF ; 必跳│
│00412FEC |.SUB EAX,0F │
│00412FEF |>MOVSX ECX,BYTE PTR DS:[EAX+EBX] ; ECX=第末字符數值+2 個數字│
│00412FF3 |.LEA ESI,DWORD PTR DS:[ECX-30] ; ESI=第末字符數值+2 個數字的數值│
│00412FF6 |.CMP ESI,1 ; 第末字符數值+2 位數字必須大於等於1│
│00412FF9 |.JGE SHORT Reglo.00413000 ; 必跳│
└──────────────────────────────────────────────┘
測試註冊碼"1141111122222222" 的末位為"2",故第4(2+2) 個數字必須大於等於1,測試註冊碼已滿足要求,呵呵,繼續啊,第二個坎00412FF9 過了.繼續來到這裡,
┌──────────────────────────────────────────────┐
│00413000 |>XOR ECX,ECX │
│00413002 |.CMP ESI,1 │
│00413005 |.SETG CL ; 根據標誌寄存器,大於1 時,置CL=1 │
│00413008 |>MOV EDX,DWORD PTR SS:[ESP+14] │
│0041300C |.INC EAX ; EAX=末字符數值+2 │
│0041300D |.CMP EAX,0F │
│00413010 |.MOV DWORD PTR DS:[EDX],ECX ; DS:[0012F3C0]=00000000 │
│00413012 |.JL SHORT Reglo.00413017 │
│00413014 |.SUB EAX,0F │
│00413017 |>MOV ECX,EAX ; ECX=末字符數值+2 │
│00413019 |.ADD EAX,3 ; EAX=末字符數值+5 │
│0041301C |.CMP EAX,0F │
│0041301F |.JL SHORT Reglo.00413024 │
│00413021 |.SUB EAX,0F │
│00413024 |>MOVSX ESI,BYTE PTR DS:[ECX+EBX] ; ESI=第末字符數值+3 個數的ASCII 值│
│00413028 |.MOV ECX,EAX ; ECX=末字符數值+5 │
│0041302A |.ADD EAX,3 ; EAX=末字符數值+8 │
│0041302D |.SUB ESI,30 ; ESI=第末字符數值+3 個數的數值│
│00413030 |.CMP EAX,0F │
│00413033 |.JL SHORT Reglo.00413038 │
│00413035 |.SUB EAX,0F │
│00413038 |>MOVSX ECX,BYTE PTR DS:[ECX+EBX] ; ECX=第末字符數值+6 個數的ASCII 值│
│0041303C |.SUB ECX,30 ; ECX=第末字符數值+6 個數的數值│
│0041303F |.MOV EDX,EAX │
│;EDX=末字符數值+8(末字符數值+8<15 時)或末字符數值+8-15(末字符數值+8>=15 時) │
│00413041 |.IMUL ECX,ECX,64 ; ECX=(第末字符數值+6 個數的數值)*64 │
│00413044 |.ADD EAX,3 ; EAX=末字符數值+11 │
│00413047 |.CMP EAX,0F │
│0041304A |.JL SHORT Reglo.0041304F │
│0041304C |.SUB EAX,0F │
│0041304F |>MOVSX EDX,BYTE PTR DS:[EDX+EBX] │
│;EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的ASCII值│
│00413053 |.MOVSX EAX,BYTE PTR DS:[EAX+EBX] │
│;EAX=第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的ASCII值│
│00413057 |.SUB EDX,30 │
│;@EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值│
│0041305A |.ADD ECX,EAX ; ECX=ECX+EAX │
│0041305C |.LEA EDX,DWORD PTR DS:[EDX+EDX*4] ; EDX=@EDX*5 │
│0041305F |.LEA ECX,DWORD PTR DS:[ECX+EDX*2-30]; ECX=ECX+@EDX*A-30 │
│00413063 |.CMP ECX,7 ; ECX=ECX+@EDX*A-30=7(@EDX 是0-9 的數字) │
│00413066 |.JNZ SHORT Reglo.004130C7 ; 這裡一跳就完蛋了│
└──────────────────────────────────────────────┘
這段代碼比較複雜,註釋寫的老長,目的是方便修改測試註冊碼,否則,註冊無法繼續下去.其關鍵是00413063 處,要求ECX+@EDX*A-30=7,這裡:
ECX=(第末字符數值+6 個數的數值)*64+第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15 )}個數字的ASCII 值.
@EDX 是 0-9 中的某個數字.
對當前的測試註冊碼"1141111122222222" 而言,第8(2+6) 個數是"1",1*64=64;第14(2+12) 個數是"2",其ASCII=32 ,所以ECX=1*64+32=96.
同時@EDX=第11(2+9) 個數是"2",故ECX+@EDX*A-30=96+2*A-30=7A 不等於7,故無法繼續,如何修改測試註冊碼呢,觀察ECX,@EDX 可知,ECX>=30,@EDX>=0,故若ECX+@EDX*A-30=7,則ECX+@EDX*A=37,就必有@EDX*A<=7 ,所以@EDX=0,ECX=37,換句話說,
1.@EDX=0,意味著:
第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值必為0.
2.ECX=37,意味著:
第 末字符數值+6 個數的數值必為 0,
第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的數值必為7.
由此可以看出,末位數對整個註冊碼具有標誌性作用,我們不妨試著修改一下,對於"1141111122222222" 而言,末位數為"2",因為2+8<15,故第11 (2+9) 位數為0.又從"第末字符數值+6 個數的數值必為0" 可知,第8 位數為0,因為2+11<15,故第14(2+12 ) 位數必為7.測試註冊碼可改為"1141111022022722"
整理一下當末位數為0-9 時的註冊模型,如下:
最後一位數 i 模型
i=0 0xxxx0xx 0xx7xxx0
i=1 x1xxxx0x x0xx7xx1
i=2 xx4xxxx0 xx0xx7x2
i=3 xxx9xxxx 0xx0xx73
i=4 7xxx6xxx x0xx0xx4
i=5 x7xxx5xx xx0xx0x5
i=6 xx7xxx6x xxx0xx06
i=7 0xx7xxx9 xxxx0xx7
i=8 x0xx7xxx 4xxxx0x8
i=9 xx0xx7xx x1xxxx09
繼續吧,還有不少工作要做,越過第三個坎00413066,我們來到這裡,
┌──────────────────────────────────────────────┐
│00413068 |.XOR EAX,EAX │
│0041306A |.MOV ECX,10 │
│0041306F |.NOP │
│00413070 |>/MOVSX EDX,BYTE PTR DS:[ECX+EBX-1];EDX=第ECX 個數字│
│00413075 |.|DEC ECX │
│00413076 |.|IMUL EDX,ECX │
│00413079 |.|ADD EAX,EDX ;@EAX=Sum{(數字的ACSII 值)*(其所在位置-1)} │
│0041307B |.|TEST ECX,ECX │
│0041307D |.\JNZ SHORT Reglo.00413070 │
│0041307F |.LEA EDX,DWORD PTR DS:[EDI+E] ;EDI=末位數的數值│
│00413082 |.CMP EDX,0F ;EDX=14(末位數為0 時) │
│00413085 |.JL SHORT Reglo.0041308A ;必跳│
│00413087 |.SUB EDX,0F │
│0041308A |>MOVSX ECX,BYTE PTR DS:[EDX+EBX] ;EDX=末位數的數值+14-15(末位數不為0 時) │
│;@ECX=第15 個數的ASCII(當末位數為0 時)或第末位數數值位數的ASCII 值(當末位數不為0 時) │
│0041308E |.IMUL ECX,EDX ;ECX=@ECX*EDX │
│00413091 |.SUB EAX,ECX ;EAX=@EAX-ECX │
│00413093 |.DEC EDX │
│00413094 |.JNS SHORT Reglo.00413099 ;結果為正就跳(SF=0) │
│00413096 |.ADD EDX,0F │
│00413099 |>MOV CL,BYTE PTR DS:[EDX+EBX] │
│;@CL=第14 個數的ASCII(當末位數為0 時)或第末位數數值-1 位數的ASCII 值(當末位數不為0 時)│
│0041309C |.MOVSX EDI,CL ; │
│0041309F |.IMUL EDI,EDX ; │
│004130A2 |.SUB EAX,EDI ;EAX=@EAX-@ECX*EDX-@CL*EDI │
│004130A4 |.CDQ │
│004130A5 |.MOV EDI,0A ;EDI=除數10 │
│004130AA |.IDIV EDI │
│004130AC |.ADD DL,30 ;DL=餘數的ASCII 值│
│004130AF |.CMP DL,CL ;CL=第14 個數或第末位數數值-1 位數的ASCII │
│004130B1 |.JNZ SHORT Reglo.004130C7 ;一跳就死│
└──────────────────────────────────────────────┘
一進來,就看見一個循環,不過這個循環倒也簡單,就是從尾到頭,將每個數字的ASCII 值與其(所在位置-1)相乘,並累加起來送給EAX,也就是說:@EAX =n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15,循環完畢後,來到了0041307F,覺得有點奇怪,因為在這段代碼附近,沒看到EDI 是何時賦值的,仔細找了好一段代碼才發現,上次EDI 是在00412FC6 處賦值的,其值是末位數的數值,對當前測試註冊碼"1141111022022722" 而言,它就是"2".這段代碼的關鍵是004130AF,它事實上是要求:
當末位數為 0 時,
設置被除數 EAX=@EAX-n15*14-n14*13
設置第 14 位數的數值為 EAX/10 的餘數
當末位數為 1 時,
設置被除數 EAX=@EAX-n1*0-n15*14=@EAX-n15*14
設置第 15 位數的數值為 EAX/10 的餘數
當末位數為 2 時,
設置被除數 EAX=@EAX-n2*1-n1*0=@EAX-n2*1
設置第 1 位數的數值為 EAX/10 的餘數
當末位數不為 0,1,2 時,
設置被除數 EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
設置第i-1 位數的數值為EAX/10 的餘數,(其中,i=末位數數值)
先在的問題是如何修改測試註冊碼,算出EAX,再求餘,顯然不是一個好辦法,解決方法是在4130AF 處下一個斷點,看OD 信息窗口中的DL,是什麼數字,再退出Reglo ,修改指定位置的數字為DL 中的值.例如,當前測試註冊碼,末位數是"2",故需要修改第1 位數為DL 中的值7,修改後,測試註冊碼變為" 7141111022022722",試試,呵呵,又過一關吶!如此以來,第四個坎也過了.之後,來到這裡,
┌──────────────────────────────────────────────┐
│004130B3 PUSH ESI │
│004130B4 PUSH 10 │
┌──────────────────────────────────────────────┐
│004130B6 CALL Reglo.00412ED0 (5)│
└──────────────────────────────────────────────┘
│004130BB ADD ESP,8 │
│004130BE TEST EAX,EAX │
│004130C0 MOV EAX,1 │
│004130C5 JNZ SHORT Reglo.004130C9 │
│004130C7 MOV EAX,EBP │
│004130C9 POP EDI │
│004130CA POP ESI │
│004130CB POP EBP │
│004130CC POP EBX │
│004130CD RETN │
└──────────────────────────────────────────────┘
這裡有個Call,應該是調用註冊成功的對話框吧,哈哈,迫不及待了,按F9 直接運行!什麼也沒彈出來,但仔細一看原來關於對話框中的兩行字符"THIS SOFTWARE IS NOT REGISTERED " 以及"Expiration date:08/01/11" 不見了,呵呵,真的註冊成功了!測試註冊碼"7141111022022722" 真的是註冊碼!
高興歸高興,可是別得意忘形,凡事都要留個心眼.其實在寫這篇破文之前,我用的測試註冊碼不是"1111111122222222" 而是"0100000000070200",這個註冊碼和"7141111022022722" 一樣可以通過以上所有的坎,可是當你用它來註冊的時候,會讓Reglo 爆掉!不信你試試!為什麼呢?---其實原因很簡單,還有暗樁!在那裡?當然不能放過上面的那個Call (5) 了,F7 步入,到了這裡,
┌──────────────────────────────────────────────┐
│00412ED0 /$SUB ESP,8 ; 指向註冊碼│
│00412ED3 |.TEST EBX,EBX │
│00412ED5 |.JNZ SHORT Reglo.00412EDD ; 這裡必跳│
│00412ED7 |.XOR EAX,EAX │
│00412ED9 |.ADD ESP,8 │
│00412EDC |.RETN │
│00412EDD |>CMP DWORD PTR SS:[ESP+C],-1 ; -1 是在堆棧中設定的某種標誌│
│00412EE2 |.JNZ SHORT Reglo.00412EFD ; 必跳│
│00412EE4 |.MOV EAX,EBX │
│00412EE6 |.LEA EDX,DWORD PTR DS:[EAX+1] │
│00412EE9 |.LEA ESP,DWORD PTR SS:[ESP] │
│00412EF0 |>/MOV CL,BYTE PTR DS:[EAX] │
│00412EF2 |.|INC EAX │
│00412EF3 |.|TEST CL,CL │
│00412EF5 |.\JNZ SHORT Reglo.00412EF0 │
│00412EF7 |.SUB EAX,EDX │
│00412EF9 |.MOV DWORD PTR SS:[ESP+C],EAX │
│00412EFD |>MOV EAX,DWORD PTR SS:[ESP+C] │
│00412F01 |.PUSH EBP │
│00412F02 |.PUSH ESI │
│00412F03 |.PUSH EDI │
│00412F04 |.XOR EBP,EBP │
│00412F06 |.XOR ESI,ESI │
│00412F08 |.XOR EDI,EDI │
│00412F0A |.TEST EAX,EAX │
│00412F0C |.MOV DWORD PTR SS:[ESP+C],2 ; 12F38C 設置標誌2 │
│00412F14 |.MOV DWORD PTR SS:[ESP+10],1 ; 12F390 設置標誌1 │
│00412F1C |.JLE SHORT Reglo.00412F76 ; 一跳就死│
│00412F1E |.MOV EDI,EDI ; EDI=0 │
│00412F20 |>/MOVSX EAX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
│00412F24 |.|PUSH EAX │
│00412F25 |.|CALL Reglo.00415ACF ; 判斷是否為0-9 的數字│
│00412F2A |.|ADD ESP,4 │
│00412F2D |.|TEST EAX,EAX ; 是,則EAX=4,否,則EAX=0 │
│00412F2F |.|JE SHORT Reglo.00412F52 ; 若不是數字,就跳過本次循環│
│00412F31 |.|MOVSX ECX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
│00412F35 |.|MOV EAX,DWORD PTR SS:[ESP+ESI*4+C] ; EAX=2(ESI=0 時)或EAX=1(ESI=1 時) │
│00412F39 |.|SUB ECX,30 ; ECX=註冊碼第EDI+1 位的數值│
│00412F3C |.|IMUL EAX,ECX │
│; 奇數位用2 乘以當前位置數字的數值,偶數位用1 乘以當前位置數字的數值│
│00412F3F |.|CMP EAX,0A │
│00412F42 |.|JL SHORT Reglo.00412F47 │
│00412F44 |.|SUB EAX,9 ; 如果EAX>=10,則EAX=EAX-9 │
│00412F47 |>|XOR EDX,EDX ; EBP=Sum(EAX) │
│00412F49 |.|ADD EBP,EAX │
│00412F4B |.|TEST ESI,ESI │
│00412F4D |.|SETE DL ; 若ZF=1,則DL 置1 │
│00412F50 |.|MOV ESI,EDX ; ESI=0(奇數位) 或1(偶數位) │
│00412F52 |>|MOV EAX,DWORD PTR SS:[ESP+18] ; EAX=10h=16d │
│00412F56 |.|INC EDI │
│00412F57 |.|CMP EDI,EAX │
│00412F59 |.\JL SHORT Reglo.00412F20 │
│00412F5B |.TEST EBP,EBP │
│00412F5D |.JE SHORT Reglo.00412F76 ; 一跳就死│
│00412F5F |.MOV EAX,EBP ; 設置被除數│
│00412F61 |.CDQ │
│00412F62 |.IDIV DWORD PTR SS:[ESP+1C] │
│;第末字符數值+3 個數的數值作為除數放在堆棧SS:[12F39C] 中│
│00412F66 |.TEST EDX,EDX ; 測試餘數是否為0 │
│00412F68 |.JNZ SHORT Reglo.00412F76 ; 一跳就死│
│00412F6A |.POP EDI │
│00412F6B |.POP ESI │
│00412F6C |.MOV EAX,1 │
│00412F71 |.POP EBP │
│00412F72 |.ADD ESP,8 │
│00412F75 |.RETN │
│00412F76 |>POP EDI │
│00412F77 |.POP ESI │
│00412F78 |.XOR EAX,EAX │
│00412F7A |.POP EBP │
│00412F7B |.ADD ESP,8 │
│00412F7E \.RETN │
└──────────────────────────────────────────────┘
這段代碼比較簡單,其主要過程是,在堆棧中設置兩個標誌2 和1,然後逐字讀入註冊碼,奇數位數字*2,偶數位數字*1,當奇數位數字*2 的值大於等於10 時,對其進行減9 操作,將各位結果累加到EBP 中,作為被除數.同時取出SS:[12F39C] 中的數字作為除數,只要整除成功,就可通過測試.但值得注意的是00412F62 處,關於除數的選擇,由於該處除數位於堆棧SS:[12F39C] 中,改變該堆棧值的操作是在004130B3 處的PUSH ESI 操作,而上一次改變ESI 是在0041302D 處,由此可知所謂的除數其實是:第末字符數值+3 個數的數值.所以測試註冊碼"7141111022022722",恰巧其第5(2+3) 個數是"1",所以可以整除,而最終註冊成功.但對於測試註冊碼"0100000000070200" 來說,其第3(0+3) 個數是"0",所以Reglo 發生了除0 操作,而崩潰了.
至此,第五個坎也過了,軟件的註冊碼算法已基本清晰.可以寫總結了,但作為一個軟件從業人員,希望提醒大家,Reglo 竟然可以通過非法註冊碼導致軟件崩潰,說明其作者在驗證註冊碼的算法中存在Bug,也就是除零操作,這是一個軟件開發人員應盡量避免的低級錯誤,希望大家在自己的軟件中不要犯同樣的毛病.
【破解總結】:
———————————————————————————————————————————
從Reglo 的註冊算法中,可以明確的感受到,"末位數"的軸心作用,幾乎每一步都涉及到末位數的操作,要么用作指定某位為操作位,要么用作固定某位的數值,要么用作判定某條件是否達成,這可能是Reglo 註冊算法的最大特色吧.
對於Reglo v3.5a 的註冊碼的形制從現在的觀點來看,其模型可描述為:
k 註冊碼模型
k=0 0!#xx0xx 0xx7x?x0
k=1 x1!#xx0x x0xx7x?1
k=2 ?x4!#xx0 xx0xx7x2
k=3 x?x9!#xx 0xx0xx73
k=4 7x?x6!#x x0xx0xx4
k=5 x7x?x5!# xx0xx0x5
k=6 xx7x?x6! #xx0xx06
k=7 0xx7x?x9 !#xx0xx7
k=8 x0xx7x?x 4!#xx0x8
k=9 xx0xx7x? x1!#xx09
說明:
k 表示末位數數值,
x 表示 0-9 的數字,任選
! 位的數值必須大於等於 1
? 位的數值必須等於EAX 除10 所得的餘數,其中
@EAX=n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15 (ni 表示註冊碼第i 位的數值)
當末位數為 0 時,EAX=@EAX-n15*14-n14*13
當末位數為 1 時,EAX=@EAX-n15*14
當末位數為 2 時,EAX=@EAX-n2*1
當末位數為3-9 時,EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
# 位的數值不能為0,且能被SUM 整除,其中:
SUM=2*(n1+n3+n5+n7+n9+n11+n13+n15)-9*y+(n2+n4+n6+n8+n10+n12+n14+n16)
(y 表示:大於等於 5 的奇數位的個數)
那麼如何快速的得到一個註冊碼呢?
根據上面的模型,首先選擇一個末位數,不妨選"9" 吧,其註冊碼模型為"xx0xx7x? x1!#xx09",對於x 部分可讓它們為"0",這樣註冊碼變為" 0000070? 01!#0009",對於# 位的數字來說,當# = "1" 時,一定可以被整除,故令#="1",對於! 位的數字來說,! 位恰好是第11 位,該位數字乘以10(11-1) 一定被10 整除,故對? 位沒有影響,所以可以令! 位的數字為1-9 中的任意一個,這樣註冊碼變為"0000070? 01510009",就剩最後一位了,0 這麼多,好辦,7*(6-1)+1*(10-1)+5*(11-1)+1*(12-1)+9 *(16-1)=35+9+50+11+135=240,240 Mod 10 = 0,故? 位為"0",這樣註冊碼就為"00000700 01510009".其實上面的# 位可以不參加運算,原因已經說過,這裡寫出來只是為了大家看著方便.
當然使用註冊機KeyGen 是最方便的,不過我不想寫keygen,有興趣的朋友可以試試---我本來的目的也不是破解.又抽空看了看Reglo 的姊妹軟件Deletor,呵呵,註冊算法很相近呀,不過把註冊失敗的對話框彈出方式變為了MessageBoxW,註冊碼也變為了UniCode,新手們可以用它的姊妹軟件進行練手,是成長的不錯選擇.
哎,終於完了!神馬都是浮雲!
Reglo v3.5a
【下載地址】:
Http://www.basta.com/
【軟件簡介】:
Reglo 是一款電腦屏幕測量工具,其實就是一把尺子,有多種單位可以選擇,用於測量電腦屏幕中圖片或其它.
【軟件限制】:
如果沒有註冊,則只有 30 天的試用期.
【破解聲明】:
我是搞軟件的,知道一款軟件的開發,需要付出很多很多,所以如果大家有點錢的話,還是多支持支持軟件的作者,這樣人家才有乾勁呀.其實,這款軟件對我沒什麼用,只是好奇它的註冊算法而已,如果大家有興趣,不妨在看完了這篇註冊算法分析後,用Reglo 的姊妹軟件試試,看它們的註冊算法是什麼.它的姊妹軟件包括:
AppToService v4.0 一個Windows 控制台,這個使用控制台的軟體可以讓你把常規程序或服務器,運行做Windows 服務.
Buzof v4.0 一款讓你可以消除煩人的系統消息的工具,比如,刪除文件時,系統總會提示是否真的要刪除等信息.
Deletor v4.2 一款可以按照你的要求,進行刪除的工具.例如,刪除滿足一定大小/時間/屬性等條件的文件夾等等.
Filo v4.2 修改文件或文件夾屬性的工具.
Splitty v4.0 文件分割合併工具.
ZMover v7.2 桌面佈局管理軟件,可以讓你指定程序窗口位置/大小/層次等.
【破解工具】:
PEID v0.94,OllyDBG v1.10
【破文作者】:
WSLVIC 電郵:Crk4u@163.com
【破解時間】:
二〇一一年七月二日
【破解過程】:
———————————————————————————————————————————
不必多說,首先是查查殼,用PEID 一看,原來是Microsoft Visual C++ 7.0,也好,省的脫殼了.然後當然是用OD 載入,載入後停在這裡:
00416197 > $ 6A 60 PUSH 60
00416199 . 68 F0284300 PUSH Reglo2.004328F0
0041619E . E8 451A0000 CALL Reglo2.00417BE8
004161A3 . BF 94000000 MOV EDI,94
004161A8 . 8BC7 MOV EAX,EDI
004161AA . E8 C10E0000 CALL Reglo2.00417070
004161AF . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004161B2 . 8BF4 MOV ESI,ESP
004161B4 . 893E MOV DWORD PTR DS:[ESI],EDI
004161B6 . 56 PUSH ESI ; /pVersionInformation
004161B7 . FF15 A8014300 CALL DWORD PTR DS:[<&KERNEL32.GetVersion>; \GetVersionExA
......
典型的VC 入口呀,不急下斷點,先看看註冊窗口是什麼樣的,按F9 運行,在標尺上點擊右鍵選擇"&About Reglo...",在彈出窗口中選擇"&Purchasing Information.. .",終於出現了,原來註冊碼分兩部分,每部分都8 位,總長度是16,也就是十六進制的0x10,好了先看看能不能輸入中文(呵呵,習慣了),輸入"瑤淼"---原來可以輸入中文,當然也可以輸入英文字母和數字,胡亂輸一通,比如"11111111-22222222" 點擊"Ok",彈出對話框"The password you have entered is incorrect." ---現在當然不能註冊,不過記下了這串E 文,點擊"確定" 回到OD 中,用OD 自帶的字符串搜索功能搜索"所有參考文本字符串",很快就搜索完畢了,可惜上面那串E 文,連影子都沒有,再用OD 插件"超級字串參考" 和"中文搜索引擎",也沒找到,暈,只好下對話框斷點了,試試MessageBoxA 吧,在命令條中輸入"bpx messageboxa",然後點擊F9 運行,輸入註冊碼"11111111-22222222",點擊"確定",呵呵,運氣不錯,被OD 斷了下來,斷點停在:
0042B8C7 |. FF15 9C034300 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA (1)
不管它,向上看,直到該段代碼的開始處,
0042B7FB /> /55 PUSH EBP (2)
點擊該行,這時,OD 顯示"跳轉來自0042B924",一看,竟然是這段代碼的最後一行
0042B924 \.^\E9 D2FEFFFF JMP Reglo2.0042B7FB (3)
於是想到應該是從其它某處調用到(1) 之後的某個地方,再執行到(3),然後再跳轉到(2),仔細觀察代碼,發現(1) 之後的0042B904 處
0042B904 |. C2 0C00 RETN 0C
非常可疑,其下一行為
0042B907 |$ 55 PUSH EBP
在該行點擊,OD 顯示"本地調用來自00401698,00406C15,00425097,0042B96A,0042C086,0042C163",到底從這裡的哪個調用來的呢,不忙,先在該處按F2,下一個斷點,按F9 運行程序,胡亂輸入"1111111122222222" 點擊"確定",發現OD 已經斷在了該處,在OD 工具欄中點擊圖標"K" --- 調用堆棧,發現OD 顯示"函數過程Reglo.0042B907,調用來自Reglo.0042B96A",於是在OD 信息窗口的"本地調用來自00401698,00406C15,00425097,0042B96A...",上點右鍵選擇"轉到Call 來自0042B96A",於是OD 立刻來到了0042B96A所在的代碼段,在該段的段頭:
0042B929 /$ B8 6DED4200 MOV EAX,Reglo.0042ED6D
上點擊,發現OD 顯示"本地調用來自004018F9, 00401F57, 0040777D, 00407CB3, 0040887B, 0040A7CC, 0040B31A, 0040B464, 00412AC9, 00412AE0, 004250B4, 004286E2, 0042875A, 0042BF9A",暈,又是一堆,咋辦呢,還是老辦法,F2 斷點→F9 運行→輸入註冊碼→點擊"確定",OD 斷在了0042B929,在點擊圖標"K",OD 顯示"函數過程Reglo.0042B929,調用來自Reglo.0040B464" ,再次轉到0040B464 所在的代碼段,該段的段頭為:
0040B350 /$ 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
下斷點,運行到該行,F8 單步到
0040B3AC |. E8 519A0100 CALL Reglo.00424E02
處時,註冊窗口彈出,呵呵,該行是調用註冊窗口的.繼續F8,直到
0040B3BA |. 8B8424 D80000>MOV EAX,DWORD PTR SS:[ESP+D8]
這時在OD 信息窗口中看到了已經輸入的註冊碼.
哈哈,萬里長征終於走到了第一站!
這點很重要,這是進行下一步調試的關鍵,於是整理一下,刪除除了0040B3BA 處以外的所有其它斷點.註冊碼分析就要開始了.在0040B3BA 下方看到一個Call
0040B3DA |. E8 A17B0000 CALL Reglo.00412F80
F7 跟進去一看,果真是進行註冊碼測試,好,先下個斷點.然後按Ctrl+F2 重新載入,輸入註冊碼時輸入"1111111122222222",OD 斷在了0040B3DA,F7 跟進,便來到了下方,
┌──────────────────────────────────────────────┐
│00412F80 PUSH EBX │
│00412F81 PUSH EBP │
│00412F82 MOV EBX,EAX ; EBX=輸入的註冊碼│
│00412F84 PUSH ESI │
│00412F85 XOR EBP,EBP │
│00412F87 TEST EBX,EBX │
│00412F89 PUSH EDI │
│00412F8A JE Reglo.004130C7 ; 一跳就死│
│00412F90 LEA EDX,DWORD PTR DS:[EAX+1] ; EDX=第二個字符的地址│
│00412F93 /MOV CL,BYTE PTR DS:[EAX] ; CL=第一個字符│
│00412F95 |INC EAX ; 下一個│
│00412F96 |TEST CL,CL ; 測試CL,是否為全零│
│00412F98 \JNZ SHORT Reglo.00412F93 ; 循環測試CL 是否為00 │
│00412F9A SUB EAX,EDX ; EAX=尾字符的地址,EDX=第二個字符的地址│
│00412F9C CMP EAX,10 ; 比較字符串長度是否為16 │
│00412F9F JNZ Reglo.004130C7 ; 不等就跳,一跳就完│
│00412FA5 MOV ESI,EAX ; ESI 指向尾字符│
└──────────────────────────────────────────────┘
這段代碼沒什麼難度,就是測試下輸入的註冊碼的長度,及每個字符是否為00,之後則來到,
┌──────────────────────────────────────────────┐
│00412FA7 /MOVSX EAX,BYTE PTR DS:[ESI+EBX-1] │
│00412FAC |DEC ESI │
│00412FAD |PUSH EAX │
┌──────────────────────────────────────────────┐
│00412FAE |CALL Reglo.00415ACF ; 判斷是否是數字(4)│
└──────────────────────────────────────────────┘
│00412FB3 |ADD ESP,4 │
│00412FB6 |TEST EAX,EAX │
│00412FB8 |JE Reglo.004130C7 ; 這裡一跳就完蛋│
│00412FBE |TEST ESI,ESI │
│00412FC0 \JNZ SHORT Reglo.00412FA7 │
└──────────────────────────────────────────────┘
這是一段循環,中間有個Call,其過程是逐字讀入註冊碼的每一位,然後用Call 進行測試,看來是關鍵測試了,F7 步入,OD 轉到這裡,
┌──────────────────────────────────────────────┐
│00415ACF PUSH EBP │
│00415AD0 MOV EBP,ESP │
│00415AD2 PUSH ECX │
│00415AD3 PUSH EBX │
│00415AD4 MOV EBX,DWORD PTR SS:[EBP+8] ; 地址移動8位│
│00415AD7 CMP EBX,0FF ; 判定是否是ASCII 字符│
│00415ADD /JBE SHORT Reglo.00415B49 ; 小於等於255 時,這裡一定跳,否則必死│
└──────────────────────────────────────────────┘
這段的關鍵是上面的最後兩句,0FF 是255,EBX 中是當前位置註冊碼的ASCII 值(別忘了這段是在循環中),所以這兩句是測試,如果當前位置註冊碼的ASCII 值<= 255 時,發生跳轉,由255 可知,要求輸入字符是"基本拉丁字符集" 和"增補拉丁字符集1" 中的字符,由於當前輸入的測試註冊碼是"1111111122222222",故滿足要求,不過你可以試試輸入註冊碼"!111111122222222",這時Reglo 會彈出對話框"Please enter no more than 8 characters." 註冊就失敗了.然後跳轉到00415B49 處,注意,中間有段代碼00415ADF-00415B47 是處理當字符的ASCII>255 時,進行的操作,我沒跟過去,如果誰有興趣不妨看看Reglo 在這段干了什麼.跳轉到了這裡,
┌──────────────────────────────────────────────┐
│00415B49 \PUSH EBX │
│00415B4A CALL Reglo.00419636 ; 判斷是否是數字:否,則跳出. │
│00415B4F POP ECX │
│00415B50 POP EBX │
│00415B51 LEAVE │
│00415B52 RETN │
└──────────────────────────────────────────────┘
又是一個 Call! 煩哪,還得跟進去,來到,
┌──────────────────────────────────────────────┐
│00419636 /$ CALL Reglo.00419174 ; 別管,給EAX 一個地址│
│0041963B |. MOV EAX,DWORD PTR DS:[EAX+64] │
│0041963E |. CMP EAX,DWORD PTR DS:[43FF84] ; Reglo.0043FF30 │
│00419644 |. JE SHORT Reglo.0041964B ; 必跳│
│00419646 |. CALL Reglo.0041911B │
│0041964B |> CMP DWORD PTR DS:[EAX+28],1 │
│0041964F |. JLE SHORT Reglo.00419661 ; 必跳│
│00419651 |. PUSH 4 │
│00419653 |. PUSH DWORD PTR SS:[ESP+8] │
│00419657 |. PUSH EAX │
│00419658 |. CALL Reglo.004198A3 │
│0041965D |. ADD ESP,0C │
│00419660 |. RETN │
│00419661 |> MOV EAX,DWORD PTR DS:[EAX+48] ; UNICODE " ((((( │
│00419664 |. MOV ECX,DWORD PTR SS:[ESP+4] │
│00419668 |. MOVZX EAX,BYTE PTR DS:[EAX+ECX*2] ; 若ECX 為0-9 的數字,則EAX=84 │
│0041966C |. AND EAX,4 ; EAX=84 And 4=4 │
│0041966F \. RETN │
└──────────────────────────────────────────────┘
怎麼又是一堆Call!而且第一句就是一個Call,F7 進入,發現一堆系統調用,包括GetLastError/TlsGetValue/TlsSetValue/GetCurrentThreadID/SetLastError 等等,它們是乾什麼的呢?為什麼這麼做?到現在也沒搞清楚,不過至少知道這個Call 改變了EAX,也就是給EAX 了一個新地址,在這個Call 後的兩句,可以看到它是在進行地址比較,不管它了,來到00419644 處,這裡一定要跳,之後來到0041964B,與1 進行比較,之後到了0041964F 處跳轉至00419661,關鍵來了,這句給了EAX 一個UniCode 字符串,這是乾啥用的?先不急,看看它的下一句,在OD 的信息窗口看到了SS:[ESP+4] 中放置的其實就是當前位置註冊碼的ASCII 值,並賦給了ECX,ok,再下一句看到了DS:[EAX+ECX *2],這又是乾什麼呢,分析看看:當前EAX 是一個指向UniCode 的地址,ECX 是ASCII 值,所以EAX+ECX*2 必是一個地址,運行到該句OD 顯示"DS:[00432AC6 ]=84",在其上點擊右鍵,選擇"數據窗口中跟隨地址",發現數據窗口中是這樣一串數據"84 00 84 00 84 00 84 00..."---共10 組"84 00",於是終於明白了這段到底是在幹什麼,也就是說從該段的第一個Call 給了EAX 一個地址,通過這個地址加上一定的偏移量,指向一些特定地址,例如00419661 處, EAX=DS:[EAX+48],如果當前位置的字符是0-9 (ASCII 的30-39),那麼從DS:[EAX+48] 開始,偏移30*2 至39*2,都將得到84 這個值,也就是上面的10 組"84 00",那麼為什麼是84 呢,從0041966C 處,可看出84 與4 進行"與" 操作,EAX 將等於4,不等於0,換句話說EAX 就是一個flag,當EAX 非零時,說明匹配成功,當前字符是0-9 的數字,否則EAX=0 說明匹配失敗.其實在UltraEdit 中也可找到這兩個字符串,其中,
UNICODE " ((((( H" 在實偏移31C62H 處
"84 00 84 00 84 00 84 00 84 00 84 00 84 00..." 在實偏移31CC2H 處
誰有興趣的話,不妨算算它們之間的偏移是否是0x30*2 - 0x39*2
至此,萬里長征第二步算完成了,可是只是知道註冊碼由數字組成,卻不知道形成規則是什麼,怎麼辦,只好一路返回,回到00412FB3 處,此處仍在循環中,在00412FC2 處按F4,運行到該行,
┌──────────────────────────────────────────────┐
│00412FC2 MOVSX EDI,BYTE PTR DS:[EBX+F] ; EDI=末字符│
│00412FC6 SUB EDI,30 │
│00412FC9 MOV EAX,EDI ; EAX=末字符的數值│
│00412FCB IMUL EAX,EDI ; EAX=末位數的平方│
│00412FCE CDQ ; CDQ 雙字擴展成四字│
│00412FCF MOV ECX,0A │
│00412FD4 IDIV ECX ; 末位數的平方(EAX)除10,商放EAX,余放EDX │
│00412FD6 MOV AL,BYTE PTR DS:[EDI+EBX] ; AL=第EDI+1 個字符│
│00412FD9 ADD DL,30 ; 加30 │
│00412FDC CMP AL,DL ; │
│00412FDE JNZ Reglo.004130C7 ; 一跳就完│
└──────────────────────────────────────────────┘
這段代碼的關鍵是:
AL 是註冊碼的第EDI+1(EDI=末字符的數值) 個字符,而DL 是註冊碼的最後一個字符的平方,除以10,所得的餘數,由CMP Al,DL 及其下一行的JNZ Reglo.004130C7 可知,註冊碼的最後一位的數值i,決定著註冊碼第i+1 位的值必須為最後一位的數值的平方除10 所得的餘數,即:
最後一位數i 第i+1 位數模型
i=0 第1(i+1) 位為0(0^2 Mod 10) 0xxxxxxx xxxxxxx0
i=1 第2(i+1) 位為1(1^2 Mod 10) x1xxxxxx xxxxxxx1
i=2 第3(i+1) 位為4(2^2 Mod 10) xx4xxxxx xxxxxxx2
i=3 第4(i+1) 位為9(3^2 Mod 10) xxx9xxxx xxxxxxx3
i=4 第5(i+1) 位為6(4^2 Mod 10) xxxx6xxx xxxxxxx4
i=5 第6(i+1) 位為5(5^2 Mod 10) xxxxx5xx xxxxxxx5
i=6 第7(i+1) 位為6(6^2 Mod 10) xxxxxx6x xxxxxxx6
i=7 第8(i+1) 位為9(7^2 Mod 10) xxxxxxx9 xxxxxxx7
i=8 第9(i+1) 位為4(8^2 Mod 10) xxxxxxxx 4xxxxxx8
i=9 第10(i+1) 位為1(9^2 Mod 10) xxxxxxxx x1xxxxx9
由於輸入的測試註冊碼"1111111122222222" 不滿足這個要求,在00412FDE 處,就跳走了.不妨把測試註冊碼改為"1141111122222222",Ctrl+F2 重新運行,終於第一個坎00412FDE 過了,然後代碼來到,
┌──────────────────────────────────────────────┐
│00412FE4 |.LEA EAX,DWORD PTR DS:[EDI+1] ; EAX=末字符數值+1 │
│00412FE7 |.CMP EAX,0F │
│00412FEA |.JL SHORT Reglo.00412FEF ; 必跳│
│00412FEC |.SUB EAX,0F │
│00412FEF |>MOVSX ECX,BYTE PTR DS:[EAX+EBX] ; ECX=第末字符數值+2 個數字│
│00412FF3 |.LEA ESI,DWORD PTR DS:[ECX-30] ; ESI=第末字符數值+2 個數字的數值│
│00412FF6 |.CMP ESI,1 ; 第末字符數值+2 位數字必須大於等於1│
│00412FF9 |.JGE SHORT Reglo.00413000 ; 必跳│
└──────────────────────────────────────────────┘
測試註冊碼"1141111122222222" 的末位為"2",故第4(2+2) 個數字必須大於等於1,測試註冊碼已滿足要求,呵呵,繼續啊,第二個坎00412FF9 過了.繼續來到這裡,
┌──────────────────────────────────────────────┐
│00413000 |>XOR ECX,ECX │
│00413002 |.CMP ESI,1 │
│00413005 |.SETG CL ; 根據標誌寄存器,大於1 時,置CL=1 │
│00413008 |>MOV EDX,DWORD PTR SS:[ESP+14] │
│0041300C |.INC EAX ; EAX=末字符數值+2 │
│0041300D |.CMP EAX,0F │
│00413010 |.MOV DWORD PTR DS:[EDX],ECX ; DS:[0012F3C0]=00000000 │
│00413012 |.JL SHORT Reglo.00413017 │
│00413014 |.SUB EAX,0F │
│00413017 |>MOV ECX,EAX ; ECX=末字符數值+2 │
│00413019 |.ADD EAX,3 ; EAX=末字符數值+5 │
│0041301C |.CMP EAX,0F │
│0041301F |.JL SHORT Reglo.00413024 │
│00413021 |.SUB EAX,0F │
│00413024 |>MOVSX ESI,BYTE PTR DS:[ECX+EBX] ; ESI=第末字符數值+3 個數的ASCII 值│
│00413028 |.MOV ECX,EAX ; ECX=末字符數值+5 │
│0041302A |.ADD EAX,3 ; EAX=末字符數值+8 │
│0041302D |.SUB ESI,30 ; ESI=第末字符數值+3 個數的數值│
│00413030 |.CMP EAX,0F │
│00413033 |.JL SHORT Reglo.00413038 │
│00413035 |.SUB EAX,0F │
│00413038 |>MOVSX ECX,BYTE PTR DS:[ECX+EBX] ; ECX=第末字符數值+6 個數的ASCII 值│
│0041303C |.SUB ECX,30 ; ECX=第末字符數值+6 個數的數值│
│0041303F |.MOV EDX,EAX │
│;EDX=末字符數值+8(末字符數值+8<15 時)或末字符數值+8-15(末字符數值+8>=15 時) │
│00413041 |.IMUL ECX,ECX,64 ; ECX=(第末字符數值+6 個數的數值)*64 │
│00413044 |.ADD EAX,3 ; EAX=末字符數值+11 │
│00413047 |.CMP EAX,0F │
│0041304A |.JL SHORT Reglo.0041304F │
│0041304C |.SUB EAX,0F │
│0041304F |>MOVSX EDX,BYTE PTR DS:[EDX+EBX] │
│;EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的ASCII值│
│00413053 |.MOVSX EAX,BYTE PTR DS:[EAX+EBX] │
│;EAX=第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的ASCII值│
│00413057 |.SUB EDX,30 │
│;@EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值│
│0041305A |.ADD ECX,EAX ; ECX=ECX+EAX │
│0041305C |.LEA EDX,DWORD PTR DS:[EDX+EDX*4] ; EDX=@EDX*5 │
│0041305F |.LEA ECX,DWORD PTR DS:[ECX+EDX*2-30]; ECX=ECX+@EDX*A-30 │
│00413063 |.CMP ECX,7 ; ECX=ECX+@EDX*A-30=7(@EDX 是0-9 的數字) │
│00413066 |.JNZ SHORT Reglo.004130C7 ; 這裡一跳就完蛋了│
└──────────────────────────────────────────────┘
這段代碼比較複雜,註釋寫的老長,目的是方便修改測試註冊碼,否則,註冊無法繼續下去.其關鍵是00413063 處,要求ECX+@EDX*A-30=7,這裡:
ECX=(第末字符數值+6 個數的數值)*64+第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15 )}個數字的ASCII 值.
@EDX 是 0-9 中的某個數字.
對當前的測試註冊碼"1141111122222222" 而言,第8(2+6) 個數是"1",1*64=64;第14(2+12) 個數是"2",其ASCII=32 ,所以ECX=1*64+32=96.
同時@EDX=第11(2+9) 個數是"2",故ECX+@EDX*A-30=96+2*A-30=7A 不等於7,故無法繼續,如何修改測試註冊碼呢,觀察ECX,@EDX 可知,ECX>=30,@EDX>=0,故若ECX+@EDX*A-30=7,則ECX+@EDX*A=37,就必有@EDX*A<=7 ,所以@EDX=0,ECX=37,換句話說,
1.@EDX=0,意味著:
第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值必為0.
2.ECX=37,意味著:
第 末字符數值+6 個數的數值必為 0,
第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的數值必為7.
由此可以看出,末位數對整個註冊碼具有標誌性作用,我們不妨試著修改一下,對於"1141111122222222" 而言,末位數為"2",因為2+8<15,故第11 (2+9) 位數為0.又從"第末字符數值+6 個數的數值必為0" 可知,第8 位數為0,因為2+11<15,故第14(2+12 ) 位數必為7.測試註冊碼可改為"1141111022022722"
整理一下當末位數為0-9 時的註冊模型,如下:
最後一位數 i 模型
i=0 0xxxx0xx 0xx7xxx0
i=1 x1xxxx0x x0xx7xx1
i=2 xx4xxxx0 xx0xx7x2
i=3 xxx9xxxx 0xx0xx73
i=4 7xxx6xxx x0xx0xx4
i=5 x7xxx5xx xx0xx0x5
i=6 xx7xxx6x xxx0xx06
i=7 0xx7xxx9 xxxx0xx7
i=8 x0xx7xxx 4xxxx0x8
i=9 xx0xx7xx x1xxxx09
繼續吧,還有不少工作要做,越過第三個坎00413066,我們來到這裡,
┌──────────────────────────────────────────────┐
│00413068 |.XOR EAX,EAX │
│0041306A |.MOV ECX,10 │
│0041306F |.NOP │
│00413070 |>/MOVSX EDX,BYTE PTR DS:[ECX+EBX-1];EDX=第ECX 個數字│
│00413075 |.|DEC ECX │
│00413076 |.|IMUL EDX,ECX │
│00413079 |.|ADD EAX,EDX ;@EAX=Sum{(數字的ACSII 值)*(其所在位置-1)} │
│0041307B |.|TEST ECX,ECX │
│0041307D |.\JNZ SHORT Reglo.00413070 │
│0041307F |.LEA EDX,DWORD PTR DS:[EDI+E] ;EDI=末位數的數值│
│00413082 |.CMP EDX,0F ;EDX=14(末位數為0 時) │
│00413085 |.JL SHORT Reglo.0041308A ;必跳│
│00413087 |.SUB EDX,0F │
│0041308A |>MOVSX ECX,BYTE PTR DS:[EDX+EBX] ;EDX=末位數的數值+14-15(末位數不為0 時) │
│;@ECX=第15 個數的ASCII(當末位數為0 時)或第末位數數值位數的ASCII 值(當末位數不為0 時) │
│0041308E |.IMUL ECX,EDX ;ECX=@ECX*EDX │
│00413091 |.SUB EAX,ECX ;EAX=@EAX-ECX │
│00413093 |.DEC EDX │
│00413094 |.JNS SHORT Reglo.00413099 ;結果為正就跳(SF=0) │
│00413096 |.ADD EDX,0F │
│00413099 |>MOV CL,BYTE PTR DS:[EDX+EBX] │
│;@CL=第14 個數的ASCII(當末位數為0 時)或第末位數數值-1 位數的ASCII 值(當末位數不為0 時)│
│0041309C |.MOVSX EDI,CL ; │
│0041309F |.IMUL EDI,EDX ; │
│004130A2 |.SUB EAX,EDI ;EAX=@EAX-@ECX*EDX-@CL*EDI │
│004130A4 |.CDQ │
│004130A5 |.MOV EDI,0A ;EDI=除數10 │
│004130AA |.IDIV EDI │
│004130AC |.ADD DL,30 ;DL=餘數的ASCII 值│
│004130AF |.CMP DL,CL ;CL=第14 個數或第末位數數值-1 位數的ASCII │
│004130B1 |.JNZ SHORT Reglo.004130C7 ;一跳就死│
└──────────────────────────────────────────────┘
一進來,就看見一個循環,不過這個循環倒也簡單,就是從尾到頭,將每個數字的ASCII 值與其(所在位置-1)相乘,並累加起來送給EAX,也就是說:@EAX =n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15,循環完畢後,來到了0041307F,覺得有點奇怪,因為在這段代碼附近,沒看到EDI 是何時賦值的,仔細找了好一段代碼才發現,上次EDI 是在00412FC6 處賦值的,其值是末位數的數值,對當前測試註冊碼"1141111022022722" 而言,它就是"2".這段代碼的關鍵是004130AF,它事實上是要求:
當末位數為 0 時,
設置被除數 EAX=@EAX-n15*14-n14*13
設置第 14 位數的數值為 EAX/10 的餘數
當末位數為 1 時,
設置被除數 EAX=@EAX-n1*0-n15*14=@EAX-n15*14
設置第 15 位數的數值為 EAX/10 的餘數
當末位數為 2 時,
設置被除數 EAX=@EAX-n2*1-n1*0=@EAX-n2*1
設置第 1 位數的數值為 EAX/10 的餘數
當末位數不為 0,1,2 時,
設置被除數 EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
設置第i-1 位數的數值為EAX/10 的餘數,(其中,i=末位數數值)
先在的問題是如何修改測試註冊碼,算出EAX,再求餘,顯然不是一個好辦法,解決方法是在4130AF 處下一個斷點,看OD 信息窗口中的DL,是什麼數字,再退出Reglo ,修改指定位置的數字為DL 中的值.例如,當前測試註冊碼,末位數是"2",故需要修改第1 位數為DL 中的值7,修改後,測試註冊碼變為" 7141111022022722",試試,呵呵,又過一關吶!如此以來,第四個坎也過了.之後,來到這裡,
┌──────────────────────────────────────────────┐
│004130B3 PUSH ESI │
│004130B4 PUSH 10 │
┌──────────────────────────────────────────────┐
│004130B6 CALL Reglo.00412ED0 (5)│
└──────────────────────────────────────────────┘
│004130BB ADD ESP,8 │
│004130BE TEST EAX,EAX │
│004130C0 MOV EAX,1 │
│004130C5 JNZ SHORT Reglo.004130C9 │
│004130C7 MOV EAX,EBP │
│004130C9 POP EDI │
│004130CA POP ESI │
│004130CB POP EBP │
│004130CC POP EBX │
│004130CD RETN │
└──────────────────────────────────────────────┘
這裡有個Call,應該是調用註冊成功的對話框吧,哈哈,迫不及待了,按F9 直接運行!什麼也沒彈出來,但仔細一看原來關於對話框中的兩行字符"THIS SOFTWARE IS NOT REGISTERED " 以及"Expiration date:08/01/11" 不見了,呵呵,真的註冊成功了!測試註冊碼"7141111022022722" 真的是註冊碼!
高興歸高興,可是別得意忘形,凡事都要留個心眼.其實在寫這篇破文之前,我用的測試註冊碼不是"1111111122222222" 而是"0100000000070200",這個註冊碼和"7141111022022722" 一樣可以通過以上所有的坎,可是當你用它來註冊的時候,會讓Reglo 爆掉!不信你試試!為什麼呢?---其實原因很簡單,還有暗樁!在那裡?當然不能放過上面的那個Call (5) 了,F7 步入,到了這裡,
┌──────────────────────────────────────────────┐
│00412ED0 /$SUB ESP,8 ; 指向註冊碼│
│00412ED3 |.TEST EBX,EBX │
│00412ED5 |.JNZ SHORT Reglo.00412EDD ; 這裡必跳│
│00412ED7 |.XOR EAX,EAX │
│00412ED9 |.ADD ESP,8 │
│00412EDC |.RETN │
│00412EDD |>CMP DWORD PTR SS:[ESP+C],-1 ; -1 是在堆棧中設定的某種標誌│
│00412EE2 |.JNZ SHORT Reglo.00412EFD ; 必跳│
│00412EE4 |.MOV EAX,EBX │
│00412EE6 |.LEA EDX,DWORD PTR DS:[EAX+1] │
│00412EE9 |.LEA ESP,DWORD PTR SS:[ESP] │
│00412EF0 |>/MOV CL,BYTE PTR DS:[EAX] │
│00412EF2 |.|INC EAX │
│00412EF3 |.|TEST CL,CL │
│00412EF5 |.\JNZ SHORT Reglo.00412EF0 │
│00412EF7 |.SUB EAX,EDX │
│00412EF9 |.MOV DWORD PTR SS:[ESP+C],EAX │
│00412EFD |>MOV EAX,DWORD PTR SS:[ESP+C] │
│00412F01 |.PUSH EBP │
│00412F02 |.PUSH ESI │
│00412F03 |.PUSH EDI │
│00412F04 |.XOR EBP,EBP │
│00412F06 |.XOR ESI,ESI │
│00412F08 |.XOR EDI,EDI │
│00412F0A |.TEST EAX,EAX │
│00412F0C |.MOV DWORD PTR SS:[ESP+C],2 ; 12F38C 設置標誌2 │
│00412F14 |.MOV DWORD PTR SS:[ESP+10],1 ; 12F390 設置標誌1 │
│00412F1C |.JLE SHORT Reglo.00412F76 ; 一跳就死│
│00412F1E |.MOV EDI,EDI ; EDI=0 │
│00412F20 |>/MOVSX EAX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
│00412F24 |.|PUSH EAX │
│00412F25 |.|CALL Reglo.00415ACF ; 判斷是否為0-9 的數字│
│00412F2A |.|ADD ESP,4 │
│00412F2D |.|TEST EAX,EAX ; 是,則EAX=4,否,則EAX=0 │
│00412F2F |.|JE SHORT Reglo.00412F52 ; 若不是數字,就跳過本次循環│
│00412F31 |.|MOVSX ECX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
│00412F35 |.|MOV EAX,DWORD PTR SS:[ESP+ESI*4+C] ; EAX=2(ESI=0 時)或EAX=1(ESI=1 時) │
│00412F39 |.|SUB ECX,30 ; ECX=註冊碼第EDI+1 位的數值│
│00412F3C |.|IMUL EAX,ECX │
│; 奇數位用2 乘以當前位置數字的數值,偶數位用1 乘以當前位置數字的數值│
│00412F3F |.|CMP EAX,0A │
│00412F42 |.|JL SHORT Reglo.00412F47 │
│00412F44 |.|SUB EAX,9 ; 如果EAX>=10,則EAX=EAX-9 │
│00412F47 |>|XOR EDX,EDX ; EBP=Sum(EAX) │
│00412F49 |.|ADD EBP,EAX │
│00412F4B |.|TEST ESI,ESI │
│00412F4D |.|SETE DL ; 若ZF=1,則DL 置1 │
│00412F50 |.|MOV ESI,EDX ; ESI=0(奇數位) 或1(偶數位) │
│00412F52 |>|MOV EAX,DWORD PTR SS:[ESP+18] ; EAX=10h=16d │
│00412F56 |.|INC EDI │
│00412F57 |.|CMP EDI,EAX │
│00412F59 |.\JL SHORT Reglo.00412F20 │
│00412F5B |.TEST EBP,EBP │
│00412F5D |.JE SHORT Reglo.00412F76 ; 一跳就死│
│00412F5F |.MOV EAX,EBP ; 設置被除數│
│00412F61 |.CDQ │
│00412F62 |.IDIV DWORD PTR SS:[ESP+1C] │
│;第末字符數值+3 個數的數值作為除數放在堆棧SS:[12F39C] 中│
│00412F66 |.TEST EDX,EDX ; 測試餘數是否為0 │
│00412F68 |.JNZ SHORT Reglo.00412F76 ; 一跳就死│
│00412F6A |.POP EDI │
│00412F6B |.POP ESI │
│00412F6C |.MOV EAX,1 │
│00412F71 |.POP EBP │
│00412F72 |.ADD ESP,8 │
│00412F75 |.RETN │
│00412F76 |>POP EDI │
│00412F77 |.POP ESI │
│00412F78 |.XOR EAX,EAX │
│00412F7A |.POP EBP │
│00412F7B |.ADD ESP,8 │
│00412F7E \.RETN │
└──────────────────────────────────────────────┘
這段代碼比較簡單,其主要過程是,在堆棧中設置兩個標誌2 和1,然後逐字讀入註冊碼,奇數位數字*2,偶數位數字*1,當奇數位數字*2 的值大於等於10 時,對其進行減9 操作,將各位結果累加到EBP 中,作為被除數.同時取出SS:[12F39C] 中的數字作為除數,只要整除成功,就可通過測試.但值得注意的是00412F62 處,關於除數的選擇,由於該處除數位於堆棧SS:[12F39C] 中,改變該堆棧值的操作是在004130B3 處的PUSH ESI 操作,而上一次改變ESI 是在0041302D 處,由此可知所謂的除數其實是:第末字符數值+3 個數的數值.所以測試註冊碼"7141111022022722",恰巧其第5(2+3) 個數是"1",所以可以整除,而最終註冊成功.但對於測試註冊碼"0100000000070200" 來說,其第3(0+3) 個數是"0",所以Reglo 發生了除0 操作,而崩潰了.
至此,第五個坎也過了,軟件的註冊碼算法已基本清晰.可以寫總結了,但作為一個軟件從業人員,希望提醒大家,Reglo 竟然可以通過非法註冊碼導致軟件崩潰,說明其作者在驗證註冊碼的算法中存在Bug,也就是除零操作,這是一個軟件開發人員應盡量避免的低級錯誤,希望大家在自己的軟件中不要犯同樣的毛病.
【破解總結】:
———————————————————————————————————————————
從Reglo 的註冊算法中,可以明確的感受到,"末位數"的軸心作用,幾乎每一步都涉及到末位數的操作,要么用作指定某位為操作位,要么用作固定某位的數值,要么用作判定某條件是否達成,這可能是Reglo 註冊算法的最大特色吧.
對於Reglo v3.5a 的註冊碼的形制從現在的觀點來看,其模型可描述為:
k 註冊碼模型
k=0 0!#xx0xx 0xx7x?x0
k=1 x1!#xx0x x0xx7x?1
k=2 ?x4!#xx0 xx0xx7x2
k=3 x?x9!#xx 0xx0xx73
k=4 7x?x6!#x x0xx0xx4
k=5 x7x?x5!# xx0xx0x5
k=6 xx7x?x6! #xx0xx06
k=7 0xx7x?x9 !#xx0xx7
k=8 x0xx7x?x 4!#xx0x8
k=9 xx0xx7x? x1!#xx09
說明:
k 表示末位數數值,
x 表示 0-9 的數字,任選
! 位的數值必須大於等於 1
? 位的數值必須等於EAX 除10 所得的餘數,其中
@EAX=n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15 (ni 表示註冊碼第i 位的數值)
當末位數為 0 時,EAX=@EAX-n15*14-n14*13
當末位數為 1 時,EAX=@EAX-n15*14
當末位數為 2 時,EAX=@EAX-n2*1
當末位數為3-9 時,EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
# 位的數值不能為0,且能被SUM 整除,其中:
SUM=2*(n1+n3+n5+n7+n9+n11+n13+n15)-9*y+(n2+n4+n6+n8+n10+n12+n14+n16)
(y 表示:大於等於 5 的奇數位的個數)
那麼如何快速的得到一個註冊碼呢?
根據上面的模型,首先選擇一個末位數,不妨選"9" 吧,其註冊碼模型為"xx0xx7x? x1!#xx09",對於x 部分可讓它們為"0",這樣註冊碼變為" 0000070? 01!#0009",對於# 位的數字來說,當# = "1" 時,一定可以被整除,故令#="1",對於! 位的數字來說,! 位恰好是第11 位,該位數字乘以10(11-1) 一定被10 整除,故對? 位沒有影響,所以可以令! 位的數字為1-9 中的任意一個,這樣註冊碼變為"0000070? 01510009",就剩最後一位了,0 這麼多,好辦,7*(6-1)+1*(10-1)+5*(11-1)+1*(12-1)+9 *(16-1)=35+9+50+11+135=240,240 Mod 10 = 0,故? 位為"0",這樣註冊碼就為"00000700 01510009".其實上面的# 位可以不參加運算,原因已經說過,這裡寫出來只是為了大家看著方便.
當然使用註冊機KeyGen 是最方便的,不過我不想寫keygen,有興趣的朋友可以試試---我本來的目的也不是破解.又抽空看了看Reglo 的姊妹軟件Deletor,呵呵,註冊算法很相近呀,不過把註冊失敗的對話框彈出方式變為了MessageBoxW,註冊碼也變為了UniCode,新手們可以用它的姊妹軟件進行練手,是成長的不錯選擇.
哎,終於完了!神馬都是浮雲!
全站熱搜