Proctal

Documentation

Modding games

You're going to learn a couple of techniques for creating mods with the command line interface of Proctal. This is not meant to be an extensive tutorial on modding but it covers enough concepts so that you get a general idea of how this works.
Games are just programs that run on a computer, and all programs load code and data into memory. With the ability to modify the contents in memory, you're able to change the behavior of a game however you want.
The examples access a program whose Process ID (PID) is 12345 on x86-64 Linux.

Finding known values

Suppose that the game shows you that you have 100 health points. How would you go about searching for that value in memory? First you must guess in what form that the programmers decided to store it. It could be stored as an integer or IEEE754 floating-point number. If the game does not use commas to represent health points, it's most likely using an integer. Now you must guess how many bits it's using to represent that integer. The most common number of bits for integers is 32, so that's a first good guess.
You can search for values in memory by using the search command. Here's how you search for every 32-bit integer with the value 100:

$ proctal search --pid=12345 --type=integer --integer-bits=32 --eq=100
7FCDB1D56638 100
7FCDB1D567A8 100
7FCDB1D571B0 100
7FCDB1D577F8 100
7FCDB1D57F38 100
7FCDB1D58738 100
7FCDB1D58BA8 100
7FCDB1D58DA8 100
7FCDB1D58FA8 100
7FCDB1D591A8 100
7FCDB1D593A8 100
7FCDB1D595A8 100
[...]

On each line you get an address and the current value on it.
It's not uncommon to get thousands of lines because there can be many other things with the same value in memory.
There is a way to filter out the noise. Start by saving the output to a file. Here's the same command from before but now redirecting the output to a file called results1.

$ proctal search --pid=12345 --type=integer --integer-bits=32 --eq=100 > results1

Using the wc program you can count how many matches you got.

$ wc -l < results1
3463

In this example there were 3463.
A common strategy for reducing the number of matches is to cause actions in the game that can change the value. The search command has the --review option that allows you to match against the results from a previous search, so you're able to filter out the addresses that do not contain the new value.
Assuming that your actions in game changed the value to 58, here's how you would match against the results stored in results1 and store the new results in results2:

$ proctal search --pid=12345 --type=integer --integer-bits=32 --eq=58 --review < results1 > results2

And counting yields:

$ wc -l < results2
34

Down to 34. That's much easier to handle. You can repeat the previous step until you either end up with 1 value or you're unable to shorten the results any further.
You can also use the watch program to see how the values change as you're playing the game:

$ watch proctal search --pid=12345 --type=integer --integer-bits=32 --eq=58 --review '<' results2
Every 2.0s: proctal search --pid=12345 --type=integer --integer-bits=32 --eq=58 --review < results2
7FCDB1D577F8 58
7FCDB1D57F38 58
7FCDB1D58738 58
7FCDB1D58BA8 58
[...]

If your guesses were right you should be able to pick the right address from the search results. If you get no results then you need to rethink about how the value might be stored in memory. Thinking like a programmer will help you guessing better.
Sometimes programmers store copies of a value in different places in memory. You need to pick the source of the copies. It's usually the one that when modified updates the others.
Read more about searching values here.

Finding unknown values

Suppose that the game displays a health bar instead of health points. How can you search for the address of a value that you don't know?
You can look for all possible values of a given type and then cause actions in the game that change the value. In this case when you gain health points you can assume that the value increases and when you lose health points the value decreases.
Using the --increased and --decreased options, you can gradually reduce the number of matches.

$ proctal search --pid=12345 --type=integer --integer-bits=32 > results1
$ wc -l < results1
172341
$ proctal search --pid=12345 --type=integer --integer-bits=32 --increased --review < results1 > results2
$ wc -l < results2
41346
$ proctal search --pid=12345 --type=integer --integer-bits=32 --decreased --review < results2 > results3
$ wc -l < results3
6121
[...]

While you may not always be able to shrink the search results to a single match, this greatly reduces the number of addresses you would have to manually check.

Reading and modifying values

By knowing the address of a value in memory you are able to read and write to it by using the read and write commands, respectively.
Assuming that 55B4AA941048 is the address where the game stores your health, this is how you would check its current value:

