From 049aee00a7803f4b1ca0e7ad65a4d531b2a5dd22 Mon Sep 17 00:00:00 2001 From: Eppin <96589679+Eppin@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:41:46 +0100 Subject: [PATCH] Add support for USB --- README.md | 17 +- .../contents/430000000000000B/config.cfg | 1 + sys-botbase/source/args.c | 2 +- sys-botbase/source/args.h | 2 +- sys-botbase/source/commands.c | 124 +- sys-botbase/source/commands.h | 14 +- sys-botbase/source/freeze.c | 2 +- sys-botbase/source/freeze.h | 2 +- sys-botbase/source/main.c | 1086 +++++++++++------ sys-botbase/source/ntp.h | 2 +- sys-botbase/source/util.c | 2 +- sys-botbase/source/util.h | 2 +- 12 files changed, 798 insertions(+), 458 deletions(-) create mode 100644 release/atmosphere/contents/430000000000000B/config.cfg diff --git a/README.md b/README.md index 08c55f5..598a4d4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# sys-botbase +# sys-botbase merged with usb-botbase -A Nintendo Switch (CFW) sys-module that allows users to remote control their switch via sockets as well as read and write to a games memory. This can be used to create bots for games and other fun automation projects. +A Nintendo Switch (CFW) sys-module that allows users to remote control their Switch via sockets or USB via a config file, as well as read and write to a game's memory. This can be used to create bots for games and other fun automation projects. ## Features: ### Fork changes +- USB support based on [Koi-3088's](https://github.com/Koi-3088/usb-botbase) fork - New command `dateSet {arg}`, where the argument `{arg}` is Unix Timestamp to be set. - New command `resetTime`, `resetTimeNTP`, `getUnixTime` and `getCurrentTime` @@ -31,10 +32,13 @@ A Nintendo Switch (CFW) sys-module that allows users to remote control their swi This project was created for the purpose of development for bot automation. The creators and maintainers of this project are not liable for any damages caused or bans received. Use at your own risk. ## Installation +1. Download [latest release](https://github.com/Eppin/sys-botbase/releases/latest) and extract into your Nintendo Switch SD card. +2. Open the `config.cfg` located in `atmosphere/contents/43000000000B` using your favorite text editor. +3. Set text to `wifi` if you want to connect wirelessly using sockets, or `usb` if you want to connect using a USB cable. Defaults to `wifi`. +4. Restart your Switch. +5. Follow [Sysbot.NET's usb-botbase setup guide](https://github.com/kwsch/SysBot.NET/wiki/Configuring-a-new-USB-Connection). -Download [latest release](https://github.com/Eppin/sys-botbase/releases/latest) and extract into your Nintendo Switch SD card. Restart your switch. - -When installed correctly, sys-botbase will make your docked joy-con's home button glow on switch bootup. If this does not happen, sys-botbase is not installed correctly. +When installed correctly, usb-botbase will make your docked joy-con's home button glow on switch bootup. If this does not happen, usb-botbase is not installed correctly. ![](joycon-glow.gif) @@ -43,4 +47,5 @@ When installed correctly, sys-botbase will make your docked joy-con's home butto - Big thank you to [jakibaki](https://github.com/jakibaki/sys-netcheat) for a great sysmodule base to learn and work with, as well as being helpful on the Reswitched discord! - Thanks to RTNX on discord for bringing to my attention a nasty little bug that would very randomly cause RAM poking to go bad and the switch (sometimes) crashing as a result. - Thanks to Anubis for stress testing! -- And thanks to olliz0r! +- Thanks to FishGuy for the initial USB-Botbase implementation. +- And thanks to olliz0r and Koi-3088! diff --git a/release/atmosphere/contents/430000000000000B/config.cfg b/release/atmosphere/contents/430000000000000B/config.cfg new file mode 100644 index 0000000..e1b6c79 --- /dev/null +++ b/release/atmosphere/contents/430000000000000B/config.cfg @@ -0,0 +1 @@ +wifi diff --git a/sys-botbase/source/args.c b/sys-botbase/source/args.c index 171fc0f..5c27cc0 100644 --- a/sys-botbase/source/args.c +++ b/sys-botbase/source/args.c @@ -29,4 +29,4 @@ int parseArgs(char *origargstr, int (*callback)(int, char **)) free(argv); free(argstr); return ret; -} \ No newline at end of file +} diff --git a/sys-botbase/source/args.h b/sys-botbase/source/args.h index bf46a9b..4f1bd2e 100644 --- a/sys-botbase/source/args.h +++ b/sys-botbase/source/args.h @@ -1 +1 @@ -int parseArgs(char* argstr, int (*callback)(int, char**)); \ No newline at end of file +int parseArgs(char* argstr, int (*callback)(int, char**)); diff --git a/sys-botbase/source/commands.c b/sys-botbase/source/commands.c index 1cb9e71..cba0b4d 100644 --- a/sys-botbase/source/commands.c +++ b/sys-botbase/source/commands.c @@ -14,6 +14,7 @@ HidDeviceType controllerInitializedType = HidDeviceType_FullKey3; HiddbgHdlsHandle controllerHandle = {0}; HiddbgHdlsDeviceInfo controllerDevice = {0}; HiddbgHdlsState controllerState = {0}; +USBResponse response; time_t curTime = 0; time_t origTime = 0; @@ -90,7 +91,7 @@ u64 GetTitleVersion(u64 pid){ s32 out; Result rc = nsInitialize(); - if (R_FAILED(rc)) + if (R_FAILED(rc)) fatalThrow(rc); NsApplicationContentMetaStatus *MetaStatus = malloc(sizeof(NsApplicationContentMetaStatus[100U])); @@ -170,20 +171,16 @@ void initController() if(bControllerIsInitialised) return; //taken from switchexamples github Result rc = hiddbgInitialize(); - - //old - //if (R_FAILED(rc) && debugResultCodes) - //printf("hiddbgInitialize: %d\n", rc); - - //new + if (R_FAILED(rc) && debugResultCodes) { printf("hiddbgInitialize(): 0x%x\n", rc); } else { workmem = aligned_alloc(0x1000, workmem_size); - if (workmem) initflag = 1; + if (workmem) + initflag = 1; else printf("workmem alloc failed\n"); - } + } // Set the controller type to Pro-Controller, and set the npadInterfaceType. controllerDevice.deviceType = controllerInitializedType; @@ -225,7 +222,6 @@ void detachController() hiddbgExit(); free(workmem); bControllerIsInitialised = false; - sessionId.id = 0; } @@ -238,7 +234,7 @@ void poke(u64 offset, u64 size, u8* val) void writeMem(u64 offset, u64 size, u8* val) { - Result rc = svcWriteDebugProcessMemory(debughandle, val, offset, size); + Result rc = svcWriteDebugProcessMemory(debughandle, val, offset, size); if (R_FAILED(rc) && debugResultCodes) printf("svcWriteDebugProcessMemory: %d\n", rc); } @@ -250,12 +246,19 @@ void peek(u64 offset, u64 size) readMem(out, offset, size); detach(); - u64 i; - for (i = 0; i < size; i++) + if (usb) { - printf("%02X", out[i]); + response.size = size; + response.data = &out[0]; + sendUsbResponse(response); + } + else + { + u64 i; + for (i = 0; i < size; i++) + printf("%02X", out[i]); + printf("\n"); } - printf("\n"); free(out); } @@ -263,8 +266,8 @@ void peekInfinite(u64 offset, u64 size) { u64 sizeRemainder = size; u64 totalFetched = 0; - u64 i; u8 *out = malloc(sizeof(u8) * MAX_LINE_LENGTH); + u8 *usbOut = malloc(size); attach(); while (sizeRemainder > 0) @@ -272,16 +275,29 @@ void peekInfinite(u64 offset, u64 size) u64 thisBuffersize = sizeRemainder > MAX_LINE_LENGTH ? MAX_LINE_LENGTH : sizeRemainder; sizeRemainder -= thisBuffersize; readMem(out, offset + totalFetched, thisBuffersize); + + u64 i; for (i = 0; i < thisBuffersize; i++) { - printf("%02X", out[i]); + if (usb) + usbOut[totalFetched + i] = out[i]; + else printf("%02X", out[i]); } totalFetched += thisBuffersize; } + detach(); - printf("\n"); + if (usb) + { + response.size = size; + response.data = &usbOut[0]; + sendUsbResponse(response); + } + else printf("\n"); + free(out); + free(usbOut); } void peekMulti(u64* offset, u64* size, u64 count) @@ -300,18 +316,25 @@ void peekMulti(u64* offset, u64* size, u64 count) } detach(); - u64 i; - for (i = 0; i < totalSize; i++) + if (usb) + { + response.size = totalSize; + response.data = &out[0]; + sendUsbResponse(response); + } + else { - printf("%02X", out[i]); + u64 i; + for (i = 0; i < totalSize; i++) + printf("%02X", out[i]); + printf("\n"); } - printf("\n"); free(out); } void readMem(u8* out, u64 offset, u64 size) { - Result rc = svcReadDebugProcessMemory(out, debughandle, offset, size); + Result rc = svcReadDebugProcessMemory(out, debughandle, offset, size); if (R_FAILED(rc) && debugResultCodes) printf("svcReadDebugProcessMemory: %d\n", rc); } @@ -346,15 +369,15 @@ void setStickState(int side, int dxVal, int dyVal) { initController(); if (side == JOYSTICK_LEFT) - { + { controllerState.analog_stick_l.x = dxVal; - controllerState.analog_stick_l.y = dyVal; - } - else - { - controllerState.analog_stick_r.x = dxVal; - controllerState.analog_stick_r.y = dyVal; - } + controllerState.analog_stick_l.y = dyVal; + } + else + { + controllerState.analog_stick_r.x = dxVal; + controllerState.analog_stick_r.y = dyVal; + } hiddbgSetHdlsState(controllerHandle, &controllerState); } @@ -373,26 +396,26 @@ void reverseArray(u8* arr, int start, int end) u64 followMainPointer(s64* jumps, size_t count) { - u64 offset; + u64 offset; u64 size = sizeof offset; - u8 *out = malloc(size); - MetaData meta = getMetaData(); - - attach(); - readMem(out, meta.main_nso_base + jumps[0], size); - offset = *(u64*)out; - int i; + u8* out = malloc(size); + MetaData meta = getMetaData(); + + attach(); + readMem(out, meta.main_nso_base + jumps[0], size); + offset = *(u64*)out; + int i; for (i = 1; i < count; ++i) - { - readMem(out, offset + jumps[i], size); - offset = *(u64*)out; + { + readMem(out, offset + jumps[i], size); + offset = *(u64*)out; // this traversal resulted in an error if (offset == 0) break; - } - detach(); - free(out); - + } + detach(); + free(out); + return offset; } @@ -530,6 +553,13 @@ void clickSequence(char* seq, u8* token) } } +void sendUsbResponse(USBResponse response) +{ + usbCommsWrite((void*)&response, 4); + if (response.size > 0) + usbCommsWrite(response.data, response.size); +} + void dateSet(uint64_t date) { Result ts = timeSetCurrentTime(TimeType_NetworkSystemClock, date); @@ -596,4 +626,4 @@ long getCurrentTime() return -1; } return curTime; -} \ No newline at end of file +} diff --git a/sys-botbase/source/commands.h b/sys-botbase/source/commands.h index b059552..54c5d7b 100644 --- a/sys-botbase/source/commands.h +++ b/sys-botbase/source/commands.h @@ -1,5 +1,6 @@ #include #include +#define TOUCHPOLLMIN 15000000L // touch screen polling rate seems to be 15ms (no idea how to change) extern Handle debughandle; extern bool bControllerIsInitialised; @@ -12,6 +13,9 @@ extern u64 buttonClickSleepTime; extern u64 keyPressSleepTime; extern u64 pollRate; extern u32 fingerDiameter; +extern bool usb; +extern u8* hdlmem; +extern size_t hdlmem_size; typedef struct { u64 main_nso_base; @@ -35,6 +39,12 @@ typedef struct { u8 state; } KeyData; +typedef struct +{ + u64 size; + void* data; +}USBResponse; + #define JOYSTICK_LEFT 0 #define JOYSTICK_RIGHT 1 @@ -65,9 +75,9 @@ u64 followMainPointer(s64* jumps, size_t count); void touch(HidTouchState* state, u64 sequentialCount, u64 holdTime, bool hold, u8* token); void key(HiddbgKeyboardAutoPilotState* states, u64 sequentialCount); void clickSequence(char* seq, u8* token); - +void sendUsbResponse(USBResponse response); void dateSet(uint64_t date); void resetTime(); void resetTimeNTP(); long getUnixTime(); -long getCurrentTime(); +long getCurrentTime(); \ No newline at end of file diff --git a/sys-botbase/source/freeze.c b/sys-botbase/source/freeze.c index 994a5e3..62b6804 100644 --- a/sys-botbase/source/freeze.c +++ b/sys-botbase/source/freeze.c @@ -97,4 +97,4 @@ u8 clearFreezes(void) } } return clearedOne; -} \ No newline at end of file +} diff --git a/sys-botbase/source/freeze.h b/sys-botbase/source/freeze.h index 6344b76..7183e12 100644 --- a/sys-botbase/source/freeze.h +++ b/sys-botbase/source/freeze.h @@ -18,4 +18,4 @@ int findNextEmptySlot(); int addToFreezeMap(u64 addr, u8* v_data, u64 v_size, u64 tid); int removeFromFreezeMap(u64 addr); int getFreezeCount(bool print); -u8 clearFreezes(void); \ No newline at end of file +u8 clearFreezes(void); diff --git a/sys-botbase/source/main.c b/sys-botbase/source/main.c index 2ae8bd2..d86f5bc 100644 --- a/sys-botbase/source/main.c +++ b/sys-botbase/source/main.c @@ -13,6 +13,8 @@ #include "util.h" #include "freeze.h" #include +#include +#include "ntp.h" #define TITLE_ID 0x430000000000000B #define HEAP_SIZE 0x00480000 @@ -29,21 +31,29 @@ typedef enum { Thread freezeThread, touchThread, keyboardThread, clickThread; // prototype thread functions to give the illusion of cleanliness -void sub_freeze(void *arg); -void sub_touch(void *arg); -void sub_key(void *arg); -void sub_click(void *arg); +void sub_freeze(void* arg); +void sub_touch(void* arg); +void sub_key(void* arg); +void sub_click(void* arg); +void usbMainLoop(); +void wifiMainLoop(); +bool isUSB(); +bool handle_connection(); +void handle_disconnect(); +bool isConnectedToInternet(USBResponse* response); +void sendResult(uint16_t success, USBResponse* response); // locks for thread Mutex freezeMutex, touchMutex, keyMutex, clickMutex; // events for releasing or idling threads -FreezeThreadState freeze_thr_state = Active; +FreezeThreadState freeze_thr_state = Active; u8 clickThreadState = 0; // 1 = break thread // key and touch events currently being processed -KeyData currentKeyEvent = {0}; -TouchData currentTouchEvent = {0}; +KeyData currentKeyEvent = { 0 }; +TouchData currentTouchEvent = { 0 }; char* currentClick = NULL; +bool usb = false; // for cancelling the touch/click thread u8 touchToken = 0; @@ -55,28 +65,29 @@ int fd_size = 5; // we aren't an applet u32 __nx_applet_type = AppletType_None; - TimeServiceType __nx_time_service_type = TimeServiceType_System; // we override libnx internals to do a minimal init void __libnx_initheap(void) { - static u8 inner_heap[HEAP_SIZE]; + static u8 inner_heap[HEAP_SIZE]; extern void* fake_heap_start; extern void* fake_heap_end; // Configure the newlib heap. fake_heap_start = inner_heap; - fake_heap_end = inner_heap + sizeof(inner_heap); + fake_heap_end = inner_heap + sizeof(inner_heap); } void __appInit(void) { Result rc; svcSleepThread(20000000000L); + rc = smInitialize(); if (R_FAILED(rc)) fatalThrow(rc); + if (hosversionGet() == 0) { rc = setsysInitialize(); if (R_SUCCEEDED(rc)) { @@ -87,6 +98,7 @@ void __appInit(void) setsysExit(); } } + rc = timeInitialize(); if (R_FAILED(rc)) { @@ -96,21 +108,35 @@ void __appInit(void) if (R_FAILED(rc)) fatalThrow(rc); } + rc = pmdmntInitialize(); - if (R_FAILED(rc)) + if (R_FAILED(rc)) fatalThrow(rc); + rc = ldrDmntInitialize(); - if (R_FAILED(rc)) - fatalThrow(rc); + if (R_FAILED(rc)) + fatalThrow(rc); + rc = pminfoInitialize(); - if (R_FAILED(rc)) - fatalThrow(rc); - rc = socketInitializeDefault(); if (R_FAILED(rc)) fatalThrow(rc); + + rc = fsInitialize(); + if (R_FAILED(rc)) + fatalThrow(rc); + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) + fatalThrow(rc); + + bool success = handle_connection(); + if (!success) + fatalThrow(rc); + rc = capsscInitialize(); if (R_FAILED(rc)) fatalThrow(rc); + rc = viInitialize(ViServiceType_Default); if (R_FAILED(rc)) fatalThrow(rc); @@ -119,22 +145,24 @@ void __appInit(void) void __appExit(void) { smExit(); - nsExit(); - audoutExit(); - socketExit(); + timeExit(); + pmdmntExit(); + ldrDmntExit(); + pminfoExit(); + handle_disconnect(); + capsscExit(); viExit(); } u64 mainLoopSleepTime = 50; u64 freezeRate = 3; bool debugResultCodes = false; - bool echoCommands = false; void makeTouch(HidTouchState* state, u64 sequentialCount, u64 holdTime, bool hold) { mutexLock(&touchMutex); - memset(¤tTouchEvent, 0, sizeof currentTouchEvent); + memset(¤tTouchEvent, 0, sizeof(currentTouchEvent)); currentTouchEvent.states = state; currentTouchEvent.sequentialCount = sequentialCount; currentTouchEvent.holdTime = holdTime; @@ -146,7 +174,7 @@ void makeTouch(HidTouchState* state, u64 sequentialCount, u64 holdTime, bool hol void makeKeys(HiddbgKeyboardAutoPilotState* states, u64 sequentialCount) { mutexLock(&keyMutex); - memset(¤tKeyEvent, 0, sizeof currentKeyEvent); + memset(¤tKeyEvent, 0, sizeof(currentKeyEvent)); currentKeyEvent.states = states; currentKeyEvent.sequentialCount = sequentialCount; currentKeyEvent.state = 1; @@ -160,20 +188,19 @@ void makeClickSeq(char* seq) mutexUnlock(&clickMutex); } -int argmain(int argc, char **argv) +int argmain(int argc, char** argv) { if (argc == 0) return 0; - + USBResponse response; //peek
if (!strcmp(argv[0], "peek")) { - if(argc != 3) + if (argc != 3) return 0; MetaData meta = getMetaData(); - u64 offset = parseStringToInt(argv[1]); u64 size = parseStringToInt(argv[2]); peekInfinite(meta.heap_base + offset, size); @@ -181,26 +208,26 @@ int argmain(int argc, char **argv) if (!strcmp(argv[0], "peekMulti")) { - if(argc < 3 || argc % 2 == 0) + if (argc < 3 || argc % 2 == 0) return 0; MetaData meta = getMetaData(); - u64 itemCount = (argc - 1)/2; + u64 itemCount = (argc - 1) / 2; u64 offsets[itemCount]; u64 sizes[itemCount]; for (int i = 0; i < itemCount; ++i) { - offsets[i] = meta.heap_base + parseStringToInt(argv[(i*2)+1]); - sizes[i] = parseStringToInt(argv[(i*2)+2]); + offsets[i] = meta.heap_base + parseStringToInt(argv[(i * 2) + 1]); + sizes[i] = parseStringToInt(argv[(i * 2) + 2]); } peekMulti(offsets, sizes, itemCount); } if (!strcmp(argv[0], "peekAbsolute")) { - if(argc != 3) + if (argc != 3) return 0; u64 offset = parseStringToInt(argv[1]); @@ -210,28 +237,27 @@ int argmain(int argc, char **argv) if (!strcmp(argv[0], "peekAbsoluteMulti")) { - if(argc < 3 || argc % 2 == 0) + if (argc < 3 || argc % 2 == 0) return 0; - u64 itemCount = (argc - 1)/2; + u64 itemCount = (argc - 1) / 2; u64 offsets[itemCount]; u64 sizes[itemCount]; for (int i = 0; i < itemCount; ++i) { - offsets[i] = parseStringToInt(argv[(i*2)+1]); - sizes[i] = parseStringToInt(argv[(i*2)+2]); + offsets[i] = parseStringToInt(argv[(i * 2) + 1]); + sizes[i] = parseStringToInt(argv[(i * 2) + 2]); } peekMulti(offsets, sizes, itemCount); } if (!strcmp(argv[0], "peekMain")) { - if(argc != 3) + if (argc != 3) return 0; MetaData meta = getMetaData(); - u64 offset = parseStringToInt(argv[1]); u64 size = parseStringToInt(argv[2]); peekInfinite(meta.main_nso_base + offset, size); @@ -239,19 +265,19 @@ int argmain(int argc, char **argv) if (!strcmp(argv[0], "peekMainMulti")) { - if(argc < 3 || argc % 2 == 0) + if (argc < 3 || argc % 2 == 0) return 0; MetaData meta = getMetaData(); - u64 itemCount = (argc - 1)/2; + u64 itemCount = (argc - 1) / 2; u64 offsets[itemCount]; u64 sizes[itemCount]; for (int i = 0; i < itemCount; ++i) { - offsets[i] = meta.main_nso_base + parseStringToInt(argv[(i*2)+1]); - sizes[i] = parseStringToInt(argv[(i*2)+2]); + offsets[i] = meta.main_nso_base + parseStringToInt(argv[(i * 2) + 1]); + sizes[i] = parseStringToInt(argv[(i * 2) + 2]); } peekMulti(offsets, sizes, itemCount); } @@ -259,48 +285,49 @@ int argmain(int argc, char **argv) //poke
if (!strcmp(argv[0], "poke")) { - if(argc != 3) + if (argc != 3) return 0; - - MetaData meta = getMetaData(); + MetaData meta = getMetaData(); u64 offset = parseStringToInt(argv[1]); u64 size = 0; u8* data = parseStringToByteBuffer(argv[2], &size); + poke(meta.heap_base + offset, size, data); free(data); - } - + } + if (!strcmp(argv[0], "pokeAbsolute")) { - if(argc != 3) + if (argc != 3) return 0; u64 offset = parseStringToInt(argv[1]); u64 size = 0; u8* data = parseStringToByteBuffer(argv[2], &size); + poke(offset, size, data); free(data); } - + if (!strcmp(argv[0], "pokeMain")) { - if(argc != 3) + if (argc != 3) return 0; - - MetaData meta = getMetaData(); + MetaData meta = getMetaData(); u64 offset = parseStringToInt(argv[1]); u64 size = 0; u8* data = parseStringToByteBuffer(argv[2], &size); + poke(meta.main_nso_base + offset, size, data); free(data); - } + } //click if (!strcmp(argv[0], "click")) { - if(argc != 2) + if (argc != 2) return 0; HidNpadButton key = parseStringToButton(argv[1]); click(key); @@ -310,9 +337,9 @@ int argmain(int argc, char **argv) //syntax: