- Their injectors are 95% copy-pasted from blackbone/xenos
- Hardly anyone who did not bother with those annoying anti-cheats like XTrap, XignCode, doesn't even know that manual mapping exists or why to use it
Also, while lurking the S4 League section, I notice ppl don't have a clue about this topic either.
(which maybe has emerged because of the perpetual existence of a public full-bypass
and because of the incompetence of the developers forgetting to implement the serverside alive-check lolz)
Concept
What manual mapping really is, is just emulating the behavior of LoadLibraryA/W and/or the windows pe loader.
As you may know, LoadLibrary is just a "wrapper", exposing many internal loader APIs.
This means anti-cheats can hook LoadLibrary itself or one of the elementary functions called inside of it and, thus,
find out that something's gonna get injected and what is gonna get injected and take appropriate action.
Now, to know what to emulate, you need to know what LoadLibrary is really doing.
After a quick look at MSDN, you may notice that it says "The specified module may cause other modules to be loaded".
This recursive approach needs to be taken into account later, e.g. if your DLL needs other dependencies to work in the target process.
So, it roughly explains that LoadLibrary "loads" the module, specified by an absolute or relative path, into the calling process.
What loading practically means:
- Use the path to open a handle to the library file (DLL)
- Using the handle, read the contents of the file into the injector's process' address space
- Optionally check if the image is really a DLL file by considering the characteristics field in the PE header
- Allocate space for the image and the shellcode which will call the entrypoint and resolve some stuff (in-depth later)
- Copy the whole image into the allocated space
- Copy the shellcode into the other code-cave and execute it
Since some of the loader's internal APIs were not called, another advantage is that the image is not registered in the PEB's module lists.
The reason that you need to additionally use shellcode is that you could theoretically just create a remote thread with
the start address of your dll entry point but you may only supply one argument.
The shellcode takes care of resolving the imports or more precisely the import address table entries. Your image uses a particular table
in memory which contains pointers to APIs like from NTDLL or KERNEL. There is no guarantee that those are loaded at the same place everytime.
As well, those entries are RVAs instead of VAs in case your image is not loaded at default imagebase.
That's why you will have to manually rebuild the IAT which is pretty easy:
- Easy way: Load dependency with LoadLibraryA (hopefully anti-cheat doesn't detect LoadLibrary when loading windows dlls)
and then parse its export table with GetProcAddress. (hopefully ac doesn't detect GetProcAddress :P) - Not soo easy way: This is a little bit costlier but still easy. Recursively call your mapping code again, applying the conquer and divide principle, for every dependency + emulate GetProcAddress (it's just parsing the dependency's export table)
Moreover you will have to resolve the relocation table. Relocations are just locations in code that use absolute virtual addresses.
In this case you need to add the difference between the assumed imagebase and the real imagebase to each entry.
This difference is always relative to the assumed imagebase, hence it can also be negative, causing a subtraction.
(That is because the relocations are also relative to the assumed imagebase and not to the real imagebase)
At the end of the shellcode it will call the entry point of the valid, mapped module.
Code examples (snippets)
Open & read file on disk
Check if valid DLL image
Copy file contents to image
Fix relocations