MSDN-WhiteKnight / CilTools

A set of tools to work with CIL in .NET applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Speed up ClrAssemblyReader.ReadMemoryIgnoreErrors

MSDN-WhiteKnight opened this issue · comments

ReadMemoryIgnoreErrors can be speeded up by scanning valid memory regions via VirtualQueryEx and reading them as whole, instead of by pages. Example:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;


namespace ConsoleApp1
{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);

        [Flags]
        enum LoadLibraryFlags : uint
        {
            None = 0,
            DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
            LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
            LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
            LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
            LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
            LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
            LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
            LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
            LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
            LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
            LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

        [StructLayout(LayoutKind.Sequential)]
        public struct MODULEINFO
        {
            public IntPtr lpBaseOfDll;
            public uint SizeOfImage;
            public IntPtr EntryPoint;
        }

        [DllImport("psapi.dll", SetLastError = true)]
        static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out IntPtr lpNumberOfBytesRead);

        [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "ReadProcessMemory")]
        static extern bool ReadProcessMemory_Byte(
            IntPtr hProcess, IntPtr lpBaseAddress, out byte lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead
            );

        [StructLayout(LayoutKind.Sequential)]
        public struct MEMORY_BASIC_INFORMATION
        {
            public IntPtr BaseAddress;
            public IntPtr AllocationBase;
            public uint AllocationProtect;
            public IntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        }

        public enum AllocationProtect : uint
        {
            PAGE_EXECUTE = 0x00000010,
            PAGE_EXECUTE_READ = 0x00000020,
            PAGE_EXECUTE_READWRITE = 0x00000040,
            PAGE_EXECUTE_WRITECOPY = 0x00000080,
            PAGE_NOACCESS = 0x00000001,
            PAGE_READONLY = 0x00000002,
            PAGE_READWRITE = 0x00000004,
            PAGE_WRITECOPY = 0x00000008,
            PAGE_GUARD = 0x00000100,
            PAGE_NOCACHE = 0x00000200,
            PAGE_WRITECOMBINE = 0x00000400
        }

        const uint MEM_COMMIT = 0x1000;
        const uint MEM_FREE = 0x10000;
        const uint MEM_RESERVE = 0x2000;

        [DllImport("kernel32.dll")]
        static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);

        public struct MemoryRegion
        {
            public long Address { get; set; }
            public long Size { get; set; }
            public bool IsReadable { get; set; }
        }

        static bool IsReadAccess(uint protectionFlags)
        {
            AllocationProtect ap = (AllocationProtect)protectionFlags;

            return ap == AllocationProtect.PAGE_EXECUTE_READ ||
                   ap == AllocationProtect.PAGE_EXECUTE_READWRITE ||
                   ap == AllocationProtect.PAGE_EXECUTE_WRITECOPY ||
                   ap == AllocationProtect.PAGE_READONLY ||
                   ap == AllocationProtect.PAGE_READWRITE ||
                   ap == AllocationProtect.PAGE_WRITECOPY;
        }

        public static MemoryRegion[] GetMemoryRegions(IntPtr hProcess, IntPtr address, int size)
        {
            List<MemoryRegion> ret = new List<MemoryRegion>(50);
            long MaxAddress = (long)address + size;

            while(true)
            {
                MEMORY_BASIC_INFORMATION m=new MEMORY_BASIC_INFORMATION();
                int result = VirtualQueryEx(
                    hProcess, address, out m, (uint)Marshal.SizeOf(m)
                    );

                if (result==0||m.RegionSize == IntPtr.Zero) break;

                MemoryRegion reg = new MemoryRegion();
                reg.Address = (long)m.BaseAddress;
                reg.Size = (long)m.RegionSize;

                if (m.State == MEM_COMMIT && IsReadAccess(m.AllocationProtect))
                {
                    reg.IsReadable = true;
                }

                ret.Add(reg);
                address = (IntPtr)((long)m.BaseAddress + (long)m.RegionSize);
                if ((long)address > MaxAddress) break;
            }

            return ret.ToArray();
        }

        static void EnumMemoryRegions(IntPtr hProcess,IntPtr address, int size)
        {
            MemoryRegion[] regions = GetMemoryRegions(hProcess, address, size);

            for (int i = 0; i < regions.Length; i++)
            {
                Console.WriteLine("0x{0} : {1} bytes, Readable: {2}",
                    regions[i].Address.ToString("X"), regions[i].Size, regions[i].IsReadable
                    );
            }
        }

        static void Main(string[] args)
        {
            string path = "C:\\Test\\Lib.dll";
            //string path = typeof(object).Assembly.Location;
            IntPtr hModule = LoadLibraryEx(path, IntPtr.Zero, LoadLibraryFlags.None);
            if (hModule == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error());

            IntPtr hProcess = Process.GetCurrentProcess().Handle;

            MODULEINFO mi = new MODULEINFO();
            bool res = GetModuleInformation(hProcess, hModule, out mi, (uint)Marshal.SizeOf(mi));
            if (res == false) throw new Win32Exception(Marshal.GetLastWin32Error());

            EnumMemoryRegions(hProcess, mi.lpBaseOfDll, (int)mi.SizeOfImage);            
            Console.ReadKey();
        }
    }
}