FolderView 1.7 註冊演算法分析 (14千字)

看雪資料發表於2015-11-15

FolderView 1.7 註冊演算法分析

破解目標:FolderView 1.7
官方主頁:http://www.southbaypc.com/
軟體簡介:這是一個簡單易用的小工具,可以將資料夾當中的每個檔案,依照大小(byte)、日期、名稱作詳細列表,並匯出成TXT純文字檔案或者將檔案資料打列出來。
下載地址:http://hn-down.skycn.net/down/fvsetup.exe

使用工具:W32Dasm、Ollydbg、Windows 自帶的計算器

作者:炎之川
時間:2003.3.3
主頁:http://skipli.yeah.net/

宣告: 此文僅用於學習及交流,若要轉載請保持文章完整。


我的第三篇演算法分析,前兩篇都是crackme,而這篇文章的分析目標是共享軟體,不過演算法不算難。分析沒有花太多時間,而寫文章卻花費了很長的時間...


用 PEiD 0.8 檢查可知檔案未加殼。用 W32Dasm 反彙編並初步分析,先在串式參考中找到註冊碼錯誤的提示:“Sorry, you have entered an incorrect registration code.”,雙擊來到下面的程式碼段:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407424(C)  //呼叫之處,右鍵雙擊之...
|
:00407469 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"FolderView"
                                  |
:0040746B 6874354100              push 00413574

* Possible StringData Ref from Data Obj ->"Sorry, you have entered an incorrect "
                                        ->"registration code."
                                  |
:00407470 68083A4100              push 00413A08
:00407475 56                      push esi

來到這裡:

* Reference To: USER32.GetDlgItemTextA, Ord:0113h
                                  |
:004073EC 8B3D1C124100            mov edi, dword ptr [0041121C]
:004073F2 68EF030000              push 000003EF
:004073F7 56                      push esi
:004073F8 FFD7                    call edi
:004073FA 8D542408                lea edx, dword ptr [esp+08]
:004073FE 6800010000              push 00000100
:00407403 52                      push edx
:00407404 68ED030000              push 000003ED
:00407409 56                      push esi
:0040740A FFD7                    call edi
:0040740C 8D442408                lea eax, dword ptr [esp+08]
:00407410 8D8C2408010000          lea ecx, dword ptr [esp+00000108]
:00407417 50                      push eax
:00407418 51                      push ecx
:00407419 E882030000              call 004077A0  //我們在這裡下斷點
:0040741E 83C408                  add esp, 00000008
:00407421 85C0                    test eax, eax
:00407423 5F                      pop edi
:00407424 7443                    je 00407469  //光棒停在這裡
:00407426 8D542404                lea edx, dword ptr [esp+04]
:0040742A 8D842404010000          lea eax, dword ptr [esp+00000104]
:00407431 52                      push edx
:00407432 50                      push eax

可知程式使用 GetDlgItemTextA 來取輸入的字元,且我們可以在 407419 處下斷點。

Ollydbg 載入檔案,在 407419 處按 F2 下斷點,然後 ctrl+F2 重新開始,F9 執行程式,填入註冊名和假註冊碼,我填入:
Name: lovefire
S/N: 78787878

