简体   繁体   中英

Using Boyer–Moore algorithms in 64 bit processes

Im in the process of making an memory scanner app similar to cheat engine, but would like to do it in c#, as an test project... so, i was using a lib that makes use of https://github.com/Adversities/Cheatool which uses Boyer–Moore.

the process detection is working..and in 32 bit processes i can find Array of bytes like `

60 8A DD 01 48 00 00 BC 88 01 04 02 00 01 44 D0 30 60 8C DD 01 46 AD 1F 00 2A 12 10 00 00 29

` correctly if i look for them using the Boyer–Moore method...

but for some reason i cant find them in 64 bit processes.

how can i update the methods found in the dll lib and my app to accommodate 64 bit processes as well? now, as im quite new to c#, and like experimenting, i drafted an test app:

code of Testing application:

   using System;
using System.Diagnostics;

using System.Linq;

using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.InteropServices;


using System.Threading;

namespace MemTester
{





    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Memory Addres Tester [?] (PID: ?)";

            //Console.Write("Process to open => ");
            //string process = Console.ReadLine();
            string process = " ";
            int ppid = 0;
            while (ppid == 0)
            {
                Console.Write("Process PID to open as int=> ");
                string ppid2 = Console.ReadLine();
                if ((ppid2 == "") || (ppid2 == null) || (ppid2.Length < 2))
                { ppid2 = "0"; }
                ppid = Convert.ToInt32(ppid2);
            }
            while (true)
            {
                Console.Write("Value to scan => ");
                string aob = Console.ReadLine();
                if (aob == "exit") break;

                MeMory(process, aob, ppid);
            }
        }
        public abstract class Manager
        {
            [DllImport("KERNEL32.DLL", SetLastError = true)]
            public static extern bool WriteProcessMemory(
                IntPtr process, IntPtr address, byte[] buffer, uint size, ref uint written);

            [DllImport("KERNEL32.DLL")]
            public static extern bool VirtualProtectEx(IntPtr process, IntPtr address,
                uint size, uint access, out uint oldProtect);

            [DllImport("KERNEL32.DLL")]
            public static extern int CloseHandle(IntPtr objectHandle);

            [DllImport("KERNEL32.DLL")]
            public static extern IntPtr OpenProcess(uint access, bool inheritHandler, uint processId);

            [Flags]
            public enum Protection
            {
                PEReadWrite = 0x40,
                PReadWrite = 0x04
            }

            [Flags]
            public enum Access
            {
                Synchronize = 0x100000,
                StandardRightsRequired = 0x000F0000,
                AllAccess = StandardRightsRequired | Synchronize | 0xFFFF
            }
        }
        public abstract class Reader
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct MEMORY_BASIC_INFORMATION
            {
                public IntPtr BaseAddress;
                public IntPtr AllocationBase;
                public uint AllocationProtect;
                public uint RegionSize;
                public uint State;
                public uint Protect;
                public uint Type;
            }

            [DllImport("KERNEL32.DLL")]
            public static extern int VirtualQueryEx(IntPtr hProcess,
                IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);

            [DllImport("KERNEL32.DLL", SetLastError = true)]
            public static extern bool ReadProcessMemory(
                IntPtr process, IntPtr address, byte[] buffer, uint size, ref uint read);

            public List<MEMORY_BASIC_INFORMATION> MemoryRegion = new List<MEMORY_BASIC_INFORMATION>();
        }
        public class BoyerMoore : Reader
        {
            private IntPtr _processHandle;

            public BoyerMoore(IntPtr processHandle)
            {
                _processHandle = processHandle;
            }

            private void MemInfo(bool unwritable)
            {
                IntPtr Addy = new IntPtr();

                while (true)
                {
                    MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION();

                    int MemDump = VirtualQueryEx(_processHandle, Addy, out memInfo, Marshal.SizeOf(memInfo));

                    if (MemDump == 0) break;

                    if ((memInfo.State & 0x1000) != 0)
                    {
                        if (unwritable && (memInfo.Protect & 0xCC) != 0)
                            MemoryRegion.Add(memInfo);
                        else
                            MemoryRegion.Add(memInfo);
                    }

                    Addy = new IntPtr(memInfo.BaseAddress.ToInt32() + (int)memInfo.RegionSize);
                }
            }

            private void BoyerAlgo(IntPtr baseAddress, byte[] memoryBrick, byte[] pattern, ref List<IntPtr> addresses)
            {
                int offSet = 0;
                while ((offSet = Array.IndexOf(memoryBrick, pattern[0], offSet)) != -1)
                {
                    if (pattern.Length > 1)
                        for (int i = 1; i < pattern.Length; i++)
                        {
                            if (memoryBrick.Length <= offSet + pattern.Length
                                || pattern[i] != memoryBrick[offSet + i]) break;

                            if (i == pattern.Length - 1)
                                addresses.Add(new IntPtr((int)baseAddress + offSet));
                        }
                    else addresses.Add(new IntPtr((int)baseAddress + offSet));
                    offSet++;
                }
            }

            private void BoyerAlgo(IntPtr baseAddress, byte[] memoryBrick, string pattern, ref List<IntPtr> addresses)
            {
                int offSet = 0;
                string[] aob = pattern.Split(' ');
                List<int> bytesPos = new List<int>();

                for (int i = 0; i < aob.Length; i++)
                    if (aob[i] != "??")
                        bytesPos.Add(i);

                if (bytesPos.Count != 0)
                    while ((offSet = Array.IndexOf(memoryBrick, (byte)Convert.ToInt32(aob[bytesPos[0]], 16), offSet)) != -1)
                    {
                        if (bytesPos.Count > 1)
                            for (int i = 1; i < bytesPos.Count; i++)
                            {
                                if (memoryBrick.Length <= offSet + pattern.Length
                                    || (byte)Convert.ToInt32(aob[bytesPos[i]], 16)
                                    != memoryBrick[(offSet - bytesPos[0]) + bytesPos[i]]) break;

                                if (i == bytesPos.Count - 1)
                                    if (aob[0] == "??")
                                        addresses.Add(new IntPtr((int)baseAddress + (offSet - bytesPos[0])));
                                    else addresses.Add(new IntPtr((int)baseAddress + offSet));
                            }
                        else
                            addresses.Add(new IntPtr((int)baseAddress + (offSet - bytesPos[0])));
                        offSet++;
                    }
                else
                    for (int i = 0; i < memoryBrick.Length; i++)
                        addresses.Add(new IntPtr((int)baseAddress + i));
            }

            public CancellationTokenSource cancelToken { get; set; } = new CancellationTokenSource();

            public Task<IntPtr[]> AoByte(string pattern, bool unwritable = false)
            {
                if (!pattern.Contains("?"))
                {
                    byte[] buff = pattern.Split(' ').Select(by =>
                    (byte)Convert.ToInt32(by, 16)).ToArray();

                    return Task.Run(() => { return GeneralScan(buff, unwritable); }, cancelToken.Token);
                }
                else return Task.Run(() => { return WCScan(pattern, unwritable); }, cancelToken.Token);
            }


            private IntPtr[] GeneralScan(byte[] buff, bool unwritable)
            {
                MemInfo(unwritable);

                List<IntPtr> addresses = new List<IntPtr>();

                for (int i = 0; i < MemoryRegion.Count; i++)
                {
                    uint read = 0;
                    byte[] wholeMemory = new byte[MemoryRegion[i].RegionSize];

                    ReadProcessMemory(_processHandle, MemoryRegion[i].BaseAddress, wholeMemory,
                        MemoryRegion[i].RegionSize, ref read);

                    BoyerAlgo(MemoryRegion[i].BaseAddress, wholeMemory, buff, ref addresses);
                }
                return addresses.ToArray();
            }

            private IntPtr[] WCScan(string pattern, bool unwritable)
            {
                MemInfo(unwritable);

                List<IntPtr> addresses = new List<IntPtr>();

                for (int i = 0; i < MemoryRegion.Count; i++)
                {
                    uint read = 0;
                    byte[] wholeMemory = new byte[MemoryRegion[i].RegionSize];

                    ReadProcessMemory(_processHandle, MemoryRegion[i].BaseAddress, wholeMemory,
                        MemoryRegion[i].RegionSize, ref read);

                    BoyerAlgo(MemoryRegion[i].BaseAddress, wholeMemory, pattern, ref addresses);
                }
                return addresses.ToArray();
            }
        }

        public class MeMorybox : Manager
        {

            public BoyerMoore BoyerScan { get; set; }


            IntPtr _processHandle;

            public MeMorybox(Process process)
            {
                _processHandle = OpenProcess((uint)Access.AllAccess, false, (uint)process.Id);


                BoyerScan = new BoyerMoore(_processHandle);

            }



            ~MeMorybox()
            {
                CloseHandle(_processHandle);
            }

        }

        static void MeMory(string process, string aob, int ppid)
        {
            //Process[] processList = Process.GetProcessesByName(process);
            Process p = Process.GetProcessById(ppid); ;

            MeMorybox notepad = new MeMorybox(p);

            Console.WriteLine("Hex value of pid:"+p.Id.ToString("x8"));

            var addresses = notepad.BoyerScan.AoByte(aob).GetAwaiter().GetResult();

        Console.WriteLine(addresses.Length + " hits found for pid:"+ppid);

        Console.WriteLine();
    }
}

testing for flash process.. firefox..<32 bit process> chrome..<64 bit process> url to test: https://apps.facebook.com/candycrushsoda

array of byte to search as test:60 ?? ?? ?? ?? ?? ?? ?? ?? 01 ?? ?? 00 01 44 ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 2A 12 10 00 00 29

it should give 1 result

in firefox--Flashplayerplugin it works fine,

in chrome--one of the processes containing module pepflashplayer.dll it >gives no result, as chrome is a 64 bit process.

To verify i double checked by using cheatengine, in both cases the aob is found correctly

I tried your code and for me, when inspecting a 64 bit process it produced arithmetic overflow on the line :

      Addy = new IntPtr(memInfo.BaseAddress.ToInt32() + (int) memInfo.RegionSize);

(Try it for yourself, turn on Build / Advanced.../ Check for arithmetic... in the project settings)

It looks like VirtualQueryEx may fill in MEMORY_BASIC_INFORMATION.BaseAddress with Int32.MaxValue() when querying 64 bit processes.

I think the easiest way to make it compatible with both 32 and 64 bit processes would be to target only 64 bit architecture and write your code accordingly, if you can afford that. I suggest you try the following:

  1. Change the project's platform target to x64
  2. Update MEMORY_BASIC_INFORMATION so that it maps to the 64 bit version of the struct (the bottom one in Remarks), instead of the other one. https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775(v=vs.85).aspx
  3. After that update the code to use 64 bit arithmetic whereever it uses BaseAddress or RegionSize (eg make them IntPtr and use .ToInt64() instead of .ToInt32())

Right..so first i had to use build with any cpu, with "prefer 32 bit" unticked then

public class MeMorybox : Manager
        {

            public BoyerMoore BoyerScan { get; set; }


            IntPtr _processHandle;

            public MeMorybox(Process process)
            {
                _processHandle = OpenProcess((uint)Access.AllAccess, false, (uint)process.Id);


                BoyerScan = new BoyerMoore(_processHandle);

            }

got updated to :

 UIntPtr _processHandle;

        public MeMorybox(Process process)
        {
            _processHandle = OpenProcess((uint)Access.AllAccess, false, (uint)process.Id);


            BoyerScan = new BoyerMoore(_processHandle);

        }

I updated memory basic information...:

 public struct _MEMORY_BASIC_INFORMATION64
    {
        public ulong BaseAddress;
        public ulong AllocationBase;
        public int AllocationProtect;
        public int __alignment1;
        public ulong RegionSize;
        public int State;
        public int Protect;
        public int Type;
        public int __alignment2;
    }

and updated the changes applied in _MEMORY_BASIC_INFORMATION as well as the process handle changes to ## UIntPtr ## thru the code

also updated :

[DllImport("KERNEL32.DLL")]
        public static extern int VirtualQueryEx(UIntPtr hProcess,
            UIntPtr lpAddress, out _MEMORY_BASIC_INFORMATION64 lpBuffer, int dwLength);

        [DllImport("KERNEL32.DLL", SetLastError = true)]
        public static extern bool ReadProcessMemory(
            UIntPtr process, ulong address, byte[] buffer, ulong size, ref uint read);

        public List<_MEMORY_BASIC_INFORMATION64> MemoryRegion = new List<_MEMORY_BASIC_INFORMATION64>();

        [DllImport("KERNEL32.DLL", SetLastError = true)]
        public static extern bool WriteProcessMemory(
            UIntPtr process, ulong address, byte[] buffer, uint size, ref uint written);

        [DllImport("KERNEL32.DLL")]
        public static extern bool VirtualProtectEx(UIntPtr process, ulong address,
            uint size, uint access, out uint oldProtect);

        [DllImport("KERNEL32.DLL")]
        public static extern int CloseHandle(UIntPtr objectHandle);

        [DllImport("KERNEL32.DLL")]
        public static extern UIntPtr OpenProcess(uint access, bool inheritHandler, uint processId);

        [Flags]
        public enum Protection
        {
            PEReadWrite = 0x40,
            PReadWrite = 0x04
        }

        [Flags]
        public enum Access
        {
            Synchronize = 0x100000,
            StandardRightsRequired = 0x000F0000,
            AllAccess = StandardRightsRequired | Synchronize | 0xFFFF
        }

    so, now, compiled for any cpu, it finds adreses corectly for 32 bit and 64 bit proceses
    many thanks to:
    @Dirk
    and
    @Loonquawl

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM