下載地址:
http://www.internals.com/utilities/WinIo.zip
一個按鍵的消息產生流程如下:
1)硬件中斷/硬件端口數據
WinIO能模擬,或者修改IDT是在這一層
2)鍵盤Port驅動(USB or PS/2)
Filter驅動在此
KeyboardClassServiceCallback也在這一層被調用
3)kbdclass驅動
處理鍵盤布局和鍵盤語言
4)Windows內核邊界(zwCreate/zwReadFile)
———————-(系統調用)———————-
5)Windows內核邊界(zwCreate/zwReadFile)
6)csrss.exe的win32k!RawInputThread讀取,完成scancode和vk的轉換
SetWindowHook工作在這里(全局)
kbd_event工作在這里
7)csrss.exe調用DispatchMessage等函數分發消息
SetWindowHook工作在這里(進程)
PostMessage和SendMessage在這里
8)各個進程處理消息
winIO可以模擬最底層的按鍵消息,借此可以繞過沒有從消息循環讀取消息的安全控件。在這只講下64位系統下的用法,我的系統是win10 64位企業版。
1.64位系統,C#使用WinIO64
需要的文件:WinIo64.dll,WinIo64.sys
a.首先安裝WinIo64.sys的數字簽名,
由於winIO64只有測試版的簽名,我們需要把windows的測試模式打開。
win鍵+R cmd:bcdedit /set testsigning on
b.將整兩個文件復制到跟exe同一目錄。
c.c#代碼
using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WinIOTest { public class WinIO { private const int KBC_KEY_CMD = 0x64; private const int KBC_KEY_DATA = 0x60; [DllImport("WinIo64.dll")] public static extern bool InitializeWinIo(); [DllImport("WinIo64.dll")] public static extern bool GetPortVal(IntPtr wPortAddr, out int pdwPortVal, byte bSize); [DllImport("WinIo64.dll")] public static extern bool SetPortVal(uint wPortAddr, IntPtr dwPortVal, byte bSize); [DllImport("WinIo64.dll")] public static extern byte MapPhysToLin(byte pbPhysAddr, uint dwPhysSize, IntPtr PhysicalMemoryHandle); [DllImport("WinIo64.dll")] public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle, byte pbLinAddr); [DllImport("WinIo64.dll")] public static extern bool GetPhysLong(IntPtr pbPhysAddr, byte pdwPhysVal); [DllImport("WinIo64.dll")] public static extern bool SetPhysLong(IntPtr pbPhysAddr, byte dwPhysVal); [DllImport("WinIo64.dll")] public static extern void ShutdownWinIo(); [DllImport("user32.dll")] public static extern int MapVirtualKey(uint Ucode, uint uMapType); private WinIO() { IsInitialize = true; } public static void Initialize() { if (InitializeWinIo()) { KBCWait4IBE(); IsInitialize = true; } else MessageBox.Show("Load WinIO Failed!"); } public static void Shutdown() { if (IsInitialize) ShutdownWinIo(); IsInitialize = false; } private static bool IsInitialize { get; set; } ///等待鍵盤緩沖區為空 private static void KBCWait4IBE() { int dwVal = 0; do { bool flag = GetPortVal((IntPtr)0x64, out dwVal, 1); } while ((dwVal & 0x2) > 0); } /// 模擬鍵盤標按下 public static void KeyDown(Keys vKeyCoad) { if (!IsInitialize) return; int btScancode = 0; btScancode = MapVirtualKey((uint)vKeyCoad, 0); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1); } /// 模擬鍵盤彈出 public static void KeyUp(Keys vKeyCoad) { if (!IsInitialize) return; int btScancode = 0; btScancode = MapVirtualKey((uint)vKeyCoad, 0); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
使用:
WinIO.Initialize(); // 注冊 WinIO.KeyDown(Keys.A); // 按下A WinIO.KeyUp(Keys.A); // 松開A WinIO.Shutdown(); // 用完后注銷
- 1
- 2
- 3
- 4
2.64位系統,C#使用WinIO32
切換成WinIO32,只需要[DllImport(“WinIo64.dll”)]改成[DllImport(“WinIo32.dll”)],WinIo32.dll復制到exe同一目錄,運行時要用管理員身份。其他與winIO64一致。
運行時要用管理員身份
運行時要用管理員身份
運行時要用管理員身份
運行時要用管理員身份
運行時要用管理員身份