Skip to content

Commit

Permalink
memory grabber fixes (TPA+FX3): fix array overrun in FindPattern; cle…
Browse files Browse the repository at this point in the history
…an up confusion about pointers vs offsets; correct dllimport signatures and clean up unnecessary type coercions due to the wrong signatures
  • Loading branch information
mjrgh committed Sep 12, 2018
1 parent fa55b3e commit 9b80fc9
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 46 deletions.
73 changes: 43 additions & 30 deletions LibDmd/Input/PinballFX/PinballFX3MemoryGrabber.cs
Expand Up @@ -137,19 +137,19 @@ public ColoredFrame CaptureDMD()
var frame = new byte[DMDWidth * DMDHeight];

// Check if a table is loaded... and retrieve DMD offset in memory.
_dmdOffset = GetDMDOffset((int)_handle);
_dmdOffset = GetDMDOffset(_handle);

// ..if not, return an empty frame (blank DMD).
if (_dmdOffset == IntPtr.Zero) {
return new ColoredFrame(128, 32, frame, Color.FromRgb(0, 0, 0));
}

// Retrieve DMD color from memory.
_dmdColor = GetDMDColor((int)_handle); // Return RGB hex color value of DMD (return null value if the color cannot be retrieved).
_dmdColor = GetDMDColor(_handle); // Return RGB hex color value of DMD (return null value if the color cannot be retrieved).
// TODO - APPLY COLOR TO THE DMD

// Grab the whole raw DMD block from game's memory.
ReadProcessMemory((int)_handle, (int)_dmdOffset, RawDMD, RawDMD.Length, 0);
ReadProcessMemory(_handle, _dmdOffset, RawDMD, RawDMD.Length, 0);

// Used to parse pixel bytes of the DMD memory block.
var rawPixelIndex = 0;
Expand Down Expand Up @@ -214,26 +214,32 @@ private static IntPtr BaseAddress(Process process)
return procMod.BaseAddress;
}

private static IntPtr GetDMDOffset(int processHandle)
// convert a raw 4-byte buffer to a physical memory pointer
private static IntPtr B4ToPointer(byte[] buf)
{
return new IntPtr(BitConverter.ToInt32(buf, 0));
}

private static IntPtr GetDMDOffset(IntPtr processHandle)
{
// Retrieve DMD offset in memory using pointers.
var pAddress = new byte[4];
ReadProcessMemory(processHandle, (int)_gameBase + (int)_pBaseAddress, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0) + 0xE8, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0) + 0x34, pAddress, pAddress.Length, 0);
return new IntPtr(BitConverter.ToInt32(pAddress, 0));
var addressBuf = new byte[4];
ReadProcessMemory(processHandle, _pBaseAddress, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf) + 0xE8, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf) + 0x34, addressBuf, addressBuf.Length, 0);
return B4ToPointer(addressBuf);
}

private static Color GetDMDColor(int processHandle)
private static Color GetDMDColor(IntPtr processHandle)
{
// Retrieve DMD color in memory using pointers.
var pAddress = new byte[4];
var addressBuf = new byte[4];
var colorBytes = new byte[4];
ReadProcessMemory(processHandle, (int)_gameBase + (int)_pBaseAddress, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0) + 0xE8, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0) + 0x64, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0) + 0x184, pAddress, pAddress.Length, 0);
ReadProcessMemory(processHandle, BitConverter.ToInt32(pAddress, 0), colorBytes, colorBytes.Length, 0);
ReadProcessMemory(processHandle, _pBaseAddress, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf) + 0xE8, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf) + 0x64, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf) + 0x184, addressBuf, addressBuf.Length, 0);
ReadProcessMemory(processHandle, B4ToPointer(addressBuf), colorBytes, colorBytes.Length, 0);
if (BitConverter.IsLittleEndian) Array.Reverse(colorBytes);
var colorCode = BitConverter.ToInt32(colorBytes, 0);

