cex-2-rex/spoofer/mmu_dump.c

287 lines
6.6 KiB
C

/*
* mmu_dump.c - Bare metal ARMv7 translation table dumper
* Copyright 2014 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.
*/
#include <stdio.h>
#include <string.h>
#include <psp2kern/kernel/modulemgr.h>
#include <psp2kern/kernel/threadmgr.h>
#include <psp2kern/kernel/sysmem.h>
#include <psp2kern/io/fcntl.h>
static int afe;
#define DUMP_PATH "ux0:dump/"
void _start() __attribute__ ((weak, alias ("module_start")));
static unsigned int pa2va(unsigned int pa)
{
unsigned int va;
unsigned int vaddr;
unsigned int paddr;
unsigned int i;
va = 0;
for (i = 0; i < 0x100000; i++)
{
vaddr = i << 12;
__asm__("mcr p15,0,%1,c7,c8,0\n\t"
"mrc p15,0,%0,c7,c4,0\n\t" : "=r" (paddr) : "r" (vaddr));
if ((pa & 0xFFFFF000) == (paddr & 0xFFFFF000))
{
va = vaddr + (pa & 0xFFF);
break;
}
}
return va;
}
static void mmu_get_perms(int ap2, int ap1, int *ur, int *uw, int *pr, int *pw)
{
/* AFE enabled, use simple permissions */
if (afe)
{
*ur = ap1 > 1;
*uw = !ap2 && ap1 > 1;
*pr = 1;
*pw = !ap2 && ap1 < 2;
}
else
{
*pw = (!ap2 && ap1);
*pr = *pw || ap1;
*ur = ap1 > 1;
*uw = !ap2 && ap1 == 3;
}
}
//magic
unsigned char magic[0x8] = {
0xA5, 0x9D, 0xCE, 0xAB, 0x00, 0x01, 0x01
};
//spoof
unsigned char testkit_pscode[0x8] = {
0x00, 0x01, 0x01, 0x02, 0x00, 0x10, 0x00, 0x03
};
static int mmu_dump_pages(unsigned int vaddr, unsigned int entry)
{
int xn;
int ng;
int s;
int ap2;
int ap1;
int pr;
int pw;
int ur;
int uw;
unsigned int paddr;
SceUID fd;
if ((entry & 0x3) == 0x1) /* large page */
{
xn = entry & 0x8000;
ng = entry & 0x800;
s = entry & 0x400;
ap2 = entry & 0x200;
ap1 = (entry >> 4) & 3;
mmu_get_perms(ap2, ap1, &ur, &uw, &pr, &pw);
paddr = entry & 0xFFFF0000;
if( paddr >= 0x40201000 && paddr < 0x5FD00000 && paddr != 0x44C20000 && paddr != 0x44C30000 && paddr != 0x443C0000){
if( (paddr-0x44300000) <= (0x44400000-0x44300000) ){
}else if ( (paddr-0x45020000) <= (0x45030000-0x45020000) ){
}else{
//spoofing happens here
int counter = 0;
for (counter = 0; counter < 0x1000; counter ++){
if(memcmp((void*)vaddr + counter, magic, 7) == 0 && counter == 0x5B0){
counter = counter + 4;
memcpy((void*) vaddr + counter, testkit_pscode, 8);
return 0;
}
}
}
}
else{
}
}
else if ((entry & 0x2)) /* small page */
{
xn = entry & 1;
ng = entry & 0x800;
s = entry & 0x400;
ap2 = entry & 0x200;
ap1 = (entry >> 4) & 3;
mmu_get_perms(ap2, ap1, &ur, &uw, &pr, &pw);
paddr = entry & 0xFFFFF000;
//0x4434C000
if( paddr >= 0x40201000 && paddr < 0x5FD00000){
if( (paddr-0x47D80000) <= (0x47D90000-0x47D80000) ){
}else if ( (paddr-0x44C09000) <= (0x44C1A000-0x44C09000) ){
}else if ( (paddr-0x44300000) <= (0x44400000-0x44300000) ){
}else if ( (paddr-0x45009000) <= (0x45019000-0x45009000) ){
}else if ( (paddr-0x44700000) <= (0x44710000-0x44700000) ){
}else{
//spoofing also happens here
int counter = 0;
for (counter = 0; counter < 0x1000; counter ++){
if(memcmp((void*)vaddr + counter, magic, 7) == 0 && counter == 0x5B0){
counter = counter + 4;
memcpy((void*) vaddr + counter, testkit_pscode, 8);
return 0;
}
}
}
}
else{
}
}
else
{
}
return 1;
}
static int mmu_dump_sections(unsigned int vaddr, unsigned int entry)
{
int ns;
int ss;
int ng;
int s;
int ap1;
int ap2;
int domain;
int xn;
int pr;
int pw;
int ur;
int uw;
unsigned int paddr;
unsigned int i;
unsigned int *tbl;
if ((entry & 0x3) == 2) /* section or supersection */
{
ns = entry & 0x80000;
ss = entry & 0x40000;
ng = entry & 0x20000;
s = entry & 0x10000;
ap2 = entry & 0x8000;
ap1 = (entry >> 10) & 3;
domain = (entry >> 5) & 15;
xn = entry & 0x10;
mmu_get_perms(ap2, ap1, &ur, &uw, &pr, &pw);
paddr = ss ? entry & 0xFF000000 : entry & 0xFFF00000;
}
else if ((entry & 0x3) == 1) /* page table */
{
domain = (entry >> 5) & 15;
ns = entry & 8;
paddr = entry & 0xFFFFFC00;
tbl = (unsigned int *)pa2va(paddr);
for (i = 0; i < 0x100; i++)
{
int ret = mmu_dump_pages(vaddr+(i<<12), tbl[i]);
if (ret == 0) {
return 0;
}
}
}
else if ((entry & 0x3) == 0) /* not mapped */
{
}
else
{
}
return 1;
}
int mmu_dump(void)
{
unsigned int ttbr[2];
int ttbcr;
int n;
unsigned int i = 0;
unsigned int *ttb_vaddr[2];
unsigned int entry;
__asm__("mrc p15,0,%0,c2,c0,0" : "=r" (ttbr[0]));
__asm__("mrc p15,0,%0,c2,c0,1" : "=r" (ttbr[1]));
__asm__("mrc p15,0,%0,c2,c0,2" : "=r" (ttbcr));
n = ttbcr & 0x7;
ttbr[0] &= (unsigned int)((int)0x80000000 >> (31 - 14 + 1 - n));
ttbr[1] &= 0xFFFFC000;
ttb_vaddr[0] = (unsigned int *)pa2va(ttbr[0]);
ttb_vaddr[1] = (unsigned int *)pa2va(ttbr[1]);
for (i = 0; i < (1 << (12 - n)); i++)
{
entry = ttb_vaddr[0][i];
int ret = mmu_dump_sections(i<<20, entry);
if(ret == 0){
return 0;
}
}
if (n)
{
for (i = ((~0xEFFF & 0xFFFF) >> n); i < 0x1000; i++)
{
entry = ttb_vaddr[1][i];
int ret = mmu_dump_sections(i<<20, entry);
if(ret == 0){
return 0;
}
}
}
return 1;
}
int module_start(SceSize argc, const void *args)
{
unsigned int sctlr;
__asm__("mrc p15,0,%0,c1,c0,0" : "=r" (sctlr));
afe = sctlr & 0x20000000;
mmu_dump();
return SCE_KERNEL_START_SUCCESS;
}
int module_stop(SceSize argc, const void *args)
{
return SCE_KERNEL_STOP_SUCCESS;
}