藍圖餐飲娛樂管理系統v2.21演算法分析

看雪資料發表於2003-07-06

軟體:          藍圖餐飲娛樂管理系統V2.41試用版
功能:          國際級完美餐飲全面性電腦管理系統
開發者:        聖藍圖軟體技術有限公司
網址:          www.adslt.com
限制:          60天時間限制
開發工具:      delphi 6.0
使用技術:      MD5 + 明碼比較 (是不是滑天下之大稽)
破解工具:      OD1.09e
====================================================
註冊名字:      lianzi2000
試煉碼:        1357924680931654
觀察:          沒有註冊錯誤提示。需要重啟驗證。
ACDC:           啟動時未見可疑檔案訪問,但有如下注冊表訪問:
               Ginshop.ExE:3180 CreateKeyHKLM\Software\JDGL\FoodSUCCESSKey: 0xE119D368
               Ginshop.ExE:3180 QueryvalueHKLM\Software\JDGL\Food\UsrNameNOTFOUND
               Ginshop.ExE:3180 QueryvalueHKLM\Software\JDGL\Food\PasswdNOTFOUND
               防Dede,會連Dede一起退出。
               防OD,若用OD載入執行會當機,必須先執行然後attach.
殼:            aspr2.12, 用aspdie可脫去.
思路:          由於當註冊碼錯誤時並不儲存,所以設想驗證應該在啟動之前,很可能就在輸入之後,只不過沒有提示而已。
====================================================
尋找驗證程式碼:
   啟動脫殼後的程式,進入管理員賬戶。主介面出來以後,執行OD並繫結。在CreateWindowExA上下斷,
   然後再“幫助資訊”選單中選擇“產品註冊”,被OD欄下(由於OD繫結時的執行緒結束了,不如此作OD會失去控制)。
   繼續執行,會看到各個控制元件被分別建立。然後開啟視窗葉面,記下注冊視窗和編輯框的控制程式碼。Delphi不採用GetWindowTextA獲取輸入,
   而是利用CallWindowProcA傳送一條WM_GETTEXT訊息給編輯控制元件,其wParam是字串存放地址(請參看API手冊)。
   所以在這個函式上下條件斷點([esp+8]==0x1a02b0) && ([esp+0xC]==WM_GETTEXT). 不出所料,點選“正版註冊”按鈕
   後這個斷點被觸發。從堆疊上讀出口令存放地址並記下,然後直接F4到返回地址,在口令存放處下記憶體斷點,
   然後執行直至程式訪問輸入的口令串。
   
====================================================
00402A38   56               PUSH ESI
00402A39   57               PUSH EDI
00402A3A   89C6             MOV ESI,EAX                              
00402A3C   89D7             MOV EDI,EDX                              
00402A3E   89C8             MOV EAX,ECX                              
00402A40   39F7             CMP EDI,ESI
00402A42   77 13            JA SHORT Ginshop.00402A57                
00402A44   74 2F            JE SHORT Ginshop.00402A75                
00402A46   C1F9 02          SAR ECX,2                                
00402A49   78 2A            JS SHORT Ginshop.00402A75
00402A4B   F3:A5            REP MOVSD
00402A4D   89C1             MOV ECX,EAX
00402A4F   83E1 03          AND ECX,3
00402A52   F3:A4            REP MOVSB     <============ 記憶體斷點在這裡觸發, 發現複製的是註冊碼的第0B個字元
00402A54   5F               POP EDI                     一路F8返回可見其將目標字元的地址放在堆疊中,
                                                       實際上是上層函式的臨時變數。
00402A55   5E               POP ESI
00402A56   C3               RETN
...................................................
最後返回到如下函式:

