ZeNiX 发表于 2009-4-14 16:07

可執行檔編碼程式 (.COM 篇)

本帖最后由 zenix 于 2009-4-15 11:19 编辑

原創於 1997 年07月07日, 首次刊於台灣旗標的 Run!PC 雜誌.

有人看了二, 要我把一也發上來. 努力一找, 還真找到了.
這個技巧一不是破解, 而是保護 CRYPTCOM.ASM可執行檔編碼器 (.COM 格式)的編寫教學.
也就是 FSE 的原型. 附件是編排好一點的 PDF

    ┌───────────────────────────────┐
    │ 個人基本資料                                                 │
    ├───────────────────────────────┤
    │ 姓名      : 楊旭峰                                           │
    │ 身份證字號: **********                                       │
    │ 通訊地址: *********************************************    │
    │ 戶籍地址: ***********************************            │
    │ 聯絡電話: (**)***-**** (公司)                              │
    │             (**)***-**** (家裡)                              │
    └───────────────────────────────┘

以上為投稿內容:
本文件內容為 PE2 所打, 未經重新編排修飾.
如蒙採用, 我每個月寫個兩三篇也沒問題....
╒═════╤═══════════════════════════════
│[技術空間]│
╘═════╛

   保護與破解的攻防技巧(一):

   可執行檔編碼程式 (.COM 篇)

                                                            / 楊旭峰
                                                zenix@ms10.hinet.net

      再過幾天就是聖誕節了, 為了和他共渡一段浪漫的假期, 決定儘早把
    這份電腦作業給完成. 我可不希望在浪漫的時刻還在擔心作業交不出來啊!

      真是一個殘酷的聖誕節, 趕剛完的程式還未打上註解, 哦! 明天還要
    上台報告. 時間為什麼總是不夠用? 知識總是學不完.

      回想當初為什麼會寫這個程式呢? 記得從玩電腦開始, 最可怕的工具軟
    體就是 PcTools. V4.XX 版, 相信今天用它的人還很多. 這一類的軟體可
    以讓使用者輕易地修改任何檔案內容. 功力好的人就拿來修改程式碼, 以
    破解軟體的保護, 功力差的人也能利用它來輕易地修改版權畫面. 而當時
    功力差的人居多, 因此隨處可見被篡改的版權畫面. 真不知他們這樣子做
    的樂趣在哪裡?

      有一天出現了一種叫做可執行檔壓縮器. 這種工具軟體可以把可執行
    檔壓縮, 而且壓縮後的檔案仍然可以正常執行. 更令軟體商心動的是, 壓
    縮後的檔案內容是編碼過的, 和原來內容不一樣, 似乎可以有效地防止被人
    『輕易』地篡改. 但是用過的人都知道壓縮檔的內容並未完全加密, 因此
    很多時候部份的檔案內容是和原來相同的. 所以如果我們真正需要的是完
    全編碼而非部份編碼的話, 那麼我們何不來自己做一個編碼器?

      可執行檔編碼器在以前真可謂是『不傳之秘』. 連原理都沒有人願意
    透露, 原因我想大家多少也都知道一些吧! 在此我們先就 .COM 檔來做一
    個編碼器! 在開始之前, 我們必須先來了解一下 .COM 檔. 在此我並不打算
    探入探討像 PSP結構之類的問題, 僅告訴你我們需要的最基本概念即可.

      ┌───────────────────┐
      │.COM 檔的特性:                        │
      ├───────────────────┤
      │(1). 程式載入時: CS=DS=ES=SS          │
      │               IP=100h            │
      │               SP=FFFE            │
      │(2). 檔案長度小於 64 K (10000h Byte)│
      │(3). 沒有檔頭, 也沒有重定位表.      │
      └───────────────────┘

      接下來我們該怎麼著手呢? 先看看一般病毒的作法吧! 如果我們大略
    地看一下被感染檔案的程式碼, 它是像這個樣子.
       ┌────────────────────────────┐
       │Start:                                                │
       │      jmpVirus    ;; 原來這裡的 3 Byte 被改成這樣   │
       │      .....                                           │
       │      .....這裡是原程式                           │
       │      .....                                           │
       │Virus:                                                │
       │      做病毒該做的事 以及 還原最前面的 3 Byte         │
       │      回到 Start                                    │
       └────────────────────────────┘

      病毒為了減少磁碟寫入時間, 會先保留原程式最開頭的 3 - 4 Byte,
      然後僅寫入最開頭的 jmp virus 或 call virus, 然後在 virus 的程式
      碼中再還原程式的最開頭. 可是我們可以不必這麼做.

      為求一切從簡, 我們決定把我們編碼器做出來的檔案分成三部份:
      [跳躍指令(到解碼段)] + [編碼過後的原程式] + [解碼及返回]

      格式訂定如下:
         ┌────────────────────┐
         │Start:                                  │
         │      pusha       ;; 保留暫存器的值   │
         │      jmp Decoder                     │
         │      .....                           │
         │      .....這裡是編碼過後的原程式   │
         │      .....                           │
         │Decoder:                              │
         │      解碼                            │
         │      popa      ;; 還原暫存器的值   │
         │      回到 Start                      │
         └────────────────────┘

      第一部份『jmp Decoder』, 組譯後的程式碼為 0e9h, ????h.
    但這 ????h 的值是多少呢? 沒錯, 就是原程式的總長度.

      第三部份的最後『回到 Start』, 我們要怎麼做呢? 我們知道Start的
    IP值為 100h, 如果我們不要變動任何節區暫存器的話, 那麼我們可以簡單
    地用這兩行來返回 Start
               ┌─────┐
               │push 100h │
               │ret       │
               └─────┘
    再來是解碼部份. 這一部份我們一定要和編碼部份配合, 否則解不回原來
    的碼就糟糕了. 在編碼及解碼方面, 這一次我們使用 XOR 來做.因為一個
    數對另一個值做 XOR 運算兩次的結果會回到原來的值. 底下就是我們寫好
    的第一個可執行檔編碼器:

