【转载】禁止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+Alt+Del、Win+L等任意系统热键_第1张图片

按下 Ctrl+Shift+Esc,我们往下跟踪,我们会发现 winlogon.exe 会调用 WLGeneric_TaskManager_Enter 例程。

【转载】禁止Ctrl+Alt+Del、Win+L等任意系统热键_第2张图片

按下 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最终走到这里
{
    // do something...
}

我的思路就是这样, 在 x可以通过窗口消息处理过程很容易找到上述代码,但 Win7 怎么找呢?

现在总结下 Winlogon.exe 是怎样处理系统快捷键的,Winlogon.exe 从 WinMain 进入到 StateMachineRun StateMachineRun 调用 SignalManagerWaitForSignal 函数循环等待系统快捷键。

SignalManagerWaitForSignal 函数应该是微软自己使用的一种同步机制,它内部的实现就是调用 WaitForSingleObject 函数等待某个事件,那是谁把该事件设置了信号?

我们下断点 SetEvent,按下 Ctrl+Alt+Del ,程序被断下来了,这是查看堆栈,因为太长了,我直接发截图吧:

【转载】禁止Ctrl+Alt+Del、Win+L等任意系统热键_第3张图片

可以看到 SignalManagerSetSignal 应该就是设置信号的,它内部调用了 SetEvent
我们可以得出结论,Winlogon.exe 内部通过 RPC 来接收系统快捷键到来的信息,然后设置信号让主线程恢复运行,处理快捷键。

我们还发现其中 WMsgKMessageHandler 调用了 WlStateMachineSetSignal,听名字是处理消息的。我们尝试去跟踪 WMsgKMessageHandler 函数,下 WMsgKMessageHandler 函数断点,重新按下 Ctrl+Alt+Del

我发现了一个有趣的现象,跟踪程序到了这里,我们可以看到这里比较 EDX,如果 EDX 不等于4的就继续跳转比较,而 Win+L 到这里的 EDX 是不同的,我们可以肯定这里 EDX 就是快捷键的 ID,如果快捷键的 ID 和某个 ID 相等,程序就会跳到相应的位置执行。

【转载】禁止Ctrl+Alt+Del、Win+L等任意系统热键_第4张图片

直接发证据,我的 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 0Win+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

你可能感兴趣的:(Windows,基础编程,windows,交互,微软,测试工具)