The idea of the code below is to extract build timestamp from PE-compatible EXE or DLL. The code works perfectly for .NET binaries (for >6 years by now, btw) and reports rubbish for .NET Core ones. For a freshly built .NET (not Core) assembly it (correctly) reports "Mon Oct 9 13:30:14 2017", for a freshly built .NET Core v.2 assembly it reports "Tue Mar 29 05:14:00 2061" (?).
It means that .NET Core assemblies do not follow PE file format _exactly_. In .NET Core case the timestamp field of IMAGE_FILE_HEADER does not contain "The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), that indicates when the file was created" (quote from pecoff.docx) as it should.
I received an answer: "this is really a change by the roslyn compilers. see: https://github.com/dotnet/roslyn/issues/5940".
Three questions in this regard:
(1) Why would roslyn change PE format (that is, if it did, see next question)?
(2) If roslyn did change PE format, why does the change show itself in .NET Core builds ONLY, but not in .NET builds (VS 2015 certainly uses roslyn for both .NET Core and .NET builds)? Are there two flavors of roslyn (a joke)?
(3) Ok, let it be, but where _is_ the build timestamp in a binary built in .NET Core 2.0 environment?
// per "Microsoft PE and COFF Specification" pecoff_v8.docx at (as of 06/18/11)// http://msdn.microsoft.com/en-us/windows/hardware/gg463125// or, as of Oct 9th, 2017, pecoff.docx available at// https://www.microsoft.com/en-us/download/confirmation.aspx?id=19509constlong c_offsetOfOffsetOfPE =0x3c;// per section 2 of pecoff_v8.docxconstInt32 c_PE00 =0x00004550;// = 50 45 00 00 = PE\0\0/// <summary>/// Extracts build time from a PE executable (EXE or DLL) per "Microsoft PE and COFF Specification" pecoff_v8.docx/// </summary>staticDateTimeGetExeOrDll_EST_TimeStampDT(){DateTime timeStamp =newDateTime();try{// This works OK for .NET but produces invalid timestamp for .NET Core assemblies(why ?)string path =MyAssembly.Location;using(FileStream fs =newFileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)){using(BinaryReader br =newBinaryReader(fs)){ fs.Position= c_offsetOfOffsetOfPE;byteOffsetOfPE= br.ReadByte();// = 0x80 fs.Position=OffsetOfPE;Int32 PE00 = br.ReadInt32();// = 0x00004550 [ = 50 45 00 00 = PE\0\0 ]if(PE00 != c_PE00){return timeStamp;}// invalid file format, return "best guess" (last write time) fs.Position=OffsetOfPE+8;UInt32 timeStampLower32Bits = br.ReadUInt32();// .ReadInt32(); // = 0x4dfcd934if(timeStampLower32Bits ==0|| timeStampLower32Bits ==0xFFFFFFFF){return timeStamp;}// invalid date/time stamp, return "best guess" (last write time) timeStamp =newDateTime(1970,1,1,0,0,0,DateTimeKind.Utc);// 00:00 January 1, 1970 (a C run-time time_t value) timeStamp = timeStamp.AddSeconds(timeStampLower32Bits);TimeZoneInfo easternStdTime =TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); timeStamp =TimeZoneInfo.ConvertTimeFromUtc(timeStamp, easternStdTime);}}}catch(Exception ex){string qqq = ex.Message;}return timeStamp;
}