ResScope1.92演算法分析

看雪資料發表於2004-07-21

ResScope,一個將比exeScope更強的軟體資源分析工具,很不錯的軟體。

delphi程式,無殼無反除錯無校驗,我喜歡,:)。
透過字串容易找到這裡,很容易看到,讀取登錄檔中的使用者名稱和註冊碼,分別計算後進行再比較。

程式碼:
  CODE:0051B6EB                 mov     cl, 1 CODE:0051B6ED                 mov     edx, offset aSoftwareRest_3 ; "SOFTWARE\\RESTOOLS\\ResScope" CODE:0051B6F2                 mov     eax, [ebp+var_8] CODE:0051B6F5                 call    @Registry@TRegistry@OpenKey$qqrx17System@AnsiStringo ; Registry::TRegistry::OpenKey(System::AnsiString,bool) CODE:0051B6FA                 test    alal CODE:0051B6FC                 jz      loc_51B888 CODE:0051B702                 lea     eax, [ebp+var_C] CODE:0051B705                 call    @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &) CODE:0051B70A                 lea     eax, [ebp+var_10] CODE:0051B70D                 call    @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &) CODE:0051B712                 mov     edx, offset aReguser_2 ; "reguser" CODE:0051B717                 mov     eax, [ebp+var_8] CODE:0051B71A                 call    @Registry@TRegistry@ValueExists$qqrx17System@AnsiString ; Registry::TRegistry::ValueExists(System::AnsiString) CODE:0051B71F                 test    alal CODE:0051B721                 jz      short loc_51B733 CODE:0051B723                 lea     ecx, [ebp+var_C] CODE:0051B726                 mov     edx, offset aReguser_2 ; "reguser" CODE:0051B72B                 mov     eax, [ebp+var_8] CODE:0051B72E                 call    @TRegistry@ReadString$qqrx10AnsiString ; TRegistry::ReadString(AnsiString) CODE:0051B733  CODE:0051B733 loc_51B733:                             ; CODE XREF: sub_51B6A0+81j CODE:0051B733                 mov     edx, offset aRegcode_1 ; "regcode" CODE:0051B738                 mov     eax, [ebp+var_8] CODE:0051B73B                 call    @Registry@TRegistry@ValueExists$qqrx17System@AnsiString ; Registry::TRegistry::ValueExists(System::AnsiString) CODE:0051B740                 test    alal CODE:0051B742                 jz      short loc_51B754 CODE:0051B744                 lea     ecx, [ebp+var_10] CODE:0051B747                 mov     edx, offset aRegcode_1 ; "regcode" CODE:0051B74C                 mov     eax, [ebp+var_8] CODE:0051B74F                 call    @TRegistry@ReadString$qqrx10AnsiString ; TRegistry::ReadString(AnsiString) CODE:0051B754  CODE:0051B754 loc_51B754:                             ; CODE XREF: sub_51B6A0+A2j CODE:0051B754                 mov     eax, [ebp+var_10] CODE:0051B757                 call    @System@@LStrLen ; System::__linkproc__ LStrLen CODE:0051B75C                 cmp     eax, 30h CODE:0051B75F                 jnz     loc_51B888 CODE:0051B765                 mov     eax, [ebp+var_C] CODE:0051B768                 call    @System@@LStrLen ; System::__linkproc__ LStrLen CODE:0051B76D                 test    eaxeax CODE:0051B76F                 jle     loc_51B888 CODE:0051B775                 lea     eax, [ebp+var_14] ; 儲存返回值 CODE:0051B775                                         ; 指向18位元組長PCHAR CODE:0051B778                 push    eax CODE:0051B779                 mov     cl, 1 CODE:0051B77B                 mov     dl, 1 CODE:0051B77D                 mov     eax, [ebp+var_C] ; "reguser" CODE:0051B780                 call    sub_51A06C      ; 處理使用者名稱 CODE:0051B785                 mov     eax, [ebp+var_14] ; 處理使用者名稱得到的18位元組長的串,先入棧,待會好比較 CODE:0051B788                 push    eax CODE:0051B789                 lea     ecx, [ebp+var_18] ; 儲存返回值。。 CODE:0051B78C                 mov     dl, 1 CODE:0051B78E                 mov     eax, [ebp+var_10] ; "regcode" CODE:0051B791                 call    sub_519298      ; 註冊碼變換函式 CODE:0051B796                 mov     edx, [ebp+var_18] CODE:0051B799                 pop     eax CODE:0051B79A                 call    @System@@LStrCmp$qqrv ; System::__linkproc__ LStrCmp(void) CODE:0051B79F                 jnz     short loc_51B7A5 ; 
 
