Locating Kernal32.dll in memory
|
Windows programming
basically, is calling code from dlls. For this purpose, it is extremely
important to have the address of the function that is to be called. However,
before executing code at the address, we need to know the location of the dll
in memory. There are two functions in kernel32.dll, which simplify this task:
LoadLibrary to acquire the address of the dll in memory and GetProcAddress to
obtain the address of the function in the dll.
However, to use these functions,
it is extremely important to know where kernel32.dll is loaded in memory.
Besides, anyone interested in writing shellcode must know the address of this
dll in memory. Once the location is identified, then the exports table of
various other dlls can be examined to expose the address of their functions.
There are different ways to
tackle this issue, and this chapter touches on three simplest ways to do so. So
here we go, righto…
a.cpp
#include <windows.h>
#include <stdio.h>
short fsregister;
void RegisterValue()
{
__asm
{
mov fsregister, fs
}
}
main()
{
RegisterValue();
printf("%x\n",fsregister);
}
Output
38
We have been in the computer
world way beyond the good old days of DOS. Therefore, we can make a statement
that Intel erred in the making of their 8086 processor. We will justify our
point. While accessing memory, the address or memory location must be put in a
register and therefore the register must be large enough to hold its address.
For this purpose, Intel closed on two 16-bit registers when it should have
given one 32-bit register instead.
Thus, each pointer or memory
address was stored in two registers, one called the segment and the other the
offset. To get at the actual address, the segment register was to be multiplied
by 16 first and then added to the offset. The result would be a 20-bit address,
just enough to access 1 MB of RAM.
This concept would be alien
to most programmers who started programming from 386 onwards as by then all
registers were 32 bits, thus forgoing the concept of segment-offset. The 80386
added two more segment registers to the four registers of 8086 (CS, DS, ES and
SS) viz, the FS and GS registers. These registers were to be used only by the
operating system internally.
The FS register is used to
point to an undocumented data structure that stores tons of useful information.
The cl compiler therefore restricts us from accessing this register. All
pointers are therefore with reference to the ds register, which points to the
data segment. Thus, we need some instructions in the assembler language to
reach the memory pointed by the fs segment register. The above program displays
the value of fsregister as 0x38.
Instead of returning a value
in the eax register, which is what we normally use, we create a variable of
type short called fsregister. Then, using the mov instruction the value of the
fs register is placed in it. This variable fsregister is then displayed using
the printf statement. If the data type of the variable was int and not short,
then an error would be seen since, the segment register is 16 bits wide. Also,
the assembler instruction of mov eax, fs, would show error since there would be
mismatch in size of the operands. Both operands of the mov must be of the same
size; register eax is 32 bits whereas fs is 16 bits large. Alternatively, the
mov could be given as mov ax, fs.
We seriously hope that the
compiler in the next version allows accessing memory pointed to by the other
segment registers, then the assembler approach could be safely evaded.
a.cpp
#include <windows.h>
#include <stdio.h>
short fsregister;
void *RegisterValue()
{
__asm
{
mov fsregister, fs
mov eax ,fs:20h
//mov [fsregister], fs
//mov eax ,fs:[20h]
}
}
unsigned long _stdcall abc(void *i)
{
void *pid = RegisterValue();
printf("Thread No %d
",i);
printf("fs=%x
Pid=%d\n",fsregister , pid);
return 0;
}
void main()
{
CreateThread(0,0,abc,(void
*)1,0,0);
CreateThread(0,0,abc,(void
*)2,0,0);
CreateThread(0,0,abc,(void
*)3,0,0);
getchar();
}
Output
Thread No 1 fs=38 Pid=1612
Thread No 2 fs=38 Pid=1612
Thread No 3 fs=38 Pid=1612
Three threads are created
using the CreateThread function. This function takes 6 parameters and all are
given a value of 0 except two. The third parameter is for the name of the
function that is to be executed each time the thread is created and the fourth
parameter represents the parameter list.
In abc, the RegisterValue
function plays a very important role. It returns the int stored at 20h bytes
from the start of the data structure pointed by the fs register. The syntax
fs:20h is similar to fs:[20h], where the square brackets denote a pointer
reference which is the default anyway. As a result of which if the brackets are
missing, the assembler does the needful. Ditto for variables.
The output displays three
statements since the abc function is called thrice. The pointer parameter i
shows the values 1,2 and 3 accordingly but the fs register has the same value.
Each program when executed
is given a unique pid or process id by the operating system. This can be easily
verified on pressing the keys Ctl-Alt-Del on the operating system and then
clicking on the button Task Manager. The tab Processes shows a list of programs
running on the system. The first column here is the name of the program
followed by its PID.
The above program does not
end unless the enter key is pressed as the system is waiting at the getchar()
function. Till then it is loaded in the memory and is assigned a unique pid.
The Task Manager, hence, shows program a.exe in memory with its pid 1612.
Clicking once on the name
tag sorts the program names alphabetically and then clicking once again
reverses the sort order. Not including the getchar() function would result in
the program quitting out thus not giving the threads a chance to complete. The
abc function would terminate without executing all the printfs.
#include <windows.h>
#include <stdio.h>
short fsregister;
void *RegisterValue()
{
__asm
{
mov fsregister, fs
mov eax ,fs:18h
}
}
unsigned long _stdcall abc(void *i)
{
void *tibinfo =
RegisterValue();
printf("Thread No %d
",i);
printf("fs=%x
tibinfo=%x\n",fsregister , tibinfo);
return 0;
}
main()
{
CreateThread(0,0,abc,(void
*)1,0,0);
CreateThread(0,0,abc,(void
*)2,0,0);
CreateThread(0,0,abc,(void
*)3,0,0);
getchar();
}
Thread No 1 fs=38
tibinfo=7ffdd000
Thread No 2 fs=38
tibinfo=7ffdc000
Thread No 3 fs=38
tibinfo=7ffdb000
The only variation here from
the previous one is that the value placed 18h from the fs register is
displayed. The earlier program displayed a value 20h away. The last program
displayed the same value for each thread as the process id of a thread is the
same. However here, the value displayed is that of a pointer that points to a
different area of memory for each thread.
#include <windows.h>
#include <stdio.h>
/*
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
typedef struct _NT_TEB
{
NT_TIB
Tib; // 00h
PVOID
EnvironmentPointer; // 1Ch
CLIENT_ID
Cid; // 20h
PVOID
ActiveRpcInfo; // 28h
PVOID
ThreadLocalStoragePointer; // 2Ch
PPEB
Peb; // 30h
ULONG
LastErrorValue; // 34h
ULONG CountOfOwnedCriticalSections; // 38h
PVOID
CsrClientThread; // 3Ch
PVOID
Win32ThreadInfo; // 40h
ULONG
Win32ClientInfo[0x1F]; // 44h
PVOID
WOW32Reserved; // C0h
ULONG
CurrentLocale; // C4h
ULONG
FpSoftwareStatusRegister; // C8h
PVOID SystemReserved1[0x36]; // CCh
PVOID
Spare1; // 1A4h
LONG
ExceptionCode; // 1A8h
ULONG
SpareBytes1[0x28]; // 1ACh
PVOID
SystemReserved2[0xA]; // 1D4h
GDI_TEB_BATCH GdiTebBatch;
// 1FCh
ULONG
gdiRgn; // 6DCh
ULONG
gdiPen; // 6E0h
ULONG
gdiBrush; // 6E4h
CLIENT_ID
RealClientId; // 6E8h
PVOID
GdiCachedProcessHandle; // 6F0h
ULONG
GdiClientPID; // 6F4h
ULONG
GdiClientTID; // 6F8h
PVOID
GdiThreadLocaleInfo; // 6FCh
PVOID
UserReserved[5]; // 700h
PVOID
glDispatchTable[0x118]; // 714h
ULONG
glReserved1[0x1A]; // B74h
PVOID
glReserved2; // BDCh
PVOID
glSectionInfo; // BE0h
PVOID
glSection; // BE4h
PVOID
glTable; // BE8h
PVOID
glCurrentRC; // BECh
PVOID
glContext; // BF0h
NTSTATUS
LastStatusValue; // BF4h
UNICODE_STRING StaticUnicodeString; // BF8h
WCHAR
StaticUnicodeBuffer[0x105]; // C00h
PVOID
DeallocationStack; // E0Ch
PVOID
TlsSlots[0x40]; // E10h
LIST_ENTRY
TlsLinks; // F10h
PVOID
Vdm; // F18h
PVOID
ReservedForNtRpc; // F1Ch
PVOID
DbgSsReserved[0x2]; // F20h
ULONG
HardErrorDisabled; // F28h
PVOID
Instrumentation[0x10]; // F2Ch
PVOID
WinSockData; // F6Ch
ULONG
GdiBatchCount; // F70h
ULONG
Spare2; // F74h
ULONG
Spare3; // F78h
ULONG
Spare4; // F7Ch
PVOID
ReservedForOle; // F80h
ULONG
WaitingOnLoaderLock; // F84h
PVOID
StackCommit; // F88h
PVOID
StackCommitMax; // F8Ch
PVOID
StackReserve; // F90h
PVOID
MessageQueue; // ???
} NT_TEB, *PNT_TEB;
*/
void *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
void *peb = RegisterValue();
printf("PEB=%x\n",peb);
}
PEB=7ffdf000
From the little that we
learnt ourselves, we are very certain that the fs register points to a certain
structure in memory. As Microsoft refuses to divulge the internal structure
format, we can only assume what the format of this structure can be. However,
there are a large number of people on the net who have spent sleepless nights figuring out what these structures
could look like. Most people on the net give this structure a name, i.e. a Thread
Environment Block or TEB. So based on this structure, we have created a typedef
_NT_TEB for what the TEB would look like.
This structure TEB starts
with another structure, which is presumed to be a Thread Information Block,
TIB. The last member of this internal structure positioned at 18h away from the
start is a pointer to a similar structure, that differs from thread to thread.
The member at 20h is the pid that was displayed earlier.
As an exercise you can
display and decipher each of the members in the structure. These fields are
valid for Windows 2000 and are likely to be different for the 95/98 family. The
member at 30h is a pointer to a structure called the Process Environment Block
or PEB and on our machine, the PEB begins at memory location 7ffdf000.
#include <windows.h>
#include <stdio.h>
int RegisterValue()
{
__asm
{
mov eax, fs:[0xc4]
}
}
main()
{
int CurrentLocale =
RegisterValue();
printf("CurrentLocale=%x\n",CurrentLocale);
}
CurrentLocale=409
The language that used with
the os on the computer is called the locale. The locale English has a value
0x409. The above program returns the member c4 from the start of the structure,
which stores the current locale.
#include <windows.h>
#include <stdio.h>
struct PEB_LDR_DATA
{
ULONG Length;
BOOLEAN
Initialized;
PVOID
SsHandle;
LIST_ENTRY
InLoadOrderModuleList;
LIST_ENTRY
InMemoryOrderModuleList;
LIST_ENTRY
InInitializationOrderModuleList;
};
struct PEB
{
UCHAR
InheritedAddressSpace;
// 00h
UCHAR
ReadImageFileExecOptions;
// 01h
UCHAR
BeingDebugged;
// 02h
UCHAR
Spare;
// 03h
PVOID
Mutant;
// 04h
PVOID
ImageBaseAddress;
// 08h
struct PEB_LDR_DATA *Ldr; // 0Ch
void *
ProcessParameters;
// 10h
PVOID
SubSystemData;
// 14h
PVOID
ProcessHeap; // 18h
PVOID
FastPebLock;
// 1Ch
void *
FastPebLockRoutine; // 20h
void *
FastPebUnlockRoutine; // 24h
ULONG
EnvironmentUpdateCount;
// 28h
PVOID* KernelCallbackTable; // 2Ch
PVOID
EventLogSection;
// 30h
PVOID
EventLog;
// 34h
void *
FreeList; // 38h
ULONG
TlsExpansionCounter;
// 3Ch
PVOID
TlsBitmap;
// 40h
ULONG
TlsBitmapBits[0x2];
// 44h
PVOID
ReadOnlySharedMemoryBase;
// 4Ch
PVOID
ReadOnlySharedMemoryHeap; // 50h
PVOID*
ReadOnlyStaticServerData;
// 54h
PVOID
AnsiCodePageData;
// 58h
PVOID
OemCodePageData;
// 5Ch
PVOID
UnicodeCaseTableData;
// 60h
ULONG
NumberOfProcessors;
// 64h
ULONG
NtGlobalFlag;
// 68h
UCHAR
Spare2[0x4];
// 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG HeapSegmentReserve; // 78h
ULONG
HeapSegmentCommit;
// 7Ch
ULONG
HeapDeCommitTotalFreeThreshold;
// 80h
ULONG
HeapDeCommitFreeBlockThreshold;
// 84h
ULONG
NumberOfHeaps; // 88h
ULONG
MaximumNumberOfHeaps;
// 8Ch
PVOID**
ProcessHeaps;
// 90h
PVOID
GdiSharedHandleTable;
// 94h
PVOID
ProcessStarterHelper; // 98h
PVOID
GdiDCAttributeList;
// 9Ch
PVOID
LoaderLock;
// A0h
ULONG
OSMajorVersion;
// A4h
ULONG
OSMinorVersion;
// A8h
ULONG OSBuildNumber; // ACh
ULONG
OSPlatformId;
// B0h
ULONG
ImageSubSystem;
// B4h
ULONG
ImageSubSystemMajorVersion;
// B8h
ULONG ImageSubSystemMinorVersion; // C0h
ULONG
GdiHandleBuffer[0x22];
// C4h
PVOID
ProcessWindowStation;
// ???
};
PEB *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
PEB *peb = RegisterValue();
PEB_LDR_DATA *ldr =
peb->Ldr;
printf("PEB=%x
Ldr=%x\n",peb,ldr);
}
PEB=7ffdf000 Ldr=131e90
After the slight detour to
reach our destination, we decided to tell you that there exists a function
RegisterValue that gives the address of the PEB. This value is stored in a
variable peb. The structure PEB is once again undocumented. However our
interest lies in the member Ldr which is 0x1c from the start. This member is a
pointer to another structure PEB_LDR_DATA, again an undocumented one, with a
value of 0x131e90.
#include <windows.h>
#include <stdio.h>
struct PEB_LDR_DATA
{
ULONG
Length;
BOOLEAN
Initialized;
PVOID
SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
};
struct PEB
{
UCHAR
InheritedAddressSpace;
// 00h
UCHAR
ReadImageFileExecOptions;
// 01h
UCHAR BeingDebugged; // 02h
UCHAR Spare; // 03h
PVOID
Mutant;
// 04h
PVOID ImageBaseAddress; // 08h
struct PEB_LDR_DATA *Ldr; // 0Ch
void * ProcessParameters; // 10h
PVOID
SubSystemData;
// 14h
PVOID
ProcessHeap;
// 18h
PVOID
FastPebLock;
// 1Ch
void *
FastPebLockRoutine; // 20h
void *
FastPebUnlockRoutine; // 24h
ULONG
EnvironmentUpdateCount;
// 28h
PVOID*
KernelCallbackTable;
// 2Ch
PVOID
EventLogSection;
// 30h
PVOID EventLog; // 34h
void *
FreeList; // 38h
ULONG
TlsExpansionCounter;
// 3Ch
PVOID
TlsBitmap;
// 40h
ULONG
TlsBitmapBits[0x2];
// 44h
PVOID
ReadOnlySharedMemoryBase;
// 4Ch
PVOID
ReadOnlySharedMemoryHeap;
// 50h
PVOID*
ReadOnlyStaticServerData;
// 54h
PVOID
AnsiCodePageData; // 58h
PVOID
OemCodePageData;
// 5Ch
PVOID
UnicodeCaseTableData;
// 60h
ULONG NumberOfProcessors; // 64h
ULONG
NtGlobalFlag;
// 68h
UCHAR
Spare2[0x4];
// 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG
HeapSegmentReserve;
// 78h
ULONG
HeapSegmentCommit;
// 7Ch
ULONG HeapDeCommitTotalFreeThreshold; // 80h
ULONG
HeapDeCommitFreeBlockThreshold;
// 84h
ULONG
NumberOfHeaps;
// 88h
ULONG
MaximumNumberOfHeaps;
// 8Ch
PVOID**
ProcessHeaps; // 90h
PVOID
GdiSharedHandleTable;
// 94h
PVOID
ProcessStarterHelper;
// 98h
PVOID
GdiDCAttributeList;
// 9Ch
PVOID
LoaderLock; // A0h
ULONG
OSMajorVersion;
// A4h
ULONG
OSMinorVersion;
// A8h
ULONG
OSBuildNumber;
// ACh
ULONG
OSPlatformId;
// B0h
ULONG
ImageSubSystem;
// B4h
ULONG
ImageSubSystemMajorVersion;
// B8h
ULONG
ImageSubSystemMinorVersion;
// C0h
ULONG
GdiHandleBuffer[0x22];
// C4h
PVOID ProcessWindowStation; // ???
};
struct PEB *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
__asm int 3
PEB *peb = RegisterValue();
printf("BeingDebugged=%x\n",peb->BeingDebugged);
printf("ImageBaseAddress=%x\n",peb->ImageBaseAddress);
printf("NumberOfProcessors=%x\n",peb->NumberOfProcessors);
}
BeingDebugged=1
ImageBaseAddress=400000
NumberOfProcessors=1
This structure PEB has some
useful members so we decided to display some of them. The first is the
BeingDebugged member. This has a value of 1 or 0. The int 3 instruction
transports us to the debugger. Once we are in the debugger we step over all the
functions to get at the above output.
The member BeingDebugged now
has a value 1 since the program is being debugged otherwise the default is a
zero. The ImageBase member is 400000 as exe files in memory are loaded at that
location. As we are running on a single processor machine, the Number of
Processors is 1.
You can take the benefit of
displaying the other members of the structure PEB at your leisure.
#include <windows.h>
#include <stdio.h>
/*
struct LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
};
*/
struct PEB_LDR_DATA
{
ULONG
Length;
BOOLEAN
Initialized;
PVOID
SsHandle;
LIST_ENTRY
InLoadOrderModuleList;
LIST_ENTRY
InMemoryOrderModuleList;
LIST_ENTRY
InInitializationOrderModuleList;
};
struct PEB
{
UCHAR
InheritedAddressSpace;
// 00h
UCHAR
ReadImageFileExecOptions;
// 01h
UCHAR
BeingDebugged;
// 02h
UCHAR
Spare;
// 03h
PVOID
Mutant;
// 04h
PVOID
ImageBaseAddress;
// 08h
struct PEB_LDR_DATA *Ldr; // 0Ch
void *
ProcessParameters;
// 10h
PVOID
SubSystemData;
// 14h
PVOID
ProcessHeap;
// 18h
PVOID
FastPebLock; // 1Ch
void *
FastPebLockRoutine; // 20h
void *
FastPebUnlockRoutine; // 24h
ULONG
EnvironmentUpdateCount;
// 28h
PVOID*
KernelCallbackTable;
// 2Ch
PVOID
EventLogSection;
// 30h
PVOID
EventLog;
// 34h
void *
FreeList; // 38h
ULONG
TlsExpansionCounter;
// 3Ch
PVOID
TlsBitmap; // 40h
ULONG
TlsBitmapBits[0x2];
// 44h
PVOID
ReadOnlySharedMemoryBase;
// 4Ch
PVOID
ReadOnlySharedMemoryHeap;
// 50h
PVOID*
ReadOnlyStaticServerData; // 54h
PVOID
AnsiCodePageData;
// 58h
PVOID
OemCodePageData;
// 5Ch
PVOID
UnicodeCaseTableData;
// 60h
ULONG
NumberOfProcessors;
// 64h
ULONG
NtGlobalFlag;
// 68h
UCHAR
Spare2[0x4];
// 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG
HeapSegmentReserve;
// 78h
ULONG HeapSegmentCommit; // 7Ch
ULONG
HeapDeCommitTotalFreeThreshold;
// 80h
ULONG
HeapDeCommitFreeBlockThreshold;
// 84h
ULONG
NumberOfHeaps;
// 88h
ULONG
MaximumNumberOfHeaps;
// 8Ch
PVOID**
ProcessHeaps;
// 90h
PVOID
GdiSharedHandleTable;
// 94h
PVOID
ProcessStarterHelper;
// 98h
PVOID
GdiDCAttributeList; // 9Ch
PVOID
LoaderLock;
// A0h
ULONG
OSMajorVersion;
// A4h
ULONG
OSMinorVersion;
// A8h
ULONG
OSBuildNumber;
// ACh
ULONG
OSPlatformId;
// B0h
ULONG
ImageSubSystem;
// B4h
ULONG
ImageSubSystemMajorVersion;
// B8h
ULONG
ImageSubSystemMinorVersion;
// C0h
ULONG
GdiHandleBuffer[0x22];
// C4h
PVOID
ProcessWindowStation;
// ???
};
struct PEB *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
PEB *peb = RegisterValue();
PEB_LDR_DATA *ldr =
peb->Ldr;
LIST_ENTRY flist;
printf("PEB=%x
Ldr=%x\n",peb,ldr);
flist =
ldr->InInitializationOrderModuleList;
printf("Flink=%p
Blink=%p Start=%p\n",flist.Flink,flist.Blink,&flist);
}
Output
PEB=7ffdf000 Ldr=131e90
Flink=00131F38
Blink=00132470 Start=0012FF74
Our goal of finding out the address
of kernel32.dll using undocumented structures is now reachable. The peb
variable points to a structure whose member ldr points to another structure
PEB_LDR_DATA as explained before. This structure has an internal structure
InInitializationOrderModuleList that is of tag LIST_ENTRY, it is not a pointer.
There are two more
structures however, our interest lies only in the third such structure. This
structure is defined in winnt.h and in turn starts with two pointers to similar
structures LIST_ENTRY. These members are called Flink and Blink.
Whenever a list is to be
stored in memory, a linked-list is used where one member of the structure
points to a similar such structure in memory. Ours is a forward one-direction
linked-list, however it can be modified to be bi-directional also. In such a
case, the list can be traversed up and down, thus requiring two members, one
keeping track of a similar structure in memory in the forward direction and the
second member in the reverse direction. Two members, Flink and Blink can be
introduced where Flink is for forward and Blink for backwards.
The Flink member points to
memory location 00131F38 where one such structure begins. The Blink member has
an address value of 00132470 where the last structure begins. Also, the first
structure InInitializationOrderModuleList begins at memory location 0012FF74.
#include <windows.h>
#include <stdio.h>
/*
struct LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
};
*/
struct PEB_LDR_DATA
{
ULONG Length;
BOOLEAN
Initialized;
PVOID
SsHandle;
LIST_ENTRY
InLoadOrderModuleList;
LIST_ENTRY
InMemoryOrderModuleList;
LIST_ENTRY
InInitializationOrderModuleList;
};
struct PEB
{
UCHAR
InheritedAddressSpace;
// 00h
UCHAR
ReadImageFileExecOptions;
// 01h
UCHAR
BeingDebugged;
// 02h
UCHAR
Spare;
// 03h
PVOID
Mutant;
// 04h
PVOID
ImageBaseAddress;
// 08h
struct PEB_LDR_DATA *Ldr; // 0Ch
void *
ProcessParameters;
// 10h
PVOID
SubSystemData;
// 14h
PVOID
ProcessHeap; // 18h
PVOID
FastPebLock;
// 1Ch
void *
FastPebLockRoutine; // 20h
void *
FastPebUnlockRoutine; // 24h
ULONG
EnvironmentUpdateCount;
// 28h
PVOID*
KernelCallbackTable;
// 2Ch
PVOID
EventLogSection;
// 30h
PVOID
EventLog;
// 34h
void *
FreeList; // 38h
ULONG
TlsExpansionCounter;
// 3Ch
PVOID
TlsBitmap;
// 40h
ULONG
TlsBitmapBits[0x2];
// 44h
PVOID
ReadOnlySharedMemoryBase;
// 4Ch
PVOID
ReadOnlySharedMemoryHeap; // 50h
PVOID*
ReadOnlyStaticServerData;
// 54h
PVOID
AnsiCodePageData;
// 58h
PVOID
OemCodePageData;
// 5Ch
PVOID
UnicodeCaseTableData;
// 60h
ULONG NumberOfProcessors; // 64h
ULONG
NtGlobalFlag;
// 68h
UCHAR
Spare2[0x4];
// 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG
HeapSegmentReserve;
// 78h
ULONG
HeapSegmentCommit;
// 7Ch
ULONG
HeapDeCommitTotalFreeThreshold;
// 80h
ULONG
HeapDeCommitFreeBlockThreshold;
// 84h
ULONG
NumberOfHeaps; // 88h
ULONG
MaximumNumberOfHeaps;
// 8Ch
PVOID**
ProcessHeaps;
// 90h
PVOID
GdiSharedHandleTable;
// 94h
PVOID
ProcessStarterHelper; // 98h
PVOID
GdiDCAttributeList;
// 9Ch
PVOID
LoaderLock;
// A0h
ULONG
OSMajorVersion;
// A4h
ULONG
OSMinorVersion;
// A8h
ULONG
OSBuildNumber;
// ACh
ULONG
OSPlatformId;
// B0h
ULONG
ImageSubSystem;
// B4h
ULONG
ImageSubSystemMajorVersion;
// B8h
ULONG
ImageSubSystemMinorVersion;
// C0h
ULONG
GdiHandleBuffer[0x22];
// C4h
PVOID
ProcessWindowStation;
// ???
};
struct PEB *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
PEB *peb = RegisterValue();
PEB_LDR_DATA *ldr =
peb->Ldr;
LIST_ENTRY
flist,slist,tlist;
printf("PEB=%x
Ldr=%x\n",peb,ldr);
flist =
ldr->InInitializationOrderModuleList;
printf("First Flink=%p
Blink=%p Start=%p\n",flist.Flink,flist.Blink,&ldr->InInitializationOrderModuleList);
slist = *flist.Flink;
printf("Second Flink=%p
Blink=%p Start=%p\n",slist.Flink,slist.Blink,flist.Flink);
tlist = *flist.Blink;
printf("Third Flink=%p
Blink=%p Start=%p\n",tlist.Flink,tlist.Blink,flist.Blink);
}
PEB=7ffdf000 Ldr=131e90
First Flink=00131F38
Blink=00132470 Start=00131EAC
Second Flink=00132470
Blink=00131EAC Start=00131F38
Third Flink=00131EAC
Blink=00131F38 Start=00132470
Lets us now understand the
internals of this structure LIST_ENTRY. The first structure starts at 00131F38
denoted by the Flink member and the first LIST_ENTRY structure starts at
00131EAC. A structure slist is created that is pointed to by the Flink
variable.
The value displayed by the
Flink member is 00132470 thus telling the location of third structure. However
the Blink variable has a value 00131EAC as it points backwards to the previous
structure. The tlist structure starts at 00131F38.
Here the Flink member points
to 00131EAC, which is the first structure called
InInitializationOrderModuleList. This completes a full circle. The Blink
variable of tlist points to the previous structure at 00131f38. Thus using the
Flink members, we come a full circle in the forward direction. The Blink
members also take us through a full circle but in the reverse direction.
#include <windows.h>
#include <stdio.h>
/*
struct LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
};
*/
struct PEB_LDR_DATA
{
ULONG
Length;
BOOLEAN
Initialized;
PVOID
SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY
InMemoryOrderModuleList;
LIST_ENTRY
InInitializationOrderModuleList;
};
struct PEB
{
UCHAR
InheritedAddressSpace;
// 00h
UCHAR
ReadImageFileExecOptions;
// 01h
UCHAR
BeingDebugged;
// 02h
UCHAR
Spare;
// 03h
PVOID
Mutant;
// 04h
PVOID
ImageBaseAddress;
// 08h
struct PEB_LDR_DATA *Ldr; // 0Ch
void *
ProcessParameters;
// 10h
PVOID
SubSystemData;
// 14h
PVOID
ProcessHeap;
// 18h
PVOID
FastPebLock; // 1Ch
void *
FastPebLockRoutine; // 20h
void *
FastPebUnlockRoutine; // 24h
ULONG
EnvironmentUpdateCount;
// 28h
PVOID*
KernelCallbackTable;
// 2Ch
PVOID EventLogSection; // 30h
PVOID
EventLog;
// 34h
void *
FreeList; // 38h
ULONG
TlsExpansionCounter;
// 3Ch
PVOID
TlsBitmap; // 40h
ULONG
TlsBitmapBits[0x2];
// 44h
PVOID
ReadOnlySharedMemoryBase;
// 4Ch
PVOID
ReadOnlySharedMemoryHeap;
// 50h
PVOID*
ReadOnlyStaticServerData; // 54h
PVOID
AnsiCodePageData;
// 58h
PVOID
OemCodePageData;
// 5Ch
PVOID
UnicodeCaseTableData;
// 60h
ULONG
NumberOfProcessors;
// 64h
ULONG
NtGlobalFlag;
// 68h
UCHAR
Spare2[0x4];
// 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG
HeapSegmentReserve;
// 78h
ULONG
HeapSegmentCommit;
// 7Ch
ULONG
HeapDeCommitTotalFreeThreshold;
// 80h
ULONG
HeapDeCommitFreeBlockThreshold;
// 84h
ULONG
NumberOfHeaps;
// 88h
ULONG
MaximumNumberOfHeaps; // 8Ch
PVOID**
ProcessHeaps;
// 90h
PVOID
GdiSharedHandleTable;
// 94h
PVOID
ProcessStarterHelper;
// 98h
PVOID
GdiDCAttributeList;
// 9Ch
PVOID
LoaderLock;
// A0h
ULONG
OSMajorVersion;
// A4h
ULONG
OSMinorVersion;
// A8h
ULONG
OSBuildNumber;
// ACh
ULONG
OSPlatformId;
// B0h
ULONG
ImageSubSystem;
// B4h
ULONG
ImageSubSystemMajorVersion;
// B8h
ULONG
ImageSubSystemMinorVersion;
// C0h
ULONG
GdiHandleBuffer[0x22];
// C4h
PVOID
ProcessWindowStation;
// ???
};
struct PEB *RegisterValue()
{
__asm
{
mov eax ,fs:30h
}
}
main()
{
PEB *peb = RegisterValue();
PEB_LDR_DATA *ldr =
peb->Ldr;
LIST_ENTRY
flist,slist,tlist;
HMODULE ntdll,kernel32;
printf("PEB=%x
Ldr=%x\n",peb,ldr);
flist =
ldr->InInitializationOrderModuleList;
printf("First Flink=%p
Blink=%p
Start=%p\n",flist.Flink,flist.Blink,&ldr->InInitializationOrderModuleList);
slist = *flist.Flink;
printf("Second Flink=%p
Blink=%p Start=%p\n",slist.Flink,slist.Blink,flist.Flink);
tlist = *flist.Blink;
printf("Third Flink=%p
Blink=%p Start=%p\n",tlist.Flink,tlist.Blink,flist.Blink);
ntdll =
LoadLibrary("ntdll.dll");
kernel32 =
LoadLibrary("kernel32.dll");
printf("Address of
ntdll=%p kernel32=%p\n",ntdll,kernel32);
int **dummy;
dummy = (int **)flist.Flink;
dummy++; dummy++;
printf("Adress of
ntdll.dll=%x\n",*dummy);
dummy = (int **)flist.Blink;
dummy++; dummy++;
printf("Adress of
kernel32.dll=%x\n",*dummy);
}
PEB=7ffdf000 Ldr=131e90
First Flink=00131F38
Blink=00132470 Start=00131EAC
Second Flink=00132470
Blink=00131EAC Start=00131F38
Third Flink=00131EAC
Blink=00131F38 Start=00132470
Address of ntdll=77F80000
kernel32=77E80000
Adress of ntdll.dll=77f80000
Adress of
kernel32.dll=77e80000
This program finally helps
us meet our goal, i.e it prints out the address of kernel32 in memory. We start
as always by displaying the PEB pointer and the address of the three LIST_ENTRY
structures. Then using the function LoadLibrary, the address of kernel32.dll in
memory is obtained which on our version of Windows 2000 Service pack 2 is
77e80000. The address of ntdll.dll is also displayed which happens to be
77f80000. Both these dll’s are loaded before the user gains control of windows.
This is how important they are to the internals of windows.
Thereafter a pointer
variable dummy is created that points to the first LIST_ENTRY structure in
memory which is at 00131F38. This variable is incremented by 8 thus it moves
across the first two members Flink and Blink. The value stored here is the
address of ntdll.dll in memory. The second LIST_ENTRY structure gives the
address of the kernel32.dll in memory.
To reach out to the second LIST_ENTRY
structure, either the Flink member can be moved ahead to another or the dummy
variable could point to the value of Blink which is the address of the previous
structure. Remember there are only three LIST_ENTRY structures, one of them is
part of the PEB_LDR_DATA structure.
The first member after the
Flink and Blink members is the address of the dll loaded in memory. The above
example declares that Windows only loads two dll’s in memory at the above
addresses. Remember all the above is undocumented thus the credit goes to
someone somewhere in the world who figured it out without Microsoft’s help.
#include <windows.h>
#include <stdio.h>
void *RegisterValue()
{
__asm
{
push esi
mov eax, fs:[0x30]
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov eax, [eax + 0x8]
pop esi
}
}
main()
{
void *addr =
RegisterValue();
printf("Address of
kernel32=%p\n",addr);
}
Address of kernel32=77E80000
The above program performs
the same job as the previous one. It returns the address of kernel32.dll in
memory but uses pure assembler. Remember while using pure assembler to give us
the address of kernel32 in memory, we will be
using shellcode.
In assembler, prior to using
any of the register, its value is first saved on the stack. Thus every assembler
function starts off by saving the registers it is going to use on the stack.
Since we are using only the esi register, its value is first pushed on the
stack.
The return value is always
stored in the eax register. Thus its value is bound to change if the function
returns a value, so we push eax on the stack. Tthe pointer at 30h from the fs
register or the PEB is placed in the eax register. Once done, we then need the
pointer to the LDR data stored 0ch bytes within the PEB.
Once again the pointer
called Ldr is placed in the eax register. Then the address of the third
LIST_ENTRY structure InInitializationOrderModuleList in the esi register is
required. The lodsd instruction takes 4 bytes pointed to by the esi register
and places it in the eax register. At this point in time the esi register
contains the value 131F38. This is where the second structure starts.
The lodsd instruction
increases the value of esi by 4 and the eax register now contains the value
132470. Thus the eax register takes the value of the Flink member or the
address of the second LIST_ENTRY structure. The first structure has the details
of ntdll.dll.
An addition of 8 will skip
the Blink and Flink members and reach at the address of kernel32 in memory.
Thus, we add 8 to the address of the start of the structure. The pointer stored
in this address is placed in the eax register.
The assembler program is a
little more difficult to understand as it deals with offsets but is smaller and
more concise. The square brackets in assembler are the equivalent of a * or
pointers in C.
#include <windows.h>
#include <stdio.h>
void *tib;
void *RegisterValue()
{
__asm
{
mov eax, fs:[0x18]
mov ebx, fs:[0]
mov [tib] , ebx
}
}
main()
{
void *tib1 =
RegisterValue();
printf("%x
%x\n",tib,tib1);
}
12ffb0 7ffde000
As mentioned earlier, there
is a structure that the fs register points to called TEB. This structure
starts with another structure TIB that
starts with a pointer to some exception handling structures. The last member of
the TIB structure points to another similar structure. The address of this
structure is displayed.
This had been seen in one of
the previous programs where the member had a unique value for each thread. The
next program uses this thread info to regain another method in order to reach
to the address of kernel32 in memory. Once again not documented.
#include <windows.h>
#include <stdio.h>
struct NTTIB
{
struct
_EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union
{
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct NTTIB *Self;
};
NTTIB *RegisterValue()
{
__asm
{
mov eax, fs:[0x18] // Extract TEB
}
}
main()
{
NTTIB *ptib;
char *pkernel32;
char *pStackBase;
long dummy;
ptib = RegisterValue();
pStackBase = (char
*)ptib->StackBase;
printf("TIB=%x
StackBase=%x\n",ptib,pStackBase);
pStackBase = pStackBase -
0x1c;
pkernel32 = *(char
**)pStackBase;
printf("StackBase=%x
Initial Address of kernel32=%x\n",pStackBase,pkernel32);
while (*pkernel32 != 'M' &&
*(pkernel32+1) != 'Z')
{
pkernel32--;
printf("Before
pkernel32=%x ",pkernel32);
dummy = (long)pkernel32;
dummy = dummy &
0xFFFF0000;
pkernel32 = (char *)dummy;
printf("After
pkernel32=%x \n",pkernel32);
}
printf("Address of
kernel32=%x\n",pkernel32);
}
TIB=7ffde000
StackBase=130000
StackBase=12ffe4 Initial
Address of kernel32=77e8615b
Before pkernel32=77e8615a
After pkernel32=77e80000
Address of kernel32=77e80000
The program starts by extracting
the pointer to the TEB stored at 18h as before. This is a pointer to a
structure NTTIB which is shown above. Thus the ptib variable contains the value
7ffde000. This pointer is then used to get at the member StackBase that gives
the base of where our stack starts. We get the StackBase as 130000.
Now undocumented research
has told us that there is a pointer 1c from the stack of the StackBase. We
therefore subtract 1ch from the StackBase variable to get a value of 12ffe4. At
this location is stored some address that points into kernel32. In our case
this initial address is 77e8615b. This information however is no help as what
we are interested in is the start of kernel32.
Now every PE file under
windows will start with the bytes M and Z and Windows loads a PE file at a
multiple of 64K. Armed with this knowledge we can now quickly get the starting
address of kernel32.
We use a while loop where we
keep looping until the first two bytes that the variable pkernel32 point to are
not M and Z. We subtract 1 from this variable and then use bitwise anding to
zero out the first or lower 16 bits. This is because kernel32 or any dll will
be found at an address divisible by 64K. Thus there is no need to check all
memory for characters M and Z, except for checking chunks of 64K. As pkernel32
is a pointer we store its value into a long and then bit wise and it. In this
case the loop is not required.
#include <stdio.h>
int abc()
{
__asm
{
push esi // save esi as its value is
being overwritten
mov esi, fs:[0x18] // TEB is stored here
lodsd // First pointer is not
used. ESI is incremented by 4
lodsd // Stack base is second
member of structure
mov eax, [eax - 0x1c] // A pointer is stored 1C
from stack base
loop_back:
dec eax // Move down by one page
xor ax, ax // Zero out the first 16 bits
cmp word ptr [eax], 0x5a4d // Chaeck if bytes at page boundary are MZ
jne loop_back
pop esi
}
}
main()
{
int addr = abc();
printf("Address of
kernel32=%x\n",addr);
}
Address of kernel32=77e80000
The C program in given in
assembler. Remember it is an alternative method in acquiring the address of
kernel32.dll in memory. The next two programs showcase the third way of finding
the address of kernel32.dll in memory. Once again in C and then in assembler.
All the registers to be used
are saved on the stack with the exception of eax. The address of the pointer
called Self is stored in the esi register. The structure TIB that it points to
starts with a pointer to a exception handling structure, explained in the
coming programs . Thus the first lodsd will load this structures address in the
eax register. After incrementing esi by 4, it will point to the second member
StackBase.
The second lodsd will load
this value into the eax register. We need a pointer 1Ch from this stack base
which we again store in the eax register. This pointer as mentioned earlier is
an address within kernel32.dll. We now decrement this location within kernel32
and also zero out the first 16 bits by xoring ax and not eax with itself. A
faster method of zeroing out a register is by xoring it with itself.
A check is performed whether
the word at the memory denoted by eax is M and Z. If it is not equal to MZ, then
the jne or jump if not equal will loop back to the label loop_back. A label in
assembler is a word with a colon at the end. There are a trillion such opcodes like jne etc. Then again, this loop
is unnecessary like the while of the C code.
#include <stdio.h>
#include <windows.h>
struct NTTIB
{
struct
_EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct NTTIB *Self;
};
struct NTTIB
*RegisterValue()
{
__asm
{
mov eax, fs:[0]
}
}
main()
{
NTTIB *ptib;
char *pkernel32;
int next;
int **pfn;
struct
_EXCEPTION_REGISTRATION_RECORD *list;
long dummy;
ptib = RegisterValue();
list =
ptib->ExceptionList;
printf("tib=%x
list=%x\n",ptib,list);
next = *(int *)list;
pfn = (int **)list;
printf("Next handler=%x
pfn=%p\n",next,pfn);
pfn++;
pkernel32 = (char *)*pfn;
printf("Initial Address
in kernel32=%x\n",pkernel32);
while (*pkernel32 != 'M'
&& *(pkernel32+1) != 'Z')
{
pkernel32--;
dummy = (long)pkernel32;
dummy = dummy &
0xFFFF0000;
pkernel32 = (char *)dummy;
}
printf("Address of
kernel32=%x\n",pkernel32);
}
tib=12ffb0 list=12ffe0
Next handler=ffffffff
pfn=0012FFE4
Initial Address in
kernel32=77e8615b
Address of kernel32=77e80000
In the third and final
method of finding the address of kernel32 in memory we use the exception
handling structures. We start with the address of the main TIB structure at
12ffb0. We then snag the first member ExceptionList which is pointer to a
documented structure _EXCEPTION_REGISTRATION_RECORD. Its internals are yet
undisclosed but we know that this
structure begins at 12ffe0.
The first member of this
structure is a pointer to a similar structure whose value is stored in the
variable next. These structures are in a linked list and the last such
structure or handler has a value of 0xffffffff. In our case this structure is
the last. Generally we would need to loop the linked list and quit out on the
last one.
The next variable displays
ffffffff. The list variable now points to the last exception handling
structure. We store its value in an int ** variable pfn as we are interested in
a value 4 bytes away or the second member. At this location is stored a pointer
into kernel32 in memory. We then use the same method that we used earlier to
get at the starting address of kernel32.dll.
#include <stdio.h>
int RegisterValue()
{
__asm
{
push ecx
xor ecx, ecx // Zero out ecx
mov esi, fs:[0]
// First Entry
not ecx // ecx set to 0xffffffff last handler
lodsd // First member is a
pointer
mov esi, eax // Move into esi
cmp [eax], ecx // Our next-handler is 0xffffffff? so no loop
mov eax, [eax + 0x04] // A pointer is stored 4 bytes way
loop_again:
dec eax
xor ax, ax
cmp word ptr [eax], 0x5a4d
jne loop_again
pop ecx
}
}
main()
{
int addr = RegisterValue();
printf("Address of
kernel32=%x\n",addr);
}
Address of kernel32=77e80000
The same C program is now in assembler. Since the ecx register
is used, its value is first saved on the stack. We zero out not by storing 0 in
it but by xoring it to itself. We then store the pointer in esi. As the value
of ecx is zero, using the not instruction will change all the 0 to one’s and
thus making the registers value ffffffff.
This approach is faster than setting the value directly. The lodsd instruction is used to load the pointer to the next
exception structure in eax. This value is then moved into esi. Then this first
member is checked to be all F’s or the last exception structure. If it is not
true then we jump back to the lodsd. There is no need for a loop. The pointer
stored 4 bytes away points inside kernel32.dll and using the same loop like
before, we reach the starting address of kernel32.dll in memory.