Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added improved code for managed assembly detection #39

Merged
merged 1 commit into from
Aug 10, 2020

Conversation

HarmJ0y
Copy link
Member

@HarmJ0y HarmJ0y commented Aug 1, 2020

-IsDotNetAssembly() should no longer cause image load events.

-IsDotNetAssembly() should no longer cause image load events.
@leechristensen
Copy link
Member

Looks good! Tested against all binaries on my machine and proves to be more accurate. Methodology:

dir /s /b C:\*.exe C:\*.dll > C:\temp\PEs.txt
class Test
{
    public static bool IsDotNetAssemblyLoadMethod(string path)
    {
        try
        {
            var myAssemblyName = AssemblyName.GetAssemblyName(path);
            return true;
        }
        catch (BadImageFormatException exception)
        {
            if (Regex.IsMatch(exception.Message, ".*This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.*", RegexOptions.IgnoreCase))
            {
                return true;
            }
        }
        catch
        {
        }

        return false;
    }

    public static bool IsDotNetAssemblyPEParse(string fileName)
    {
        try
        {
            // from https://stackoverflow.com/a/15608028
            using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            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
        {
            return false;
        }
    }

    public static void Main()
    {
        foreach (var path in System.IO.File.ReadAllLines(@"C:\Users\lchristensen\Downloads\PE-nonmatch.txt"))
        {
            var method = IsDotNetAssemblyLoadMethod(path);
            var parser = IsDotNetAssemblyPEParse(path);

            if (method != parser)
            {
                Console.WriteLine($"{method,-10} {parser,-10} {path}");
            }

        }
    }
}

@leechristensen leechristensen merged commit f528263 into master Aug 10, 2020
@leechristensen leechristensen deleted the feature-dotnetdetection branch August 10, 2020 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants