Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


[C#] Scan Memory Looking for a Specific Byte Array

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
Zarathos
How do I cheat?
Reputation: 0

Joined: 24 Apr 2013
Posts: 5

PostPosted: Wed Apr 24, 2013 5:36 am    Post subject: [C#] Scan Memory Looking for a Specific Byte Array Reply with quote

I'm developing a little memory scanner in C#. When I open a process, the first thing I want to do is to check if the process is correct (it must contains a specific signature that can be anywhere inside its memory). Here is my code:

Code:
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ReadProcessMemory([In] IntPtr processHandle, [In] IntPtr processAddress, [Out] Byte[] buffer, [In] UInt32 bytesToRead, [Out] out IntPtr bytesRead);

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
internal static extern UInt32 VirtualQueryEx([In] IntPtr processHandle, [In, Optional] IntPtr processAddress, [Out] out MEMORY_BASIC_INFORMATION buffer, [In] UInt32 bufferSize);



internal struct MEMORY_BASIC_INFORMATION
{
    public static UInt32 Size = (UInt32)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION));

    public IntPtr BaseAddress;
    public IntPtr AllocationBase;
    public AllocationProtect AllocationProtect;
    public IntPtr RegionSize;
    public StateEnum State;
    public AllocationProtect Protect;
    public TypeEnum lType;
}



public void Open()
{
    Byte[] toFind = new Byte[] { 31, 55, 78, 33, 00, 00, 00, 37 };
    UInt32 address = 0;

    do
    {
        MEMORY_BASIC_INFORMATION info = new MEMORY_BASIC_INFORMATION();
        NativeMethods.VirtualQueryEx(m_Process.Handle, (IntPtr)address, out info, MEMORY_BASIC_INFORMATION.Size);

        Byte[] buffer = new Byte[(UInt32)info.RegionSize];
        IntPtr bytesRead;

        if (NativeMethods.ReadProcessMemory(m_Process.Handle, info.BaseAddress, buffer, (UInt32)buffer.Length, out bytesRead))
        {
            if (buffer.Contains(toFind)) // Extension Method
                m_IsValid = true;
        }
        else
            m_IsValid = false;

        if (address == (UInt32)info.BaseAddress + (UInt32)info.RegionSize)
            break;

        address = (UInt32)info.BaseAddress + (UInt32)info.RegionSize;
    }
    while (address <= 0x7fffffff);

    m_IsValid = false;
}


The first problem is: this process is taking FOREVER to complete... I don't know why but it looks like it's endlessly looping (one day I let it running for debug purposes for more than one hour without reaching the end of the method). Checking for Marshal.GetLastWin32Error() inside my loop I noticed that sometimes I get an ERROR_PARTIAL_COPY (0x0000012B) after calling ReadProcessMemory... is it the possible cause?

Then I also have some questions:

1) Should I call OpenProcess before proceeding with the scan loop?
2) I would like to make my application both x32 and x64 compatible. What should I change inside my code to be sure it will properly work with both systems?
3) While scanning the process memory in order to find my target byte array, should I check the current MEMORY_BASIC_INFORMATION's AllocationProtect, State, Protect and/or lType to see if I can skip the region because it's not necessary or it can't be read?

Many thanks guys!
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Wed Apr 24, 2013 7:10 am    Post subject: Reply with quote

It's taking forever since you are constantly reading memory over and over. Instead, just dump the region and scan for your array inside that dump.

Here's some ported code I made a while ago for this exact purpose:
Code:

/**
 * sigScan C# Implementation - Written by atom0s [aka Wiccaan]
 * Class Version: 2.0.0
 *
 * [ CHANGE LOG ] -------------------------------------------------------------------------
 *
 *      2.0.0
 *          - Updated to no longer require unsafe or fixed code.
 *          - Removed unneeded methods and code.
 *         
 *      1.0.0
 *          - First version written and release.
 *         
 * [ CREDITS ] ----------------------------------------------------------------------------
 *
 *      sigScan is based on the FindPattern code written by
 *      dom1n1k and Patrick at GameDeception.net
 *     
 *      Full credit to them for the purpose of this code. I, atom0s, simply
 *      take credit for converting it to C#.
 *
 */

