BCB 編譯器支援 __emit__ 伪指令,可將機器碼直接嵌入代碼流。利用 x86 IN AL, DX / OUT DX, AL 指令,繞過 Windows HAL,直接操作 I/O 埠。**僅適用於 32 位應用運行於 Windows 9x/NT 虛擬機監控器允許的情況** (需 IOPL=3 或 VxD 支援),**Windows 2000/XP/後續版本預設禁止 Ring 3 I/O 指令,會觸發 EXCEPTION_PRIV_INSTRUCTION 崩潰**。
// 讀取 8 位端口
unsigned char __fastcall inportb(unsigned short port) {
unsigned char value;
__emit__(0x8B, 0x95, &port); // MOV EDX, [EBP+offset(port)] — 將端口地址載入 EDX
__emit__(0x66, 0xEC); // IN AL, DX — 從端口讀入 AL
__emit__(0x88, 0x85, &value); // MOV [EBP+offset(value)], AL — 存回 value
return value;
}
// 寫入 8 位端口
void __fastcall outportb(unsigned short port, unsigned char value) {
__emit__(0x8B, 0x95, &port); // MOV EDX, port
__emit__(0x8A, 0x85, &value); // MOV AL, value
__emit__(0x66, 0xEE); // OUT DX, AL
}
| 位元組 | 指令 | 說明 |
|---|---|---|
| 8B 95 xx xx | MOV EDX, [EBP+disp] | 從堆疊框架取 port 參數到 EDX |
| 66 EC | IN AL, DX | 操作數前綴 0x66 (16位) + IN AL,DX |
| 88 85 xx xx | MOV [EBP+disp], AL | 將 AL 存回區域變量 value |
| 8A 85 xx xx | MOV AL, [EBP+disp] | 取 value 到 AL |
| 66 EE | OUT DX, AL | 操作數前綴 0x66 + OUT DX,AL |
giveio.sys, inpout32.sys, winio.sys) 設置 IOPL=3 時可用。| 方案 | 原理 | 適用場景 |
|---|---|---|
| inpout32.dll / inpoutx64.dll | 簽名驅動映射 I/O 埠到用戶態 | 舊式 ISA/PCI 卡、並口/串口舊設備、教學演示 |
| WinRing0 (Ring0.dll) | 開源驅動支援 MSR/PCI Config/I/O | 超頻工具、硬體監控、風扇控制 |
| Windows Driver Kit (WDK) + KMDF | 編寫正規內核驅動,暴露 IOCTL 給用戶態 | 所有新硬體驅動、生產級項目 |
| libusb / WinUSB | 用戶態 USB 協議驅動 | USB 設備 (無需寫驅動) |
| Linux: ioperm()/iopl() + inb()/outb() | 內核支援用戶態 I/O (需 root/CAP_SYS_RAWIO) | Linux 嵌入式、工控 |
__emit__ 內嵌 I/O 指令是「黑魔法」,理解其機制有助於掌握 x86 I/O 與 Windows 權限模型,**但實際工程中請務必選用正規驅動/用戶態庫**,確保穩定、安全、跨版本相容。