$ proctal read --pid=12345 --type=integer --integer-bits=32 --address=55B4AA941048
58

And this is how you would replenish your health back to 100:

$ proctal write --pid=12345 --type=integer --integer-bits=32 --address=55B4AA941048 100

Check its value again and you should see that it has changed to 100:

$ proctal read --pid=12345 --type=integer --integer-bits=32 --address=55B4AA941048
100

Freezing values

What if instead of having to manually set your health points back to 100 you could just make the game unable to change it?
You can sort of achieve this by passing the --repeat option to the write command. Assuming that the address for your health points is 55B4AA941048, here's what the command would look like:

$ proctal write --pid=12345 --type=integer --integer-bits=32 --address=55B4AA941048 --repeat 1000

What the command will actually do is repeatedly write the same value over and over again, making it seem like the value is never changing when actually any changes that the game attempts to make are just overwritten quickly. The command will keep executing indefinitely until you explicitly terminate it.
The game may sometimes modify and access the value faster than the command can write to it again, though.

Rewriting code

Programs can place code and data segments at random offsets. This means that the addresses for the values you find using the search command become useless after you close the program. You would have to search for them again every time you start the program.
While data is very volatile in memory, code tends to be static which makes it very easy to search for. If instead of needing to modify values, you modify the instructions that access those values then you only have to search for code.
The watch command can track reads and writes to a memory address. After it detects that an instruction accessed the memory address, it prints out the address of the instruction that would be executed next. Even though it's not the actual instruction that accessed the memory address, it's still useful information.
Suppose that the address where the game stores your health points is 55B4AA941048. Here's how you track for reads and writes on that address:

$ proctal watch --pid=12345 --read --write --unique 55B4AA941048
55B4AA7406AB
55B4AA7406B6

The --unique option is used so that the watch command does not print the same address twice.
Now when you lose health in the game, more addresses should show up:

$ proctal watch --pid=12345 --read --write --unique 726BA1DA
[...]
55B4AA740676
55B4AA740682
55B4AA740688

The instruction that decreases the value must be located nearby those addresses that showed up. You need to examine the code around those areas. One way to do this is by using gdb which supports breakpoints, disassembling instructions and reading registers.
Here's a gdb session where a breakpoint is set at address 55B4AA740676, then when the breakpoint is hit the surrounding instructions are inspected and the value of a register is printed:

$ gdb --pid=12345
Attaching to process 12345
(gdb) break *0x55B4AA740676
(gdb) continue
Continuing.

Breakpoint 1, 0x55b4aa740676
(gdb) layout asm

    0x55b4aa740670         mov    eax,DWORD PTR [rip+0x2009d2]
  > 0x55b4aa740676         mov    rdi,rbx
    0x55b4aa740679         sub    eax,0x1
    0x55b4aa74067c         mov    DWORD PTR [rip+0x2009c6],eax
    0x55b4aa740682         mov    esi,DWORD PTR [rip+0x2009c0]

(gdb) print $eax
58

There seems to be a mov instruction that loads a value from memory to the register eax, then a sub instruction substracts 1 from that value and finally another mov instruction stores the new value to that address. If the eax register contains the current amount of health points then this is the code that is decrementing the value. Everything else seems irrelevant.
But before you make any changes to the code, store a copy of the bytecode. You will need this later to find the code again.
The read command can also disassemble instructions and supports the --show-bytes option that makes it print the bytecode underneath each instruction.

$ proctal read --pid=12345 --type=x86 --address=55B4AA740676 --array=4 --show-address --show-bytes
55B4AA740676	mov     rdi, rbx
        48 89 DF
55B4AA740679	sub     eax, 1
        83 E8 01
55B4AA74067C	mov     dword ptr [rip + 0x2009c6], eax
        89 05 C6 09 20 00
55B4AA740682	mov     esi, dword ptr [rip + 0x2009c0]
        8B 35 C0 09 20 00