namespace YOUR_NAMESPACE_HERE.Classes
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.InteropServices;

    public class SigScan
    {
        /// <summary>
        /// ReadProcessMemory
        /// 
        ///     API import definition for ReadProcessMemory.
        /// </summary>
        /// <param name="hProcess">Handle to the process we want to read from.</param>
        /// <param name="lpBaseAddress">The base address to start reading from.</param>
        /// <param name="lpBuffer">The return buffer to write the read data to.</param>
        /// <param name="dwSize">The size of data we wish to read.</param>
        /// <param name="lpNumberOfBytesRead">The number of bytes successfully read.</param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool ReadProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            [Out] byte[] lpBuffer,
            int dwSize,
            out int lpNumberOfBytesRead
            );

        /// <summary>
        /// m_vDumpedRegion
        /// 
        ///     The memory dumped from the external process.
        /// </summary>
        private byte[] m_vDumpedRegion;

        /// <summary>
        /// m_vProcess
        /// 
        ///     The process we want to read the memory of.
        /// </summary>
        private Process m_vProcess;

        /// <summary>
        /// m_vAddress
        /// 
        ///     The starting address we want to begin reading at.
        /// </summary>
        private IntPtr m_vAddress;

        /// <summary>
        /// m_vSize
        /// 
        ///     The number of bytes we wish to read from the process.
        /// </summary>
        private Int32 m_vSize;


        #region "sigScan Class Construction"
        /// <summary>
        /// SigScan
        /// 
        ///     Main class constructor that uses no params. 
        ///     Simply initializes the class properties and 
        ///     expects the user to set them later.
        /// </summary>
        public SigScan()
        {
            this.m_vProcess = null;
            this.m_vAddress = IntPtr.Zero;
            this.m_vSize = 0;
            this.m_vDumpedRegion = null;
        }
        /// <summary>
        /// SigScan
        /// 
        ///     Overloaded class constructor that sets the class
        ///     properties during construction.
        /// </summary>
        /// <param name="proc">The process to dump the memory from.</param>
        /// <param name="addr">The started address to begin the dump.</param>
        /// <param name="size">The size of the dump.</param>
        public SigScan(Process proc, IntPtr addr, int size)
        {
            this.m_vProcess = proc;
            this.m_vAddress = addr;
            this.m_vSize = size;
        }
        #endregion

        #region "sigScan Class Private Methods"
        /// <summary>
        /// DumpMemory
        /// 
        ///     Internal memory dump function that uses the set class
        ///     properties to dump a memory region.
        /// </summary>
        /// <returns>Boolean based on RPM results and valid properties.</returns>
        private bool DumpMemory()
        {
            try
            {
                // Checks to ensure we have valid data.
                if (this.m_vProcess == null)
                    return false;
                if (this.m_vProcess.HasExited)
                    return false;
                if (this.m_vAddress == IntPtr.Zero)
                    return false;
                if (this.m_vSize == 0)
                    return false;

                // Create the region space to dump into.
                this.m_vDumpedRegion = new byte[this.m_vSize];

                int nBytesRead;

                // Dump the memory.
                var ret = ReadProcessMemory(
                    this.m_vProcess.Handle, this.m_vAddress, this.m_vDumpedRegion, this.m_vSize, out nBytesRead
                    );

                // Validation checks.
                return ret && nBytesRead == this.m_vSize;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// MaskCheck
        /// 
        ///     Compares the current pattern byte to the current memory dump
        ///     byte to check for a match. Uses wildcards to skip bytes that
        ///     are deemed unneeded in the compares.
        /// </summary>
        /// <param name="nOffset">Offset in the dump to start at.</param>
        /// <param name="btPattern">Pattern to scan for.</param>
        /// <param name="strMask">Mask to compare against.</param>
        /// <returns>Boolean depending on if the pattern was found.</returns>
        private bool MaskCheck(int nOffset, IEnumerable<byte> btPattern, string strMask)
        {
            // Loop the pattern and compare to the mask and dump.
            return !btPattern.Where((t, x) => strMask[x] != '?' && ((strMask[x] == 'x') && (t != this.m_vDumpedRegion[nOffset + x]))).Any();
        }

        #endregion

        #region "sigScan Class Public Methods"
        /// <summary>
        /// FindPattern
        /// 
        ///     Attempts to locate the given pattern inside the dumped memory region
        ///     compared against the given mask. If the pattern is found, the offset
        ///     is added to the located address and returned to the user.
        /// </summary>
        /// <param name="btPattern">Byte pattern to look for in the dumped region.</param>
        /// <param name="strMask">The mask string to compare against.</param>
        /// <param name="nOffset">The offset added to the result address.</param>
        /// <returns>IntPtr - zero if not found, address if found.</returns>
        public IntPtr FindPattern(byte[] btPattern, string strMask, int nOffset)
        {
            try
            {
                // Dump the memory region if we have not dumped it yet.
                if (this.m_vDumpedRegion == null || this.m_vDumpedRegion.Length == 0)
                {
                    if (!this.DumpMemory())
                        return IntPtr.Zero;
                }

                // Ensure the mask and pattern lengths match.
                if (strMask.Length != btPattern.Length)
                    return IntPtr.Zero;

                // Loop the region and look for the pattern.
                for (int x = 0; x < this.m_vDumpedRegion.Length; x++)
                {
                    if (this.MaskCheck(x, btPattern, strMask))
                    {
                        // The pattern was found, return it.
                        return new IntPtr((int)this.m_vAddress + (x + nOffset));
                    }
                }

                // Pattern was not found.
                return IntPtr.Zero;
            }
            catch (Exception)
            {
                return IntPtr.Zero;
            }
        }

        /// <summary>
        /// ResetRegion
        /// 
        ///     Resets the memory dump array to nothing to allow
        ///     the class to redump the memory.
        /// </summary>
        public void ResetRegion()
        {
            this.m_vDumpedRegion = null;
        }
        #endregion

        #region "sigScan Class Properties"
        public Process Process
        {
            get { return this.m_vProcess; }
            set { this.m_vProcess = value; }
        }
        public IntPtr Address
        {
            get { return this.m_vAddress; }
            set { this.m_vAddress = value; }
        }
        public Int32 Size
        {
            get { return this.m_vSize; }
            set { this.m_vSize = value; }
        }
        #endregion

    }
}


