Homebrew Homebrew app sys-patch - sysmod that patches on boot

Most people (accidentally or not) upgrade their consoles in OFW mode...


Sorry if I'm too obvious but I think you can't prevent system updates in OFW mode.

😬
 
  • Like
Reactions: Blythe93
@bth

Thanks, I'll have a look at that, currently I'm just using a hosts file to do the blocking, but this seems to make the switch crash if checking online for anything, example manual checking for an update/eshop/linking account, etc. I thought maybe spoofing the version number would work and be an easy patch, but I'll try the way you suggest now. I have IDA/Ghidra set up and a python file for IDA which lets me read decompressed/unpacked firmware files so I'll see if I can find what I am looking for.
 
@bth

Thanks, I'll have a look at that, currently I'm just using a hosts file to do the blocking, but this seems to make the switch crash if checking online for anything, example manual checking for an update/eshop/linking account, etc. I thought maybe spoofing the version number would work and be an easy patch, but I'll try the way you suggest now. I have IDA/Ghidra set up and a python file for IDA which lets me read decompressed/unpacked firmware files so I'll see if I can find what I am looking for.
for (NIM) 21.0.0 the function which is used in 6 ish contexts to call data from atum is 0xEB250


atum.jpg
 
  • Like
Reactions: bombayjack
Most people (accidentally or not) upgrade their consoles in OFW mode...


Sorry if I'm too obvious but I think you can't prevent system updates in OFW mode.

