Windows热键注册原理
生活随笔
收集整理的這篇文章主要介紹了
Windows热键注册原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
要像系統注冊一個全局熱鍵,需要用到RegisterHotKey,函數用法如下(MSDN):
BOOL?RegisterHotKey(??????
????????????HWND?hWnd,
????????????int?id,
????????????UINT?fsModifiers,
????????????UINT?vk
);
????函數功能:該函數定義一個系統范圍的熱鍵。
函數原型:BOOL?RegisterHotKey(HWND?hWnd,int?id,UINT?fsModifiers,UINT?vk);
參數:
hWnd:接收熱鍵產生WM_HOTKEY消息的窗口句柄。若該參數NULL,傳遞給調用線程的WM_HOTKEY消息必須在消息循環中中進行處理。
id:定義熱鍵的標識符。調用線程中的其他熱鍵不能使用同樣的標識符。應用功能程序必須定義一個0X0000-0xBFFF范圍的值。一個共享的動態鏈接庫(DLL)必須
定義一個0xC000-0xFFFF范圍的值伯GlobalAddAtom函數返回該范圍)。為了避免與其他動態鏈接庫定義的熱鍵沖突,一個DLL必須使用GlobalAddAtom函數獲得熱鍵的標
識符。
fsModifoers:定義為了產生WM_HOTKEY消息而必須與由nVirtKey參數定義的鍵一起按下的鍵。該參數可以是如下值的組合:
MOD_ALT:按下的可以是任一Alt鍵。MOD_CONTROL:按下的可以是任一Ctrl鍵。
MOD_SHIFT:按下的可以是任一Shift鍵。
MOD_WIN:按下的可以是任一Windows按鍵。這些鍵可以用Microsoft?Windows日志記錄下來。
MOD_NOREPEAT:Windows?7或者后續版本:?更改熱鍵行為,以便鍵盤自動重復不會產生多個熱鍵通知。
vk:定義熱鍵的虛擬鍵碼。
返回值:若函數調用成功,返回一個非O值。若函數調用失敗,則返回值為0。若要獲得更多的錯誤信息,可以調用GetLastError函數。
備注:當某鍵被接下時,系統在所有的熱鍵中尋找匹配者。一旦找到一個匹配的熱鍵,系統將把WM_HOTKEY消息傳遞給登記了該熱鍵的線程的消息隊列。該消息被傳
送到隊列頭部,因此它將在下一輪消息循環中被移去。該函數不能將熱鍵同其他線程創建的窗口關聯起來。
若為一熱鍵定義的擊鍵己被其他熱鍵所定義,則RegisterHotKey函數調用失敗。
若hWnd參數標識的窗口已用與id參數定義的相同的標識符登記了一個熱鍵,則參數fsModifiers和vk的新值將替代這些參數先前定義的值。
Windows?CE:Windows?CE?2.0以上版本對于參數fsModifiers支持一個附加的標志位。叫做MOD_KEYUP。
若設置MOD_KEYUP位,則當發生鍵被按下或被彈起的事件時,窗口將發送WM_HOTKEY消息。
RegisterHotKey可以被用來在線程之間登記熱鍵。
速查:Windows?NT:3.1及以上版本;Windows:95及以上版本;Windows?CE:不支持;頭文件:winuser.h;庫文件:Hotkey.lib。
????F12鍵是調試器所使用的保留,所以不應將其注冊為熱鍵
代碼:#define?MOD_ALT?????????0x0001?=????1 #define?MOD_CONTROL?????0x0002?=???10 #define?MOD_SHIFT???????0x0004?=??100 #define?MOD_WIN?????????0x0008?=?1000
在IDA中反匯編RegisterHotKey
代碼:.text:77D1EBB3?????????????????mov?????eax,?11EAh????//系統服務號 .text:77D1EBB8?????????????????mov?????edx,?7FFE0300h?? .text:77D1EBBD?????????????????call????dword?ptr?[edx] .text:77D1EBBF?????????????????retn????10h .text:77D1EBBF?_NtUserRegisterHotKey@16?endp 系統把服務號保存在eax寄存器,直接call?[edx]
OD查看得到7FFE0300
代碼:dd?7FFE0300 7FFE0300??7C92E510??ntdll.KiFastSystemCall 7FFE0304??7C92E514??ntdll.KiFastSystemCallRet Windbg查看得到
代碼:lkd>?dd?ffdf0300?l2 ffdf0300??7c92e510?7c92e514lkd>?u?7c92e510 7c92e510?8bd4????????????mov?????edx,esp 7c92e512?0f34????????????sysenter windows中0x7FFE0000和0x0FFDF0000被映射到同一個物理地址,供4KB,但在用戶模式下該地址是不可寫的,內核模式下的可寫,4K空間操作系統占用一部分,
余下的大約有3K
USER:0x7FFE0000
KERNEL:0x0FFDF0000
在Windbg可用dt?nt!_KUSER_SHARED_DATA命令查看該共享區域
代碼:lkd>?dt?nt!_KUSER_SHARED_DATA+0x000?TickCountLow?????:?Uint4B+0x004?TickCountMultiplier?:?Uint4B+0x008?InterruptTime????:?_KSYSTEM_TIME+0x014?SystemTime???????:?_KSYSTEM_TIME+0x020?TimeZoneBias?????:?_KSYSTEM_TIME+0x02c?ImageNumberLow???:?Uint2B+0x02e?ImageNumberHigh??:?Uint2B+0x030?NtSystemRoot?????:?[260]?Uint2B+0x238?MaxStackTraceDepth?:?Uint4B+0x23c?CryptoExponent???:?Uint4B+0x240?TimeZoneId???????:?Uint4B+0x244?Reserved2????????:?[8]?Uint4B+0x264?NtProductType????:?_NT_PRODUCT_TYPE+0x268?ProductTypeIsValid?:?UChar+0x26c?NtMajorVersion???:?Uint4B+0x270?NtMinorVersion???:?Uint4B+0x274?ProcessorFeatures?:?[64]?UChar+0x2b4?Reserved1????????:?Uint4B+0x2b8?Reserved3????????:?Uint4B+0x2bc?TimeSlip?????????:?Uint4B+0x2c0?AlternativeArchitecture?:?_ALTERNATIVE_ARCHITECTURE_TYPE+0x2c8?SystemExpirationDate?:?_LARGE_INTEGER+0x2d0?SuiteMask????????:?Uint4B+0x2d4?KdDebuggerEnabled?:?UChar+0x2d5?NXSupportPolicy??:?UChar+0x2d8?ActiveConsoleId??:?Uint4B+0x2dc?DismountCount????:?Uint4B+0x2e0?ComPlusPackage???:?Uint4B+0x2e4?LastSystemRITEventTickCount?:?Uint4B+0x2e8?NumberOfPhysicalPages?:?Uint4B+0x2ec?SafeBootMode?????:?UChar+0x2f0?TraceLogging?????:?Uint4B+0x2f8?TestRetInstruction?:?Uint8B+0x300?SystemCall???????:?Uint4B+0x304?SystemCallReturn?:?Uint4B+0x308?SystemCallPad????:?[3]?Uint8B+0x320?TickCount????????:?_KSYSTEM_TIME+0x320?TickCountQuad????:?Uint8B+0x330?Cookie???????????:?Uint4B 11EA?=?1000111101010?=?13~14位選擇服務描述表,選擇KeServiceDescriptorTableShadow,系統共有4個服務描述表,第一個在ntoskrnl.exe中
并導出KeServiceDescriptorTable指針
可見該函數沒做任何處理直接進入內核(win32k.sys)中,在Windbg反匯編:
代碼:lkd>?uf?win32k!NtUserRegisterHotKey win32k!NtUserRegisterHotKey+0x34: bf899720?33c0????????????xor?????eax,eax????//eax?=?NULL bf899722?eb29????????????jmp?????win32k!NtUserRegisterHotKey+0x36?(bf89974d)win32k!NtUserRegisterHotKey: bf899729?8bff????????????mov?????edi,edi bf89972b?55??????????????push????ebp bf89972c?8bec????????????mov?????ebp,esp bf89972e?56??????????????push????esi bf89972f?e8b673f6ff??????call????win32k!EnterCrit?(bf800aea) bf899734?f74510f07fffff??test????dword?ptr?[ebp+10h],0FFFF7FF0h????//fsModifiers是否有效,是否大于1000b?11111111111111110111111111110000bf89973b?752d????????????jne?????win32k!NtUserRegisterHotKey+0x14?(bf89976a)//fsModifiers無效則跳轉win32k!NtUserRegisterHotKey+0x20: bf89973d?8b4d08??????????mov?????ecx,dword?ptr?[ebp+8]??//hWnd bf899740?85c9????????????test????ecx,ecx?? bf899742?74dc????????????je??????win32k!NtUserRegisterHotKey+0x34?(bf899720)//hWnd?==?NULLwin32k!NtUserRegisterHotKey+0x27: bf899744?e86a7ef6ff??????call????win32k!ValidateHwnd?(bf8015b3)//則驗證句柄 bf899749?85c0????????????test????eax,eax?? bf89974b?7427????????????je??????win32k!NtUserRegisterHotKey+0x30?(bf899774)?//返回NULLwin32k!NtUserRegisterHotKey+0x36: bf89974d?ff7514??????????push????dword?ptr?[ebp+14h]??//vk bf899750?ff7510??????????push????dword?ptr?[ebp+10h]??//fsModifiers bf899753?ff750c??????????push????dword?ptr?[ebp+0Ch]??//id bf899756?50??????????????push????eax??????//pWnd bf899757?e8aefeffff??????call????win32k!_RegisterHotKey?(bf89960a) bf89975c?8bf0????????????mov?????esi,eaxwin32k!NtUserRegisterHotKey+0x47: bf89975e?e8b373f6ff??????call????win32k!LeaveCrit?(bf800b16) bf899763?8bc6????????????mov?????eax,esi bf899765?5e??????????????pop?????esi bf899766?5d??????????????pop?????ebp bf899767?c21000??????????ret?????10hwin32k!NtUserRegisterHotKey+0x14: bf89976a?68ec030000??????push????3ECh????//錯誤碼:1004,參數無效 bf89976f?e83da0f6ff??????call????win32k!UserSetLastError?(bf8037b1)win32k!NtUserRegisterHotKey+0x30: bf899774?33f6????????????xor?????esi,esi bf899776?ebe6????????????jmp?????win32k!NtUserRegisterHotKey+0x47?(bf89975e)/***************************************/ PWND?FASTCALL?ValidateHwnd(HWND?hwnd); //NtUserRegisterHotKey偽代碼:
代碼:BOOLEN?APIENTRY NtUserRegisterHotKey(HWND?hWnd,int?id,UINT?fsModifiers,UINT?vk) {BOOLEN?bRet;PWND?pWnd?=?NULL;EnterCrit();if(!(fsModifiers?&?0x0FFFF7FF0h)){if(hWnd){pWnd?=?ValidateHwnd(hWnd);}bRet?=?_RegisterHotKey(pWnd,id,fsModifiers,vk);}else{UserSetLastError(1004);//1004無效標志bRet?=?FALSE;}????LeaveCrit();return?bRet; } //系統熱鍵結構:
代碼:typedef?struct?_HOT_KEY_ITEM {??PETHREAD?Thread;HWND?spwnd;UINT?fsModifiers;????UINT?vk;int?id;struct?_HOT_KEY_ITEM?phkNext; }?HOT_KEY_ITEM,?*PHOT_KEY_ITEM; _RegisterHotKey偽代碼如下:
代碼:BOOL?_RegisterHotKey(PWND?pwnd,int?id,UINT?fsModifiers,UINT?vk) {PHOT_KEY_ITEM?phk;BOOL?fKeysExist?=?FALSE;PTHREADINFO?ptiCurrent;PWINDOWSTATION?pwinsta?=?_GetProcessWindowStation(NULL);DWORD?ErrorCode;ptiCurrent?=?gptiCurrent;//如果調用者不是WindowStation初始化的線程和不適當的權限if(grpwinstaList?&&?!CheckWinstaWriteAttributesAccess())?{return?FALSE;}//不能為其他線程的窗口注冊熱鍵if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{if?(GETPTI(pwnd)?!=?ptiCurrent)?{UserSetLastError(1408);??//1408錯誤碼:無效窗口;它屬于另一線程。return?FALSE;}}phk?=?FindHotKey(ptiCurrent,?pwnd,?id,?fsModifiers,?vk,?FALSE,?&fKeysExist);//如果其他線程已經注冊過該熱鍵,返回FALSEif?(fKeysExist)?{UserSetLastError(1409);??//1409錯誤碼:熱鍵已被注冊return?FALSE;}if?(phk?==?NULL)?{//熱鍵并未被注冊phk?=?(PHOT_KEY_ITEM)HeavyAllocPool(sizeof(HOT_KEY_ITEM),?TAG_HOTKEY);//分配失敗,返回FALSEif?(phk?==?NULL)?{return?FALSE;}phk->pti?=?ptiCurrent;if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{phk->spwnd?=?NULL;HMAssignmentLock(&phk->spwnd,?pwnd);}?else?{phk->spwnd?=?pwnd;}phk->fsModifiers?=?fsModifiers;phk->vk?=?vk;phk->id?=?id;//插入到系統熱鍵鏈表中//gphkFirst?-?這是不導出變量存儲了系統結構熱鍵(phkNext指向下一個熱鍵結構域)地址phk->phkNext?=?gphkFirst;gphkFirst?=?phk;}?else?{//如果本線程已注冊過該熱鍵,則重新覆蓋phk->fsModifiers?=?fsModifiers;phk->vk?=?vk;}??return?TRUE; } //用Windbg查看下gphkFirst
代碼:lkd>?dd?gphkFirst?L1 bf9af814??e2ce10d8
e2ce10d8就是最近一次軟件向系統注冊的全局熱鍵,繼續
代碼:lkd>?dd?e2ce10d8?l6 e2ce10d8??e2265008?bbe35a28?00000003?00000054 e2ce10e8??0000c024?e2291a68 e2265008?是ETHREAD,查看發現是QQ的一個線程
bbe35a28?是窗口句柄
00000003?是功能鍵11,說明有Ctrl+Alt鍵
00000054?是VK_?,0x54對應ASCI碼的大寫T,Ctrl+ATL+T(QQ上:發送騰訊微博的)
0000c024?是熱鍵的ID
e2291a68?是下一個熱鍵結構
代碼:PHOT_KEY_ITEM?FindHotKey(PTHREADINFO?ptiCurrent,PWND?pwnd,int?id,UINT?fsModifiers,UINT?vk,BOOL?fUnregister,PBOOL?pfKeysExist) {PHOT_KEY_ITEM?phk,?phkRet,?phkPrev;//初始化返回值*pfKeysExist?=?FALSE;phkRet?=?NULL;phk?=?gphkFirst;while?(phk){if?((phk->pti?==?ptiCurrent)?&&?(phk->spwnd?==?pwnd)?&&?(phk->id?==?id))?{if?(fUnregister)?{//摘掉熱鍵if?(phk?==?gphkFirst)?{gphkFirst?=?phk->phkNext;}?else?{phkPrev->phkNext?=?phk->phkNext;}if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{Unlock(&phk->spwnd);}UserFreePool((PVOID)phk);return((PHOT_KEY_ITEM)1);}phkRet?=?phk;}//如果熱鍵已經注冊過,設置已存在標志if?((phk->fsModifiers?==?fsModifiers)?&&?(phk->vk?==?vk))?{if?(phk->spwnd?==?PWND_FOCUS)?{if?(phk->pti?==?ptiCurrent)?{*pfKeysExist?=?TRUE;}}?else?{*pfKeysExist?=?TRUE;}}phkPrev?=?phk;phk?=?phk->phkNext;}return?phkRet; } //遍歷系統熱鍵
代碼:VOID?DumpHotKeys() {ULONG?dwAddr;KAPC_STATE?ApcState;PETHREAD?pThread;PEPROCESS?pProc;PHOTKEY?phk;//必須在GUI線程中遍歷KeStackAttachProcess(?pExpEprocess?,?&ApcState?);dwAddr?=?*(PULONG)gphkFirst;KeUnstackDetachProcess(&ApcState);phk?=?(PHOTKEY)dwAddr;//解析系統所有熱鍵while(?phk?!=?NULL?){pThread?=?*(PULONG)phk->pti;//0x220位置指向當前線程的EPROCESSpProc???=?*(PULONG)(?(ULONG)pThread?+?0x220?);//EPROCESS?+?0x174指向進程名字KdPrint(("Process?Name?:?%s\n"?,?(ULONG)pProc?+?0x174?));KdPrint(("id?:?%d\n"?,?phk->id?));KdPrint(("Combination?:?%s?+?%X\n"?,?GetButton(?phk->fsModifiers?)?,?phk->vk?));KdPrint(("------------------------------------------\n"));phk?=?phk->phkNext;} }
BOOL?RegisterHotKey(??????
????????????HWND?hWnd,
????????????int?id,
????????????UINT?fsModifiers,
????????????UINT?vk
);
????函數功能:該函數定義一個系統范圍的熱鍵。
函數原型:BOOL?RegisterHotKey(HWND?hWnd,int?id,UINT?fsModifiers,UINT?vk);
參數:
hWnd:接收熱鍵產生WM_HOTKEY消息的窗口句柄。若該參數NULL,傳遞給調用線程的WM_HOTKEY消息必須在消息循環中中進行處理。
id:定義熱鍵的標識符。調用線程中的其他熱鍵不能使用同樣的標識符。應用功能程序必須定義一個0X0000-0xBFFF范圍的值。一個共享的動態鏈接庫(DLL)必須
定義一個0xC000-0xFFFF范圍的值伯GlobalAddAtom函數返回該范圍)。為了避免與其他動態鏈接庫定義的熱鍵沖突,一個DLL必須使用GlobalAddAtom函數獲得熱鍵的標
識符。
fsModifoers:定義為了產生WM_HOTKEY消息而必須與由nVirtKey參數定義的鍵一起按下的鍵。該參數可以是如下值的組合:
MOD_ALT:按下的可以是任一Alt鍵。MOD_CONTROL:按下的可以是任一Ctrl鍵。
MOD_SHIFT:按下的可以是任一Shift鍵。
MOD_WIN:按下的可以是任一Windows按鍵。這些鍵可以用Microsoft?Windows日志記錄下來。
MOD_NOREPEAT:Windows?7或者后續版本:?更改熱鍵行為,以便鍵盤自動重復不會產生多個熱鍵通知。
vk:定義熱鍵的虛擬鍵碼。
返回值:若函數調用成功,返回一個非O值。若函數調用失敗,則返回值為0。若要獲得更多的錯誤信息,可以調用GetLastError函數。
備注:當某鍵被接下時,系統在所有的熱鍵中尋找匹配者。一旦找到一個匹配的熱鍵,系統將把WM_HOTKEY消息傳遞給登記了該熱鍵的線程的消息隊列。該消息被傳
送到隊列頭部,因此它將在下一輪消息循環中被移去。該函數不能將熱鍵同其他線程創建的窗口關聯起來。
若為一熱鍵定義的擊鍵己被其他熱鍵所定義,則RegisterHotKey函數調用失敗。
若hWnd參數標識的窗口已用與id參數定義的相同的標識符登記了一個熱鍵,則參數fsModifiers和vk的新值將替代這些參數先前定義的值。
Windows?CE:Windows?CE?2.0以上版本對于參數fsModifiers支持一個附加的標志位。叫做MOD_KEYUP。
若設置MOD_KEYUP位,則當發生鍵被按下或被彈起的事件時,窗口將發送WM_HOTKEY消息。
RegisterHotKey可以被用來在線程之間登記熱鍵。
速查:Windows?NT:3.1及以上版本;Windows:95及以上版本;Windows?CE:不支持;頭文件:winuser.h;庫文件:Hotkey.lib。
????F12鍵是調試器所使用的保留,所以不應將其注冊為熱鍵
代碼:#define?MOD_ALT?????????0x0001?=????1 #define?MOD_CONTROL?????0x0002?=???10 #define?MOD_SHIFT???????0x0004?=??100 #define?MOD_WIN?????????0x0008?=?1000
在IDA中反匯編RegisterHotKey
代碼:.text:77D1EBB3?????????????????mov?????eax,?11EAh????//系統服務號 .text:77D1EBB8?????????????????mov?????edx,?7FFE0300h?? .text:77D1EBBD?????????????????call????dword?ptr?[edx] .text:77D1EBBF?????????????????retn????10h .text:77D1EBBF?_NtUserRegisterHotKey@16?endp 系統把服務號保存在eax寄存器,直接call?[edx]
OD查看得到7FFE0300
代碼:dd?7FFE0300 7FFE0300??7C92E510??ntdll.KiFastSystemCall 7FFE0304??7C92E514??ntdll.KiFastSystemCallRet Windbg查看得到
代碼:lkd>?dd?ffdf0300?l2 ffdf0300??7c92e510?7c92e514lkd>?u?7c92e510 7c92e510?8bd4????????????mov?????edx,esp 7c92e512?0f34????????????sysenter windows中0x7FFE0000和0x0FFDF0000被映射到同一個物理地址,供4KB,但在用戶模式下該地址是不可寫的,內核模式下的可寫,4K空間操作系統占用一部分,
余下的大約有3K
USER:0x7FFE0000
KERNEL:0x0FFDF0000
在Windbg可用dt?nt!_KUSER_SHARED_DATA命令查看該共享區域
代碼:lkd>?dt?nt!_KUSER_SHARED_DATA+0x000?TickCountLow?????:?Uint4B+0x004?TickCountMultiplier?:?Uint4B+0x008?InterruptTime????:?_KSYSTEM_TIME+0x014?SystemTime???????:?_KSYSTEM_TIME+0x020?TimeZoneBias?????:?_KSYSTEM_TIME+0x02c?ImageNumberLow???:?Uint2B+0x02e?ImageNumberHigh??:?Uint2B+0x030?NtSystemRoot?????:?[260]?Uint2B+0x238?MaxStackTraceDepth?:?Uint4B+0x23c?CryptoExponent???:?Uint4B+0x240?TimeZoneId???????:?Uint4B+0x244?Reserved2????????:?[8]?Uint4B+0x264?NtProductType????:?_NT_PRODUCT_TYPE+0x268?ProductTypeIsValid?:?UChar+0x26c?NtMajorVersion???:?Uint4B+0x270?NtMinorVersion???:?Uint4B+0x274?ProcessorFeatures?:?[64]?UChar+0x2b4?Reserved1????????:?Uint4B+0x2b8?Reserved3????????:?Uint4B+0x2bc?TimeSlip?????????:?Uint4B+0x2c0?AlternativeArchitecture?:?_ALTERNATIVE_ARCHITECTURE_TYPE+0x2c8?SystemExpirationDate?:?_LARGE_INTEGER+0x2d0?SuiteMask????????:?Uint4B+0x2d4?KdDebuggerEnabled?:?UChar+0x2d5?NXSupportPolicy??:?UChar+0x2d8?ActiveConsoleId??:?Uint4B+0x2dc?DismountCount????:?Uint4B+0x2e0?ComPlusPackage???:?Uint4B+0x2e4?LastSystemRITEventTickCount?:?Uint4B+0x2e8?NumberOfPhysicalPages?:?Uint4B+0x2ec?SafeBootMode?????:?UChar+0x2f0?TraceLogging?????:?Uint4B+0x2f8?TestRetInstruction?:?Uint8B+0x300?SystemCall???????:?Uint4B+0x304?SystemCallReturn?:?Uint4B+0x308?SystemCallPad????:?[3]?Uint8B+0x320?TickCount????????:?_KSYSTEM_TIME+0x320?TickCountQuad????:?Uint8B+0x330?Cookie???????????:?Uint4B 11EA?=?1000111101010?=?13~14位選擇服務描述表,選擇KeServiceDescriptorTableShadow,系統共有4個服務描述表,第一個在ntoskrnl.exe中
并導出KeServiceDescriptorTable指針
可見該函數沒做任何處理直接進入內核(win32k.sys)中,在Windbg反匯編:
代碼:lkd>?uf?win32k!NtUserRegisterHotKey win32k!NtUserRegisterHotKey+0x34: bf899720?33c0????????????xor?????eax,eax????//eax?=?NULL bf899722?eb29????????????jmp?????win32k!NtUserRegisterHotKey+0x36?(bf89974d)win32k!NtUserRegisterHotKey: bf899729?8bff????????????mov?????edi,edi bf89972b?55??????????????push????ebp bf89972c?8bec????????????mov?????ebp,esp bf89972e?56??????????????push????esi bf89972f?e8b673f6ff??????call????win32k!EnterCrit?(bf800aea) bf899734?f74510f07fffff??test????dword?ptr?[ebp+10h],0FFFF7FF0h????//fsModifiers是否有效,是否大于1000b?11111111111111110111111111110000bf89973b?752d????????????jne?????win32k!NtUserRegisterHotKey+0x14?(bf89976a)//fsModifiers無效則跳轉win32k!NtUserRegisterHotKey+0x20: bf89973d?8b4d08??????????mov?????ecx,dword?ptr?[ebp+8]??//hWnd bf899740?85c9????????????test????ecx,ecx?? bf899742?74dc????????????je??????win32k!NtUserRegisterHotKey+0x34?(bf899720)//hWnd?==?NULLwin32k!NtUserRegisterHotKey+0x27: bf899744?e86a7ef6ff??????call????win32k!ValidateHwnd?(bf8015b3)//則驗證句柄 bf899749?85c0????????????test????eax,eax?? bf89974b?7427????????????je??????win32k!NtUserRegisterHotKey+0x30?(bf899774)?//返回NULLwin32k!NtUserRegisterHotKey+0x36: bf89974d?ff7514??????????push????dword?ptr?[ebp+14h]??//vk bf899750?ff7510??????????push????dword?ptr?[ebp+10h]??//fsModifiers bf899753?ff750c??????????push????dword?ptr?[ebp+0Ch]??//id bf899756?50??????????????push????eax??????//pWnd bf899757?e8aefeffff??????call????win32k!_RegisterHotKey?(bf89960a) bf89975c?8bf0????????????mov?????esi,eaxwin32k!NtUserRegisterHotKey+0x47: bf89975e?e8b373f6ff??????call????win32k!LeaveCrit?(bf800b16) bf899763?8bc6????????????mov?????eax,esi bf899765?5e??????????????pop?????esi bf899766?5d??????????????pop?????ebp bf899767?c21000??????????ret?????10hwin32k!NtUserRegisterHotKey+0x14: bf89976a?68ec030000??????push????3ECh????//錯誤碼:1004,參數無效 bf89976f?e83da0f6ff??????call????win32k!UserSetLastError?(bf8037b1)win32k!NtUserRegisterHotKey+0x30: bf899774?33f6????????????xor?????esi,esi bf899776?ebe6????????????jmp?????win32k!NtUserRegisterHotKey+0x47?(bf89975e)/***************************************/ PWND?FASTCALL?ValidateHwnd(HWND?hwnd); //NtUserRegisterHotKey偽代碼:
代碼:BOOLEN?APIENTRY NtUserRegisterHotKey(HWND?hWnd,int?id,UINT?fsModifiers,UINT?vk) {BOOLEN?bRet;PWND?pWnd?=?NULL;EnterCrit();if(!(fsModifiers?&?0x0FFFF7FF0h)){if(hWnd){pWnd?=?ValidateHwnd(hWnd);}bRet?=?_RegisterHotKey(pWnd,id,fsModifiers,vk);}else{UserSetLastError(1004);//1004無效標志bRet?=?FALSE;}????LeaveCrit();return?bRet; } //系統熱鍵結構:
代碼:typedef?struct?_HOT_KEY_ITEM {??PETHREAD?Thread;HWND?spwnd;UINT?fsModifiers;????UINT?vk;int?id;struct?_HOT_KEY_ITEM?phkNext; }?HOT_KEY_ITEM,?*PHOT_KEY_ITEM; _RegisterHotKey偽代碼如下:
代碼:BOOL?_RegisterHotKey(PWND?pwnd,int?id,UINT?fsModifiers,UINT?vk) {PHOT_KEY_ITEM?phk;BOOL?fKeysExist?=?FALSE;PTHREADINFO?ptiCurrent;PWINDOWSTATION?pwinsta?=?_GetProcessWindowStation(NULL);DWORD?ErrorCode;ptiCurrent?=?gptiCurrent;//如果調用者不是WindowStation初始化的線程和不適當的權限if(grpwinstaList?&&?!CheckWinstaWriteAttributesAccess())?{return?FALSE;}//不能為其他線程的窗口注冊熱鍵if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{if?(GETPTI(pwnd)?!=?ptiCurrent)?{UserSetLastError(1408);??//1408錯誤碼:無效窗口;它屬于另一線程。return?FALSE;}}phk?=?FindHotKey(ptiCurrent,?pwnd,?id,?fsModifiers,?vk,?FALSE,?&fKeysExist);//如果其他線程已經注冊過該熱鍵,返回FALSEif?(fKeysExist)?{UserSetLastError(1409);??//1409錯誤碼:熱鍵已被注冊return?FALSE;}if?(phk?==?NULL)?{//熱鍵并未被注冊phk?=?(PHOT_KEY_ITEM)HeavyAllocPool(sizeof(HOT_KEY_ITEM),?TAG_HOTKEY);//分配失敗,返回FALSEif?(phk?==?NULL)?{return?FALSE;}phk->pti?=?ptiCurrent;if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{phk->spwnd?=?NULL;HMAssignmentLock(&phk->spwnd,?pwnd);}?else?{phk->spwnd?=?pwnd;}phk->fsModifiers?=?fsModifiers;phk->vk?=?vk;phk->id?=?id;//插入到系統熱鍵鏈表中//gphkFirst?-?這是不導出變量存儲了系統結構熱鍵(phkNext指向下一個熱鍵結構域)地址phk->phkNext?=?gphkFirst;gphkFirst?=?phk;}?else?{//如果本線程已注冊過該熱鍵,則重新覆蓋phk->fsModifiers?=?fsModifiers;phk->vk?=?vk;}??return?TRUE; } //用Windbg查看下gphkFirst
代碼:lkd>?dd?gphkFirst?L1 bf9af814??e2ce10d8
e2ce10d8就是最近一次軟件向系統注冊的全局熱鍵,繼續
代碼:lkd>?dd?e2ce10d8?l6 e2ce10d8??e2265008?bbe35a28?00000003?00000054 e2ce10e8??0000c024?e2291a68 e2265008?是ETHREAD,查看發現是QQ的一個線程
bbe35a28?是窗口句柄
00000003?是功能鍵11,說明有Ctrl+Alt鍵
00000054?是VK_?,0x54對應ASCI碼的大寫T,Ctrl+ATL+T(QQ上:發送騰訊微博的)
0000c024?是熱鍵的ID
e2291a68?是下一個熱鍵結構
代碼:PHOT_KEY_ITEM?FindHotKey(PTHREADINFO?ptiCurrent,PWND?pwnd,int?id,UINT?fsModifiers,UINT?vk,BOOL?fUnregister,PBOOL?pfKeysExist) {PHOT_KEY_ITEM?phk,?phkRet,?phkPrev;//初始化返回值*pfKeysExist?=?FALSE;phkRet?=?NULL;phk?=?gphkFirst;while?(phk){if?((phk->pti?==?ptiCurrent)?&&?(phk->spwnd?==?pwnd)?&&?(phk->id?==?id))?{if?(fUnregister)?{//摘掉熱鍵if?(phk?==?gphkFirst)?{gphkFirst?=?phk->phkNext;}?else?{phkPrev->phkNext?=?phk->phkNext;}if?((pwnd?!=?PWND_FOCUS)?&&?(pwnd?!=?PWND_INPUTOWNER))?{Unlock(&phk->spwnd);}UserFreePool((PVOID)phk);return((PHOT_KEY_ITEM)1);}phkRet?=?phk;}//如果熱鍵已經注冊過,設置已存在標志if?((phk->fsModifiers?==?fsModifiers)?&&?(phk->vk?==?vk))?{if?(phk->spwnd?==?PWND_FOCUS)?{if?(phk->pti?==?ptiCurrent)?{*pfKeysExist?=?TRUE;}}?else?{*pfKeysExist?=?TRUE;}}phkPrev?=?phk;phk?=?phk->phkNext;}return?phkRet; } //遍歷系統熱鍵
代碼:VOID?DumpHotKeys() {ULONG?dwAddr;KAPC_STATE?ApcState;PETHREAD?pThread;PEPROCESS?pProc;PHOTKEY?phk;//必須在GUI線程中遍歷KeStackAttachProcess(?pExpEprocess?,?&ApcState?);dwAddr?=?*(PULONG)gphkFirst;KeUnstackDetachProcess(&ApcState);phk?=?(PHOTKEY)dwAddr;//解析系統所有熱鍵while(?phk?!=?NULL?){pThread?=?*(PULONG)phk->pti;//0x220位置指向當前線程的EPROCESSpProc???=?*(PULONG)(?(ULONG)pThread?+?0x220?);//EPROCESS?+?0x174指向進程名字KdPrint(("Process?Name?:?%s\n"?,?(ULONG)pProc?+?0x174?));KdPrint(("id?:?%d\n"?,?phk->id?));KdPrint(("Combination?:?%s?+?%X\n"?,?GetButton(?phk->fsModifiers?)?,?phk->vk?));KdPrint(("------------------------------------------\n"));phk?=?phk->phkNext;} }
總結
以上是生活随笔為你收集整理的Windows热键注册原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: u盘安装linux8,实体机安装cent
- 下一篇: Qt实现全局键盘事件监听器-Window