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.

 

Back to the main page