Windows热键注册(反汇编方法 查看win32api 原理)

来源:互联网 发布:2016广电禁止网络电视 编辑:程序博客网 时间:2024/05/18 02:33
 1556人阅读 评论(0) 收藏 举报
windows汇编apinullmicrosoftc
要像系统注册一个全局热键,需要用到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键是调试器所使用的保留,所以不应将其注册为热键

[plain] view plaincopy
  1. #define MOD_ALT         0x0001 =    1  
  2. #define MOD_CONTROL     0x0002 =   10  
  3. #define MOD_SHIFT       0x0004 =  100  
  4. #define MOD_WIN         0x0008 = 1000  

在IDA中反汇编RegisterHotKey
[plain] view plaincopy
  1. .text:77D1EBB3                 mov     eax, 11EAh    //系统服务号  
  2. .text:77D1EBB8                 mov     edx, 7FFE0300h    
  3. .text:77D1EBBD                 call    dword ptr [edx]  
  4. .text:77D1EBBF                 retn    10h  
  5. .text:77D1EBBF _NtUserRegisterHotKey@16 endp  

系统把服务号保存在eax寄存器,直接call [edx]
OD查看得到7FFE0300
[plain] view plaincopy
  1. dd 7FFE0300  
  2. 7FFE0300  7C92E510  ntdll.KiFastSystemCall  
  3. 7FFE0304  7C92E514  ntdll.KiFastSystemCallRet  

Windbg查看得到

[plain] view plaincopy
  1. <span style="font-size:14px;">lkd> dd ffdf0300 l2  
  2. ffdf0300  7c92e510 7c92e514  
  3.   
  4. lkd> u 7c92e510  
  5. 7c92e510 8bd4            mov     edx,esp  
  6. 7c92e512 0f34            sysenter</span>  

windows中0x7FFE0000和0x0FFDF0000被映射到同一个物理地址,供4KB,但在用户模式下该地址是不可写的,内核模式下的可写,4K空间操作系统占用一部分,
余下的大约有3K
USER:0x7FFE0000
KERNEL:0x0FFDF0000
在Windbg可用dt nt!_KUSER_SHARED_DATA命令查看该共享区域
[plain] view plaincopy
  1. lkd> dt nt!_KUSER_SHARED_DATA  
  2.    +0x000 TickCountLow     : Uint4B  
  3.    +0x004 TickCountMultiplier : Uint4B  
  4.    +0x008 InterruptTime    : _KSYSTEM_TIME  
  5.    +0x014 SystemTime       : _KSYSTEM_TIME  
  6.    +0x020 TimeZoneBias     : _KSYSTEM_TIME  
  7.    +0x02c ImageNumberLow   : Uint2B  
  8.    +0x02e ImageNumberHigh  : Uint2B  
  9.    +0x030 NtSystemRoot     : [260] Uint2B  
  10.    +0x238 MaxStackTraceDepth : Uint4B  
  11.    +0x23c CryptoExponent   : Uint4B  
  12.    +0x240 TimeZoneId       : Uint4B  
  13.    +0x244 Reserved2        : [8] Uint4B  
  14.    +0x264 NtProductType    : _NT_PRODUCT_TYPE  
  15.    +0x268 ProductTypeIsValid : UChar  
  16.    +0x26c NtMajorVersion   : Uint4B  
  17.    +0x270 NtMinorVersion   : Uint4B  
  18.    +0x274 ProcessorFeatures : [64] UChar  
  19.    +0x2b4 Reserved1        : Uint4B  
  20.    +0x2b8 Reserved3        : Uint4B  
  21.    +0x2bc TimeSlip         : Uint4B  
  22.    +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE  
  23.    +0x2c8 SystemExpirationDate : _LARGE_INTEGER  
  24.    +0x2d0 SuiteMask        : Uint4B  
  25.    +0x2d4 KdDebuggerEnabled : UChar  
  26.    +0x2d5 NXSupportPolicy  : UChar  
  27.    +0x2d8 ActiveConsoleId  : Uint4B  
  28.    +0x2dc DismountCount    : Uint4B  
  29.    +0x2e0 ComPlusPackage   : Uint4B  
  30.    +0x2e4 LastSystemRITEventTickCount : Uint4B  
  31.    +0x2e8 NumberOfPhysicalPages : Uint4B  
  32.    +0x2ec SafeBootMode     : UChar  
  33.    +0x2f0 TraceLogging     : Uint4B  
  34.    +0x2f8 TestRetInstruction : Uint8B  
  35.    +0x300 SystemCall       : Uint4B  
  36.    +0x304 SystemCallReturn : Uint4B  
  37.    +0x308 SystemCallPad    : [3] Uint8B  
  38.    +0x320 TickCount        : _KSYSTEM_TIME  
  39.    +0x320 TickCountQuad    : Uint8B  
  40.    +0x330 Cookie           : Uint4B  


