┌┐┌┐┌┐∞   == 軟體蛀蟲 ==    拷貝心得第三十一集
       ┘└┘└┘└┘   軟體壓縮落伍了    遵守智慧財產權才對
  ================================================================
 ┌───┐
 │ 前言 │
 └───┘
   各位大爺們 ,您是否已發現到目前市面上的軟體都開始使用壓縮軟體來壓
 過他們的軟體 ,使得磁片佔用量減小 ,同時也能防止被破解 ,在這麼多的優
 點下 ,實在令人不得不佩服發明這種小聰明的人 ,不過由於此種做法會使得
 軟體無法做適度修改 ,造成合法使用者以及盜拷者都無法修改軟體 ,這種做
 法雖然對於軟體商能夠延長軟體"售命" ,不過筆者仍不贊成使用此法保護 ,
 因為會解的高手仍有 ,所以這種做法簡直可說只對合法者產生某種程度的障
 礙 ,因此本集就是介紹如何將軟體解壓縮。

   解壓過程只適用 SYMDEB.COM (Assembly Debug) ,對於 DOS 附的DEBUG
 比較不適用 ,因為缺少一些讀寫的特殊功能。
 ------------------------------------------------------------------
 ┌─────────┐
 │ 1-1 EXE 檔的檔頭 │
 └─────────┘
    一般的 EXE 檔有個檔頭 ,約佔用 512 Bytes 或更多 ,它的各位元代表
  的意義如下: (請用pctools看EXE檔的檔頭)
        01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
   01   0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7
   02   8 8 9 9 A A B B C C X X X X X X
   03   X X X....

   0.檔頭一開始都是 4D 5A
   1.總檔案大小除以512後所得餘數
   2.總檔案大小除以512後所得商數加 1
   3.檔案重定位數目
   4.此數*10 = 檔頭大小 ,例如 20h=512bytes
   5.程式所須最小記憶體量(一般為0000)
   6.程式所須最大記憶體量(一般為FFFF)
   7.程式載入後的 SS-DS-10h
   8.程式載入後的 SP 值
   9.字組查驗和 (一般無作用)
   A.程式載入後的 IP
   B.程式載入後的 CS-DS-10h
   C.第一個重定位表距離檔頭的位置
   X.重定位表 (每4Bytes一組)

----------------------------------------------------------------------------
┌───────────┐
│ 1-2  解壓縮 vs DEBUG │
└───────────┘
   由於市面上軟體眾多 ,且大多附有解壓程式 ,所以我們就略過不談 ,只談PKLITE
 商業版 ,相信大家都玩過 PKLITE ,它是一種能將程式壓縮後仍能夠執行的軟體 ,
 其作用類似早期的 LZEXE ,只是它的商業版在存檔時 ,少了幾個重要位元 ,使得軟
 體無法解壓縮 ,因此變成根本無法直接去改它 ,像一些國外來的軟體也用它壓縮 ,
 其作用也只是不想被人改成隨意註冊版 ,可惜的是這樣我們根本不能將該軟體中文
 化 ,所以我們必須找到方法去解壓他。

   且由於壓縮軟體太多總類 ,我們也沒那麼多空間去擺解壓軟體 ,所以用DEBUG去練
 習解壓才是根本的方法 ,不然硬碟遲早會不夠用的。

   以下範例皆使用 SYMDEB ,如您使用 DOS 所附的 DEBUG ,請提早更新 ,以免做到
 假死。

 ---------------------------------------------------------------------------

┌───────────────────┐
│ 2-1  PKLITE 商業版在記憶體的解壓過程 │
└───────────────────┘
    一般人都可能猜出該程式的解壓動做 ,即軟體載入後 ,PKLITE將該程式搬到高一點
  的記憶體 ,然後解壓縮時放到原PKLITE載入時的記憶體位置 ,最後執行 EXE 檔頭重
  定位以及其相關工作的工作 ,然後執行該軟體的主程式。

    EXE 檔內常可見到 CALL 1234:5678 ,其中 1234會跟CS有所變動 ,這就是檔頭重定
  位表的做用 ,例如原始程式是放 CALL XXXX:5678 ,但檔頭會把 XXXX 改為 XXXX+CS
  然後又放回原來的地方 ,變成 CALL XXXX+CS:5678。

    所以我們要在 PKLITE 執行該解檔頭動作時中斷該程式 ,並將尚未被改的原始程式
  寫回磁片 ,存為 DATA.$$$ , 並利用原 PKLITE 的解檔頭的公式 ,將檔頭重定位表算
  出 ,並寫回磁片 ,存為 HEAD.$$$ ,並將檔頭所有該填的資訊填回即可解壓完成。

  被PKLITE壓過的軟體載入記憶體 ---> PKLITE程式將其所有程式碼搬到另一塊
  空白記憶體 ---> 解壓縮 ,並將解出的資料放到原來 PKLITE 剛載入記憶體的
  位置 ---> 解壓檔頭移位表以及將原始程式移位 ---> 進入原始程式(RUN)

  我們就是利用程式將檔頭移位表解壓後 ,中斷其程式 ,並寫回原始程式碼 ,以及利用
  原來PKLITE的移位表運算公式 ,將移位表求出來 ,寫回新檔頭資訊。

