NewsReactor 1.0 Build 5009的序號產生器制分析 (16千字)

看雪資料發表於2001-08-07

NewsReactor 1.0 Build 5009的序號產生器制分析
by Fpc[CCG]&6767[BCG]    @2001/08

tools: SI, wdasm, BcB 5.0

Homepage: http://www.daansystems.com
Features: NewsReactor is a tool to download binaries from usenet newsgroups.
    NewsReactor scans, combines and downloads files from a selected newsgroup.
    This program is shareware. You can register NewsReactor to get the full version.


這個軟體的註冊很有意思,分析加除錯用了兩天,巨爽,不是說軟體,而是這個過程。本來要寫crackme,現在不用了,嘿嘿。

1、它的未註冊版在啟動時會有一個幾秒鐘的Nag。單擊按鈕“Enter RegCode”,在開啟的對話方塊中輸入名字:LazyUser和註冊碼:123654;

2、^D切換到SoftIce,下命令:Bpx hMemcpy,按下F5返回到Windows;

3、單擊“OK”,被攔下,因為有兩個輸入框,所以要按一次F5,還會被攔下;

4、按n次F12到程式領空,你會到下面(用wdasm的反彙編結果來表示):



:0040C84C 8D4C2434                lea ecx, dword ptr [esp+34]

* Reference To: MFC42.Ordinal:09D2, Ord:09D2h
                                  |
:0040C850 E893D50000              Call 00419DE8
:0040C855 83F801                  cmp eax, 00000001        <- 返回處
:0040C858 0F85F7010000            jne 0040CA55            <- eax=1表示有輸入,所以不會跳
:0040C85E 8D8C2498000000          lea ecx, dword ptr [esp+00000098]
:0040C865 51                      push ecx
:0040C866 8BCF                    mov ecx, edi

* Reference To: MFC42.Ordinal:035A, Ord:035Ah
                                  |
:0040C868 E813D70000              Call 00419F80            <- 這幾個call是按編號來的,不理
:0040C86D 8D942494000000          lea edx, dword ptr [esp+00000094]
:0040C874 8BCE                    mov ecx, esi
:0040C876 52                      push edx

* Reference To: MFC42.Ordinal:035A, Ord:035Ah
                                  |
:0040C877 E804D70000              Call 00419F80
:0040C87C 6A0A                    push 0000000A
:0040C87E 8BCE                    mov ecx, esi

* Reference To: MFC42.Ordinal:1ADA, Ord:1ADAh
                                  |
:0040C880 E899DB0000              Call 0041A41E
:0040C885 6A0D                    push 0000000D
:0040C887 8BCE                    mov ecx, esi

* Reference To: MFC42.Ordinal:1ADA, Ord:1ADAh
                                  |
:0040C889 E890DB0000              Call 0041A41E


:0040C88E 8B36                    mov esi, dword ptr [esi]    <- esi取得Irc(Inputed Reg Code)的地址
:0040C890 395EF8                  cmp dword ptr [esi-08], ebx    <- 判斷Irc長度是否為0
                                <- 這種情況常見:[esi]處為某字串,那麼[esi-8]處有可能就是該字串的長度
:0040C893 0F8471010000            je 0040CA0A            <- 跳走就完蛋



:0040C899 A0106D4200              mov al, byte ptr [00426D10]    <- 下面這一小段是對緩衝區清0
:0040C89E B93F000000              mov ecx, 0000003F
:0040C8A3 8884249C000000          mov byte ptr [esp+0000009C], al
:0040C8AA 33C0                    xor eax, eax
:0040C8AC 8DBC249D000000          lea edi, dword ptr [esp+0000009D]
:0040C8B3 895C2410                mov dword ptr [esp+10], ebx
:0040C8B7 F3                      repz
:0040C8B8 AB                      stosd
:0040C8B9 66AB                    stosw
:0040C8BB AA                      stosb


:0040C8BC 8BFE                    mov edi, esi            <- 這一小段是經典的取字串長度程式碼
:0040C8BE 83C9FF                  or ecx, FFFFFFFF
:0040C8C1 33C0                    xor eax, eax
:0040C8C3 895C2414                mov dword ptr [esp+14], ebx
:0040C8C7 F2                      repnz
:0040C8C8 AE                      scasb
:0040C8C9 F7D1                    not ecx            <- ecx=Len+1


