Monday, September 24, 2012

Base Relocation Demystified

///////////////////////////////////////////////////////////////////////////////

Base Relocation Demystified

by cawan - cawan[at]ieee.org (2005)

///////////////////////////////////////////////////////////////////////////////

Info:

The paper will discuss the mechanism of base relocation in detail. The reason
that I write this paper is to explain the method in finding the KiServiceTable
by checking requests to KeServiceDescriptorTable through relocation
information. The idea was firstly introduced by 90210 and the assembly
implementation was presented by Piotr Bania. Besides, Tan Chew Keong has
adopted this technique in his SDTrestore (Version 0.2) in finding the
KiServiceTable. I would like to express my special thanks to 90210,
Piotr Bania, and Tan Chew Keong for their excellent codes in order to inspire
my desire to look into the details of base relocation.

///////////////////////////////////////////////////////////////////////////////

Table of Contents:

1. EXE vs DLL
2. IMAGE_DATA_DIRECTORY and Data Blocks of Base Relocation Table
3. IMAGE_FIXUP_ENTRY of Base Relocation Table
4. Dumping SDT From ntoskrnl.exe

///////////////////////////////////////////////////////////////////////////////

1. EXE vs DLL

I am not attempt to explain what is PE format since there are quite a number of
good literatures are available by utilizing Google [1,2]. The attribute of base
relocation is initialized from the Characteristics field (16 bits) in
IMAGE_FILE_HEADER to determine,

1. Either the relocation information is stripped or not (Bit 0)
2. Either the file is dll or not (bit 13)

For the first case, if the bit is set, there is no relocation information in
the file. Hence, base relocation is not available in this type of file. For the
second case, if the bit is set, the PE loader will treat the file as DLL.
Otherwise, it is treated as EXE. Now, the question is what\'s the difference
between the file is treated as DLL or EXE? Despite all the others discussion
which is not related to the base relocation, the answer is the immediate
address call in EXE is relative to the module handle, instead of relative
to the ImageBase in DLL. A sample format for immediate address call instruction
is shown below:-

call dword ptr [xxxxxxxx]

Let us have an examples with ntoskrnl.exe to show the appropriate definition.
By going through the paper of \"Exploiting Windows Device Drivers\" by Piotr
Bania, we intend to get the address of KeUserModeCallback_Routine where is
called by an instruction in KeUserModeCallback. The KeUserModeCallback is
exported in the PE\'s export table. So, it can be located by using
GetProcAddress. In order to start our experiments about base relocation, the
ntoskrnl.exe is duplicated from c:\\winnt\\system32\\ into c:\\ for two identical
copies and rename as ntoskrnl1.exe and ntoskrnl2.exe, respectively.
Then, both the files are loaded into memory by using LoadLibraryExA in
sequence to observe the action of base relocation. Both the module handles
must not be same since they are loaded in different portion of memory space.
According to Piotr Bania, ntoskrnl.exe should has an immediate address call
instruction in KeUserModeCallback. Thus, each of the loaded module should
comprises the instruction where the calling address is our interest in this
section. Let us look into the following codes.

#include
#include

int main(void)
{
HANDLE libhandlea,prochandlea,calladdra;
HANDLE libhandleb,prochandleb,calladdrb;

libhandlea=LoadLibraryExA(\"c:\\\\ntoskrnl1.exe\",0,1);
if(libhandlea==0)
return 0;

prochandlea=GetProcAddress((HINSTANCE)libhandlea,\"KeUserModeCallback\");
if(prochandlea==0)
return 0;

__asm
{
push edi
push eax
mov edi,prochandlea
scan_calla:
inc edi
cmp word ptr [edi],015ffh
jne scan_calla

mov eax,[edi+2]
mov calladdra,eax
pop eax
pop edi
}

printf(\"\\nlibhandle(ntoskrnl1.exe): %.8x\",libhandlea);
printf(\"\\nprochandle(KeUserModeCallback): %.8x\", prochandlea);
printf(\"\\ncalladdrA: %.8x\", calladdra);
printf(\"\\n\");

libhandleb=LoadLibraryExA(\"c:\\\\ntoskrnl2.exe\",0,1);
if(libhandleb==0)
return 0;

prochandleb=GetProcAddress((HINSTANCE)libhandleb,\"KeUserModeCallback\");
if(prochandleb==0)
return 0;

__asm
{
push edi
push eax
mov edi,prochandleb
scan_callb:
inc edi
cmp word ptr [edi],015ffh
jne scan_callb

mov eax,[edi+2]
mov calladdrb,eax
pop eax
pop edi
}

printf(\"\\nlibhandle(ntoskrnl2.exe): %.8x\",libhandleb);
printf(\"\\nprochandle(KeUserModeCallback): %.8x\", prochandleb);
printf(\"\\ncalladdrB: %.8x\", calladdrb);
printf(\"\\n\");

FreeLibrary((HINSTANCE)libhandlea);
FreeLibrary((HINSTANCE)libhandleb);

return 0;
}

The codes will search from KeUserModeCallback for appropriate instruction and
locate the calling addrees. Before running the codes, it is important to
identify the Characteristics field in IMAGE_FILE_HEADER first. By default,
both of the files are having 0x030E where the base relocation is NOT stripped
and it will be treated as EXE. The output is shown below.

libhandle(ntoskrnl1.exe): 00440000
prochandle(KeUserModeCallback): 00470e62
calladdrA: 00485500

libhandle(ntoskrnl2.exe): 005f0000
prochandle(KeUserModeCallback): 00620e62
calladdrB: 00485500

From the output, we know that the first image (ntoskrnl1.exe) is loaded at
0x00440000 and the second image is loaded at 0x005f0000. The reason why the
first image is not loaded at the ImageBase of PE (00400000) is because it
has been taken by the main process. Besides, each of the KeUserModeCallback
handle is relative to the respective module handle. However, both of the
calling addresses (calladdrA and calladdrB) are unchanged although their
module and KeUserModeCallback handles are located at different location.
The reason is an EXE will not expected to be loaded within a process. Instead,
it is normally initializing a process and locates itself at ImageBase. While
the EXEs are loaded within a process, both the calladdrA and calladdrB are
in fact relative to ImageBase. So, their RVA should be

0x00485500 - 0x00400000 = 0x00085500
---------- ---------- ----------
calladdr ImageBase RVA

To start our first experiment, let us modify the Characteristics field of
each file in order to \"cheat\" the loader to treat the EXE as DLL and observe
their calling addresses again. So, we change the their Characteristics field
from 0x030E to 0x210E and run the code again. The output is shown below.

libhandle(ntoskrnl1.exe): 00440000
prochandle(KeUserModeCallback): 00470e62
calladdrA: 004c5500

libhandle(ntoskrnl2.exe): 005f0000
prochandle(KeUserModeCallback): 00620e62
calladdrB: 00675500

From the output, we know that the module and KiUserModeCallback handles of each
file are not affected either they are loaded as EXE or DLL, as expected.
However, their respective calling address has been changed! The calladdrA and
calladdrB are changed into 0x004C5500 and 0x00675500, respectively. Let us
calculate their RVAs of calling address.

0x004c5500 - 0x00440000 = 0x00085500 //RVA of calladdrA
0x00675500 - 0x005f0000 = 0x00085500 //RVA of calladdrB
---------- ---------- ----------
calladdr ModuleHandle RVA

Obviously, while the files are treated as DLL, the calling address is relative
to the module handle, but not the ImageBase. Base relocation table will keep
a record to track each of such calling address in order to perform appropriate
modification while an executable (EXE or DLL) is getting loaded. We will
discuss the detail of base relocation table in the following sections. As an
additional annotation, the filename extention will never affect the loader in
order to treat the file as EXE or DLL. In other words, if we rename the
ntoskrnl.exe into ntoskrnl.dll, the loader will still identify the file as
ntoskrnl.exe, but not ntoskrnl.dll, as expected. Let us look into base
relocation table now.

///////////////////////////////////////////////////////////////////////////////

2. IMAGE_DATA_DIRECTORY and Data Blocks of Base Relocation Table

Base relocation table is pointed by BaseRelocate in IMAGE_DATA_DIRECTORY, which
is at 0xA0 from IMAGE_FILE_HEADER. The BaseRelocate is expressed in RVA, so it
has to add with module handle in order to point at base relocation table. The
following codes show how the base relocation table can be located.

#include
#include

int main(void)
{
HANDLE syshandle;
UINT imagebase,basereloc_rva,basereloc_va;
syshandle=LoadLibraryExA(\"c:\\\\ntoskrnl1.exe\",0,1);
if (syshandle==0)
return 0;

__asm
{
push eax
push ebx

mov eax,syshandle
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0x34]
mov imagebase,ebx
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0xa0]
mov basereloc_rva,ebx

pop ebx
pop eax
}

basereloc_va=(UINT)syshandle+basereloc_rva;

printf(\"\\nsyshandle: %.8x\",syshandle);
printf(\"\\nimagebase: %.8x\",imagebase);
printf(\"\\nbasereloc_rva: %.8x\",basereloc_rva);
printf(\"\\nbasereloc_va: %.8x\",basereloc_va);
printf(\"\\n\");

FreeLibrary((HMODULE)syshandle);

return 0;

}

The output is shown below.

syshandle: 00440000
imagebase: 00400000
basereloc_rva: 00193e80
basereloc_va: 005d3e80

From the output, we know that the BaseRelocate is 0x00193e80 in RVA. Since the
module handle is 0x00440000, then the address of base relocation table should be

0x00440000 + 0x00193e80 = 0x005d3e80
---------- ---------- ----------
ModuleHandle BaseRelocate BaseRelocationTable

Now, let us look into the memory space where the base relocation table is
resided.

005D3E80 00 00 00 00 64 01 00 00 ....d...
005D3E88 E4 36 E8 36 F0 36 F4 36 ?¤6?¨6?°6?′6
005D3E90 FC 36 00 37 08 37 0C 37 ??6.7.7.7
005D3E98 14 37 18 37 20 37 24 37 .7.7 7$7
005D3EA0 2C 37 30 37 40 37 50 37 ,707@7P7
005D3EA8 5C 37 60 37 6C 37 70 37 \\7`7l7p7

The first 8 bytes of the table are known as IMAGE_BASE_RELOCATION block and is
defined as follow.

typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION;

The first 4 bytes is defined as VirtualAddress or also known as allocated
memory block in RVA. The following 4 bytes is SizeOfBlock where indicate the
size of the allocated memory block in unit of byte. The size of
IMAGE_BASE_RELOCATION block is included in the SizeOfBlock. After the
8 bytes of IMAGE_BASE_RELOCATION block, this is the start of the entry of
base relocation table. Records of locations getting modified by loader are
located here. Each record takes 2 bytes and it is defined as IMAGE_FIXUP_ENTRY.
The definition of IMAGE_FIXUP_ENTRY is shown below.

typedef struct {
WORD offset:12;
WORD type:4;
} IMAGE_FIXUP_ENTRY;

By referring to the memory space of base relocation table above, the first
IMAGE_FIXUP_ENTRY is started from 0x005D3E88 until the end of the allocated
memory block. In general, the base relocation table comprises a number of
allocated memory block. In order to locate the starting address of the second
memory block in base relocation table, the address of the first
IMAGE_FIXUP_ENTRY plus the size of the memory block should be the exact
location. However, the SizeOfBlock given in IMAGE_BASE_RELOCATION (0x00000164)
has included the size of the IMAGE_BASE_RELOCATION block. Hence, the
SizeOfBlock should be deducted by the size of IMAGE_BASE_RELOCATION block
before adding to the address of the first IMAGE_FIXUP_ENTRY in order to obtain
the starting address of the second memory block. The starting address of the
second memory block should be

(Address of first IMAGE_FIXUP_ENTRY)+(SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))
=0x005D3E88+(0x00000164-8)
=0x005D3FE4

Let us have a look to the memory space of 0x005D3FE4.

005D3FCC 6C 3F 70 3F 78 3F 7C 3F l?p?x?|?
005D3FD4 84 3F 88 3F 90 3F 94 3F a€?????.?a€??
005D3FDC 9C 3F A0 3F AC 3F B0 3F ?“????????°?
005D3FE4 00 10 00 00 70 02 00 00 ....p...
005D3FEC 4C 30 50 30 58 30 5C 30 L0P0X0\\0
005D3FF4 64 30 68 30 74 30 78 30 d0h0t0x0

From the given memory view, we know that the first memory block is ended at
0x005D3FE3 where the last IMAGE_FIXUP_ENTRY is 0x3FB0. The second memory block
is started at 0x005D3FE4, exactly. The VirtualAddress and SizeOfBlock of the
second memory block are 0x00001000 and 0x00000270, respectively. Similarly, the
address of the first IMAGE_FIXUP_ENTRY in the second memory block is at
0x005D3FEC. With the same calculation, the starting address of the third memory
block of base relocation table is

0x005D3FEC+(0x00000270-8)=0x005D4254

Again, the respective memory space is shown below.

005D4234 D0 3D D8 3D DC 3D E4 3D ??=??=??=?¤=
005D423C E8 3D F4 3D F8 3D 04 3E ?¨=?′=??=.>
005D4244 08 3E 14 3E 18 3E 24 3E .>.>.>$>
005D424C 28 3E 34 3E 38 3E 00 00 (>4>8>..
005D4254 00 20 00 00 1C 00 00 00 . ......
005D425C 74 31 78 31 80 31 84 31 t1x1a??1a€?1

Again, with the same calculation, the starting address of the fourth memory
block is

0x005D425C+(0x0000001C-8)= 0x005D4270

The memory view of 0x005D4270 is shown below.

005D4258 1C 00 00 00 74 31 78 31 ....t1x1
005D4260 80 31 84 31 90 31 9C 31 a??1a€?1.1?“1
005D4268 A0 31 A8 31 AC 31 00 00 ??1?¨1??1..
005D4270 00 50 00 00 F8 02 00 00 .P..??...
005D4278 CC 34 D0 34 DC 34 E0 34 ??4??4??4??4
005D4280 EC 34 F0 34 F8 34 FC 34 ??4?°4??4??4

As a summary of observation to the four allocated memory block of base
relocation table, there is nothing special in IMAGE_BASE_RELOCATION block.
However, it is interesting to identify that starting from the second memory
block of base relocation table, each of the memory block is ended with
0x0000 (2 bytes), excluding the first memory block. In next section, the
IMAGE_FIXUP_ENTRY will be discussed in detail.

///////////////////////////////////////////////////////////////////////////////

3. IMAGE_FIXUP_ENTRY of Base Relocation Table

As mentioned in the previous section, IMAGE_FIXUP_ENTRY is defined as follow.

typedef struct {
WORD offset:12;
WORD type:4;
} IMAGE_FIXUP_ENTRY;

Each of the IMAGE_FIXUP_ENTRY takes 16 bits (2 bytes) where the first 12 bits
represent the offset to the location which is getting modified by the loader.
On the other hand, the remaining 4 bits represent the base relocation type.
For ntoskrnl.exe, the base relocation type should be 3(IMAGE_REL_BASED_HIGHLOW)
which means the location getting modified is located at an address where the
offset is summated with ImageBase and allocated memory block. Since we use
LoadLibraryExA to load the ntoskrnl.exe from the main program, it is impossible
for the ntoskrnl.exe to be loaded at its Imagebase (0x00400000) because it is
already taken by the main program. So, the offset should be summated with
module handle, instead of ImageBase, associated with allocated memory block
in order to identify the exact location getting modified. As reminder, the
allocated memory block is represented by the VirtualAddress in
IMAGE_BASE_RELOCATION block. In order to have better visualization about
IMAGE_FIXUP_ENTRY, let us have an example. We run the codes in the previous
section and look into the memory at 0x005D3E80 (starting address of the base
relocation table) again.

005D3E80 00 00 00 00 64 01 00 00 ....d...
005D3E88 E4 36 E8 36 F0 36 F4 36 ?¤6?¨6?°6?′6
005D3E90 FC 36 00 37 08 37 0C 37 ??6.7.7.7
005D3E98 14 37 18 37 20 37 24 37 .7.7 7$7
005D3EA0 2C 37 30 37 40 37 50 37 ,707@7P7
005D3EA8 5C 37 60 37 6C 37 70 37 \\7`7l7p7
005D3EB0 80 37 90 37 98 37 9C 37 a??7.7??7?“7
005D3EB8 A8 37 B8 37 C8 37 D8 37 ?¨7??7??7??7

The first IMAGE_FIXUP_ENTRY located at 0x005D3E88 is 0x36E4. The following
IMAGE_FIXUP_ENTRY are 0x36E8, 0x36F0, 0x36F4, and so on. By referring to the
definition of IMAGE_FIXUP_ENTRY, those the 0x6E4, 0x6E8, 0x6F0, 0x6F4 are
offsets of the summation of module handle and allocated memory block, which
represent the exact locations getting modified. Hence, those the locations
can be calculated as below.

0x6E4+0x00440000+0x00000000=0x004406E4
0x6E8+0x00440000+0x00000000=0x004406E8
0x6F0+0x00440000+0x00000000=0x004406F0
0x6F4+0x00440000+0x00000000=0x004406F4
----- ---------- ---------- ----------
A B C D

where,

A = offset of IMAGE_FIXUP_ENTRY
B = Module Handle
C = VirtualAddress of IMAGE_BASE_RELOCATION
D = Location to be Modified by Loader

In order to have more understanding about the base relocation table, let us
have an interesting example with ntoskrnl.exe again. In section 1, we already
know that there is an instruction of immediate address call in
KeUserModeCallback. So, we will first identify the calling address and the
caller location of the instruction by getting into KeUserModeCallback with
GetProcAddress and search for the instruction of immediate address call.
The call instruction that is being used is 0xff15. The following codes show
the implementation process.

#include
#include

int main(void)
{

HANDLE syshandle,prochandle;
UINT calladdr,loc_calladdr;

syshandle=LoadLibraryExA(\"c:\\\\ntoskrnl1.exe\",0,1);
if (syshandle==0)
return 0;

prochandle=GetProcAddress((HINSTANCE)syshandle,\"KeUserModeCallback\");
if (prochandle==0)
return 0;

__asm
{
push esi
push eax

mov esi,prochandle
scan_call:
inc esi
cmp word ptr [esi],015ffh
jne scan_call
mov eax,esi
add eax,2
mov loc_calladdr,eax
mov eax,[esi+2]
mov calladdr,eax

pop eax
pop esi

}

printf(\"\\nsyshandle: %.8x\",syshandle);
printf(\"\\nprochandle: %.8x\",prochandle);
printf(\"\\ncalladdr: %.8x\",calladdr);
printf(\"\\nloc_calladdr: %.8x\",loc_calladdr);
printf(\"\\n\");

FreeLibrary((HMODULE)syshandle);

return 0;
}

The output is shown below.

syshandle: 00440000
prochandle: 00470e62
calladdr: 00485500
loc_calladdr: 00470f22

From the output, we know that at address 0x00470f22, there is an address that
will be called by instruction 0xff15, and the address is 0x00485500. Of course,
one of the records in base relocation table must point to 0x00470f22, and this
address must contain 0x00485500. So, let us try to find this record in base
relocation table. The following codes show the implementation process.

#include
#include


struct fixupblock
{
unsigned long pagerva;
unsigned long blocksize;
};

int main(void)
{
HANDLE syshandle,prochandle;
UINT calladdr;
UINT imagebase,basereloc_rva,basereloc_va;

syshandle=LoadLibraryExA(\"c:\\\\ntoskrnl1.exe\",0,1);
if (syshandle==0)
return 0;

prochandle=GetProcAddress((HMODULE)syshandle,\"KeUserModeCallback\");
if (prochandle==0)
return 0;

__asm
{
push eax
push ebx

mov eax,syshandle
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0x34]
mov imagebase,ebx
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0xa0]
mov basereloc_rva,ebx

mov eax,prochandle
scan_call:
inc eax
cmp word ptr [eax],015ffh
jne scan_call
mov ebx,[eax+2]
mov calladdr,ebx

pop ebx
pop eax
}

basereloc_va=(UINT)syshandle+basereloc_rva;

fixupblock *fixblk=(fixupblock *)basereloc_va;
while(fixblk->blocksize)
{
int numentry=(fixblk->blocksize-sizeof(fixupblock))>>1;
unsigned short *offsetptr=(unsigned short *)(fixblk+1);
for(int i=0;i {
int reloctype=(*offsetptr&0xf000)>>12;
if(reloctype==3)
{
UINT *codeloc=(UINT *)((UINT)syshandle+fixblk->pagerva+(*offsetptr&0x0fff));
if(*codeloc==calladdr && *(WORD *)((UINT)codeloc-2)==0x15ff)
{
printf(\"\\ngot it !!\");

printf(\"\\ncalladdr from baserelocate: %.8x\",*(UINT *)codeloc);
printf(\"\\nloc_calladdr from baserelocate: %.8x\",(UINT)codeloc);

printf(\"\\n\");
FreeLibrary((HMODULE)syshandle);
return 0;
}
}
offsetptr++;
}
fixblk=(fixupblock *)offsetptr;
}

printf(\"\\nError...\");
printf(\"\\n\");

FreeLibrary((HMODULE)syshandle);

return 0;
}

The output is shown below.

got it !!
calladdr from baserelocate: 00485500
loc_calladdr from baserelocate: 00470f22

Well, both of the calling address and caller location are identical regardless
the methods being used to locate at the instruction in KeUserModeCallback. So,
I wish the interpretation so far is sufficient to guide all the readers to have
a clear picture to the mechanism of base relocation. In next section, we will
have a complete example regarding how to dump KiServiceTable or SDT from
ntoskrnl.exe by locating the entry through the base relocation table

///////////////////////////////////////////////////////////////////////////////

4. Dumping SDT From ntoskrnl.exe

The following code is implemented to dump KiServiceTable (SDT) from
ntodkrnl.exe file.

#include
#include

typedef struct
{
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flag;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
}SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;

typedef (WINAPI *ntquery) (UINT,PVOID,ULONG,PULONG);

ntquery NtQuerySystemInformation;

struct fixupblock
{
unsigned long pagerva;
unsigned long blocksize;
};

int main(void)
{
HANDLE syshandle,prochandle;
UINT imagebase,basereloc_rva,basereloc_va;

syshandle=LoadLibraryExA(\"c:\\\\ntoskrnl1.exe\",0,1);
if (syshandle==0)
return 0;

prochandle=GetProcAddress((HMODULE)syshandle,\"KeServiceDescriptorTable\");
if (prochandle==0)
return 0;

UINT offset=(UINT)prochandle-(UINT)syshandle;

PSYSTEM_MODULE_INFORMATION pSysModuleInfo;
LONG status;
ULONG retsize;
PVOID pBuffer;
pBuffer=LocalAlloc(LPTR,4);
if(pBuffer==0)
{
printf(\"\\nLocalAlloc error...\");
return 0;
}

NtQuerySystemInformation=(ntquery)GetProcAddress(GetModuleHandle(\"ntdll\"),
\"NtQuerySystemInformation\");

if(!NtQuerySystemInformation)
return 0;

status=NtQuerySystemInformation(11,pBuffer,4,&retsize);
if(status==0xC0000004)
{
LocalFree(pBuffer);
pBuffer=LocalAlloc(LPTR,retsize);
if(pBuffer==0)
{
printf(\"\\nLocalAlloc error...\");
return 0;
}

status=NtQuerySystemInformation(11,pBuffer,retsize,&retsize);
if(status!=NO_ERROR)
{
printf(\"\\nNtQuerySystemInformation error...\");
return 0;
}
}

pSysModuleInfo=(PSYSTEM_MODULE_INFORMATION)((ULONG)pBuffer+4);

UINT kernelbase=(UINT)pSysModuleInfo->Base;

__asm
{
push eax
push ebx

mov eax,syshandle
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0x34]
mov imagebase,ebx
mov ebx,[eax+0x3c]
mov ebx,[eax+ebx+0xa0]
mov basereloc_rva,ebx

pop ebx
pop eax
}

basereloc_va=(UINT)syshandle+basereloc_rva;

fixupblock *fixblk=(fixupblock *)basereloc_va;
while(fixblk->blocksize)
{
int numentry=(fixblk->blocksize-sizeof(fixupblock))>>1;
unsigned short *offsetptr=(unsigned short *)(fixblk+1);
for(int i=0;i {
int reloctype=(*offsetptr&0xf000)>>12;
if(reloctype==3)
{
UINT *codeloc=(UINT *)((UINT)syshandle+fixblk->pagerva+(*offsetptr&0x0fff));
if(*codeloc==offset+imagebase && *(WORD *)((UINT)codeloc-2)==0x05c7)
{
printf(\"\\ngot it !!\");
printf(\"\\ncode: %.4x\",*(WORD *)((UINT)codeloc-2));
printf(\"\\ndesaddr: %.8x\",*(UINT *)codeloc);
printf(\"\\nsourceaddr: %.8x\",*(UINT *)((UINT)codeloc+4));
UINT sdtoffset=*(UINT *)((UINT)codeloc+4)-imagebase;
UINT sdtaddr=sdtoffset+(UINT)syshandle;
UINT sdtentry;

printf(\"\\nDumping SDT...\\n\");

for (int j=0;j<16 br="br" j="j">{
sdtentry=*(UINT *)((UINT *)sdtaddr+j);
printf(\"\\n%x\",sdtentry-imagebase+kernelbase);
}

printf(\"\\n\");
FreeLibrary((HMODULE)syshandle);
return 0;
}
}
offsetptr++;
}
fixblk=(fixupblock *)offsetptr;
}

printf(\"\\nError...\");
printf(\"\\n\");
printf(\"\\n\");

FreeLibrary((HMODULE)syshandle);

return 0;
}

The output is shown below.

got it !!
code: 05c7
desaddr: 00484e00
sourceaddr: 00476730
Dumping SDT...

804ca37a
804fecea
80501b5e
804fed14
80501b94
8045eba6
80501bd4
80501c14
80499fd8
804fabe0
804fa7c0
804e9e5c
804e9e12
8049a480
8044d670
80499c6e

The output shows 16 entries of SDT getting dumped out from ntoskrnl.exe,
exactly. To verify the entries are accurate, let us check the SDT with windbg.

kd> dd keservicedescriptortable
80484e00 80476730 00000000 000000f8 80476b14
80484e10 00000000 00000000 00000000 00000000
80484e20 00000000 00000000 00000000 00000000
80484e30 00000000 00000000 00000000 00000000
80484e40 00000000 00000000 00000000 00000000
80484e50 00000000 00000000 00000000 00000000
80484e60 ffdff120 85e9b120 00000000 00000000
80484e70 00000000 00000000 00000000 00000000

kd> dd 80476730
80476730 804ca37a 804fecea 80501b5e 804fed14
80476740 80501b94 8045eba6 80501bd4 80501c14
80476750 80499fd8 804fabe0 804fa7c0 804e9e5c
80476760 804e9e12 8049a480 8044d670 80499c6e
80476770 804ce49a 804d4ef4 804ea220 804337f8
80476780 804a55b4 80417f90 804c92d6 80494084
80476790 804517f6 805020f0 804cabda beb223bd
804767a0 8046b834 804de1dc 804940c8 8049b9ce

Since the entries shown in windbg are identical with the dumped output from
ntoskrnl.exe, so the result is confirmed.

///////////////////////////////////////////////////////////////////////////////

References

[1] LUEVELSMEYER, \"The PE File Format\"

[2] Microsoft, \"winnt.h\"

[3] Piotr Bania, \"Exploiting Windows Device Drivers\"

[4] 90210, \"A More Stable Way To Locate Real KiService Table\"

[5] Piotr Bania, \"Finding KiServiceTable In More Stable Way\"

[6] Tan Chew Keong, \"Defeating Kernel Native API Hookers By Direct Service
Dispatch Table Restoration\"

[7] Matt Pietrek, \"An In-Depth Look Into The Win32 Portable Executable File
Format\"

[8] Matt Pietrek, \"An In-Depth Look Into The Win32 Portable Executable File
Format, Part 2\"

///////////////////////////////////////////////////////////////////////////////

Friday, February 26, 2010

How To Get The Installed Software List Of All The Computers In Your Network

How To Get The Installed Software List Of All The Computers In Your Network

by cawan – cawan[at]ieee.org

It is important to monitor the installed software list of all the computers in a network to detect unauthorized the installation of pirated software. However, there is no simple tool to do this job. So, we will discuss a simple method to implement this feature by using batch files. The batch files will be put in the DC and run there because DC has authorized access to all the computers in the network. Here, we assume all the computers are Windows box that logged in to the DC. Hence, the DC can perform file copy from/to all the clients, as well as starting a scheduled task in all the clients.

All right, the first question is about how to get the list of installed software in a computer. Somebody advocates parsing dedicated inf file in certain folder from %windir%. However, this is not a good solution because the folder name is varied to the service pack and update versions of the windows. The ideal solution would be to export the registry entries from [hklm\software\microsoft\windows\currentversion\uninstall]. So, by executing the command like “reg export hklm\software\microsoft\windows\currentversion\uninstall list.reg”, we can get a file named “list.reg” which contains all the installed software names inside. Well, now we know how to get the list of installed software already, then the second question is about how to let all the computers in the network to run this command and generate individual “list.reg” respectively. I propose to use task scheduler service of windows for the purpose. So, from the DC, we can use the command like “at \\CLIENT_IP TIME /i COMMAND” to do our job. Now, let start to write our simple batch files. First, we create a “run.bat” with the content as “reg export hklm\software\microsoft\windows\currentversion\uninstall list.reg”. After that, we create a “put.bat” with the following contents,

//////////////////////////put.bat/////////////////////////
copy run.bat \\%1\c$\windows\system32
at \\%1 %2 /i run.bat
/////////////////////////////////////////////////////////

Then, to run the “put.bat”, we create a “startput.bat” with the following contents,

//////////////////////////startput.bat////////////////////
for /f %%a in ('type IP.txt') do call put.bat %%a %1
////////////////////////////////////////////////////////

So, the “startput.bat” will read the IP entries in “IP.txt” line-by-line and call “put.bat” to copy “run.bat” into those computers with its IP included in “IP.txt”, and finally creates a scheduled task in all of them respectively to run the “run.bat” at some time. For example, at 08:30, we execute “startput.bat 8:40” in DC to distribute “run.bat” to all the computers and each of them will run the “run.bat” after 10 minutes later (at 8:40) and generate a “list.reg” which contains the list of installed software. There is a little issue regarding how to generate the “IP.txt” with all the valid IPs as windows box. For this case, we can use fscan to grab the IPs with port 139 or 445 is opened.

Now, we have all the computers with individual “list.reg”, the question now is about how to get all those distributed “list.reg” and put into the DC with a filename which can represent the clients such as “192.168.1.100.txt”, “192.168.1.101.txt” and so on. Let us create two batch files, with the names of “get.bat” and “startget.bat” to do the job. Their contents are shown below,

//////////////////////////get.bat/////////////////////////
copy \\%1\c$\windows\system32\list.reg .\%1.txt
/////////////////////////////////////////////////////////

//////////////////////////startget.bat////////////////////
for /f %%a in ('type IP.txt') do call get.bat %%a
////////////////////////////////////////////////////////

So, by executing “startget.bat” at the DC, we can get a list of files named with individual IP addresses in “IP.txt” which contains the installed software list respectively in our local folder. The contents of the installed software list are majority repeated because those entries in [hklm\software\microsoft\windows\currentversion\uninstall] contain subkeys. Thus, we need to use tools such as grep tool for windows which is mentioned in last post to extract and generate a unique list of installed software for individual computer. For any further queries, please feel free to contact me.

Wednesday, February 17, 2010

How To Provide Time Scheduled Actions For m0n0wall

How To Provide Time Scheduled Actions For m0n0wall

by cawan - cawan[at]ieee.org

m0n0wall is a version of free embedded firewall which is based on FreeBSD. It has very outstanding performance in providing packet filtering based on the rules with conditions of protocol (TCP, UDP, ICMP…), source / destination IP address, source / destination port range, and etc. However, it is lacking the capability to enable or disable dedicated rule by time scheduler. Thus, it is not so flexible when being adopted in office environment. For example, if we want to block a few of the URLs (e.g. facebook.com, youtube.com…) from 9:00 am to 1:00 pm, and 2:00 pm to 8:00 pm, then it is a little bit trouble to change appropriate rules on the m0n0wall at those specific times manually. Then, the issue of providing an automatic solution arose and this is the reason to have this article.

Let us assume that we have a rule in m0n0wall to decide those URLs getting blocked or not by enable or disable the rule. So, now, we need to know exactly the packets of communication from the browser to the m0n0wall. After that, we can replay the packets to toggle the rule accordingly. I use ethereal to capture the packets for analysis purposes. First of all, to get access to the admin page, we need to login. Then, we get to Firewall->Rules->LAN to enable or disable the rule. Finally, we need to click “Apply Changes” button to enforce the setting on m0n0wall. While doing analysis on the packets being captured, I found that each packet uses “Authorization: Basic” once authenticated to get full access to the m0n0wall. There is a hash value generated which is appending to the end of “Authorization: Basic” to represent the validity of the authentication. For my case, it is “Authorization: Basic YWRtaW46c3VuZGVuYWl0”. On the other hand, there is no dedicated packet to enable or disable the rule. Instead, it is only a single packet to toggle the rule, if the rule is current enabled, then after toggle, it become disabled, and vice versa. For my case, the so called toggle packet is shown as below,

GET /firewall_rules.php?if=lan&act=toggle&id=15 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Referer: http://192.168.1.254/firewall_rules.php?if=lan
Accept-Language: zh-cn
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Host: 192.168.1.254
Connection: Keep-Alive
Authorization: Basic YWRtaW46c3VuZGVuYWl0

Again, please note that the last line of “Authorization: Basic YWRtaW46c3VuZGVuYWl0” is appended to fool the m0n0wall to assume it is an authenticated packet. In other words, by sending this packet to m0n0wall, the m0n0wall will toggle the rule immediately. However, the story is yet to finish. We need to emulate the click event to the “Apply Changes” button to enforce the rule. Otherwise, the rule will just seem to be toggled but actually not. The “Apply Change” event is represented by the following packet,

POST /firewall_rules.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Referer: http://192.168.1.254/firewall_rules.php?if=lan
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Host: 192.168.1.254
Content-Length: 26
Connection: Keep-Alive
Cache-Control: no-cache
Authorization: Basic YWRtaW46c3VuZGVuYWl0

apply=Apply+changes&if=lan


Well, the “Apply Change” event use POST instead of GET in toggle action. After the “Apply Change”, m0n0wall checks the current rules status by using packet as below,

GET /firewall_rules.php?if=lan HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Referer: http://192.168.1.254/firewall_rules.php
Accept-Language: zh-cn
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Host: 192.168.1.254
Connection: Keep-Alive
Authorization: Basic YWRtaW46c3VuZGVuYWl0


All right, now we already understand the mechanism of m0n0wall to perform rule checking, toggling, and applying the changes. So, we can create 2 script files to block and unblock the network, by enabling and disabling the dedicated rule in m0n0wall. I am using a Windows XP box to do this job, and I write 2 batch files for it, block.bat and unblock.bat, as shown below,

/////////////////block.bat/////////////////

nc -v -n 192.168.1.254 80 <> checkstring.txt
find "gray" checkstring.txt
if errorlevel == 1 goto notfound
goto found
:found
nc -v -n 192.168.1.254 80 <> nul
nc -v -n 192.168.1.254 80 <> nul
echo block completed!
goto end
:notfound
echo already blocked!
:end
nc -v -n 192.168.1.254 80 <> checkstring.txt
net send 192.168.1.158 System_Blocked

///////////////////////////////////////////////

/////////////////unblock.bat/////////////////

nc -v -n 192.168.1.254 80 <> checkstring.txt
find "gray" checkstring.txt
if errorlevel == 1 goto notfound
goto found
:notfound
nc -v -n 192.168.1.254 80 <> nul
nc -v -n 192.168.1.254 80 <> nul
echo unblock completed!
goto end
:found
echo already unblocked!
:end
nc -v -n 192.168.1.254 80 <> checkstring.txt
net send 192.168.1.158 System_Unblocked

///////////////////////////////////////////////

The “test.txt” in the batch files contains the packet to perform rule checking. For “toggle1.txt” and “toggle2.txt”, they are representing the packets of rule toggling and applying change, respectively. In order to filter the return result, I use grep tool for Windows to get the line with “block dns”, and use Windows default find.exe to search for the word of “gray”. Other than that, everything is very straight forward. Now, you just launch your windows tasks scheduler and make it calls block.bat and unblock.bat at specific times, then a time scheduled action for m0n0wall is considered done. If you have any further queries, please feel free to contact me.

Saturday, January 23, 2010

How To Downgrade The Firmware Version of Siemens S450 SIP Handset

How To Downgrade The Firmware Version of Siemens S450 SIP Handset

by cawan - cawan[at]ieee.org

Lets ask two questions first...

1. Why we need to do so?
Because the new firmware cannot work well! (dropout, connection lost on call, self-reboot...)

2. why need to write a long article to talk about how to downgrade the S450 firmware?
Because S450 do not allow user to downgrade the firmware version once it is being upgraded to the latest version. In other words, if our phone cannot work well once getting upgraded, it cannot be reverted.

Lets start the story from here.

First of all, we need to know how to download and save a local copy of S450 firmware from gigaset.com. Lets start from studying the web directory structure of gigaset.com.

By gathering and analizing the sniffed packets with ethereal to the communication between S450 and gigaset.com, the web directory structure of gigaset.com can be summarized as below.

Lets say http://gigaset.com/chagall/2/14/ is an image path of a specific version of firmware. Then it has a master directory file with the name of master.bin to show the baselines directory of the firmware with the name of baselines.com. By reading the baselines.bin, we can get know the exact filename of the firmware version to download. Lets have an example,

a) Get into http://gigaset.com/chagall/2/14/
b) Get the master directory file - master.bin with http://gigaset.com/chagall/2/14/master.bin
c) Read the master.bin, and get baselines.bin which is stated in master.bin
d) Get the baselines.bin with http://gigaset.com/chagall/2/14/baselines.bin
e) Read the baselines.bin and get the filename(s) of the firmware, for my example, it is chagall083_02.bin
f) Get the firmware file with http://gigaset.com/chagall/2/14/chagall083_02.bin

Now, lets have a look to the naming convention of the S450 firmware, as shown below,

chagall083_02.bin -> v02083
chagall072_01.bin -> v01072
chagall184_02.bin -> v02184
chagall191_02.bin -> v02191
chagall214_02.bin -> v02214 <- Latest Firmware at 23-Jan-2010 We can get a list of the firmware version for S450 from the following site, http://gigaset.com/shc/0,1935,hq_en_0_123868_rArNrNrNrN_variation%253A-5_pageType%253Adownloads_imagePos%253A0,00.html

So, now, we already know how to get the exact filename of the firmware version that we are looking for. For example, if we need to get the firmware version of v02214, then the correct filename should be chagall214_02.bin. So, we can try to download the firmware directly. For chagall214_02.bin (v02214), it should be http://gigaset.com/chagall/2/chagall214_02.bin, and for chagall191_02.bin (v02191), it should be http://gigaset.com/chagall/2/chagall191_02.bin instead.

May be someone will ask why chagall083_02.bin (v02083) is located at http://gigaset.com/chagall/2/14/chagall083_02.bin but not http://gigaset.com/chagall/2/chagall083_02.bin? This is because http://gigaset.com/chagall/2/ only stores a few of the most recent firmware version, and all the older version will be moved into a deeper path such as /14 in our example. So, once the firmware version is moved into the deeper path, then we need to recursively traverse into the gigaset web directory to get the desired firmware file with appropriate baselines.bin file.

Lets start to have a look to the methods to upgrade S450 firmware into the latest version.

1. From handset: Setting->Base->Software Upgrade

2. From Web: (using Siemens Gigaset site)

Goto Setting->Miscellaneous->Firmware Upgrade
In "Data Server" the default is "gigaset.com/chagall/"
In "User Defined Firmware" put "http://gigaset.com/chagall/2/chagall214_02.bin"
Then click "Upgrade Firmware"

3. From Web: (using own local web site)

Goto Setting->Miscellaneous->Firmware Upgrade
In "Data Server" the default is "gigaset.com/chagall/" (no need to change this, it is for self-healing purpose)
In "User Defined Firmware" put "http://192.168.1.1/chagall214_02.bin" (we put chagall214_02.bin into the root directory of http://192.168.1.1/) .
Then click "Upgrade Firmware"

We have mentioned about the term of self-healing above, then what is it? The Siemens S450 has the ability to perform so called self-healing once the firmware spoilt due to the incomplete firmware upgrade process. It will based on the default setting of the S450 to connect Siemens Gigaset site (as stated in the "Data Server" column) to download the latest firmware automatically. However, if the "User Defined Firmware" is specified with a valid URL (such as "http://192.168.1.1/chagall214_02.bin"), then it will override the "Data Server" in order to get the latest firmware instead. As additional info, if "User Defined Firmware" is left blank or not specified correctly, then S450 will use port 20 to transfer the latest firmware from gigaset site. So, for those S450 installed in NAT, it is necessary to set port forwarding of port 20 to the S450 IP address at the router and also allow port 20 at the firewall to enable self-healing from gigaset site.

Well, now, we reach to the most crucial part of this article - how to downgrade the firmware version. For my case, I use firmware version v02191 for some times with no error. However, once I upgrade the firmware version into v02214, my S450 totally cannot call into Asterisk server or Barix Annuncicom in peer-to-peer mode. Everytime I initiate a call, it keeps showing "Connection Lost" and stop then. When I enable to show Status, I keep getting "IP Status Code:701"...So, now I really need to have my v02191 back! Then I try to fill in "http://gigaset.com/chagall/2/chagall191_02.bin" in "User Defied Firmware" and click "Upgrade Firmware" button, it shows a message box with "Firmware Upgrade Not Possible: Latest firmware version is already installed." and the upgrade process stopped. Then, I download the chagall191_02.bin and put it in the root directory of my local web server and start the upgrade process again with "http://192.168.1.1/chagall191_02.bin", I got the same error. After that, I tried to rename the file into chagall999_02.bin, I got the same error too.

After having some packet analysis with ethereal, I get know my S450 will get a chunk of data from chagall191_02.bin first before showing the error message box and stop the firmware upgrade process. So, it is very obvious the S450 should has some kind of "version checking" mechanism to the chagall191_02.bin file. By performing hexadecimal file compare to the chagall191_02.bin and chagall214_02.bin, there are not many bytes are in different for the chunk that is transfer from my web server to the S450. In very quickly, I found that at offset 0x200, chagall191_02.bin is 0xBF (191 in decimal) and chagall214_02.bin is 0xD6 (214 in decimal). Now, I can just "fool" the S450 to assume the chagall191_02.bin (v02191) as v02255 by changing the 0xBF of chagall191_02.bin at offset 0x200 to 0xff. Then I try to "upgrade" the new "v02255" firmware again. This time, I get a message box with "Firmware update started. The device will be disconnected and this interface terminated. The device will then shut down and restart. Once completed, you may start the interface again.", and I can see the upgrade process is started from ethereal. Once the upgrade process is completed, I just turn off and turn on the power again to restart the S450. Then, I try to access the S450 with my browser but it has not response! Then it seems the S450 got some kind of checksum to ensure the data integrity of the firmware. After that, I try to ping the S450 and I can get response from it (I assume the bootstrap of the S450 is always exist to support self-healing). So now I replace the edited chagall191_02.bin with the original one at my web server. Then I restart my S450 again. From ethereal, I can see my S450 is downloading the original chagall191_02.bin from my web server, in other words, the so called "firmware downgrade" process is just started! Once completed, I restart my S450 again and get access to it from my web browser. Finally, I get get into it and when I click into the "Status", it shows the firmware version is 0219100000. So, my firmware downgrade process is completed.

So, this is the whole process of firmware downgrade of Siemens S450 Sip handset. Please take you own risk to practice all the steps as mentioned above. For any further queries, please contact me.