PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C] kernel32.dll!GetProcAddress Finder



blackberry
12.04.2010, 23:34
Was macht das?
Es findet die Adresse der Funktion GetProcAddress (http://msdn.microsoft.com/en-us/library/ms683212%28VS.85%29.aspx). Diese kann dann dazu verwendet werden weitere Funktionen (LoadLibrary, ...) zu laden.

Wie funktioniert das?
Windows speichert eine Liste aller geladenen Module (das Programm selber, benötigte DLLs, ...), deren Startelement von der PEB_LDR_DATA Struktur referenziert wird. Die Adresse dieser Struktur befindet sich im Process Enviroment Block (PEB). Ein Zeiger zum PEB befindet sich in fs:[30h].
Danke dieser Vorraussetzungen ist es möglich, die Position von kernel32.dll im Speicher (welche bei verschiedenen Versionen/Servicepacks/Windows Versionen und Relocations variiert und somit nicht hardcoded werden sollte) zu lokalisieren.
Von dort an kann man ganz normal kernel32.dll parsen (--> PE Format) und den ExportTable "durchlaufen".
Dort findet man schlussendlich die RVA (Adresse die relativ zur momentanen Position von kernel32.dll ["ImageBase"] ist).
Zu dieser addiert man dann noch die über den PEB gefundene Adresse von kernel32.dll und schon hat man die Adresse von GetProcAddress, welche man anschließend über Funktionszeiger (ich liebe C :D) aufrufen kann.

Wozu ist das gut?
Import verstecken, Heuristiken austricksen und natürlich einfach zum Spaß :P

Wieso ist das entstanden?
Das ich das implementiert habe ist ursprünglich schon etwas länger her (die originale Version ist in MASM), doch jetzt habe ich das ganze auf Anfrage auch nach C portiert.

Genaueres - Siehe:
http://free-hack.com/showpost.php?p=419081&postcount=5
http://free-hack.com/showthread.php?t=52835

gpa.h:

#ifndef W32_GPA_H
#define W32_GPA_H

#include <windows.h>

typedef struct _LSA_UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
}
LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;

typedef struct _LDR_MODULE
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
}
LDR_MODULE, *PLDR_MODULE;

/*
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
}
LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
*/

typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
}
PEB_LDR_DATA, *PPEB_LDR_DATA;

/*
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
}
PEB_LDR_DATA, *PPEB_LDR_DATA;
*/

typedef struct _PEB
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PVOID ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PVOID PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
}
PEB;

#endif

gpa.c:

#include <stdio.h>
#include <windows.h>
#include <ctype.h>
#include "gpa.h"


typedef FARPROC (*GPA)(HMODULE, LPCSTR);

PEB *getPEBaddr(void)
{
asm(
"MOVL %fs:(0x30), %eax;"
"LEAVE;"
"RET;"
);
}

int unicode_ascii_equal(char *as, UNICODE_STRING *us)
{
int i;

if (strlen(as) != ((int) us->Length / 2))
{
return 1;
}

for(i = 0; as[i]; i++)
{
if (tolower(as[i]) != tolower(((char *) us->Buffer)[2 * i]))
{
return i + 2;
}
}

return 0;
}