0056D52C   B9 01000000      MOV ECX,1
0056D531   BA 0B000000      MOV EDX,0B
0056D536   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]
0056D539   E8 527DE9FF      CALL Ginshop.00405290    <=========顯然是剛剛呼叫的函式
0056D53E   8B45 EC          MOV EAX,DWORD PTR SS:[EBP-14]  <============目標字元的地址就在這裡
0056D541   E8 D6C6E9FF      CALL Ginshop.00409C1C                    這個函式返回字元對應的值,如'9'->9
0056D546   8BD8             MOV EBX,EAX                              ; ebx==value(code[0b])
0056D548   8D45 E8          LEA EAX,DWORD PTR SS:[EBP-18]            
0056D54B   50               PUSH EAX
0056D54C   B9 01000000      MOV ECX,1                                ; 只取一個字元
0056D551   BA 0C000000      MOV EDX,0C                               ; 字元索引0xC
0056D556   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]            ; 試煉碼
0056D559   E8 327DE9FF      CALL Ginshop.00405290           <======== 取字元
0056D55E   8B45 E8          MOV EAX,DWORD PTR SS:[EBP-18]      
0056D561   E8 B6C6E9FF      CALL Ginshop.00409C1C               ;取值
0056D566   8BF0             MOV ESI,EAX                              
0056D568   8D45 E4          LEA EAX,DWORD PTR SS:[EBP-1C]            
0056D56B   50               PUSH EAX
0056D56C   B9 01000000      MOV ECX,1
0056D571   BA 0D000000      MOV EDX,0D
0056D576   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]        ;同上    
0056D579   E8 127DE9FF      CALL Ginshop.00405290                    
0056D57E   8B45 E4          MOV EAX,DWORD PTR SS:[EBP-1C]
0056D581   E8 96C6E9FF      CALL Ginshop.00409C1C                    ; get the value
0056D586   8BF8             MOV EDI,EAX                              ; edi==value(code[0d])

至此,取了註冊碼第0b,0c and 0d個字元,並轉換成相應的數值,如果是"0xa0x6"的形式,取值函式會自動轉換。