11EA = 1000111101010 = 13~14位选择服务描述表,选择KeServiceDescriptorTableShadow,系统共有4个服务描述表,第一个在ntoskrnl.exe中
并导出KeServiceDescriptorTable指针

可见该函数没做任何处理直接进入内核(win32k.sys)中,在Windbg反汇编:
[plain] view plaincopy
  1. lkd> uf win32k!NtUserRegisterHotKey  
  2. win32k!NtUserRegisterHotKey+0x34:  
  3. bf899720 33c0            xor     eax,eax    //eax = NULL  
  4. bf899722 eb29            jmp     win32k!NtUserRegisterHotKey+0x36 (bf89974d)  
  5.   
  6. win32k!NtUserRegisterHotKey:  
  7. bf899729 8bff            mov     edi,edi  
  8. bf89972b 55              push    ebp  
  9. bf89972c 8bec            mov     ebp,esp  
  10. bf89972e 56              push    esi  
  11. bf89972f e8b673f6ff      call    win32k!EnterCrit (bf800aea)  
  12. bf899734 f74510f07fffff  test    dword ptr [ebp+10h],0FFFF7FF0h    //fsModifiers是否有效,是否大于1000b 11111111111111110111111111110000  
  13.   
  14.   
  15. bf89973b 752d            jne     win32k!NtUserRegisterHotKey+0x14 (bf89976a)//fsModifiers无效则跳转  
  16.   
  17. win32k!NtUserRegisterHotKey+0x20:  
  18. bf89973d 8b4d08          mov     ecx,dword ptr [ebp+8]  //hWnd  
  19. bf899740 85c9            test    ecx,ecx    
  20. bf899742 74dc            je      win32k!NtUserRegisterHotKey+0x34 (bf899720)//hWnd == NULL  
  21.   
  22. win32k!NtUserRegisterHotKey+0x27:  
  23. bf899744 e86a7ef6ff      call    win32k!ValidateHwnd (bf8015b3)//则验证句柄  
  24. bf899749 85c0            test    eax,eax    
  25. bf89974b 7427            je      win32k!NtUserRegisterHotKey+0x30 (bf899774) //返回NULL  
  26.   
  27. win32k!NtUserRegisterHotKey+0x36:  
  28. bf89974d ff7514          push    dword ptr [ebp+14h]  //vk  
  29. bf899750 ff7510          push    dword ptr [ebp+10h]  //fsModifiers  
  30. bf899753 ff750c          push    dword ptr [ebp+0Ch]  //id  
  31. bf899756 50              push    eax      //pWnd  
  32. bf899757 e8aefeffff      call    win32k!_RegisterHotKey (bf89960a)  
  33. bf89975c 8bf0            mov     esi,eax  
  34.   
  35. win32k!NtUserRegisterHotKey+0x47:  
  36. bf89975e e8b373f6ff      call    win32k!LeaveCrit (bf800b16)  
  37. bf899763 8bc6            mov     eax,esi  
  38. bf899765 5e              pop     esi  
  39. bf899766 5d              pop     ebp  
  40. bf899767 c21000          ret     10h  
  41.   
  42. win32k!NtUserRegisterHotKey+0x14:  
  43. bf89976a 68ec030000      push    3ECh    //错误码:1004,参数无效  
  44. bf89976f e83da0f6ff      call    win32k!UserSetLastError (bf8037b1)  
  45.   
  46. win32k!NtUserRegisterHotKey+0x30:  
  47. bf899774 33f6            xor     esi,esi  
  48. bf899776 ebe6            jmp     win32k!NtUserRegisterHotKey+0x47 (bf89975e)  
  49.   
  50. /***************************************/  
  51. PWND FASTCALL ValidateHwnd(  
  52.      HWND hwnd);  

