Win7 禁止Ctrl+Alt+Del、Win+L等任意系統熱鍵

標 題: 【原創】Win7 修改Winlogon.exe進程一個字節禁止Ctrl+Alt+Del、Win+L等任意系統熱鍵
作 者: heiheiabcd
時 間: 2012-12-01,10:08:55
鏈 接: http://bbs.pediy.com/showthread.php?t=159346

由於想做個屏幕鎖程序,因此想研究了下Win7的Ctrl+Alt+Del,我對win7的安全機制一點都不懂,希望有不對的地方來大家多多指點

首先說下xp下的快捷鍵,xp下的快捷鍵是通過CreateWindowEx函數創建標題爲"SAS Window"的窗口。
而Ctrl+Alt+Del、Ctrl+Shift+Esc快捷鍵以熱鍵的方式註冊到SAS Window上,因此注入到Winlogon.exe進程調用UnregisterHotKey就可以取消快捷鍵,但取消後無法恢復

那win7下的是怎樣的呢?起碼在Winlogon.exe的輸入表沒看到CreateWindowEx,另外我通過注入Winlogon.exe進程,枚舉窗口,一個窗口都枚舉不了,這說明在安全桌面一個窗口都沒有,那Winlogon.exe到底是怎樣響應Ctrl+Alt+Del的呢?



想法一
我以前有一個想法,就是Hook SwitchDesktop來禁止Winlogon.exe切換到安全桌面,這樣Ctrl+Alt+Del就自然失效了,但這樣還是有一些問題,看下面的這段Winlogon.exe切換桌面的代碼,發現如果SwitchDesktop調用失敗後並不會立馬返回,而是重複調用SwitchDesktop,直到SwitchDesktop調用成功
代碼:
DWORD __stdcall sub_100212B(HDESK hDesktop, int a2, int a3)
{
  unsigned int v3; // ebx@1
  DWORD i; // edi@1
  DWORD v5; // esi@2
  int v6; // eax@3

  v3 = 0;
  for ( i = 0; ; Sleep(i) )
  {
    v5 = 0;
    v6 = a3 ? SwitchDesktopWithFade(hDesktop, a3) : SwitchDesktop(hDesktop);
    if ( v6 )
      break;
    v5 = GetLastError();
    if ( off_103C144 != &off_103C144 )
    {
      if ( *((_BYTE *)off_103C144 + 28) & 4 )
      {
        if ( *((_BYTE *)off_103C144 + 25) >= 2u )
          sub_1002A84(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 11, dword_1010308, v5, a3);
      }
    }
    if ( v3 >= 0xA )
    {
      if ( !a2 )
        return v5;
    }
    else
    {
      ++v3;
      i += 100;
    }
  }
  return v5;
}


想法二
另外一個想法是直接掛起Winlogon.exe進程,由於Winlogon.exe只負責登錄和註銷等工作,因此掛起它也沒什麼,但還是上面的問題,如果在掛起Winlogon.exe進程期間,按下了Ctrl+Alt+Del,等我們恢復掉掛起的Winlogon.exe進程,Ctrl+Alt+Del還是會得到響應



想法三
難道禁止Ctrl+Alt+Del真的不可能嗎?這是我對Ctrl+Alt+Del的一點分析流程,希望能對大家有幫助

衆所周知Ctrl+Shift+Esc是打開任務管理器的快捷鍵,因此我打算從任務管理器入手,由於我虛擬機裝不了win7 32位,只能拿我的真實機win7 64位來做實驗,相信win7 32和64區別應該不大

用IDA打開winlogon.exe後,我們打開字符串窗口,點擊SoftwareSASGeneration往上查看,
儼然看到幾個字符串"osk.exe -s","DisableChangePassword","DisableSwitchUserOption","DisableTaskMgr","taskmgr.exe /%lu "。