Some things to note though:
- You should check the page properties you are scanning to ensure you are allowed to access them.
- You should be cautious of page boundaries.
- My above code was written with the intentions of being used for specific targets so I didn't bother implementing a bunch of checks and such.

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
Zarathos
How do I cheat?
Reputation: 0

Joined: 24 Apr 2013
Posts: 5

PostPosted: Wed Apr 24, 2013 7:27 am    Post subject: Reply with quote

Thank you very much for the reply!
For what concerns your code... which api call should I use to check if the dump is readable? Which properties should I check for? I have to scan the whole process memory... but your code wants a size parameter... how can I obtain the memory size of a process? Should I dump it completely or little by little?

Thanks!
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Wed Apr 24, 2013 8:40 am    Post subject: Reply with quote

Zarathos wrote:
Thank you very much for the reply!
For what concerns your code... which api call should I use to check if the dump is readable? Which properties should I check for? I have to scan the whole process memory... but your code wants a size parameter... how can I obtain the memory size of a process? Should I dump it completely or little by little?

Thanks!


If you plan to alter the code to check for valid page access, use VirtualQueryEx.

You will need to go page by page dumping and scanning if you must scan the entire process.

You are best off dumping things page-by-page then scanning within them, returning the results, etc.

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
Pingo
Grandmaster Cheater
Reputation: 8

Joined: 12 Jul 2007
Posts: 571

PostPosted: Wed Apr 24, 2013 3:37 pm    Post subject: Reply with quote

Try this also. Might be some use to you.
No comments sorry,.
Usage..
Code:
            Byte[] toFind = new Byte[] { 31, 55, 78, 33, 00, 00, 00, 37 };
            IntPtr MyAddress = AobScan("Process Name Here", toFind);

But you sure your bytes arnt in hex format?
I'v always used hex format and i can't ever recall someone doing it in dec.
like this...
Code:
Byte[] toFind = new Byte[] { 0x31, 0x55, 0x78, 0x33, 0, 0, 0, 0x37 };

Code:
        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, int lpNumberOfBytesRead);
        [DllImport("kernel32.dll")]
        protected static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);

        [StructLayout(LayoutKind.Sequential)]
        protected 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;
        }
        List<MEMORY_BASIC_INFORMATION> MemReg { get; set; }

        public void MemInfo(IntPtr pHandle)
        {
            IntPtr Addy = new IntPtr();
            while (true)
            {
                MEMORY_BASIC_INFORMATION MemInfo = new MEMORY_BASIC_INFORMATION();
                int MemDump = VirtualQueryEx(pHandle, Addy, out  MemInfo, Marshal.SizeOf(MemInfo));
                if (MemDump == 0) break;
                if ((MemInfo.State & 0x1000) != 0 && (MemInfo.Protect & 0x100) == 0)
                    MemReg.Add(MemInfo);
                Addy = new IntPtr(MemInfo.BaseAddress.ToInt32() + MemInfo.RegionSize);
            }
        }
        public IntPtr _Scan(byte[] sIn, byte[] sFor)
        {
            int[] sBytes = new int[256]; int Pool = 0;
            int End = sFor.Length - 1;
            for (int i = 0; i < 256; i++)
                sBytes[i] = sFor.Length;
            for (int i = 0; i < End; i++)
                sBytes[sFor[i]] = End - i;
            while (Pool <= sIn.Length - sFor.Length)
            {
                for (int i = End; sIn[Pool + i] == sFor[i]; i--)
                    if (i == 0) return new IntPtr(Pool);
                Pool += sBytes[sIn[Pool + End]];
            }
            return IntPtr.Zero;
        }
        public IntPtr AobScan(string ProcessName, byte[] Pattern)
        {
            Process[] P = Process.GetProcessesByName(ProcessName);
            if (P.Length == 0) return IntPtr.Zero;
            MemReg = new List<MEMORY_BASIC_INFORMATION>();
            MemInfo(P[0].Handle);
            for (int i = 0; i < MemReg.Count; i++)
            {
                byte[] buff = new byte[MemReg[i].RegionSize];
                ReadProcessMemory(P[0].Handle, MemReg[i].BaseAddress, buff, MemReg[i].RegionSize, 0);

                IntPtr Result = _Scan(buff, Pattern);
                if (Result != IntPtr.Zero)
                    return new IntPtr(MemReg[i].BaseAddress.ToInt32() + Result.ToInt32());
            }
            return IntPtr.Zero;
        }


