The intricacies of a cheat loader
A loader is arguably the most important component of a cheat provider, but what distinguishes one made by a hobbyist from that made by a pay-to-cheat provider? In this blog post, we'll take a look at some of the practices p2c providers use to ensure the security of their product.
Intro
Hot summer hello.
In the recent months I've noticed an increase of posts inquiring about loader, security and similar and I wanted more people to see my work, so I decided to make this post to shed some light on what more advanced loaders do in order to protect their product.
In general, when we talk about a loader in this particular sense, I mean a client application that connects to a remote server, authenticates a user, performs manual mapping on the module (typically a .dll PE file) and streams it to the client with imports already resolved and relocations already performed. This works quite well for a cheat provider because it ensures that the cheat works for this particular boot (as addresses of the imports will change upon reboot due to ASLR) and requires the attacker to understand the mapping process in order to be able to reconstruct the image.
This technique is commonly known as "servermapping", which is what the CS2 cheat Illusionary used in our previous blog.
For the sake of saving time - I won't go into much detail about what each item we'll talk about represents.
We're now going to talk about the common approaches seen in the wild, their attack vectors and some possible mitigations of those attack vectors.
Imports
Simply put, when it comes to imports, the "small fish" tend to walk the PE Import directory and for each IMAGE_IMPORT_DESCRIPTOR, read the OriginalFirstThunk and save the RVA (Relative Virtual Address) of the IAT slot that needs to be patched. After that, the server patches the IAT slots with the absolute addresses it calculated (AllocationBase + SavedRVA).
The client then has already resolved actual runtime addresses for every function.
The offense #1
Relocations
When handling the relocations, two things tend to happen at once:
Strip: When iterating sections for parsing, if the section name is .reloc, the server zeroes out all bytes at that file offset, this means that the data sent to the client has no relocation table.
Parse: Separately, the server reads the Base Relocation Directory from the mapped image (before zeroing hits the mapped copy). It walks IMAGE_BASE_RELOCATION blocks and the final RVA stored is block.VirtualAddress + (entry & 0xFFF)
For every stored reloc RVA, it reads the value at that offset in the mapped image and adds a delta which gets calculated like so
delta = new_base - preferred_base
You may ask, why does this matter? It makes the image reconstruction harder since the attacker dumping the mapped image has no reloc table to rebase from.