Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is that possible to find dynamic adresses or allocate and inject ? #80

Open
aymericingargiola opened this issue Apr 8, 2021 · 3 comments

Comments

@aymericingargiola
Copy link

aymericingargiola commented Apr 8, 2021

I am building a run history tracker Electron app for The Binding of Isaac and actually i am parsing the logs but it miss a lot of informations (coins, damage, speed, bombs...) so i had the idea to read the game memory and then i found your project.

I am able to run memoryjs and it seems to work,

const memoryjs = require('memoryjs') const isaacProcessName = "isaac-ng.exe" const isaacExe = memoryjs.openProcess(isaacProcessName) console.log(isaacExe) const address = memoryjs.virtualAllocEx( isaacExe.handle, null, 0x60, memoryjs.MEM_RESERVE | memoryjs.MEM_COMMIT, memoryjs.PAGE_EXECUTE_READWRITE, ) console.log(Allocated address: 0x${address.toString(16).toUpperCase()})

{ dwSize: 304, th32ProcessID: 46544, cntThreads: 12, th32ParentProcessID: 37352, pcPriClassBase: 8, szExeFile: 'isaac-ng.exe', handle: 2908, modBaseAddr: 11206656 } Allocated address: 0x8A0000

But here is the tricky part... this game doesn't has static way to get information, so i found an cheat engine script that works and does :

`
This script dumps player structure into sPlayer, then show important offsets
like coins, keys, hearts, etc...
[ENABLE]
aobscanmodule(ReadMoney,isaac-ng.exe,FF B0 B0 12 00 00) // should be unique
alloc(newmem,$1000)
globalalloc(sPlayer,4)
label(code)
label(return)

newmem:
mov [sPlayer],eax
code:
push [eax+000012B0]
jmp return

ReadMoney:
jmp newmem
nop
return:
registersymbol(ReadMoney)

[DISABLE]

ReadMoney:
db FF B0 B0 12 00 00

unregistersymbol(ReadMoney)
dealloc(newmem)

{
// ORIGINAL CODE - INJECTION POINT: isaac-ng.exe+4AB53B

isaac-ng.exe+4AB512: 68 58 44 C3 00 - push isaac-ng.exe+7A4458
isaac-ng.exe+4AB517: 8D 45 98 - lea eax,[ebp-68]
isaac-ng.exe+4AB51A: 8B CE - mov ecx,esi
isaac-ng.exe+4AB51C: 50 - push eax
isaac-ng.exe+4AB51D: E8 DE 96 B5 FF - call isaac-ng.epoxy_handle_external_wglMakeCurrent+2F20
isaac-ng.exe+4AB522: F3 0F 10 45 80 - movss xmm0,[ebp-80]
isaac-ng.exe+4AB527: F3 0F 58 45 98 - addss xmm0,[ebp-68]
isaac-ng.exe+4AB52C: 8B 45 94 - mov eax,[ebp-6C]
isaac-ng.exe+4AB52F: 8B 0D 04 E4 C0 00 - mov ecx,[isaac-ng.exe+77E404]
isaac-ng.exe+4AB535: 81 C1 00 B7 01 00 - add ecx,0001B700
// ---------- INJECTING HERE ----------
isaac-ng.exe+4AB53B: FF B0 B0 12 00 00 - push [eax+000012B0]
// ---------- DONE INJECTING ----------
isaac-ng.exe+4AB541: F3 0F 11 45 A0 - movss [ebp-60],xmm0
isaac-ng.exe+4AB546: F3 0F 10 45 84 - movss xmm0,[ebp-7C]
isaac-ng.exe+4AB54B: F3 0F 58 45 9C - addss xmm0,[ebp-64]
isaac-ng.exe+4AB550: 51 - push ecx
isaac-ng.exe+4AB551: 68 A0 01 00 00 - push 000001A0
isaac-ng.exe+4AB556: F3 0F 11 45 A4 - movss [ebp-5C],xmm0
isaac-ng.exe+4AB55B: E8 70 60 01 00 - call isaac-ng.exe+4C15D0
isaac-ng.exe+4AB560: F7 D8 - neg eax
isaac-ng.exe+4AB562: BA C8 0F BA 00 - mov edx,isaac-ng.exe+710FC8
isaac-ng.exe+4AB567: B9 C0 0F BA 00 - mov ecx,isaac-ng.exe+710FC0
}
`

image

Then sPlayer is the right adresse every time
Is that possible to do this with memoryjs ? I search the whole evening and i'm a bit lost

Thank you

@dsasmblr
Copy link

dsasmblr commented Jan 26, 2022

Your question can be consolidated to this: "Can I use memoryjs to read the value in a register from an instruction located at a specific memory address?"

I don't think this library can do that at the moment; however, I think some of the core setup is there with the Debugger. A perhaps crude initial idea what this might look like in pseudocode, I see something as follows:

function getContext(address, accessType) {
    // Set BP on `address` to fire on `accessType` (read, write, or access).
    // When fired, capture data in registers (and maybe a stack snapshot of a specified size).
    // Detatch BP when finished so execution continues without a hitch.
    // Return the information that was captured
}

Then the usage might be something like this:

const procName = "isaac-ng.exe";
const procObj = memoryjs.openProcess(procName);
const address = memoryjs.processObject.modBaseAddr + 0x4AB53B;  // isaac-ng.exe+4AB53B

const ctx = memoryjs.getContext(address, "read");

const sPlayer = ctx.registers.eax; // sPlayer would now have the data from eax.

I don't mean to oversimply that or anything. I'm not sure what all might actually be involved with making something like this work between sync/async, hooking a specific instruction and capturing that subroutine's context on access, deciding on how to reference various register aliases (rax, eax, ax, ah, al), etc., etc.

Does this sound feasible to you at all, @Rob--?

@Rob--
Copy link
Owner

Rob-- commented Jan 26, 2022

@dsasmblr Pushed some WIP commit to a new branch get-registers (a470393). Seems like it is possible to return register values: when I view the registers in Visual Studio while debugging the target process they seem to align with the register values I see being returned here.

It includes a function to get register values arbitrarily:

const memoryjs = require('./index');

const processName = "testing.exe";
const processObject = memoryjs.openProcess(processName);

const address = 0x000000CC8A71FD20;
const ctx = memoryjs.getContext(processObject.th32ProcessID, address, memoryjs.TRIGGER_ACCESS);
console.log(ctx);

Produces the output:

$ node test
{
  P1Home: 2319,
  P2Home: 140708667870361,
  P3Home: 2449467113472,
  P4Home: 819450599993,
  P5Home: 2449495186944,
  P6Home: 2449495187152,
  ContextFlags: 1363828432,
  MxCsr: 570,
  SegCs: 8192,
  SegDs: 0,
  SegEs: 0,
  SegFs: 0,
  SegGs: 48784,
  SegSs: 20799,
  EFlags: 570,
  Dr0: 0,
  Dr1: 2449467696784,
  Dr2: 513,
  Dr3: 2449495187168,
  Dr6: 0,
  Dr7: 8192,
  Rax: 2449467155188,
  Rcx: 819450600400,
  Rdx: 0,
  Rbx: 1023,
  Rsp: 140694906068688,
  Rbp: 2449495120800,
  Rsi: 2449495195360,
  Rdi: 0,
  R8: 1,
  R9: 1024,
  R10: 2449495187152,
  R11: 2449467113472,
  R12: 140708667858611,
  R13: 2449467113472,
  R14: 2449467113472,
  R15: 2319,
  Rip: 1024,
  VectorControl: 0,
  DebugControl: 0,
  LastBranchToRip: 2449467680944,
  LastBranchFromRip: 0,
  LastExceptionToRip: 140694919478920,
  LastExceptionFromRip: 3
}

This context object is also now included with debug events:

{
  processId: 38804,
  threadId: 39180,
  exceptionCode: 2147483652,
  exceptionFlags: 0,
  exceptionAddress: 140701280637937,
  hardwareRegister: 0,
  context: {
    P1Home: 0,
    P2Home: 0,
    P3Home: 0,
    P4Home: 0,
    ....
  }
}

@dsasmblr
Copy link

dsasmblr commented Jan 28, 2022

@Rob-- Dude, thank you so much for cooking up that branch! Getting this pinned down will drastically reduce the need to scan for multilevel pointers beforehand to use within memoryjs scripts.

Okay, I pulled your branch down, compiled, etc. and I can't quite seem to get it to work correctly (which is probably due to my own error(s), lol).

PROBLEM: The main issue I'm having is that the breakpoint appears to immediately fire, thus pausing execution of the game until I kill the script running in the terminal. debugEvent comes back as null, which I've gathered is the expected result of no debug event occurring.

I parted out the getContext function you wrote and created a standalone script based on it so I could do some logging from within. Here's what I've got:

import * as memoryjs from "memoryjs";

function getContext(processId, address, trigger) {
    memoryjs.Debugger.attach(processId);
    const register = memoryjs.Debugger.setHardwareBreakpoint(processId, address, trigger, memoryjs.BYTE);

    const debugEvent = memoryjs.awaitDebugEvent(register, 1000);

    if (debugEvent) {
        memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId);
        memoryjs.Debugger.detach(processId);

        return debugEvent.context;
    }

    // console.log({debugEvent})

    // memoryjs.Debugger.detach(processId);
}

const processName = "game.exe";
const process = memoryjs.openProcess(processName);

const baseAddress = process.modBaseAddr;
const address = baseAddress + 0x25C49CA;

const ctx = getContext(process.th32ProcessID, address, memoryjs.TRIGGER_EXECUTE);

console.log(ctx);

I'm using TypeScript, so I made some slight temporary modifications on my end like removing the callback param in the function definition and the block related to it, etc. This also means I'm using ts-node to run scripts in the terminal (ex. ts-node debugger.ts) which I don't believe makes a difference, but noting it just in case you've heard of any issues.

Anyway, from within the function definition, I've logged all the arguments I've passed to it to verify everything is good there.

debugEvent is always null when I log it anywhere in the function, thus execution never flows through the if block. Adding in the memoryjs.Debugger.detach(processId); after the if block was just to verify that, with it, execution will continue normally after running the script as opposed to having to manually kill it for the game to resume execution after the breakpoint is hit.

Grasping at straws, I've tried changing the trigger to ACCESS, WRITE, EXECUTE, and a few other memory flags by their manual codes (like 0x10, as defined via the src/consts.js file) just to see what would happen since this is an instruction in an address that gets executed (I've checked via Cheat Engine that the address's default permissions are read and execute).

The instruction, specifically, is: movsd xmm5,[rbx+128]

I experience the same behavior of the breakpoint activating. Accordingly, the result of console.log(ctx); is always undefined.

Finally, I tried rewriting a version of the script using the manual example provided here: https://github.com/Rob--/memoryjs/blob/master/examples/debugging.js

The result is the same.

I'm at a bit of a loss at the moment as to what to try, so I figured I'd chime in with what all I've tried and see what you think. When I set a breakpoint via Cheat Engine just to make sure that it doesn't activate until I do something in the game that would make execution flow through that instruction, it works properly.

Thanks for your time, Rob!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants