Made with C#, Unity
Over the past several years, I've programmed mods for a lot of video games, especially for games made with the Unity game engine. I actually started programming because I wanted to mod my all-time favorite game, RimWorld. It just so happens that Unity games are quite easy to decompile into readable code using tools like IlSpy and DnSpy (thanks C#!). Unity is also one of the most popular game engines and has a great modding community. Tools like Harmony 2 and C#'s reflection support make it possible to create powerful mods without much pain.
A couple years ago, I started to do some game hacking, starting with Battlefield 4. It was a fun way to learn some low-level programming concepts like working with pointers, reading and writing memory of other processes, and locating data structures in memory.
A great tool that I use often is Cheat Engine. It's a tool that lets you scan a program's memory for values, find data structures in memory, and write and inject your own assembly scripts into the program's code.
This is a fun workaround I learned recently. Below is an assembly script that is going to be injected into the game's code, in the middle of a function that handles setting the value of the player's shield strength.
newmem: pushfd cmp [ebp+2C],esi jne shieldRegen jmp healthRegen shieldRegen: cmp [ebp-C],esi jne cleanup cmp [ebp-8],esi jne cleanup cmp [ebp+58],1F jne cleanup subsd xmm0,[esi] mulsd xmm0,[shieldRegenMult] addsd xmm0,[esi] jmp cleanup
The injected script is supposed to double the shield recharge rate by multiplying the difference between the new value and the current value by two before updating the current value. The problem is that the original function also handles changing several other values. So if the script is injected directly, it may double values other than the shield strength. This is very dangerous because we don't know what those other values are. Changing a value that is not supposed to be changed could crash the game or even corrupt your save file.
The comparisons using
cmp are actually for "detecting" when the function is modifying shield strength and not some other value.
I use Cheat Engine to add a breakpoint in the assembly code the game executes when shield strength changes.
Then I look at the values on the stack at that point in time and try to find any unique patterns in the values.
cmp [ebp-C],esi will check if the value of
esi is equal to the value on the stack at offset
If not, then we know the current value being changed is not shield strength.