0

EDIT: I think I have an idea of a possible solution for the actual searching of values. By making sure the user input ends in 0 the issue should be resolved. This would involve subtracting the last digit from the uint (which I do not know how to get, unless I go the convert to string, trim end back to uint method which is ugly but I guess it could work) and then subtracting it. If anyone has any tips on how to do this please help me out!

I've been working on a program to search memory on the Xbox 360 for specific values, if you are familiar, it is similar to "Cheat Engine". I've gotten the basics down, but I just ran into an issue. My method to search memory is dependent on starting your search at an address that will line up with your value. If that doesn't make sense to you here is the code:

private void searchInt32(int Value, uint Address, uint BytesToSearch)
    {
        for (uint i = 0; i <= BytesToSearch; i+=4)
        {
            int recoveredMem = XboxSupport.littleEndtoInt(XboxSupport.GetMem(Address + i, 4), 0);
            //Recover Memory (As Bytes) and convert to integer from address (incremented based on for loop)
            if (recoveredMem == Value) //Check if recovered mem = search value
            {
                writeToFile(Address + i, Convert.ToString(Value)); //If recovered mem = search value, write to a text file
            }
            siStatus.Caption = String.Format("Searching Bytes {0} out of {1}...", i, BytesToSearch); //Update status caption
        }
    }

As you can see, the code is kept to a minimum and it's also about as fast as possible when it comes to recovering memory from a console. But, if the 4 bytes it recovers don't line up with the value, it will never return what you want. That's obviously a serious issue because the user won't know where their value is or what address to start at to return the correct value. I then attempted to use the following code to fix the issue:

private void searchUInt32(uint Value, uint Address, uint BytesToSearch)
    {

        siStatus.Caption = String.Format("Recovering Memory...");
        byte[] data = XboxSupport.GetMem(Address, BytesToSearch); //Dump Console Memory
        FileStream output = new FileStream("SearchData.dat", FileMode.Create);
        BinaryWriter writer = new BinaryWriter(output);
        writer.Write(data); //Write dump to file
        writer.Close();
        output = new FileStream("SearchData.dat", FileMode.Open);
        BinaryReader reader = new BinaryReader(output); //Open dumped file
        for (uint i = 0; i *4 < reader.BaseStream.Length; i++)
        {
            byte[] bytes = reader.ReadBytes(4); //Read the 4 bytes
            Array.Reverse(bytes);
            uint currentValue = BitConverter.ToUInt32(bytes, 0); //Convert to UInt
            if(currentValue == Value) //Compare
                writeToFile(Address + i * 4, Convert.ToString(Value));
            siStatus.Caption = String.Format("Searching Bytes {0} out of {1}...", i * 4, BytesToSearch);
        }
        reader.Close();
        File.Delete("SearchData.dat");
    }

There is a lot more code, but essentially it does the same thing, just using a file. My original goal was to have users be able to input their own memory blocks to be searched, but right now it seems that just won't work. I do not really want to have the program search all of the memory because that might end up being a slow process (depending on the size of the process being dumped) and often times the values being looked for can be narrowed down to areas of writeable code, removing junk addresses from the executable portion of the process. I am just looking to see if anyone has any suggestions, I was thinking I could possibly get the entry address from the process (I have a function for it) and using a little math correct user input addresses to work properly but I wasn't entirely sure how to do it. If anyone has any suggestions or solutions I'd appreciate any help I can get. If any of my post needs to be clarified/cleaned up please let me know, I'll be glad to do anything that might help me to an answer. Thanks!

Edit: Temporary (hopefully) Solution:

When I load addresses into the tool they are loaded as strings from a text file, then a conversion to uint is attempted. I solved the not even issue using the following code:

sA[0] = sA[0].Remove(sA[0].Length - 1) + "0"; //Remove last character and replace w/ 0
//Add 16 to the search length
Matt
  • 621
  • 1
  • 6
  • 24
  • not entirely relevant but for tidy sake i would put i+=4 in the for loop to avoid all those *4s and /4 – Weyland Yutani Oct 28 '13 at 17:57
  • I'm not sure whether to forward this kind of cause, but it would seem you've lost all hope at performance about here: `byte[] data = XboxSupport.GetMem(Address, BytesToSearch);` Much better to do it all in memory and in bite-sized chunks... You ought to be able to figure out how to find your input value by rearranging it (perhaps more than once) instead of rearranging the data being searched as well... –  Oct 28 '13 at 18:01
  • @ebyrob I don't entirely follow you. The GetMem function recovers an amount of bytes specified by "BytesToSearch" from the address I specify. I assume you are referring to the second I have posted which is, in my opinion not efficient or even good code. Sadly I do not have the option to view the console memory without recovering it first, but I assume something along the lines of my first method would be close to what you are referring to? As for the rearranging part, could you perhaps give me an example? Thanks. – Matt Oct 28 '13 at 18:10
  • Yes, your first example does a better scan. It looks in 1 4 byte block at a time for a match instead of what might be many MB or even GB. If I were doing it in memory I'd start with maybe an 8k (8192 byte) block and search that with a single call to `.GetMem()`. Once that was working I'd vary the block size up and down to see if it mattered much. Finally, I'd consider faster methods of searching each single 8k block given my integer input. (search of a fixed bit string in infinite data length is an infinite speedup solution space, or so I've been told) –  Oct 28 '13 at 18:37
  • Haha! I knew it will be a Cheat Engine! :) In case you search for a longer pattern, optimizations: http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm, http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm, and others in http://en.wikipedia.org/wiki/String_searching_algorithm (they all can be applied for byte pattern search as well). Although I guess most of the time you'll search for one byte (health points for example). In cheat engines they repeat the search multiple times at different times to narrow down the number of potential target byte locations – Csaba Toth Oct 28 '13 at 23:22

1 Answers1

1

Instead of dumping memory to disk and reading every iteration, scan the target process' memory in chunks, and then marshal the data to leverage the efficiency of pointer arithmetic.

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

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

        private static unsafe void Main(string[] args) {
            Process process = Process.GetProcessesByName("notepad")[0]; //example target process

            int search = 100;  //search value
            int segment = 0x10000; //avoid the large object heap (> 84k)
            int range = 0x7FFFFFFF - segment; ; //32-bit example

            int bytesRead;
            List<int> addresses = new List<int>();

            DateTime start = DateTime.Now;

            for (int i = 0; i < range; i += segment) {
                byte[] buffer = new byte[segment];

                if (!ReadProcessMemory(process.Handle, new IntPtr(i), buffer, segment, out bytesRead)) {
                    continue;
                }

                IntPtr data = Marshal.AllocHGlobal(bytesRead);
                Marshal.Copy(buffer, 0, data, bytesRead);

                for (int j = 0; j < bytesRead; j++) {
                    int current = *(int*)(data + j);

                    if (current == search) {
                        addresses.Add(i + j);
                    }
                }

                Marshal.FreeHGlobal(data);
            }

            Console.WriteLine("Duration: {0} seconds", (DateTime.Now - start).TotalSeconds);
            Console.WriteLine("Found: {0}", addresses.Count);
            Console.ReadLine();
        }
    }
}

Test Results

Duration: 1.142 seconds
Found: 3204

Create a generic class to make type marshaling easier, like so:

public static class MarshalHelper
{
    public unsafe static T Read<T>(IntPtr address)
    {
        object value;

        switch (Type.GetTypeCode(typeof(T)))
        {
            case TypeCode.Int16:
                value = *(short*)address;
                break;
            case TypeCode.Int32:
                value = *(int*)address;
                break;
            case TypeCode.Int64:
                value = *(long*)address;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        return (T)value;
    }
}
hyru
  • 189
  • 6
  • The issue is that I'm not searching a process that can be found on the computer - Otherwise this code would be excellent. Sadly there is not currently a way to scan running process memory on an Xbox 360, at least that I am currently aware of – Matt Oct 29 '13 at 00:32
  • @NoviceProgrammer Wait, so `XboxSupport.GetMem()` is a made-up function that doesn't exist? PS - Use bit-wise arithmetic. Clear the lowest order bit in n. `n = n | 0xFFFFFFFE` –  Oct 29 '13 at 14:41
  • @ebyrob Yes, it is a function I created. And what is the bit-wise arithmetic? Does that just make the lowest value a 0 or totally remove it? – Matt Oct 30 '13 at 05:22
  • @NoviceProgrammer You can't really "remove" a bit. It would set the lowest bit (0x1) to 0. Actually that should be an `&` not an `|`. –  Oct 30 '13 at 13:29
  • @ebyrob That would only set the lowest bit to 0 if it were 1 though correct? – Matt Oct 30 '13 at 15:53
  • @NoviceProgrammer 1 AND 0 == 0. Also: 0 AND 0 == 0. So it works in either case. Note: the comment should have been: `n = n & 0xFFFFFFFE` –  Oct 30 '13 at 17:28