Skip to content

Commit 68cf049

Browse files
committedApr 5, 2023
initial commit
1 parent 8e20501 commit 68cf049

File tree

6 files changed

+928
-0
lines changed

6 files changed

+928
-0
lines changed
 

‎.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.vscode
2+
.VSCodeCounter
3+
build/
4+
sdcard/
5+
*.elf
6+
*.nro
7+
*.nacp
8+
*.psd
9+
*.nsp
10+
*.nso
11+
*.npdm

‎Makefile

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#---------------------------------------------------------------------------------
2+
.SUFFIXES:
3+
#---------------------------------------------------------------------------------
4+
5+
ifeq ($(strip $(DEVKITPRO)),)
6+
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
7+
endif
8+
9+
TOPDIR ?= $(CURDIR)
10+
include $(DEVKITPRO)/libnx/switch_rules
11+
12+
#---------------------------------------------------------------------------------
13+
# TARGET is the name of the output
14+
# BUILD is the directory where object files & intermediate files will be placed
15+
# SOURCES is a list of directories containing source code
16+
# DATA is a list of directories containing data files
17+
# INCLUDES is a list of directories containing header files
18+
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
19+
#
20+
# NO_ICON: if set to anything, do not use icon.
21+
# NO_NACP: if set to anything, no .nacp file is generated.
22+
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
23+
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
24+
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
25+
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
26+
# ICON is the filename of the icon (.jpg), relative to the project folder.
27+
# If not set, it attempts to use one of the following (in this order):
28+
# - <Project name>.jpg
29+
# - icon.jpg
30+
# - <libnx folder>/default_icon.jpg
31+
#
32+
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
33+
# If not set, it attempts to use one of the following (in this order):
34+
# - <Project name>.json
35+
# - config.json
36+
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
37+
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
38+
# NACP building is skipped as well.
39+
#---------------------------------------------------------------------------------
40+
TARGET := exefs
41+
BUILD := build
42+
SOURCES := source
43+
DATA := data
44+
INCLUDES := include
45+
#ROMFS := romfs
46+
APP_VERSION := 2.2.0
47+
48+
#---------------------------------------------------------------------------------
49+
# options for code generation
50+
#---------------------------------------------------------------------------------
51+
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
52+
53+
CFLAGS := -g -Wall -O2 -ffunction-sections \
54+
$(ARCH) $(DEFINES)
55+
56+
CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\"
57+
58+
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
59+
60+
ASFLAGS := -g $(ARCH)
61+
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-wrap,exit -Wl,-Map,$(notdir $*.map)
62+
63+
LIBS := -lnx
64+
65+
#---------------------------------------------------------------------------------
66+
# list of directories containing libraries, this must be the top level containing
67+
# include and lib
68+
#---------------------------------------------------------------------------------
69+
LIBDIRS := $(PORTLIBS) $(LIBNX)
70+
71+
72+
#---------------------------------------------------------------------------------
73+
# no real need to edit anything past this point unless you need to add additional
74+
# rules for different file extensions
75+
#---------------------------------------------------------------------------------
76+
ifneq ($(BUILD),$(notdir $(CURDIR)))
77+
#---------------------------------------------------------------------------------
78+
79+
export OUTPUT := $(CURDIR)/$(TARGET)
80+
export TOPDIR := $(CURDIR)
81+
82+
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
83+
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
84+
85+
export DEPSDIR := $(CURDIR)/$(BUILD)
86+
87+
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
88+
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
89+
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
90+
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
91+
92+
#---------------------------------------------------------------------------------
93+
# use CXX for linking C++ projects, CC for standard C
94+
#---------------------------------------------------------------------------------
95+
ifeq ($(strip $(CPPFILES)),)
96+
#---------------------------------------------------------------------------------
97+
export LD := $(CC)
98+
#---------------------------------------------------------------------------------
99+
else
100+
#---------------------------------------------------------------------------------
101+
export LD := $(CXX)
102+
#---------------------------------------------------------------------------------
103+
endif
104+
#---------------------------------------------------------------------------------
105+
106+
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
107+
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
108+
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
109+
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
110+
111+
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
112+
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
113+
-I$(CURDIR)/$(BUILD)
114+
115+
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
116+
117+
ifeq ($(strip $(CONFIG_JSON)),)
118+
jsons := $(wildcard *.json)
119+
ifneq (,$(findstring $(TARGET).json,$(jsons)))
120+
export APP_JSON := $(TOPDIR)/$(TARGET).json
121+
else
122+
ifneq (,$(findstring config.json,$(jsons)))
123+
export APP_JSON := $(TOPDIR)/config.json
124+
endif
125+
endif
126+
else
127+
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
128+
endif
129+
130+
ifeq ($(strip $(ICON)),)
131+
icons := $(wildcard *.jpg)
132+
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
133+
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
134+
else
135+
ifneq (,$(findstring icon.jpg,$(icons)))
136+
export APP_ICON := $(TOPDIR)/icon.jpg
137+
endif
138+
endif
139+
else
140+
export APP_ICON := $(TOPDIR)/$(ICON)
141+
endif
142+
143+
ifeq ($(strip $(NO_ICON)),)
144+
export NROFLAGS += --icon=$(APP_ICON)
145+
endif
146+
147+
ifeq ($(strip $(NO_NACP)),)
148+
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
149+
endif
150+
151+
ifneq ($(APP_TITLEID),)
152+
export NACPFLAGS += --titleid=$(APP_TITLEID)
153+
endif
154+
155+
ifneq ($(ROMFS),)
156+
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
157+
endif
158+
159+
.PHONY: $(BUILD) clean all
160+
161+
#---------------------------------------------------------------------------------
162+
all: $(BUILD)
163+
164+
$(BUILD):
165+
@[ -d $@ ] || mkdir -p $@
166+
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
167+
168+
#---------------------------------------------------------------------------------
169+
clean:
170+
@echo clean ...
171+
ifeq ($(strip $(APP_JSON)),)
172+
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
173+
else
174+
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
175+
endif
176+
177+
178+
#---------------------------------------------------------------------------------
179+
else
180+
.PHONY: all
181+
182+
DEPENDS := $(OFILES:.o=.d)
183+
184+
#---------------------------------------------------------------------------------
185+
# main targets
186+
#---------------------------------------------------------------------------------
187+
ifeq ($(strip $(APP_JSON)),)
188+
189+
all : $(OUTPUT).nro
190+
191+
ifeq ($(strip $(NO_NACP)),)
192+
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
193+
else
194+
$(OUTPUT).nro : $(OUTPUT).elf
195+
endif
196+
197+
else
198+
199+
all : $(OUTPUT).nsp
200+
201+
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
202+
203+
$(OUTPUT).nso : $(OUTPUT).elf
204+
205+
endif
206+
207+
$(OUTPUT).elf : $(OFILES)
208+
209+
$(OFILES_SRC) : $(HFILES_BIN)
210+
211+
#---------------------------------------------------------------------------------
212+
# you need a rule like this for each extension you use as binary data
213+
#---------------------------------------------------------------------------------
214+
%.bin.o %_bin.h : %.bin
215+
#---------------------------------------------------------------------------------
216+
@echo $(notdir $<)
217+
@$(bin2o)
218+
219+
-include $(DEPENDS)
220+
221+
#---------------------------------------------------------------------------------------
222+
endif
223+
#---------------------------------------------------------------------------------------