>> ======== 從下一行開始 ==========

;; ┌─────────────────────────────╖
;; │檔名: CRYPTCOM.ASM可執行檔編碼器 (.COM 格式)            ║
;; │版本: 1.0                                                 ║
;; │日期: 1997-12-20                                          ║
;; │組譯: TASM   CRYPTCOM.ASM                               ║
;; │      TLINK /t CRYPTCOM                                 ║
;; │                                                          ║
;; │作者: 楊旭峰 Zenix Yang   zenix@ms10.hinet.net            ║
;; ╘═════════════════════════════╝
      .MODEL TINY
      .CODE
      .286c

      ORG 100h
Start:
      callShow_Logo            ;; 顯示標題
      callOpen_Input_File      ;; 開啟 IN.COM
      callCreate_Output_File   ;; 建立 OUT.COM
      callRead_Body            ;; 讀取 IN.COM 的內容
      callEncrypt_Body         ;; 對所讀取的內容編碼
      callWrite_Output_File      ;; 產生編碼後的 OUT.COM
      jmp   End_All                ;; 結束了
;;--------------------------------------------
;; 顯示標題
;;--------------------------------------------
Show_Logo PROC near
         movah, 09h
         movdx, OFFSET msg_Logo
         int21h
         ret
endp
;;--------------------------------------------
;; 開啟 IN.COM
;;--------------------------------------------
Open_Input_File PROC near
      movah, 3dh
      moval, 0h                ;; AL=0, 以唯讀方式開啟檔案
      movdx, OFFSET input_file
      int21h
      jc   Error_Open            ;; 開啟檔案失敗嗎?
      movinput_handle, ax      ;; 把 IN.COM 的檔案代碼存起來
      ret
    ;; --------------------------------
    ;;開啟或建立檔案失敗了就來這裡
    ;; --------------------------------
    Error_Open:
      movah, 09h
      movdx, OFFSET msg_error_open
      int21h
      movah, 4ch
      int21h
endp
;;--------------------------------------------
;; 建立 OUT.COM
;;--------------------------------------------
Create_Output_File PROC near
      movah, 3ch
      movcx, 0h
      movdx, OFFSET output_file
      int21h
      jc   error_open         ;; 建立檔案失敗嗎?
      movoutput_handle, ax    ;; 把 OUT.COM 的檔案代碼存起來
      ret
endp
;;--------------------------------------------
;; 讀取 IN.COM 的內容
;;--------------------------------------------
Read_Body PROC near
      movah, 3fh
      movcx, 0F000h               ;; 我們這裡只處理長度小於
                                    ;; 61,440 (0F000h) byte 的檔案
      movdx, OFFSET Encrypted_Body
      movbx, input_handle
      int21h
      movBody_Length, ax   ;; 把檔案長度存起來
      ret
endp
;;--------------------------------------------
;; 對讀取的內容編碼
;;--------------------------------------------
Encrypt_Body PROC near
      movcx, Body_Length
      movbx, OFFSET Encrypted_Body
   encrypt_loop:
      xorbyte ptr , cl
      incbx
      loop encrypt_loop
      ret
