Soft-ICE使用說明及實例——破解ACDSee

http://blog.csdn.net/he_rong/archive/2004/06/25/25906.aspx

 

爲了以後說話方便, 這裏把 Soft-ICE 的一些簡單使用方法說一下, 以免不通 E 文的同志們找不到中文的 Soft-ICE 說明而抓瞎. 

  Soft-ICE 由三部分 (以後說的 Soft-ICE, 如果不加特殊說明, 均指 Soft-ICE for Windows 95 的 2.0 版本以上) 組成: WINICE.EXE, WLDR.EXE (在 3.0 中這個文件叫做 LOADER32.EXE) 和顯示驅動程序 SIWVID.386. 

  另外, Soft-ICE 在啓動的時候要裝入一些 DLL/EXE 的函數名信息, 你必須手工指定這些 DLL, 按照: 

  exp=d:/path/name.ext 

  的格式寫在 WINICE.DAT 文件裏. 本文附錄裏面有俺用的 WINICE.DAT, 你可以直接用起來, 省得自己寫那麼多行了. 注意, 一定要把下面幾行包括進去, 否則 WINICE 可能什麼東東也攔不到: 

  exp=c:/win95/system/kernel32.dll
  exp=c:/win95/system/user32.dll
  exp=c:/win95/system/gdi32.dll
  exp=c:/win95/system/comctl32.dll


  一般我們使用 WLDR (以後把 LOADER32 也稱爲 WLDR) 來裝入一個 EXE 文件或者一個 DLL 文件, 大多數的時候, 我們也可以直接執行 EXE 文件, 通過跟蹤它的各種消息來找到它. 啓動 WINICE 的熱鍵是 Ctrl+D. 先介紹常規的辦法: 

  啓動 WLDR, 然後選擇你要跟的程序, 單擊 Load 按鈕, 屏幕上一陣亂閃後就進入了文本模式, 這就是 Soft-ICE 的跟蹤界面, 雖然簡單了點, 但是很友好. 可以像 DOSKEY 那樣用光標上下鍵重複上次輸入的內容, 也可以輸入上次輸入內容的一部分, 然後按光標上鍵, 上次輸入內容就完整地貼了出來. 

  一般情況下, 如果裝入的一個 NE 程序, WINICE 會直接找到它的入口點, 並且把當前的光標定在 EXE 的頭一條指令上; 如果是 PE 程序, WINICE 會停在一個 INVALID 區, 按下 F10 後可以到 EXE 頭部. 

  比較重要的功能鍵: 

  (1) F10 : 單步執行; CALL, INT 會被跳過;
  (2) F8 : 單步執行; CALL, INT 會被切入;
  (3) F4 : 查看程序畫面;
  (4) F11 : 對於 CALL 形式的子程序, 直接執行完畢, 在 RET(F) 之後
       回到 CALL 的下一條指令;


  比較重要的幾條命令是: 

  (1) G: 執行程序, 後面如果加地址, 則執行到該地址爲止, 比如: 

    2400:0480    MOV   AH,30
    2400:0482    INT   21
    2400:0484    CMP   AL,09
    2400:0486    JZ   04F9
    2400:0488    MOV   AH,4C
    2400:048A    INT   21


  假設當前 CS:IP (EIP) 爲 2400:0480, 如果我們直接執行 G, 就會一 G 到底, 直接回到命令行, 原因在於 DOS 9.00 還沒出, 程序直接執行 DOS TERMINATE 功能了. 

  如果你把 DOS 版本號先改成 9.0, 然後執行 G 4F9, WINICE 會跑到 2400:04F9 處然後停下; 但是如果你不改版本號也這麼來一把, 就也會一 G 到底, 回到命令行了, 因爲 2400:04F9 在這種情況下永遠不會被執行到. 

  (2) P: 單步執行程序; 只執行 P 時, 相當於按下 F10 鍵; 如果後面加入 P RET 參數, 會執行到最近的一條 RET/RETF 處, 注意, IRET 會被忽略, 所以要小心; 

  (3) T: 相當於按下 F8; 

  (4) BPINT: 設置中斷斷點; 格式爲 "BPINT 中斷號 [條件]"; 在 WINICE 2.0 裏面, 條件只能是下面三種之一: 

    bpint xx ah=ab
    bpint xx al=cd
    bpint xx ax=abcd


  而在 WINICE 3.0 裏面, 條件的格式豐富得多: 

    bpint xx if ah==ab
    bpint xx if al==cd
    bpint xx if ax==abcd
    bpint xx if dx->4==abcd (當 DS:DX+4 處的值爲 0xabcd 的時候)


  還有一些, 不是很常用, 等到用的時候再說吧.       ;) 

  (5) BPX: 設置執行地址斷點; 格式爲 "BPX 地址", 還以上面的例子來說, 假如我們執行 bpx 486, 然後來一把 G 4F9 的話就不會一 G 到底了, 因爲我們在那個判斷處設了斷點, WINICE 會執行到 2400:0486 然後停下; 

  BPX 的第二種用法是我們這個教程的關鍵, 格式爲 "BPX 函數名". 這個函數名可以是任意一個 Windows API 函數, 虛擬機指令, DLL 的引出函數等等. 功能強勁. 比如說, 我們先啓動 Notepad, 然後在裏面隨便敲些東東, 然後按 Ctrl+D, 執行: 

  :bpx messageboxa     (不用區分大小寫)
  :g


  然後關閉 Notepad, 這時 WINICE 會被激活, 原因是斷點條件已經符合了. Notepad 此時將彈出一個消息框提醒你存盤, 這就進入了 MessageBox 函數, 後面加一個 A 是由於我們現在在 Windows 95 裏面, 函數是區分字符集的. A 表示 ANSI, W 表示 Wide, 即 Unicode (Wide character-set). 

  (6) BPM: 設置內存訪問斷點; 格式爲 "BPM 地址"; 比如: BPM F000:E6F9 會把斷點設在內存中存放 BIOS 更新版本號的內存位置上, 只要有程序訪問這個地址, WINICE 就會激活. 另外, 還可以單獨設定對地址的訪問條件: 讀(R)/寫(W)/執行(X). 只需要在後面把對應的字母加上就可以了. 以那段 "一 G 到底" 爲例: 

  :bpm 2400:0486 x
  :g


  和 bpx 486 的效果完全一樣. 

  (7) BMSG: 跟蹤 Windows 消息; 格式爲 "BMSG 消息名"; 比如我們執行 Notepad, 然後 Ctrl+D 激活 WINICE, 輸入: 

  :bmsg wm_char
  :g


  然後回到 Notepad 中, 隨便按一個鍵, WINICE 就激活了; 原因在於我們在按鍵消息上設置了斷點. 

  (8) BL: 列出所有的斷點; 格式爲 "BL"; 它會把所有斷點按從 0 開始的編號列出; 

  (9) BC: 清除斷點; 格式爲 "BC 斷點編號", 這個編號就是 BL 列出的那個; 還有一種格式爲 "BC *", 作用是清除所有的斷點. 

  (10) RFL: 改變標誌字; 格式爲 "RFL 標誌位"; 比如當前 Z 標誌位 (零位) 爲置位狀態, 執行 "rfl z" 之後會被清楚; 如果 C 標誌位爲清除狀態, 那麼 "rfl c" 將使之置位; 比如: 

  :g 486
  :rfl z
  :g 4f9


  這次就真可以 G 到 4F9 處了. 

  (11) A: 進入 WINICE 小彙編狀態; 格式爲 "A 地址"; 也可以不加地址值, 直接在當前 CS:IP 處彙編; 舉例: 

  :g 486
  :a 2400:0486
  2400:0486 jnz 4f9
  2400:0488 (按下 ENTER 鍵結束彙編)
  :g 4f9


  也可以 G 到 4F9 處. 

  (12) E: 進入 WINICE 內存修改狀態; 格式爲 "E 地址"; 也可以不加地址值, 直接在 DS:DX 處修改; 比如: 

  :e 2400:0485
  2400:0485 - 09 74 XX XX XX XX XX XX XX XX XX XX XX XX XX XX
  (光標停在 09 下面, 我們輸入 09 EB, 然後按 ENTER 結束脩改)
  2400:0485 - 09 EB XX XX XX XX XX XX XX XX XX XX XX XX XX XX


  然後 "g 4f9" 就可以到 4F9 啦; 我們輸入的 EB 是 JMP 的機器碼. 

  (13) U: 反彙編; 格式爲 "U 地址", 也可以不加地址, 直接在當前 CS:IP (EIP) 往後反彙編 12 行 (行數由 CODE 欄的寬度而定). 

  (14) R: 更改寄存器的值; 格式爲 "R 寄存器名=值", 也可以不加值, WINICE 會讓你在最上面的寄存器區直接修改, 用光標鍵可以在各個位之間移動. 比如: 

  :g 484
  :r ax=0009
  :g 4f9


  就到了 4F9 處. 

  基本的指令就是這 14 條, 如果需要, 俺隨時補充就是了. 




