Here it is, Stage 3, the last stage of HENkaku.
This was by far the toughest to crack, so, let's dive in!
This was by far the toughest to crack, so, let's dive in!
HENkaku - Stage 3
In Stage 2, we analyzed how HENkaku exploits two distinct kernel bugs to achieve code execution: a memory leak bug (in the sceIoDevctl function) to defeat KASLR and a use-after-free (in the sceNetIoctl function) to break into the kernel and do ROP.
However, since the execution flow switches over to a ROP chain planted into the kernel, we still couldn't figure out what was happening next.
Like I mentioned in the previous write-up's ending note, dumping the kernel (more specifically, the SceSysmem module) was now necessary. Team molecule did not provide any additional vulnerability that we could use for this purpose, so, it was up to the participants to figure it out themselves.
I had already found a potential memory leak vulnerability while playing around with Stage 2 but, unfortunately, due to it's nature (out-of-bounds read) it wasn't enough to reach the SceSysmem module.
Frustrated, I began looking for other plausible entry-points. It took me several attempts and required analyzing several key components of the Vita's system:
- Network:
The SceNet module was the origin of the use-after-free and I had already an OOB read there, so, what else could be in there?
- Filesystem:
The SceDriverUser module exposes a decent amount of unique system calls for the filesystem. Some of them crash. Can I leak memory here?
- Audio:
Developers don't pay much attention to security when it comes to implement media handling. Some specific audio handling features are taken care by the kernel itself. Can I compromise it?
- Graphics:
Just like with audio, graphics are a common source of flaws. The Vita has plenty of libraries with unique system calls for this (SceGpuEs4User, SceGxm, ScePaf). Will this help?
- Application:
User applications are managed by modules that heavily communicate with the kernel (SceAppUtil and SceDriverUser via SceAppMgr calls). Perhaps this can be taken down?
Eventually, one of those gave me what I wanted and I was able to dump the entire Vita's kernel memory. After locating the SceSysmem module among the dumped binaries I became able to solve the rest of the challenge.
On a side note, I did attempt blind ROP at first by relocating a few gadgets and taking wild guesses, but team molecule made sure it wouldn't be that easy. The gadgets' placement makes it very difficult to predict what each one will do.
Anyway, here is the result:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Kernel ROP chain | |
/* | |
scesysmem_base + 0x00000347 | |
POP {PC} | |
*/ | |
0x00(x_stack + 0x00008A8C) = scesysmem_base + 0x00000031 // PC | |
/* | |
scesysmem_base + 0x00000031 | |
POP {R0,PC} | |
*/ | |
0x00(x_stack + 0x00008A90) = 0x08106803 // R0 | |
0x00(x_stack + 0x00008A94) = scesysmem_base + 0x0001EFF1 // PC | |
/* | |
scesysmem_base + 0x0001EFF1 | |
LSLS R0, R0, #1 -> R0 is 0x1020D006 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008A98) = 0x00000038 // R3 | |
0x00(x_stack + 0x00008A9C) = scesysmem_base + 0x0001EFE1 // PC | |
/* | |
scesysmem_base + 0x0001EFE1 | |
MOV R1, R0 -> R1 is 0x1020D006 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008AA0) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008AA4) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008AA8) = scesysmem_base + 0x0001B571 // PC | |
/* | |
scesysmem_base + 0x0001B571 | |
LSLS R2, R0, #5 -> R2 is 0x041A00C0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008AAC) = 0x00000000 // R3 | |
0x00(x_stack + 0x00008AB0) = scesysmem_base + 0x00001E43 // PC | |
/* | |
scesysmem_base + 0x00001E43 | |
AND.W R2, R2, #0xF0000 -> R2 is 0x041A00C0 & 0xF0000 = 0xA0000 | |
CMP.W R2, #0x40000 | |
BEQ loc_AB1E50 | |
MOVS R0, #0 | |
POP {R3-R5,PC} | |
*/ | |
0x00(x_stack + 0x00008AB4) = 0x00000000 // R3 | |
0x00(x_stack + 0x00008AB8) = scesysmem_base + 0x0001FC6D // R4 | |
0x00(x_stack + 0x00008ABC) = scesysmem_base + 0x0000EA73 // R5 | |
0x00(x_stack + 0x00008AC0) = scesysmem_base + 0x00000031 // PC | |
/* | |
scesysmem_base + 0x00000031 | |
POP {R0,PC} | |
*/ | |
0x00(x_stack + 0x00008AC4) = scesysmem_base + 0x00027913 // R0 | |
0x00(x_stack + 0x00008AC8) = scesysmem_base + 0x0000A523 // PC | |
/* | |
// Allocate kernel memblock (scesysmem_base + 0x00027913 == "Magic") | |
// kern_memblock_alloc("Magic", 0x1020D006, 0xA0000, 0x00000000, 0x00000000); | |
scesysmem_base + 0x0000A523 | |
MOVS R4, #0 | |
SUB SP, SP, #8 | |
STR R4, [SP,#0x10+var_10] | |
BL sub_A6A384 // kern_memblock_alloc | |
ADD SP, SP, #8 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008ACC) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008AD0) = scesysmem_base + 0x00000CE3 // PC | |
/* | |
scesysmem_base + 0x00000CE3 | |
POP {R4-R7,PC} | |
*/ | |
0x00(x_stack + 0x00008AD4) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008AD8) = scesysmem_base + 0x0001F2B1 // R5 | |
0x00(x_stack + 0x00008ADC) = scesysmem_base + 0x00000067 // R6 | |
0x00(x_stack + 0x00008AE0) = scesysmem_base + 0x0000587F // R7 | |
0x00(x_stack + 0x00008AE4) = scesysmem_base + 0x00019713 // PC | |
/* | |
scesysmem_base + 0x00019713 | |
ADD R3, SP, #0x28 -> Will modify stack! | |
BLX R7 | |
*/ | |
/* | |
scesysmem_base + 0x0000587F | |
MOVS R2, R0 -> R2 is memblock_id | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008AE8) = scesysmem_base + 0x00001605 // R4 | |
0x00(x_stack + 0x00008AEC) = scesysmem_base + 0x00001E1D // PC | |
/* | |
scesysmem_base + 0x00001E1D | |
MOV R0, R3 -> R3 is out_buf | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008AF0) = 0x00000000 // R4 | |
0x00(x_stack + 0x00008AF4) = scesysmem_base + 0x0001EFE1 // PC | |
/* | |
scesysmem_base + 0x0001EFE1 | |
MOV R1, R0 -> R1 is out_buf | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008AF8) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008AFC) = scesysmem_base + 0x00001603 // PC | |
/* | |
scesysmem_base + 0x00001603 | |
MOV R0, R2 -> R0 is memblock_id | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B00) = scesysmem_base + 0x0001F2B1 // R3 | |
0x00(x_stack + 0x00008B04) = scesysmem_base + 0x00001F17 // PC | |
/* | |
// Call kern_memblock_getaddr(memblock_id, out_buf); | |
// out_buf contains the memblock's base address | |
scesysmem_base + 0x00001F17 | |
BL sub_A61EC8 // kern_memblock_getaddr | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B08) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B0C) = scesysmem_base + 0x00000031 // PC | |
/* | |
scesysmem_base + 0x00000031 | |
POP {R0,PC} | |
*/ | |
0x00(x_stack + 0x00008B10) = scesysmem_base + 0x0000B913 // R0 -> memblock_addr | |
0x00(x_stack + 0x00008B14) = scesysmem_base + 0x00023B61 // PC | |
/* | |
scesysmem_base + 0x00023B61 | |
MOV R7, R0 -> R7 is memblock_addr | |
MOVT R0, 0x8002 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B18) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B1C) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B20) = scesysmem_base + 0x000232EB // PC | |
/* | |
scesysmem_base + 0x000232EB | |
MOVS R0, #8 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008B24) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B28) = scesysmem_base + 0x0001B571 // PC | |
/* | |
scesysmem_base + 0x0001B571 | |
LSLS R2, R0, #5 -> R2 is (0x08 << 0x05) = 0x100 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008B2C) = scesysmem_base + 0x00023B61 // R3 | |
0x00(x_stack + 0x00008B30) = scesysmem_base + 0x000232F1 // PC | |
/* | |
scesysmem_base + 0x000232F1 | |
MOVS R0, #0x80 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008B34) = scesysmem_base + 0x00001411 // R3 | |
0x00(x_stack + 0x00008B38) = scesysmem_base + 0x00000AE1 // PC | |
/* | |
scesysmem_base + 0x00000AE1 | |
MOVS R1, R0 -> R1 is 0x80 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008B3C) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B40) = scesysmem_base + 0x000050E9 // PC | |
/* | |
scesysmem_base + 0x000050E9 | |
MOV R0, R7 -> R0 is memblock_addr | |
BLX R3 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008B44) = scesysmem_base + 0x00001411 // PC | |
/* | |
scesysmem_base + 0x00001411 | |
POP {R4,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008B48) = 0x00000090 // R4 | |
0x00(x_stack + 0x00008B4C) = scesysmem_base + 0x0001F2B1 // R5 | |
0x00(x_stack + 0x00008B50) = scesysmem_base + 0x00012B11 // PC | |
/* | |
scesysmem_base + 0x00012B11 | |
ADDS.W R0, R0, R4,LSL#2 -> R0 is memblock_addr + 0x240 | |
BEQ loc_A72ADE | |
ADD SP, SP, #8 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008B54) = scesysmem_base + 0x00000CE3 // SP | |
0x00(x_stack + 0x00008B58) = scesysmem_base + 0x000000D1 // SP + 0x04 | |
0x00(x_stack + 0x00008B5C) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008B60) = scesysmem_base + 0x0001F2B1 // PC | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (memblock_addr + 0x240 ^ 0x40) = memblock_addr + 0x200 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B64) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B68) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B6C) = scesysmem_base + 0x0001FDC5 // PC | |
/* | |
scesysmem_base + 0x0001FDC5 | |
MOV R3, LR -> R3 is scesysmem_base + 0x000039EB + 0x02 | |
BLX R4 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008B70) = scesysmem_base + 0x0001D8DB // PC | |
/* | |
// AES_setkey(keybuf, type, size, key, 0); | |
// Call sub_A7D544(memblock_addr + 0x240, 0x80, 0x100, scesysmem_base + 0x39EB + 0x02, 0x00000000); | |
scesysmem_base + 0x0001D8DB | |
MOVS R4, #0 | |
SUB SP, SP, #8 | |
STR R4, [SP] | |
BL sub_A7D544 | |
ADD SP, SP, #8 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008B74) = scesysmem_base + 0x00019399 // R4 | |
0x00(x_stack + 0x00008B78) = scesysmem_base + 0x00019399 // PC | |
/* | |
scesysmem_base + 0x00019399 | |
MOV R0, R9 -> R0 is memblock_addr + 0x200 | |
MOV R1, R4 -> R1 is scesysmem_base + 0x00019399 | |
LDR R2, [SP,#0x38+var_30] -> R2 is scesysmem_base + 0x00000347 | |
MOVS R3, #0 -> R3 is 0x00000000 | |
BLX R5 -> R5 is scesysmem_base + 0x0001F2B1 | |
*/ | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (memblock_addr + 0x200 ^ 0x40) = memblock_addr + 0x240 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B7C) = scesysmem_base + 0x00011C5F // R3 | |
0x00(x_stack + 0x00008B80) = scesysmem_base + 0x00019399 // PC | |
/* | |
scesysmem_base + 0x00019399 | |
MOV R0, R9 -> R0 is memblock_addr + 0x240 | |
MOV R1, R4 -> R1 is scesysmem_base + 0x00019399 | |
LDR R2, [SP,#0x38+var_30] -> R2 is 0x00000000 | |
MOVS R3, #0 -> R3 is 0x00000000 | |
BLX R5 -> R5 is scesysmem_base + 0x0001F2B1 | |
*/ | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (memblock_addr + 0x240 ^ 0x40) = memblock_addr + 0x200 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B84) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008B88) = scesysmem_base + 0x0000B913 // PC | |
/* | |
scesysmem_base + 0x0000B913 | |
MOVS R6, R0 -> R6 is memblock_addr + 0x240 | |
ADD R0, R4 -> R0 is memblock_addr + 0x240 + scesysmem_base + 0x00019399 | |
ADD R1, R2 -> R1 is scesysmem_base + 0x00019399 + 0x00000000 | |
CMP R0, R1 | |
ITE HI | |
MOVHI R0, #0 -> R0 is always bigger than R1 (likely a workaround gadget) | |
MOVLS R0, #1 | |
ADD SP, SP, #8 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008B8C) = 0x00000000 // SP | |
0x00(x_stack + 0x00008B90) = scesysmem_base + 0x0001EFE1 // SP + 0x04 | |
0x00(x_stack + 0x00008B94) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008B98) = scesysmem_base + 0x00001861 // PC | |
/* | |
scesysmem_base + 0x00001861 | |
MOVS R0, #0 -> R0 is 0 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008B9C) = scesysmem_base + 0x0001FC6D // R3 | |
0x00(x_stack + 0x00008BA0) = scesysmem_base + 0x0001F2B1 // PC | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (0x00 ^ 0x40) = 0x40 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BA4) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BA8) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BAC) = scesysmem_base + 0x00019399 // PC | |
/* | |
scesysmem_base + 0x00019399 | |
MOV R0, R9 -> R0 is 0x40 | |
MOV R1, R4 -> R1 is scesysmem_base + 0x00000347 | |
LDR R2, [SP,#0x38+var_30] -> R2 is scesysmem_base + 0x00000347 | |
MOVS R3, #0 -> R3 is 0x00000000 | |
BLX R5 -> R5 is scesysmem_base + 0x0001F2B1 | |
*/ | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (0x40 ^ 0x40) = 0x00 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BB0) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BB4) = scesysmem_base + 0x00019399 // PC | |
/* | |
scesysmem_base + 0x00019399 | |
MOV R0, R9 -> R0 is 0x00 | |
MOV R1, R4 -> R1 is scesysmem_base + 0x00000347 | |
LDR R2, [SP,#0x38+var_30] -> R2 is scesysmem_base + 0x0001614D | |
MOVS R3, #0 -> R3 is 0x00000000 | |
BLX R5 -> R5 is scesysmem_base + 0x0001F2B1 | |
*/ | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (0x00 ^ 0x40) = 0x40 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BB8) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BBC) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BC0) = scesysmem_base + 0x0001614D // PC | |
/* | |
scesysmem_base + 0x0001614D | |
ADDEQ R0, #0x10 -> R0 is 0x40 + 0x10 = 0x50 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008BC4) = scesysmem_base + 0x000233D3 // R3 | |
0x00(x_stack + 0x00008BC8) = scesysmem_base + 0x0001F2B1 // PC | |
/* | |
scesysmem_base + 0x0001F2B1 | |
EOR.W R9, R0, 0x40 -> R9 is (0x50 ^ 0x40) = 0x10 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BCC) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BD0) = scesysmem_base + 0x000000AF // PC | |
/* | |
scesysmem_base + 0x000000AF | |
NEGLS R0, R0 -> R0 is ~(0x10) = 0xFFFFFFF0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008BD4) = scesysmem_base + 0x00001605 // R3 | |
0x00(x_stack + 0x00008BD8) = scesysmem_base + 0x0001EFE1 // PC | |
/* | |
scesysmem_base + 0x0001EFE1 | |
MOV R1, R0 -> R1 is 0xFFFFFFF0 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BDC) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BE0) = scesysmem_base + 0x000050E9 // PC | |
/* | |
scesysmem_base + 0x000050E9 | |
MOV R0, R7 -> R0 is memblock_addr | |
BLX R3 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008BE4) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008BE8) = scesysmem_base + 0x00001347 // PC | |
/* | |
scesysmem_base + 0x00001347 | |
MOV R2, R0 -> R2 is memblock_addr | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008BEC) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008BF0) = scesysmem_base + 0x000000B9 // PC | |
/* | |
scesysmem_base + 0x000000B9 | |
SUBS R0, R2, R1 -> R0 is memblock_addr - 0xFFFFFFF0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008BF4) = scesysmem_base + 0x0001F2B1 // R3 | |
0x00(x_stack + 0x00008BF8) = scesysmem_base + 0x00001347 // PC | |
/* | |
scesysmem_base + 0x00001347 | |
MOV R2, R0 -> R2 is memblock_addr - 0xFFFFFFF0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008BFC) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008C00) = scesysmem_base + 0x0000039B // PC | |
/* | |
scesysmem_base + 0x0000039B | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008C04) = kx_loader_addr // R4 | |
0x00(x_stack + 0x00008C08) = scesysmem_base + 0x0001CB95 // PC | |
/* | |
scesysmem_base + 0x0001CB95 | |
SUBS R1, R4, R1 -> R1 is kx_loader_addr - 0xFFFFFFF0 | |
BLX R3 | |
*/ | |
0x00(x_stack + 0x00008C0C) = scesysmem_base + 0x0001EA93 // PC | |
/* | |
scesysmem_base + 0x0001EA93 | |
MOV R0, R6 -> R0 is memblock_addr + 0x240 | |
BLX R3 | |
*/ | |
0x00(x_stack + 0x00008C10) = scesysmem_base + 0x00001411 // PC | |
/* | |
scesysmem_base + 0x00001411 | |
POP {R4,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008C14) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008C18) = scesysmem_base + 0x000209D7 // R5 | |
0x00(x_stack + 0x00008C1C) = scesysmem_base + 0x000209D3 // PC | |
/* | |
scesysmem_base + 0x000209D3 | |
STR R5, [SP,#0x0C] | |
LDR R5, [SP,#0x38] | |
STR R5, [SP,#0x10] | |
BLX R4 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008C20) = scesysmem_base + 0x00001411 // PC -> SP | |
/* | |
scesysmem_base + 0x00001411 | |
POP {R4,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008C24) = scesysmem_base + 0x00000347 // R4 -> SP + 0x04 | |
0x00(x_stack + 0x00008C28) = scesysmem_base + 0x0001BAF5 // R5 -> SP + 0x08 | |
0x00(x_stack + 0x00008C2C) = scesysmem_base + 0x00001605 // PC -> SP + 0x0C -> scesysmem_base + 0x000209D7 | |
/* | |
scesysmem_base + 0x000209D7 | |
STR R5, [SP,#0x10] | |
BLX R4 -> scesysmem_base + 0x00000347 | |
ADD SP, SP, #0x1C | |
POP {R4,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008C30) = scesysmem_base + 0x00000347 // PC -> SP + 0x10 -> scesysmem_base + 0x0000652B | |
/* | |
scesysmem_base + 0x0000652B | |
ADD SP, SP, #0xC | |
POP {PC} | |
*/ | |
0x00(x_stack + 0x00008C34) = scesysmem_base + 0x0000652B // SP | |
0x00(x_stack + 0x00008C38) = scesysmem_base + 0x00000347 // SP + 0x04 | |
0x00(x_stack + 0x00008C3C) = scesysmem_base + 0x0001BAF5 // SP + 0x08 | |
0x00(x_stack + 0x00008C40) = scesysmem_base + 0x00022A49 // PC -> SP + 0x10 -> scesysmem_base + 0x0001BAF5 | |
/* | |
scesysmem_base + 0x0001BAF5 | |
// AES_decrypt | |
// Call sub_A7BAF4(memblock_addr + 0x240, kx_loader_addr - 0xFFFFFFF0, memblock_addr - 0xFFFFFFF0); | |
// Decrypt kx_loader_addr + 0x10 into memblock_addr + 0x10 | |
*/ | |
0x00(x_stack + 0x00008C44) = 0xFFFFFEB0 // SP | |
0x00(x_stack + 0x00008C48) = scesysmem_base + 0x0000039B // SP + 0x04 | |
0x00(x_stack + 0x00008C4C) = 0x00000040 // SP + 0x08 | |
0x00(x_stack + 0x00008C50) = scesysmem_base + 0x00022A49 // SP + 0x0C | |
0x00(x_stack + 0x00008C54) = scesysmem_base + 0x00000347 // SP + 0x10 | |
0x00(x_stack + 0x00008C58) = scesysmem_base + 0x0000652B // SP + 0x14 -> SP + 0x38 | |
0x00(x_stack + 0x00008C5C) = scesysmem_base + 0x00000347 // SP + 0x18 | |
0x00(x_stack + 0x00008C60) = scesysmem_base + 0x0000039B // R4 -> SP + 0x1C | |
0x00(x_stack + 0x00008C64) = 0x00000040 // R5 | |
0x00(x_stack + 0x00008C68) = scesysmem_base + 0x00001605 // PC | |
/* | |
scesysmem_base + 0x00001605 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008C6C) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008C70) = scesysmem_base + 0x0001D9EB // PC | |
/* | |
scesysmem_base + 0x0001D9EB | |
ADD R2, SP, #0xBC -> R2 is SP + 0xBC | |
BLX R3 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008C74) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008C78) = scesysmem_base + 0x00000853 // PC | |
/* | |
scesysmem_base + 0x00000853 | |
POP {R0,R1,PC} | |
*/ | |
0x00(x_stack + 0x00008C7C) = scesysmem_base + 0x0001D8DB // R0 | |
0x00(x_stack + 0x00008C80) = 0x00000038 // R1 | |
0x00(x_stack + 0x00008C84) = scesysmem_base + 0x000000AB // PC | |
/* | |
scesysmem_base + 0x000000AB | |
SUBS R2, R2, R1 -> R2 is SP + 0xBC - 0x38 = SP + 0x84 | |
IT LS | |
NEGLS R0, R0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008C88) = scesysmem_base + 0x000000D1 // R3 | |
0x00(x_stack + 0x00008C8C) = scesysmem_base + 0x0002328B // PC | |
/* | |
scesysmem_base + 0x0002328B | |
MOV R1, R2 -> R1 is SP + 0x84 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008C90) = scesysmem_base + 0x00022FCD // R4 | |
0x00(x_stack + 0x00008C94) = scesysmem_base + 0x000000D1 // PC | |
/* | |
scesysmem_base + 0x000000D1 | |
MOV R4, R1 -> R4 is SP + 0x84 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008C98) = scesysmem_base + 0x0001EFF1 // R3 | |
0x00(x_stack + 0x00008C9C) = scesysmem_base + 0x0002A117 // PC | |
/* | |
scesysmem_base + 0x0002A117 | |
POP {R2,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008CA0) = scesysmem_base + 0x00000347 // R2 | |
0x00(x_stack + 0x00008CA4) = scesysmem_base + 0x00001605 // R5 | |
0x00(x_stack + 0x00008CA8) = scesysmem_base + 0x00019399 // PC | |
/* | |
scesysmem_base + 0x00019399 | |
MOV R0, R9 -> R0 is 0x10 | |
MOV R1, R4 -> R1 is SP + 0x84 | |
LDR R2, [SP,#0x38+var_30] -> R2 is scesysmem_base + 0x0001BF1F | |
MOVS R3, #0 -> R3 is 0x00000000 | |
BLX R5 -> R5 is scesysmem_base + 0x00001605 | |
*/ | |
/* | |
scesysmem_base + 0x00001605 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008CAC) = scesysmem_base + 0x00000347 // R3 -> SP | |
0x00(x_stack + 0x00008CB0) = scesysmem_base + 0x000039EB // PC -> SP + 0x04 | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008CB4) = scesysmem_base + 0x0001BF1F // PC -> SP + 0x08 | |
/* | |
scesysmem_base + 0x0001BF1F | |
MOV R2, R4 -> R2 is SP + 0x84 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008CB8) = 0xFFFFFEB0 // R3 | |
0x00(x_stack + 0x00008CBC) = scesysmem_base + 0x0000039B // PC | |
/* | |
scesysmem_base + 0x0000039B | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008CC0) = 0x00000240 // R4 | |
0x00(x_stack + 0x00008CC4) = scesysmem_base + 0x00022A49 // PC | |
/* | |
scesysmem_base + 0x00022A49 | |
SUBS R0, R0, R4 -> R0 is 0x10 - 0x240 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008CC8) = scesysmem_base + 0x000039EB // R4 | |
0x00(x_stack + 0x00008CCC) = scesysmem_base + 0x00003D73 // PC | |
/* | |
scesysmem_base + 0x00003D73 | |
ITE NE | |
MOVNE R0, R3 -> R0 is 0xFFFFFEB0 | |
MOVEQ R0, #0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008CD0) = 0x00000000 // R3 | |
0x00(x_stack + 0x00008CD4) = scesysmem_base + 0x000021FD // PC | |
/* | |
scesysmem_base + 0x000021FD | |
ADD R0, R2 -> R0 is 0xFFFFFEB0 + SP + 0x84 = SP - 0xCC | |
CMP R3, #0 -> R3 is 0x00000000 | |
BNE loc_A621E2 | |
POP {R4} | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008CD8) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008CDC) = scesysmem_base + 0x000050E9 // R3 | |
0x00(x_stack + 0x00008CE0) = scesysmem_base + 0x00000AE1 // PC | |
/* | |
scesysmem_base + 0x00000AE1 | |
MOVS R1, R0 -> R1 is SP - 0xCC | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008CE4) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008CE8) = scesysmem_base + 0x0002A117 // PC | |
/* | |
scesysmem_base + 0x0002A117 | |
POP {R2,R5,PC} | |
*/ | |
0x00(x_stack + 0x00008CEC) = scesysmem_base + 0x00000347 // R2 | |
0x00(x_stack + 0x00008CF0) = scesysmem_base + 0x0001F2B1 // R5 | |
0x00(x_stack + 0x00008CF4) = scesysmem_base + 0x00000067 // PC | |
/* | |
// Branch to kx_loader | |
scesysmem_base + 0x00000067 | |
MOV SP, R1 -> SP is SP + 0x90 | |
BLX R2 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008CF8) = scesysmem_base + 0x000039EB // R3 | |
0x00(x_stack + 0x00008CFC) = scesysmem_base + 0x0001BF47 // PC | |
/* | |
scesysmem_base + 0x0001BF47 | |
MOVNE R1, #0 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008D00) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008D04) = scesysmem_base + 0x000050E9 // PC | |
/* | |
scesysmem_base + 0x000050E9 | |
MOV R0, R7 -> R0 is memblock_addr | |
BLX R3 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008D08) = scesysmem_base + 0x0000AF33 // PC | |
/* | |
scesysmem_base + 0x0000AF33 | |
MOV R4, R1 -> R4 is 0 | |
MOV R5, R0 -> R5 is memblock_addr | |
BL sub_A7FBA8 | |
MOV R1, R5 -> R1 is memblock_addr | |
MOV R2, R4 -> R2 is 0 | |
MOVS R3, #0 -> R3 is 0 | |
BL sub_A6CF34 | |
POP {R3-R5,PC} | |
*/ | |
0x00(x_stack + 0x00008D0C) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008D10) = scesysmem_base + 0x0001D9EB // R4 | |
0x00(x_stack + 0x00008D14) = second_payload // R5 | |
0x00(x_stack + 0x00008D18) = scesysmem_base + 0x0001FC6D // PC | |
/* | |
scesysmem_base + 0x0001FC6D | |
BLX R3 -> scesysmem_base + 0x00000347 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008D1C) = scesysmem_base + 0x0000EA73 // PC | |
/* | |
scesysmem_base + 0x0000EA73 | |
MOV R3, R0 -> R3 is memblock_addr | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008D20) = scesysmem_base + 0x0000039B // R4 | |
0x00(x_stack + 0x00008D24) = scesysmem_base + 0x00000853 // PC | |
/* | |
scesysmem_base + 0x00000853 | |
POP {R0,R1,PC} | |
*/ | |
0x00(x_stack + 0x00008D28) = 0xFFFFFFFF // R0 | |
0x00(x_stack + 0x00008D2C) = 0x08106803 // R1 | |
0x00(x_stack + 0x00008D30) = scesysmem_base + 0x000233D3 // PC | |
/* | |
scesysmem_base + 0x000233D3 | |
LSLS R2, R1, #1 -> R2 is (0x08106803 << 0x01) = 0x1020D006 | |
BX LR | |
*/ | |
0x00(x_stack + 0x00008D34) = scesysmem_base + 0x00000347 // R4 | |
0x00(x_stack + 0x00008D38) = scesysmem_base + 0x00000433 // PC | |
/* | |
scesysmem_base + 0x00000433 | |
SUBS R1, R2, #1 -> R1 is (0x1020D006 - 0x01) = 0x1020D005 | |
ANDS R0, R1 -> R0 is (0xFFFFFFFF & 0x1020D005) = 0x1020D005 | |
BEQ loc_A60440 | |
CLZ.W R0, R0 -> R0 is 3 | |
SUB.W R4, R3, R0,LSR#3 -> R4 is (memblock_addr - 0x03) >> 0x03 | |
loc_A60440 | |
SUBS R0, R4, #1 -> R0 is ((memblock_addr - 0x03) >> 0x03) - 0x01 | |
POP {R4,PC} | |
*/ | |
0x00(x_stack + 0x00008D3C) = scesysmem_base + 0x000233D3 // R4 | |
0x00(x_stack + 0x00008D40) = scesysmem_base + 0x000150A3 // PC | |
/* | |
scesysmem_base + 0x000150A3 | |
MOV R0, R3 -> R0 is memblock_addr | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008D44) = 0x00000000 // R3 | |
0x00(x_stack + 0x00008D48) = scesysmem_base + 0x0000A74D // PC | |
/* | |
scesysmem_base + 0x0000A74D | |
sub_A6A74C(memblock_addr, 0x1020D005); | |
*/ | |
0x00(x_stack + 0x00008D4C) = scesysmem_base + 0x00000000 // R4 | |
0x00(x_stack + 0x00008D50) = scesysmem_base + 0x00000853 // PC | |
/* | |
scesysmem_base + 0x00000853 | |
POP {R0,R1,PC} | |
*/ | |
0x00(x_stack + 0x00008D54) = scesysmem_base + 0x0001BF1F // R0 | |
0x00(x_stack + 0x00008D58) = 0x00000200 // R1 | |
0x00(x_stack + 0x00008D5C) = scesysmem_base + 0x00001605 // PC | |
/* | |
scesysmem_base + 0x00001605 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008D60) = scesysmem_base + 0x00000347 // R3 | |
0x00(x_stack + 0x00008D64) = scesysmem_base + 0x000050E9 // PC | |
/* | |
scesysmem_base + 0x000050E9 | |
MOV R0, R7 -> R0 is memblock_addr | |
BLX R3 -> scesysmem_base + 0x00000347 | |
*/ | |
0x00(x_stack + 0x00008D68) = scesysmem_base + 0x00001605 // PC | |
/* | |
scesysmem_base + 0x00001605 | |
POP {R3,PC} | |
*/ | |
0x00(x_stack + 0x00008D6C) = scesysmem_base + 0x00022FCD // R3 | |
0x00(x_stack + 0x00008D70) = scesysmem_base + 0x000039EB // PC | |
/* | |
scesysmem_base + 0x000039EB | |
BLX R3 -> scesysmem_base + 0x00022FCD | |
POP {R3,PC} | |
*/ | |
/* | |
scesysmem_base + 0x00022FCD | |
kern_flush_cache(memblock_addr, 0x00000200); // sub_A82FCC | |
*/ | |
0x00(x_stack + 0x00008D74) = scesysmem_base + 0x00000853 // R3 | |
0x00(x_stack + 0x00008D78) = scesysmem_base + 0x00011C5F // PC | |
/* | |
scesysmem_base + 0x00011C5F | |
BLX R7 -> Jump to memblock_addr | |
*/ |
So, random comments and mistakes aside, this gives us a clear view of what the kernel ROP chain is doing:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Allocate a new memory block | |
char* memblock_name = "Magic"; | |
uint32_t memblock_type = 0x1020D006; | |
uint32_t memblock_size = 0xA0000; | |
void* memblock_opts = 0x00000000; | |
uint32_t memblock_id = kern_memblock_alloc(memblock_name, memblock_type, memblock_size, memblock_opts, 0); | |
// Retrieve the memory block's address into a buffer | |
uint32_t *out_buf; | |
kern_memblock_getaddr(memblock_id, out_buf); | |
// Read out the address | |
uint32_t memblock_addr = (uint32_t)out_buf[0]; | |
// Generate AES-256-ECB key using SceSysmem code! | |
void* k_buf = (void *)memblock_addr + 0x240; // Output buffer to store the key | |
uint32_t key_type = 0x80; // Key type? | |
uint32_t key_size = 0x100; // Key size (0x80 is 128-bit, 0x100 is 256-bit) | |
void* key = (void *)scesysmem_base + 0x39EB + 0x02; // The key is code! | |
uint32_t mode = 0x00000000; // Encryption mode (0 is ECB, 1 is CBC, 2 is CFB1) | |
AES_setkey(k_buf, key_type, key_size, key, mode); | |
// Decrypt the HENkaku's kernel loader | |
void *in_buf = (void *)kx_loader_addr + 0x10; | |
void *out_buf = (void *)memblock_addr + 0x10; | |
AES_decrypt(k_buf, in_buf, out_buf); | |
// Execute the kx_loader | |
kx_loader(); | |
// Clean up? | |
sce_sysmemfordriver_call0(memblock_addr, 0, 0, 0); | |
sce_sysmemfordriver_call1(memblock_addr, 0x1020D005); | |
// Probably cache flush | |
sce_cpufordriver_call0(memblock_addr, 0x00000200); |
If you recall, the kernel loader was an encrypted chunk of 0x100 bytes that was appended to the bottom of the ROP chain we copy into a kernel stack using sceIoDevctl:
- // NULLs for padding at the bottom of the chain
0x00(x_stack + 0x00008D7C) = 0x00000000;
0x00(x_stack + 0x00008D80) = 0x00000000;
0x00(x_stack + 0x00008D84) = 0x00000000;
// Code starts here
0x00(x_stack + 0x00008D88) = ...;
The kernel ROP decrypts this chunk using AES-256-ECB and the key is a piece of code from SceSysmem itself.
This is what the kernel loader looks like (note that base offset is set to 0x00000000):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Entry point | |
sub_00000010(scesysmem_base, payload_addr) | |
{ | |
r4 = scesysmem_base | |
sub_00000356(); | |
r5 = scesysmem_base >> 0x20 | |
sub_0000035A(); | |
// Decrypt and launch HENkaku's payload | |
sub_00000020(scesysmem_base, payload_addr); | |
sub_00000538(); | |
} | |
// Decrypt and launch HENkaku's payload | |
sub_00000020(scesysmem_base, payload_addr) | |
{ | |
sp = (sp - 0x1C) | |
r4 = 0 | |
r8 = sp + 0x18 | |
r7 = scesysmem_base + 0xA500 | |
r5 = scesysmem_base | |
r7 = scesysmem_base + 0xA521 | |
0x0F(sp) = 0 | |
r11 = payload_addr | |
r6 = scesysmem_base + 0x1F00 | |
r9 = scesysmem_base + 0x23000 | |
r10 = scesysmem_base + 0x1BA00 | |
r1 = 0x1020D006 | |
r2 = 0xB000 | |
r3 = 0 | |
r0 = sp + 0x18 | |
// Allocate memblock1 | |
r0 = kern_memblock_alloc(sp + 0x18, 0x1020D006, 0xB000, 0); | |
r2 = 0xB000 | |
r3 = 0 | |
0x04(sp) = r0 // memblock1_id | |
r0 = sp + 0x18 | |
r1 = 0x1020D005 | |
r6 = scesysmem_base + 0x1F15 // sub_A61F14 | |
// Allocate memblock2 | |
r0 = kern_memblock_alloc(sp + 0x18, 0x1020D005, 0xB000, 0); | |
r12 = 0x04(sp) // memblock1_id | |
r7 = r0 // memblock2_id | |
r1 = sp + 0x10 | |
r9 = scesysmem_base + 0x23095 | |
r10 = scesysmem_base + 0x1BAF5 | |
r0 = memblock1_id | |
// Get memblock1's address | |
r0 = kern_memblock_getaddr(memblock1_id, sp + 0x10); | |
r0 = memblock2_id | |
r1 = sp + 0x14 | |
// Get memblock2's address | |
r0 = kern_memblock_getaddr(memblock2_id, sp + 0x14); | |
r3 = scesysmem_base + 0x8200 | |
r0 = 0x10(sp) // memblock1_addr | |
r3 = scesysmem_base + 0x825D | |
r1 = payload_addr | |
r2 = 0xA000 | |
r7 = scesysmem_base + 0x1D800 | |
// Call copy_from_user to read the HENkaku's payload | |
// into our new memory block | |
copy_from_user(memblock1_addr, payload_addr, 0xA000); | |
r6 = 0x10(sp) // memblock1_addr | |
r1 = 0x80 | |
r3 = 0x40 | |
r7 = scesysmem_base + 0x1D8D9 | |
r2 = 0x80 | |
r6 = memblock1_addr + 0xA000 | |
r0 = memblock1_addr + 0xA000 | |
r3 = payload_key | |
// Set the HENkaku's payload key (AES-128-ECB) | |
AES_setkey(memblock1_addr + 0xA000, 0x80, 0x80, payload_key, 0); | |
while (r4 != 0xA000) | |
{ | |
r1 = 0x10(sp) // memblock1_addr | |
r0 = memblock1_addr + 0xA000 | |
r1 = memblock1_addr + r4 | |
r4 = r4 + 0x10 | |
r2 = memblock1_addr + r4 | |
// Decrypt the payload in place | |
AES_decrypt(memblock1_addr + 0xA000, memblock1_addr + r4, memblock1_addr + r4); | |
} | |
r0 = 0x14(sp) // memblock2_addr | |
r2 = 0xB000 | |
r1 = 0x10(sp) // memblock1_addr | |
// Copy from data memory block to executable memory block | |
kern_memcpy(memblock2_addr, memblock1_addr, 0xB000); | |
r3 = 0x14(sp) // memblock2_addr | |
r2 = 0x10(sp) // memblock1_addr | |
r3 = memblock2_addr + 0x01 | |
r2 = memblock1_addr + 0xAF00 | |
// Set PC | |
r4 = memblock2_addr + 0x01 // payload() | |
// Set SP | |
sp = memblock1_addr + 0xAF00 | |
r0 = scesysmem_base | |
// Call payload | |
payload(scesysmem_base); | |
sp = (sp + 0x1C) | |
return; | |
} |
In sum, the loader allocates two memory blocks, one for data and another for code. Then it fetches the HENkaku's payload from user memory (using copy_from_user) and decrypts it in place using a static key (stored inside the kernel loader binary data). Finally, it copies the decrypted payload into an executable memory block, set's PC and SP and jumps to it.
Now we have HENkaku running on our system!
As proof, here are the SHA-1 hashes of the two crucial keys for the entire process:
Kernel loader key (AES-256-ECB): f1a8e9415bf3551377a36a1a5b25ba64f2d96494
Kernel payload key (AES-128-ECB): eacac4a780065c8c106349e412696aabd1b1b8d1
And that's it! This concludes the final stage of the HENkaku's KOTH challenge.
I don't plan on dwelving much into how I leaked the kernel's memory and I don't plan on releasing the keys themselves out of respect for other groups attempting to complete the challenge and for the developers themselves.
I believe the goal of this challenge was not to simply crown the first person to crack HENkaku, but to get the whole community engaged and bringing new ideas to the table.
By not releasing the decrypted binaries or the method I used to leak memory, others still have the chance to solve the challenge themselves.
I may publish a few more posts detailing some interesting features of the HENkaku's payload, but I will leave the full source code reveal to the developers themselves.
Until next time!
First
ReplyDeleteCongratz!
ReplyDeleteWow congratz!!!
ReplyDeletewow congrats nice nice
ReplyDeleteWow amazing!!!
ReplyDeletecongratulations....
ReplyDeleteAmazing job 😨
ReplyDeleteAmazing job 😨
ReplyDeleteImpressive work, was indeed the hardest part of the reversing.
ReplyDeleteLooking forward to read your next updates.
Kudos!
And here are the True Heroes, Congratulations.
ReplyDeleteI never imagined that someone would reach it this far.
Greatings.
Howdy. Trying to follow through this from a starting point of running some custom userland code, I'm not seeing:
ReplyDelete1) What the purpose of calling sceIoOpen("molecule0:") is. Seems like there's an important missing piece here, and what about the state of r1 and r2 for this call?
2) Any data being leaked from sceIoDevctl calls. Although it's returning a mysterious error code, nothing is modified in the output buffer.
Could you fill in any relevant blanks? TIA!
It was an interesting read, keep up the good work!
ReplyDeleteThanks for the informative and helpful post, obviously in your blog everything is good..
ReplyDelete성인웹툰
Its an amazing website, really enjoy your articles.
ReplyDeleteYoure so right. Im there with you. Your weblog is definitely worth a read if anyone comes throughout it
ReplyDelete
ReplyDeleteIm lucky I did because now Ive received a whole new view of this
That is a good tip especially to those new to the blogosphere. Brief but very accurate info…
ReplyDeleteMany thanks for sharing this one. A must read article!
ReplyDeleteGreat Post for Beginner to understand.
ReplyDeleteYou make so many great points here that I read your article a couple of times.
ReplyDeleteI appreciate your skills and style in elaborating on the topic.
ReplyDeleteThank you for sharing this fascinating information. Best of luck.
ReplyDelete
ReplyDeleteYour creative writing abilities inspired me to get my own blog now Thank you.
I like the helpful information that provide in articles. Keep doing it.
ReplyDeleteThis blog is very informative the stuff you provide I really enjoyed reading
ReplyDelete
ReplyDeleteYou really amazed me with your writing talent
ReplyDeleteI truly like your way of blogging. I bookmarked it to my bookmark website list.
Extremely useful info particularly the final section 🙂 I take care of such info a lot.
ReplyDeleteI was looking for this certain info for a long time. Thanks and best of luck.
ReplyDeleteMy brother recommended I might like this web site. He was entirely right.
ReplyDeleteThis post truly made my day. Thanks!
ReplyDeleteYou can not imagine just how much time I had spent for this info!
ReplyDeleteI have read some just right stuff here.
ReplyDeleteI surprise how so much attempt you put to create this kind of wonderful informative site.
ReplyDeleteYour site is so cool. I am impressed by the details that you? Ive on this website.
ReplyDeleteWill without a doubt come and visit this blog all the more frequently.
ReplyDeleteIts an amazing website, really enjoy your articles. Helpful and interesting too.
ReplyDeleteIts wonderful as your other articles : D, regards for posting .
ReplyDelete
ReplyDeleteThanks again for a great article!
This is very well written and your points are well-expressed.
ReplyDeleteValuable information. Quality content is good.
ReplyDeleteThis blog carries amazing and genuinely data.
ReplyDeleteExcellent stuff! Thanks for sharing this one!
ReplyDeleteThank you for your post, I look for such article along time, today I find it finally.
ReplyDeleteThis is so much hilarious blog, Anyways thanks for posting ideas.
ReplyDeleteVery informative and well-written post! Greatjob for your hardwork man!
ReplyDeleteI am appreciative of the fact that you have shared this knowledge with us.
ReplyDeleteDefinitely a good blog is very good. Thank you for sharing.
ReplyDelete
ReplyDeleteThis is the perfect website for anyone who really wants to understand this topic.
ReplyDeleteThis is the perfect
ReplyDeleteThanks for some other wonderful article
ReplyDeleteYou're so smart
ReplyDeleteHi there, I want to subscribe for this website to get hottest updates, so
where can i do it please assist.
ReplyDeleteGreat web site! It looks really expert! Maintain the helpful job!
Great blog here! Additionally your website quite a bit up very fast! thanks!!
ReplyDeleteI am beginner to blogs and I enjoyed you’re web page. Want to bookmark this site.
ReplyDeleteI am really happy that you writing content article like this. its greattt Thanksss
ReplyDeleteExcellent blog post, i hope you will keep more post for sharing. Many thanks
ReplyDelete