endp
;;--------------------------------------------
;; 產生編碼後的 OUT.COM
;;--------------------------------------------
write_output_file PROC near
      mov    ax, body_length            ;;
      mov    word ptr First_Jump+2, ax;; 製造 jmp Decoder 程式碼
      mov    word ptr Decoder+1, ax   ;; 製造 mov cx, 檔案長度

      ;; ---------------------------------------------
      ;; 寫入 OUT.COM 的第一部份 [跳躍指令(到解碼段)]
      ;; ---------------------------------------------
      mov    cx, (OFFSET End_First_Jump) - (OFFSET First_Jump)
      mov    dx, OFFSET First_Jump
      call   Write_File

      ;; ---------------------------------------------
      ;; 寫入 OUT.COM 的第二部份 [編碼過後的原程式]
      ;; ---------------------------------------------
      mov    cx, Body_Length
      mov    dx, OFFSET Encrypted_Body
      call   Write_File

      ;; ---------------------------------------------
      ;; 寫入 OUT.COM 的第三部份 [解碼及返回]
      ;; ---------------------------------------------
      mov    cx, (OFFSET End_Decoder) - (OFFSET Decoder)
      mov    dx, OFFSET Decoder
      call   Write_File
      ret

    ;;--------------
    Write_File:
      movah, 40h
      movbx, output_handle
      int21h
    ret
endp

;;--------------------------------------------
;; 結束了
;;--------------------------------------------
End_all:
         movbx, input_handle      ;;
         movah, 3eh               ;;
         int21h                   ;; 關閉 IN.COM

         movbx, output_handle   ;;
         movah, 3eh               ;;
         int21h                   ;; 關閉 OUT.COM

         movdx, OFFSET msg_Thanks ;;
         movah, 09h               ;;
         int21h                   ;; 結束的訊息

         movah, 4ch               ;;
         int21h                   ;; 程式結束

;;--------------------------------------------
;; 這裡放一些我們要用到的資料
;;--------------------------------------------
input_file   DB 'IN.COM',0      ;;要編碼的檔案名稱
output_file    DB 'OUT.COM',0   ;;編碼後的檔案名稱

input_handle   DW 0               ;;IN.COM的檔案處理代碼
output_handleDW 0               ;;OUT.COM 的檔案處理代碼

body_length    DW 0               ;;IN.COM 的檔案長度

    ;;------------------
    ;; 標題
    ;;------------------
msg_logo   DB 0Ah, 0Dh
         DB "---== My First and Simple .COM EncryptorV0.1 ==---",0ah, 0dh
         DB "------------------------......----",0ah, 0dh
         DB " ... Encrypts IN.COM into OUT.COM",0ah, 0dh
         DB 0Ah, 0dh, '$'

    ;;------------------
    ;; 訊息: 錯誤發生
    ;;------------------
msg_error_open   DB 'Error!', 0AH, 0DH, '$'
    ;;------------------
    ;; 訊息: 編碼成功
    ;;------------------
msg_Thanks       DB 'File: IN.COM was encrypted to OUT.COM', 0AH, 0DH, '$'

;;--------------------------------------------
;; 這裡是 OUT.COM 要用到的程式碼
;;--------------------------------------------
      ;; ----------------------------------------
      ;; OUT.COM 的第一部份 [跳躍指令(到解碼段)]
      ;; ----------------------------------------
First_Jump:
            pusha
            db   0e9h ;; 我們會把這裡改成 OUT.COM 的 jmp Decoder
            DW   0    ;;
End_First_Jump:
      ;; ---------------------------------
      ;; OUT.COM 的第三部份 [解碼及返回]
      ;; ---------------------------------
Decoder:
          movcx, 0;; 我們會把這裡改成 OUT.COM 的 mov cx, 解碼長度
          movsi, (OFFSET End_First_Jump) - (OFFSET First_Jump)
          movdi, 100h
          addsi, di

          ;; -----
          ;; 解碼
          ;; -----
Decrypt_Loop:
          lodsb
          xoral, cl
          stosb
          loop Decrypt_Loop

          popa
          push100h;; 返回位址為 IP=100h
          ret
End_Decoder:

      ;; --------------------------------------
      ;; OUT.COM 的第二部份 [編碼過後的原程式]
      ;; --------------------------------------
Encrypted_Body:

end Start

>> ======== 在上一行結束 ==========

      再來我們希望加上一些反追蹤技巧, 所以把上面程式 OUT.COM 的第三部份
