Atmosphere-libs@0d161b8588 | ||
libvmod | ||
vax | ||
vboot | ||
vloader | ||
vtest | ||
.gitignore | ||
.gitmodules | ||
LICENSE | ||
Makefile | ||
README.md |
vax
vax
is a custom module/code injection ecosystem for Nintendo Switch games.
It's essentially a C++ port/improved version of SaltyNX.
Code injection procedure
vax (sysmodule)
This is the basic component of this project:
-
On the one hand, it periodically scans for new-started processes.
If the new process is an application and the SD card contains modules to load for that specific game, it hooks the game and starts with the injection process (explained below)
-
On the other hand, it hosts three separate ports:
-
vax:mod
: this port interface is used byvboot
,vloader
and any other custom module. It serves two main purposes:-
Acting as a privileged service manager: the
GetServiceHandle(...)
command it serves is similar to SM's one as it actually does that internally, but through this sysmodule's full permissions.- For services like FS, which have their own permission system which works by calling a certain command after accessing it, vax calls it by itself, returning to the module a FS service with full permissions.
-
Allowing various funcionality specific to modules: currently only consists on the
MemoryCopy(...)
command, which works similar to a usualmemcpy
but allowing addresses the module can't directly interact with.
-
-
vax:boot
andvax:ldr
, port interfaces only meant to be accessed fromvboot
andvloader
respectively. -
As a note, SaltyNX would previously serve all these purposes under the
SaltySD
port and through a more primitive and unsafe IPC code. It'd also only provide SD card filesystem handles, while now this whole concept was redesigned to have whole full privileged services.
-
Resource limits and ports
Note that games tend to have (or just always have?) a maximum amount of ports which can be opened of 1
, which is meant to open SM and regularly access other services.
Thus, for all the ports which are hosted and accessed through this project and custom modules, the procedure is always to open-port/call-desired-command/close-port, so that after calling the desired command the single port session available is not used by anything, all this before the game starts so the ports can be accessed one-by-one.
Another consequence of this is that custom modules (through replaced funcions being called later after the game starts) can no longer access vax:mod
, since games seem to (always do?) leave their SM session open, not allowing any more ports to be accessed. Thus, anything they might want to obtain from the port (like SD card access) must be done on the module entrypoint.
vboot
When a process injection starts, vax
finds random code regions in the game code and overwrites it with vboot
's segments, always after having made a backup of the original game code there. Then vboot
is started by adjusting the process's main thread's PC.
Since vboot
was badly placed in the game's memory (we randomly pasted it over game memory), its main purpose is to setup a basic heap through SetHeapSize
(note that the game didn't even get to start so nothing was set up) where the actual injection entrypoint code, vloader
, will be placed and executed.
After setting up this basic heap (which vloader
will later extend to fit all modules to be loaded), vboot
loads vloader
there through vax:boot
interface commands, and then executes it.
vloader
This program is responsible for setting everything up and launching all custom modules.
First of all, the moment it starts it notifies vax
that vboot
finished through vax:ldr
commands, so that the game code backup which was overwritten by this program can be restored. It also registers all the existing game modules and sets up newlib/libnx to properly work.
As it's explained below, this program uses an internal heap to be able to use standard libc(++) code.
After setting up the mentioned initial stuff, it proceeds to manually patch the target game's SetHeapSize
and GetInfo
SVCs to force the extra heap size we need for our modules when the game later attempts to initialize the heap, effectively extending it while keeping the module heap area untouched.
Then modules are loaded from sdmc:/atmosphere/contents/<game-app-id>/vax/<any-elf-files>
, and they get launched one-by-one.
After finishing with this, it finally jumps to the game's startup code, normally starting the game with all the modules having done their job. Note that all modules and this program never get unloaded, since their code might be used later in-game (for instance, when replaced functions try to call module-code).
Custom modules
Custom modules must be compiled using libvmod
libs, which are the base libs for any kind of module. Even vboot
(partially) and vloader
have to make use of them, since they are the ones which set up everything correctly.
Custom modules have a certain set of limitations:
- Services must not be accessed the usual way: they must be obtained through
vax:mod
(more specifically byvmod::sf::GetService(...)
or dedicated lib code, likevmod::fs
for FS code) - Anything which internally requires libnx-style TLR (essentially many standard C calls, like s*printfs, FS api, etc..., plus a big chunk of libnx's code) should be avoided or wrapped using the
VMOD_DO_WITH_LIBNX_TLR
macro, which executes the code inside temporarily swapping the game's TLR with a libnx-styled one.
TODO: continue with this