:0040C8CB 2BF9                    sub edi, ecx            <- 這一段是經典的字串複製程式
:0040C8CD 8D94249C010000          lea edx, dword ptr [esp+0000019C]
:0040C8D4 8BC1                    mov eax, ecx
:0040C8D6 8BF7                    mov esi, edi
:0040C8D8 8BFA                    mov edi, edx            <- 目標地址
:0040C8DA 55                      push ebp
:0040C8DB C1E902                  shr ecx, 02
:0040C8DE F3                      repz
:0040C8DF A5                      movsd
:0040C8E0 8BC8                    mov ecx, eax
:0040C8E2 8D542414                lea edx, dword ptr [esp+14]
:0040C8E6 83E103                  and ecx, 00000003
:0040C8E9 8D8424A0010000          lea eax, dword ptr [esp+000001A0]
:0040C8F0 F3                      repz
:0040C8F1 A4                      movsb


:0040C8F2 8D4C2418                lea ecx, dword ptr [esp+18]
:0040C8F6 8DAC24A0010000          lea ebp, dword ptr [esp+000001A0]
:0040C8FD 51                      push ecx            <- 目標地址二
:0040C8FE 52                      push edx            <- 目標地址一

* Possible StringData Ref from Data Obj ->"%lu-%lu"
                                  |
:0040C8FF 68C05E4200              push 00425EC0            <- 處理的方式
:0040C904 50                      push eax            <- 待處理字串的偏移址

* Reference To: MSVCRT.sscanf, Ord:02B5h
                                  |
:0040C905 FF15FCD74100            Call dword ptr [0041D7FC]    <- 對字串格式化處理

:0040C90B 83C410                  add esp, 00000010
:0040C90E 85C0                    test eax, eax            <- eax為對幾個部分進行了格式化,為0表失敗或完成
:0040C910 0F8492000000            je 0040C9A8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040C9A2(C)
|
:0040C916 8D4C242C                lea ecx, dword ptr [esp+2C]    <- 入口的push其實就是上面的格式化處理結果
:0040C91A 8D542414                lea edx, dword ptr [esp+14]    <-
:0040C91E 51                      push ecx
:0040C91F 8B4C2428                mov ecx, dword ptr [esp+28]    <- 註冊碼將生成到這裡
:0040C923 52                      push edx
:0040C924 895C2434                mov dword ptr [esp+34], ebx
:0040C928 895C2438                mov dword ptr [esp+38], ebx
:0040C92C 895C243C                mov dword ptr [esp+3C], ebx
:0040C930 E84B170000              call 0040E080            <- !!計算註冊碼的核心,下面有介紹


:0040C935 8D7C242C                lea edi, dword ptr [esp+2C]
:0040C939 83C9FF                  or ecx, FFFFFFFF
:0040C93C 33C0                    xor eax, eax
:0040C93E 8D9424A0000000          lea edx, dword ptr [esp+000000A0]
:0040C945 F2                      repnz
:0040C946 AE                      scasb
:0040C947 F7D1                    not ecx
:0040C949 2BF9                    sub edi, ecx
:0040C94B 6A2D                    push 0000002D            <-
:0040C94D 8BF7                    mov esi, edi
:0040C94F 8BFA                    mov edi, edx
:0040C951 8BD1                    mov edx, ecx
:0040C953 83C9FF                  or ecx, FFFFFFFF
:0040C956 F2                      repnz
:0040C957 AE                      scasb
:0040C958 8BCA                    mov ecx, edx
:0040C95A 4F                      dec edi
:0040C95B C1E902                  shr ecx, 02
:0040C95E F3                      repz
:0040C95F A5                      movsd
:0040C960 8BCA                    mov ecx, edx
:0040C962 55                      push ebp
:0040C963 83E103                  and ecx, 00000003
:0040C966 F3                      repz
:0040C967 A4                      movsb

* Reference To: MSVCRT.strchr, Ord:02B7h
                                  |
:0040C968 8B35F4D74100            mov esi, dword ptr [0041D7F4]
:0040C96E FFD6                    call esi            <- 檢查Irc中是否含有‘-’,不是必須的
:0040C970 83C408                  add esp, 00000008
:0040C973 3BC3                    cmp eax, ebx
:0040C975 7431                    je 0040C9A8            <- 如果不含,則跳下去,準備比較了
                                <- 當只在Irc中輸入一個數,比如:34553
                                <- 就會直接跳下去
:0040C977 40                      inc eax            <- 當Irc中含‘-’時,這一段被執行到
:0040C978 6A2D                    push 0000002D
:0040C97A 50                      push eax
:0040C97B FFD6                    call esi
:0040C97D 8BE8                    mov ebp, eax            <-
:0040C97F 83C408                  add esp, 00000008
:0040C982 3BEB                    cmp ebp, ebx            <- 經測試: ebp=ebx=0
:0040C984 7422                    je 0040C9A8            <- 所以跳走, 進行比較

:0040C986 8D442418                lea eax, dword ptr [esp+18]
:0040C98A 8D4C2414                lea ecx, dword ptr [esp+14]
:0040C98E 50                      push eax
:0040C98F 45                      inc ebp
:0040C990 51                      push ecx