Expand Down Expand Up @@ -272,30 +278,36 @@ private static IntPtr GetPointerBaseAddress(Process gameProc)
// Open the process to allow memory operations.
var processHandle = OpenProcess(SYNCHRONIZE | PROCESS_VM_READ, false, gameProc.Id);
if (processHandle == IntPtr.Zero) {
Logger.Error("Unable to open FX3 process: win32 error " + Marshal.GetLastWin32Error());
return processHandle;
}

// Find DMD pointer base address offset in memory with its signature pattern.
IntPtr baseOffset = FindPattern(gameProc, (int)BaseAddress(gameProc), 0xFFFFFF, DMDPointerSig, 25);
IntPtr baseOffset = FindPattern(gameProc, _gameBase, gameProc.MainModule.ModuleMemorySize, DMDPointerSig, 25);
if (baseOffset == IntPtr.Zero) {
Logger.Error("DMD pointer base address pattern not found");
return IntPtr.Zero;
}

var offsetBytes = new byte[4];
ReadProcessMemory((int)gameProc.Handle, (int)baseOffset, offsetBytes, offsetBytes.Length, 0);
_pBaseAddress = new IntPtr(BitConverter.ToInt32(offsetBytes, 0) - (int)_gameBase);
ReadProcessMemory(gameProc.Handle, baseOffset, offsetBytes, offsetBytes.Length, 0);
_pBaseAddress = new IntPtr(BitConverter.ToInt32(offsetBytes, 0));

// Return game's process handle.
return processHandle;
}

// Function to search byte pattern in process memory then return its offset.
private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte[] bytePattern, int offset)
private static IntPtr FindPattern(Process gameProc, IntPtr gameBase, int size, byte[] bytePattern, int offset)
{
// Create a byte array to store memory region.
var memoryRegion = new byte[size];

// Dump process memory into the array.
ReadProcessMemory((int)gameProc.Handle, gameBase, memoryRegion, size, 0);
ReadProcessMemory(gameProc.Handle, gameBase, memoryRegion, size, 0);

// Loop into dumped memory region to find the pattern.
for (var x = 0; x < memoryRegion.Length; x++) {
for (var x = 0; x < memoryRegion.Length - bytePattern.Length; x++) {

// If we find the first pattern's byte in memory, loop through the entire array.
for (var y = 0; y < bytePattern.Length; y++) {
Expand All @@ -310,7 +322,7 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte
}
// We've reached the end of the pattern array, we've found the offset.
if (y == bytePattern.Length - 1)
return new IntPtr(gameBase + offset + x); // Return the offset.
return gameBase + offset + x; // Return the offset.
}
}
// We've reached the end of memory region, offset not found.
Expand All @@ -319,16 +331,16 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte

#region Dll Imports

[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);

[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesWritten);

[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, int flAllocationType, int flProtect);

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
Expand All @@ -338,10 +350,11 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte
const UInt32 WAIT_OBJECT_0 = 0x00000000;
const UInt32 WAIT_TIMEOUT = 0x00000102;

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);

#endregion

}
}
}

34 changes: 18 additions & 16 deletions LibDmd/Input/TPAGrabber/TPAGrabber.cs
Expand Up @@ -140,7 +140,7 @@ public byte[] CaptureDMD()

// Check if a table is loaded..
var tableLoaded = new byte[1];
ReadProcessMemory((int)_handle, (int)_gameBase + (int)_gameState, tableLoaded, 1, 0);
ReadProcessMemory(_handle, _gameState, tableLoaded, 1, 0);

