Иногда бывает нужно запретить запуск некоторых прог на компе — будь то комп в универе, дабы студенты учились программированию, а не устраивали чемпионаты по Квейку,
или в компьютерном клубе, где крайне нежелательны проги для подбора паролей, сканеры уязвимостей, нюкеры и прочее. Так что речь в этой статье пойдет о написании такой программы, и еще о ее несколько нестандартном применении :))

Есть несколько способов запретить выполнение определенных прог. Очень многие из них действуют по похожим алгоритмам — смотрят все запущенные системой приложения, или окна программ, и если запущенный файл или окно попадает в «черный список», то программа завершает работу этой проги.
В этой статье мы рассмотрим как способы нахождения запуска программ, так и способы их ликвидации :)), в смысле завершения. 

Для начала рассмотрим само «орудие убийства» — функцию завершения процесса.
Она называется TerminateProcess (завершить процесс), и должна иметь в качестве аргумента заголовок (handle) процесса и код завершения, причем этот аргумент почти всегда равен 0.
Теперь об обнаружении запретных программ. Если несколько способов, причем некоторые различны для NT семейства и для семейства 9x.
Например, можно проверять имена всех запущенных файлов. Рассмотрим, как это делается для Win9X и WinME. Алгоритм следующий — перебираем все запущенные процессы, определяем имена запущенных файлов, и если файл из черного списка — завершаем процесс.
Для этого, во-первых, нужно подключить модуль tlhelp32.Затем опишем структуру процесса:

ProcStruct: PROCESSENTRY32;

Создадим заголовок структуры функцией CreateToolhelp32Snapshot.
И затем будем перебирать процессы, пока они не кончатся :))
Разумно будет выделить процедуру, которая по параметру-строке будет завершать файл с именем, который будет указан в этой строке.

function KillPrc( Name : string ) : boolean;
var
q:boolean;
han : THandle;//
заголовок
ProcStruct : PROCESSENTRY32; //
из модуля tlhelp32
sID : string;//
строка — имя exe-файла данного процесса.
begin
Result := false;
//
создаем заголовок 
han := CreateToolhelp32Snapshot( TH32CS_SNAPALL, 0 );
//
если он пустой — выходим
if han = 0 then
exit;

//определяем размер структуры
ProcStruct.dwSize := sizeof( PROCESSENTRY32 );
//
Если процесс в структуре первый — тогда запускаем цикл
if Process32First( han, ProcStruct ) then
begin
repeat
//
Находим имя exe-файла из процесса.
//sID — имя. 

sID := ExtractFileName( ProcStruct.szExeFile);

//d2 — идентификатор процесса, поле th32ProcessID структуры

d2:=ProcStruct.th32ProcessID;

//Открываем процесс, т.е. получаем его заголовок по идентификатору, это будет 
//
нужно для выполнения функции TerminateProcess
//
hn — заголовок очередного процесса, ProcStruct.th32ProcessID — ID процесса

hn:=OpenProcess(1,true,ProcStruct.th32ProcessID);
//
если имя exe-файла текущего процесса совпадает с файлом из «черного списка» 
//
— тогда завершаем процесс. 

if sid=Name then TerminateProcess(hn,0);

//Закрываем открытый заголовок процесса, чтобы это переменной можно было 
//
воспользоваться для другого процесса

CloseHandle( hn );
//
выходим, когда процессов больше нет.

until not Process32Next( han, ProcStruct );
end;

//закрываем заголовок

CloseHandle( han );
end;

Теперь нам нужно только лишь вызвать эту функцию — и тогда определенный процесс завершится. Причем нужно сделать выполнение этой функции в цикле — дабы
пресечь все попытки запуска. Можно сделать так, чтобы список читался из файла, записать его в массив и запускать функцию с каждым из элементов

var
dat:array[1..10] of string;//
массив строк — имен EXE файлов
f:text;//
файл
fs:string;//
строка — имя файла, из которого будем читать.
m,n:integer;

begin

AssignFile(f,’blacklog.txt’);
reset(f);
While not(eof(f)) do begin
n:=n+1;
//
читаем строки из файла
Readln(f,dat[n]);

end;

CloseFile(f);

While ext<>true //пока нет условия выхода — идет цикл
do begin
for m:=1 to n do 
KillPrc(dat[m]);
Sleep(1000);
end;

Но этот способ плох тем, что мы пробегаем список процессов несколько раз, для каждого имени из массива. Так что проверку каждого имени можно встроить в саму функцию KillPrc, тогда функция избавится от аргумента,
а вместо строки if sid=Name then TerminateProcess(hn,0); станет 

for m:=1 to n do 
if sID=dat[m] then TerminateProcess(hn,0);

Так что не нужно будет запускать функцию несколько раз, т.к. цикл
перенесен в саму функцию. Также можно сделать и так, чтобы могли запускаться лишь определенные проги, а все остальные шли лесом 🙂 Можно сделать это так — перечислить все процессы, и если имя exe-файла не входит в «белый список», то завершить его.

Но вышеописанный способ будет действовать только в Win9X из-за использования функций ProcessFirst,
ProcessNext. Под NT будут использоваться другие функции, но мы рассмотрим принципиально другой способ обнаружения — по заголовкам окон будем искать ID процесса.
Этот способ универсален. Причем он хорош еще и тем, что имя файла можно изменить простым нажатием
Shift-F6 (или F2 — смотря в чем сидишь 🙂 ), а заголовок окна -только колупанием в проге. 

procedure KillPrc2;
var
hwnd1:hWND;
p:pChar;
s:AnsiString;
dat:array[1..10]of AnsiString;//
массив имен окон, 
ii:longint;
hnd:THandle;
begin

hwnd1:=GetDesktopWindow;//получаем заголовок для десктопа
hwnd1:= GetWindow(hwnd1, GW_CHILD);//
получаем заголовок для «дочернего окна»

//в данном случае дочерние окна — окна приложений.
//
пока окно не пустое — циклимся

while (hwnd1<>0) do begin

hwnd1:= GetWindow(hwnd1, GW_HWNDNEXT); //получаем следующее дочернее окно
GetWindowText(hwnd1,p,128);//
получаем текст этого окна
for m:=1 to n do 
if pos(dat[m],p)>0 then begin

//если в заголовке окна есть строка из массива — завершаем процесс
//
для этого получим идентификатор процесса для данного окна
ii:=GetWindowProcessID(hwnd1);
//
далее находим заголовок
hnd:=OpenProcess(1,true,ii);
//
и завершаем процесс
TerminateProcess(hnd,0);
//
закрываем заголовок.
CloseHandle(hnd);

end;
end;

end;

А основная часть программы такая же, как и в предыдущем способе. 
Но этот способ опять таки основан на завершении процесса, без сохранения каких-либо данных и прочего, и аналогичен нажатию трех кнопок.
Можно же поступить более культурно — «сказать» приложению, а не системе, что надо бы закрыться :))  

Как ты знаешь, управление окнами и прочим основано на передаче сообщений
(Messages). Сообщение отправляется окну функцией:

SendMessage(HWND:окно, msg:cообщение, wParam,lParam:доп. параметры)

Поэтому впишем вместо определения ID процесса и прочего 

SendMessage(hnwd1,WM_CLOSE,0,0);

По идее, окно должно закрыться :)) Но в некоторых случаях приходится
действовать более жестко, сообщая окну, что оно должно самоуничтожиться :))
Это делается посылкой сообщений WM_DESTROY

SendMessage(hnwd1,WM_DESTROY,0,0);

или

SendMessage(hnwd1,WM_NCDDESTROY,0,0);

Лучше применить и то и то — большинство программ
завершается так, это видно если просмотреть запись обмена сообщениями какой-либо проги (это делается, например, прогой Spy++ из разряда служебных прог Visual C++, полезнейшая прога, дает доступ ко всем окна, показывает процессы, потоки и много чего еще)

<00135> 000002F0 S ..WM_DESTROY
<00136> 000002F0 R ..WM_DESTROY
<00137> 000002F0 S ..WM_NCDESTROY
<00138> 000002F0 R ..WM_NCDESTROY
<00139> 000002F0 R .WM_CLOSE

Здесь мы видим, что сначала посылается сообщение WM_DESTROY, затем на него приходит ответ
(о его удачном завершении), дальше также идет с мессагой 
WM_NCDESTROY, и в конце идет сообщение WM_CLOSE — и окно закрывается, куда ему деваться :))
В принципе, посылка сообщения может быть заменена выполнением функции
DestroyWindow, но предпочтительнее вышеописанный вариант.

Чем хорош способ работы с окнами — это тем, что он может завершать не просто определенные приложения, а в зависимости от того, что это приложение в данный момент выполняет — это относится к интернет-браузерам, различным проигрывателям и прочему. 

Допустим, не хочешь ты слушать какую-нить попсу на WinAmpe’e, тогда пишешь в «Черный список» названия групп
(или названия песен — если так будет удобнее) — если будет запущена MP3шка этой группы(разумеется, с
заполненным полем «Artist» в теге MP3 файла, дабы она отображалась в заголовке окна), то винамп незамедлительно закроется 🙂
Причем закроется любой проигрыватель (не только WinAmp), рискнувший запустить MP3-шку этой группы. Или тоже самое со страницами в Инете — по ключевому слову
(или фразе), которая появляется в окне при заходе на определенный сайт
(или группу сайтов) браузер незамедлительно закроется. Так же можно запретить все инсталляции и деинсталляции — достаточно внести в черный список слова Install и
UnInstall. 

Но и это еще не предел — кроме банального закрывания можно сделать более изощренно — например, как только пойдет какая-нить фигня по WinAmp’у, заставить его играть что-то другое, вогнав в шок незадачливого юзера, начисто лишенного вкуса :)) Это делается достаточно просто:

if pos(‘Остойная песня — WinAmp’,p)>0 then 
WinExec(‘winamp.exe c:\music\…\супер_песня.mp3’,1);

И вставить это вместо TerminateProcess, DestroyWindow и прочего деструктива :)) Причем винамп не будет закрыватся — просто заиграет новая песня, и все!
Или можно совместить закрытие приложения с каким либо приколом:
например, закрыть окно и выдать какой-нить устрашающий звуковой фрагмент, выдранной из любимой игрушки или фильма. Делается это так:

if pos(‘запретная прога’,p)>0 then begin

//это мы закрыаем эту прогу
ii:=GetWindowProcessID(hwnd1);
hnd:=OpenProcess(1,true,ii);
TerminateProcess(hnd,0);
CloseHandle(hnd);

//запускаем WinAmp c данным фрагментом
WinExec(‘winamp.exe c:\…\звук.mp3’,0);
//
заметь, в параметрах запуска стоит 0, это значит что окно будет скрыто, 
//
т.е. юзер не увидит, что запустился винамп запустился!
//
особо впечатлительные могут подумать, не завелся ли на компе
//
искусственный интеллект :))
//
далее можно убрать винамп, дабы он зря не занимал звуковой канал,
//
это можно сделать найдя его окно и завершив его:
hwnd:=FindWindow(‘ Winamp v1.x’,’звук — Winamp’);
//
И далее закрыть его любым способом

Так что областей применения этих программ — море, все зависит лишь от твоей фантазии 🙂

Оставить мнение

Check Also

Скрытая сила пробела. Эксплуатируем критическую уязвимость в Apache Tomcat

В этой статье мы поговорим о баге в Apache Tomcat, популярнейшем веб-сервере для сайтов на…