[解碼及返回] 改成如下, 這樣一來如果我們就無法使用 Debug, Symdeb
甚至 Soft-ICE 的 LDR.EXE 載入 OUT.COM 來追蹤程式了.
至於原理, 我們就留待下回再分析了.

>> ======== 從下一行開始 ==========
      ;; ---------------------------------
      ;; OUT.COM 的第三部份 [解碼及返回]
      ;; ---------------------------------
Decoder:
          movcx, 0;; 我們會把這裡改成 OUT.COM 的 mov cx, 解碼長度
          movax, ds
          andax, dx
          movds, ax
          moves, ax
          movdi, si
          addsi, (OFFSET End_First_Jump) - (OFFSET First_Jump)

          ;; -----
          ;; 解碼
          ;; -----
Decrypt_Loop:
          lodsb
          xoral, cl
          stosb
          loop Decrypt_Loop

          popa
          push100h;; 返回位址為 IP=100h
          ret
End_Decoder:

>> ======== 在上一行結束 ==========

   最後附上 CRYPTCOM.COM 有加入反追蹤技巧的版本. 您可以用下列的方法把
   程式還原: (如果你沒有 TASM 的話.)

   DEBUG.EXE < CRYPTCOM.ASC

>> ======== 從下一行開始 ==========
N CRYPTCOM.COM
E 0100 e8 12 00 e8 17 00 e8 2e 00 e8 3b 00 e8 4a 00 e8 54 00 e9 80 00 b4 09
E 0117 ba c5 01 cd 21 c3 b4 3d b0 00 ba b0 01 cd 21 72 04 a3 bf 01 c3 b4 09
E 012e ba 59 02 cd 21 b4 4c cd 21 b4 3c b9 00 00 ba b7 01 cd 21 72 e9 a3 c1
E 0145 01 c3 b4 3f b9 00 f0 ba a9 02 8b 1e bf 01 cd 21 a3 c3 01 c3 8b 0e c3
E 015c 01 bb a9 02 30 0f 43 e2 fb c3 a1 c3 01 a3 8c 02 a3 8f 02 b9 04 00 ba
E 0173 8a 02 e8 14 00 8b 0e c3 01 ba a9 02 e8 0a 00 b9 1b 00 ba 8e 02 e8 01
E 018a 00 c3 b4 40 8b 1e c1 01 cd 21 c3 8b 1e bf 01 b4 3e cd 21 8b 1e c1 01
E 01a1 b4 3e cd 21 ba 62 02 b4 09 cd 21 b4 4c cd 21 49 4e 2e 43 4f 4d 00 4f
E 01b8 55 54 2e 43 4f 4d 00 00 00 00 00 00 00 0a 0d 2d 2d 2d 3d 3d 20 4d 79
E 01cf 20 46 69 72 73 74 20 61 6e 64 20 53 69 6d 70 6c 65 20 2e 43 4f 4d 20
E 01e6 45 6e 63 72 79 70 74 6f 72 20 20 56 30 2e 31 20 3d 3d 2d 2d 2d 0a 0d
E 01fd 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d
E 0214 2d 2e 2e 2e 5b 5a 65 4e 69 58 20 6f 66 20 70 43 45 20 27 39 38 5d 2e
E 022b 2e 2e 2d 2d 2d 2d 0a 0d 20 2e 2e 2e 20 45 6e 63 72 79 70 74 73 20 49
E 0242 4e 2e 43 4f 4d 20 69 6e 74 6f 20 4f 55 54 2e 43 4f 4d 0a 0d 0a 0d 24
E 0259 45 72 72 6f 72 21 0a 0d 24 46 69 6c 65 3a 20 49 4e 2e 43 4f 4d 20 77
E 0270 61 73 20 65 6e 63 72 79 70 74 65 64 20 74 6f 20 4f 55 54 2e 43 4f 4d
E 0287 0a 0d 24 60 e9 00 00 b9 00 00 8c d8 23 c2 8e d8 8e c0 8b fe 83 c6 04
E 029e ac 32 c1 aa e2 fa 61 68 00 01 c3
RCX
01a9

w
q

>> ======== 在上一行結束 ==========

╒═════╤═══════════════════════════════
│[文件結束]│
╘═════╛
                                                            / 楊旭峰
                                                zenix@ms10.hinet.net

winmemory 发表于 2009-4-14 22:47

帮LZ顶了,支持原创技术

liyan4168 发表于 2009-4-15 07:52

什么啊!!!没看懂....

Hmily 发表于 2009-4-15 13:33

感谢zenix发布DOS下脱壳文章,加精华鼓励!
页: [1]
查看完整版本: 可執行檔編碼程式 (.COM 篇)