Add/Remove 4Good v2.01 註冊演算法分析 (18千字)

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

Add/Remove 4Good v2.01 註冊演算法分析

破解目標:Add/Remove 4Good v2.01
官方主頁:http://www.4developers.com/
軟體簡介:是一個可以幫助你管理控制檯中的新增/移除程式,除了可代替反安裝軟體外還會連同軟體的清單移除,另外也可以清除以無效的軟體清單等。
下載地址:http://www.4developers.com/software/addremove.exe

使用工具:W32Dasm、Ollydbg、Windows 自帶的計算器、32bit Calculator 1.6 by cybult,用來臨時報佛腳的彙編課本-_-b

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

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



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

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040402F(C)
|
:00404040 E80BFDFFFF              call 00403D50  //在這裡設定斷點
:00404045 85C0                    test eax, eax
:00404047 0F84A8000000            je 004040F5
:0040404D 8D44240C                lea eax, dword ptr [esp+0C]
:00404051 8D4C2408                lea ecx, dword ptr [esp+08]
:00404055 33DB                    xor ebx, ebx
:00404057 50                      push eax
:00404058 51                      push ecx
:00404059 53                      push ebx
:0040405A 683F000F00              push 000F003F
:0040405F 53                      push ebx
:00404060 53                      push ebx
:00404061 53                      push ebx

* Possible Reference to Dialog: 
                                  |
:00404062 68E84B4300              push 00434BE8
:00404067 6802000080              push 80000002
:0040406C 895C2430                mov dword ptr [esp+30], ebx
:00404070 895C242C                mov dword ptr [esp+2C], ebx

* Reference To: ADVAPI32.RegCreateKeyExA, Ord:01CDh  //以下是註冊成功,將註冊資訊寫入登錄檔
                                  |
:00404074 FF1514904200            Call dword ptr [00429014]
:0040407A 3BC3                    cmp eax, ebx
:0040407C 7530                    jne 004040AE
:0040407E 8B542408                mov edx, dword ptr [esp+08]
:00404082 57                      push edi
:00404083 6800020000              push 00000200
:00404088 68F88F4300              push 00438FF8
:0040408D 6A03                    push 00000003
:0040408F 53                      push ebx

* Possible StringData Ref from Data Obj ->"4D"
                                  |
:00404090 68E44B4300              push 00434BE4
:00404095 52                      push edx

* Reference To: ADVAPI32.RegSetValueExA, Ord:01F9h
                                  |
:00404096 FF1510904200            Call dword ptr [00429010]
:0040409C 8BF8                    mov edi, eax
:0040409E 8B44240C                mov eax, dword ptr [esp+0C]
:004040A2 50                      push eax

* Reference To: ADVAPI32.RegCloseKey, Ord:01C9h
                                  |
:004040A3 FF1518904200            Call dword ptr [00429018]
:004040A9 3BFB                    cmp edi, ebx
:004040AB 5F                      pop edi
:004040AC 741B                    je 004040C9

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040407C(C)
|
:004040AE 53                      push ebx
:004040AF 53                      push ebx

* Possible StringData Ref from Data Obj ->"Failed to record registration "
                                        ->"information.

Please contact support@4dev.com"
                                  |
:004040B0 68D04E4300              push 00434ED0
:004040B5 E8F2F10100              call 004232AC
:004040BA 6A01                    push 00000001
:004040BC 8BCE                    mov ecx, esi
:004040BE E801940100              call 0041D4C4
:004040C3 5E                      pop esi
:004040C4 5B                      pop ebx
:004040C5 83C408                  add esp, 00000008
:004040C8 C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004040AC(C)
|
:004040C9 3BF3                    cmp esi, ebx
:004040CB 7504                    jne 004040D1
:004040CD 33C0                    xor eax, eax
:004040CF EB03                    jmp 004040D4

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004040CB(C)
|
:004040D1 8B461C                  mov eax, dword ptr [esi+1C]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004040CF(U)
|
:004040D4 53                      push ebx

* Possible Reference to Dialog: 
                                  |
:004040D5 68C04E4300              push 00434EC0

* Possible StringData Ref from Data Obj ->"Welcome to the REGISTERED version "  //註冊成功的提示
                                        ->"of Add/Remove 4Good.

Please restart "
                                        ->"Add/Remove 4Good so that all limitations "
                                        ->"are removed."
                                  |
:004040DA 68404E4300              push 00434E40
:004040DF 50                      push eax

