unlocknvram.c 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // iOS 11 moves OFVariables to const
  2. // https://twitter.com/s1guza/status/908790514178301952
  3. // however, if we:
  4. // 1) Can find IODTNVRAM service
  5. // 2) Have tfp0 / kernel read|write|alloc
  6. // 3) Can leak kernel address of mach port
  7. // then we can fake vtable on IODTNVRAM object
  8. // async_wake satisfies those requirements
  9. // however, I wasn't able to actually set or get ANY nvram variable
  10. // not even userread/userwrite
  11. // Guess sandboxing won't let to access nvram
  12. #include <stdlib.h>
  13. #include <CoreFoundation/CoreFoundation.h>
  14. #include "kmem.h"
  15. #include "symbols.h"
  16. #include "find_port.h"
  17. // convertPropToObject calls getOFVariableType
  18. // open convertPropToObject, look for first vtable call -- that'd be getOFVariableType
  19. // find xrefs, figure out vtable start from that
  20. // following are offsets of entries in vtable
  21. // it always returns false
  22. const uint64_t searchNVRAMProperty = 0x590;
  23. // 0 corresponds to root only
  24. const uint64_t getOFVariablePerm = 0x558;
  25. typedef mach_port_t io_service_t;
  26. typedef mach_port_t io_connect_t;
  27. extern const mach_port_t kIOMasterPortDefault;
  28. CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
  29. io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
  30. // get kernel address of IODTNVRAM object
  31. uint64_t get_iodtnvram_obj(void) {
  32. // get user serv
  33. io_service_t IODTNVRAMSrv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
  34. // leak user serv
  35. // it should use via_kmem_read method by now, so second param doesn't matter
  36. uint64_t nvram_up = find_port_address_electra(IODTNVRAMSrv, 0x41414141);
  37. // get kern obj -- IODTNVRAM*
  38. uint64_t IODTNVRAMObj = rk64_electra(nvram_up + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
  39. return IODTNVRAMObj;
  40. }
  41. uint64_t orig_vtable = -1;
  42. void unlocknvram(void) {
  43. uint64_t obj = get_iodtnvram_obj();
  44. uint64_t vtable_start = rk64_electra(obj);
  45. uint64_t vtable_end = vtable_start;
  46. // Is vtable really guaranteed to end with 0 or was it just a coincidence?..
  47. // should we just use some max value instead?
  48. while (rk64_electra(vtable_end) != 0) vtable_end += sizeof(uint64_t);
  49. uint32_t vtable_len = (uint32_t) (vtable_end - vtable_start);
  50. // copy vtable to userspace
  51. uint64_t *buf = calloc(1, vtable_len);
  52. rkbuffer(vtable_start, buf, vtable_len);
  53. // alter it
  54. buf[getOFVariablePerm/sizeof(uint64_t)] = buf[searchNVRAMProperty/sizeof(uint64_t)];
  55. // allocate buffer in kernel and copy it back
  56. uint64_t fake_vtable = kmem_alloc_wired(vtable_len);
  57. wkbuffer(fake_vtable, buf, vtable_len);
  58. // replace vtable on IODTNVRAM object
  59. wk64_electra(obj, fake_vtable);
  60. free(buf);
  61. }
  62. void locknvram(void) {
  63. if (orig_vtable == -1) {
  64. return;
  65. }
  66. uint64_t obj = get_iodtnvram_obj();
  67. if (obj == 0) { // would never happen but meh
  68. return;
  69. }
  70. wk64_electra(obj, orig_vtable);
  71. }