這個軟體的爆破起來不是很容易
程式碼:
  CODE:00525CBB                 mov     eaxds:off_525214  ; * Reference to class THexDumpPass CODE:00525CC0                 call    sub_425EEC      ; * Reference to: Classes.TComponent.Create(TComponent;boolean;TComponent); CODE:00525CC5                 mov     ebxeax CODE:00525CC7                 lea     eax, [ebp+var_130] CODE:00525CCD                 call    sub_518E18      ; 使用者名稱進行變換轉為一串字元 CODE:00525CD2                 mov     edx, [ebp+var_130] CODE:00525CD8                 mov     eaxebx CODE:00525CDA                 mov     ecx, [eax] CODE:00525CDC                 call    dword ptr [ecx+18h] ; Classes::TComponent::SetName()
 
在此處設定使用者名稱變換而來得串設定元件名字,然後使用註冊碼變換而來得串來FindComponent(),如果註冊碼正確,兩個串應該相等,則能得到正確的控制程式碼,
儲存起來,然後在校驗註冊時候使用該控制程式碼,若沒找到則例外出錯。

下面來看看註冊演算法:
使用peid來探測一下用了什麼演算法,一下出來8種演算法,夠嚇人,不過還有不少沒探測出來,:)。
DEDE反彙編看看,注意到類名中有大量的類似TCipher_MD5、TCipher_Blowfish、TCipher_IDEA類似這樣的類名,
熟悉的人一眼就看出來使用DEC控制元件,這是Hagen Reddmann寫的Delphi Encryption Compendium 控制元件,控制元件支援20餘種Hash,40多種分組密碼演算法,
而且附帶原始碼,是很好的學習演算法的教程。

在分析過程中可以用delphi編譯一個小例子,然後跟蹤看看,並參考原始碼效果很好。
先來看看處理使用者名稱的函式
CODE:0051B780                 call    sub_51A06C      ; 處理使用者名稱
使用者ID的生成是呼叫CPUID指令獲取cpu資訊,然後編碼至16位元組。與使用者名稱連線起來,透過簡單演算法變換為18h位元組,
程式碼:
  CODE:0051A0C3                 mov     edxds:dword_578440 ; 使用者ID,類似"92F1L8EACT2FFNFF"一串字元。。。 CODE:0051A0C9                 call    @System@@LStrCat$qqrv ; System::__linkproc__ LStrCat(void) CODE:0051A4A6                 push    offset unk_575B10 ; IVector,初始化向量 CODE:0051A4AB                 mov     edx, offset byte_575B08 ; keyCAST..64位金鑰 CODE:0051A4B0                 lea     eax, [ebp+var_F4_TCast128Data] ; 0012FAF8 CODE:0051A4B0                                         ;   TCast128Data= record CODE:0051A4B0                                         ;     InitBlock: array[0..7] of byte;    { initial IV } CODE:0051A4B0                                         ;     LastBlock: array[0..7] of byte;    { current IV } CODE:0051A4B0                                         ;     xKey: array[0..31] of DWord; CODE:0051A4B0                                         ;     Rounds: integer; CODE:0051A4B0                                         ;   end; CODE:0051A4B6                 mov     ecx, 8          ; sizeof(Key) CODE:0051A4BB                 call    Cast128Init     ; 注意此處Cast128非呼叫DEC的函式,乃是獨立的類。                                                       ; 注意其sbox乃是作者隨機生成
 

採用CBC模式呼叫Cast128演算法,即密碼分組連結模式即每次分組加密結果,與下組明文xor後,作為輸入。
程式碼:
  CODE:0051A50E                 lea     edx, [ebp+eax+var_48] ; 輸入串,輸出也儲存再該地址 CODE:0051A512                 lea     eax, [ebp+var_F4_TCast128Data] ; 金鑰初始化結果 CODE:0051A518                 call    Cast128EncryptCBC ; procedure Cast128EncryptCBC(var Data: TCast128Data; InData, OutData: pointer); CODE:0051A518                                         ;   { encrypts the data in a 64bit block using the CBC chaining mode }
 
下面就是呼叫DEC中的密碼演算法來進行加密
程式碼:
  CODE:0051A6FB                 mov     edxds:off_4F31E0 ; Reference to class TCipher_3Way,這個可由DEDE反彙編程式碼中看到 CODE:0051A701                 mov     eax, [ebp+var_14] CODE:0051A704                 call    @TCipherManager@@SetClass ; TCipherManager::__linkproc__ SetClass CODE:0051A709                 xor     ecxecx CODE:0051A70B                 mov     edx, [ebp+var_18] CODE:0051A70E                 mov     eax, [ebp+var_14] CODE:0051A711                 call    @TCipherManager@@InitKey ; procedure TCipherManager.InitKey(const Key: String; IVector: Pointer); CODE:0051A716                 push    18h             ; 待Encode串長度 CODE:0051A718                 lea     ecx, [ebp+var_60] ; 返回串 CODE:0051A71B                 lea     edx, [ebp+var_48] ; Source串,由Cast128而來 CODE:0051A71E                 mov     eax, [ebp+var_14] CODE:0051A721                 call    @TCipherManager@@EncodeBuffer ; procedure TCipherManager.EncodeBuffer CODE:0051A721                                         ; (const Source; var Dest; DataSize: Integer);
 