--- 如何拆解 ACDSee '95
  ACDSee '95 是一個速度快, 功能強, 格式多, BUG 少的 ...... 圖形瀏覽程序 (不要想歪了哦! ;) . 我們以 ACDSee '95 1.0 正式版爲例來看看此類程序如何拆解. 

  首先是得到該程序, 該程序可以在: 

  http://www.acdsys.com/download.htm(l) 

  下獲得. 也可以在國內的許多 BBS 上得到. 注意: 我們需要它的正式版, 否則許多地址值是錯誤的, 但是原理是一樣的. 

  首先用用這個程序, 當你看了 30 張左右的圖片之後, 該程序就開始頻繁提醒你註冊了. 另外, 在程序的 About 對話框裏面也有 Register 按鈕讓你註冊. 我們來試試註冊會是什麼樣子. 在 Register 對話框裏面隨便輸入一個名字, 比如 "eGIS - pCE '97" 吧, 然後按 Tab 鍵跑到 Code 欄裏面輸入個數字, 比如 12345, 然後按回車鍵. 

  啊! ACDSee '95 彈出一隻 Message Box, 告訴你輸入的號是無效的.   :..( 

  讓我們來想想看這種判斷應該是如何工作的, 這不難猜: 

  (1) 取得用戶輸入的名字;
  (2) 取得用戶輸入的註冊號;
  (3) 使用某種算法檢驗;
  (4) 判斷是否合法;
  (5) 如果合法就顯示註冊消息;
  (6) 如果非法就彈出一隻 Message Box.


  好. 知道了這個就好辦. 我們不難看到, 拆解的關鍵在於上面的第四步, 如何令這個程序認爲你輸入的號是正確的. 從那個 "一 G 到底" 程序裏面我們應該學到些簡單的拆解經驗, 在那個例子裏, 我們就是改動了一個 Z 標誌來達到可以 G 到 4F9 的目的的, 這個方法是 "普適" 的, 也適於這個比較複雜的例子. 

  現在我們開始拆. 首先我們猜想 ACDSEE 使用了 GetWindowTextA 函數來取得用戶輸入的信息, 來試試: 

  :bpx getwindowtexta
  :g 

  然後再按 ENTER 鍵, 結果 WINICE 沒有被激活. 爲什麼呢? 原因是很簡單的啦, 就是 ACDSEE 沒用這個函數. 那用的是什麼呢? 不難想到 GetDlgItemTextA 函數, 再跟一次看看: 

  :bc *
  :bpx getdlgitemtexta
  :g 

  按下回車鍵. Bingo! WINICE 被激活了! 好, 說明我們跟的函數是對的. 但是不要着急, 我們輸入了兩條信息: 名字和註冊號, 因此這個函數會被執行兩次, 所以我們繼續 G 一下. 果然, WINICE 又攔到一個 GetDlgItemTextA 函數: 

  USER32! GetDlgItemTextA
  --------------------------------------------------------------
  0137:BFF61657    MOV   CL,96
  0137:BFF61659    PUSH  EBP
  0137:BFF6165A    MOV   EBP,ESP
  0137:BFF6165C    PUSH  ECX
  0137:BFF6165D    SUB   ESP,3C
  0137:BFF61660    PUSH  WORD PTR [EBP+08]


  好了好了, 就 A 到這兒吧. 反正 WINICE 攔到了這個函數, 並且我們不關心它是如何得到那些數據的, 我們直接走完這個函數. 按下 F11 鍵就回到了調用它的地方: 

  0137:004016FB    CALL  EDI  ; 我們就是從這裏回來的    ;)
  0137:004016FD    XOR   DI,DI ; 當前 EIP
  0137:00401700    LEA   EBX,[ESP+18]
  0137:00401704    CMP   BYTE [ESP+18],0
  0137:00401709    JZ   401723
  0137:0040170B    MOVSX  EAX,BYTE PTR [EBX]


  我們看到 EIP 下面兩條指令都和 [ESP+18] 有關. 究竟 [ESP+18] 處是什麼東東呢? 我們來 DUMP 一下它: 

  :d esp+18 

  哈哈! 原來 [ESP+18] 處存的是你輸入的姓名. 那麼下面一條 CMP 的作用很明顯了, 它是判斷你是不是什麼都沒有輸入, 我們既然輸入了姓名, 就可以狠光明正大地 G 到地址 40170B 去也. 

  接下來的一段是: 

  0137:0040170E   PUSH  EAX
  0137:0040170F   CALL  0045A230
  0137:00401714   ADD   ESP,04
  0137:00401717   TEST  EAX,EAX
  0137:00401719   JZ   0040171D
  0137:0040171B   INC   DI
  0137:0040171D   INC   EBX
  0137:0040171E   CMP   BYTE PTE [EBX],0
  0137:00401721   JNZ   0040170B
  0137:00401723   CMP   DI,05
  0137:00401727   JL   00401841
  0137:0040172D   LEA   EAX,[ESP+38]    ; 註冊號
  0137:00401731   LEA   EAX,[ESP+18]    ; 名字
  0137:00401735   PUSH  EAX
  0137:00401736   PUSH  ECX
  0137:00401737   PUSH  0047A128
  0137:0040173C   CALL  00403560
  0137:00401741   ADD   ESP,0C
  0137:00401744   CMP   EAX,01
  0137:00401747   SBB   EAX,EAX
  0137:00401749   INC   EAX
  0137:0040174A   TEST  EAX,EAX
  0137:0040174C   JL   00401841
  0137:00401752   LEA   EAX,[ESP+14]
  0137:00401756   LEA   ECX,[ESP+0C]
  0137:0040175A   PUSH  EAX
  0137:0040175B   PUSH  ECX 

  A 了這麼長, 我們來看看這段代碼的用處吧. 首先我們看到在 40172D 這個地方用到了 [ESP+38] 和 [ESP+18], 經過 DUMP 知道, [ESP+38] 存放着我們輸入的序列號, 那麼上面的一個循環就可以直接跳過來了. 輸入: 

  :g 40173c 

  然後有一個 CALL, 它前面的幾個壓棧函數壓進去的是名字和序號, 然後經過一個子程序, 然後下面又有判斷, 那麼這個 CALL 極有可能是判斷名字和序號是否符合的子程序, 我們來看看是不是這麼回事. 先 G 到下面的判斷處: 

  :g 401744 

  我們看到這時候 EAX 是 0, 然後繼續走到 40174C, 發現它是要跳到 401841 執行程序. 我們繼續走, 發現那個破框彈了出來. 之前的唯一一個分支就是在 40174C 這個地方了, 因此我們重複上面的步驟跟到 401744, 將 EAX 改成 1, 到 40174C 的時候繼續執行下一條指令. 我們來 G 一把. 

  Bingo!!! ACDSee '95 告訴我們它已經被註冊了. 但是別得意, 看看它的標題欄吧, 還是 [unregistered] 的呢.     :( 

  這是怎麼回事呢? 不難想到, 還有別的判斷. 是不是還要跟呢? 不用啦. 既然我們找到了判斷子程序, 就可以直接改它了. 根據我們上次的經驗, EAX 是 1 的時候表示名字和註冊號是相符的, 那麼我們把程序的返回值 EAX 改成恆爲 1 就可以了. 

  重新跟蹤, 到 40173C 這個地方按下 F8 開始. 然後進入程序, 我們用 Ctrl+光標下鍵移動代碼, 到 4035FE 處: 

  0137:004035FE    TEST  EAX,EAX
  0137:00403600    MOV   EAX,00000001
  0137:00403605    JZ   00403609
  0137:00403607    XOR   EAX,EAX
  0137:00403609    POP   EDI
  0137:0040360A    POP   ESI
  0137:0040360B    POP   EBX
  0137:0040360C    ADD   ESP,4
  0137:00403612    RET 

  如果你有一些反彙編 C++ 編譯出的 EXE 的經驗, 就可以看到這實際是: 

  return ( fValid ? 1 : 0 ) ; 

  的編譯結果. 可以看到, 403605 是一個關鍵的地方, 就是這條該死的指令把可愛的 EAX 弄成了 0. 知道這個就好辦啦. 我們把它跳過去: 

  :a 403605
  0137:00403605 jmp 403609
  0137:00403607 (按回車結束彙編)


  然後重新執行一次. HOHOHO! 這次標題欄也變成註冊版的樣子啦. 拆解工作完畢! 

  回到 DOS 下, 把我們的成果寫回 EXE 裏面就可以了. 剛纔我們改的是把 JZ 改成了 JMP, 也就是說, 把 

  B8 01 00 00 00 74 02 33 C0 5F 5E 5B
  改成了:
  B8 01 00 00 00 EB 02 33 C0 5F 5E 5B
  (黑話叫做 "74 改 EB"   :)


  爲什麼要把要查找的串弄得這麼長呢? 直接把 "74 02" 替換成 "EB 02" 不就得了嗎? 不是這樣的. 在 EXE 裏面可能有許多個 "74 02", 因爲 JMP $+2 是一條太普通不過的指令了, 而一個 EXE 裏面有多個 

    MOV   EAX,00000001
    JNZ   @HERE+2
    XOR   EAX,EAX
    POP   EDI
    POP   ESI
    POP   EBX


  的機會就少得多了, 一般情況下只有一處, 這就是我們要改的一處. 

  把特徵串取得太長了也沒什麼實際意義, 反而會浪費地球上有限的紙資源, 地球只有一個, 是我們的家, 我們要愛護它 ...... 

  好, 用一個你喜愛的二進制文件編輯器改掉它. 然後重新執行 ACDSee '95. 

  酷! 大功告成啦. 趕緊找個 MM 吹噓一把 ......   ;) 

  爽過以後總結一下經驗, 下次泡的時候就是老槍了: 

  經驗一: 你現在知道了註冊碼的判斷步驟;
  經驗二: 你知道了程序可以用 GetDlgItemText 和 GetWindowText 取得
      在 Edit Box 中用戶輸入的數據;
  經驗三: 你知道了程序判斷註冊號是否合法的地方可能不止一處, 但是一
      般都用同一個子程序來完成檢驗功能;
  經驗四: 你知道了取得替換碼的一些原則: 即 --- 不要太長也不要太短.

發佈了23 篇原創文章 · 獲贊 2 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章