雙擊對"taskmgr.exe /%lu "的引用,來到下面這段代碼,很明顯這是打開任務管理器,我最在意的是誰調用的這段代碼,可惜IDA沒顯示誰調用了函數,但是有數據引用。後來通過WinDbg才知道這個函數名字叫WLGeneric_TaskManager_Enter
代碼:
int __stdcall sub_102916D(int a1)
{
  void *v1; // esi@1
  int v3; // [sp+10h] [bp-B0h]@5
  char Dst; // [sp+14h] [bp-ACh]@5
  int v5; // [sp+18h] [bp-A8h]@18
  int v6; // [sp+3Ch] [bp-84h]@18
  __int16 v7; // [sp+40h] [bp-80h]@18
  void *v8; // [sp+54h] [bp-6Ch]@21
  HANDLE hObject; // [sp+58h] [bp-68h]@22
  int v10; // [sp+64h] [bp-5Ch]@5
  wchar_t *v11; // [sp+68h] [bp-58h]@18
  int v12; // [sp+6Ch] [bp-54h]@9
  wchar_t Dest; // [sp+70h] [bp-50h]@19
  CPPEH_RECORD ms_exc; // [sp+A8h] [bp-18h]@5

  v1 = off_103C144;
  if ( off_103C144 != &off_103C144 )
  {
    if ( *((_DWORD *)off_103C144 + 7) & 0x100 )
    {
      if ( *((_BYTE *)off_103C144 + 25) >= 4u )
      {
        sub_1001838(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 160, dword_1001F40);
        v1 = off_103C144;
      }
    }
  }
  v3 = 0;
  memset(&Dst, 0, 0x40u);
  v10 = *(_DWORD *)(a1 + 4);
  ms_exc.disabled = 0;
  if ( v1 != &off_103C144 )
  {
    if ( *((_BYTE *)v1 + 28) & 1 )
    {
      if ( *((_BYTE *)v1 + 25) >= 5u )
        sub_1001F55(*((_DWORD *)v1 + 4), *((_DWORD *)v1 + 5), 161, dword_1001F40, *(_DWORD *)(a1 + 24));
    }
  }
  sub_1004BC9(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", L"DisableTaskMgr", 0, &v12);
  sub_1004BC9(L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", L"DisableTaskMgr", v12, &v12);
  if ( v12 )
  {
    if ( off_103C144 != &off_103C144 )
    {
      if ( *((_BYTE *)off_103C144 + 28) & 1 )
      {
        if ( *((_BYTE *)off_103C144 + 25) >= 4u )
          sub_1001838(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 162, dword_1001F40);
      }
    }
  }
  else
  {
    if ( off_103C144 != &off_103C144 )
    {
      if ( *((_BYTE *)off_103C144 + 28) & 1 )
      {
        if ( *((_BYTE *)off_103C144 + 25) >= 5u )
          sub_1001838(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 163, dword_1001F40);
      }
    }
    v3 = 68;
    v6 = 1;
    v7 = 5;
    v5 = 0;
    v11 = 0;
    if ( *(_DWORD *)(a1 + 24) )
    {
      if ( sub_1004D43(&Dest, 26, L"taskmgr.exe /%lu ", *(_DWORD *)(a1 + 24)) >= 0 )
        v11 = &Dest;
    }
    if ( !sub_1008379(L"taskmgr.exe", v11, 0, &v3, &v8, 0) )
    {
      CloseHandle(hObject);
      CloseHandle(v8);
    }
  }
  ms_exc.disabled = -2;
  sub_1029343();
  return sub_1001D07(0, 0);
}
什麼都別說了,用64位WinDbg附加winlogon.exe進程,在WLGeneric_TaskManager_Enter函數下斷點,按下Ctrl+Alt+Del,程序停在WLGeneric_TaskManager_Enter,繼續往上返回,我們看到最終的調用來自StateMachineRun

說起StateMachineRun函數,我們查看winlogon.exe的調用堆棧,可以看到winlogon.exe從WinMain執行,然後進入StateMachineRun,StateMachineRun內部會調用SignalManagerWaitForSignal循環等待系統快捷鍵的來臨
代碼:
winlogon!StateMachineRun+0x404
winlogon!WinMain+0x13a3
winlogon!I_WMsgkSendMessage+0x252
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
在SignalManagerWaitForSignal下一行下斷點,可以看到一旦Win+L,Win+U,Ctrl+Alt+Del任何一個快捷鍵按下都會斷下來
按下Ctrl+Shift+Esc,我們往下跟蹤,我們會發現winlogon.exe會調用WLGeneric_TaskManager_Enter例程
按下Win+U,我們可以看到,Winlogon.exe調用了WLGeneric_AccesNotifyAsUser_Enter例程
Win+L和Ctrl+Alt+Del有點特殊,下SwitchDesktop斷點

查看Win+L的堆棧,Win+L的例程應該是WLGeneric_InitiateLock_Execute
代碼:
USER32!SwitchDesktop
winlogon!ResilientSwitchDesktopWithFade+0x32
winlogon!CSession::SwitchDesktop+0x2a2
winlogon!WlAccessibilitySwitchDesktop+0x20
winlogon!WLGeneric_InitiateLock_Execute+0xff
winlogon!StateMachineWorkerCallback+0x7f
ntdll!TppWorkpExecuteCallback+0xa4
ntdll!TppWorkerThread+0x5ff
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
Ctrl+Alt+Del的堆棧是,Ctrl+Alt+Del例程應該是WLGeneric_CAD_Execute
代碼:
USER32!SwitchDesktop
winlogon!ResilientSwitchDesktopWithFade+0x32
winlogon!CSession::SwitchDesktop+0x2a2
winlogon!WlAccessibilitySwitchDesktop+0x20
winlogon!HandleSecurityOptions+0x51
winlogon!WLGeneric_CAD_Execute+0x76
winlogon!StateMachineWorkerCallback+0x7f
ntdll!TppWorkpExecuteCallback+0xa4
ntdll!TppWorkerThread+0x5ff
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
我們先看一下SignalManagerWaitForSignal函數,主要看紅色標註地方,修改我說的那個判斷標記的地方,別讓程序出SignalManagerWaitForSignal,讓它一直用WaitForSingleObject函數處等待。如果按我說的做了,然後快捷鍵都將不會響應,而且是可恢復的,但還是有上面幾個想法的問題
代碼:
int __stdcall sub_1001C19(int a1, unsigned int a2, int a3, int a4, int a5, int a6)
{
  void *v6; // eax@2
  unsigned int v7; // eax@6
  int v8; // ecx@6
  int result; // eax@10
  int v10; // esi@16
  char v11; // al@22
  signed int v12; // [sp+Ch] [bp-4h]@1

  *(_DWORD *)a5 = -1;
  v12 = 0;//標記,設置爲FALSE
  *(_DWORD *)a6 = 0;
  *(_DWORD *)(a6 + 4) = 0;
  *(_DWORD *)(a6 + 8) = 0;
  *(_DWORD *)(a6 + 12) = 0;
LABEL_2:
  v6 = off_103C144;
LABEL_3:
  if ( v6 != &off_103C144 )
  {
    if ( *((_BYTE *)v6 + 28) & 2 )
    {
      if ( *((_BYTE *)v6 + 25) >= 5u )
        sub_1001838(*((_DWORD *)v6 + 4), *((_DWORD *)v6 + 5), 25, dword_1025A24);
    }
  }
  while ( 1 )
  {
    RtlEnterCriticalSection(a1);
    v7 = 0;
    if ( a2 )
    {
      v8 = a3;
      while ( 1 )
      {
        *(_DWORD *)a5 = v7;
        if ( *(_DWORD *)(*(_DWORD *)(a1 + 32) + 4 * *(_DWORD *)v8) )\\這裏有個ja跳轉,nop掉,函數就不會出去
          break;
        ++v7;
        v8 += 12;
        if ( v7 >= a2 )
          goto LABEL_10;
      }
      v10 = *(_DWORD *)(a1 + 36) + 16 * *(_DWORD *)v8;
      v8 = 4 * *(_DWORD *)v8;
      *(_DWORD *)a6 = *(_DWORD *)v10;
      v10 += 4;
      *(_DWORD *)(a6 + 4) = *(_DWORD *)v10;
      v10 += 4;
      *(_DWORD *)(a6 + 8) = *(_DWORD *)v10;
      v12 = 1;  //標記,設置TRUE     
      *(_DWORD *)(a6 + 12) = *(_DWORD *)(v10 + 4);
      if ( off_103C144 != &off_103C144 )
      {
        if ( *((_BYTE *)off_103C144 + 28) & 2 )
        {
          if ( *((_BYTE *)off_103C144 + 25) >= 5u )
            sub_1001F55(
              *((_DWORD *)off_103C144 + 4),
              *((_DWORD *)off_103C144 + 5),
              26,
              dword_1025A24,
              *(_DWORD *)(*(_DWORD *)(a1 + 32) + v8));
        }
      }
    }
LABEL_10:
    result = RtlLeaveCriticalSection(v8, a1);
    if ( v12 )//這裏判斷標記,修改這裏,把這裏的跳轉nop掉,函數照樣也出不去
      return result;
    if ( WaitForSingleObject(*(HANDLE *)(a1 + 24), 0xFFFFFFFFu) )
    {
      v11 = GetLastError();
      if ( off_103C144 != &off_103C144 )
      {
        if ( *((_BYTE *)off_103C144 + 28) & 2 )
        {
          if ( *((_BYTE *)off_103C144 + 25) >= 2u )
            sub_1001F55(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 27, dword_1025A24, v11);
        }
      }
      Sleep(0x3E8u);
    }
    v6 = off_103C144;
    if ( off_103C144 != &off_103C144 )
    {
      if ( *((_BYTE *)off_103C144 + 28) & 2 && *((_BYTE *)off_103C144 + 25) >= 5u )
      {
        sub_1001838(*((_DWORD *)off_103C144 + 4), *((_DWORD *)off_103C144 + 5), 28, dword_1025A24);
        goto LABEL_2;
      }
      goto LABEL_3;
    }
  }
}


想法四,最終完美實現一個字節更改並且可恢復
我們想下winlogon.exe是怎樣判斷是哪個快捷鍵的
代碼:
switch(快捷鍵ID)
{
case Ctrl+Alt+Del://我們修改改ID,讓程序無法執行該方法
{
  打開桌面安全選項();
}
break;
case Win+L://我們修改改ID,讓程序無法執行該方法
{
  鎖定桌面();
}
break;
case Ctrl+Shift+Esc://我們修改改ID,讓程序無法執行該方法
{
  打開任務管理器();
}
break;
case Win+P://我們修改改ID,讓程序無法執行該方法
{
  切換窗口();
}
break;
default://不做任何操作,讓winlogon.exe最終走到這裏
{

}
我的思路就是這樣, 在xp可以通過窗口消息處理過程很容易找到上述代碼,但Win7怎麼找呢?

現在總結下Winlogon.exe是怎樣處理系統快捷鍵的,Winlogon.exe從WinMain進入到StateMachineRun,StateMachineRun調用SignalManagerWaitForSignal函數循環等待系統快捷鍵。

SignalManagerWaitForSignal函數應該是微軟自己使用的一種同步機制,它內部的實現就是調用WaitForSingleObject函數等待某個事件,那是誰把該事件設置了信號?

我們下斷點SetEvent,按下Ctrl+Alt+Del,程序被斷下來了,這是查看堆棧,因爲太長了,我直接發截圖吧
可以看到SignalManagerSetSignal應該就是設置信號的,它內部調用了SetEvent。
我們可以得出結論,Winlogon.exe內部通過RPC來接收系統快捷鍵到來的信息,然後設置信號讓主線程恢復運行,處理快捷鍵

我們還發現其中WMsgKMessageHandler調用了WlStateMachineSetSignal,聽名字是處理消息的。我們嘗試去跟蹤WMsgKMessageHandler函數,下WMsgKMessageHandler函數斷點,重新按下Ctrl+Alt+Del。

我發現了一個有趣的現象,跟蹤程序到了這裏,我們可以看到這裏比較EDX,如果EDX不等於4的就繼續跳轉比較,而Win+L到這裏的EDX是不同的,我們可以肯定這裏EDX就是快捷鍵的ID,如果快捷鍵的ID和某個ID相等,程序就會跳到相應的位置執行
直接發證據,我的Win7 64位是這樣的
代碼:
.text:00000001000170B0                 cmp     ebx, 4
.text:00000001000170B3                 jnz     short loc_10001710B;不等於4跳轉
省略。。。
.text:000000010001710B                 cmp     ebx, 5
.text:000000010001710E                 jnz     short loc_10001713D;不等於5跳轉
省略。。。
.text:000000010001713D                 cmp     ebx, 6
.text:0000000100017140                 jnz     short loc_10001717D;不等於6跳轉
省略。。。
.text:000000010001717D                 cmp     ebx, 7
.text:0000000100017180                 jnz     short loc_1000171B0;不等於7跳轉
省略。。。
.text:00000001000171B0                 cmp     ebx, 8
.text:00000001000171B3                 jnz     short loc_1000171E3;不等於8跳轉
省略。。。
text:00000001000171E3                 cmp     ebx, 9
.text:00000001000171E6                 jnz     short loc_100017219;不等於9跳轉
省略。。。
.text:0000000100017219                 test    ebx, ebx
.text:000000010001721B                 jnz     loc_1000173CA;不等於0跳轉
省略。。。
可以看出一共有7個快捷鍵,其中Ctrl+Alt+Del的ID位0,Win+L的ID爲5,文章在這裏要說聲結束了,至於怎麼屏蔽。比喻我們要屏蔽Win+L,可以把和000000010001710B處的"cmp     ebx, 5"改成"cmp     ebx, aa",這樣程序永遠也不能跳到本應該屬於它的處理例程,該方法應該不會有任何問題,我反正測試通過

向32位Win7的朋友左旭東要了個他的Winlogon.exe逆向了,再次表示感謝他,我在Win7 32照樣發現了上述代碼處,32位Win7的朋友也可以仿照
代碼:
.text:0101621A                 cmp     eax, 4
.text:0101621D                 jz      loc_101B779
.text:01016223                 cmp     eax, 5
.text:01016226                 jz      loc_101B7BA
.text:0101622C                 cmp     eax, 6
.text:0101622F                 jz      loc_101B7E6
.text:01016235                 cmp     eax, 7
.text:01016238                 jz      loc_101B810
.text:0101623E                 cmp     eax, 8
.text:01016241                 jz      loc_101B83A
.text:01016247                 cmp     eax, 9
.text:0101624A                 jz      loc_101B864
.text:01016250                 test    eax, eax
.text:01016252                 jnz     short loc_1016296

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章