|
La dll advapi32.dll esporta numerose funzioni tra cui diverse funzioni
con nome insignificante del tipo System<3 cifre> . Son tutte funzioni non
documentate ma di alcune di queste si conosce il significato. Il titolo di
questo articolo forse è un pò fuorviante nel senso che non sto svelando al mondo
misteri arcani; già da tempo si conosce il significato delle System006 e
System007, tuttavia non c'è molto materiale al riguardo. Così ho deciso
di dare il mio umile contributo alla causa della diffusione della conoscenza
riguardo a ste 2 benedette funzioni. Tagliamo corto e diciamo che la
System006 restituisce l'LM Hash di una stringa (più precisamente una
null terminated string con caratteri presi dall'insieme ASCII) mentre la
System007 restituisce l'NT Hash di una stringa (più precisamente una
counted string con caratteri presi dall'insieme UNICODE). Darò per scontato che
si sappia cosa sono l'LM Hash e l'NT Hash tanto basta un pò documentarsi sul
web. Già un anno fà m'ero creato un applicativo che restituisce l'elenco delle
utenze locali di Windows e per ognuna l'LM Hash e l'NT Hash:
Windows Password Hashes
l'applicativo si basa sul codice pubblicato da Todd A. Sabin nel 1998 (si,
... 10 anni fà) e denominato pwdump2 a cui hanno fatto poi seguito altre
versioni; le versioni più recenti basate su quell'implementazione sono
reperibili (tutte rigorosamente OpenSource) all'indirizzo
http://www.foofus.net/
La mia implementazione è l'unica presente sul web che consiste in un una
semplice procedura che esegue Code Injection su lsass.exe e preleva i valori di
utenze e Hashes delle password; negli altri casi viene sempre utilizzata una dll
da mappare nello spazio di memoria di lsass.exe; a suo tempo siccome non sapevo
cosa fare decisi di implementare il tutto in una procedura che si occupasse di
tutto. Basta quindi chiamare tale procedura e si ottiene l'elenco. Ma torniamo a
noi e all'obiettivo di questo articolo: mi son creato 2 procedure wrapper, una
che restituisce l'LM Hash e l'altra che restituisce l'NT Hash; è da sottolineare
che sia l'LM Hash sia l'NT hash non sono altro che una sequenza di 16 byte:
quello che vediamo normalmente restituito in output ad esempio dal programma cui
ho fatto riferimento, non è altro che la rappresentazione esadecimale di quella
sequenza; ogni byte richiede 2 cifre esadecimale per essere rappresentato,
abbiamo 16 byte e quindi avremo 32 cifre esadecimali per la rappresentazione.
ecco le dichiarazioni
type
PWHASH = record
data: array[1..16] of Byte;
end;
PPWHASH = ^PWHASH;
function RtlNtStatusToDosError(
Status: Integer
): Cardinal; stdcall; external 'ntdll.dll';
function RtlAnsiStringToUnicodeString(
DestinationString: PUNICODE_STRING;
SourceString: PANSI_STRING;
AllocateDestinationString: BOOLEAN
): Integer; stdcall; external 'ntdll.dll';
procedure RtlFreeUnicodeString(UnicodeString: PUNICODE_STRING); stdcall; external 'ntdll.dll';
function SystemFunction006(
Password: PAnsiChar;
HashLM: PPWHASH
): Cardinal; stdcall; external 'advapi32.dll';
function SystemFunction007(
Password: PUNICODE_STRING;
HashLM: PPWHASH
): Cardinal; stdcall; external 'advapi32.dll';
ed ora le implementazioni
function ErrStrNative(nomeFunc: string; status: Integer): Boolean;
var
error_code: Cardinal;
error_description: string;
error_string: string;
begin
result := False;
error_string := nomeFunc + ': ' +
'NTSTATUSCODErr=' + IntToHex(status,8);
error_code := RtlNtStatusToDosError(status);
error_description := SysErrorMessage(error_code);
error_string := error_string +
'; CODErr=' + IntToStr(error_code) +
'; Descr=' + error_description;
ScriviLog(error_string);
result := True;
end;
function CalcLMHash(Password: PAnsiChar; var LMHash: string): Boolean;
var
status: Integer;
Hash: PWHASH;
PassParam: PAnsiChar;
begin
Result := False;
try
if Length(Password) > 14 then
begin
PassParam := '';
end
else
begin
PassParam := PAnsiChar(UpperCase(Password));
end;
status := SystemFunction006(PassParam, @Hash);
if status < 0 then
begin
ErrStrNative('SystemFunction006', status);
Exit;
end;
LMHash := ConvertDataToHex(@Hash, 16, '');
result := True;
finally
end;
end;
function CalcNTHash(Password: PAnsiChar; var NTHash: string): Boolean;
var
PasswordAnsString: ANSI_STRING;
PasswordUncString: UNICODE_STRING;
AllocatedPasswordUncString: Boolean;
status: Integer;
Hash: PWHASH;
begin
Result := False;
AllocatedPasswordUncString := False;
try
PasswordAnsString.Buffer := PAnsiChar(Password);
PasswordAnsString.Length := Length(Password);
PasswordAnsString.MaximumLength := PasswordAnsString.Length;
status := RtlAnsiStringToUnicodeString(
@PasswordUncString,
@PasswordAnsString,
True
);
if status < 0 then
begin
ErrStrNative('RtlAnsiStringToUnicodeString', status);
Exit;
end;
AllocatedPasswordUncString := True;
status := SystemFunction007(@PasswordUncString, @Hash);
if status < 0 then
begin
ErrStrNative('SystemFunction007', status);
Exit;
end;
NTHash := ConvertDataToHex(@Hash, 16, '');
result := True;
finally
if AllocatedPasswordUncString then
RtlFreeUnicodeString(@PasswordUncString);
end;
end;
hashes_undocumented.7z
|