|
In questo articolo vengono uniti tra loro i concetti esposti nell'articolo
sul CodeInjection (all'interno del quale alla fine vengono presentate 2
procedure per caricare e scaricare una dll relativamente allo spazio di memoria
di un processo remoto) e nell'articolo sulle Native Api dedicate alla gestione
delle dll: in pratica andremo a riscrivere l'implementazione della funzione del
thread remoto usando le Native API. Ho fatto un pò di prove (senza esagerare lo
ammetto) e non ho trovato differenze sostanziali tra una implementazione e
l'altra: del resto non mi aspettavo nulla di speciale, più che altro ho deciso
di implementare il tutto per puro divertimento.
1. Implementazione
I concetti di riferimento sono quelli approfonditi nel 2 articoli sopra
citati: si può quindi passare direttamente al codice
1.1 RemoteLoadLibraryNative
function RemoteLoadLibraryNative(
PID: Cardinal; //PID del processo remoto
NomeDll: PAnsiChar;
var outValue: Cardinal; //valore importante ottenuto dalla funzione remota
var codError: Cardinal; //errore riscontrato dalla funzione remota
synch: Boolean): Boolean; //esecuzione sincrona del thread remoto
type
TRemoteLoadLibraryNativeData = record
errorcod: Cardinal; //codice di errore rilevato nella funzione remota
output: Cardinal; //risultato significativo nella funzione remota
//Indirizzi delle API usate: elenco puntatori a funzioni; N.B. rigorosamente stdcall
//Es. pLoadLibraryA: function(pLibFileName: PAnsiChar): Cardinal; stdcall;
pExitThread: procedure (dwExitCode: Cardinal); stdcall; //API obbligatoria
pRtlAnsiStringToUnicodeString: function(
DestinationString : PUNICODE_STRING;
SourceString : PANSI_STRING;
AllocateDestinationString : Boolean
): Integer; stdcall;
pLdrGetDllHandle: function(
pwPath : PWORD;
Unused : pointer;
ModuleFileName : PUNICODE_STRING;
pHModule : pCardinal
): Integer; stdcall;
pLdrLoadDll: function(
PathToFile : PWideChar;
Flags : Cardinal;
ModulFileName : PUNICODE_STRING;
ModuleHandle : pCardinal
): integer; stdcall;
pRtlFreeUnicodeString: procedure(
UnicodeString : PUNICODE_STRING
); stdcall;
pRtlNtStatusToDosError: function(
const Status : Integer
): Cardinal; stdcall;
//Puntatori a dati; Es. tutte le stringhe vanno messe qui dentro
//Es. pNomeDll: Pointer;
pNomeDll: Pointer;
NomeDllLength: Cardinal;
//ModuleFileNameLength: Cardinal;
//ModuleHandle: Cardinal;
end;
var
hProcess: Cardinal; //handle al processo remoto
RemoteLoadLibraryNativeData: TRemoteLoadLibraryNativeData;
pRemoteLoadLibraryNativeData: Pointer; //indirizzo del record nello
//spazio di memoria del processo remoto
pRemoteLoadLibraryNativeThread: Pointer; //indirizzo della funzione del thread
//nello spazio di memoria del processo remoto
output_value: Cardinal;
error_code: Cardinal;
functionSize, parameterSize: Cardinal;
procedure RemoteLoadLibraryNativeThread(lpParameter: pointer); stdcall;
var
//qui posso definire delle variabili locali
AnsString: ANSI_STRING;
UncString: UNICODE_STRING;
status: Integer;
hModule: Cardinal;
begin
with TRemoteLoadLibraryNativeData(lpParameter^) do
begin
//implementazione: tutte le API vengono chiamate tramite i puntatori nel record
AnsString.Buffer := pNomeDll;
AnsString.Length := NomeDllLength;
AnsString.MaximumLength := NomeDllLength;
status := pRtlAnsiStringToUnicodeString(
@UncString,
@AnsString,
True);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
pExitThread(0);
end;
status := pLdrGetDllHandle(nil, nil, @UncString, @output);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
if errorcod <> 126 then
pExitThread(0);
end
else
pExitThread(1);
status := pLdrLoadDll(nil, 0, @UncString, @output);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
pExitThread(0);
end;
pRtlFreeUnicodeString(@UncString);
pExitThread(1);
//N.B. chiamare sempre pExitCodeThread per definire il codice di uscita del thread
//Es. pExitCodeThread(1) significa successo
// pExitCodeThread(0) significa fallimento
end;
end;
//dealloco la memoria in remoto
function RemoteLoadLibraryNativeUnloadData(): Boolean;
begin
Result := False;
with RemoteLoadLibraryNativeData do
begin
//chiamo UnloadData su tutti i puntatori a dati inclusi nel record
UnloadData(hProcess, pNomeDll);
end;
//deallocazione spazio per il parametro
UnloadData(hProcess, pRemoteLoadLibraryNativeData);
//deallocazione spazio per la funzione
UnloadData(hProcess, pRemoteLoadLibraryNativeThread);
Result := True;
end;
//definisco i valori dei campi del record e copio i dati in remoto
function RemoteLoadLibraryNativeInjectData(): Boolean;
begin
Result := False;
try
//inizializzazione valori campi del record:
with RemoteLoadLibraryNativeData do
begin
errorcod := 0;
output := 0;
//assegno i valori ai puntatori a funzione
//(tramite GetModuleHandle e GetProcAddres)
//Es. pLoadLibraryA := GetProcAddress(GetModuleHandle('kernel32'),
'LoadLibraryA');
pExitThread := GetProcAddress(GetModuleHandle('kernel32'),
'ExitThread'); //API Obbligatoria
pRtlAnsiStringToUnicodeString := GetProcAddress(GetModuleHandle('ntdll'),
'RtlAnsiStringToUnicodeString');
pLdrGetDllHandle := GetProcAddress(GetModuleHandle('ntdll'),
'LdrGetDllHandle');
pLdrLoadDll := GetProcAddress(GetModuleHandle('ntdll'),
'LdrLoadDll');
pRtlFreeUnicodeString := GetProcAddress(GetModuleHandle('ntdll'),
'RtlFreeUnicodeString');
pRtlNtStatusToDosError := GetProcAddress(GetModuleHandle('ntdll'),
'RtlNtStatusToDosError');
NomeDllLength := Length(NomeDll) + 1;
//assegno i valori ai puntatori ai dati (tramite InjectData);
//N.B. se una InjectData ritorna False allora bisogna chiamare Exit
if not InjectData(hProcess,
NomeDll,
Length(NomeDll),
False,
pNomeDll) then
begin
Exit;
end;
end;
//copio il parametro
parameterSize := SizeOf(RemoteLoadLibraryNativeData);
if not InjectData(hProcess,
@RemoteLoadLibraryNativeData,
parameterSize,
False,
pRemoteLoadLibraryNativeData) then
begin
Exit;
end;
//copio la funzione
functionSize := SizeOfProc(@RemoteLoadLibraryNativeThread);
if not InjectData(hProcess,
@RemoteLoadLibraryNativeThread,
functionSize,
True,
pRemoteLoadLibraryNativeThread) then
begin
Exit;
end;
Result := True;
finally
if not Result then
begin
RemoteLoadLibraryNativeUnloadData;
end;
end;
end;
begin
//inizializzo a zero le variabili locali
hProcess := 0;
pRemoteLoadLibraryNativeData := nil;
pRemoteLoadLibraryNativeThread := nil;
output_value := 0;
error_code := 0;
try
hProcess := OpenProcessEx(PROCESS_CREATE_THREAD +
PROCESS_QUERY_INFORMATION +
PROCESS_VM_OPERATION +
PROCESS_VM_WRITE +
PROCESS_VM_READ,
False,
PID
);
if hProcess = 0 then
begin
ErrStr('OpenProcessEx');
Exit;
end;
if not RemoteLoadLibraryNativeInjectData() then
Exit;
if not InjectThread(hProcess,
pRemoteLoadLibraryNativeThread,
pRemoteLoadLibraryNativeData,
output_value,
error_code,
synch) then
Exit;
//output_value e error_code sono stati inizializzati a 0.
//sono stati modificati dalla InjectThread solo se synch = true;
//se synch=false sono rimasti uguali a zero
outvalue := output_value;
codError := error_code;
Result := True;
finally
if synch or (not Result) then
begin
RemoteLoadLibraryNativeUnloadData;
end;
if hProcess <> 0 then
begin
if not CloseHandle(hProcess) then
begin
ErrStr('CloseHandle');
end;
end;
end;
end;
1.2 RemoteFreeLibraryNative
function RemoteFreeLibraryNative(PID: Cardinal; //PID del processo remoto
NomeDll: PAnsiChar;
var outValue: Cardinal; //valore importante ottenuto dalla funzione remota
var codError: Cardinal; //errore riscontrato dalla funzione remota
synch: Boolean): Boolean; //esecuzione sincrona del thread remoto
type
TRemoteFreeLibraryNativeData = record
errorcod: Cardinal; //codice di errore rilevato nella funzione remota
output: Cardinal; //risultato significativo nella funzione remota
//Indirizzi delle API usate: elenco puntatori a funzioni; N.B. rigorosamente stdcall
//Es. pLoadLibraryA: function(pLibFileName: PAnsiChar): Cardinal; stdcall;
pExitThread: procedure (dwExitCode: Cardinal); stdcall; //API obbligatoria
pRtlAnsiStringToUnicodeString: function(
DestinationString : PUNICODE_STRING;
SourceString : PANSI_STRING;
AllocateDestinationString : Boolean
): Integer; stdcall;
pLdrGetDllHandle: function(
pwPath : PWORD;
Unused : pointer;
ModuleFileName : PUNICODE_STRING;
pHModule : pCardinal
): Integer; stdcall;
pLdrUnLoadDll: function(
ModuleHandle: Cardinal
): Integer; stdcall;
pRtlFreeUnicodeString: procedure(
UnicodeString : PUNICODE_STRING
); stdcall;
pRtlNtStatusToDosError: function(
const Status : Integer
): Cardinal; stdcall;
//Puntatori a dati; Es. tutte le stringhe vanno messe qui dentro
//Es. pNomeDll: Pointer;
pNomeDll: Pointer;
NomeDllLength: Cardinal;
//ModuleFileNameLength: Cardinal;
//ModuleHandle: Cardinal;
end;
var
hProcess: Cardinal; //handle al processo remoto
RemoteFreeLibraryNativeData: TRemoteFreeLibraryNativeData;
pRemoteFreeLibraryNativeData: Pointer; //indirizzo del record nello
//spazio di memoria del processo remoto
pRemoteFreeLibraryNativeThread: Pointer; //indirizzo della funzione del thread
//nello spazio di memoria del processo remoto
output_value: Cardinal;
error_code: Cardinal;
functionSize, parameterSize: Cardinal;
procedure RemoteFreeLibraryNativeThread(lpParameter: pointer); stdcall;
var
//qui posso definire delle variabili locali
AnsString: ANSI_STRING;
UncString: UNICODE_STRING;
status: Integer;
hModule: Cardinal;
begin
with TRemoteFreeLibraryNativeData(lpParameter^) do
begin
//implementazione: tutte le API vengono chiamate tramite i puntatori nel record
AnsString.Buffer := pNomeDll;
AnsString.Length := NomeDllLength;
AnsString.MaximumLength := NomeDllLength;
status := pRtlAnsiStringToUnicodeString(
@UncString,
@AnsString,
True);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
pExitThread(0);
end;
status := pLdrGetDllHandle(nil, nil, @UncString, @output);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
pExitThread(0);
end;
status := pLdrUnLoadDll(output);
if status < 0 then
begin
errorcod := pRtlNtStatusToDosError(status);
pExitThread(0);
end;
pRtlFreeUnicodeString(@UncString);
pExitThread(1);
//N.B. chiamare sempre pExitCodeThread per definire il codice di uscita del thread
//Es. pExitCodeThread(1) significa successo
// pExitCodeThread(0) significa fallimento
end;
end;
//dealloco la memoria in remoto
function RemoteFreeLibraryNativeUnloadData(): Boolean;
begin
Result := False;
with RemoteFreeLibraryNativeData do
begin
//chiamo UnloadData su tutti i puntatori a dati inclusi nel record
UnloadData(hProcess, pNomeDll);
end;
//deallocazione spazio per il parametro
UnloadData(hProcess, pRemoteFreeLibraryNativeData);
//deallocazione spazio per la funzione
UnloadData(hProcess, pRemoteFreeLibraryNativeThread);
Result := True;
end;
//definisco i valori dei campi del record e copio i dati in remoto
function RemoteFreeLibraryNativeInjectData(): Boolean;
begin
Result := False;
try
//inizializzazione valori campi del record:
with RemoteFreeLibraryNativeData do
begin
errorcod := 0;
output := 0;
//assegno i valori ai puntatori a funzione (tramite GetModuleHandle e GetProcAddres)
//Es. pLoadLibraryA := GetProcAddress(GetModuleHandle('kernel32'),
'LoadLibraryA');
pExitThread := GetProcAddress(GetModuleHandle('kernel32'),
'ExitThread'); //API Obbligatoria
pRtlAnsiStringToUnicodeString := GetProcAddress(GetModuleHandle('ntdll'),
'RtlAnsiStringToUnicodeString');
pLdrGetDllHandle := GetProcAddress(GetModuleHandle('ntdll'),
'LdrGetDllHandle');
pLdrUnloadDll := GetProcAddress(GetModuleHandle('ntdll'),
'LdrUnloadDll');
pRtlFreeUnicodeString := GetProcAddress(GetModuleHandle('ntdll'),
'RtlFreeUnicodeString');
pRtlNtStatusToDosError := GetProcAddress(GetModuleHandle('ntdll'),
'RtlNtStatusToDosError');
NomeDllLength := Length(NomeDll) + 1;
//assegno i valori ai puntatori ai dati (tramite InjectData);
//N.B. se una InjectData ritorna False allora bisogna chiamare Exit
if not InjectData(hProcess,
NomeDll,
Length(NomeDll),
False,
pNomeDll) then
begin
Exit;
end;
end;
//copio il parametro
parameterSize := SizeOf(RemoteFreeLibraryNativeData);
if not InjectData(hProcess,
@RemoteFreeLibraryNativeData,
parameterSize,
False,
pRemoteFreeLibraryNativeData) then
begin
Exit;
end;
//copio la funzione
functionSize := SizeOfProc(@RemoteFreeLibraryNativeThread);
if not InjectData(hProcess,
@RemoteFreeLibraryNativeThread,
functionSize,
True,
pRemoteFreeLibraryNativeThread) then
begin
Exit;
end;
Result := True;
finally
if not Result then
begin
RemoteFreeLibraryNativeUnloadData;
end;
end;
end;
begin
//inizializzo a zero le variabili locali
hProcess := 0;
pRemoteFreeLibraryNativeData := nil;
pRemoteFreeLibraryNativeThread := nil;
output_value := 0;
error_code := 0;
try
hProcess := OpenProcessEx(PROCESS_CREATE_THREAD +
PROCESS_QUERY_INFORMATION +
PROCESS_VM_OPERATION +
PROCESS_VM_WRITE +
PROCESS_VM_READ,
False,
PID
);
if hProcess = 0 then
begin
ErrStr('OpenProcessEx');
Exit;
end;
if not RemoteFreeLibraryNativeInjectData() then
Exit;
if not InjectThread(hProcess,
pRemoteFreeLibraryNativeThread,
pRemoteFreeLibraryNativeData,
output_value,
error_code,
synch) then
Exit;
//output_value e error_code sono stati inizializzati a 0.
//sono stati modificati dalla InjectThread solo se synch = true;
//se synch=false sono rimasti uguali a zero
outvalue := output_value;
codError := error_code;
Result := True;
finally
if synch or (not Result) then
begin
RemoteFreeLibraryNativeUnloadData;
end;
if hProcess <> 0 then
begin
if not CloseHandle(hProcess) then
begin
ErrStr('CloseHandle');
end;
end;
end;
end;
RemoteLoadUnloadNative.7z
|