Rob-- / memoryjs

Read and write process memory in Node.js (Windows API functions exposed via Node bindings)
MIT License
636 stars 88 forks source link

How does one even use this? #44

Closed MelerEcckmanLawler closed 5 years ago

MelerEcckmanLawler commented 5 years ago

Trying to get the hang of this before moving on to doing what I originally set out to, but can't even get a simple test to work. I've got a notepad window open with the text "Hello World!" and I'm trying to find it with this code:

const memoryjs = require('memoryjs');
const processes = memoryjs.getProcesses();

var p;

for (let i = 0; i < processes.length; i++) {
  if (processes[i].szExeFile == 'notepad.exe') {
    p = memoryjs.openProcess(processes[i].th32ProcessID);
    }
  }
}

addr = memoryjs.findPattern(p.handle, 'notepad.exe', /^H/, memoryjs.STRING, 0, 0);
txt = memoryjs.readMemory(p.handle, addr, memoryjs.STRING);
console.log(txt); // outputs 'MZ', but should output 'Hello World!'

MZ is apparently the magic byte identifying a DOS executable, so I have a feeling it's getting this string from the actual notepad.exe file in memory. Not what I want.

Rob-- commented 5 years ago

Just do const p = memoryjs.openProcess('notepad.exe');

And also I'm not really sure what you're trying to do with the /^H/ in findPattern. The signature argument for findPattern is a string in the form of XX ? ? XX ? where XX are known bytes and ? are unknown bytes.

In this case you would need to find where notepad stores the text displayed in memory and read memory from that address.

MelerEcckmanLawler commented 5 years ago

The /^H/ was supposed to be a regular expression to find strings beginning with H, but I see now the pattern function doesn't expect a regular expression.

I am trying to find the memory address where notepad stores displayed text by searching the entire memory of notepad for the string that matches my pattern. Here is my new code:

const memoryjs = require('memoryjs');

const p = memoryjs.openProcess('notepad.exe');
const sig = '48??????????21'; //'Hello World!' -> '48656C6C6F20576F726C6421' -> '48??????????21'
const addr = memoryjs.findPattern(p.handle, 'notepad.exe', sig, memoryjs.READ, 0, 0);
const txt = memoryjs.readMemory(p.handle, addr, 'string');

console.log(txt); // should show 'Hello World!' but actually shows nothing or gives the error: 'TypeError: unable to read string (no null-terminator found after 1 million chars)'

My open notepad window contains the text Hello World! and I thought my code could find it. But apparently not?

I'm trying to do what CheatEngine does, searching for memory addresses within a process which contain a certain string. Isn't memoryjs capable of that? If not, then I am disappointed.

UPDATE: Changing the signature type to memoryjs.NORMAL gives me the string MZ� which is the original problem I had in the first place. Clearly the signature is being totally ignored, because MZ� are magic-bytes which definitely don't match the pattern 48??????????21.

UPDATE: Seems the signature must have a space before and after each question mark, because after adding spaces it's now returning something that does match the signature. It's not what I'm looking for though, it's some nonsesnse (H�H���!) but I will continue experimenting.

UPDATE: Neither the signatures 48 ? ? ? ? ? ? ? ? ? ? 2121 nor 48 ? ? ? ? ? ? ? ? ? ? 21 21 nor even the exact, full string 48656C6C6F20576F726C642121 are able to find the displayed text Hello World!!. They just give the following error:

TypeError: unable to read string (no null-terminator found after 1 million chars)

Why is CheatEngine able to find the string but not memoryjs?

Rob-- commented 5 years ago

I see what is happening. I'm not gonna lie, I don't know much about text and unicode. But from what I see, it seems that notepad.exe stores text with Utf16 encoding (I guess this means each character takes up 2 bytes instead of 1 byte which memoryjs is expecting).

Take a look at the following image: Cheat Engine

As you can tell, the text Hello world! in hex is 48 65 6c 6c 6f 20 77 6f 72 6c 64 21. Now, I'm guessing Notepad stores text as Utf16 because in memory Cheat Engine is telling us the text appears as 48 00 65 00 6c 00 6c 00 6f 00 20 00 77 00 6f 00 72 00 6c 00 64 00 21 which implies each character is taking up 2 bytes.

Therefore, if you alter your signature to have 00 after each byte, you will be able to find the correct address. However, my implementation of reading a string in memory causes the readMemory function to fail when it encounters a 00 byte (null character/string terminator) because the text is expected to be Utf8. To counter this, this section of code would need to be altered to support Utf16 strings. This also means a parameter would need to be passed when reading strings from memory to flag whether the string is Utf8 or Utf16 (or even higher).

MelerEcckmanLawler commented 5 years ago

That makes sense, thank you very much!