‎README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# AA-Reboot Applet
2+
3+
NOTE: THIS REQUIRES THE AA-REBOOT NRO FILE IN ORDER TO WORK AND A LINKED/FAKELINKED USER
4+
5+
It replaces the 0100000000001010 Applet using LayerdFS to start the AA-Reboot.nro at sdmc:/switch/AA-Reboot.nro.
6+
7+
0100000000001010 is the "Nintendo Switch Online Applet" on the homescreen but keep in mind it also replaces the LoginApplet, the ShareApplet and the LobbyApplet.
8+
9+
The Loginapplet is inaccessible in CFW because the Nintendo servers are blocked.
10+
The Shareapplet is accessible but can't be used because you can't post pictures because the Nintendo servers are blocked.
11+
12+
Tip: If you have only user on your switch, you can select "Skip Selection Screen" in the system settings to start aa-reboot instantly!
13+
14+
Special thanks to tallbl0nde for the original Forwarder code in his NX-Activity-Log homebrew [https://github.com/tallbl0nde/NX-Activity-Log]

‎exefs.json

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
{
2+
"name": "hbloader",
3+
"title_id": "0x01000000000010013",
4+
"title_id_range_min": "0x01000000000010013",
5+
"title_id_range_max": "0x01000000000010013",
6+
"main_thread_stack_size": "0x100000",
7+
"main_thread_priority": 44,
8+
"default_cpu_id": 0,
9+
"process_category": 0,
10+
"pool_partition": 0,
11+
"is_64_bit": true,
12+
"address_space_type": 1,
13+
"is_retail": true,
14+
"filesystem_access": {
15+
"permissions": "0xFFFFFFFFFFFFFFFF"
16+
},
17+
"service_host": [
18+
"*"
19+
],
20+
"service_access": [
21+
"*"
22+
],
23+
"kernel_capabilities": [
24+
{
25+
"type": "kernel_flags",
26+
"value": {
27+
"highest_thread_priority": 59,
28+
"lowest_thread_priority": 28,
29+
"highest_cpu_id": 2,
30+
"lowest_cpu_id": 0
31+
}
32+
},
33+
{
34+
"type": "syscalls",
35+
"value": {
36+
"svcUnknown00": "0x00",
37+
"svcSetHeapSize": "0x01",
38+
"svcSetMemoryPermission": "0x02",
39+
"svcSetMemoryAttribute": "0x03",
40+
"svcMapMemory": "0x04",
41+
"svcUnmapMemory": "0x05",
42+
"svcQueryMemory": "0x06",
43+
"svcExitProcess": "0x07",
44+
"svcCreateThread": "0x08",
45+
"svcStartThread": "0x09",
46+
"svcExitThread": "0x0A",
47+
"svcSleepThread": "0x0B",
48+
"svcGetThreadPriority": "0x0C",
49+
"svcSetThreadPriority": "0x0D",
50+
"svcGetThreadCoreMask": "0x0E",
51+
"svcSetThreadCoreMask": "0x0F",
52+
"svcGetCurrentProcessorNumber": "0x10",
53+
"svcSignalEvent": "0x11",
54+
"svcClearEvent": "0x12",
55+
"svcMapSharedMemory": "0x13",
56+
"svcUnmapSharedMemory": "0x14",
57+
"svcCreateTransferMemory": "0x15",
58+
"svcCloseHandle": "0x16",
59+
"svcResetSignal": "0x17",
60+
"svcWaitSynchronization": "0x18",
61+
"svcCancelSynchronization": "0x19",
62+
"svcArbitrateLock": "0x1A",
63+
"svcArbitrateUnlock": "0x1B",
64+
"svcWaitProcessWideKeyAtomic": "0x1C",
65+
"svcSignalProcessWideKey": "0x1D",
66+
"svcGetSystemTick": "0x1E",
67+
"svcConnectToNamedPort": "0x1F",
68+
"svcSendSyncRequestLight": "0x20",
69+
"svcSendSyncRequest": "0x21",
70+
"svcSendSyncRequestWithUserBuffer": "0x22",
71+
"svcSendAsyncRequestWithUserBuffer": "0x23",
72+
"svcGetProcessId": "0x24",
73+
"svcGetThreadId": "0x25",
74+
"svcBreak": "0x26",
75+
"svcOutputDebugString": "0x27",
76+
"svcReturnFromException": "0x28",
77+
"svcGetInfo": "0x29",
78+
"svcFlushEntireDataCache": "0x2A",
79+
"svcFlushDataCache": "0x2B",
80+
"svcMapPhysicalMemory": "0x2C",
81+
"svcUnmapPhysicalMemory": "0x2D",
82+
"svcGetFutureThreadInfo": "0x2E",
83+
"svcGetLastThreadInfo": "0x2F",
84+
"svcGetResourceLimitLimitValue": "0x30",
85+
"svcGetResourceLimitCurrentValue": "0x31",
86+
"svcSetThreadActivity": "0x32",
87+
"svcGetThreadContext3": "0x33",
88+
"svcWaitForAddress": "0x34",
89+
"svcSignalToAddress": "0x35",
90+
"svcUnknown36": "0x36",
91+
"svcUnknown37": "0x37",
92+
"svcUnknown38": "0x38",
93+
"svcUnknown39": "0x39",
94+
"svcUnknown3a": "0x3A",
95+
"svcUnknown3b": "0x3B",
96+
"svcDumpInfo": "0x3C",
97+
"svcDumpInfoNew": "0x3D",
98+
"svcUnknown3e": "0x3E",
99+
"svcUnknown3f": "0x3F",
100+
"svcCreateSession": "0x40",
101+
"svcAcceptSession": "0x41",
102+
"svcReplyAndReceiveLight": "0x42",
103+
"svcReplyAndReceive": "0x43",
104+
"svcReplyAndReceiveWithUserBuffer": "0x44",
105+
"svcCreateEvent": "0x45",
106+
"svcUnknown46": "0x46",
107+
"svcUnknown47": "0x47",
108+
"svcMapPhysicalMemoryUnsafe": "0x48",
109+
"svcUnmapPhysicalMemoryUnsafe": "0x49",
110+
"svcSetUnsafeLimit": "0x4A",
111+
"svcCreateCodeMemory": "0x4B",
112+
"svcControlCodeMemory": "0x4C",
113+
"svcSleepSystem": "0x4D",
114+
"svcReadWriteRegister": "0x4E",
115+
"svcSetProcessActivity": "0x4F",
116+
"svcCreateSharedMemory": "0x50",
117+
"svcMapTransferMemory": "0x51",
118+
"svcUnmapTransferMemory": "0x52",
119+
"svcDebugActiveProcess": "0x60",
120+
"svcBreakDebugProcess": "0x61",
121+
"svcTerminateDebugProcess": "0x62",
122+
"svcGetDebugEvent": "0x63",
123+
"svcContinueDebugEvent": "0x64",
124+
"svcGetProcessList": "0x65",
125+
"svcGetThreadList": "0x66",
126+
"svcGetDebugThreadContext": "0x67",
127+
"svcSetDebugThreadContext": "0x68",
128+
"svcQueryDebugProcessMemory": "0x69",
129+
"svcReadDebugProcessMemory": "0x6A",
130+
"svcWriteDebugProcessMemory": "0x6B",
131+
"svcSetHardwareBreakPoint": "0x6C",
132+
"svcGetDebugThreadParam": "0x6D",
133+
"svcConnectToPort": "0x72",
134+
"svcSetProcessMemoryPermission": "0x73",
135+
"svcMapProcessMemory": "0x74",
136+
"svcUnmapProcessMemory": "0x75",
137+
"svcQueryProcessMemory": "0x76",
138+
"svcMapProcessCodeMemory": "0x77",
139+
"svcUnmapProcessCodeMemory": "0x78",
140+
"svcCallSecureMonitor": "0x7F"
141+
}
142+
},
143+
{
144+
"type": "application_type",
145+
"value": 2
146+
},
147+
{
148+
"type": "min_kernel_version",
149+
"value": "0x30"
150+
},
151+
{
152+
"type": "handle_table_size",
153+
"value": 512
154+
},
155+
{
156+
"type": "debug_flags",
157+
"value": {
158+
"allow_debug": true,
159+
"force_debug": true
160+
}
161+
}
162+
]
163+
}

‎source/main.c

+466
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,466 @@
1+
#include <switch.h>
2+
#include <string.h>
3+
#include <fcntl.h>
4+
#include <unistd.h>
5+
6+
#define DEFAULT_NRO "sdmc:/switch/AA-Reboot.nro"
7+
8+
const char g_noticeText[] =
9+
"nx-hbloader but it starts aa reboot";
10+
11+
static char g_argv[2048];
12+
static char g_nextArgv[2048];
13+
static char g_nextNroPath[512];
14+
u64 g_nroAddr = 0;
15+
static u64 g_nroSize = 0;
16+
static NroHeader g_nroHeader;
17+
static bool g_isApplication = 0;
18+
19+
static NsApplicationControlData g_applicationControlData;
20+
static bool g_isAutomaticGameplayRecording = 0;
21+
static bool g_smCloseWorkaround = false;
22+
23+
static u64 g_appletHeapSize = 0;
24+
static u64 g_appletHeapReservationSize = 0;
25+
26+
static u128 g_userIdStorage;
27+
28+
static u8 g_savedTls[0x100];
29+
30+
// Minimize fs resource usage
31+
u32 __nx_fs_num_sessions = 1;
32+
u32 __nx_fsdev_direntry_cache_size = 1;
33+
bool __nx_fsdev_support_cwd = false;
34+
35+
// Used by trampoline.s
36+
Result g_lastRet = 0;
37+
38+
extern void* __stack_top;//Defined in libnx.
39+
#define STACK_SIZE 0x100000 //Change this if main-thread stack size ever changes.
40+
41+
void __libnx_initheap(void)
42+
{
43+
static char g_innerheap[0x4000];
44+
45+
extern char* fake_heap_start;
46+
extern char* fake_heap_end;
47+
48+
fake_heap_start = &g_innerheap[0];
49+
fake_heap_end = &g_innerheap[sizeof g_innerheap];
50+
}
51+
52+
static Result readSetting(const char* key, void* buf, size_t size)
53+
{
54+
Result rc;
55+
u64 actual_size;
56+
const char* const section_name = "hbloader";
57+
rc = setsysGetSettingsItemValueSize(section_name, key, &actual_size);
58+
if (R_SUCCEEDED(rc) && actual_size != size)
59+
rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
60+
if (R_SUCCEEDED(rc))
61+
rc = setsysGetSettingsItemValue(section_name, key, buf, size, &actual_size);
62+
if (R_SUCCEEDED(rc) && actual_size != size)
63+
rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
64+
if (R_FAILED(rc)) memset(buf, 0, size);
65+
return rc;
66+
}
67+
68+
void __appInit(void)
69+
{
70+
Result rc;
71+
72+
rc = smInitialize();
73+
if (R_FAILED(rc))
74+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 1));
75+
76+
rc = setsysInitialize();
77+
if (R_SUCCEEDED(rc)) {
78+
SetSysFirmwareVersion fw;
79+
rc = setsysGetFirmwareVersion(&fw);
80+
if (R_SUCCEEDED(rc))
81+
hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro));
82+
readSetting("applet_heap_size", &g_appletHeapSize, sizeof(g_appletHeapSize));
83+
readSetting("applet_heap_reservation_size", &g_appletHeapReservationSize, sizeof(g_appletHeapReservationSize));
84+
setsysExit();
85+
}
86+
87+
rc = fsInitialize();
88+
if (R_FAILED(rc))
89+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 2));
90+
}
91+
92+
void __wrap_exit(void)
93+
{
94+
// exit() effectively never gets called, so let's stub it out.
95+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 39));
96+
}
97+
98+
static void* g_heapAddr;
99+
static size_t g_heapSize;
100+
101+
static u64 calculateMaxHeapSize(void)
102+
{
103+
u64 size = 0;
104+
u64 mem_available = 0, mem_used = 0;
105+
106+
svcGetInfo(&mem_available, InfoType_TotalMemorySize, CUR_PROCESS_HANDLE, 0);
107+
svcGetInfo(&mem_used, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0);
108+
109+
if (mem_available > mem_used+0x200000)
110+
size = (mem_available - mem_used - 0x200000) & ~0x1FFFFF;
111+
if (size == 0)
112+
size = 0x2000000*16;
113+
if (size > 0x6000000 && g_isAutomaticGameplayRecording)
114+
size -= 0x6000000;
115+
116+
return size;
117+
}
118+
119+
static void setupHbHeap(void)
120+
{
121+
void* addr = NULL;
122+
u64 size = calculateMaxHeapSize();
123+
124+
if (!g_isApplication) {
125+
if (g_appletHeapSize) {
126+
u64 requested_size = (g_appletHeapSize + 0x1FFFFF) &~ 0x1FFFFF;
127+
if (requested_size < size)
128+
size = requested_size;
129+
}
130+
else if (g_appletHeapReservationSize) {
131+
u64 reserved_size = (g_appletHeapReservationSize + 0x1FFFFF) &~ 0x1FFFFF;
132+
if (reserved_size < size)
133+
size -= reserved_size;
134+
}
135+
}
136+
137+
Result rc = svcSetHeapSize(&addr, size);
138+
139+
if (R_FAILED(rc) || addr==NULL)
140+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 9));
141+
142+
g_heapAddr = addr;
143+
g_heapSize = size;
144+
}
145+
146+
static Handle g_procHandle;
147+
148+
static void procHandleReceiveThread(void* arg)
149+
{
150+
Handle session = (Handle)(uintptr_t)arg;
151+
Result rc;
152+
153+
void* base = armGetTls();
154+
hipcMakeRequestInline(base);
155+
156+
s32 idx = 0;
157+
rc = svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX);
158+
if (R_FAILED(rc))
159+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 15));
160+
161+
HipcParsedRequest r = hipcParseRequest(base);
162+
if (r.meta.num_copy_handles != 1)
163+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 17));
164+
165+
g_procHandle = r.data.copy_handles[0];
166+
svcCloseHandle(session);
167+
}
168+
169+
//Gets the PID of the process with application_type==APPLICATION in the NPDM, then sets g_isApplication if it matches the current PID.
170+
static void getIsApplication(void) {
171+
Result rc=0;
172+
u64 cur_pid=0, app_pid=0;
173+
174+
g_isApplication = 0;
175+
176+
rc = svcGetProcessId(&cur_pid, CUR_PROCESS_HANDLE);
177+
if (R_FAILED(rc)) return;
178+
179+
rc = pmshellInitialize();
180+
181+
if (R_SUCCEEDED(rc)) {
182+
rc = pmshellGetApplicationProcessIdForShell(&app_pid);
183+
pmshellExit();
184+
}
185+
186+
if (R_SUCCEEDED(rc) && cur_pid == app_pid) g_isApplication = 1;
187+
}
188+
189+
//Gets the control.nacp for the current title id, and then sets g_isAutomaticGameplayRecording if less memory should be allocated.
190+
static void getIsAutomaticGameplayRecording(void) {
191+
if (hosversionAtLeast(5,0,0) && g_isApplication) {
192+
Result rc=0;
193+
u64 cur_tid=0;
194+
195+
rc = svcGetInfo(&cur_tid, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
196+
if (R_FAILED(rc)) return;
197+
198+
g_isAutomaticGameplayRecording = 0;
199+
200+
rc = nsInitialize();
201+
202+
if (R_SUCCEEDED(rc)) {
203+
size_t dummy;
204+
rc = nsGetApplicationControlData(0x1, cur_tid, &g_applicationControlData, sizeof(g_applicationControlData), &dummy);
205+
nsExit();
206+
}
207+
208+
if (R_SUCCEEDED(rc) && g_applicationControlData.nacp.video_capture == 2) g_isAutomaticGameplayRecording = 1;
209+
}
210+
}
211+
212+
static void getOwnProcessHandle(void)
213+
{
214+
Result rc;
215+
216+
Handle server_handle, client_handle;
217+
rc = svcCreateSession(&server_handle, &client_handle, 0, 0);
218+
if (R_FAILED(rc))
219+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 12));
220+
221+
Thread t;
222+
rc = threadCreate(&t, &procHandleReceiveThread, (void*)(uintptr_t)server_handle, NULL, 0x1000, 0x20, 0);
223+
if (R_FAILED(rc))
224+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 10));
225+
226+
rc = threadStart(&t);
227+
if (R_FAILED(rc))
228+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 13));
229+
230+
hipcMakeRequestInline(armGetTls(),
231+
.num_copy_handles = 1,
232+
).copy_handles[0] = CUR_PROCESS_HANDLE;
233+
234+
svcSendSyncRequest(client_handle);
235+
svcCloseHandle(client_handle);
236+
237+
threadWaitForExit(&t);
238+
threadClose(&t);
239+
}
240+
241+
void loadNro(void)
242+
{
243+
NroHeader* header = NULL;
244+
size_t rw_size=0;
245+
Result rc=0;
246+
247+
if (g_smCloseWorkaround) {
248+
// For old applications, wait for SM to handle closing the SM session from this process.
249+
// If we don't do this, smInitialize will fail once eventually used later.
250+
// This is caused by a bug in old versions of libnx that was fixed in commit 68a77ac950.
251+
g_smCloseWorkaround = false;
252+
svcSleepThread(1000000000);
253+
}
254+
255+
memcpy((u8*)armGetTls() + 0x100, g_savedTls, 0x100);
256+
257+
if (g_nroSize > 0)
258+
{
259+
// Unmap previous NRO.
260+
header = &g_nroHeader;
261+
rw_size = header->segments[2].size + header->bss_size;
262+
rw_size = (rw_size+0xFFF) & ~0xFFF;
263+
264+
// .text
265+
rc = svcUnmapProcessCodeMemory(
266+
g_procHandle, g_nroAddr + header->segments[0].file_off, ((u64) g_heapAddr) + header->segments[0].file_off, header->segments[0].size);
267+
268+
if (R_FAILED(rc))
269+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 24));
270+
271+
// .rodata
272+
rc = svcUnmapProcessCodeMemory(
273+
g_procHandle, g_nroAddr + header->segments[1].file_off, ((u64) g_heapAddr) + header->segments[1].file_off, header->segments[1].size);
274+
275+
if (R_FAILED(rc))
276+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 25));
277+
278+
// .data + .bss
279+
rc = svcUnmapProcessCodeMemory(
280+
g_procHandle, g_nroAddr + header->segments[2].file_off, ((u64) g_heapAddr) + header->segments[2].file_off, rw_size);
281+
282+
if (R_FAILED(rc))
283+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 26));
284+
285+
g_nroAddr = g_nroSize = 0;
286+
}
287+
288+
if (g_nextNroPath[0] == '\0')
289+
{
290+
memcpy(g_nextNroPath, DEFAULT_NRO, sizeof(DEFAULT_NRO));
291+
memcpy(g_nextArgv, DEFAULT_NRO, sizeof(DEFAULT_NRO));
292+
}
293+
294+
memcpy(g_argv, g_nextArgv, sizeof g_argv);
295+
296+
uint8_t *nrobuf = (uint8_t*) g_heapAddr;
297+
298+
NroStart* start = (NroStart*) (nrobuf + 0);
299+
header = (NroHeader*) (nrobuf + sizeof(NroStart));
300+
uint8_t* rest = (uint8_t*) (nrobuf + sizeof(NroStart) + sizeof(NroHeader));
301+
302+
rc = fsdevMountSdmc();
303+
if (R_FAILED(rc))
304+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 404));
305+
306+
int fd = open(g_nextNroPath, O_RDONLY);
307+
if (fd < 0)
308+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 3));
309+
310+
// Reset NRO path to load hbmenu by default next time.
311+
g_nextNroPath[0] = '\0';
312+
313+
if (read(fd, start, sizeof(*start)) != sizeof(*start))
314+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 4));
315+
316+
if (read(fd, header, sizeof(*header)) != sizeof(*header))
317+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 4));
318+
319+
if (header->magic != NROHEADER_MAGIC)
320+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 5));
321+
322+
size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
323+
if (read(fd, rest, rest_size) != rest_size)
324+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 7));
325+
326+
close(fd);
327+
fsdevUnmountAll();
328+
329+
size_t total_size = header->size + header->bss_size;
330+
total_size = (total_size+0xFFF) & ~0xFFF;
331+
332+
rw_size = header->segments[2].size + header->bss_size;
333+
rw_size = (rw_size+0xFFF) & ~0xFFF;
334+
335+
bool has_mod0 = false;
336+
if (start->mod_offset > 0 && start->mod_offset <= (total_size-0x24)) // Validate MOD0 offset
337+
has_mod0 = *(uint32_t*)(nrobuf + start->mod_offset) == 0x30444F4D; // Validate MOD0 header
338+
339+
int i;
340+
for (i=0; i<3; i++)
341+
{
342+
if (header->segments[i].file_off >= header->size || header->segments[i].size > header->size ||
343+
(header->segments[i].file_off + header->segments[i].size) > header->size)
344+
{
345+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 6));
346+
}
347+
}
348+
349+
// todo: Detect whether NRO fits into heap or not.
350+
351+
// Copy header to elsewhere because we're going to unmap it next.
352+
memcpy(&g_nroHeader, header, sizeof(g_nroHeader));
353+
header = &g_nroHeader;
354+
355+
u64 map_addr;
356+
357+
do {
358+
map_addr = randomGet64() & 0xFFFFFF000ull;
359+
rc = svcMapProcessCodeMemory(g_procHandle, map_addr, (u64)nrobuf, total_size);
360+
361+
} while (rc == 0xDC01 || rc == 0xD401);
362+
363+
if (R_FAILED(rc))
364+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 18));
365+
366+
// .text
367+
rc = svcSetProcessMemoryPermission(
368+
g_procHandle, map_addr + header->segments[0].file_off, header->segments[0].size, Perm_R | Perm_X);
369+
370+
if (R_FAILED(rc))
371+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 19));
372+
373+
// .rodata
374+
rc = svcSetProcessMemoryPermission(
375+
g_procHandle, map_addr + header->segments[1].file_off, header->segments[1].size, Perm_R);
376+
377+
if (R_FAILED(rc))
378+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 20));
379+
380+
// .data + .bss
381+
rc = svcSetProcessMemoryPermission(
382+
g_procHandle, map_addr + header->segments[2].file_off, rw_size, Perm_Rw);
383+
384+
if (R_FAILED(rc))
385+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 21));
386+
387+
u64 nro_size = header->segments[2].file_off + rw_size;
388+
u64 nro_heap_start = ((u64) g_heapAddr) + nro_size;
389+
u64 nro_heap_size = g_heapSize + (u64) g_heapAddr - (u64) nro_heap_start;
390+
391+
#define M EntryFlag_IsMandatory
392+
393+
static ConfigEntry entries[] = {
394+
{ EntryType_MainThreadHandle, 0, {0, 0} },
395+
{ EntryType_ProcessHandle, 0, {0, 0} },
396+
{ EntryType_AppletType, 0, {AppletType_LibraryApplet, 0} },
397+
{ EntryType_OverrideHeap, M, {0, 0} },
398+
{ EntryType_Argv, 0, {0, 0} },
399+
{ EntryType_NextLoadPath, 0, {0, 0} },
400+
{ EntryType_LastLoadResult, 0, {0, 0} },
401+
{ EntryType_SyscallAvailableHint, 0, {0xffffffffffffffff, 0x9fc1fff0007ffff} },
402+
{ EntryType_RandomSeed, 0, {0, 0} },
403+
{ EntryType_UserIdStorage, 0, {(u64)(uintptr_t)&g_userIdStorage, 0} },
404+
{ EntryType_HosVersion, 0, {0, 0} },
405+
{ EntryType_EndOfList, 0, {(u64)(uintptr_t)g_noticeText, sizeof(g_noticeText)} }
406+
};
407+
408+
ConfigEntry *entry_AppletType = &entries[2];
409+
410+
if (g_isApplication) {
411+
entry_AppletType->Value[0] = AppletType_SystemApplication;
412+
entry_AppletType->Value[1] = EnvAppletFlags_ApplicationOverride;
413+
}
414+
415+
// MainThreadHandle
416+
entries[0].Value[0] = envGetMainThreadHandle();
417+
// ProcessHandle
418+
entries[1].Value[0] = g_procHandle;
419+
// OverrideHeap
420+
entries[3].Value[0] = nro_heap_start;
421+
entries[3].Value[1] = nro_heap_size;
422+
// Argv
423+
entries[4].Value[1] = (u64) &g_argv[0];
424+
// NextLoadPath
425+
entries[5].Value[0] = (u64) &g_nextNroPath[0];
426+
entries[5].Value[1] = (u64) &g_nextArgv[0];
427+
// LastLoadResult
428+
entries[6].Value[0] = g_lastRet;
429+
// RandomSeed
430+
entries[8].Value[0] = randomGet64();
431+
entries[8].Value[1] = randomGet64();
432+
// HosVersion
433+
entries[10].Value[0] = hosversionGet();
434+
435+
u64 entrypoint = map_addr;
436+
437+
g_nroAddr = map_addr;
438+
g_nroSize = nro_size;
439+
440+
memset(__stack_top - STACK_SIZE, 0, STACK_SIZE);
441+
442+
if (!has_mod0) {
443+
// Apply sm-close workaround to NROs which do not contain a valid MOD0 header.
444+
// This heuristic is based on the fact that MOD0 support was added very shortly after
445+
// the fix for the sm-close bug (in fact, two commits later).
446+
g_smCloseWorkaround = true;
447+
}
448+
449+
extern NORETURN void nroEntrypointTrampoline(u64 entries_ptr, u64 handle, u64 entrypoint);
450+
nroEntrypointTrampoline((u64) entries, -1, entrypoint);
451+
}
452+
453+
int main(int argc, char **argv)
454+
{
455+
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
456+
457+
getIsApplication();
458+
getIsAutomaticGameplayRecording();
459+
smExit(); // Close SM as we don't need it anymore.
460+
setupHbHeap();
461+
getOwnProcessHandle();
462+
loadNro();
463+
464+
fatalThrow(MAKERESULT(Module_HomebrewLoader, 8));
465+
return 0;
466+
}

