模擬鍵盤鼠標操作——IoAccessMap介紹<?xml:namespace prefix = w ns = "urn:schemas-microsoft-com:office:word" />
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
NetRoc 2007.06.25
關鍵詞:I/O Permission Bit Map、按鍵精靈、模擬鍵盤鼠標
今天又被噁心了,上班不想工作。來談談IoAccessMap相關的一些東西吧。
我在上一篇XTrap驅動分析的文章裏面提到過,現在的一些模擬鍵盤鼠標輸入的程序使用了一種所謂硬件模式的東西,例如按鍵精靈。其實就是使用了WinIo這樣一些打開進程在Ring3訪問端口權限的庫。這裏會詳細分析一下他們的實現機制,以及對付這些工具的推薦方法。
首先摘抄一段WinIo驅動裏面的代碼:
case IOCTL_WINIO_ENABLEDIRECTIO:
OutputDebugString("IOCTL_WINIO_ENABLEDIRECTIO");
pIOPM = MmAllocateNonCachedMemory(sizeof(IOPM));
if (pIOPM)
{
RtlZeroMemory(pIOPM, sizeof(IOPM));
Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);
Ke386SetIoAccessMap(1, pIOPM);
}
else
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
break;
ok,打開端口權限的所有祕密就在這裏了。不過我們要先介紹一下Inter CPU對IoAccessMap的定義。
I386架構下,每個進程擁有的TSS塊包含一個I/O Permission Bit Map,其中定義了該進程對各個端口的讀寫權限。I/O Permission Bit Map的每一位對應一個端口。例如和鼠標鍵盤相關的端口對應於bit60和bit64。另外,在EFLAGS寄存器中的bit12、bit13指示了IN, INS, OUT, OUTS, CLI, STI這幾條指令需要的IOPL。當CPL大於IOPL時,IO訪問時就會再查詢I/O Permission Bit Map裏面對應bit是否爲0,如果爲0則允許IO訪問,否則會觸發general-protection exception (#GP)。
所以,如果要打開某個進程的IO權限,只需要修改它的I/O Permission Bit Map就可以了。
Windows爲每個進程保存的TSS結構如下:
typedef struct _KTSS {
USHORT Backlink;
USHORT Reserved0;
ULONG Esp0;
USHORT Ss0;
USHORT Reserved1;
ULONG NotUsed1[4];
ULONG CR3;
ULONG Eip;
ULONG EFlags;
ULONG Eax;
ULONG Ecx;
ULONG Edx;
ULONG Ebx;
ULONG Esp;
ULONG Ebp;
ULONG Esi;
ULONG Edi;
USHORT Es;
USHORT Reserved2;
USHORT Cs;
USHORT Reserved3;
USHORT Ss;
USHORT Reserved4;
USHORT Ds;
USHORT Reserved5;
USHORT Fs;
USHORT Reserved6;
USHORT Gs;
USHORT Reserved7;
USHORT LDT;
USHORT Reserved8;
USHORT Flags;
USHORT IoMapBase;
KIIO_ACCESS_MAP IoMaps[IOPM_COUNT];
//
// This is the Software interrupt direction bitmap associated with
// IO_ACCESS_MAP_NONE
//
KINT_DIRECTION_MAP IntDirectionMap;
} KTSS, *PKTSS;
其中的IOPM_COUNT定義爲1。貌似本來Windows想爲每個Process保存多個IoAccessMap,默認只保存了一個。在訪問相關函數時,如果制定MapNumber爲0,則表示不使用IoAccessMap。所以0號map是虛擬出來的,這點在wrk1.2代碼中可以確認。
以下三個函數用於訪問IoAccessMap:
BOOLEAN Ke386QueryIoAccessMap(ULONG MapNumber, PKIO_ACCESS_MAP IoAccessMap);用於查詢IoAccessMap
BOOLEAN Ke386SetIoAccessMap(ULONG MapNumber, PKIO_ACCESS_MAP IoAccessMap);用於設置IoAccessMap
BOOLEAN Ke386IoSetAccessProcess(PKPROCESS Process, ULONG MapNumber);用於設置某個Process使用的IoAccessMap號
下面分別講一下三個函數的作用。
Ke386QueryIoAccessMap將系統KTSS中的IoMaps複製出來,也就是查詢當前的IoAccessMap。MapNumber傳入Map號,當爲0的時候表示指定IO_ACCESS_MAP_NONE,此時將傳入IoAccessMap的所有位置1,當MapNumber爲其他值時,複製對應的Map。
Ke386SetIoAccessMap將IoAccessMap中的內容複製到指定MapNumber的內容中,當爲0時返回FALSE,當爲其他合法值時,Ke386SetIoAccessMap會創建一個DPC,並在DPC中將IoAccessMap的內容複製到進程TSS的IoMaps中。
Ke386IoSetAccessProcess則是設置Process使用的IoAccessMap號。同樣,會創建一個DPC,並由DPC修改指定進程的EPROCESS中的IopmOffset。
Ok,回過頭來看WinIo的代碼就一目瞭然了
pIOPM = MmAllocateNonCachedMemory(sizeof(IOPM));
if (pIOPM)
{
RtlZeroMemory(pIOPM, sizeof(IOPM));
Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);
Ke386SetIoAccessMap(1, pIOPM);
}
首先Alloc一塊內存,大小總是爲0x2000字節,並將所有位置爲0,也就是說打開0~0x10000端口的讀寫權限。
Ke386IoSetAccessProcess的調用是確認使用IoAccessMap,然後通過Ke386SetIoAccessMap將map設置進去。
這樣的方式似乎有點暴力了,如果改良一下的話,可以設置爲只打開需要的端口。
這種方式其實爲系統打開了很大的漏洞,在ring3爲一個進程打開了權限,實際上就是爲任何人打開了權限。再加上往64端口寫東西又可以重起機器什麼的,呵呵……不說了。
當然,如果要關掉對應的權限,就可以反其道而行之,將map中對應位設置爲1即可。這就是我在上篇文章中提到的方法了。
話說回來,wrk真是好東西,哈哈。