int main(void)
{
int i;
int offset;
int ordinal;
int SymbolRVA;
short *ExportOrdinalTable;
int *ExportAddressTable;
int index;
PEB *peb;
PEB_LDR_DATA *pld;
LDR_MODULE *ldrm;
IMAGE_DOS_HEADER *idh;
IMAGE_NT_HEADERS *inh;
IMAGE_EXPORT_DIRECTORY *ied;
GPA _GetProcAddress;


peb = getPEBaddr();
pld = peb->Ldr;

ldrm = (LDR_MODULE *) pld->InLoadOrderModuleList.Flink;
while(ldrm->BaseAddress && unicode_ascii_equal("kernel32.dll", &ldrm->BaseDllName) != 0)
{
ldrm = (LDR_MODULE *) ldrm->InLoadOrderModuleList.Flink;
}

if (!ldrm->BaseAddress)
{
return 1;
}

printf("kernel32.dll addr = 0x%08X\n", ldrm->BaseAddress);

idh = (IMAGE_DOS_HEADER *) ldrm->BaseAddress;
inh = (IMAGE_NT_HEADERS *) (ldrm->BaseAddress + idh->e_lfanew);

if (inh->OptionalHeader.DataDirectory[0].Size > 0)
{
ied = (IMAGE_EXPORT_DIRECTORY *) (ldrm->BaseAddress + inh->OptionalHeader.DataDirectory[0].VirtualAddress);

printf("%d functions\n", ied->NumberOfFunctions);

offset = ied->AddressOfNames;
for(i = 0; i < ied->NumberOfFunctions; i++)
{
index = *((int *) (ldrm->BaseAddress + offset));
//printf("%s\n", ldrm->BaseAddress + index);

if (!strcmp((char *) (ldrm->BaseAddress + index), "GetProcAddress"))
{
printf("---------> function found! (i = %d [0x%X])\n", i, i);

ExportOrdinalTable = (short *) (ldrm->BaseAddress + ied->AddressOfNameOrdinals);
ordinal = ExportOrdinalTable[i];
printf("ordinal = %d\n", ordinal);

ExportAddressTable = (int *) (ldrm->BaseAddress + ied->AddressOfFunctions);
SymbolRVA = ExportAddressTable[ordinal];

printf("GPA = %X [RVA]\n", SymbolRVA);
printf("GPA = %X\n", ldrm->BaseAddress + SymbolRVA);
_GetProcAddress = (GPA) (ldrm->BaseAddress + SymbolRVA);

break;
}

offset += 4;
}
}

// _GetProcAddress := address of GetProcAddress in kernel32.dll

return 0;
}

Quellen für die Strukturdefinitionen:
Microsoft's MSDN und undocumented.ntinternals.net.

Anmerkung:
In gpa.h finden sich auskommentierte Strukturen. Diese sind da, weil sie für die anderen Strukturen, oder sogar im Programm selber wichtig sind, jedoch bereits definiert wurden.
MSDN sagt z.B., dass die Struktur _PEB bzw. das dazugehörige Typedef in "Winternl.h" definiert sein sollten.
Da MinGW diesen Header jedoch nicht besitzt habe ich mir das Zeug zusammengeschustert und die überflüssigen Strukturen da gelassen, für den Fall, dass jemand diese nicht hat.


MfG. BlackBerry

br00_pwn
13.04.2010, 01:21
Gute Arbeit, Black :)

Bozok
13.04.2010, 06:10
Sehr schön, jedoch würde ich gleich Uppercase('kernel32.dll') nehmen, das war bei mir in Windows 7 der Fall wodurch sowas nicht geklappt hat :/

blackberry
13.04.2010, 15:04
Ich nutze auch Windows 7 und es hat bei mir sehr gut funktioniert.
Dennoch habe ich noch einen kleinen Fehler gefunden und dabei gleich noch ein tolower() bei dem Vergleich der Zeichen eingefügt.

jojoomgasd
17.05.2010, 22:24
dafür muss aber GetProcAddress aber in der ImportTable sein oder?

wenn nicht dann ist das hammer :P

man kann ja auch noch 'GetModuleHandleA' laden und somit alle api's zur laufzeit laden :P

l0dsb
17.05.2010, 23:53
Zur Erwähnen wäre noch, dass diese Vorgehensweise auch sehr oft in Malware (siehe 29A) und Softwareschutzsystemen (ASProtect, EXECryptor, Themida und ähnliche) verwendet wird.

Dort werden aber nicht komplette Funktionsnamen verglichen, sondern lediglich deren Hashes (üblicherweise in dword-Größe). Das mag schneller sein, einleuchtend ist jedoch, dass die Funktionsnamen der gesuchten Funktionen nicht sofort ersichtlich sind. ;)

Zum Vorposter: Nein, GetProcAddress muss nicht in der IT sein, aber kernel32.dll im Speicher. LoadLibrary wäre da auch noch eine Idee, um auch APIs nachladen zu können, deren DLL noch nicht geladen ist.

Ach ja: Ich warte auf ein neues UnpackUs, BlackBerry! :) Hoffentlich mit regerer Beteiligung. :/