I'v had really good results with this method, almost instant results like CE.

_________________
Back to top
View user's profile Send private message
geksagen
How do I cheat?
Reputation: 0

Joined: 05 May 2015
Posts: 2

PostPosted: Tue May 05, 2015 7:53 pm    Post subject: overflow Reply with quote

Sorry , I do not know much language. (googol translator)
my operating system Windows 64 bit,
when the execution of your code , an error overflow

Code:
Addy = new IntPtr(MemInfo.BaseAddress.ToInt32() + MemInfo.RegionSize);


p/s
I've used the search
Back to top
View user's profile Send private message
geksagen
How do I cheat?
Reputation: 0

Joined: 05 May 2015
Posts: 2

PostPosted: Wed May 06, 2015 9:47 am    Post subject: Reply with quote

Code:
Addy = new IntPtr(MemInfo.BaseAddress.ToInt32() + (int)MemInfo.RegionSize);

work
Back to top
View user's profile Send private message
Ludwig
Advanced Cheater
Reputation: 0

Joined: 10 Jan 2016
Posts: 68

PostPosted: Thu Jun 01, 2017 11:27 am    Post subject: Reply with quote

right..this is a pretty old post...
im new on c#, but bzy building an similar app
using https://github.com/Adversities/Cheatool
ive made a test version...:
https://stackoverflow.com/questions/44314769/using-boyer-moore-algorithms-in-64-bit-processes
now, im running into an issue, using this lib, 32 bit processes works fine...i can use aob's i use in ce...
64 bit processes, for some reason, gives issues...
mainly..say chrome, a 64 bit on my pc, it wont find the aob, even if i force it with the correct pid of the process..tho CE got no issue finding the same aob with wilds, in 32 or 64 bit processes. is there a way to modify the booyermoore scan to accommodate 64 bit processes as well?
then..my app, i need to build it in 32 bit preferred to use booyer satisfactory...but some sections in my code wont work if im looking for modules in 64 bit processes, like chrome...that's my 2nd issue
changing the build to un-tick "32 bit preferred, fixes the module issue, but them the scan function don't work at all


any hlp would b gr8tely appreciated


Last edited by Ludwig on Tue Jun 06, 2017 1:10 pm; edited 2 times in total
Back to top
View user's profile Send private message
Matze500
Expert Cheater
Reputation: 8

Joined: 25 Jan 2012
Posts: 241
Location: Germany

PostPosted: Thu Jun 01, 2017 7:29 pm    Post subject: Reply with quote

There is a checkbox in the build settings which needs to be unchecked. Maybe that is the problem It's call prefere 32 bit.

Greets Matze

_________________
Back to top
View user's profile Send private message
Ludwig
Advanced Cheater
Reputation: 0

Joined: 10 Jan 2016
Posts: 68

PostPosted: Sat Jun 03, 2017 11:01 am    Post subject: Reply with quote

Matze500 wrote:
There is a checkbox in the build settings which needs to be unchecked. Maybe that is the problem It's call prefere 32 bit.

Greets Matze

tried that..not the issue..
opened a question on stackoverflo:https://stackoverflow.com/questions/44314769/using-boyer-moore-algorithms-in-64-bit-processes
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Sun Jun 04, 2017 5:58 pm    Post subject: This post has 1 review(s) Reply with quote

You are doing a byte by byte compare/scan so the algo you use is not going to matter based on the applications bit mode. A value between 0 and 255 (byte) is going to be the same in 32bit and 64bit.

Looking at your code, you are using PROCESS_ALL_ACCESS, I'd suggest debugging the code and making sure things are returning properly for every API call you are making. Generally, things like this are where your problem are going to be.

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites