Skip to content

SaltySD Code Edits

jugeeya edited this page Jun 4, 2019 · 2 revisions

Code Edits

Replacing a function

There are 3 steps to replacing a function:

  1. Research the function
  2. Reimplement the function
  3. Use SaltySD_function_replace_sym

So first of all you want to locate a function you want to reimplement. In order to do this, you'll need to use a software reverse engineering framework to inspect the game's NSO file. For this I'd recommend GHIDRA with the switch GHIDRA loader.

Editor's note: this replacement won't convince "the game" that we're always in training mode. It only convinces the smash ball, as you can see this function is used by the smash ball to determine its behavior. However, the idea remains the same, and so this should always be considered when replacing a function. - jugeeya

For my example, I'm going to convince the game we're always in training mode. I've found a function called app::smashball::is_training_mode(void) in GHIDRA, here's a small excerpt from the disassembly and decompilation of them offered by GHIDRA:

                             **************************************************************
                             * app::smashball::is_training_mode()                         *
                             **************************************************************
                             undefined is_training_mode(void)
             undefined         w0:1           <RETURN>                                XREF[1]:     710107aa50(W)  
             ulonglong         x0:8           isTraining                              XREF[1]:     710107aa50(W)  
             undefined8        Stack[-0x10]:8 local_10                                XREF[2]:     710107a9f0(W), 
                                                                                                   710107aa64(*)  
                             _ZN3app9smashball16is_training_modeEv           XREF[1]:     Entry Point(*)  
                             app::smashball::is_training_mode
      710107a9f0 fd 7b bf a9     stp        x29,x30,[sp, #local_10]!
      710107a9f4 fd 03 00 91     mov        x29,sp
      710107a9f8 48 b4 02 90     adrp       x8,0x7106702000
      710107a9fc 08 41 1f 91     add        x8,x8,#0x7d0
// WARNING: Unknown calling convention yet parameter storage is locked
// app::smashball::is_training_mode()

ulonglong is_training_mode(void)

{
  int iVar1;
  ulonglong isTraining;
  
  if (((DAT_71067027d0 & 1) == 0) && (iVar1 = FUN_7102ff51d0(&DAT_71067027d0), iVar1 != 0)) {
    FUN_7102cf2500();
    FUN_71000001c0(&LAB_7102cf22a0,&DAT_71066b56f0,&PTR_ConstantUnitZ_710426e000);
    FUN_7102ff51e0(&DAT_71067027d0);
  }
  isTraining = 1;
  if (modeByte_71066b5720 != 0xc) {
    isTraining = (ulonglong)(modeByte_71066b5720 == 0x20);
  }
  return isTraining;
}

Important takeaways from this:

  • is_training_mode takes no arguments (void) and returns a ulonglong (aka a 64 bit unsigned integer).
  • is_training_mode has a "mangled" name of _ZN3app9smashball16is_training_modeEv

Now, when we reimplement this we need to make sure we match the arguments taken and the return value given, otherwise things will likely break. So our reimplementation should look something like this:

(in our main.c)

uint64_t is_training_mode() {
  return 1;
}

In this case, the ulonglong represents a bool, so we return 1 to indicate it is true. Now to tell the framework to replace the game's training mode function with your own just add the following line to main.c's main() function:

SaltySD_function_replace_sym("_ZN3app9smashball16is_training_modeEv", &is_training_mode);

The first argument is our mangled name, the second is a pointer to the function we want to replace it with.