In questo articolo verrà descritta una funzione, utile per conoscere l'indirizzo di una funzione esportata da una libreria, partendo dal suo Image Base Address.
Ora tanto per essere chiari, non ha molto senso usare questa funzione in fase di compilazione, dato che potremo sempre usare la funzione del kernel32 GetProcAddress, il problema sorge quando il codice che devo preparare deve trovare le funzioni da chiamare a runtime e in quel caso non potrebbe utilizzare la GetProcAddress dato che non ne conoscerebbe neppure l'indirizzo.
Nel seguente esempio useremo anche la funzione GetModuleHandleKernel32 per trovare l'IBA della libreria kernel32.dll, dopo di che al suo interno cercheremo l'indirizzo della funzione LoadLibraryA.
program KernelLoadLibraryAddress2;
{$APPTYPE CONSOLE}
uses
SysUtils;
function GetModuleHandleKernel32:cardinal;
begin
asm
xor edx,edx
mov edx,fs:[edx]
mov ebx,[edx+4]
jmp @1
@2:
mov ebx,[edx+4]
mov edx,[edx]
@1:
cmp edx,$FFFFFFFF
jnz @2
shr ebx,12
shl ebx,12
@3:
cmp word[ebx],$5A4D
je @4
add ebx,-$FFF
add ebx,-$1
jmp @3
@4:
mov result,ebx
end;
end;
function GetAddressFunction(IBA:cardinal;nome:PChar;Dim:cardinal):cardinal;
var
N,i,j:cardinal;
OK:boolean;
NAT,FAT:Cardinal;
P:PChar;
begin
asm
mov eax,IBA
mov eax,[eax+$3c] // trova il PE offset
add eax,IBA
add eax,$78
mov eax,[eax] // trova l'offset della Export Directory
add eax,IBA // Indirizzo della Export Directory
mov ecx,[eax+$18] // Numero delle funzioni esportate (N)
mov N,ecx
mov edx,[eax+$1C] // offset della tabella delle funzioni
add edx,IBA // Indirizzo della tabella delle funzioni (FAT)
mov FAT,edx
mov ebx,[eax+$20] // offset della tabella dei nomi
add ebx,IBA // Indirizzo della tabella dei nomi (NAT)
mov NAT,ebx
end;
i:=0;
OK:=false;
while i<=n do begin //percorre tutta la lista dei nomi fino a trovare quello esatto
P:=PChar(PCardinal(NAT)^+IBA);
ok:=false;
j:=0;
while j<=Dim do begin
if nome[j]=#0 then begin
if P[j]=#0 then begin
ok:=true;
break;
end
else
break;
end
else begin
if P[j]<>nome[j] then
break;
end;
inc(j);
end;
if ok then
break;
NAT:=NAT+4;
inc(i);
end;
//uno volta trovato l'indice lo usiamo per trovare
//l'indirizzo esatto della funzione esportata
if ok then begin
result:=Pcardinal(FAT+4*i)^+IBA;
end
else
result:=0;
end;
var
IBA:cardinal;
Indirizzo:cardinal;
begin
IBA:=GetModuleHandleKernel32;
writeln(format('Indirizzo modulo kernel32.dll: %xh',[IBA]));
Indirizzo:=GetAddressFunction(IBA,'LoadLibraryA',12);
writeln(format('Indirizzo modulo kernel32.dll: %xh',[Indirizzo]));
readln;
end.
questo metodo è molto diretto, e devo dire che la ricerca tra i nomi delle funzioni si sarebbe potuta fare anche usando un valore di hash del nome, cioè prima avremo dovuto calcolare il valore di hash del nome cercato, e dopo avremo dovuto calcolare il valore di hash di tutte le funzioni fino a quando questo non avrebbe avuto lo stesso valore di quello originario. Questo sicuramente avrebbe reso molto meno complicato l'algoritmo.
Vediamo una sua implementazione:
function GetAddressFunction(IBA:cardinal;nome:PChar):cardinal;
begin
asm
mov ebx,IBA
mov ebx,[ebx+$3c] // trova il PE offset
add ebx,IBA
add ebx,$78
mov ebx,[ebx] // trova l'offset della Export Directory
add ebx,IBA // Indirizzo della Export Directory
mov ecx,[ebx+$18] // Numero delle funzioni esportate (N) ecx
mov edx,[ebx+$20] // offset della tabella dei nomi
add edx,IBA // Indirizzo della tabella dei nomi (NAT) edx
xor ebx,ebx //calcola hash nome funzione da trovare
mov esi,nome
@2:
xor eax,eax
lodsb
cmp al,ah // fine stringa?
je @1
add ebx,eax
jmp @2
@1: //percorre la lista dei nomi delle funzioni
mov esi,[edx+ecx*4] //calcolandone l'hash
add esi,IBA
xor edi,edi
@3:
xor eax,eax
lodsb
cmp al,ah // fine stringa?
je @4
add edi,eax
jmp @3
@4:
cmp ebx,edi //confronta hash
je @5
test ecx,ecx
jz @7
dec ecx
jmp @1
@7:
xor eax,eax
mov result,eax
jmp @6
@5:
mov ebx,IBA // se ha trovato un riscontro:
mov ebx,[ebx+$3c] // trova il PE offset
add ebx,IBA
add ebx,$78
mov ebx,[ebx] // trova l'offset della Export Directory
add ebx,IBA // Indirizzo della Export Directory
mov edx,[ebx+$1C] // offset della tabella delle funzioni
add edx,IBA // Indirizzo della tabella delle funzioni (FAT) edx
shl ecx,2 //Indirizzo funzione = [FAT+4*i]+IBA;
add edx,ecx //
mov edx,[edx] //
add edx,IBA //
mov result,edx //
@6:
lea ebx,ebp-$0c
end;
end;
Riporto anche il relativo codice macchina:
// begin
00408B84 55 push ebp
00408B85 8BEC mov ebp,esp
00408B87 83C4F4 add esp,-$0c
00408B8A 53 push ebx
00408B8B 8955F8 mov [ebp-$08],edx
00408B8E 8945FC mov [ebp-$04],eax
00408B91 8D5DF4 lea ebx,[ebp-$0c]
00408B94 8B5DFC mov ebx,[ebp-$04] // mov ebx,IBA
00408B97 8B5B3C mov ebx,[ebx+$3c] // trova il PE offset
00408B9A 035DFC add ebx,[ebp-$04] // add ebx,IBA
00408B9D 83C378 add ebx,$78 // add ebx,$78
00408BA0 8B1B mov ebx,[ebx] // trova l'offset della Export Directory
00408BA2 035DFC add ebx,[ebp-$04] // add ebx,IBA - Indirizzo della Export Directory
00408BA5 8B4B18 mov ecx,[ebx+$18] // Numero delle funzioni esportate (N) ecx
00408BA8 8B5320 mov edx,[ebx+$20] // offset della tabella dei nomi
00408BAB 0355FC add edx,[ebp-$04] // add edx,IBA - Indirizzo della tabella dei nomi (NAT) edx
00408BAE 31DB xor ebx,ebx //calcola hash nome funzione da trovare
00408BB0 8B75F8 mov esi,[ebp-$08] // mov esi,nome
00408BB3 31C0 xor eax,eax
00408BB5 AC lodsb
00408BB6 38E0 cmp al,ah // fine stringa?
00408BB8 7404 jz $00408bbe // je @1
00408BBA 01C3 add ebx,eax
00408BBC EBF5 jmp $00408bb3 // jmp @2
00408BBE 8B348A mov esi,[edx+ecx*4] //calcola l'hash
00408BC1 0375FC add esi,[ebp-$04] // add esi,IBA
00408BC4 31FF xor edi,edi
00408BC6 31C0 xor eax,eax
00408BC8 AC lodsb
00408BC9 38E0 cmp al,ah // fine stringa?
00408BCB 7404 jz $00408bd1 // je @4
00408BCD 01C7 add edi,eax
00408BCF EBF5 jmp $00408bc6 // jmp @3
00408BD1 39FB cmp ebx,edi //confronta hash
00408BD3 740E jz $00408be3 // je @5
00408BD5 85C9 test ecx,ecx
00408BD7 7403 jz $00408bdc // jz @7
00408BD9 49 dec ecx
00408BDA EBE2 jmp $00408bbe // jmp @1
00408BDC 31C0 xor eax,eax
00408BDE 8945F4 mov [ebp-$0c],eax // mov result,eax
00408BE1 EB24 jmp $00408c07 // jmp @6
00408BE3 8B5DFC mov ebx,[ebp-$04] // mov ebx,IBA - se ha trovato un riscontro:
00408BE6 8B5B3C mov ebx,[ebx+$3c] // trova il PE offset
00408BE9 035DFC add ebx,[ebp-$04] // add ebx,IBA
00408BEC 83C378 add ebx,$78
00408BEF 8B1B mov ebx,[ebx] // mov ebx,[ebx] - trova l'offset della Export Directory
00408BF1 035DFC add ebx,[ebp-$04] // add ebx,IBA - Indirizzo della Export Directory
00408BF4 8B531C mov edx,[ebx+$1c] // offset della tabella delle funzioni
00408BF7 0355FC add edx,[ebp-$04] // add edx,IBA - Indirizzo della tabella delle funzioni (FAT) edx
00408BFA C1E102 shl ecx,$02 //Indirizzo funzione = [FAT+4*i]+IBA;
00408BFD 01CA add edx,ecx
00408BFF 8B12 mov edx,[edx]
00408C01 0355FC add edx,[ebp-$04] // add edx,IBA
00408C04 8955F4 mov [ebp-$0c],edx // mov result,edx
00408C07 8D5DF4 lea ebx,[ebp-$0c]
00408C0A 8B03 mov eax,[ebx] // end;
00408C0C 5B pop ebx
00408C0D 8BE5 mov esp,ebp
00408C0F 5D pop ebp
00408C10 C3 ret
Naturalmente per poter essere usato bisognerà adattarlo al contesto, ma la parte centrale dovrebbe rimanere la stessa.





