diff options
author | Kirill Osenkov <github@osenkov.com> | 2017-05-05 01:23:47 +0300 |
---|---|---|
committer | Kirill Osenkov <github@osenkov.com> | 2017-05-05 01:23:47 +0300 |
commit | 47a9b873b6b99a5678bba4c2ad07bdb836493881 (patch) | |
tree | 14412e383f26d1892c1c8e99a7ff2755b5e62ccf /Mono.Addins/Mono.Addins.Database | |
parent | 925f835acdcec8e830a722367b563506370bc499 (diff) |
Add an IsManagedAssembly() implementation that doesn't throw on native assemblies.
The downside of the previous method is that it was throwing a first-chance exception on native binaries. This makes it harder to debug.
Also the new implementation is 4 times faster for success cases and 5-6 times faster for failure cases (native binary).
Diffstat (limited to 'Mono.Addins/Mono.Addins.Database')
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/Util.cs | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/Util.cs b/Mono.Addins/Mono.Addins.Database/Util.cs index 72e48c1..5a80a35 100644 --- a/Mono.Addins/Mono.Addins.Database/Util.cs +++ b/Mono.Addins/Mono.Addins.Database/Util.cs @@ -248,12 +248,73 @@ namespace Mono.Addins.Database yield return gacDir + "_MSIL"; } - internal static bool IsManagedAssembly (string file) + internal static bool IsManagedAssembly (string filePath) { - try { - AssemblyName.GetAssemblyName (file); - return true; - } catch (BadImageFormatException) { + try + { + using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) + using (BinaryReader binaryReader = new BinaryReader(fileStream)) + { + if (fileStream.Length < 64) + { + return false; + } + + // PE Header starts @ 0x3C (60). Its a 4 byte header. + fileStream.Position = 0x3C; + uint peHeaderPointer = binaryReader.ReadUInt32(); + if (peHeaderPointer == 0) + { + peHeaderPointer = 0x80; + } + + // Ensure there is at least enough room for the following structures: + // 24 byte PE Signature & Header + // 28 byte Standard Fields (24 bytes for PE32+) + // 68 byte NT Fields (88 bytes for PE32+) + // >= 128 byte Data Dictionary Table + if (peHeaderPointer > fileStream.Length - 256) + { + return false; + } + + // Check the PE signature. Should equal 'PE\0\0'. + fileStream.Position = peHeaderPointer; + uint peHeaderSignature = binaryReader.ReadUInt32(); + if (peHeaderSignature != 0x00004550) + { + return false; + } + + // skip over the PEHeader fields + fileStream.Position += 20; + + const ushort PE32 = 0x10b; + const ushort PE32Plus = 0x20b; + + // Read PE magic number from Standard Fields to determine format. + var peFormat = binaryReader.ReadUInt16(); + if (peFormat != PE32 && peFormat != PE32Plus) + { + return false; + } + + // Read the 15th Data Dictionary RVA field which contains the CLI header RVA. + // When this is non-zero then the file contains CLI data otherwise not. + ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248)); + fileStream.Position = dataDictionaryStart; + + uint cliHeaderRva = binaryReader.ReadUInt32(); + if (cliHeaderRva == 0) + { + return false; + } + + return true; + } + } + catch (Exception) + { return false; } } |