┌─────────────┐
│ 2-2  PKLITE 商業版解壓法 │
└─────────────┘
   PKLITE商業版應該有很多不同的版本 ,因為筆者已遇到兩種版本了 ,不過仍然能用
 此法解壓縮 ,只是您必需確認何處才是主程式進入點 ,這點就由各位去猜測了。

 範例:
     筆者以手上的 XRSDOOR.EXE (V2.05) 為例 ,該軟體一開始就被 PKLITE 商業版壓
   過 .尚未被其它軟體壓過 ,不過如您手上的軟體被數種軟體壓過 ,只要最後一道是
   PKLITE 商業版 ,都可用此法。

 G128 ---> T --> G 1D4 暫停一下(然後請確認CS:20D是RETF)
 它的程式應該是

         L2     MOV   ES,DX
                LODSW
                INC   AX
                JZ    L1
                DEC   AX
                JZ    L2
         L3     XCHG  AX,CX
                LODSW
                XCHG  AX,DI
                ADD   ES:[DI],BX
                LOOP  L3
                  .
                  .
         L1       .
                  .
                  .
                RETF


 而我們在 "L2" 處暫停 ,每個版本指令碼相同 ,但 ADDRESS 不同
 記住 BX 值 ,本例值為 19DA
 算出 (CS-BX)*10h = 資料長度 ,本例為2618*10h=26180h ,將末4位放到CX ,其它放
 到BX ,本例 BX=2 ,CX=6180 ,然後取名為 DATA.$$$
 鍵入 "W 19DA:0" ,其中19DA是剛才記住的BX值

 再重新載入並執行到程式進入點 (20D-RETF後的位置)
 抄下 DS.SS.CS.IP.SP
      DS=19CA
      SS=3E0B
      CS=19DA
      IP=0
      SP=80
      SS-DS-10=2431h
      CS-DS-10=0000h

 再重新載入並執行到 "L2" 處暫停 ,找一塊空白記憶體 ,填寫本程式 ,本例小弟找
 9000:0 的記憶體。

 LL1     MOV     ES,DX
         LODSW
         INC     AX
         JZ      LL2
         DEC     AX
         JZ      LL1
         XCHG    AX,CX
 LL3     LODSW
         XCHG    AX,DI
         PUSH    AX
         MOV     AX,ES
         SUB     AX,BX
         MOV     CS:[BP+2],AX
         MOV     CS:[BP],DI
         ADD     BP,4
         POP     AX
         LOOP    LL3
         ADD     DX,0FFF
         JMP     LL1
 LL2     INT     3

 修改 CS:IP 指向剛 KEYIN 的程式
 然後修改 BP 值 ,將 CS:BP 指向另一塊空白記憶體 ,本例 BP=5000h
 再執行這段程式。
 執行完後 ,BP會增加 ,將增加部份算出 ,填入 CX 內 ,並將其它 AX,BX,DX歸零
 然後取名 HEAD.$$$ "W CS:原BP=5000"  ,退回 DOS
 (筆者忘記範例的BP增加多少 ,假設為2434h好了 ,將它除4=90D記下來)

 鍵入
 DEBUG
 -N HEAD.$$$
 -L 120
 看看 CX 值 ,再加 20 ,然後隨意湊個大於CX+20的整數 ??00 ,例如原長 90D ,
 湊整數等於 A00 ,修改 CX=長度(本例CX=A00) ,然後鍵入 "W CS:100" ,回DOS

 COPY/B HEAD.$$$+DATA.$$$ UNPACK.EXE
 用PCTOOLS EDIT "UNPACK.EXE" 將前兩行填入以下資料 ,只填畫線處

    4D 5A XX XX XX XX 0D 09 A0 00 00 00 FF FF 31 24
          ^^^^^ ^^^^^ ^^^^^ ^^^^^             ^^^^^
    80 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00
    ^^^^^       ^^^^^ ^^^^^
 其餘不必動它 ,那裡該填什麼 ,可參考檔頭資料。

 現在您已解壓成功啦 ,試著執行看看結果。
 -------------------------------------------------------------------------
 ┌──────────┐
 │ 3-1  LZEXE 解壓縮  │
 └──────────┘
   筆者認為許多人手上可能沒有PKLITE商業版 ,所以再舉 LZEXE 解壓法

   1.先執行 G2A -->T--> G109 暫停 (然後請確認 CS:155是JMP ,否則表示版本不同)
   2.記住 BX 值
   3.算出(CS-BX)*10h
   4.將第3項的末四位放到CX ,第五位數以前的數字放到BX(不足者補零)
   5.寫回位址為第2項的 BX:0 ,檔名 DATA.$$$ (鍵入"N DATA.$$$" 和 "W ????:0")
   6.重新載入原程式
   7.進入程式進入點  G2A-->T-->G155-->T
   8.抄下 DS.SS.CS.IP.SP.
   9.重新載入原程式
  10.執行 G2A -->T-->G109
  11.找一塊空白記憶體撰寫本程式 ,例如9000:0000
     9000:0000  LODSB
     9000:0001  OR      AL,AL
     9000:0003  JZ      002A
     9000:0005  MOV     AH,00
     9000:0007  ADD     DI,AX
     9000:0009  MOV     AX,DI
     9000:000B  AND     DI,0FFF
     9000:000F  MOV     CL,04
     9000:0011  SHR     AX,CL
     9000:0013  ADD     DX,AX
     9000:0015  MOV     ES,DX
     9000:0017  PUSH    AX
     9000:0018  MOV     AX,ES
     9000:001A  SUB     AX,BX
     9000:001C  MOV     CS:[BP+2],AX
     9000:0020  MOV     CS:[BP+0],DI
     9000:0024  ADD     BP,+04
     9000:0027  POP     AX
     9000:0028  JMP     0000
     9000:002A  LODSW
     9000:002B  OR      AX,AX
     9000:002D  JNZ     0037
     9000:002F  ADD     DX,0FFF
     9000:0033  MOV     ES,DX
     9000:0035  JMP     0000
     9000:0037  CMP     AX,0001
     9000:003A  JNZ     0007
     9000:003C  INT     3
  12.修改BP值 ,使得 9000:BP 指向另一塊空白記憶體 ,如BP=5000h
  13.修改CS.IP使其指向本程式
  14.鍵入"G"執行它
  15.算出BP增加的長度 (現在的BP-5000h)
  16.將第15項之結果放到CX暫存器內
  17.將AX.BX.DX清除為零
  18.存檔 CS:5000h ,檔名HEAD.$$$ (鍵入 "N HEAD.$$$" 和 "W CS:5000")
  19.退回DOS ,鍵入 "DEBUG HEAD.$$$"
  20.鍵入 "L 120"
  21.鍵入 "? CX/4" 和記下此值
  22.將 (CX+20h) ,然後湊整數 ,使之符合大於(CX+20) ,且末兩位數為零
     例如CX+20後  123變成200   567=600    4532=4600     6437=6500
  23.將湊出之整數放到 CX
  24.鍵入 "W CS:100"
  25.退回DOS鍵入 "COPY/B HEAD.$$$+DATA.$$$ UNPACK.EXE"
  26.DIR看看檔案長度 ,除以512byte ,的到的商和餘數轉成16進制
  27.用PCTOOLS填寫 UNPACK.EXE 的前20h bytes
     內容如下:
        4D 5A XX XX XX XX XX XX XX XX 00 00 FF FF XX XX
              ^^^^^ ^^^^^ ^^^^^ ^^^^^             ^^^^^ <-註1至註5

        XX XX 00 00 XX XX XX XX 20 00 00 00 00 00 00 00
        ^^^^^       ^^^^^ ^^^^^                         <-註6至註8

    以下數字皆為低位元/高位元...別稿錯唷!
    註1──填入26項的餘數
    註2──填入26項的商數加1
    註3──填入21項記下的數字
    註4──填入22項湊整數後除以10h ,如A00=A0
    註5──填入 8項的SS-DS-10
    註6──填入 8項的SP
    註7──填入 8項的IP
    註8──填入 8項的CS-DS-10

  好啦 ,解壓成功了 ,試著執行看看。
 -------------------------------------------------------------------------
 結論:
   您是不是學到很多 ,您一定遇過用很多種軟體壓過的軟體吧 ,如先用PKLITE壓過
   再用LZEXE壓過 ,再.....
   您只須管它最後是什麼程式壓縮的 ,您就只須破它最後一道鎖即可 ,不用一道道解
   開和反壓縮。

   筆者文筆不是很好 ,且稍嫌雜亂 ,若您會組合語言 ,會TRACE壓縮軟體 ,多看幾次
   相信不是很難看懂的 ,不然您就實地操作看看。