下面級連的還有
程式碼:
  CODE:0051A7F6                 mov     edxds:off_4F29DC ; * Reference to class TCipher_Blowfish  (注: pbox,sbox非標) CODE:0051A8F1                 mov     edxds:off_4F2940 ; * Reference to class TCipher_Gost      (注: Gost_Data非標) CODE:0051AA15                 mov     edxds:off_4F2A7C ; * Reference to class TCipher_IDEA CODE:0051AB39                 mov     edxds:off_4F3144 ; * Reference to class TCipher_Q128      (注: Q128_Data非標) CODE:0051AC5D                 mov     edxds:off_4F2B18 ; * Reference to class TCipher_SAFER     (注: DEC庫中預設沒有新增入列表,需修改程式碼註冊該Cipher類到控制元件)  CODE:0051AD58                 mov     edxds:off_4F2E34 ; * Reference to class TCipher_SAFER_K128 CODE:0051AE7C                 mov     edxds:off_4F2BB4 ; * Reference to class TCipher_SAFER_K40 CODE:0051AF77                 mov     edxds:off_4F2CF4 ; * Reference to class TCipher_SAFER_K64
 
對使用者名稱的變換用了10種演算法,不過呼叫DEC庫格式比較標準,跟蹤起來也什麼困難.

下面看註冊碼變換演算法
程式碼:
  CODE:0051B791                 call    sub_519298      ; 註冊碼變換函式 註冊碼為30h字元,先解碼後為18h位元組資料,每2字元變換為一個位元組 然後 CODE:005193C2                 push    offset unk_575AE0 ; GOST初始化向量 CODE:005193C7                 mov     edx, offset unk_575AE8 ; GOST金鑰 20h位元組 CODE:005193CC                 lea     eax, [ebp+var_98] ;   TGOSTData= record CODE:005193CC                                         ;     InitBlock: array[0..7] of byte;    { initial IV } CODE:005193CC                                         ;     LastBlock: array[0..7] of byte;    { current IV } CODE:005193CC                                         ;     XKey: array[0..7] of DWord; CODE:005193CC                                         ;   end; CODE:005193D2                 mov     ecx, 20h CODE:005193D7                 call    GOSTInit        ; procedure GOSTInit(var Data: TGOSTData; Key: pointer; Len: integer; IV: pointer                                                       ; 注意此處Cast128非呼叫DEC的函式,乃是獨立的類。其Gost_Data非標
       

同樣採用CBC模式解碼資料,每次處理8位元組,共處理3次。
程式碼:
  CODE:00519429                 lea     edx, [ebp+eax+var_50] ; 輸出 CODE:0051942D                 lea     eax, [ebp+var_98] ; var Data: TGOSTData CODE:00519433                 call    GOSTDecryptCBC  ; //procedure GOSTDecryptCBC(var Data: TGOSTData; InData, OutData: pointer); CODE:00519433                                         ;   { decrypts the data in a 64bit block using the CBC chaining mode }
                   
同樣下面級連了一串變換,不過此處呼叫的都是@TCipherManager@@DecodeBuffer,解碼函式

程式碼:
  CODE:005195FB                 mov     edxds:off_4F327C ; * Reference to class TCipher_Twofish    (注: Twofish_Data,Twofish_8x8值非標) CODE:00519714                 mov     edxds:off_4F300C ; * Reference to class TCipher_TEAN CODE:00519810                 mov     edxds:off_4F2F74 ; * Reference to class TCipher_TEA CODE:0051990C                 mov     edxds:off_4F33B4 ; * Reference to class TCipher_Square CODE:00519A2D                 mov     edxds:off_4F3318 ; * Reference to class TCipher_Shark CODE:00519B29                 mov     edxds:off_4F30A8 ; * Reference to class TCipher_SCOP CODE:00519C4A                 mov     edxds:off_4F2D94 ; * Reference to class TCipher_SAFER_SK64 CODE:00519D6B                 mov     edxds:off_4F2C54 ; * Reference to class TCipher_SAFER_SK40 CODE:00519E67                 mov     edxds:off_4F2ED4 ; * Reference to class TCipher_SAFER_SK128
 

最後將使用者名稱變換輸出結果和註冊碼變換輸出進行比較。
對於1.75版只使用了Cast128和Gost兩種演算法,而1.92版堆疊了大概有18種密碼學演算法,
但由於該庫格式又比較固定,總體說來所以不難做出序號產生器。基本上就照著作者的流程,使用者名稱變換部分全部照抄,
註冊碼變換部分從後往前逆向使用。類似這樣,只是把DecodeBuffer改為EncodeBuffer就可以了,類似
程式碼:
      CipherManager1.Algorithm := 'Twofish';     CipherManager1.InitKey(keystr, nil);     CipherManager1.EncodeBuffer(Source,Out,$18);
 
另外注意其中有些DEC庫函式的s盒被更改,所以還需要修改DEC控制元件中的cipher.inc裡面的值,重新編譯該控制元件。

感謝DiKeN/IPB提供ResScope1.75序號產生器原始碼,省下了我不少時間,:)。

yesky1/IPB
7.21

相關文章