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
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