IMAGE_FILE_HEADER.TimeDateStamp is described as:
The low 32 bits of the time stamp of the image. This represents the date and time the image was created by the linker. The value is represented in the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Universal Coordinated Time, according to the system clock.
So it's a Unix time/epoch. For the vast majority of DLLs (or EXEs) on an old Win7x64 system I get plausible values, like:
- 2020-01-30 02:23:55 =
C:\Windows\system32\USER32.dll
(bytes at file offset$F8, 6 bytes after thePEsignature, are$3B $3E $32 $5E) - 2011-12-16 08:37:19 =
C:\Windows\system32\msvcrt.dll - 2019-10-26 18:30:19 =
C:\Program Files\Open-Shell\ClassicExplorer64.dll - 2020-03-12 06:38:45 =
C:\Program Files\Java\jre1.8.0_251\bin\java.dll - 2025-07-05 11:00:00 =
C:\Program Files\7-Zip\7-zip.dll - 2003-06-25 04:22:10 =
C:\Program Files (x86)\Winamp\winamp.exe(version 2.95 from back then)
But for Mozilla products like Firefox 115.20.0esr (x64) and Thunderbird 68.8.0 (x64) there are DLLs whose name start with api-ms-win- which produce weird dates/times when interpreted the same way:
- 2075-10-13 12:54:12 =
C:\Program Files\Firefox\api-ms-win-crt-environment-l1-1-0.dll
(bytes at file offset$0D, 6 bytes after thePEsignature, are$74 $D7 $F8 $C6) - 2105-11-25 12:44:53 =
C:\Program Files\Firefox\api-ms-win-crt-time-l1-1-0.dll - 1988-11-01 22:53:54 =
C:\Program Files\Firefox\api-ms-win-crt-utility-l1-1-0.dll - 2055-06-07 02:00:36 =
C:\Program Files\Firefox\api-ms-win-core-processthreads-l1-1-1.dll - 2062-12-31 21:17:22 =
C:\Program Files\Thunderbird\api-ms-win-crt-environment-l1-1-0.dll - 1974-04-21 14:27:11 =
C:\Program Files\Thunderbird\api-ms-win-core-processthreads-l1-1-1.dll
Is it just gibberish in those DLLs? How to interpret them correctly? Were these DLLs compiled in C# and it's some kind of compiler bug? Process Explorer 16.32x64 displays a much better date for such DLLs:
- 2025-01-27 22:01 =
C:\Program Files\Firefox\api-ms-win-crt-environment-l1-1-0.dll
This should be reproducible on other Windows systems with other DLLs, too. The following is needed in case it's not defined already - shouldn't be of much interest:
type
IMAGE_OPTIONAL_HEADER32 = record
Magic: Word;
MajorLinkerVersion, MinorLinkerVersion: Byte;
SizeOfCode, SizeOfInitializedData, SizeOfUninitializedData,
AddressOfEntryPoint, BaseOfCode, BaseOfData: DWORD;
// NT additional fields.
ImageBase, SectionAlignment, FileAlignment: DWORD;
MajorOperatingSystemVersion, MinorOperatingSystemVersion,
MajorImageVersion, MinorImageVersion,
MajorSubsystemVersion, MinorSubsystemVersion: Word;
Win32VersionValue, SizeOfImage, SizeOfHeaders, CheckSum: DWORD;
Subsystem, DllCharacteristics: Word;
SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve,
SizeOfHeapCommit, LoaderFlags, NumberOfRvaAndSizes: DWORD;
DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
end;
PIMAGE_NT_HEADERS32 = ^IMAGE_NT_HEADERS;
IMAGE_NT_HEADERS = record
Signature: DWORD;
FileHeader: IMAGE_FILE_HEADER;
OptionalHeader: IMAGE_OPTIONAL_HEADER32;
end;
TImgSecHdrMisc = record
case Integer of
0: (PhysicalAddress: DWORD);
1: (VirtualSize: DWORD);
end;
PIMAGE_SECTION_HEADER = ^IMAGE_SECTION_HEADER;
_IMAGE_SECTION_HEADER = record
Name: array [0..IMAGE_SIZEOF_SHORT_NAME - 1] of BYTE;
Misc: TImgSecHdrMisc;
VirtualAddress, SizeOfRawData, PointerToRawData,
PointerToRelocations, PointerToLinenumbers: DWORD;
NumberOfRelocations, NumberOfLinenumbers: WORD;
Characteristics: DWORD;
end;
TLoadedImage= record
ModuleName: PAnsiChar;
hFile: THandle;
MappedAddress: PUCHAR;
FileHeader: PIMAGE_NT_HEADERS32;
LastRvaSection: PIMAGE_SECTION_HEADER;
NumberOfSections: ULONG;
Sections: PIMAGE_SECTION_HEADER;
Characteristics: ULONG;
fSystemImage, fDOSImage: ByteBool;
Links: LIST_ENTRY;
SizeOfImage: ULONG;
end;
function MapAndLoad(ImageName, DllPath: PAnsiChar; var LoadedImage: TLoadedImage;
DotDll: BOOL; ReadOnly: BOOL): BOOL; stdcall; external 'imagehlp.dll';
function UnMapAndLoad(const LoadedImage: TLoadedImage): BOOL; stdcall; external 'imagehlp.dll';
function StrPasW( const WStr: PWideChar ): WideString;
begin
Result:= WStr;
end;
function SystemTimeToString( t: SystemTime ): WideString;
const
BUF= 200;
var
w1: PWideChar;
begin
GetMem( w1, BUF* 2 );
result:= '';
if GetDateFormatW( LOCALE_USER_DEFAULT, 0, @t, nil, w1, BUF )<> 0 then result:= result+ StrPasW( w1 );
if GetTimeFormatW( LOCALE_USER_DEFAULT, 0, @t, nil, w1, BUF )<> 0 then result:= result+ ' '+ StrPasW( w1 );
FreeMem( w1 );
end;
function FileTimeToString( t: FileTime ): WideString;
var
s: SystemTime;
begin
FileTimeToSystemTime( t, s );
result:= SystemTimeToString( s );
end;
The interesting part is here, where I read 2 different DLLs - one has a date in the future, one a plausible date:
function IntToFileTime( i: Int64 ): TFileTime;
begin
result.dwLowDateTime := Int64Rec(i).Lo;
result.dwHighDateTime := Int64Rec(i).Hi;
end;
const
DELTA_UNIXTIME_FILETIME: Int64= 116444736000000000;
function UnixTimeToFileTime( iUnix: Int64 ): TFileTime;
begin
result:= IntToFileTime( iUnix* Int64(10000000)+ DELTA_UNIXTIME_FILETIME ); // 1 s to 100 ns
end;
procedure TfrmMain.Button1Click(Sender: TObject);
var
vLI: TLoadedImage;
begin
if MapAndLoad( PAnsiChar('C:\Program Files\Firefox\api-ms-win-core-localization-l1-2-0.dll'), nil, vLI, FALSE, TRUE ) then begin
Caption:= Caption+ ' '+ FileTimeToString( UnixTimeToFileTime( vLI.FileHeader^.FileHeader.TimeDateStamp ) ); // '2040-06-21 10:44:07' = weird
UnmapAndLoad( vLI );
end;
if MapAndLoad( PAnsiChar('C:\Program Files\Firefox\nss3.dll'), nil, vLI, FALSE, TRUE ) then begin
Caption:= Caption+ ' '+ FileTimeToString( UnixTimeToFileTime( vLI.FileHeader^.FileHeader.TimeDateStamp ) ); // '2025-01-27 14:21:04' = correct
UnmapAndLoad( vLI );
end;
end;