0056D588   8D45 C8          LEA EAX,DWORD PTR SS:[EBP-38]            ; 臨時變數
0056D58B   8B55 F4          MOV EDX,DWORD PTR SS:[EBP-C]             ; 註冊名字
0056D58E   8A541A FF        MOV DL,BYTE PTR DS:[EDX+EBX-1]           ; 根據索引取字元get char =name(value(code[0b])-1)
0056D592   8850 01          MOV BYTE PTR DS:[EAX+1],DL               ; 存放
0056D595   C600 01          MOV BYTE PTR DS:[EAX],1                  ; 長度為1
0056D598   8D55 C8          LEA EDX,DWORD PTR SS:[EBP-38]
0056D59B   8D45 C4          LEA EAX,DWORD PTR SS:[EBP-3C]
0056D59E   E8 D95AE9FF      CALL Ginshop.0040307C                    ; 取出的字元複製到另一處
0056D5A3   8D45 C0          LEA EAX,DWORD PTR SS:[EBP-40]            ; new var
0056D5A6   8B55 F4          MOV EDX,DWORD PTR SS:[EBP-C]             ; name
0056D5A9   8A5432 FF        MOV DL,BYTE PTR DS:[EDX+ESI-1]           ; 取第二個字元get char =name(value(code[0c])-1)
0056D5AD   8850 01          MOV BYTE PTR DS:[EAX+1],DL               ;
0056D5B0   C600 01          MOV BYTE PTR DS:[EAX],1                  ;
0056D5B3   8D55 C0          LEA EDX,DWORD PTR SS:[EBP-40]            ;
0056D5B6   8D45 C4          LEA EAX,DWORD PTR SS:[EBP-3C]            ;
0056D5B9   B1 02            MOV CL,2                                 ;
0056D5BB   E8 8C5AE9FF      CALL Ginshop.0040304C                    ; 把兩個字元連線
0056D5C0   8D55 C4          LEA EDX,DWORD PTR SS:[EBP-3C]            ;
0056D5C3   8D45 BC          LEA EAX,DWORD PTR SS:[EBP-44]            ;
0056D5C6   E8 B15AE9FF      CALL Ginshop.0040307C                    ;
0056D5CB   8D45 C0          LEA EAX,DWORD PTR SS:[EBP-40]            ;
0056D5CE   8B55 F4          MOV EDX,DWORD PTR SS:[EBP-C]             ;
0056D5D1   8A543A FF        MOV DL,BYTE PTR DS:[EDX+EDI-1]           ; 取第三個字元 get char =name(value(code[0d])-1)
0056D5D5   8850 01          MOV BYTE PTR DS:[EAX+1],DL               ;
0056D5D8   C600 01          MOV BYTE PTR DS:[EAX],1                  ;
0056D5DB   8D55 C0          LEA EDX,DWORD PTR SS:[EBP-40]
0056D5DE   8D45 BC          LEA EAX,DWORD PTR SS:[EBP-44]
0056D5E1   B1 03            MOV CL,3
0056D5E3   E8 645AE9FF      CALL Ginshop.0040304C                    ; 三個字元都連起來"0al"
0056D5E8   8D55 BC          LEA EDX,DWORD PTR SS:[EBP-44]            ;
0056D5EB   8D45 CC          LEA EAX,DWORD PTR SS:[EBP-34]            ;
0056D5EE   E8 E979E9FF      CALL Ginshop.00404FDC                    ; 複製到記憶體某處
0056D5F3   8B45 CC          MOV EAX,DWORD PTR SS:[EBP-34]            ;
0056D5F6   8D55 D0          LEA EDX,DWORD PTR SS:[EBP-30]            ;存放結果的變數
0056D5F9   E8 525EFEFF      CALL Ginshop.00553450           <============ 跟進
0056D5FE   8D45 D0          LEA EAX,DWORD PTR SS:[EBP-30]            ; 函式計算結果
0056D601   8D55 E0          LEA EDX,DWORD PTR SS:[EBP-20]            ; 指標用來存放返回結果
0056D604   E8 BB5EFEFF      CALL Ginshop.005534C4                    ; 這個函式把上一個函式返回的數值結果轉換成字串,
0056D609   8B55 E0          MOV EDX,DWORD PTR SS:[EBP-20]            ; 如0xED=>"ED"
0056D60C   8D45 F4          LEA EAX,DWORD PTR SS:[EBP-C]             ; 註冊名字
0056D60F   E8 0478E9FF      CALL Ginshop.00404E18                    ; 這裡複製結果字串到另一個變數,並調整引用計數
0056D614   8D45 B8          LEA EAX,DWORD PTR SS:[EBP-48]            ;
0056D617   50               PUSH EAX
0056D618   B9 0A000000      MOV ECX,0A                               ; 要複製的字元計數
0056D61D   BA 09000000      MOV EDX,9                                ; 從第9個字元開始(從1算起)
0056D622   8B45 F4          MOV EAX,DWORD PTR SS:[EBP-C]             ; 結果字串ed79d869e220dc026ff71ca87cbf2fca
0056D625   E8 667CE9FF      CALL Ginshop.00405290                    ; 取得子串
0056D62A   FF75 B8          PUSH DWORD PTR SS:[EBP-48]               ; 子串e220dc026f
0056D62D   8D55 B4          LEA EDX,DWORD PTR SS:[EBP-4C]            ; new var
0056D630   8BC3             MOV EAX,EBX                              ; 註冊碼第B為對應的數值ebx==value(code[0b])
0056D632   E8 05C5E9FF      CALL Ginshop.00409B3C                    ; 這個函式用 wsprintf("%d",x)把數值重新轉成字元
0056D637   FF75 B4          PUSH DWORD PTR SS:[EBP-4C]               ; 結果
0056D63A   8D55 B0          LEA EDX,DWORD PTR SS:[EBP-50]
0056D63D   8BC6             MOV EAX,ESI                              ; 第0C位esi==value(code[0c])
0056D63F   E8 F8C4E9FF      CALL Ginshop.00409B3C                    ; 同上
0056D644   FF75 B0          PUSH DWORD PTR SS:[EBP-50]
0056D647   8D55 AC          LEA EDX,DWORD PTR SS:[EBP-54]
0056D64A   8BC7             MOV EAX,EDI                              ; 第0D位edi==value(code[0d])
0056D64C   E8 EBC4E9FF      CALL Ginshop.00409B3C
0056D651   FF75 AC          PUSH DWORD PTR SS:[EBP-54]
0056D654   8D45 F4          LEA EAX,DWORD PTR SS:[EBP-C]             ; 結果字串ed79d869e220dc026ff71ca87cbf2fca
0056D657   BA 04000000      MOV EDX,4
0056D65C   E8 977AE9FF      CALL Ginshop.004050F8                    ; 這裡把子串e220dc026f和剛剛轉回的3個字元連起來
0056D661   8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]             ; 成為正確註冊碼
0056D664   8B55 F4          MOV EDX,DWORD PTR SS:[EBP-C]             ; 這裡可以看到正確註冊碼
0056D667   E8 6877E9FF      CALL Ginshop.00404DD4                    ; 調整引用計數
0056D66C   33C0             XOR EAX,EAX
0056D66E   5A               POP EDX
0056D66F   59               POP ECX
0056D670   59               POP ECX
0056D671   64:8910          MOV DWORD PTR FS:[EAX],EDX
0056D674   68 A3D65600      PUSH Ginshop.0056D6A3
0056D679   8D45 AC          LEA EAX,DWORD PTR SS:[EBP-54]            ; 清除堆疊
0056D67C   BA 04000000      MOV EDX,4
0056D681   E8 1E77E9FF      CALL Ginshop.00404DA4
0056D686   8D45 CC          LEA EAX,DWORD PTR SS:[EBP-34]
0056D689   E8 F276E9FF      CALL Ginshop.00404D80
0056D68E   8D45 E0          LEA EAX,DWORD PTR SS:[EBP-20]
0056D691   BA 08000000      MOV EDX,8
0056D696   E8 0977E9FF      CALL Ginshop.00404DA4
0056D69B   C3               RETN
============= 返回後=======================================
0056D7E0   E8 E3FCFFFF      CALL Ginshop.0056D4C8                    ; 這是上面的函式
0056D7E5   8B55 F8          MOV EDX,DWORD PTR SS:[EBP-8]             ; 試煉碼
0056D7E8   8B45 F4          MOV EAX,DWORD PTR SS:[EBP-C]             ; 真碼
0056D7EB   E8 54BEE9FF      CALL Ginshop.00409644                    ; 明碼比較:返回0為成功
0056D7F0   85C0             TEST EAX,EAX
0056D7F2   74 0C            JE SHORT Ginshop.0056D800                ; 跳走就對了
0056D7F4   6A 0A            PUSH 0A
0056D7F6   E8 B91BEAFF      CALL <JMP.&kernel32.Sleep>               ; 失敗時就躺倒不幹了
0056D7FB   E9 95000000      JMP Ginshop.0056D895
0056D800   33C0             XOR EAX,EAX
0056D802   55               PUSH EBP                                 ;下面不用看了,明顯就是儲存註冊資訊了。
0056D803   68 86D85600      PUSH Ginshop.0056D886
0056D808   64:FF30          PUSH DWORD PTR FS:[EAX]
0056D80B   64:8920          MOV DWORD PTR FS:[EAX],ESP
0056D80E   B2 01            MOV DL,1
0056D810   A1 408D4700      MOV EAX,DWORD PTR DS:[478D40]
0056D815   E8 92B6F0FF      CALL Ginshop.00478EAC
0056D81A   8945 F0          MOV DWORD PTR SS:[EBP-10],EAX          
0056D81D   BA 02000080      MOV EDX,80000002
0056D822   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]
0056D825   E8 5EB7F0FF      CALL Ginshop.00478F88
0056D82A   8D45 EC          LEA EAX,DWORD PTR SS:[EBP-14]
0056D82D   BA CCD85600      MOV EDX,Ginshop.0056D8CC                 ; ASCII "Software\JDGL\Food"
0056D832   E8 E175E9FF      CALL Ginshop.00404E18
0056D837   B1 01            MOV CL,1
0056D839   8B55 EC          MOV EDX,DWORD PTR SS:[EBP-14]
0056D83C   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]
0056D83F   E8 88B8F0FF      CALL Ginshop.004790CC
0056D844   84C0             TEST AL,AL
0056D846   74 20            JE SHORT Ginshop.0056D868
0056D848   8B4D F8          MOV ECX,DWORD PTR SS:[EBP-8]
0056D84B   BA E8D85600      MOV EDX,Ginshop.0056D8E8                 ; ASCII "Passwd"
0056D850   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]
0056D853   E8 F0BBF0FF      CALL Ginshop.00479448
0056D858   8B4D FC          MOV ECX,DWORD PTR SS:[EBP-4]
0056D85B   BA F8D85600      MOV EDX,Ginshop.0056D8F8                 ; ASCII "UsrName"
0056D860   8B45 F0          MOV EAX,DWORD PTR SS:[EBP-10]
............................................................