//NtUserRegisterHotKey伪代码:
[plain] view plaincopy
  1. BOOLEN APIENTRY  
  2. NtUserRegisterHotKey(HWND hWnd,  
  3.                      int id,  
  4.                      UINT fsModifiers,  
  5.                      UINT vk)  
  6. {  
  7.   BOOLEN bRet;  
  8.   PWND pWnd = NULL;  
  9.   EnterCrit();  
  10.   if(!(fsModifiers & 0x0FFFF7FF0h))  
  11.   {  
  12.     if(hWnd)  
  13.     {  
  14.       pWnd = ValidateHwnd(hWnd);  
  15.     }  
  16.     bRet = _RegisterHotKey(pWnd,id,fsModifiers,vk);  
  17.   }  
  18.   else  
  19.   {  
  20.     UserSetLastError(1004);//1004无效标志  
  21.     bRet = FALSE;  
  22.   }      
  23.   LeaveCrit();  
  24.   return bRet;  
  25. }  

//系统热键结构:
[plain] view plaincopy
  1. typedef struct _HOT_KEY_ITEM  
  2. {    
  3.   PETHREAD Thread;  
  4.   HWND spwnd;  
  5.   UINT fsModifiers;      
  6.   UINT vk;  
  7.   int id;  
  8.   struct _HOT_KEY_ITEM phkNext;  
  9. } HOT_KEY_ITEM, *PHOT_KEY_ITEM;  

_RegisterHotKey伪代码如下:
[plain] view plaincopy
  1. BOOL _RegisterHotKey(  
  2.            PWND pwnd,  
  3.            int id,  
  4.            UINT fsModifiers,  
  5.            UINT vk)  
  6. {  
  7.     PHOT_KEY_ITEM phk;  
  8.     BOOL fKeysExist = FALSE;  
  9.     PTHREADINFO ptiCurrent;  
  10.     PWINDOWSTATION pwinsta = _GetProcessWindowStation(NULL);  
  11.     DWORD ErrorCode;  
  12.    
  13.     ptiCurrent = gptiCurrent;  
  14.   
  15.   //如果调用者不是WindowStation初始化的线程和不适当的权限  
  16.     if(grpwinstaList && !CheckWinstaWriteAttributesAccess())  
  17.   {  
  18.         return FALSE;  
  19.     }  
  20.   
  21.   //不能为其他线程的窗口注册热键  
  22.     if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER))  
  23.   {  
  24.         if (GETPTI(pwnd) != ptiCurrent)  
  25.     {  
  26.             UserSetLastError(1408);  //1408错误码:无效窗口;它属于另一线程。  
  27.             return FALSE;  
  28.         }  
  29.     }  
  30.    
  31.     phk = FindHotKey(ptiCurrent, pwnd, id, fsModifiers, vk, FALSE, &fKeysExist);  
  32.   
  33.   //如果其他线程已经注册过该热键,返回FALSE  
  34.     if (fKeysExist)  
  35.   {  
  36.         UserSetLastError(1409);  //1409错误码:热键已被注册  
  37.         return FALSE;  
  38.     }  
  39.    
  40.     if (phk == NULL)  
  41.   {  
  42.   
  43.     //热键并未被注册  
  44.         phk = (PHOT_KEY_ITEM)HeavyAllocPool(sizeof(HOT_KEY_ITEM), TAG_HOTKEY);  
  45.   
  46.     //分配失败,返回FALSE  
  47.         if (phk == NULL)  
  48.     {  
  49.             return FALSE;  
  50.         }  
  51.       
  52.         phk->pti = ptiCurrent;  
  53.       
  54.         if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER))  
  55.     {  
  56.             phk->spwnd = NULL;  
  57.             HMAssignmentLock(&phk->spwnd, pwnd);  
  58.         }  
  59.     else  
  60.     {  
  61.             phk->spwnd = pwnd;  
  62.         }  
  63.         phk->fsModifiers = fsModifiers;  
  64.         phk->vk = vk;  
  65.         phk->id = id;  
  66.   
  67.     //插入到系统热键链表中  
  68.     //gphkFirst - 这是不导出变量存储了系统结构热键(phkNext指向下一个热键结构域)地址  
  69.         phk->phkNext = gphkFirst;  
  70.         gphkFirst = phk;  
  71.       
  72.     }  
  73.   else  
  74.   {  
  75.     //如果本线程已注册过该热键,则重新覆盖  
  76.         phk->fsModifiers = fsModifiers;  
  77.         phk->vk = vk;  
  78.     }    
  79.     return TRUE;  
  80. }  


//用Windbg查看下gphkFirst
[plain] view plaincopy
  1. lkd> dd gphkFirst L1  
  2. bf9af814  e2ce10d8  

