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

Speed up ClrAssemblyReader.ReadMemoryIgnoreErrors #76

Open
MSDN-WhiteKnight opened this issue Sep 11, 2021 · 0 comments
Open

Speed up ClrAssemblyReader.ReadMemoryIgnoreErrors #76

MSDN-WhiteKnight opened this issue Sep 11, 2021 · 0 comments

Comments

@MSDN-WhiteKnight
Copy link
Owner

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();
        }
    }
}

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

No branches or pull requests

1 participant