Okay, so just about everyone knows how to read/write from/to textfiles, but you have to save the file first, then provide some sort of link to the file in your program before you can interact with it. But how about reading from it live? What if you want to record the contents of your Notepad instance as you type into it?

Why bother?

Honestly, I can’t think of any real world examples off the top of my head in which something like this would come in handy, but I thought it was fun, and it’s a good exercise in memory.

Also, no one said you had to read from Notepad. I read my health value from the game Overwatch here: 7

Reading Memory

If we want to read from Notepad real-time without saving, then surely we will need to read from notepad.exe’s memory. This will be the main topic of this post.

If you’re using C++, then you will want to look into ReadProcessMemory()

I’ll be using C#, in which there are plenty of libraries to help you accomplish this. I’ll be using VAMemory.dll because it’s light-weight and does exactly what we need.

Setup

The first thing we need to do is target the process, notepad.exe. We will create a VAMemory object to do this. This will check to see if the process is active.

VAMemory vam = new VAMemory("notepad");
bool check = vam.CheckProcess();
if (check == true) {
	// do stuff  
}
else {
	Console.ForegroundColor = ConsoleColor.Red;
	Console.WriteLine("Notepad not found.");
}

Now when we run our program, if notepad.exe is not running then we will get an error.

1

Memory addresses and relocation

In order to read directly from Notepad’s memory, we will need to find the location in memory in which our notepad.exe process resides. Not only that, but we also need to find, within Notepad’s memory, the location in which Notepad itself stores the beginning of the user’s input.

Memory locations are generally expressed in hexadecimal format. The location in which the Notepad process resides/begins is typically referred to as the base address. VAMemory can find the base address with the following code:

IntPtr baseAddr = (IntPtr)vam.getBaseAddress;
string hexValueBaseAddr = baseAddr.ToString("X");
Console.WriteLine("base address = 0x" + hexValueBaseAddr);
  • The first line grabs the base address and casts it as a pointer.
  • The second line converts the base address to a string in hexadecimal format.
  • The third line prints the address in the format 0xADDRESS

So let’s give this a spin… open notepad and then run the program. 2

…restart notepad and try it again just for good measure.

3

Wait, what the heck? Why did the base address change? In fact, it changes every time you restart Notepad!

Enter Relocation. Relocation will ensure that the base address changes each time we start Notepad. This is why a function exists to calculate the base address.

Finding text start offset

We found the base address, but we can’t just read directly from that address, that would be silly and would imply that a single memory address holds the entirety of Notepad’s text input.

What do we know about this text location? Well we know where Notepad’s base address is, and that’s where the processes’ allocated memory starts. That means the location of where the text input is will be baseAddress + (some offset).

In other words, the location of our address will be the base address offset by some value. This value is static, unlike our pesky base address. The easiest way to find this offset is to use a memory scanner or debugger of some sort. I used Cheat Engine.

I typed a char into Notepad, scanned for it, and did some filtering. In this instance of notepad, I found the address of the text start location to be 0x0045B490. I mentioned earlier that the offset is static, but that doesn’t mean this address is static!

Remember that this address is equal to the baseAddress + offset, and baseAddress is dynamic! We need to find the offset, I found it using a pointer scan, it turns out to be 0x0x00100E8.

That means the address that holds the first char in Notepad is baseAddress + 0x00100E8.

Examining Notepad

While searching for the textStart address, I typed a D into Notepad as the first and only character, and found the following:

5 4

Our memory viewer is telling us there is a 44 as the first char, but we have a D as the first char. Well it turns out 44 = D!

6

We can change this value directly and see the results, J in hex = 4A, so we will try changing the 44 to 4A:

7

Experimenting further, it turns out that each char is separated by 2 bytes in memory. So if the first character is stored at location baseAddress + offset, then:

  • The 2nd char is stored at baseAddress + offset + 2
  • The nth char is stored at baseAddress + offset + 2*n

This post is getting quite long, so I’ll cut to the chase. With this knowledge, we can set up a simple loop that runs continuously, updating until reaching a null char.

while (true)
{
	prevText = text;
	string output = vam.ReadStringUnicode((IntPtr)txtStart, 2);
	if (!output.Contains((char)0))
	{
		text = text + output;
		txtStart += 2;
	} 

	if (prevText != text)
	{
		Console.Clear();
		Console.WriteLine(text);
	}
}

Here’s what it looks like! 8

There are some caveats I didn’t cover with whitespace, null chars, and also the address of text input mysteriously relocating after a specific amount of chars, but I figure this post has gone on long enough.

You can do some other cool things using the same concepts applied here, for example, using the GDI+ library to draw an overlay on top of a 3D application, here is an ammo counter being overlayed on top of the game Overwatch:

9

Thanks for reading.