* Reference To: USER32.MessageBoxA, Ord:01DEh
                                  |
:004040E0 FF158C944200            Call dword ptr [0042948C]
:004040E6 6A01                    push 00000001
:004040E8 8BCE                    mov ecx, esi
:004040EA E8D5930100              call 0041D4C4
:004040EF 5E                      pop esi
:004040F0 5B                      pop ebx
:004040F1 83C408                  add esp, 00000008
:004040F4 C3                      ret



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00404047(C)
|
:004040F5 6AFF                    push FFFFFFFF

* Reference To: USER32.MessageBeep, Ord:01DDh
                                  |
:004040F7 FF1594944200            Call dword ptr [00429494]
:004040FD 85F6                    test esi, esi
:004040FF 7403                    je 00404104
:00404101 8B761C                  mov esi, dword ptr [esi+1C]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004040FF(C)
|
:00404104 6A30                    push 00000030

* Possible StringData Ref from Data Obj ->"UNREGISTERED User"
                                  |
:00404106 682C4E4300              push 00434E2C

* Possible StringData Ref from Data Obj ->"The registration information you "  //註冊失敗的提示
                                        ->"have entered could not be validated.
Please "
                                        ->"re-enter the registration code "
                                        ->"and user name you have legally "
                                        ->"obtained from 4Developers.

If "
                                        ->"you have not registered yet you "
                                        ->"can click one of the buttons below "
                                        ->"to see how to register Add/Remove "
                                        ->"4Good.
For help e-mail support@4dev.com"
                                  |
:0040410B 68F44C4300              push 00434CF4
:00404110 56                      push esi

* Reference To: USER32.MessageBoxA, Ord:01DEh
                                  |
:00404111 FF158C944200            Call dword ptr [0042948C]
:00404117 5E                      pop esi
:00404118 5B                      pop ebx
:00404119 83C408                  add esp, 00000008
:0040411C C3                      ret



用 Ollydbg 載入檔案,在 404040 處按 F2 下斷點,然後 Ctrl+F2 重新開始,F9 執行程式,在 NAG 對話方塊中填入註冊名和假註冊碼,我填入:
Name: lovefire (需要大於6個字元)
S/N: 6582-78787878 (為什麼要這樣寫?原因見下面的分析)

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

00404040  > E8 0BFDFFFF    CALL AddRem.00403D50  //斷在這裡,關鍵call,F7進入
00404045  . 85C0          TEST EAX,EAX  //eax 是否為0?
00404047  . 0F84 A8000000  JE AddRem.004040F5  //為0則跳到下面註冊失敗的messagebox
0040404D  . 8D4424 0C      LEA EAX,DWORD PTR SS:[ESP+C]
00404051  . 8D4C24 08      LEA ECX,DWORD PTR SS:[ESP+8]
00404055  . 33DB          XOR EBX,EBX
00404057  . 50            PUSH EAX                                ; /pDisposition
00404058  . 51            PUSH ECX                                ; |pHandle
00404059  . 53            PUSH EBX                                ; |pSecurity => NULL
0040405A  . 68 3F000F00    PUSH 0F003F                              ; |Access = KEY_ALL_ACCESS
0040405F  . 53            PUSH EBX                                ; |Options => REG_OPTION_NON_VOLATILE
00404060  . 53            PUSH EBX                                ; |Class => NULL
00404061  . 53            PUSH EBX                                ; |Reserved => 0
00404062  . 68 E84B4300    PUSH AddRem.00434BE8                    ; |Subkey = "Software\4Developers\AddRemove"
00404067  . 68 02000080    PUSH 80000002                            ; |hKey = HKEY_LOCAL_MACHINE
0040406C  . 895C24 30      MOV DWORD PTR SS:[ESP+30],EBX            ; |
00404070  . 895C24 2C      MOV DWORD PTR SS:[ESP+2C],EBX            ; |
00404074  . FF15 14904200  CALL DWORD PTR DS:[<&ADVAPI32.RegCreateK>; \RegCreateKeyExA


----------------------------------------------------------------
進入 404040 的call:


00403D50  /$ 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
00403D56  |. 6A FF          PUSH -1
00403D58  |. 68 C87F4200    PUSH AddRem.00427FC8
00403D5D  |. 50            PUSH EAX
00403D5E  |. 64:8925 000000>MOV DWORD PTR FS:[0],ESP
00403D65  |. 83EC 10        SUB ESP,10
00403D68  |. 83C9 FF        OR ECX,FFFFFFFF  //ecx 或 FFFFFFFF,結果使 ecx = FFFFFFFF
00403D6B  |. 33C0          XOR EAX,EAX  //eax 清零
00403D6D  |. 53            PUSH EBX
00403D6E  |. 56            PUSH ESI
00403D6F  |. 57            PUSH EDI
00403D70  |. BF F88F4300    MOV EDI,AddRem.00438FF8                  ;  ASCII "lovefire"  //註冊使用者名稱放入 edi
00403D75  |. F2:AE          REPNE SCAS BYTE PTR ES:[EDI//掃描字串,處理其長度。這個指令最後使 ecx=FFFFFFF6(因為填入的註冊使用者名稱是8位)
00403D77  |. F7D1          NOT ECX  //ecx 邏輯非,得 00000009
00403D79  |. 49            DEC ECX  //ecx-1=00000009-1=00000008。這是使用者名稱的長度!
//以上操作到這裡終於得到了註冊使用者名稱的長度!用了這麼複雜的方法,有點暈~~~

00403D7A  |. 83F9 06        CMP ECX,6  //註冊名需要大於6位
00403D7D  |. 73 12          JNB SHORT AddRem.00403D91  //大於6則跳到下面,繼續計算註冊碼
00403D7F  |. 5F            POP EDI
00403D80  |. 5E            POP ESI
00403D81  |. 5B            POP EBX
00403D82  |. 8B4C24 10      MOV ECX,DWORD PTR SS:[ESP+10]
00403D86  |. 64:890D 000000>MOV DWORD PTR FS:[0],ECX
00403D8D  |. 83C4 1C        ADD ESP,1C
00403D90  |. C3            RETN
00403D91  |> 68 084C4300    PUSH AddRem.00434C08                    ;  ASCII "6582-"  //預設的數“6582-”
00403D96  |. 8D4C24 18      LEA ECX,DWORD PTR SS:[ESP+18]
00403D9A  |. E8 2ECB0100    CALL AddRem.004208CD    //註冊碼頭5位“6582-”
00403D9F  |. 8BF0          MOV ESI,EAX
00403DA1  |. BF F88F4300    MOV EDI,AddRem.00438FF8  //取註冊名到edi
00403DA6  |. 83C9 FF        OR ECX,FFFFFFFF  //ecx 或 FFFFFFFF,最後使ecx=FFFFFFFF
00403DA9  |. 33C0          XOR EAX,EAX  //清零
00403DAB  |. 33DB          XOR EBX,EBX  //清零
00403DAD  |. F2:AE          REPNE SCAS BYTE PTR ES:[EDI//取輸入的使用者名稱第一個字元
00403DAF  |. F7D1          NOT ECX
00403DB1  |. 49            DEC ECX
00403DB2  |. 33D2          XOR EDX,EDX  //清零
00403DB4  |. 8BC1          MOV EAX,ECX  //註冊名長度送 wax
00403DB6  |. B9 0C000000    MOV ECX,0C
00403DBB  |. F7F1          DIV ECX
00403DBD  |. A1 9C484300    MOV EAX,DWORD PTR DS:[43489C]  //將一個預設的數 87ae2401my69 送到 eax,下面將用到
00403DC2  |. 895C24 24      MOV DWORD PTR SS:[ESP+24],EBX
00403DC6  |. 8A0C02        MOV CL,BYTE PTR DS:[EDX+EAX//edx=8,這裡取上面的預設字串中第8個字元的下一位,即“m”
00403DC9  |. 8D5424 0C      LEA EDX,DWORD PTR SS:[ESP+C]
00403DCD  |. 51            PUSH ECX
00403DCE  |. 56            PUSH ESI
00403DCF  |. 52            PUSH EDX
00403DD0  |. E8 31710100    CALL AddRem.0041AF06  //將m連線到 6582- 之後,此時得到註冊碼第二部分的第一個字元“m”
//以上是一個片斷,透過上面的程式碼可以知道,軟體的註冊碼分為兩個部分,第一部分是固定的“6582-”,第二部分的第一個字元由註冊使用者名稱的位數和軟體預設的一串字元來決定,註冊使用者名稱幾位,則從預設字串中找出對應位數的字元。例如註冊使用者名稱七位,則取“1”,11位則取“9”,12位從頭取,取“8”

00403DD5  |. 8D4C24 14      LEA ECX,DWORD PTR SS:[ESP+14]
00403DD9  |. C64424 24 02  MOV BYTE PTR SS:[ESP+24],2
00403DDE  |. E8 7CCA0100    CALL AddRem.0042085F
00403DE3  |. BF F88F4300    MOV EDI,AddRem.00438FF8                  ;  ASCII "lovefire"
00403DE8  |. 83C9 FF        OR ECX,FFFFFFFF
00403DEB  |. 33C0          XOR EAX,EAX
00403DED  |. F2:AE          REPNE SCAS BYTE PTR ES:[EDI]
00403DEF  |. F7D1          NOT ECX
00403DF1  |. 49            DEC ECX
00403DF2  |. 0F84 8E000000  JE AddRem.00403E86

00403DF8  |> F6C3 01        /TEST BL,1  //test BL,1 即 BL and 1
00403DFB  |. 75 71          |JNZ SHORT AddRem.00403E6E  //如果等於0,則結束迴圈,跳到 403E6E
//因為 test BL,1 即 BL and 1,所以當取註冊名的雙數位時,BL and 1 的結果為0,直接跳過迴圈。
//即軟體 *只取註冊名的單數位字元* 參與計算

//下面是計算註冊碼的過程
00403DFD  |. 8A83 F88F4300  |MOV AL,BYTE PTR DS:[EBX+438FF8]  //第一位註冊名送 al
00403E03  |. 3C 7F          |CMP AL,7F  //al與 7F 比較
00403E05  |. 0F8F BF000000  |JG AddRem.00403ECA  //小於方可繼續
00403E0B  |. 3C 20          |CMP AL,20  //al與 20 比較
00403E0D  |. 0F8C B7000000  |JL AddRem.00403ECA  //大於方可繼續
//以上比較,是看註冊使用者名稱是否位於ASCII表中的非控制字元部分,即註冊使用者名稱只能是英文字母、數字和可列印字元。

00403E13  |. 0FBEC0        |MOVSX EAX,AL  //al送eax,此時al中是註冊名的第一位字元,以第一位註冊名字元“l”為例,此時 eax=6C
00403E16  |. 99            |CDQ  //這裡將edx清零(不過這個是什麼指令?好像沒有見過…)
00403E17  |. 2BC2          |SUB EAX,EDX  //eax= eax-edx = 6C-0 = 6C
00403E19  |. D1F8          |SAR EAX,1  //算術右移1位,等於一次除2運算,所以 eax= eax/2 = 6C/2 =36
00403E1B  |. 04 20          |ADD AL,20  //al= al+20 = 36+20 =56(即“V”)

//以下將求得的值作一系列的比較
00403E1D  |. 3C 5A          |CMP AL,5A  //al 與 5A(Z)比較
00403E1F  |. 884424 10      |MOV BYTE PTR SS:[ESP+10],AL
00403E23  |. 7E 0A          |JLE SHORT AddRem.00403E2F  //小於則跳轉到403E2F
00403E25  |. 3C 61          |CMP AL,61  //如果 al 大於 5A,則到此處,繼續與 61 比較
00403E27  |. 7D 06          |JGE SHORT AddRem.00403E2F  //大於等於 61 則跳轉
00403E29  |. 04 06          |ADD AL,6  //如果al小於61,則 al+6
00403E2B  |. 884424 10      |MOV BYTE PTR SS:[ESP+10],AL
00403E2F  |> 3C 39          |CMP AL,39  //如果al小於5A,則跳到此處與 39 比較
00403E31  |. 7E 0A          |JLE SHORT AddRem.00403E3D  //不大於39,則跳出比較迴圈
00403E33  |. 3C 41          |CMP AL,41  //如果小於 39,則繼續於 41 比較
00403E35  |. 7D 06          |JGE SHORT AddRem.00403E3D  //大於 41 則跳出比較迴圈
00403E37  |. 04 08          |ADD AL,8  //如果 al 小於 41,則 al+8
//經過以上迴圈及比較,最後得到註冊碼第二部分的第二位字元“V”
//以上迴圈即是計算註冊碼的過程
00403E39  |. 884424 10      |MOV BYTE PTR SS:[ESP+10],AL
00403E3D  |> 8B4424 10      |MOV EAX,DWORD PTR SS:[ESP+10]
00403E41  |. 8D4C24 0C      |LEA ECX,DWORD PTR SS:[ESP+C]
00403E45  |. 50            |PUSH EAX
00403E46  |. 8D5424 1C      |LEA EDX,DWORD PTR SS:[ESP+1C]
00403E4A  |. 51            |PUSH ECX
00403E4B  |. 52            |PUSH EDX
00403E4C  |. E8 B5700100    |CALL AddRem.0041AF06  //將算出的“V”加到之前算出的部分註冊碼之後,得“6582-mV”
00403E51  |. 50            |PUSH EAX
00403E52  |. 8D4C24 10      |LEA ECX,DWORD PTR SS:[ESP+10]
00403E56  |. C64424 28 03  |MOV BYTE PTR SS:[ESP+28],3
00403E5B  |. E8 38CB0100    |CALL AddRem.00420998
00403E60  |. 8D4C24 18      |LEA ECX,DWORD PTR SS:[ESP+18]
00403E64  |. C64424 24 02  |MOV BYTE PTR SS:[ESP+24],2
00403E69  |. E8 F1C90100    |CALL AddRem.0042085F
00403E6E  |> BF F88F4300    |MOV EDI,AddRem.00438FF8                ;  ASCII "lovefire"
00403E73  |. 83C9 FF        |OR ECX,FFFFFFFF
00403E76  |. 33C0          |XOR EAX,EAX
00403E78  |. 43            |INC EBX
00403E79  |. F2:AE          |REPNE SCAS BYTE PTR ES:[EDI]
00403E7B  |. F7D1          |NOT ECX
00403E7D  |. 49            |DEC ECX  //以上作為記數器
00403E7E  |. 3BD9          |CMP EBX,ECX 
00403E80  |.^0F82 72FFFFFF  \JB AddRem.00403DF8  //跳回 403DF8 開始新的一次迴圈。


演算法可以總結如下:
1、註冊名只允許是位於 ASCII 碼錶 20~7E 之間的字元。(所以也不支援中文)
2、軟體的註冊碼分為兩個部分,第一部分是固定的字元“6582-”;第二部分的第一個字元由註冊名的位數和一個預設的字串“87ae2401my69”決定,若註冊名有N位(N>6),則取字串的N+1位字元,迴圈取。
3、註冊碼第二部分剩下的字元,由輸入的註冊名的單數位字元決定,其計算如下:

設 SN1 為透過註冊名字元初步計算出來的值,SN 為正確的註冊碼字元,則:
SN1=((註冊名單數位的字元的ASCII值-0)/2)+20

a) 若 SN1<5A,SN1<39,則 SN = SN1
b) 若 SN1<5A,SN1>39,SN1>41,則 SN = SN1
c) 若 SN1<5A,SN1>39,SN1<41,則 SN = SN1+8
d) 若 SN1>5A,SN1<61,則 SN = SN1+6
e) 若 SN1>5A,SN1>61,則 SN = SN1(此項可排除)

又因為註冊使用者名稱介於[20,7F),所以 SN1 取值範圍只能是[30,5F],畫個簡單的數軸就可以看出:
SN1 介於 [30,39] 與 [41,5A] 時,SN = SN1
SN1 介於 [39,41] 時,SN = SN1+8
SN1 介於 [5A,5F] 時,SN = SN1+6

反推回註冊使用者名稱可知:
在 ASCII 碼錶中,當使用者名稱的值位於:
20~32 時,SN =((註冊名字元的ASCII值-0)/2)+20
33~42 時,SN =(((註冊名字元的ASCII值-0)/2)+20)+8
43~74 時,SN =(((註冊名字元的ASCII值-0)/2)+20)
75~7E 時,SN =(((註冊名字元的ASCII值-0)/2)+20)+6

為了證實以上演算法,我們手工構造一個註冊使用者名稱,使之符合以上範圍限制:+060S0z (雙數位無作用,隨便取)
+  0  6  0  S  0  z (字元)
2B    36      53      7A (ASCII 值)

則:
+: ((2B-0)/2)+20 = 35 = 5
6: (((36-0)/2)+20)+8 = 43 = C
S: ((53-0)/2)+20 = 49 = I
z: (((7A-0)/2)+20)+6 = 63 = c

再加上註冊名7位,取“87ae2401my69”的第7+1位數“1”,最後構成註冊碼“6582-15CIc”,測試一下,註冊成功!

註冊資訊儲存在 HKEY_LOCAL_MACHINE\SOFTWARE\4Developers\AddRemove 下的“4D”子鍵中。

再往下是軟體將輸入的註冊碼和計算出的註冊碼做對比,與演算法無關,所以這裡不再詳細分析。

至此演算法分析完成,一個可用的註冊碼:Name: lovefire  S/N: 6582-mVaSY

最後特別感謝小樓兄的指點:)

相關文章