To prevent the game from decreasing health points you must prevent that sub instruction from executing.
If you were to actually remove the instruction, you would have to relocate every instruction coming after it and recalculate the addresses that might be used by the instructions. This would be a costly operation.
But you don't have to.
You can instead overwrite the sub instruction with nop instructions. There is a variety of nop instructions but the most commonly used one takes 1 byte.
That particular sub instruction takes 3 bytes, so you have to write 3 nop instructions over it.

$ proctal write --pid=12345 --type=x86 --address=55b4aa740679 --array=3 'nop'

The game's code would look like this:

$ proctal read --pid=12345 --type=x86 --address=55B4AA740676 --array=6 --show-address
55B4AA740676	mov	rdi, rbx
55b4aa740679	nop
55b4aa74067A	nop
55b4aa74067B	nop
55b4aa74067c	mov	DWORD PTR [rip + 0x2009c6], eax
55b4aa740682	mov	esi, dword ptr [rip + 0x2009c0]

Now the game will not decrease your health points anymore.
You also could have made the game increase your health points by replacing the sub instruction with an add instruction, which also takes the same number of bytes.

$ proctal write --pid=12345 --type=x86 --address=55b4aa740679 'add	eax, 1'

The game's code would look like this:

$ proctal read --pid=12345 --type=x86 --address=55B4AA740676 --array=4 --show-address
55B4AA740676	mov	rdi, rbx
55B4AA740679	add	eax, 1
55B4AA74067C	mov	DWORD PTR [rip + 0x2009c6], eax
55B4AA740682	mov	esi, dword ptr [rip + 0x2009c0]

Now close and start the game. You're going to use the pattern command to find the new address of the code. You must build a pattern out of the original bytecode. You can just copy and paste, however, you can also use ?? (question marks) to match any byte. This allows you to ignore volatile instruction operands, such as addresses which increases the chance of the pattern working on different versions of the game.
Here's an example that matches the code exactly as it is in memory:

$ proctal pattern --pid=12345 '
        48 89 DF
        83 E8 01
        89 05 C6 09 20 00
        8B 35 C0 09 20 00
55B24157B676

And here's how you would be able to find the code again even if the addresses of the last two instructions change in a new version of the game:

$ proctal pattern --pid=12345 '
        48 89 DF
        83 E8 01
        89 05 ?? ?? ?? ??
        8B 35 ?? ?? ?? ??'
55B24157B676

There you have it, now the code is at address 55B24157B676.

$ proctal read --pid=12345 --type=x86 --address=55B24157B676 --array=4 --show-address
55B24157B676	mov     rdi, rbx
55B24157B679	sub     eax, 1
55B24157B67C	mov     dword ptr [rip + 0x2009c6], eax
55B24157B682	mov     esi, dword ptr [rip + 0x2009c0]

You can apply your changes again using the new address.

Injecting code

In the previous example it just so happened that the modification could fit into the existing space but what if it didn't? You can instead redirect code execution to a different block of memory that you have more control of.
You can use the allocate command to allocate new blocks of memory. This is how you allocate 1000 bytes with read, write and execute permissions:

$ proctal allocate --pid=12345 --read --write --execute 1000
7F6E0F750008

Here 7F6E0F750008 is the address where the new block starts.
Suppose that you want to count how many health points you would have lost. You can store the counter as a 32-bit integer in memory. You can use the first 4 bytes of the memory block for that.
Here's the code from the previous example again.

$ proctal read --pid=12345 --type=x86 --address=55B4AA740676 --array=4 --show-address
55B4AA740676	mov	rdi, rbx
55B4AA740679	sub	eax, 1
55B4AA74067C	mov	DWORD PTR [rip + 0x2009c6], eax
55B4AA740682	mov	esi, dword ptr [rip + 0x2009c0]

One way to redirect execution is to place a jmp instruction. You have to place the address of the code you want to execute in a register. You can use the rax register seeing as its holding the number of healths points that will be decremented by 1 which you no longer want to happen. Here's how the code for the jump instruction looks like:

mov	rax, 0x7F6E0F75000C
	jmp	[rax]

You can use the measure command to figure out how much space this takes up.

$ proctal measure --type=x86 --address=55B4AA740676 "mov rax, 0x7F6E0F75000C" "jmp [rax]"
12