=============== 跟進函式553450 ===================
00553450  /$ 55             PUSH EBP
00553451  |. 8BEC           MOV EBP,ESP
00553453  |. 83C4 A4        ADD ESP,-5C
00553456  |. 53             PUSH EBX
00553457  |. 8BDA           MOV EBX,EDX
00553459  |. 8945 FC        MOV DWORD PTR SS:[EBP-4],EAX
0055345C  |. 8B45 FC        MOV EAX,DWORD PTR SS:[EBP-4]         ;"0al"
0055345F  |. E8 BC1DEBFF    CALL Ginshop.00405220            ;調整引用計數
00553464  |. 33C0           XOR EAX,EAX
00553466  |. 55             PUSH EBP
00553467  |. 68 B6345500    PUSH Ginshop.005534B6            
0055346C  |. 64:FF30        PUSH DWORD PTR FS:[EAX]         ;安裝SEH
0055346F  |. 64:8920        MOV DWORD PTR FS:[EAX],ESP
00553472  |. 8D45 A4        LEA EAX,DWORD PTR SS:[EBP-5C]   ;"0al"
00553475  |. E8 AEFEFFFF    CALL Ginshop.00553328           <========跟進看看:
................................................
      00553328   C700 01234567    MOV DWORD PTR DS:[EAX],67452301    <=========眼熟嗎?我反正是啼笑皆非...
      0055332E   C740 04 89ABCDEF MOV DWORD PTR DS:[EAX+4],EFCDAB89  ;明知道後來要明碼比較,何必糟踐MD5呢
      00553335   C740 08 FEDCBA98 MOV DWORD PTR DS:[EAX+8],98BADCFE
      0055333C   C740 0C 76543210 MOV DWORD PTR DS:[EAX+C],10325476
      00553343   33D2             XOR EDX,EDX
      00553345   8950 10          MOV DWORD PTR DS:[EAX+10],EDX          
      00553348   33D2             XOR EDX,EDX
      0055334A   8950 14          MOV DWORD PTR DS:[EAX+14],EDX
      0055334D   83C0 18          ADD EAX,18
      00553350   BA 40000000      MOV EDX,40
      00553355   E8 BE4FEBFF      CALL Ginshop.00408318                  
      0055335A   C3               RETN
