Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading all process memory to find address of a string variable c#

I have 2 programs written in c#, first one called "ScanMe" contains a string variable that contains value "FINDMEEEEEEE", and a double variable that has the value of 1546.22915487. And the other program called "MemoryScan" reads all the memory of the first program. I want to get the memory address of the string variable of that process

When i execute "MemoryScan" and reads all the memory of "ScanMe" process, then i try to find the byte array of the string in all the data scanned and i get nothing. If i try to find the double i get the specific memory address and also i can change its value, but when i try to do that with the string variable i cant cause i dont even get the address of that string variable.

ScanMe and MemoryScan Code:

class Program
{
    public static string FindMeString = "FINDMEEEEEEE";
    public static double FindMeDouble = 1546.22915487;
    static void Main(string[] args)
    {
        while (FindMeDouble == 1546.22915487)
        {
            System.Threading.Thread.Sleep(2000);
        }
        Console.WriteLine(FindMeDouble.ToString());
        Console.ReadLine();
    }
}

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

namespace MemoryScan
{
class MemoryController
{

    // REQUIRED CONSTS
    const int PROCESS_QUERY_INFORMATION = 0x0400;
    const int MEM_COMMIT = 0x00001000;
    const int PAGE_READWRITE = 0x04;
    const int PROCESS_WM_READ = 0x0010;
    readonly Dictionary<IntPtr, byte[]> Regions = new Dictionary<IntPtr, byte[]>();
    readonly List<SearchResult> _results = new List<SearchResult>();

    // REQUIRED METHODS
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll")]
    public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint dwSize, ref int lpNumberOfBytesRead);

    [DllImport("kernel32.dll")]
    static extern void GetSystemInfo(out SystemInfo lpSystemInfo);

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

    public enum ProcessorArchitecture
    {
        X86 = 0,
        X64 = 9,
        Arm = -1,
        Itanium = 6,
        Unknown = 0xFFFF
    }

    // REQUIRED STRUCTS
    [StructLayout(LayoutKind.Sequential)]
    public struct SystemInfo
    {
        public ProcessorArchitecture ProcessorArchitecture;
        public uint PageSize;
        public IntPtr MinimumApplicationAddress;
        public IntPtr MaximumApplicationAddress;
        public IntPtr ActiveProcessorMask;
        public uint NumberOfProcessors;
        public uint ProcessorType;
        public uint AllocationGranularity;
        public ushort ProcessorLevel;
        public ushort ProcessorRevision;
    }

    [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 void FindProcessMemory(int processId)
    {
        // getting minimum & maximum address
        SystemInfo sys_info;
        GetSystemInfo(out sys_info);

        uint max_Address = (uint)sys_info.MaximumApplicationAddress;
        // opening the process with desired access level
        IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, processId);
        IntPtr current = IntPtr.Zero;

        int bytesRead = 0;  // number of bytes read with ReadProcessMemory
        int dwLength = Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION));

        while ((uint)current < max_Address && VirtualQueryEx(processHandle, current, out MEMORY_BASIC_INFORMATION mem_basic_info, dwLength) != 0)
        {
            // if this memory chunk is accessible
            if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT)
            {
                byte[] buffer = new byte[(int)mem_basic_info.RegionSize];

                // read everything in the buffer above
                if (ReadProcessMemory(processHandle, mem_basic_info.BaseAddress, buffer, (uint)mem_basic_info.RegionSize, ref bytesRead))
                {
                    Regions.Add(mem_basic_info.BaseAddress, buffer);
                }
                else
                    Console.WriteLine($"Error code: Marshal.GetLastWin32Error()");
            }

            // move to the next memory chunk
            current = IntPtr.Add(mem_basic_info.BaseAddress, mem_basic_info.RegionSize.ToInt32());
        }
        byte[] data = System.Text.Encoding.Unicode.GetBytes("FINDMEEEEEEE");
        foreach (IntPtr address in Regions.Keys)
        {
            foreach (int i in ByteSearch.AllIndexOf(Regions[address], data))
                _results.Add(new SearchResult(IntPtr.Add(address, i), data));
        }
        Console.ReadLine();
    }

}
public static class ByteSearch
{
    static int[] createTable(byte[] pattern)
    {
        int[] table = new int[256];

        for (int i = 0; i < table.Length; i++)
            table[i] = pattern.Length;

        for (int i = 0; i < pattern.Length - 1; i++)
            table[Convert.ToInt32(pattern[i])] = pattern.Length - i - 1;

        return table;
    }

