|  | ||||||||||||||||||||||||||||||
|   |  | 
 | 
 | |||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
| 
 | 
 Блокируем Ctrl-Alt-DelИсточник: pblog Доброго времени суток. На многих форумах программистов очень часто встречается вопрос "Как заблокировать комбинацию клавиш Ctrl-Alt-Del?". В этой статье я расскажу, как можно реально заблокировать комбинацию клавиш Ctrl-Alt-Del без каких-либо извращений с заменой файлов и прочего. Статья не рассчитана на новичков, читатель этой статьи как минимум должен знать, что такое инжект и запуск удалённых потоков. Доброго времени суток. На многих форумах программистов очень часто встречается вопрос "Как заблокировать комбинацию клавиш Ctrl-Alt-Del?". В этой статье я расскажу, как можно реально заблокировать комбинацию клавиш Ctrl-Alt-Del без каких-либо извращений с заменой файлов и прочего. Статья не рассчитана на новичков, читатель этой статьи как минимум должен знать, что такое инжект и запуск удалённых потоков.    Первое что хотелось бы сказать - это то, что комбинация клавиш Ctrl-Alt-Del предназначена не только для вызова диспетчера задач. В первую очередь она предназначена для вызова окна процесса Winlogon, в котором у нас имеется возможность завершить текущий сеанс, завершить работу компьютера, и, разумеется, вызвать диспетчер задач. Те, кто ставил в WinXP настройки повышенной безопасности, тем знакомо вот это окно. Сначала подумаем, как можно заблокировать диспетчер задач Windows. Многим сразу придёт в голову идея об удалении или замене файла taskmgr.exe. Во-первых это не так просто так как в Windows XP введена система защиты файлов и при любом изменении её системных файлов она их сразу восстановит. Резервные копии важных файлов находятся в папке system32dllcache. Папка system32 и system32dllcache взаимно связаны и восстанавливаются, друг от друга удаление из system32dllcache влечёт восстановление из system32. Поэтому надо удалять (или заменять) файл taskmgr.exe из обеих папок одновременно. Ладно, мы удалили (или заменили) этот файл, но радость не долгая, выводится сообщение о повреждении системных файлов Windows и просьба вставить диск с дистрибутивом для обновления системы. В принципе и это тоже можно обойти. К чему я веду? К тому, что замена или удаление файлов это не вариант, да и "через жопу" это получается. Более внимательные пользователи заметят, что диспетчер задач Windows не запускается два раза, т.е. если он уже запущен, то во второй раз он уже не запустится. И возникает идея о скрытии окна диспетчера, окно скрыто, а он работает и в результате, как ни нажимай Ctrl-Alt-Del или Ctrl-Shift-Esc мы ничего не получим. Следующий код блокирует и разблокирует диспетчер задач Windows. uses shellapi;
procedure TForm1.Button1Click(Sender: TObject);
var
  TDWH:THandle;
begin
  TDWH:=FindWindow(nil,'Диспетчер задач Windows');
  if TDWH=0 then ShellExecute(0,'open','taskmgr.exe',nil,nil,SW_HIDE)
            else ShowWindow(TDWH,SW_HIDE);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  TDWH:THandle;
begin
  TDWH:=FindWindow(nil, 'Диспетчер задач Windows');
  ShowWindow(TDWH,SW_SHOWNORMAL);
end;Всё очень просто. Также вдобавок можно перемещать окно диспетчера далеко за пределы экрана. Итак, задача блокировки диспетчера задач успешно решена. Но наша цель это комбинация клавиш Ctrl-Alt-Del или Ctrl-Shift-Esc. Первая идея, которая возникает, когда надо перехватить комбинации клавиш Ctrl-Alt-Del или Ctrl-Shift-Esc - это воспользоваться стандартным механизмом хуков. Но те, кто уже пытался так сделать, скажут, что через стандартный механизм хуков нельзя перехватить ни одну из важных комбинаций. И возникает такое ощущение, что эти комбинации можно перехватить разве что, написав драйвер-фильтр режима ядра. Но есть другой способ. Итак, наша цель - это процесс winlogon.exe, этот процесс управляет входом пользователей в систему и выходом из нее. Процесс winlogon.exe один из основных процессов отвечающих за безопасность системы. Этот процесс нельзя завершить из диспетчера задач, но можно завершить любой другой сторонней программой. Вот только после завершения winlogon.exe сразу буден сгенерирован BSOD. Итак, процесс winlogon.exe очень важен. В нём находится окно с заголовком "SAS window" и классом "SAS Window class" именно этому и только этому окну посылается сообщение WM_HOTKEY, когда были нажаты комбинации Ctrl-Alt-Del или Ctrl-Shift-Esc и ещё несколько важных комбинаций. Найти это окно через функцию FindWindow нельзя, вернее можно, но только будучи в контексте процесса winlogon. Из обычной программы найти это окно нельзя. Итак, наша задача подменить оконную функцию окна "SAS window" и отлавливать в нём сообщения WM_HOTKEY, если нажаты эти волшебные комбинации, то просто не вызываем оригинальную оконную функцию. Но чтобы, всё это сделать, нам надо быть в контексте процесса winlogon. Самый простой способ - это внедрить в него нашу DLL. Вот собственно и код этой DLL library HookDLL;
uses
  Windows,Messages;
