作 者: 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); }
說起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
查看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
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
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最終走到這裏 { }
現在總結下Winlogon.exe是怎樣處理系統快捷鍵的,Winlogon.exe從WinMain進入到StateMachineRun,StateMachineRun調用SignalManagerWaitForSignal函數循環等待系統快捷鍵。
SignalManagerWaitForSignal函數應該是微軟自己使用的一種同步機制,它內部的實現就是調用WaitForSingleObject函數等待某個事件,那是誰把該事件設置了信號?
我們下斷點SetEvent,按下Ctrl+Alt+Del,程序被斷下來了,這是查看堆棧,因爲太長了,我直接發截圖吧
我們可以得出結論,Winlogon.exe內部通過RPC來接收系統快捷鍵到來的信息,然後設置信號讓主線程恢復運行,處理快捷鍵
我們還發現其中WMsgKMessageHandler調用了WlStateMachineSetSignal,聽名字是處理消息的。我們嘗試去跟蹤WMsgKMessageHandler函數,下WMsgKMessageHandler函數斷點,重新按下Ctrl+Alt+Del。
我發現了一個有趣的現象,跟蹤程序到了這裏,我們可以看到這裏比較EDX,如果EDX不等於4的就繼續跳轉比較,而Win+L到這裏的EDX是不同的,我們可以肯定這裏EDX就是快捷鍵的ID,如果快捷鍵的ID和某個ID相等,程序就會跳到相應的位置執行
.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跳轉 省略。。。
向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