* Possible StringData Ref from Data Obj ->"%lu-%lu"
                                  |
:0040C991 68C05E4200              push 00425EC0
:0040C996 55                      push ebp

* Reference To: MSVCRT.sscanf, Ord:02B5h
                                  |
:0040C997 FF15FCD74100            Call dword ptr [0041D7FC]
:0040C99D 83C410                  add esp, 00000010
:0040C9A0 85C0                    test eax, eax
:0040C9A2 0F856EFFFFFF            jne 0040C916            <-



* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040C910(C), :0040C975(C), :0040C984(C)
|
:0040C9A8 8D9424A0000000          lea edx, dword ptr [esp+000000A0]
:0040C9AF 52                      push edx

* Possible StringData Ref from Data Obj ->"Registered by: %s
"
                                  |
:0040C9B0 68AC5E4200              push 00425EAC
:0040C9B5 E8869A0000              call 00416440
:0040C9BA 83C408                  add esp, 00000008
:0040C9BD 8D8424A0000000          lea eax, dword ptr [esp+000000A0]
:0040C9C4 8D4C2420                lea ecx, dword ptr [esp+20]
:0040C9C8 50                      push eax

* Reference To: MFC42.Ordinal:0219, Ord:0219h
                                  |
:0040C9C9 E8D0D50000              Call 00419F9E            <- 好象是字串複製的呼叫


:0040C9CE 8B7C2428                mov edi, dword ptr [esp+28]
:0040C9D2 8B00                    mov eax, dword ptr [eax]
:0040C9D4 8B0F                    mov ecx, dword ptr [edi]
:0040C9D6 50                      push eax            <- 假碼
:0040C9D7 50                      push ecx            <- 真碼

* Reference To: MSVCRT._mbscmp, Ord:0159h
                                  |
:0040C9D8 FF15E8D74100            Call dword ptr [0041D7E8]    <- 比較
                                <- 下面的不用管了,因為如果兩者一致,就成功;不一致則失敗

... ...


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040C893(C)
|
:0040CA0A 53                      push ebx
:0040CA0B 6A30                    push 00000030

* Possible StringData Ref from Data Obj ->"Registration code invalid!"
                                  |
:0040CA0D 68EC5C4200              push 00425CEC
:0040CA12 EB3C                    jmp 0040CA50

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040C9F8(C)
|

... ...


:0040CA48 53                      push ebx
:0040CA49 6A30                    push 00000030

* Possible StringData Ref from Data Obj ->"Thank you for registering!
Please "
                                        ->"restart NewsReactor,"
                                  |
:0040CA4B 68385D4200              push 00425D38

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CA12(U)
|

* Reference To: MFC42.Ordinal:04B0, Ord:04B0h
                                  |
:0040CA50 E8F9D60000              Call 0041A14E

... ...


:0040CAAB C20400                  ret 0004


下面我們來看它是如何計算註冊碼的:


* Referenced by a CALL at Addresses:
|:00404C2F  , :0040C204  , :0040C930 
|
:0040E080 83EC10                  sub esp, 00000010

:0040E083 8B442414                mov eax, dword ptr [esp+14]    <- 格式化處理後數字的位置

:0040E087 53                      push ebx            <- 儲存暫存器
:0040E088 56                      push esi
:0040E089 57                      push edi

:0040E08A 8B10                    mov edx, dword ptr [eax]    <- edx取得數字,因為我輸入的是:123654,所以edx=0x1E306
:0040E08C 8B4804                  mov ecx, dword ptr [eax+04]    <- 如果我們沒有輸入‘-’,只有一個數字,則ecx=0;
                                <- 若輸入格式是"數字-數字", 則ecx=

:0040E08F C744240C36A60A02        mov [esp+0C], 020AA636    <- 一個陣列的初始化
:0040E097 C7442410418D9514        mov [esp+10], 14958D41
:0040E09F C744241454D75700        mov [esp+14], 0057D754
:0040E0A7 C7442418C8BD3400        mov [esp+18], 0034BDC8

:0040E0AF B82037EFC6              mov eax, C6EF3720        <- eax初始化
:0040E0B4 BE20000000              mov esi, 00000020        <- 迴圈次數

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040E0FC(C)                            <- 這個迴圈當然是關鍵了
|                                <- 這段程式碼極為精練, 肯定是直接用匯編寫成的


:0040E0B9 8BFA                    mov edi, edx
:0040E0BB 8BDA                    mov ebx, edx
:0040E0BD C1EF05                  shr edi, 05
:0040E0C0 C1E304                  shl ebx, 04
:0040E0C3 33FB                    xor edi, ebx