var
  OldWndProc: pointer;
  ThreadID:DWORD;
const
  CtrlAltDel_CODE = (VK_DELETE shl 16) or (MOD_CONTROL or MOD_ALT);
  CtrlShiftEsc_CODE = (VK_ESCAPE shl 16) or (MOD_CONTROL or  MOD_SHIFT);
function GethWnd: HWND;
begin
  result:= FindWindow('SAS Window class','SAS window');
end;  
function NewWndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  if  msg=WM_HOTKEY then
   begin
    if (lParam=CtrlAltDel_CODE) or (lParam = CtrlShiftEsc_CODE)  then
     result:= 0                                                  else
     result:= CallWindowProc(OldWndProc, hWnd, Msg, wParam, lParam);
   end                         else
   result:= CallWindowProc(OldWndProc, hWnd, Msg, wParam, lParam);
end;
procedure SetWndProc(hWnd: HWND);
begin
  OldWndProc:= pointer(SetWindowLong(hWnd, GWL_WNDPROC, cardinal(@NewWndProc)));
end;
procedure UnSetWndProc(hWnd: HWND);
begin
  SetWindowLong(hWnd, GWL_WNDPROC, cardinal(OldWndProc));
end;
function HookThread(PARAM1:DWORD):DWORD; stdcall;
var
  ThrID:DWORD;
begin
  Result:=0;
  SetWndProc(GethWnd);
  sleep(60*1000);
  UnSetWndProc(GethWnd);
  sleep(10);
  CreateThread(nil,0,GetProcAddress(GetModuleHandle('kernel32'),'FreeLibrary'),pointer(hInstance),0,ThrID);
end;
begin
  CreateThread(nil,0,@HookThread,nil,0,ThreadID);
end.Код этой DLL блокирует комбинации ровно на минуту, потом DLL выгружается из памяти winlogon. Так же можно изменить функцию NewWndProc так чтобы перенаправить заблокированные комбинации на другие, также можно заблокировать любую комбинацию клавиш которую нельзя перехватить через стандартный механизм хуков. А вот и код, который загрузит нашу DLL в процесс winlogon. function Start(ProcessID: Cardinal; DllFileName: string): Boolean;
var
  hProcess, hTh: THandle;
  BytesWritten, ThreadID, DllNameLen: Cardinal;
  LoadLibraryProc, MemPtr: Pointer;
  ExitCode: DWord;
begin
  Result := false;
  SetDebugPriv();
  hProcess := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE,true, ProcessID);
  if hProcess = 0 then exit;
  DllNameLen := Length(DllFileName) + 1;
  MemPtr := VirtualAllocEx(hProcess, nil, DllNameLen, MEM_COMMIT, PAGE_READWRITE);
  if MemPtr  nil then
   begin //3
    if WriteProcessMemory(hProcess, MemPtr, PChar(DllFileName), DllNameLen, BytesWritten) then
     begin //2
      LoadLibraryProc := GetProcAddress(GetModuleHandle('kernel32.dll'), 'LoadLibraryA');
      hTh := CreateRemoteThread(hProcess, nil, 0, LoadLibraryProc, MemPtr, 0, ThreadID);
      if hTh  0 then
       begin  //1
        if (WaitForSingleObject(hTh, INFINITE) = WAIT_OBJECT_0) and GetExitCodeThread(hTh, ExitCode) then
         Result := ExitCode  0;
        CloseHandle(hTh);
       end;  //1
     end; //2
    VirtualFreeEx(hProcess, MemPtr, 0, MEM_RELEASE);
   end;  //3
  CloseHandle(hProcess);
end;
Эта процедура принимает ID процесса winlogon и путь к нашей DLL. Разумеется, до этого надо позаботиться о получении привилегии "SeDebugPrivilege" чтобы можно было открыть процесс winlogon c нужным нам доступом. Полный код программы и DLL смотрите в архиве с исходниками в конце статьи. Итак, приступим. Нам надо будет внедрить в процесс winlogon код, который будет находить окно и изменять оконную процедуру целевого окна и код самой оконной процедуры. Разумеется, внедряемый код должен быть базонезависимым, так как мы заранее не знаем, по какому адресу мы его скопируем, и ему нужны адреса используемых им API функций. На Delphi сделать это будет уже проблематично. И поэтому сделаем мы это на ассемблере, так как на нём проще всего написать базонезависимый код. Сначала приведу код, который будет выполняться в контексте процесса winlogon section '.remcode' code executable readable writeable
;----------------------------------------
; REMOTE CODE START ---------------------
;----------------------------------------
RemoteCodeStart:
    call .delta
   .delta:
    pop ebp
    sub ebp, .delta
    lea eax,  [ebp+SAS_WndTitle]
    push eax
    lea eax,  [ebp+SAS_WndClass]
    push eax
    call [ebp+pFindWindowA]
    mov [ebp+SAS_WndHandle], eax
    test eax, eax
    jnz @f
    jmp .thrend
   @@:
    lea eax, [ebp+NewWindowProc]
    push eax
    push GWL_WNDPROC
    push [ebp+SAS_WndHandle]
    call [ebp+pSetWindowLongA]
    mov [ebp+OldWindowProc], eax
    push 10000
    call [ebp+pSleep]
    push [ebp+OldWindowProc]
    push GWL_WNDPROC
    push [ebp+SAS_WndHandle]
    call [ebp+pSetWindowLongA]
   .thrend:
    push 0
    call [ebp+pExitThread]
proc NewWindowProc hwnd, wmsg, wparam, lparam
    call .delta
   .delta:
    pop eax
    sub eax, .delta ; delta offset -> eax
    cmp [wmsg], WM_HOTKEY
    jnz .continue
    cmp [lparam], CtrlAltDel_CODE
    jz .exit_proc
    cmp [lparam], CtrlShiftEsc_CODE
    jz .exit_proc
   .continue:
    push [lparam]
    push [wparam]
    push [wmsg]
    push [hwnd]
    push [eax+OldWindowProc]
    call [eax+pCallWindowProcA]
    jmp .return
   .exit_proc:
    xor eax, eax
   .return:
    ret
endp
SAS_WndClass db 'SAS Window class',0
SAS_WndTitle db 'SAS window',0
SAS_WndHandle dd 0
OldWindowProc dd 0
pFindWindowA	 dd 0
pSetWindowLongA  dd 0
pCallWindowProcA dd 0
pExitThread	 dd 0
pSleep		 dd 0
RemoteCodeEnd:
RemoteCodeSize equ RemoteCodeEnd-RemoteCodeStart
;----------------------------------------
; REMOTE CODE END -----------------------
;----------------------------------------
В начале обоих процедур мы сначала вычисляем дельта-смещение, дельта смещение нам нужно, для того чтобы узнать, на сколько отличаются адреса переменных перемещённых с нашим кодом от тех адресов, которые поставил нам компилятор во время компиляции. Зная, дельта-смещение мы можем спокойно обращаться к переменным, не боясь возникновения ошибок доступа к памяти. Приводить полный код программы, я думаю, смысла не имеет, поэтому только приведу код, который подготавливает переменные, копирует код и запускает поток в процессе winlogon     invoke OpenProcess, PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE, FALSE, [proc_entry.th32ProcessID]
    mov [WinlogonProcHandle], eax
    test eax, eax
    jnz @f
    jmp .exit
   @@:
    mov eax, [FindWindow]
    mov [pFindWindowA], eax
    mov eax, [SetWindowLong]
    mov [pSetWindowLongA], eax
    mov eax, [ExitThread]
    mov [pExitThread], eax
    mov eax, [CallWindowProc]
    mov [pCallWindowProcA], eax
    mov eax, [Sleep]
    mov [pSleep], eax
    invoke VirtualAllocEx, [WinlogonProcHandle], 0, RemoteCodeSize, MEM_COMMIT+MEM_RESERVE, PAGE_EXECUTE_READWRITE
    mov [RemoteThreadBaseAddress], eax
    invoke WriteProcessMemory, [WinlogonProcHandle], eax, RemoteCodeStart, RemoteCodeSize, Writed
    invoke CreateRemoteThread, [WinlogonProcHandle], 0, 0, [RemoteThreadBaseAddress], 0, 0, RemoteThreadID
   .exit:
    invoke ExitProcess, 0 Сначала мы открываем процесс winlogon с нужным нам уровнем доступа (код получения привилегий и получения ID winlogon я не привёл). Потом заполняем переменные, в которых будут храниться адреса используемых API функций, потом выделяем кусок памяти в процессе winlogon, потом копируем туда код, который будет выполняться в его контексте, потом запускаем удалённый поток. Полный код программы приведён в архиве с исходниками к этой статье. Ссылки по теме 
 
 |  | |||||||