WinAmp V2.11的序號產生器制分析(一)(初學者必讀) (12千字)

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

WinAmp V2.11的序號產生器制分析(一)(初學者必讀)
    轉載本文請註明出處,並保持文章的完整性。本文不得用於任何商業場合,僅供學習和參考。
作者: Fpc (感謝直接和間接提供幫助的所有朋友)
目的: 分析序號產生器制
工具: SI 4.05, Wdasm 8.93, Hedit 2.0

軟體名稱:WinAmp Ver 2.11
作    者: Nullsoft,Inc.      Apr 13,1999
檔案大小:612KB
編譯器  :VC++ 6.0
授權方式:共享軟體
使用平臺:Win95/98
軟體簡介:著名的MP3播放器

軟體來源:不清楚
下載地址:請到各大專的下載網站自己查詢。


    首先引用 WinAmp (WA) 關於中的一段:“Winamp is shareware, and may be previewed for 14 days. Continued use of Winamp after 14 days requires that you register it. winamp will neither cease functioning nor warn you of the end of the 14 day trial period. It is your responsibility to register.”。說的很明白,也是說到做到。共享軟體作成這樣,都不忍心去CRAK,它也的確沒有任何加密、干擾手段,真是不知該說些什麼。所以本文僅限於CRAK教學之用。
    這個程式我看了三天(“當兵的,你不看我了”,“我都看了三天了”),其中有二天是在跟蹤。程式寫的很規範,也沒有加殼,所以需要的只是耐心。現在讓我給各位分析一下*COUGH*


    1、 在WA選單中選擇“Nullsoft Winamp....”,顯示“About”對話方塊,選“Shareware”->“Enter Registration Info”進入註冊頁面。
    2、 輸入: Name = Fpc ,Reg# = 123456 。“OK”竟未被啟用,應該是每輸入一次就作一次判斷。
    3、 刪除Reg#中的‘6’,^D到SI中,下 bpx hmemcpy(萬能中斷,也是我最常用的);
    4、 F5回來,在Reg#後加‘6’,立刻中斷到SI中,下 BD *阻斷,按F12 N次,發現始終在USER中,最後直接就返回到WA中,有點奇怪;
    5、 還是刪除‘6’,^D到SI中,下 be hmemcpy 啟用中斷;
    6、 再F5回來,仍輸入‘6’,立刻中斷到SI中。這次按F5,一次,二次,三次,返回到WA中。猜想第一次不知做什麼,第二次取 RegisterName(Rn),第三次是取InputedRegisterCode(Irc),OK;
    7、 重複上面5、6兩步,不過這次在SI中按一次F5,N次F12,正確的回到WA領空,地址是 00401F28,向下看 @00401F2E是一個外部的 CALL呼叫(其實是GetDlgItemInt),是取 Irc的。再向下是一個 Call+ Cmp+ Jmp的程式段,這個 CALL 4252D1就是計算註冊碼的核心。要問我是怎麼知道的,就是:靜態分析+動態分析+嘗試+耐心。


    上面簡要介紹了 SI動態攔截方式,若用 WDASM靜態分析需要查詢“Licensed to”字串,它不在 String資源中 :-( ,而是在 DATA中找到。分析的角度不同,得到的結果是類似的,我們當然要多瞭解和嘗試。這個串會在 @00401DA5 處找到,再向上還是對關鍵 Call 4252D1的呼叫。根據這一呼叫返回的註冊成功與否,程式用 wsprintfA列印不同的字串:“LICENSED TO:%s(%d)”或“UNREGISTERED SHAREWARE ......”,最後顯示在About對話方塊中。整個這一段程式碼是在選擇選單的第一項時執行,向上翻頁,記下的它的入口地址:401CCE。
    關閉WA,開啟SI的 Symbol Loader,調入WA,然後 Load,這時在SI設斷:Bpx 401CCE。^D執行 WA。注意SI不會被啟用,除非你選擇了選單的第一項,下面藉助“程式”作分析,希望大家耐心看下去。


;程式段(1),在顯示“About”時執行

* Referenced by a CALL at Addresses:
|:00401B9B  , :00401CB1 
|
:00401CCE 55                      push ebp
:00401CCF 8BEC                    mov ebp, esp
:00401CD1 81EC00060000            sub esp, 00000600
:00401CD7 A050914400              mov al, byte ptr [00449150]        <== 0->al
:00401CDC 53                      push ebx
:00401CDD 56                      push esi
:00401CDE 57                      push edi
:00401CDF 888500FAFFFF            mov byte ptr [ebp+FFFFFA00], al        <== Ebp-600=6DC6F8-600
:00401CE5 B9FF000000              mov ecx, 000000FF
:00401CEA 33C0                    xor eax, eax
:00401CEC 8DBD01FAFFFF            lea edi, dword ptr [ebp+FFFFFA01]    <== 準備將記憶體清0
:00401CF2 F3                      repz
:00401CF3 AB                      stosd
:00401CF4 66AB                    stosw

* Reference To: KERNEL32.GetPrivateProfileStringA, Ord:013Ah
                                  |
:00401CF6 8B3534E14300            mov esi, dword ptr [0043E134]        <== 準備功能呼叫

* Possible StringData Ref from Data Obj ->"winamp.ini"            <== 檔名
                                  |
:00401CFC 687C214400              push 0044217C
:00401D01 AA                      stosb                        <== 到此將Ebp-600到Ebp-200清為0
:00401D02 8D8500FEFFFF            lea eax, dword ptr [ebp+FFFFFE00]    <== 緩衝區1 首址(控制程式碼?)
:00401D08 6800020000              push 00000200                    <== 緩衝區長度為200
:00401D0D BB50914400              mov ebx, 00449150
:00401D12 50                      push eax

* Possible StringData Ref from Data Obj ->"RegisteredTo"
                                  |
:00401D13 BF6C214400              mov edi, 0044216C            <== 讀內容
:00401D18 53                      push ebx
:00401D19 57                      push edi

* Possible StringData Ref from Data Obj ->"WinampRegInfo"
                                  |
:00401D1A 685C214400              push 0044215C                <== 小節名
:00401D1F FFD6                    call esi

    這個Call將註冊資訊(RI)讀到 Ebp-200處,後面以0補足至 Ebp。當然,WA沒有註冊時,RI是不存在的,自己輸入的方式是:
    在WIN9X系統目錄下,找“winamp.ini”檔案並開啟(什麼,沒有?不會自己生成一個!),新增[WinampRegInfo]小節,輸入RI。我的“winamp.ini”相關內容如下:

[WinampRegInfo]
RegisteredTo=Fpc!!!123456

注意RI中,Fpc是Rn,123456是Irc,中間的“!!!”是必須的,在下面的分析中你會找到原因。


:00401D21 80BD00FEFFFF00          cmp byte ptr [ebp+FFFFFE00], 00    <== 檢查RI是否為空
:00401D28 7524                    jne 00401D4E                <== 正確則向下跳
:00401D2A 68A0794500              push 004579A0
:00401D2F 8D8500FEFFFF            lea eax, dword ptr [ebp+FFFFFE00]
:00401D35 6800020000              push 00000200
:00401D3A 50                      push eax
:00401D3B 53                      push ebx
:00401D3C 57                      push edi
:00401D3D FF35E0594500            push dword ptr [004559E0]
:00401D43 FFD6                    call esi                <== 不用關心是在讀什麼
:00401D45 80BD00FEFFFF00          cmp byte ptr [ebp+FFFFFE00], 00
:00401D4C 7466                    je 00401DB4                <== 這裡千萬不能去

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401D28(C)
|
:00401D4E 8D8500FEFFFF            lea eax, dword ptr [ebp+FFFFFE00]<== 緩衝區1 首址

* Possible StringData Ref from Data Obj ->"!!!"
                                  |
:00401D54 6858214400              push 00442158                <== 準備檢查RI中是否含有"!!!"
:00401D59 50                      push eax
:00401D5A E8F1330300              call 00435150                <== 這個Call有意思,錯誤則eax=0,正確則eax指向RI的!!!123456,我這裡返回值是6DC4FB
:00401D5F 59                      pop ecx                    <== 對這個Call的分析見(二)
:00401D60 85C0                    test eax, eax
:00401D62 59                      pop ecx
:00401D63 744F                    je 00401DB4                <== Eax=0就去死
:00401D65 802000                  and byte ptr [eax], 00        <== 使得Rn以'\0'結束
:00401D68 33F6                    xor esi, esi
:00401D6A 83C003                  add eax, 00000003            <== Eax指向Irc:123456

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401D84(U)
|
:00401D6D 8A08                    mov cl, byte ptr [eax]        <== 取一個字元
:00401D6F 80F930                  cmp cl, 30                <== 數字為有效字元,非數字表示完成
:00401D72 7C12                    jl 00401D86
:00401D74 80F939                  cmp cl, 39
:00401D77 7F0D                    jg 00401D86
:00401D79 0FBEC9                  movsx ecx, cl                <== Ecx中無用位清0
:00401D7C 8D14B6                  lea edx, dword ptr [esi+4*esi]    <= Edx=5*Esi
:00401D7F 40                      inc eax                    <== Edx的初始值為0,這三條指令將字串變為數字
:00401D80 8D7451D0                lea esi, dword ptr [ecx+2*edx-30]    <= 很有意思
:00401D84 EBE7                    jmp 00401D6D


  這一段以C來表示:

  char Irc[6]="123456"        /* 當然註冊碼不止6位    */
  long Temp=0;
  int i;

  for( i=0; i<6; i++)
  {
      if( !isdigit( Irc[i] ) )
        break;
    Temp*=10;
    Temp+=Irc[i]-'0';
  }


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401D72(C), :00401D77(C)
|
:00401D86 8D8500FEFFFF            lea eax, dword ptr [ebp+FFFFFE00]<== Eax指向Rn
:00401D8C 50                      push eax
:00401D8D E83F350200              call 004252D1                <== !! 關鍵Call,由Rn計算得出Rc,儲存在Eax。具體分析請看(三)
:00401D92 3BC6                    cmp eax, esi                <== 比較Rc與Irc
:00401D94 59                      pop ecx
:00401D95 751D                    jne 00401DB4                <== 不相等則GAME OVER,大家將此處NOP掉,看會發生什麼有趣的事情
:00401D97 50                      push eax
:00401D98 8D8500FEFFFF            lea eax, dword ptr [ebp+FFFFFE00]
:00401D9E 50                      push eax
:00401D9F 8D8500FAFFFF            lea eax, dword ptr [ebp+FFFFFA00]

* Possible StringData Ref from Data Obj ->"----- LICENSED TO: %s (%d) -----"
                                  |
:00401DA5 6834214400              push 00442134
:00401DAA 50                      push eax

* Reference To: USER32.wsprintfA, Ord:02ACh
                                  |
:00401DAB FF1568E34300            Call dword ptr [0043E368]        <== 列印授權資訊
:00401DB1 83C410                  add esp, 00000010

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401D4C(C), :00401D63(C), :00401D95(C)
|
:00401DB4 80BD00FAFFFF00          cmp byte ptr [ebp+FFFFFA00], 00
:00401DBB 5F                      pop edi
:00401DBC 5E                      pop esi
:00401DBD 5B                      pop ebx
:00401DBE 751C                    jne 00401DDC
:00401DC0 6A00                    push 00000000
:00401DC2 6A00                    push 00000000

* Possible Reference to String Resource ID=00018: "UNREGISTERED SHAREWARE VERSION - PLEASE REGISTER"
                                  |
:00401DC4 6A12                    push 00000012
:00401DC6 E8C5A10200              call 0042BF90
:00401DCB 50                      push eax
:00401DCC 8D8500FAFFFF            lea eax, dword ptr [ebp+FFFFFA00]
:00401DD2 50                      push eax

* Reference To: USER32.wsprintfA, Ord:02ACh
                                  |
:00401DD3 FF1568E34300            Call dword ptr [0043E368]        <== 列印未授權資訊
:00401DD9 83C414                  add esp, 00000014            <== 向下很長一段與分析無關

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401DBE(C)
|
:00401DDC 8D8500FAFFFF            lea eax, dword ptr [ebp+FFFFFA00]
:00401DE2 50                      push eax

* Possible Reference to Dialog: DialogID_009C, CONTROL_ID:0483, "License"
                                  |
:00401DE3 6883040000              push 00000483
:00401DE8 FF7508                  push [ebp+08]

* Reference To: USER32.SetDlgItemTextA, Ord:022Ch
                                  |
:00401DEB FF1534E34300            Call dword ptr [0043E334]
:00401DF1 C9                      leave
:00401DF2 C3                      ret

.........


* Possible StringData Ref from Data Obj ->"!!!"
                                  |
:00401EAC 6858214400              push 00442158
:00401EB1 50                      push eax
:00401EB2 E829330300              call 004351E0
:00401EB7 59                      pop ecx
:00401EB8 8D8580FBFFFF            lea eax, dword ptr [ebp+FFFFFB80]
:00401EBE 59                      pop ecx
:00401EBF 57                      push edi
:00401EC0 50                      push eax
:00401EC1 E8FA330300              call 004352C0
:00401EC6 59                      pop ecx
:00401EC7 8D840580FBFFFF          lea eax, dword ptr [ebp+eax-00000480]
:00401ECE 50                      push eax
:00401ECF 53                      push ebx
:00401ED0 FF7508                  push [ebp+08]
:00401ED3 FFD6                    call esi
:00401ED5 8D8580FBFFFF            lea eax, dword ptr [ebp+FFFFFB80]

* Possible StringData Ref from Data Obj ->"winamp.ini"
                                  |
:00401EDB 687C214400              push 0044217C
:00401EE0 50                      push eax

* Possible StringData Ref from Data Obj ->"RegisteredTo"
                                  |
:00401EE1 686C214400              push 0044216C

* Possible StringData Ref from Data Obj ->"WinampRegInfo"
                                  |
:00401EE6 685C214400              push 0044215C

* Reference To: KERNEL32.WritePrivateProfileStringA, Ord:02E5h
                                  |
:00401EEB FF1530E14300            Call dword ptr [0043E130]            <== 這一段會在點選“OK”時執行,向ini檔案寫RI

* Possible Reference to String Resource ID=00001: "You must first exit Winamp before uninstalling."
                                  |
:00401EF1 6A01                    push 00000001
:00401EF3 FF7508                  push [ebp+08]

* Reference To: USER32.EndDialog, Ord:00B9h
                                  |
:00401EF6 FF151CE44300            Call dword ptr [0043E41C]
:00401EFC 8B7D10                  mov edi, dword ptr [ebp+10]

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

* Possible Reference to Dialog: DialogID_00BF, CONTROL_ID:048B, ""
                                  |
:00401EFF B88B040000              mov eax, 0000048B
:00401F04 663BF8                  cmp di, ax
:00401F07 7405                    je 00401F0E
:00401F09 663BFB                  cmp di, bx
:00401F0C 7552                    jne 00401F60

對關鍵 Call 004252D1的分析請見(三)

相關文章