// ..if not, return an empty frame (blank DMD).
if (tableLoaded[0] == 0) {
Expand All @@ -149,13 +149,13 @@ public byte[] CaptureDMD()

// Retrieve the DMD entrypoint from EAX registry (returned by our codecave).
var eax = new byte[4];
ReadProcessMemory((int)_handle, (int)_codeCave, eax, 4, 0);
ReadProcessMemory(_handle, _codeCave, eax, 4, 0);

// Now we have our DMD location in memory + little hack to re-align the DMD block.
var dmdOffset = BitConverter.ToInt32(eax, 0) - 0x1F406;
var dmdOffset = new IntPtr(BitConverter.ToInt32(eax, 0) - 0x1F406);

// Grab the whole raw DMD block from game's memory.
ReadProcessMemory((int)_handle, dmdOffset, RawDMD, MemBlockSize + 2, 0);
ReadProcessMemory(_handle, dmdOffset, RawDMD, MemBlockSize + 2, 0);

// Check the DMD CRC flag, skip the frame if the value is incorrect.
if (RawDMD[0] != 0x02) return null;
Expand Down Expand Up @@ -231,7 +231,7 @@ private static IntPtr PatchCodeCave(Process gameProc)
{
// Defines offset address of our codecave.
_gameBase = BaseAddress(gameProc);
var patchOffset = _gameBase + (int)_dmdPatch;
var patchOffset = _dmdPatch;

// Access rights to the process.
const int PROCESS_VM_OPERATION = 0x0008;
Expand Down Expand Up @@ -292,29 +292,31 @@ private static byte[] ASMJump(IntPtr location, IntPtr destination)
private static void FindOffsets(Process gameProc)
{
// Get game process base address
var gameBase = (int)BaseAddress(gameProc);
var gameBase = BaseAddress(gameProc);

// Retrieve DMD creation offset
_dmdPatch = FindPattern(gameProc, gameBase, 0xFFFFFF, DMDCreationSignature, 0) - gameBase;
_dmdPatch = FindPattern(gameProc, gameBase, gameProc.MainModule.ModuleMemorySize, DMDCreationSignature, 0);
Logger.Info("DMD patch address = " + _dmdPatch.ToString("x"));

// Retrieve game state pointer + offset
var gameStatePointer = FindPattern(gameProc, gameBase, 0xFFFFFF, GameStateSignature, 34);
var gameStatePointer = FindPattern(gameProc, gameBase, gameProc.MainModule.ModuleMemorySize, GameStateSignature, 34);
var pointerOffset = new byte[4];
ReadProcessMemory((int)gameProc.Handle, (int)gameStatePointer, pointerOffset, pointerOffset.Length, 0);
_gameState = new IntPtr(BitConverter.ToInt32(pointerOffset, 0) - gameBase);
ReadProcessMemory(gameProc.Handle, gameStatePointer, pointerOffset, pointerOffset.Length, 0);
_gameState = new IntPtr(BitConverter.ToInt32(pointerOffset, 0));
Logger.Info("Game state address = " + _gameState.ToString("x"));
}

// Function to search byte pattern in process memory then return its offset.
private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte[] bytePattern, int Offset)
private static IntPtr FindPattern(Process gameProc, IntPtr gameBase, int size, byte[] bytePattern, int Offset)
{
// Create a byte array to store memory region.
var memoryRegion = new byte[size];

// Dump process memory into the array.
ReadProcessMemory((int)gameProc.Handle, gameBase, memoryRegion, size, 0);
ReadProcessMemory(gameProc.Handle, gameBase, memoryRegion, size, 0);

// Loop into dumped memory region to find the pattern.
for (var x = 0; x < memoryRegion.Length; x++) {
for (var x = 0; x < memoryRegion.Length - bytePattern.Length; x++) {

// If we find the first pattern's byte in memory, loop through the entire array.
for (var y = 0; y < bytePattern.Length; y++) {
Expand All @@ -329,7 +331,7 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte
}
// We've reached the end of the pattern array, we've found the offset.
if (y == bytePattern.Length - 1)
return new IntPtr(gameBase + Offset + x); // Return the offset.
return gameBase + Offset + x; // Return the offset.
}
}
// We've reached the end of memory region, offset not found.
Expand All @@ -339,7 +341,7 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte
#region Dll Imports

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesWritten);
Expand All @@ -357,7 +359,7 @@ private static IntPtr FindPattern(Process gameProc, int gameBase, int size, byte
const UInt32 WAIT_OBJECT_0 = 0x00000000;
const UInt32 WAIT_TIMEOUT = 0x00000102;

[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);

#endregion
Expand Down

0 comments on commit 9b80fc9

Please sign in to comment.