..................................................

0055347A  |. 8B45 FC        MOV EAX,DWORD PTR SS:[EBP-4]
0055347D  |. E8 B61BEBFF    CALL Ginshop.00405038    ;取字串長度
00553482  |. 50             PUSH EAX
00553483  |. 8B45 FC        MOV EAX,DWORD PTR SS:[EBP-4]
00553486  |. E8 A51DEBFF    CALL Ginshop.00405230    ;陷阱,如果沒有字串(eax==0)會返回一個程式碼段的地址,
0055348B  |. 8BD0           MOV EDX,EAX              ;導致crash,由SEH接受控制。
0055348D  |. 8D45 A4        LEA EAX,DWORD PTR SS:[EBP-5C]
00553490  |. 59             POP ECX
00553491  |. E8 C6FEFFFF    CALL Ginshop.0055335C    ;MD5 大法.....
00553496  |. 8BD3           MOV EDX,EBX
00553498  |. 8D45 A4        LEA EAX,DWORD PTR SS:[EBP-5C]
0055349B  |. E8 3CFFFFFF    CALL Ginshop.005533DC
005534A0  |. 33C0           XOR EAX,EAX
005534A2  |. 5A             POP EDX
005534A3  |. 59             POP ECX
005534A4  |. 59             POP ECX
005534A5  |. 64:8910        MOV DWORD PTR FS:[EAX],EDX
005534A8  |. 68 BD345500    PUSH Ginshop.005534BD
005534AD  |> 8D45 FC        LEA EAX,DWORD PTR SS:[EBP-4]
005534B0  |. E8 CB18EBFF    CALL Ginshop.00404D80
005534B5  \. C3             RETN
==========================================================
MD5 演算法程式碼太長,就不貼了,反正沒有什麼特別,參照相關文獻就可以了。

演算法總結:

註冊碼必須有13個以上個字元,前十個字元由後面的字元和註冊名字共同確定;
從第十一個字元開始的註冊碼必須給出三個個位數字,可以使10進位制也可以是16進位制(以'x'或'0x'開始);
將這三個數字作為索引,從註冊名字中取出3個字元(索引從1開始);
取出的字元連成一個串,做MD5運算,得到16位元組的結果,轉換成字串(32字元);
取該字串的 8 - 17 子串(索引從0開始)作為註冊碼的前10位。
對於lianzi2000,註冊碼為 e220dc026f931


遺留問題:
根據軟體作者說明,該軟體可以被註冊為普及版或標準版,但我沒有發現有那裡有這個判斷。
修改機器時間到11月份,仍然正常執行,包括普及版中理應受到限制的功能。


相關文章