12 bytes. That means that if you were to place this code at 55B4AA740676, you would overwrite all the instructions up to 55B4AA740682. While it does overwrite some instructions that you don't want to execute, it also overwrites one that is needed to keep the program running correctly. When this happens you can move the instructions you want to preserve into the memory block.
The code in the memory block will be made up of three sections. The first section increments the health point counter. The second section contains the instructions that were overwritten. The third and last section returns control back to the program.
The code will have to be placed at 7F6E0F75000C because, remember, the counter is stored at 7F6E0F750008.

$ proctal write --pid=12345 --type=integer --integer-bits=32 --address=7F6E0F750008 0
$ proctal write --pid=12345 --type=x86 --address=7F6E0F75000C \
	'mov	rax, 0x7F6E0F750008' \
	'add	DWORD PTR [rax], 1' \
\
	'mov	rdi, rbx' \
\
	'mov	rax, 0x55B4AA941048' \
	'jmp	rax'

With the code written to the memory block, you can now redirect execution to it.

$ proctal write --pid=12345 --type=x86 --address=55B4AA740676 \
	'mov	rdi, 0x7F6E0F75000C' \
	'jmp	rdi'

Now you can check how many health points you would have lost by reading the 32-bit integer at address 7F6E0F750008.

$ proctal read --pid=12345 --type=integer --integer-bits=32 --address=7F6E0F750008
548

When overwriting code, you need to make sure that the code is not being referenced by other instructions, such as jump instructions, otherwise you risk having the program jump to the middle of the bytecode of a single instruction which will result in the CPU misinterpreting every instruction it then come across.
You also need to be careful with what you leave in the registers and in memory. If you were to increase the stack space and then forget to decrease before returning control back to the program, you risk having the program read the wrong return address from memory that could make the program jump to some random block that could potentially have instructions that hopefully lead to a crash instead of corrupting data.

Scripting

Being able to create mods out of text command sounds very exciting but it's be very time consuming to have to type all those commands every time you want to activate a mod. And unless you take notes you will eventually forget the thought process that you went through when those commands commands were written.
The command line interface of Proctal can be scripted very easily. Here is a bash script that does everything that was described in the previous section. When it finishes it prints the address of the health point counter that you can then read from.

#!/bin/bash

pid="12345"

# Look for the address where we want to redirect program execution to our code.
# We're only expecting a single address but in case more show up, use the
# first one.
inject_address="$(proctal pattern --pid="$pid" '
        48 89 DF
        83 E8 01
        89 05 ?? ?? ?? ??
        8B 35 ?? ?? ?? ??' | head -1)"

# Allocate new block with enough space to store our code.
new_block_address="$(proctal allocate --pid="$pid" --read --write --execute 1000)"

# The return address. Calculated from the injection point.
return_address="$(printf "%X" $(("0x$inject_address" + 12)))"

# The address for the health point counter. It's located at the first address
# of the new block.
counter_address="$new_block_address"

# The address where the code is located. It's located after the counter.
code_address="$(printf "%X" $(("0x$counter_address" + 4)))"

# Initialize the counter to 0.
proctal write --pid="$pid" --type=integer --integer-bits=32 --address="$counter_address" 0

# Write out the code. Notice that it's separated in 3 sections.
# The first section increments the counter.
# The second section contains the code that was overwritten.
# The third section returns control back to the program.
proctal write --pid="$pid" --type=x86 --address="$code_address" \
	"mov	rax, 0x$counter_address" \
	'add	DWORD PTR [rax], 1' \
\
	'mov	rdi, rbx' \
\
	"mov	rax, 0x$return_address" \
	'jmp	rax'

# Redirect execution to our code.
proctal write --pid="$pid" --type=x86 --address="$inject_address" \
	"mov	rax, 0x$code_address" \
	'jmp	rax'

echo "$counter_address"

Shell scripts come with a lot of advantages.
They do not require manual intervention. As you can see, it's possible to store the address of the new block in a variable and use it in the code. It even makes the code more readable.
Being able to write comments lets you document what the commands are doing right on the spot. Good comments can help you make changes quicker in the future.