I have a program that enables western players to be able to play a Japanese game in English (https://github.com/jmctune/ahkmon). An external program strictly reads the game's memory for dialog within the game and sends the text to your clipboard, which my autohotkey script then picks up, gets translated and sends the translated text to an overlay. This part all works well as of today and is used by several members of this game's community.
What I'm trying to do is take apart the external program that reads the games memory (there is no writing involved whatsoever - it's just reading the Japanese text) and instead, implement it into autohotkey as I can do some cool stuff with it if I can bring it in my script instead.
The creator of this external program is no longer reachable and we're left with a compiled binary. I managed to get the assembly code for the program so I could see how the memory is being read.
I'm attempting to use RHCP's classMemory library to replicate what's being done, but am quite stuck. The program's assembly code that's relevant is written in C# and the relevant bits to this method are here:
Code: Select all
public MemReader()
{
this.processHandle = IntPtr.Zero;
this.closestBlock = 16777216;
HashSet<int> nums = new HashSet<int>();
nums.Add(16777216);
nums.Add(805306368);
nums.Add(536870912);
nums.Add(0);
this.closestBlocks = nums;
this.pageAddress = -1;
this.pageSize = -1;
base();
}
public IntPtr getDialogAddress()
{
if (this.runCount >= 60)
{
this.reset();
this.runCount = 0;
}
MemReader.MEMORY_BASIC_INFORMATION mEMORYBASICINFORMATION = new MemReader.MEMORY_BASIC_INFORMATION();
MemReader.SYSTEM_INFO sYSTEMINFO = new MemReader.SYSTEM_INFO();
MemReader.GetSystemInfo(out sYSTEMINFO);
// Declare regionSize variable
int regionSize = (int)sYSTEMINFO.minimumApplicationAddress;
// Delcare num variable
int num = (int)sYSTEMINFO.maximumApplicationAddress;
regionSize = 16777216;
num = 1073741824;
num = 1342177280;
int num1 = 0;
while (regionSize < num)
{
MemReader.VirtualQueryEx(this.processHandle, (IntPtr)regionSize, out mEMORYBASICINFORMATION, (uint)Marshal.SizeOf(typeof(MemReader.MEMORY_BASIC_INFORMATION)));
if (mEMORYBASICINFORMATION.Protect == 4 && mEMORYBASICINFORMATION.State == 4096)
{
bool flag = false;
foreach (Tuple<int, int> module in this.modules)
{
if (regionSize >= module.Item1 && regionSize <= module.Item2)
{
flag = true;
}
if (num < module.Item1 || num > module.Item2)
{
continue;
}
flag = true;
}
foreach (int closestBlock in this.closestBlocks)
{
if (regionSize + (int)mEMORYBASICINFORMATION.RegionSize >= closestBlock)
{
continue;
}
flag = true;
}
if (regionSize + (int)mEMORYBASICINFORMATION.RegionSize < this.closestBlock)
{
flag = true;
}
byte[] numArray = new byte[(int)mEMORYBASICINFORMATION.RegionSize];
if (MemReader.ReadProcessMemory((int)this.processHandle, regionSize, numArray, (int)mEMORYBASICINFORMATION.RegionSize, ref num1))
{
int num2 = numArray.Search(new byte[] { 255, 255, 255, 127, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 253, 255, 168, 153 }, 0);
if (num2 > 0)
{
int num3 = regionSize + num2 + 32 + 4;
byte[] numArray1 = new byte[4];
if (MemReader.ReadProcessMemory((int)this.processHandle, num3, numArray1, (int)numArray1.Length, ref num1))
{
this.closestBlock = (int)Math.Round((double)BitConverter.ToInt32(numArray1, 0) / 268435456, 0) * 268435456;
this.closestBlocks.Add((int)Math.Round((double)BitConverter.ToInt32(numArray1, 0) / 268435456, 0) * 268435456);
return (IntPtr)BitConverter.ToInt32(numArray1, 0);
}
}
}
}
regionSize += (int)mEMORYBASICINFORMATION.RegionSize;
}
// This is just 0 or null
return IntPtr.Zero;
}
Code: Select all
#SingleInstance force
#Include <classMemory>
if (_ClassMemory.__Class != "_ClassMemory") {
msgbox class memory not correctly installed. Or the (global class) variable "_ClassMemory" has been overwritten
}
dqx := new _ClassMemory("ahk_exe DQXGame.exe", "", hProcessCopy)
if !isObject(dqx)
{
msgbox failed to open a handle
if (hProcessCopy = 0)
msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
else if (hProcessCopy = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
}
;; Base address of where DQX is
baseAddress := dqx.getModuleBaseAddress()
aAOBPattern := [255, 255, 255, 127, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 253, 255, 168, 153] ;; this is the array of bytes that tells us if a dialog box is open or closed
regionSize := 16777216 ;; this is a fixed size of the memory's region
value := dqx.processPatternScan(baseAddress, endAddress := 0x7FFFFFFF, aAOBPattern*)
value2 := regionSize + value + 32 + 4
msgBox % value2