    public static bool matchAtOffset(byte[] toSearch, byte[] pattern, int index)
    {
        if (index + pattern.Length > toSearch.Length)
            return false;

        for (int i = 0; i < pattern.Length; i++)
        {
            if (toSearch[i + index] != pattern[i])
                return false;
        }

        return true;
    }

    public static bool Contains(byte[] toSearch, byte[] pattern)
    {
        return FirstIndexOf(toSearch, pattern) != -1;
    }

    public static int FirstIndexOf(byte[] toSearch, byte[] pattern)
    {
        int[] table = createTable(pattern);
        int position = pattern.Length - 1;

        while (position < toSearch.Length)
        {
            int i;

            for (i = 0; i < pattern.Length; i++)
            {
                if (pattern[pattern.Length - 1 - i] != toSearch[position - i])
                    break;

                if (i == pattern.Length - 1)
                    return position - i;
            }

            position += table[Convert.ToInt32(toSearch[position - i])];
        }

        return -1;
    }

    public static int LastIndexOf(byte[] toSearch, byte[] pattern)
    {
        int ret = -1;
        int[] table = createTable(pattern);
        int position = pattern.Length - 1;

        while (position < toSearch.Length)
        {
            int i;
            bool found = false;

            for (i = 0; i < pattern.Length; i++)
            {
                if (pattern[pattern.Length - 1 - i] != toSearch[position - i])
                    break;

                if (i == pattern.Length - 1)
                {
                    ret = position - i;
                    found = true;
                }
            }

            if (found)
                position++;
            else
                position += table[Convert.ToInt32(toSearch[position - i])];
        }

        return ret;
    }

    public static int[] AllIndexOf(byte[] toSearch, byte[] pattern)
    {
        List<int> indices = new List<int>();
        int[] table = createTable(pattern);
        int position = pattern.Length - 1;

        while (position < toSearch.Length)
        {
            int i;
            bool found = false;

            for (i = 0; i < pattern.Length; i++)
            {
                if (pattern[pattern.Length - 1 - i] != toSearch[position - i])
                    break;

                if (i == pattern.Length - 1)
                {
                    indices.Add(position - i);
                    found = true;
                }
            }

            if (found)
                position++;
            else
                position += table[Convert.ToInt32(toSearch[position - i])];
        }

        return indices.ToArray();
    }
}
public class SearchResult
{
    public SearchResult(IntPtr add, byte[] value)
    {
        Address = add;
        Buffer = value;
    }

    public IntPtr Address { get; set; }

    public byte[] Buffer { get; set; }
}

}

Why i cant find the string and when i try to find the double i find it with no problem and even also i can change its value with the extern writeprocessmemory? Thanks.

like image 668
Susta250 Avatar asked Aug 29 '19 20:08

Susta250


1 Answers

I'm not sure I could reproduce your exact condition, since you didn't provide a minimal reproducible example. However, I got it up and running - with similar results. I then figured out the following:

You are not checking the return value of ReadProcessMemory. MSDN says

If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.

The 0 will be mapped to false, depending on the PInvoke signature you use.

To get the last error, use Marshal.GetLastWin32Error(). On my PC, the error code was 299 and MSDN says

ERROR_PARTIAL_COPY

299 (0x12B)

Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

At the same time, the number of bytes read (ref bytesRead) is 0, so it didn't read the process' memory.

See this related SO question about this error code.

like image 93
Thomas Weller Avatar answered Oct 05 '22 15:10

Thomas Weller