|
Negli articoli precedenti ho parlato di intercettazione delle api
di Windows ed ho utilizzato il componente TJclPeMapImgHooks
che fa parte della Jedi Code Library (JCL) scaricabile liberamente all'
indirizzo www.delphi-jedi.org. Quel
componente utilizza della tecnica di patch della tabella IAT (Import Address
Table) per consentire di sostituire una funzione con una versione propria. Gli
esempi allegati trattano l' intercettazione di api nel contesto del processo
corrente; ho esteso l' intercettazione delle api a tutti i processi utilizzando
gli hook di sistema; bene .... ora voglio utilizzare un' altra tecnica per l'
intercettazione delle api ed un altra tecnica per l' applicazione del codice di
intercettazione a tutti i processi presenti e futuri. Infatti bisogna dire che
patchando la tabella IAT posso intercettare tutte le funzione implementate in
librerie caricate staticamente; se però un dato programma chiama una funzione
implementata in una dll caricando dinamicamente la dll stessa (LoadLibrary della
dll seguito da GetProcAddress per la determinazione dell' indirizzo della
funzione) in questo caso allora questa funzione non la riesco ad intercettare.
Una tecnica molto valida per l' intercettazione delle api di Windows è la
tecnica di Detours: andando all' indirizzo
http://research.microsoft.com/sn/detours/
si può scaricare il pacchetto comprensivo di sorgenti della
libreria, esempi e un pò di documentazione. Questa tecnica consiste nel mettere
come prima istruzione della funzione che si vuole intercettare un jump
incondizionato alla funzione che ho preparato e che deve essere chiamata nel
momento in cui il programma chima quella da intercettare. In breve chiameremo
FunzioneOriginale la funzione che si vuole intercettare e FunzioneSostitutiva la
funzione che ci siamo creati noi e che dovrà essere chiamata al posto dell'
originale. Se metto come prima istruzione della FunzioneOriginale l' istruzione
jmp <Displacement>, quando il programma chiama la
FunzioneOriginale, esso esegue un salto incondizionato all' indirizzo che dista
<Displacement> dall' indirizzo immediatamente successivo all' instruzione
jmp <Displacement>. jmp è 1 byte (codice E9 in esadecimale) mentre
<Displacement> rappresenta una distanza ed è espresso tramite 4 byte:
complessivamente l' istruzione jmp <Displacement> sono 5 byte. Per fare
in modo che venga chiamata FunzioneSostitutiva quando viene chiamata
FunzioneOriginale, il salto incondizionato che eseguo come prima istruzione di
FunzioneOriginale, mi deve portare all' indirizzo di FunzioneSostitutiva e
quindi <Displacement> sarà dato da
@FunzioneSostitutiva - (@FunzioneOriginale + 4)
dove il simbolo @ rappresenta l' indirizzo della funzione a
cui è collegato (come in Delphi).
A questo punto quando il programma chiama FunzioneOriginale,
verrà chiamata FunzioneSostitutiva. Bene... io voglio intercettare e non
sostitire totalmente: in sostanza la FunzioneSostitutiva deve essere in grado di
chiamare FunzioneOriginale coì come era implementata in principio; è in questo
contesto che entra in gioco la "funzione trampolino" che chiameremo
"FunzioneTrampolino": in pratica devo vedere quali sono le istruzioni della
FunzioneOriginale che rientrano nel primi 5 byte della medesima (byte che
vengono sovrascritti appunto dal jmp <Displacement>) e farle
diventare le prime istruzioni della FunzioneTrampolino: subito dopo queste
istruzioni, andrò ad inserire nella FunzionTrampolino un jump incondizionato
all' istruzione immediatamente successiva nella FunzioneOriginale. In questo
modo la FunzioneTrampolino ricostruisce alla perfezione la FunzioneOriginale:
infatti contiene le istruzioni che vengono sovrascritte dal jmp
<Displacement> e subito dopo un jump incondizionato (una istruzione sempre
del tipo jmp <Displacement>) alla istruzione immediatamente successiva
nella FunzioneOriginale. La FunzioneSostituitiva nella sua implementazione ad un
certo punto chiamerà la FunzioneTrampolino preservano quindi il funzionamento
della FunzioneOriginale: ad esempio se voglio semplicemente monitorare la
chiamate alla FunzioneOriginale, la FunzioneSostitutiva mi invierà un messaggio
in cui si comunica la chiamata della FunzioneOriginale e poi subito dopo
chiamerà la FunzioneTrampolino. Messa in questi termini non sembra poi così complessa:
invece la complessità viene fuori nel momento in cui si decide di realizzarne
una implementazione corretta in tutte le circostanza. La prima cosa importante
da osservare è che le istruzioni della FunzioneOriginale che rientrano nei primi
5 byte non necessariamente rientrano esattamente nei 5 byte: bisogna quindi
disporre di un disassemblatore che ci consenta di vedere quali sono le
istruzioni coinvolte nei primi 5 byte. L' implementazione ufficiale della
tecnica dispone di procedure per il riconoscimento automatico delle istruzioni
coinvolte nei primi 5 byte, raccogliendo tutti i tipi di comandi assembler ... e
poi altre rifiniture. Spulciando un pò nella rete alla ricerca di una
implementazione Delphi di questa tecnica, ho visto che sul sito
www.thedelphimagazine.com (una
delle mie mete preferite) vengono resi disponibili per il download i codici
sorgenti allegati ogni mese alla rivista. Nel numero 101 (Gennaio 2004) vi è una
eccellente implementazione realizzata da Erik van Bilsen: è una classe Delphi
molto ben realizzata che contiene tutto quello che viene esposto nella
implementazione originale sul sito Microsoft (compreso appunto il riconoscimento
automatico delle istruzioni nei primi 5 byte della FunzioeOriginale). Allora mi
sono messo un pò a giocare partendo dall' esempio allegato. Anche in questo caso
l' intercettazione viene realizzata nell' ambito del processo corrente e mi sono
detto :"si certo Carlo questo codice non l' hai realizzato tu ... ma in ogni
caso puoi realizzare il modulo che consenta l' applicazione del codice a tutti i
processi realizzando un intercettazione a livello di sistema (e non solo a
livello di processo corrente). Bene: un hook di sistema e tutto a posto come
nella situazione precedente ... uhhh che bello si intercettano le api a livello
di tutti i processi. Si ... però ho fatto troppo poco per meritarmi qualcosina
ed allora vado un pò più in profondità ... guarda caso se apro cmd.exe le api
non vengono intercettate: dunqueeeee ... si il fatto è che cmd.exe non ha
associata una coda dei messaggi e quindi l' hook di sistema (che appunto
interviene nel contesto del sistema di messaggistica) qui non ha alcun effetto e
la dll contenente il codice per l' intercettazione non viene mappata nello
spazio di memoria di cmd.exe. Ok allora devo mapparla direttamente tramite
CreateRemoteThread (guarda un pò di articoli precedenti che ne ho parlato
talmente a stufo che ciò la nausea).Bene bene .... quindi se vogliamo
coinvolgere tutti i processi dobbiamo dimenticarci degli hook di sistema (che
tra l' altro poi oltre ad essere innocui con cmd.exe non fanno nulla nemmeno coi
processi tipo winlogon.exe, etc...). Possiamo enumerare tutti i processi e per
ognuno di essi mappare la dll col codice di intercettazione: bene detto fatto:
procedure ModificaPrivilegio(szPrivilege: pChar; fEnable: Boolean);
var
NewState: TTokenPrivileges;
luid: TLargeInteger;
hToken: THandle;
ReturnLength: DWord;
begin
//apro il token di accesso asociato al processo corrente (il cui handle
//è ottenuto tramite GetCurrentProcess. Specifico TOKEN_ADJUST_PRIVILEGES
//come tipo di accesso al Token: in questa maniera sono in grado di abilitare
//e/o disabilitare Privilegi. hToken è l' handle del token di accesso appena
//aperto.
OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
hToken);
//ricavo il LUID (Locally Unique Identifier) corrispondente al privilegio
//specificato: si tratta in sostanza di un identificativo univoco del privilegio;
//varia da sessione a sessione ed anche tra un riavvio e l' altro del sistema
LookupPrivilegeValue(nil, szPrivilege, luid);
//lavoro su NewState (di tipo TTokenPrivileges). Rappresenta un elenco di privilegi;
//nel caso specifico conterrà un solo privilegio (ProvilegeCount = 1). L' arrary
//Privileges contiene oggetti con 2 campi: il luid del privilegio (Luid) ed
//il livello di abilitazione del medesimo (Attributes)
NewState.PrivilegeCount := 1;
NewState.Privileges[0].Luid := luid;
if fEnable then //abilitiamo il privilegio
NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
else //disabilitiamo il privilegio
NewState.Privileges[0].Attributes := 0;
//eseguiamo la modifica sullo stato di abilitazione del privilegio
//nel contesto del token di accesso aperto
AdjustTokenPrivileges(
hToken,
FALSE,
NewState,
sizeof(NewState),
nil,
ReturnLength);
//chiudo l' handle al token di accesso aperto
CloseHandle(hToken);
end;
function PidProcesso(NomeProcesso: string): LongWord;
var
pe: TProcessEntry32;
hSnap: THandle;
begin
Result := 0;
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe.dwSize := sizeof(TProcessEntry32);
//Prelevo informazioni sul primo processo nello snapshot di sistema
Process32First(hSnap, pe);
repeat //loop sui processi
Result := pe.th32ProcessID;
if (LowerCase(pe.szExeFile) = LowerCase(NomeProcesso)) then
begin
break;
end;
until (not (Process32Next(hSnap, pe)) ) ;
CloseHandle(hSnap);
end;
function BaseAddrDllProcesso(PID: LongWord; NomeDll: string): PByte;
var
me: TModuleEntry32;
hSnap: THandle;
begin
Result := 0;
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
me.dwSize := sizeof(TModuleEntry32);
//Prelevo informazioni sul primo modulo del processo in questione
Module32First(hSnap, me);
repeat //loop sui moduli
if LowerCase(me.szModule) = LowerCase(NomeDll) then
begin
result := me.modBaseAddr;
break;
end;
until (not (Module32Next(hSnap, me)));
CloseHandle(hSnap);
end;
function InjectDll(PID: dword; DLL: pChar): LongWord;
var
BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
begin
Result := 0;
if DLL = nil then
Exit;
//conferisco, al processo che chiama la funzione, il privilegio
//di debug
ModificaPrivilegio('SeDebugPrivilege', TRUE);
//ottengo un handle al processo destinazione (identificato da PID);
//specifico come valore del primo parametro della OpenProcess un elenco
//di privilegi di accesso al processo in questione tali da consentirmi
//le operazioni che andrò ad eseguire di seguito. Basta consultare il Platform
//SDK e cercare le funzioni successive per vedere con quali diritti di accesso
//un processo debba essere aperto per consentire appunto l' esecuzione di
//ciascuna di esse
Process := OpenProcess(PROCESS_CREATE_THREAD +
PROCESS_QUERY_INFORMATION +
PROCESS_VM_OPERATION +
PROCESS_VM_WRITE +
PROCESS_VM_READ,
False,
PID
);
//vado ad allocare della memoria nello spazio di memoria del processo destinazione
//(identificato dall' handle Process). Tale fetta di memoria deve contenere una
//stringa che definisce il nome della dll che si vuole mappare (comprensivo di
//percorso completo). Quindi la sua dimensione sarà data da Length(DLL). Mi
//viene restituito (Paramaters) l' indirizzo (nel contesto del processo destinazione)
//a partire dal quale è presente tale area di memoria
Paramaters := VirtualAllocEx(
Process,
nil,
Length(DLL),
MEM_COMMIT,
PAGE_READWRITE);
//vado a scrivere nella fetta di memoria (appena allocata nel processo
//destinazione tramite VirtualAllocEx) la stringa che definisce il nome della
//dll da mappare (comprensivo di percorso completo). Gli passerò l' handle
//del processo destinazione (Process, ottenuto tramite OpenProcess), l' indirizzo
//di partenza dell' area di memoria allocata per inserirvi il nome della dll da
//mappare (Paramaters, ottenuto tramite VirtualAllocEx), l' indirizzo a partire
//da quale è memorizzato il nome della dll nel contesto del processo corrente
//(Pointer(DLL)), la dimensione dei dati da copiare (Lenght(DLL))
WriteProcessMemory(Process,
Paramaters,
Pointer(DLL),
Length(DLL),
BytesWritten);
//Bene, ora che ho copiato il nome della dll nello spazio di memoria del processo
//remoto, posso creare un thread nel processo remoto: tale thread consiste
//nell' esecuzione di LoadLibraryA passando come parametro il nome della dll:
//in parole povere il caricamento della dll nello spazio di memeoria del processo
//remoto. Il parametri fondamentali sono: l' handle del processor remoto (Process,
//ottenuto tramite OpenProcess), l' indirizzo a partire dal quale è implementata
//l' api LoadLibraryA (il 4° parametro), l' indirizzo a partire dal quale è
//memrizzato il nome della dll comprensivo di percorso completo (Paramaters).
//Da notare che quando si parla di indirizzo di una funzione da invocare nel
//contesto di un processo remoto (nel caso specifico l' api LoadLibraryA) si parla
//di indirizzo nello spazio di memoria del processo remoto; il 4° parametro della
//CreateRemoteThread è l' indirizzo dell' api LoadLibraryA nel contesto del processo
//corrente e di conseguenza nasce il dubbio sulla validità; il linea di massima una dll
//non viene sempre caricata allo stesso indirizzo nel' ambito di un processo e di
//conseguenza anche l' indirizzo delle funzioni esportate dalla dll medesima può
//variare di volta in volta (in quanto è soggetto a variazione l' indirizzo di base
//di caricamento di una dll nello spazio di memoria di un processo). Tuttavia la dll
//kernel32.dll fa eccezione in quanto viene caricata sempre allo stesso indirizzo. Più
//precisamente tutte le dll hanno un indirizzo di preferenza a partire dal quale possono
//essere caricate ma sono soggette alla cosidetta rilocazione (variazione sull' indirizzo
//base di caricamento rispetto al valore desiderato) in relazione al contesto. La dll
//kernel32.dll non è soggetta ad alcuna rilocazione. Se si provasse a forzare il suo
//caricamento in altri indirizzi si otterrebbe immediatamente un errore irreversibile
Thread := CreateRemoteThread(Process,
nil,
0,
GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'),
Paramaters,
0,
ThreadId);
//mi metto in attesa della terminazione del Thread innescato nel processo remoto
//tramite CreateRemoteThread. L' handle del Thread è stato restituito dalla
//CreateRemoteThread. Non ci sono interruzioni all' attesa diverse dalla terminazione
//del thread medesimo (time out infinito: secondo parametro è INFINITE)
WaitForSingleObject(Thread, INFINITE);
//una volta terminato il thread remoto copio come output della funzione DllInject
//il codice di uscita del thread in questione: si tratta del codice di uscita della
//funzione che mi ha definito il thread ossia LoadLibraryA e quindi altro non è il
//valore restituito da LoadLibraryA ossia l' handle della dll caricata.
GetExitCodeThread(Thread, Result);
//libero la memoria allocata nel processo remoto (che contiene il nome della DLL)
//come parametri avrò naturalmente l' handle del processo remoto (Process, ottenuto
//tramite OpenProcess) e Paramaters che è appunto l' indirizzo (nel contesto del
//processo remoto) a partire dal quale è memorizzato il nome della Dll
VirtualFreeEx(Process,
Paramaters,
0,
MEM_RELEASE);
//chiudo gli handle al processo remoto (Process) ed al thread creato nel processo
//remoto (Thread)
CloseHandle(Thread);
CloseHandle(Process);
end;
procedure UninjectDll(PID: dword; BaseAddrDLL: PByte);
var
BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
begin
//ablito il privilegio di debug
ModificaPrivilegio('SeDebugPrivilege', TRUE);
//apro il processo remoto
Process := OpenProcess(PROCESS_CREATE_THREAD +
PROCESS_QUERY_INFORMATION +
PROCESS_VM_OPERATION +
PROCESS_VM_WRITE +
PROCESS_VM_READ,
False,
PID
);
//creo ed eseguo un thread nel processo remoto. A differenza della InjectDll,
//in questo caso non abbiamo bisogno di copiare l' argomento di input della
//funzione che mi definisce l' esecuzione del thread (in questo caso FreeLibrary,
//nel caso precedente LoadLibraryA) in quanto il valore restituito da BaseAddrDll
//è già un indirizzo valido nel contesto del processo remoto; non c' è quindi più
//bisogno di allocare memoria (VirtualAllocEx), scrivere nella memoria allocata
//(WriteProcessMemory) e deallocare la memoria (VirtualFreeEx)
Thread := CreateRemoteThread(Process,
nil,
0,
GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'FreeLibrary'),
BaseAddrDLL,
0,
ThreadId);
//mi metto in attesa della terminazione del thread remoto (che consiste appunto in
//una FreeLibrary sulla dll precedentemente mappata con DllInject
WaitForSingleObject(Thread, INFINITE);
//chiudo gli handle al processo remoto (Process) ed al thread creato nell' ambito
//del processo remoto (Thread)
CloseHandle(Thread);
CloseHandle(Process);
end;
procedure InjectDllAllProcesses(DLL: pChar);
var
pe: TProcessEntry32;
hSnap: THandle;
begin
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe.dwSize := sizeof(TProcessEntry32);
//Prelevo informazioni sul primo processo nello snapshot di sistema
Process32First(hSnap, pe);
repeat //loop sui processi
InjectDll(pe.th32ProcessID, DLL);
until (not (Process32Next(hSnap, pe)) ) ;
CloseHandle(hSnap);
end;
procedure UnInjectDllAllProcesses(DLL: pChar);
var
pe: TProcessEntry32;
hSnap: THandle;
DllHandle: DWORD;
dll_base_addr: PByte;
begin
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe.dwSize := sizeof(TProcessEntry32);
//Prelevo informazioni sul primo processo nello snapshot di sistema
Process32First(hSnap, pe);
repeat //loop sui processi
dll_base_addr := BaseAddrDllProcesso(pe.th32ProcessId, Dll);
if dword(dll_base_addr) <> 0 then
UnInjectDll(pe.th32ProcessID, dll_base_addr);
until (not (Process32Next(hSnap, pe)) ) ;
CloseHandle(hSnap);
end;
Ok ... tanto bel codice ben commentato: InjectDllAllProcesses e
UnijectDllAllprocesse ... siiii sono loro 2 le funzioni che ci consentono di
mappare la nostra dll magica in tutti i processi e per contro di scaricarla da
tutti i processi. Non mi dilungherei troppo anche perchè sennò facciamo notte
(no anzi qui è già notte ... o meglio mezzanotte). Ora pensiamo un attimo: è
tutto a posto???? Neanche per sogno in quanto dobbiamo fare in modo che tale dll
venga mappata anche in tutti i processi che verranno creati in futuro: dobbiamo
quindi intercettare la creazione di nuovi processi e mappare la dll in ognuno di
essi. Utilizzeremo quindi sempre la classe DELPHI che implementa la tecnica di
Detours per intercettare l' api CreateProcess. La funzione che si sostituisce a
CreateProcess (o meglio le 2 funzioni che si sostituiscono rispettivamente a
CreateProcessA e CreateProcessW) dovranno eseguire il CreateProcess originale e
poi mappare la dll nello spazio di memoria del processo creato. Una volta
creato, il processo (o meglio il thread primario del processo) può chiamare
delle api che io voglio intercettare o anche creare a sua volta un altro
processo: tutte queste operazioni potrebbero avvenire prima che la nostra dll
sia stata effettivamente mappata perdendoci quindi qualcosa di bello che è
successo nel frattempo; è opportuno quindi che il processo venga creato in forma
SUSPENDED: in questo modo il thread primario non eseguirà nulla; vado quindi a
mappare la dll nello spazio di memoria del processo e riattivo il processo con
un ResumeThread passandogli come input l' handle del thread primario. Sia il PID
del nuovo processo (necessario per mappare la dll nel suo spazio di memoria
tramite la funzione InjectDll) sia l' handle del suo thread primario (necessario
per risvegliare il processo dallo stato SUSPENDED tramite l' api ResumeThread)
sono campi dell' ultimo parametro dell' api CreateProcess (che è appunto l'
output della CreateProcess): rispettivamente lpProcessInformation.dwProcessId e
lpProcessInformation.hThread. Ricapitoliamo quindi un attimo: mappiamo la dll in
tutti i processi attivi; la dll consente l' intercettazione della CreateProcess
e quindi la creazione di processi figli: ogni volta che un processo crea un
processo figlio la dll viene mappata automaticamente nello spazio di memoria del
processo figlio (è quello che succede nell' implementazione della funzione che
si va a sostituire alla CreateProcess originale); in questo modo ricorsivamente
i processi figli mapperanno in automatico tale dll in tutti i processi loro
figli e così via. Ho quindi sotto controllo la creazione di ogni processo. A
questo punto l' unico neo è la definizione del percorso completo della dll nella
funzione InjectDll: scrivere il percorso completo non è elegante perchè tutte le
volte che sposto il programma o la dll lo devo ridefinire; potrei mettere la dll
nella cartella di sistema solo che anche quello è un vincolo in sè: la soluzione
migliore è scrivere il percorso completo della dll in un File Mappato In Memoria
(in maniera che sia accessibile nel contesto di tutti i processi): l'
eseguibile, prima di eseguire il mapping iniziale su tutti i processi, scrive il
nome della dll (comprensivo di percorso completo) nel File Mappato in Memoria;
nell' entrypoint di ogni dll ci sarà la lettura dell' informazione; in pratica
l' eseguibile crea il file mappato in memoria (CreateFileMapping) ne ottiene una
copia in scrittura nel proprio spazio di memoria (MapViewOfFile) e ci scrive la
stringa (MapViewOfFile restituisce un puntatore che può essere usato come
qualsiasi puntatore e quindi scrivere sul file mappato in memoria equivale ad
eseguire l' assegnazione di una stringa). Ogni dll aprirà il File mappato in
memoria (OpenFileMapping), ne otterrà una copia in lettura nel proprio spazio di
memoria e va a leggere la stringa che specifica il nome della dll comprensivo di
percorso completo. Un' ultima cosa: l' esempio che vado a riportare ridefinisce
il funzionamento dell' api DrawFocusRect a livello di sistema e ci troveremo
quindi con il rettangolino del focus (su pulsanti elementi di una lista etc...)
di color rosso. Dato che c' ero ho deciso di inviare anche un messaggio a video
ogni volta che viene generato un nuovo processo consentendo l' esecuzione o meno
del medesimo per verificare appunto la validità della procedura di
intercettazione dell' api CreateProcess. E' importante notare che la dll viene
mappata in tutti i processi: i processi come ad esempio winlogon.exe
appartengono ad una winstation differente dalla winstation in cui vediamo
eseguire i nostri programmini e quindi una MessageBox eseguita da quei processi
blocca tutto il sistema in quanto non è visibile e non si riesce quindi a
chiuderla. E' opportuno quindi usare l' api WTSSendMessage che ci consente di
inviare messaggi spacificando la winstation. Di seguito l' implementazione della
unit utilizzata nell' implementazione della dll (in essa sono visibili tutti i
passaggi necessari per l' intercettazione dell' api DrowFocusRect nonchè
CreateProcess)
unit InterceptUnit;
interface
uses
Windows,
SysUtils,
Classes,
Graphics,
Detours,
ProcessUtilities,
JwaWtsApi32,
MMF;//unit per la gestione dei file mappati in memoria
procedure CreaImportHooks;
procedure RimuoviImportHooks;
implementation
const
clSkyBlue = TColor($F0CAA6);
var
NomeDll: pChar;
TrampolineDrawFocusRect: function (hDC: HDC; const lprc: TRect): BOOL; stdcall = nil;
TrampolineCreateProcessA: function(lpApplicationName: PAnsiChar;
lpCommandLine: PAnsiChar;
lpProcessAttributes,
lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL;
dwCreationFlags: DWORD;
lpEnvironment: Pointer;
lpCurrentDirectory: PAnsiChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation
): BOOL; stdcall = nil;
TrampolineCreateProcessW: function(lpApplicationName: PWideChar;
lpCommandLine: PWideChar;
lpProcessAttributes,
lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL;
dwCreationFlags: DWORD;
lpEnvironment: Pointer;
lpCurrentDirectory: PWideChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation
): BOOL; stdcall = nil;
function DetourDrawFocusRect(hDC: HDC; const lprc: TRect): BOOL; stdcall;
var
RedBrush: HBrush;
begin
RedBrush := CreateSolidBrush(clRed);
FrameRect(hDC,lprc,RedBrush);
DeleteObject(RedBrush);
Result := True;
end;
function DetourCreateProcessA(lpApplicationName: PAnsiChar;
lpCommandLine: PAnsiChar;
lpProcessAttributes,
lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL;
dwCreationFlags: DWORD;
lpEnvironment: Pointer;
lpCurrentDirectory: PAnsiChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation
): BOOL; stdcall;
var
pResponse: dword;
begin
WTSSendMessageA(WTS_CURRENT_SERVER_HANDLE,
0,
'esecuzione processo',
20,
lpCommandLine,
Length(lpCommandLine),
MB_YESNO,
0,
pResponse,
True);
if pResponse = IDYES then
begin
//creo il processo tenendo sospeso il suo thread primario in maniera che non esegua nulla
TrampolineCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags + CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
InjectDll(lpProcessInformation.dwProcessId, NomeDll);
ResumeThread(lpProcessInformation.hThread);
end;
end;
function DetourCreateProcessW(lpApplicationName: PWideChar;
lpCommandLine: PWideChar;
lpProcessAttributes,
lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL;
dwCreationFlags: DWORD;
lpEnvironment: Pointer;
lpCurrentDirectory: PWideChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation
): BOOL; stdcall;
var
pResponse: dword;
begin
WTSSendMessageW(WTS_CURRENT_SERVER_HANDLE,
0,
'esecuzione processo',
20,
lpCommandLine,
Length(lpCommandLine) * 2,
MB_YESNO,
0,
pResponse,
True);
if pResponse = IDYES then
begin
//creo il processo tenendo sospeso il suo thread primario in maniera che non esegua nulla
TrampolineCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags + CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
InjectDll(lpProcessInformation.dwProcessId, NomeDll);
ResumeThread(lpProcessInformation.hThread);
end;
end;
//procedura per definire tutte le intercettazioni e sostituzioni
procedure CreaImportHooks;
begin
//legge dal file mappato in memoria il nome della dll comprensivo di percorso completo
NomeDll := LeggiNomeDll();//definita nella unit MMF.pas
@TrampolineDrawFocusRect := DetourCreate(@DrawFocusRect,@DetourDrawFocusRect);
@TrampolineCreateProcessA := DetourCreate(@CreateProcessA,@DetourCreateProcessA);
@TrampolineCreateProcessW := DetourCreate(@CreateProcessW,@DetourCreateProcessW);
end;
//procedura per eliminare tutte le intercettazioni e sostituzioni
procedure RimuoviImportHooks;
begin
DetourRemove(@TrampolineDrawFocusRect,@DetourDrawFocusRect);
DetourRemove(@TrampolineCreateProcessA,@DetourCreateProcessA);
DetourRemove(@TrampolineCreateProcessW,@DetourCreateProcessW);
CloseMapFile;
end;
end.
Sorgenti |