按 Check,被 Ollydbg 斷下,
(; 後是 Ollydbg 所分析的內容,// 後是我加的註釋)


004073FE  .  68 00010000  PUSH    100                              ; /Count = 100 (256.)
00407403  .  52            PUSH    EDX                              ; |Buffer
00407404  .  68 ED030000  PUSH    3ED                              ; |ControlID = 3ED (1005.)
00407409  .  56            PUSH    ESI                              ; |hWnd
0040740A  .  FFD7          CALL    EDI                              ; \GetDlgItemTextA
0040740C  .  8D4424 08    LEA    EAX, DWORD PTR SS:[ESP+8]  //取假碼到 EAX
00407410  .  8D8C24 080100>LEA    ECX, DWORD PTR SS:[ESP+108]  //取註冊名到 ECX
00407417  .  50            PUSH    EAX  //假碼入棧,保留到最後比較
00407418  .  51            PUSH    ECX  //註冊名入棧
00407419  .  E8 82030000  CALL    FolderVi.004077A0  //F7 跟進
0040741E  .  83C4 08      ADD    ESP, 8
00407421  .  85C0          TEST    EAX, EAX
00407423  .  5F            POP    EDI
00407424  .  74 43        JE      SHORT FolderVi.00407469

----------------------------------------------------------------
進入 407419 的call

004077A0  /$  8B4C24 04    MOV    ECX, DWORD PTR SS:[ESP+4]
004077A4  |.  81EC 00010000 SUB    ESP, 100
004077AA  |.  8D4424 00    LEA    EAX, DWORD PTR SS:[ESP]
004077AE  |.  50            PUSH    EAX
004077AF  |.  51            PUSH    ECX
004077B0  |.  E8 AB000000  CALL    FolderVi.00407860
004077B5  |.  8B8424 100100>MOV    EAX, DWORD PTR SS:[ESP+110]
004077BC  |.  8D5424 08    LEA    EDX, DWORD PTR SS:[ESP+8]
004077C0  |.  52            PUSH    EDX
004077C1  |.  50            PUSH    EAX
004077C2  |.  E8 89FFFFFF  CALL    FolderVi.00407750  //關鍵call,F7 跟進
004077C7  |.  F7D8          NEG    EAX
004077C9  |.  1BC0          SBB    EAX, EAX
004077CB  |.  F7D8          NEG    EAX
004077CD  |.  81C4 10010000 ADD    ESP, 110
004077D3  \.  C3            RETN


----------------------------------------------------------------
進入 4077C2 的call,演算法開始

00407860  /$  81EC 00010000 SUB    ESP, 100
00407866  |.  A0 7C684100  MOV    AL, BYTE PTR DS:[41687C]
0040786B  |.  53            PUSH    EBX
0040786C  |.  55            PUSH    EBP
0040786D  |.  56            PUSH    ESI
0040786E  |.  57            PUSH    EDI
0040786F  |.  884424 10    MOV    BYTE PTR SS:[ESP+10], AL
00407873  |.  B9 3F000000  MOV    ECX, 3F
00407878  |.  33C0          XOR    EAX, EAX
0040787A  |.  8D7C24 11    LEA    EDI, DWORD PTR SS:[ESP+11]
0040787E  |.  F3:AB        REP    STOS DWORD PTR ES:[EDI]
00407880  |.  66:AB        STOS    WORD PTR ES:[EDI]
00407882  |.  AA            STOS    BYTE PTR ES:[EDI]
00407883  |.  8BBC24 140100>MOV    EDI, DWORD PTR SS:[ESP+114]  //取輸入的註冊使用者名稱,放到 edi
0040788A  |.  57            PUSH    EDI                              ; /String  //edi 入棧
0040788B  |.  FF15 68114100 CALL    DWORD PTR DS:[<&KERNEL32.lstrlen>; \lstrlenA  //取輸入的註冊名長度,然後放到eax(可以透過經過此call後,eax值的變化看出)
00407891  |.  8BF0          MOV    ESI, EAX  //註冊名長度送到 esi
00407893  |.  33C9          XOR    ECX, ECX  //清零
00407895  |.  33C0          XOR    EAX, EAX  //清零,做計數器
00407897  |.  85F6          TEST    ESI, ESI  //比較esi是否為0,即是否已輸入註冊名
00407899  |.  76 13        JBE    SHORT FolderVi.004078AE  //如果沒有輸入,後果很明顯...;)

//下面開始的第一個迴圈
0040789B  |.  8B15 F8394100 MOV    EDX, DWORD PTR DS:[4139F8]  //給edx賦值,發現這裡固定是賦值32
004078A1  |>  0FBE1C38      /MOVSX  EBX, BYTE PTR DS:[EAX+EDI//逐位取註冊名的字元,第一次取“l”,即6C
004078A5  |.  03DA          |ADD    EBX, EDX  //ebx = ebx+edx = 6C+32 = 9E
004078A7  |.  03CB          |ADD    ECX, EBX  //ecx = ecx+ebx = 0+9E = 9E
004078A9  |.  40            |INC    EAX  //eax+1,計數器+1
004078AA  |.  3BC6          |CMP    EAX, ESI  //比較eax與esi,esi 中是整個註冊名長度值,所以這裡比較是否已取完註冊名
004078AC  |.^ 72 F3        \JB      SHORT FolderVi.004078A1  //沒有取完就繼續迴圈
//第一個迴圈結束

第一次迴圈完成後得到的ebx的值,放入第二次迴圈繼續計算,每次迴圈最終值放入ecx中。

這個部分的註冊演算法為:
設 ecx 的值從初始到所有迴圈完成,分別為N(0),N(1),N(2),N(3),N(4)...則:
ecx = ((註冊名字元的ASCII值)+32)+N(n-1)

以我填入的註冊名“lovefire”為例:
N(0) = 0 (初始的 ecx=0)
N(1) = 6C+32+0 = 9E  //第一次迴圈,所以N(1-1)=N(0)=0
N(2) = 6F+32+9E = 13F  //第二次迴圈,所以 N(2-1)=N(1)=9E
N(3) = 76+32+13F = 1E7  //以下類推
N(4) = 65+32+1E7 = 27E
N(5) = 66+32+27E = 316
N(6) = 69+32+316 = 3B1
N(7) = 72+32+3B1 = 455
N(8) = 65+32+455 = 4EC = 1260(十進位制)

接下來一段是對這個算出來的數做一些處理:

004078AE  |>  8B9C24 180100>MOV    EBX, DWORD PTR SS:[ESP+118]
004078B5  |.  51            PUSH    ECX                              ; /<%u>  //ecx 入棧
004078B6  |.  68 503A4100  PUSH    FolderVi.00413A50                ; |Format = "%u-"  //格式
004078BB  |.  53            PUSH    EBX                              ; |s
004078BC  |.  FF15 0C124100 CALL    DWORD PTR DS:[<&USER32.wsprintfA>; \wsprintfA  //給ecx的數值後加上“-”符號

到此處得到一個值“1260-”。


然後開始進行第二個迴圈,先是一些準備事項:
004078C2  |.  83C4 0C      ADD    ESP, 0C
004078C5  |.  33C9          XOR    ECX, ECX  //清零
004078C7  |.  33C0          XOR    EAX, EAX  //清零
004078C9  |.  85F6          TEST    ESI, ESI  //比較esi是否為0,即是否已輸入註冊名
004078CB  |.  76 14        JBE    SHORT FolderVi.004078E1  //如果沒有輸入,後果很明顯...;)
//下面開始第二個迴圈
004078CD  |.  8B15 FC394100 MOV    EDX, DWORD PTR DS:[4139FC]  //給 edx 賦值,這個值是固定的,為 28
004078D3  |>  0FBE2C38      /MOVSX  EBP, BYTE PTR DS:[EAX+EDI//逐位取註冊名的字元,第一次取“l”,即6C,放到 ebp
004078D7  |.  0FAFEA        |IMUL    EBP, EDX  //ebp = ebp*edx
004078DA  |.  03CD          |ADD    ECX, EBP  //ecx = ecx+ebp
004078DC  |.  40            |INC    EAX  //eax+1,計數器+1
004078DD  |.  3BC6          |CMP    EAX, ESI  //是否已取完註冊名?
004078DF  |.^ 72 F2        \JB      SHORT FolderVi.004078D3  //沒有取完就繼續迴圈
//迴圈完成

這個部分的註冊演算法為:
設 ecx 的值從初始到所有迴圈完成,分別為N(0),N(1),N(2),N(3),N(4)...則:
ecx=(註冊名字元的ASCII值)*28+N(n-1)

以我填入的註冊名“lovefire”為例:
N(0) = 0 (初始的 ecx=0)
N(1) = 6C*28+0 = 10E0  //第一次迴圈,所以N(1-1)=N(0)=0
N(2) = 6F*28+ 10E0 = 2238  //第二次迴圈,所以 N(2-1)=N(1)=10E0
N(3) = 76*28+ 2238 = 34A8  //以下類推
N(4) = 65*28+ 34A8 = 4470
N(5)= 66*28+ 4470 = 5460
N(6) = 69*28+ 5460 = 64C8
N(7) = 72*28+ 64C8 = 7698
N(8) = 65*28+ 7698 = 8660 = 34400(十進位制)

接下來繼續對得到的數進行一些處理:
004078E1  |>  51            PUSH    ECX                              ; /<%u> = 8660 (34400.) 
004078E2  |.  8D4C24 14    LEA    ECX, DWORD PTR SS:[ESP+14]      ; |
004078E6  |.  68 503A4100  PUSH    FolderVi.00413A50                ; |Format = "%u-"
004078EB  |.  51            PUSH    ECX                              ; |s
004078EC  |.  FF15 0C124100 CALL    DWORD PTR DS:[<&USER32.wsprintfA>; \wsprintfA
//將得到的 34400 加上“-”,得到註冊碼的第二部分 34400-,並放入ecx
004078F2  |.  83C4 0C      ADD    ESP, 0C
004078F5  |.  8D5424 10    LEA    EDX, DWORD PTR SS:[ESP+10]
004078F9  |.  52            PUSH    EDX                              ; /StringToAdd
004078FA  |.  53            PUSH    EBX                              ; |ConcatString
004078FB  |.  FF15 94114100 CALL    DWORD PTR DS:[<&KERNEL32.lstrcat>; \lstrcatA  //和第一次算出的碼合併為“1260-34400-”

至此得到一個新的字串“1260-34400-”。


第三次迴圈:

00407901  |.  33C9          XOR    ECX, ECX
00407903  |.  33C0          XOR    EAX, EAX
00407905  |.  85F6          TEST    ESI, ESI
00407907  |.  76 13        JBE    SHORT FolderVi.0040791C
//對迴圈3進行一些初始設定,與迴圈1、2類似,不再多做解釋
00407909  |.  8B15 003A4100 MOV    EDX, DWORD PTR DS:[413A00]  //給edx賦初始值 1E(固定值)
//下面開始的三次迴圈
0040790F  |>  0FBE2C38      /MOVSX  EBP, BYTE PTR DS:[EAX+EDI//逐位取註冊名的字元的ASCII值,放到 ebp
00407913  |.  03EA          |ADD    EBP, EDX  //ebp = ebp+edx
00407915  |.  03CD          |ADD    ECX, EBP  //ecx = ecx+ebp
00407917  |.  40            |INC    EAX  //計數器+1
00407918  |.  3BC6          |CMP    EAX, ESI  //比較是否已取完註冊名
0040791A  |.^ 72 F3        \JB      SHORT FolderVi.0040790F  //沒有則繼續迴圈
//迴圈結束

這個部分的註冊演算法為:
設 ecx 的值從初始到所有迴圈完成,分別為N(0),N(1),N(2),N(3),N(4)...則:
ecx=(註冊名字元的ASCII值)+1E+N(n-1)

可知,輸入的註冊名“lovefire”,經過迴圈計算後得到一個數“44C”,轉換為十進位制則為“1100”

接下來……
0040791C  |>  51            PUSH    ECX                              ; /<%u> = 44C (1100.)
0040791D  |.  8D4424 14    LEA    EAX, DWORD PTR SS:[ESP+14]      ; |
00407921  |.  68 503A4100  PUSH    FolderVi.00413A50                ; |Format = "%u-"
00407926  |.  50            PUSH    EAX                              ; |s
00407927  |.  FF15 0C124100 CALL    DWORD PTR DS:[<&USER32.wsprintfA>; \wsprintfA
0040792D  |.  83C4 0C      ADD    ESP, 0C
00407930  |.  8D4C24 10    LEA    ECX, DWORD PTR SS:[ESP+10]
00407934  |.  51            PUSH    ECX                              ; /StringToAdd
00407935  |.  53            PUSH    EBX                              ; |ConcatString
00407936  |.  FF15 94114100 CALL    DWORD PTR DS:[<&KERNEL32.lstrcat>; \lstrcatA

以上分別程式碼先給算出的“1100”加上“-”,得到“1100-”,在和前面的字串合併,得到“1260-34400-1100”


第四次迴圈:

0040793C  |.  33C9          XOR    ECX, ECX
0040793E  |.  33C0          XOR    EAX, EAX
00407940  |.  85F6          TEST    ESI, ESI
00407942  |.  76 14        JBE    SHORT FolderVi.00407958
00407944  |.  8B15 043A4100 MOV    EDX, DWORD PTR DS:[413A04]  //給edx賦初始值 0B(固定值)
//迴圈開始
0040794A  |>  0FBE2C38      /MOVSX  EBP, BYTE PTR DS:[EAX+EDI//逐位取註冊名的字元的ASCII值,放到 ebp
0040794E  |.  0FAFEA        |IMUL    EBP, EDX  //ebp = ebp*edx
00407951  |.  03CD          |ADD    ECX, EBP  //ecx = ecx+ebp
00407953  |.  40            |INC    EAX  //計數器+1
00407954  |.  3BC6          |CMP    EAX, ESI  //比較是否已取完註冊名?
00407956  |.^ 72 F2        \JB      SHORT FolderVi.0040794A  //沒有則繼續迴圈
//迴圈結束

這個部分的註冊演算法為:
設 ecx 的值從初始到所有迴圈完成,分別為N(0),N(1),N(2),N(3),N(4)...則:
ecx=(註冊名字元的ASCII值)*0B+N(n-1)

可知,輸入的註冊名“lovefire”,經過迴圈計算後得到一個數“24F4”,轉換為十進位制則為“9460”。

00407958  |>  51            PUSH    ECX                              ; /<%u>
00407959  |.  8D5424 14    LEA    EDX, DWORD PTR SS:[ESP+14]      ; |
0040795D  |.  68 6C384100  PUSH    FolderVi.0041386C                ; |Format = "%u"
00407962  |.  52            PUSH    EDX                              ; |s
00407963  |.  FF15 0C124100 CALL    DWORD PTR DS:[<&USER32.wsprintfA>; \wsprintfA
00407969  |.  83C4 0C      ADD    ESP, 0C
0040796C  |.  8D4424 10    LEA    EAX, DWORD PTR SS:[ESP+10]
00407970  |.  50            PUSH    EAX                              ; /StringToAdd
00407971  |.  53            PUSH    EBX                              ; |ConcatString
00407972  |.  FF15 94114100 CALL    DWORD PTR DS:[<&KERNEL32.lstrcat>; \lstrcatA

以上程式碼將前後算出的數值合併為“1260-34400-1100-9460”,這就是真正的註冊碼。


再繼續往下走,是對比假碼與真碼,先對比真假註冊碼長度,如果相等,則逐位比較,如果輸入的註冊名與計算出的註冊碼相同,則註冊成功,將註冊資訊寫入“HKEY_CURRENT_USER\Software\FolderView\Registration”,分別是"Name"和"Code"兩個子鍵。因為與演算法關係不大,且很容易看懂,所以比較註冊碼這部分的程式碼就不復製出來了;)

至此,FolderView 1.7 註冊演算法分析完成。

一組可用的註冊碼:Name: lovefire  S/N: 1260-34400-1100-9460


;by 炎之川 2003.3.3

相關文章