再貼一篇,進階篇,比上面那個難一些!! (6千字)

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

原著:raZZia
翻譯:TAE![CCG]
軟體 2: Ize 2.04 from Gadgetware
    Ize from Gadgetware 是一個聰明得小程式,它放了一雙眼睛在你的螢幕上,
    它會跟著你的滑鼠得,它有輸入姓名和註冊碼得註冊功,對付這個軟體得策略
    仍然和上面一個相同:找出我們輸入的資料在記憶體中的地址。

    步驟 1: 執行 Ize。 選擇註冊並輸入名字和註冊碼,我用的是:“razzia”
    和“12345”

    步驟 2: 進入 (CTRL-D) Softice 並且在 GetDlgItemTextA 上設定斷點。

    步驟 3: 離開 SoftIce 並且點選 OK。這將帶你回到 Softice。你將回到函式
    GetDlgItemTextA 中。按 F11 離開函式,你將看到以下程式碼:
 
    mov esi, [esp + 0C]
    push 00000064
    push 0040C3A0      ;<--我們的姓名被存放到記憶體中的這個地方!
    mov edi, [USER32!GetDlgItemTextA]  ;<-- edi 載入一個地址 GetDlgItemTextA
    push 00004EE9
    push esi
    call edi          ;<-- 執行 GetDlgItemTextA
    push 00000064            ;<-- (你應該在這裡)
    push 0040C210      ;<--我們輸入的註冊碼將被放在這裡
    push 00004EEA
    push esi
    call edi          ;<-- 再次執行 GetDlgItemTextA

    我們看見在這個程式碼段中函式 GetDlgItemTextA 被呼叫了兩次,第一次的Call已經
    執行,用 ED 40C3A0 命令我們可以看到我們在記憶體中的名字。
 
    我們跟著程式來讀取輸入的註冊碼把,鍵入 G 並且回車,現在,我們再一次回到函式
    GetDlgItemTextA 中現在按F11離開這裡,我們看看記憶體 40C210 中有啥,哦,是我們
    輸入的註冊碼!
 
    現在我們知道我們輸入的名字和註冊碼被放到哪裡了,我們把這些記下來!
     
    步驟 4: 好的, 下一步呢?我們已經知道了名字和假註冊碼被放在哪裡了,我們需要
    找出程式用這些資料幹了什麼,通常我們需要下斷點來找出程式的那些地方讀取了這
    些數,可是在這裡卻不需要。
    看看以下程式碼段:

    push 0040C210  ;<--儲存我們輸入的註冊碼 (作為下面那個Call的引數)
    call 00404490  ;<--這個CALL是幹什麼的呢?
    add esp, 00000004
    mov edi, eax  ;<-- 儲存 EAX  (hmmmm)

    我們進如Call看看它究竟做了什麼,但那是不必的,依你經驗來猜猜看這個Call是幹什麼
    的?它計算假註冊碼放入了 EAX。我們繼續按 F10直到我們透過了CAll並且看看 EAX 中
    的內容,用 ?EAX 命令,在我這裡它顯示:00003039    0000012345 "09"。

    知道了 EDI 是我們的註冊碼,那麼我們繼續:

    push 0040C3A0 ;<-- 儲存我們輸入的名字 (作為下個Call的引數)
    push 00409080 ;<-- 儲存我們未知的記憶體地址 (作為下個Call的引數)
    call 004043B0 ;<-- 執行我們未知的函式
    add esp, 00000008
    cmp edi, eax  ;<--比較 EDI (我們輸入的註冊碼) 和 EAX (未知)
    jne 004018A1  ;<--如果不同就跳

    我們看見那個CALL有兩個入口引數。一個是我們輸入名字的地址,另一個我們還不知道是什麼,
    但我們可以用ED 409080 找到它。我們看見文字‘Ize’。這個函式使用這兩個引數計算正確的
    註冊碼,如果你僅僅想破解它,那麼你只需要在Call後面設定斷點,然後檢視EAX的值就可以了
    它將顯示出正確的註冊碼來,但我們想知道它是如何計算出註冊碼的,所以我們跟蹤到函式內部
    之後,我們將在那兒試著找到 EAX 的內容。
   
    步驟 5: 一旦進入了有趣的函式你將發現一段相當長的執行過程。對我們來說是不需要的,不需
    要列出完整的CALL清單,因為這對我們做註冊碼來說根本不需要。

    但是一旦找出哪一部分是對我們計算註冊碼所必要的時候,你就應該一步步的跟蹤它,並且仔細
    的把它記下來!

    在做了這些以後,我們發現函式的第一部分計算出一些“Key”,之後這些“key”被儲存在記憶體
    中並且帶入了函式的第二部分。

    函式的第二部分計算出正確的註冊碼,但這是基於“Key”和我們輸入的名字的!

    下面的程式碼是我們作出序號產生器所必要的:

    (在跟蹤下面程式碼之前不要忘了記錄,使用到的暫存器是下面幾個:EBX指向我們輸入名字的第一
    個字母,EDX 是 0,EBP 是 0,至於“key”我們說得簡單些,它存放在記憶體地址 0040B828 中,
    它最初的值是 0xA4CC )

    :00404425 movsx byte ptr edi, [ebx + edx]  ;<-- 把名字的第一個字母放入 EDI
    :00404429 lea esi, [edx+01] ;<-- ESI 是字母所在的位數 "letter-number"
    :0040442C call 00404470  ;<-- 一個Call
    :00404431 imul edi, eax  ;<-- EDI=EDI*EAX (EAX是上面那個CAll返回的一個值)
    :00404434 call 00404470  ;<-- 又Call了
    :00404439 mov edx, esi
    :0040443B mov ecx, FFFFFFFF
    :00404440 imul edi, eax  ;<-- EDI=EDI*EAX (EAX是上面那個call返回的一個值)
    :00404443 imul edi, esi  ;<-- EDI=EDI*ESI (ESI 是字母所在的位數)
    :00404446 add ebp, edi  ;<-- EBP=EBP+EDI  (注意 EBP 最後將是我們正確的註冊碼)
    :00404448 mov edi, ebx  ;<--這幾行計算我們輸入名字的長度
    :0040444A sub eax, eax  ;<--這幾行計算我們輸入名字的長度
    :0040444C repnz        ;<--這幾行計算我們輸入名字的長度
    :0040444D scasb        ;<--這幾行計算我們輸入名字的長度
    :0040444E not ecx      ;<--這幾行計算我們輸入名字的長度
    :00404450 dec ecx      ;<-- ECX 現在已經是我們名字的長度了
    :00404451 cmp ecx, esi
    :00404453 ja 00404425  ;<-- 如果不是最後一個字母,繼續迴圈
    :00404455 mov eax, ebp  ;<--  將 EBP 存入 EAX !!!!
    :00404457 pop ebp
    :00404458 pop edi
    :00404459 pop esi
    :0040445A pop ebx
    :0040445B ret
           
    去看看那個CALL是幹什麼的!
   
    :00404470 mov eax, [0040B828]    ;<-- "key" 放入 EAX
    :00404475 mul eax, eax, 015A4E35  ;<-- EAX=EAX * 15A4E35
    :0040447B inc eax                ;<-- EAX=EAX + 1
    :0040447C mov [0040B828], eax    ;<-- 用EAX中的值代替 "key"
    :00404481 and eax, 7FFF0000      ;<-- EAX=EAX && 7FFF0000
    :00404486 shr eax, 10            ;<-- EAX=EAX >>10
    :00404489 ret

    上面的迴圈程式碼對我們輸入名字的所有字母進行了操作。每個字母計算出了一些值,所有的這些
    值相加到 EBP 中,之後這個值放入了 EAX,並且函式返回 EAX,那就是我們要找的,我們要知道
    的是 EAX 是如何得到這個值的!

    步驟 6: 可以做序號產生器了,我們把上面那段計算註冊碼的程式碼翻譯為 C 語言吧!
    下面就是序號產生器程式碼: (注意:我是一個差勁的 C 程式設計師 :)

    #include <stdio.h>
    #include <string.h>
    main() {
        char Name[100];
        int NameLength,Offset;
        unsigned long Letter,DummyA;
        unsigned long Key = 0xa4cc;
        unsigned long Number = 0;
        printf("Ize 2.04 crack by razzia\n");
        printf("Enter your name: ");
        gets(Name);
        NameLength=strlen(Name);
        for (Offset=0;Offset<NameLength;Offset=Offset+1) {
          Letter=Name[Offset];
          DummyA=Key;
          DummyA=DummyA*0x15a4e35;
          DummyA=DummyA+1;
          Key=DummyA;
          DummyA=DummyA & 0x7fff0000;
          DummyA=DummyA >> 0x10;
          Letter=Letter*DummyA;
          DummyA=Key;
          DummyA=DummyA*0x15a4e35;
          DummyA=DummyA+1;
          Key=DummyA;
          DummyA=DummyA & 0x7fff0000;
          DummyA=DummyA >> 0x10;
          Letter=Letter*DummyA;
          Letter=Letter*(Offset+1);
          Number=Number+Letter;
        }
        printf("\nYour registration number is : %lu\n",Number);
    }

相關文章