e2ce10d8就是最近一次软件向系统注册的全局热键,继续
[plain] view plaincopy
  1. lkd> dd e2ce10d8 l6  
  2. e2ce10d8  e2265008 bbe35a28 00000003 00000054  
  3. e2ce10e8  0000c024 e2291a68  

e2265008 是ETHREAD,查看发现是QQ的一个线程
bbe35a28 是窗口句柄
00000003 是功能键11,说明有Ctrl+Alt键
00000054 是VK_?,0x54对应ASCI码的大写T,Ctrl+ATL+T(QQ上:发送腾讯微博的)
0000c024 是热键的ID
e2291a68 是下一个热键结构

[plain] view plaincopy
  1. PHOT_KEY_ITEM FindHotKey(  
  2.            PTHREADINFO ptiCurrent,  
  3.            PWND pwnd,  
  4.            int id,  
  5.            UINT fsModifiers,  
  6.            UINT vk,  
  7.            BOOL fUnregister,  
  8.            PBOOL pfKeysExist)  
  9. {  
  10.     PHOT_KEY_ITEM phk, phkRet, phkPrev;  
  11.   
  12.   //初始化返回值  
  13.     *pfKeysExist = FALSE;  
  14.     phkRet = NULL;  
  15.    
  16.     phk = gphkFirst;  
  17.    
  18.     while (phk)  
  19.   {  
  20.    
  21.         if ((phk->pti == ptiCurrent) && (phk->spwnd == pwnd) && (phk->id == id))  
  22.     {  
  23.             if (fUnregister)  
  24.       {  
  25.   
  26.         //摘掉热键  
  27.                 if (phk == gphkFirst)  
  28.         {  
  29.                     gphkFirst = phk->phkNext;  
  30.                 }  
  31.         else  
  32.         {  
  33.                     phkPrev->phkNext = phk->phkNext;  
  34.                 }  
  35.           
  36.                 if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER))  
  37.         {  
  38.                     Unlock(&phk->spwnd);  
  39.                 }  
  40.                 UserFreePool((PVOID)phk);  
  41.           
  42.                 return((PHOT_KEY_ITEM)1);  
  43.             }  
  44.             phkRet = phk;  
  45.         }  
  46.   
  47.     //如果热键已经注册过,设置已存在标志  
  48.         if ((phk->fsModifiers == fsModifiers) && (phk->vk == vk))  
  49.     {  
  50.             if (phk->spwnd == PWND_FOCUS)  
  51.       {  
  52.                 if (phk->pti == ptiCurrent)  
  53.         {  
  54.                     *pfKeysExist = TRUE;  
  55.                 }  
  56.             }  
  57.       else  
  58.       {  
  59.                 *pfKeysExist = TRUE;  
  60.             }  
  61.         }  
  62.       
  63.         phkPrev = phk;  
  64.         phk = phk->phkNext;  
  65.     }  
  66.    
  67.     return phkRet;  
  68. }  

//遍历系统热键
[plain] view plaincopy
  1. VOID DumpHotKeys()  
  2. {  
  3.   ULONG dwAddr;  
  4.   KAPC_STATE ApcState;  
  5.   PETHREAD pThread;  
  6.   PEPROCESS pProc;  
  7.   PHOTKEY phk;  
  8.   
  9.   //必须在GUI线程中遍历  
  10.   KeStackAttachProcess( pExpEprocess , &ApcState );  
  11.   dwAddr = *(PULONG)gphkFirst;  
  12.   KeUnstackDetachProcess(&ApcState);  
  13.   phk = (PHOTKEY)dwAddr;  
  14.   
  15.   //解析系统所有热键  
  16.   while( phk != NULL )  
  17.   {  
  18.     pThread = *(PULONG)phk->pti;  
  19.     //0x220位置指向当前线程的EPROCESS  
  20.     pProc   = *(PULONG)( (ULONG)pThread + 0x220 );  
  21.   
  22.     //EPROCESS + 0x174指向进程名字  
  23.     KdPrint(("Process Name : %s\n" , (ULONG)pProc + 0x174 ));  
  24.     KdPrint(("id : %d\n" , phk->id ));  
  25.     KdPrint(("Combination : %s + %X\n" , GetButton( phk->fsModifiers ) , phk->vk ));  
  26.     KdPrint(("------------------------------------------\n"));  
  27.     phk = phk->phkNext;  
  28.   }  
  29. }  

0 0
原创粉丝点击