😬
I pretty much update the sysnand to the latest firmware anyway and use the 32GB nand/nor or whatever it is for the latest large games that run better from that than they do from sdcard, the switch is banned (that's why I got it for free from a friend) and use emuand for other firmware as they don't care about fuses are burned.

In OFW if you don't want to update, just keep it in airplane mode all the time and just use emunand from sd card. Even if you did update OFW to the latest and were waiting for atmosphere, you can still use emunand from a dumped backup. I've got a mini emunand stored on google drive, my computer and mega - that way I can always get my hands on a backup emunand if my sd card gets corrupted.
Post automatically merged:

for (NIM) 21.0.0 the function which is used in 6 ish contexts to call data from atum is 0xEB250


View attachment 543942
Thanks, I'll try putting RTN at the top of that function and check what other functions are calling it, then NOP the calls and see what happens, I'll just make IPS patches to test as I already patched fusee.bin and know how to make the ips patches. Actually I sometimes patch Android apps, and they use Arm64 instructions, same as the Switch, so hopefully I can understand what's going on.
 
Last edited by bombayjack,
I pretty much update the sysnand to the latest firmware anyway and use the 32GB nand/nor or whatever it is for the latest large games that run better from that than they do from sdcard, the switch is banned (that's why I got it for free from a friend) and use emuand for other firmware as they don't care about fuses are burned.

In OFW if you don't want to update, just keep it in airplane mode all the time and just use emunand from sd card. Even if you did update OFW to the latest and were waiting for atmosphere, you can still use emunand from a dumped backup. I've got a mini emunand stored on google drive, my computer and mega - that way I can always get my hands on a backup emunand if my sd card gets corrupted.
Post automatically merged:


Thanks, I'll try putting RTN at the top of that function and check what other functions are calling it, then NOP the calls and see what happens, I'll just make IPS patches to test as I already patched fusee.bin and know how to make the ips patches.
eb250.jpg


it's just matter of identifying which specific of these functions (which ultimately call for EB250) does the thing you don't want.
 
  • Like
Reactions: bombayjack
View attachment 543943

it's just matter of identifying which specific of these functions (which ultimately call for EB250) does the thing you don't want.
I can just jump to that offset in IDA and in IDA you can easily see by right clicking on the function what other functions reference it, click the reference and you end up on the branch call, so unless it's returning some value you can nop it or branch over the code to ignore the entire section, I'll try with Ghida as well as that's got a better decompiler for viewing the code. Thanks.
 
I can just jump to that offset in IDA and in IDA you can easily see by right clicking on the function what other functions reference it, click the reference and you end up on the branch call, so unless it's returning some value you can nop it or branch over the code to ignore the entire section, I'll try with Ghida as well as that's got a better decompiler for viewing the code. Thanks.
if i were a guessing man, i would say the function at 0xE9970 sure looks like it decides how something should or shouldn't happen.
e9970.jpg
 
  • Like
Reactions: bombayjack
@bth

In Sys-patch main at the end of that function we have this:

// note: sysmod exits here. To keep it running, add a for loop (remember to sleep!)

We just return 0, and there's no for loop, so it shouldn't keep running once it's been run once, Is this correct?
 
@bth

In Sys-patch main at the end of that function we have this:

// note: sysmod exits here. To keep it running, add a for loop (remember to sleep!)

We just return 0, and there's no for loop, so it shouldn't keep running once it's been run once, Is this correct?
it's designed to exit memory and not continue running after the console has booted.
there's no reason for it to continue being loaded in memory or persist after its done with its job.
Keeping it there for some sort of interaction with the overlay would be unwanted and unwarranted, there isn't enough memory to go around these days (after 19.0.0, getting worse every update, to worst at 21.0.0)

the overlay itself is just enabling incorrect behaviour from end users, the act of having a million sysmodules loaded at all times is bad behaviour in 2025.
 
it's designed to exit memory and not continue running after the console has booted.
there's no reason for it to continue being loaded in memory or persist after its done with its job.
Keeping it there for some sort of interaction with the overlay would be unwanted and unwarranted, there isn't enough memory to go around these days (after 19.0.0, getting worse every update, to worst at 21.0.0)

the overlay itself is just enabling incorrect behaviour from end users, the act of having a million sysmodules loaded at all times is bad behaviour in 2025.
Thanks, that's what I thought. @AllOver was maybe confused as he thought it continued to run in the background or maybe I picked him up wrong. I know the overlay is a separate program and the reality is that it just reads/writes the ini file that sys-patch uses. I've been modding the overlay and added a couple of new functions to it, It's working good for me but I still want to change a couple of things on it and then I'll give it to you if you want to look at it, as it might have some stuff you find useful.
 
as for controlling firmware updates, im pretty sure you can manage to curb the behaviour from NS (010000000000001F)


(probably should control it further up the chain than the example below, as this is bound to generate lots of telemetry lmao - most probably 0x12CCD4 / inside of function 0x8CCAC where 0x12CCD4 is called from 0x8CD5C)


ns2100.jpg



ns2100-2.jpg
 
Last edited by bth,
@bth

I had a look at that function and made an IPS patch to ret it, (I am using fw2.5.0 to test), but it still downloaded the update files automatically. I found this though, I think it looks at the settings to check if you have autoupdate enabled:

Code:
__int64 __fastcall sub_710011B554(__int64 a1, __int64 a2, char a3)
{
  __int64 v6; // x0
  __int64 v7; // x0
  _BYTE *v8; // x8

  v6 = sub_710011BABC(a1, "auto_update_enabled");
  if ( !(unsigned int)sub_71002A8800(a2 + 184, v6, *(unsigned __int16 *)(a2 + 314) + 1LL) )
  {
    v8 = (_BYTE *)(a1 + 58);
    goto LABEL_5;
  }
  v7 = sub_710011BB04(a1, "auto_update_enabled");
  if ( !(unsigned int)sub_71002A8800(a2 + 184, v7, *(unsigned __int16 *)(a2 + 314) + 1LL) )
  {
    v8 = (_BYTE *)(a1 + 90);
LABEL_5:
    *v8 = a3 & 1;
  }
  return 0LL;
}

Another interesting function is this one (which is probably for a game update)

Code:
_BYTE *__fastcall sub_710009490C(
        _BYTE *result,
        __int64 a2,
        unsigned int a3,
        unsigned int a4,
        __int64 a5,
        char a6,
        int a7,
        unsigned __int8 a8)
{
  __int64 v15; // [xsp+0h] [xbp-150h] BYREF
  _BYTE v16[256]; // [xsp+8h] [xbp-148h] BYREF
  _BYTE v17[72]; // [xsp+108h] [xbp-48h] BYREF

  if ( *result )
  {
    sub_71002A6AB0(v17, 0LL, 72LL);
    sub_710029026C(v17, "application_update");
    sub_71002A6AB0(v16, 0LL, 256LL);
    sub_71002905FC(v17, v16, 256LL);
    result = (_BYTE *)sub_7100290608(v17, &unk_71002C1FB8);
    if ( !(_DWORD)result )
    {
      v15 = a2;
      result = (_BYTE *)sub_7100290628(v17, "ApplicationId", &v15);
      if ( !(_DWORD)result )
      {
        result = (_BYTE *)sub_7100290618(v17, "CurrentVersion", a3);
        if ( !(_DWORD)result )
        {
          result = (_BYTE *)sub_7100290618(v17, "LatestVersion", a4);
          if ( !(_DWORD)result )
          {
            result = (_BYTE *)sub_7100290618(v17, "LastLaunchedDate", *(_QWORD *)a5);
            if ( !(_DWORD)result )
            {
              result = (_BYTE *)sub_7100290618(v17, "LastTerminatedDate", *(_QWORD *)(a5 + 8));
              if ( !(_DWORD)result )
              {
                result = (_BYTE *)sub_7100290618(v17, "LaunchCount", *(_QWORD *)(a5 + 16));
                if ( !(_DWORD)result )
                {
                  result = (_BYTE *)sub_7100290618(v17, "LastUsedLicenseType", *(unsigned __int8 *)(a5 + 32));
                  if ( !(_DWORD)result )
                  {
                    result = (_BYTE *)sub_7100290618(v17, "LastLaunchStorage", *(unsigned __int8 *)(a5 + 33));
                    if ( !(_DWORD)result )
                    {
                      result = (_BYTE *)sub_7100290648(v17, "IsAutoUpdateEnabled", a6 & 1);
                      if ( !(_DWORD)result )
                      {
                        result = (_BYTE *)sub_7100290618(v17, "AutoUpdateDisabledReason", a7);
                        if ( !(_DWORD)result )
                        {
                          result = (_BYTE *)sub_7100290618(v17, "UpdateContext", a8);
                          if ( !(_DWORD)result )
                            return (_BYTE *)sub_7100290668(v17);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  return result;
}
 
Last edited by bombayjack,
Has there been an update to sysp for the system update and atmosphere update that just dropped?
 
Has there been an update to sysp for the system update and atmosphere update that just dropped?
https://switchbrew.org/wiki/21.1.0

NIM/NIFM is not essential even if they were broken (i havent checked yet, but i assume they are fine), ES/FS are unaffected, which are what lets you install things

edit: all the patches for 21.1.0 seem fine
Post automatically merged:

@bth

I had a look at that function and made an IPS patch to ret it, (I am using fw2.5.0 to test), but it still downloaded the update files automatically. I found this though, I think it looks at the settings to check if you have autoupdate enabled:

makes sense, the function mostly is just telemetry stuff.

i suspect the svc/function of interest is:
(NS - 21.1.0)
function 0x8f7d0


mov x0, #0x7c10
movk x0, #6, lsl #16
NOP (optional)

00 82 8f d2
c0 00 a0 f2
1f 20 03 df (optional)


could also reorder it
NOP
mov x0, #0x7c10
movk x0, #6, lsl #16
(RET)

1f 20 03 df
00 82 8f d2
c0 00 a0 f2
(RET)


at 0x8F7EC




(this is what the .ips/patch example strings below are)
or from 0x0 of 0x8F7D0
mov x0, #0x7c10
movk x0, #6, lsl #16
RET

00 82 8f d2
c0 00 a0 f2
c0 03 5f d6

this should return what 8ccac is expected to return to 0x8f7d0, if it's not doing an update


patch bytes (21.0.0 + 21.1.0)
50 41 54 43 48 08 F7 D0 00 0C 00 82 8F D2 C0 00 A0 F2 C0 03 5F D6 45 4F 46

4FF17667783989FA86596E93D9226C7D9E23F930.ips
391DB3BABF75B37F120641F1CDE2C46CE50F971F.ips


patch bytes (20.5.0)
50 41 54 43 48 09 06 3C 00 0C 00 82 8F D2 C0 00 A0 F2 C0 03 5F D6 45 4F 46
958BA9B667CF3AEF5C7FF5CC801F41764F4DF0E7.ips

(test .ips files attatched in atmosphere.zip)


if someone has a banned console, and are willing to remove dns mitm/prodinfo blanking config and be on/set up emummc and force downgrade said emummc to 20.5.0 or 21.0.0, and see if updates aren't received with these, that'd be nice.

ns-2110.jpg


mov-movk-ret.jpg
 

Attachments

Last edited by bth,
i suspect the svc/function of interest is:
(NS - 21.1.0)
function 0x8f7d0


mov x0, #0x7c10
movk x0, #6, lsl #16
NOP (optional)

00 82 8f d2
c0 00 a0 f2
1f 20 03 df (optional)


could also reorder it
NOP
mov x0, #0x7c10
movk x0, #6, lsl #16
(RET)

1f 20 03 df
00 82 8f d2
c0 00 a0 f2
(RET)


at 0x8F7EC




(this is what the .ips/patch example strings below are)
or from 0x0 of 0x8F7D0
mov x0, #0x7c10
movk x0, #6, lsl #16
RET

00 82 8f d2
c0 00 a0 f2
c0 03 5f d6

this should return what 8ccac is expected to return to 0x8f7d0, if it's not doing an update


patch bytes (21.0.0 + 21.1.0)
50 41 54 43 48 08 F7 D0 00 0C 00 82 8F D2 C0 00 A0 F2 C0 03 5F D6 45 4F 46

4FF17667783989FA86596E93D9226C7D9E23F930.ips
391DB3BABF75B37F120641F1CDE2C46CE50F971F.ips


patch bytes (20.5.0)
50 41 54 43 48 09 06 3C 00 0C 00 82 8F D2 C0 00 A0 F2 C0 03 5F D6 45 4F 46
958BA9B667CF3AEF5C7FF5CC801F41764F4DF0E7.ips

(test .ips files attatched in atmosphere.zip)


if someone has a banned console, and are willing to remove dns mitm/prodinfo blanking config and be on/set up emummc and force downgrade said emummc to 20.5.0 or 21.0.0, and see if updates aren't received with these, that'd be nice.
I'll test it later today and let you know how it goes.

EDIT:
In firmware 20.0.5/21.0.1 I think the actual firmware is broken (nothing to do with any patches), even if Automatic Software updates is turned off, in the background it still downloads updated firmware nca files in the background, once completed and you try to start a game it tells you to update.

With the IPS applied, it's still downloading the update nca files. It's this I'm trying to stop. I know I can just use a hosts file, but it would be better to add a blocking patch.

The worst case scenario is that I can easily add an option in the overlay to create a blocking hosts file, but that would be the last option I would want to do.
 
Last edited by bombayjack,
  • Like
Reactions: Blythe93
even if Automatic Software updates is turned off, in the background it still downloads updated firmware nca files in the background,
Yes, that is only for game updates. firmware updates cannot be disabled with that.

and the .ips patch wont undo the process already in action, the one i made would only in theory prevent firmware update from being initiated, it doesn't remove one pending already. (the codepath after, which the .ips patch prevents from running, is the codepaths later in the stage, which are from the screenshots above, it sending back information to NIM, with telemetry also sent to nintendo for debugging purposes, and to identify which firmware you should receive, which was received, etc.

the other end would be identifying which function in NIM that calls for the function identified in NS, and stop it from there instead. (0x8F7D0 in NS)
 
the .ips patch wont undo the process already in action, the one i made would only in theory prevent firmware update from being initiated, it doesn't remove one pending already.
Even if all pending update files are removed, it still initiates and starts downloading those update nca files again. Thanks for the tip on it's only for stopping game updates, doh! no wonder all the patches I made/tried yesterday never worked and the firmware update files were still being downloaded. I thought that option in the settings was for all updates (including firmware), but I've not had a switch for that long, you live and learn.

I was looking for a section in the code which looks for the firmware version and compares it to the updated version it finds on the server, then was going to patch so that it thinks it's upto date. Thanks for the tips on what modules to check, I'll have a look at that in a day or two as I've spent long enough trying to find the things to patch so far and it's exhausting me out. I'll write a temporary option to the overlay for now to just create a hosts file as that's probably a good thing to have in anycase and can easily be implemented.

Hosts file can be called "blocking.txt" and contain these entries:
Code:
# 90DNS-equivalent
127.0.0.1 *nintendo.com
127.0.0.1 *nintendo.net
127.0.0.1 *nintendo.jp
127.0.0.1 *nintendo.co.jp
127.0.0.1 *nintendo.co.uk
127.0.0.1 *nintendo-europe.com
127.0.0.1 *nintendowifi.net
127.0.0.1 *nintendo.es
127.0.0.1 *nintendo.co.kr
127.0.0.1 *nintendo.tw
127.0.0.1 *nintendo.com.hk
127.0.0.1 *nintendo.com.au
127.0.0.1 *nintendo.co.nz
127.0.0.1 *nintendo.at
127.0.0.1 *nintendo.be
127.0.0.1 *nintendods.cz
127.0.0.1 *nintendo.dk
127.0.0.1 *nintendo.de
127.0.0.1 *nintendo.fi
127.0.0.1 *nintendo.fr
127.0.0.1 *nintendo.gr
127.0.0.1 *nintendo.hu
127.0.0.1 *nintendo.it
127.0.0.1 *nintendo.nl
127.0.0.1 *nintendo.no
127.0.0.1 *nintendo.pt
127.0.0.1 *nintendo.ru
127.0.0.1 *nintendo.co.za
127.0.0.1 *nintendo.se
127.0.0.1 *nintendo.ch
127.0.0.1 *nintendo.pl
127.0.0.1 *nintendoswitch.com
127.0.0.1 *nintendoswitch.com.cn
127.0.0.1 *nintendoswitch.cn
95.216.149.205 *conntest.nintendowifi.net
95.216.149.205 *ctest.cdn.nintendo.net
 
I'll write a temporary option to the overlay
Feel free to make a completely separate project doing that, as that is not within scope of what sys-patch should do. Or, keep it to yourself in your own fork, i guess.

you only really need 127.0.0.1 *nintendo*, since NIFM patch replaces the need for x-organization: nintendo header on ctest domain, and the NIM patch eliminates a crash that can occur if that condition is skipped.



also

https://switchbrew.org/wiki/NS_services#GetDownloadProgress
that should be what is in function 0x93140 (21.1.0 - NS)

which feeds information to:
https://switchbrew.org/wiki/NIM_services#SystemUpdateTaskInfo

which is called from 0x113410 (21.0.0 - NIM)
 
Last edited by bth,
  • Like
Reactions: bombayjack
Feel free to make a completely separate project doing that, as that is not within scope of what sys-patch should do. Or, keep it to yourself in your own fork, i guess.

you only really need 127.0.0.1 *nintendo*, since NIFM patch replaces the need for x-organization: nintendo header on ctest domain, and the NIM patch eliminates a crash that can occur if that condition is skipped.



also

https://switchbrew.org/wiki/NS_services#GetDownloadProgress
that should be what is in function 0x93140 (21.1.0 - NS)

which feeds information to:
https://switchbrew.org/wiki/NIM_services#SystemUpdateTaskInfo

which is called from 0x113410 (21.0.0 - NIM)
Yes, using the single wildcard line is a better idea and prevents the nca's being downloaded. I just tested on fw 2.0.5 and it's working fine. Maybe you can add some code into the main program to have a "nintendo" blocker and just add that instead of creating a patch, if enabled it writes the file, if not it deletes it.
 
Yes, using the single wildcard line is a better idea and prevents the nca's being downloaded. I just tested on fw 2.0.5 and it's working fine. Maybe you can add some code into the main program to have a "nintendo" blocker and just add that instead of creating a patch, if enabled it writes the file, if not it deletes it.
sys-patch should not deploy atmosphere related config files to the end user.

either way, the return of 0x11653c (21.1.0) in NIM is what determines if an update is being initiated.

and that gets the value determining if its updating from function if that function (the one below) returns zero, it will fetch the update.


0x166FE0 (20.5.0)
0x1657F0 (21.0.0)
0x1657E0 (21.1.0)

(RET 1 - patch applied to that offset (00 08 size)
20 00 80 d2 c0 03 5F


nim-2110.jpg
ret1.jpg
 

Site & Scene News

Popular threads in this forum