1. 程式人生 > >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 [email protected] endp
系統把服務號儲存在eax暫存器,直接call [edx]
OD檢視得到7FFE0300
程式碼:
dd 7FFE0300
7FFE0300  7C92E510  ntdll.KiFastSystemCall
7FFE0304  7C92E514  ntdll.KiFastSystemCallRet
Windbg檢視得到
程式碼:
lkd> dd ffdf0300 l2
ffdf0300  7c92e510 7c92e514

lkd> 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 11111111111111110111111111110000


bf89973b 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 == NULL

win32k!NtUserRegisterHotKey+0x27:
bf899744 e86a7ef6ff      call    win32k!ValidateHwnd (bf8015b3)//則驗證控制代碼
bf899749 85c0            test    eax,eax  
bf89974b 7427            je      win32k!NtUserRegisterHotKey+0x30 (bf899774) //返回NULL

win32k!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,eax

win32k!NtUserRegisterHotKey+0x47:
bf89975e e8b373f6ff      call    win32k!LeaveCrit (bf800b16)
bf899763 8bc6            mov     eax,esi
bf899765 5e              pop     esi
bf899766 5d              pop     ebp
bf899767 c21000          ret     10h

win32k!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);

  //如果其他執行緒已經註冊過該熱鍵,返回FALSE
    if (fKeysExist) 
  {
        UserSetLastError(1409);  //1409錯誤碼:熱鍵已被註冊
        return FALSE;
    }
  
    if (phk == NULL) 
  {

    //熱鍵並未被註冊
        phk = (PHOT_KEY_ITEM)HeavyAllocPool(sizeof(HOT_KEY_ITEM), TAG_HOTKEY);

    //分配失敗,返回FALSE
        if (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位置指向當前執行緒的EPROCESS
    pProc   = *(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熱鍵註冊原理

要像系統註冊一個全域性熱鍵,需要用到RegisterHotKey,函式用法如下(MSDN):BOOL RegisterHotKey(                  HWND hWnd,            int id,            UINT fsModifi

Windows熱鍵註冊(反彙編方法 檢視win32api 原理)

要像系統註冊一個全域性熱鍵,需要用到RegisterHotKey,函式用法如下(MSDN): BOOL RegisterHotKey(                   HWND hWnd,             int id,             UINT fsM

windows 程式設計 註冊熱鍵

網上找了很多關於註冊熱鍵的,都是基於MFC 響應的。 本文是 直接響應 對於註冊多個熱鍵,使用hotkeyid ,我這裡是使用 對於 輸入的按鍵,進行拼接,將保證每次鍵入的id 都不同,方便移除熱鍵/****************************/ /***直接

windows 微軟註冊

alt cnblogs 分享 windows es2017 ima ges http src windows 微軟註冊

初級遊戲外掛編程詳解 windows運行原理+遊戲輔助編程 遊戲外掛編程

穩定 程序員 操作系統 上下 open 服務 dll stdio.h 五個 詳解遊戲輔助編程 【目錄】 1-什麽是Windows API 2-Windows進程 3-Windows 的內存的運行原理 4-windows 中句柄的概念 5-Windows的變量類型

windows系統註冊TeeChart8.ocx

下面win7為例。 win7系統64位版本:  1、把TeeChart8.ocx拷貝到系統的C:\Windows\SysWOW64\目錄。 2、以管理員身份執行cmd.exe,它的位置在C:\Windows\SysWOW64\目錄。 3、執行以下命令: cd .

Windows如何註冊Com元件

                1.先了解什麼是Com元件 Com元件百科地址:https://baike.baidu.com

Delphi全域性熱鍵註冊

private { Private declarations } Aatom: ATOM; procedure hotkey(var msg: TMessage); message WM_HOTKEY; procedure TForm1.Fo

windows服務註冊 -- svn伺服器開機自啟

啟用SVN服務,按下windows+R鍵,輸入cmd進入命令介面,輸入svnserve -d -r e:\ejsvn;e:\ejsvn為你伺服器目錄; 注意:這是臨時開啟的服務,命令執行後不能關閉視窗

獲取Windows系統註冊的檔案型別和圖示

煩死,寫了快一個小時。不知道怎麼碰了一下,就沒了。隨便記記吧。 就是獲取下面這個新建裡面的內容: 思路: (1)獲取系統已註冊的檔案型別 登錄檔路徑:MFC版獲取檔案型別 地址:http://blog.csdn.net/qq2399431200/article/deta

myeclipse 2013 SR2 for(MAC/WINDOWS)破解 註冊過程

前天去官網下載的時候都還只是sr1,結果昨天去看居然有sr2了,之前一直在windows下用 2013,網上的註冊機都還很好用。但最近換了mac,下載的sr1我卻無論如何都沒辦法破解,下載了N個版本的註冊機都不行。 今天終於把它搞定了。與大家分享一下: 1 首先來這裡下載註

QT 如何註冊系統熱鍵,類似 Windows上的RegisterHotKey函式一樣

下載庫: 在linux下編譯安裝,ubuntu9.10上會報找不到X11/libx.h,執行命令apt-get install libx11-dev安裝完後再編譯就可以了 2:在qt中使用qxt庫     在pro檔案中加上     CONFIG  += qxt    

玩轉Windows服務系列——Debug、Release版本的註冊和解除安裝,及其原理

Windows服務Debug版本 註冊 Services.exe -regserver 解除安裝 Services.exe -unregserver Windows服務Release版本 註冊 Services.exe -service 解除安裝 Services

windows dos 修改註冊

edev 能夠 最好 cal pro write prot storage ont U盤被寫保護了,然後需要修改註冊表,解決辦法這個不一定是最好的,僅僅是為了能讓dos能夠做處理,記錄一下: rem delete firstREG DELETE HKEY_LOCAL_MAC

Elasticsearch 註冊Windows服務

size 註意 ont enter window search nts ice ast 首先 cd 到es文件夾中的bin: 然後註冊為windows服務,註意 elasticsearch-service 即為上圖中標註elasticsearch-service.b

詳解Windows註冊表分析取證

classes 創建 dev html ida soft free 人在 隱藏 大多數都知道windows系統中有個叫註冊表的東西,但卻很少有人會去深入的了解它的作用以及如何對它進行操作。然而對於計算機取證人員來說註冊表無疑是塊巨大的寶藏。通過註冊表取證人員能分析出系統發生

Node.js原生及Express方法實現註冊登錄原理

美化 set head ack function charset stat input col 由於本文只是實現其原理,所以沒有使用數據庫,只是在js裏面模擬數據庫,當然實際中還是需要用數據庫的。 1.node.js原生方法 ①html頁面,非常簡單,沒有一絲美化~我們叫它

把Tomcat註冊windows服務

control bsp png adb mage windows ont 自動 cmd windows安裝java及配置環境 http://www.cnblogs.com/17shiqi/p/7397939.html Tomcat註冊為windows服務 本人是win7

Windows 編程入門,如何註冊賬號

ima 界面 登錄 windows com tle 賬號註冊 登陸 分享 第一步:點擊立即註冊 第二步:點擊註冊,用郵箱作為賬號註冊登錄,由於我已經註冊了,所以就出現這個頁面 第三步:登陸界面 Windows 編程入門,如何註冊賬號

Windows Server下把BAT批處理註冊成服務在後臺運行且註銷後能正常運行

sha 方法 pytho 面具 blog overflow 程序 str .cn 批處理有如下特點: 1、登錄到當前窗口運行時,如果關閉控制臺會連同啟動的程序一起關閉。 2、如果是以start /b的形式啟動,那麽同樣也是在控制臺關閉後者註銷當前窗口也會一起關閉。 3