1

Here's a piece of code for obtaining the time when a .NET assembly was built. Note:

const int c_LinkerTimestampOffset = 8;

and later:

int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);

this code extracts the TimeDateStamp member of IMAGE_FILE_HEADER structure that is stored inside the assembly. The structure is defined as follows:

typedef struct _IMAGE_FILE_HEADER {
    WORD  Machine;
    WORD  NumberOfSections;
    DWORD TimeDateStamp;
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD  SizeOfOptionalHeader;
    WORD  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

and WORD is two bytes and should be two-bytes aligned. When I compile the following code with Visual C++ 10:

IMAGE_FILE_HEADER header;
char* start = (char*)&header;
char* field = (char*)(&header.TimeDateStamp);
int diff = field - start;

diff equals 4 as I personally expected.

Is that a bug in the C# code? Why is offset value of 8 used?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979

3 Answers3

3

Its used to skip the additional signature, as i contains the offset to the NT headers, not the file image header (see the formal PE structure):

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature; //<- we need to skip this
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

so the actual sum is sizeof(WORD /* FileHeader.Machine */) + sizeof(WORD /* FileHeader.NumberOfSections */) + sizeof(DWORD /* Signature */)

bottom line, not a bug, just some magic to skip a bit of structure nesting/inlining.

Necrolis
  • 25,836
  • 3
  • 63
  • 101
0

DWORD is a int unsigned value, 4 bytes.

The C# code is not correct, it reads 8 bytes. I think it should be 4.

int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);

I had written a PE reader few days ago. I used this for the "Image File Header":

public class IMAGE_FILE_HEADER 
{
     public UInt16 Machine;
     public UInt16 NumberOfSections;
     public UInt32 TimeDateStamp;
     public UInt32 PointerToSymbolTable;
     public UInt32 NumberOfSymbols;
     public UInt16 SizeOfOptionalHeader;
     public UInt16 Characteristics;
}
CodeTherapist
  • 2,776
  • 14
  • 24
-1

You are most likely getting a 8 bytes diff because you are on a 64 bit system/target platform, if you would be on a 32 bit it would have been what you expected it to be.

On 32 bit systems DWORD is defined as a uint (32bits/4bytes) and on 64 bit systems its a ulong (64bits/8bytes).

A WORD essentially stands for the address size of the architecture. When it was coined a WORD was 16 bits and a DWORD was a double word, 32 bits (4 bytes).

One would expect DWORD to be 64bits/8bytes on x86 systems but MS wanted to keep legacy support so it kept the old definition: http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx

maka
  • 566
  • 4
  • 11