:0040E0C5 8BD8                    mov ebx, eax
:0040E0C7 C1EB0B                  shr ebx, 0B
:0040E0CA 83E303                  and ebx, 00000003
:0040E0CD 03FA                    add edi, edx
:0040E0CF 8B5C9C0C                mov ebx, dword ptr [esp+4*ebx+0C]

:0040E0D3 03D8                    add ebx, eax
:0040E0D5 054786C861              add eax, 61C88647
:0040E0DA 33FB                    xor edi, ebx
:0040E0DC 2BCF                    sub ecx, edi            <-


:0040E0DE 8BF9                    mov edi, ecx
:0040E0E0 8BD9                    mov ebx, ecx
:0040E0E2 C1EF05                  shr edi, 05
:0040E0E5 C1E304                  shl ebx, 04
:0040E0E8 33FB                    xor edi, ebx

:0040E0EA 8BD8                    mov ebx, eax
:0040E0EC 83E303                  and ebx, 00000003
:0040E0EF 03F9                    add edi, ecx
:0040E0F1 8B5C9C0C                mov ebx, dword ptr [esp+4*ebx+0C]
:0040E0F5 03D8                    add ebx, eax
:0040E0F7 33FB                    xor edi, ebx
:0040E0F9 2BD7                    sub edx, edi            <-

:0040E0FB 4E                      dec esi
:0040E0FC 75BB                    jne 0040E0B9


:0040E0FE 8B442424                mov eax, dword ptr [esp+24]        <- 準備存放處理結果
:0040E102 5F                      pop edi
:0040E103 5E                      pop esi
:0040E104 5B                      pop ebx
:0040E105 8910                    mov dword ptr [eax], edx        <- 結果的第一部分
:0040E107 894804                  mov dword ptr [eax+04], ecx        <- 結果的第二部分
:0040E10A 83C410                  add esp, 00000010
:0040E10D C20800                  ret 0008


對於這樣的演算法我們應該怎樣分析呢?
一、將上面這段程式略微整理一下,從0X0開始窮舉,檢測結果是否全部為可顯示字元,如果是,該數就是一個註冊碼;

二、寫出逆演算法,根據姓名生成註冊碼,難度大,有挑戰性。

我想寫逆演算法,不但要在程式碼上實現,還得知道必要的引數。在上面這段程式中,出口值經測試可知eax=0,edx為註冊碼的第一部分,ecx為註冊碼的第二部分,就是說假設我們想以使用者名稱“43214321”來註冊,那麼edx=0x31323334,ecx=0x31323334,以此作為條件來看應該輸入什麼樣的註冊碼才能成功。

根據對上面程式的分析,經過長達幾個小時的除錯,終於證明如下的程式段可實現這個功能(注意:其中的數值以0x開頭,視編譯器的需要而定):

很多引數直接搬過來就能用,注意程式碼的順序和關鍵位置即可。

begin:
    sub esp, 00000030
    push ebx
    push esi
    push edi

    mov [esp+0x0C], 0x020AA636
    mov [esp+0x10], 0x14958D41
    mov [esp+0x14], 0x0057D754
    mov [esp+0x18], 0x0034BDC8

    ;初始化
    xor eax, eax
    mov ecx, 0x31323334
    mov edx, 0x31323334
    mov esi, 0x20

loop1:
    mov edi, ecx
    mov ebx, ecx
    shr edi, 05
    shl ebx, 04
    xor edi, ebx   
    add edi, ecx   

    mov ebx, eax
    and ebx, 0x3 
    mov ebx, dword ptr [esp+4*ebx+0x0C]
    add ebx, eax   

    xor edi, ebx   
    add edx, edi                <- 關鍵位置


    sub eax, 0x61C88647            <- 關鍵位置

    mov edi, edx
    mov ebx, edx
    shr edi, 05
    shl ebx, 04
    xor edi, ebx
    add edi, edx
    
    mov ebx, eax
    shr ebx, 0x0B
    and ebx, 0x3
    mov ebx, dword ptr [esp+4*ebx+0x0C]
    add ebx, eax

    xor edi, ebx
    add ecx, edi                <- 關鍵位置

    dec esi
    jnz loop1
    
exit1:
    pop edi
    pop esi
    pop ebx
    add esp, 00000030


OK,經過這個迴圈,edx=705987235,ecx=970065253。好了重新註冊,輸入名字:43214321,註冊碼:705987235-970065253,Coolll! "Thank you for...."。

其實被這個逆演算法折磨很長時間,一開始想偷懶,只輸入一個數字怎樣都不能成功,原因在於edx的值可知,而ecx相當於是隨機的(就是未知的一個值),後來偶然想到‘-’的用處,才成功。


關於註冊碼:名字可以是為少於、等於8個的任意字元,若長度大於8則會失敗;儲存在同一目錄下的NewsReactor.ini,

[Registration

相關文章