2011年8月6日星期六

Reglo v3.5a 註冊算法詳析

【軟件名稱】:
    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 A801​​4300 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,新手們可以用它的姊妹軟件進行練手,是成長的不錯選擇.

    哎,終於完了!神馬都是浮雲!

没有评论:

发表评论