‎source/trampoline.s

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.section .text.nroEntrypointTrampoline, "ax", %progbits
2+
3+
.global nroEntrypointTrampoline
4+
.type nroEntrypointTrampoline, %function
5+
.align 2
6+
7+
.global __libnx_exception_entry
8+
.type __libnx_exception_entry, %function
9+
10+
.cfi_startproc
11+
12+
nroEntrypointTrampoline:
13+
14+
// Reset stack pointer.
15+
adrp x8, __stack_top //Defined in libnx.
16+
ldr x8, [x8, #:lo12:__stack_top]
17+
mov sp, x8
18+
19+
// Call NRO.
20+
blr x2
21+
22+
// Save retval
23+
adrp x1, g_lastRet
24+
str w0, [x1, #:lo12:g_lastRet]
25+
26+
// Reset stack pointer and load next NRO.
27+
adrp x8, __stack_top
28+
ldr x8, [x8, #:lo12:__stack_top]
29+
mov sp, x8
30+
31+
b loadNro
32+
33+
.cfi_endproc
34+
35+
.section .text.__libnx_exception_entry, "ax", %progbits
36+
.align 2
37+
38+
.cfi_startproc
39+
40+
__libnx_exception_entry:
41+
adrp x7, g_nroAddr
42+
ldr x7, [x7, #:lo12:g_nroAddr]
43+
cbz x7, __libnx_exception_entry_fail
44+
br x7
45+
46+
__libnx_exception_entry_fail:
47+
mov w0, #0xf801
48+
bl svcReturnFromException
49+
b .
50+
51+
.cfi_endproc

0 commit comments

Comments
 (0)
Please sign in to comment.