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
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,
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
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.
Programs can place code and data segments at random offsets.
This means that the addresses for the values you find using the
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
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.
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
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
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.
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
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.