nvpatch.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * nvpatch.c - Patch kernel to unrestrict NVRAM variables
  3. *
  4. * Copyright (c) 2014 Samuel Groß
  5. * Copyright (c) 2016 Pupyshev Nikita
  6. * Copyright (c) 2017 Siguza
  7. */
  8. #include <errno.h> // errno
  9. #include <stdio.h> // fprintf, stderr
  10. #include <stdlib.h> // free, malloc
  11. #include <string.h> // memmem, strcmp, strnlen
  12. #include <mach/vm_types.h> // vm_address_t
  13. #include <mach-o/loader.h>
  14. #include "kernel.h"
  15. #define MAX_HEADER_SIZE 0x4000
  16. #define STRING_SEG "__TEXT"
  17. #define STRING_SEC "__cstring"
  18. #define OFVAR_SEG "__DATA"
  19. #define OFVAR_SEC "__data"
  20. enum
  21. {
  22. kOFVarTypeBoolean = 1,
  23. kOFVarTypeNumber,
  24. kOFVarTypeString,
  25. kOFVarTypeData,
  26. };
  27. enum
  28. {
  29. kOFVarPermRootOnly = 0,
  30. kOFVarPermUserRead,
  31. kOFVarPermUserWrite,
  32. kOFVarPermKernelOnly,
  33. };
  34. typedef struct
  35. {
  36. vm_address_t name;
  37. uint32_t type;
  38. uint32_t perm;
  39. int32_t offset;
  40. } OFVar;
  41. typedef struct
  42. {
  43. vm_address_t addr;
  44. vm_size_t len;
  45. char *buf;
  46. } segment_t;
  47. int nvpatch(const char *target) {
  48. struct mach_header_64 *hdr = malloc(MAX_HEADER_SIZE);
  49. if (hdr == NULL) return -1;
  50. memset(hdr, 0, MAX_HEADER_SIZE);
  51. kread(kernel_base, hdr, MAX_HEADER_SIZE);
  52. segment_t cstring = {
  53. .addr = 0,
  54. .len = 0,
  55. .buf = NULL,
  56. },
  57. data = {
  58. .addr = 0,
  59. .len = 0,
  60. .buf = NULL,
  61. };
  62. for (struct load_command *cmd = (struct load_command *)(hdr + 1),
  63. *end = (struct load_command *)((char *)cmd + hdr->sizeofcmds);
  64. cmd < end;
  65. cmd = (struct load_command *)((char *)cmd + cmd->cmdsize)) {
  66. switch (cmd->cmd) {
  67. case LC_SEGMENT_64:
  68. {
  69. struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
  70. struct section_64 *sec = (struct section_64 *)(seg + 1);
  71. for (size_t i = 0; i < seg->nsects; ++i) {
  72. if (strcmp(sec[i].segname, STRING_SEG) == 0 &&
  73. strcmp(sec[i].sectname, STRING_SEC) == 0) {
  74. cstring.addr = sec[i].addr;
  75. cstring.len = sec[i].size;
  76. cstring.buf = malloc(cstring.len);
  77. kread(cstring.addr, cstring.buf, cstring.len);
  78. } else if (strcmp(sec[i].segname, OFVAR_SEG) == 0 &&
  79. strcmp(sec[i].sectname, OFVAR_SEC) == 0) {
  80. data.addr = sec[i].addr;
  81. data.len = sec[i].size;
  82. data.buf = malloc(data.len);
  83. kread(data.addr, data.buf, data.len);
  84. }
  85. }
  86. }
  87. default:
  88. break;
  89. }
  90. }
  91. if (cstring.buf == NULL) {
  92. printf("failed to find %s.%s section \n", STRING_SEG, STRING_SEC);
  93. return -2;
  94. }
  95. if (data.buf == NULL) {
  96. printf("failed to find %s.%s section \n", OFVAR_SEG, OFVAR_SEC);
  97. return -3;
  98. }
  99. char first[] = "little-endian?";
  100. char *str = memmem(cstring.buf, cstring.len, first, sizeof(first));
  101. if (str == NULL) {
  102. printf("failed to find string %s \n", first);
  103. return -4;
  104. }
  105. vm_address_t str_addr = (str - cstring.buf) + cstring.addr;
  106. printf("found string %s at %lx \n", first, str_addr);
  107. OFVar *gOFVars = NULL;
  108. for (vm_address_t *ptr = (vm_address_t *)data.buf,
  109. *end = (vm_address_t *)&data.buf[data.len];
  110. ptr < end;
  111. ++ptr) {
  112. if (*ptr == str_addr) {
  113. gOFVars = (OFVar *)ptr;
  114. break;
  115. }
  116. }
  117. if (gOFVars == NULL) {
  118. printf("failed to find gOFVariables \n");
  119. return -5;
  120. }
  121. vm_address_t gOFAddr = ((char *)gOFVars - data.buf) + data.addr;
  122. printf("found gOFVariables at %lx \n", gOFAddr);
  123. size_t numvars = 0;
  124. size_t longest_name = 0;
  125. for (OFVar *var = gOFVars; (char *)var < &data.buf[data.len]; ++var) {
  126. if (var->name == 0) {
  127. break;
  128. }
  129. if (var->name < cstring.addr || var->name >= cstring.addr + cstring.len) {
  130. printf("gOFVariables[%lu].name is out of bounds \n", numvars);
  131. return -6;
  132. }
  133. char *name = &cstring.buf[var->name - cstring.addr];
  134. size_t maxlen = cstring.len - (name - cstring.buf);
  135. size_t namelen = strnlen(name, maxlen);
  136. if (namelen == maxlen) {
  137. printf("gOFVariables[%lu].name exceeds __cstring size \n", numvars);
  138. return -7;
  139. }
  140. for (size_t i = 0; i < namelen; ++i) {
  141. if (name[i] < 0x20 || name[i] > 0x7f) {
  142. printf("gOFVariables[%lu].name contains non-printable character: 0x%02x \n", numvars, name[i]);
  143. return -8;
  144. }
  145. }
  146. longest_name = namelen > longest_name ? namelen : longest_name;
  147. switch (var->type) {
  148. case kOFVarTypeBoolean:
  149. case kOFVarTypeNumber:
  150. case kOFVarTypeString:
  151. case kOFVarTypeData:
  152. break;
  153. default:
  154. printf("gOFVariables[%lu] has unknown type: 0x%x \n", numvars, var->type);
  155. return -9;
  156. }
  157. switch (var->perm) {
  158. case kOFVarPermRootOnly:
  159. case kOFVarPermUserRead:
  160. case kOFVarPermUserWrite:
  161. case kOFVarPermKernelOnly:
  162. break;
  163. default:
  164. printf("gOFVariables[%lu] has unknown permissions: 0x%x \n", numvars, var->perm);
  165. return -10;
  166. }
  167. ++numvars;
  168. }
  169. if (numvars <= 0) {
  170. printf("gOFVariables contains zero entries \n");
  171. return -11;
  172. }
  173. for (size_t i = 0; i < numvars; ++i) {
  174. char *name = &cstring.buf[gOFVars[i].name - cstring.addr];
  175. if (strcmp(name, target) == 0) {
  176. if (gOFVars[i].perm != kOFVarPermKernelOnly) {
  177. printf("target var %s is not set as kernel-only \n", target);
  178. goto done;
  179. }
  180. vm_size_t off = ((char *)&gOFVars[i].perm) - data.buf;
  181. uint32_t newperm = kOFVarPermUserWrite;
  182. kwrite(data.addr + off, &newperm, sizeof(newperm));
  183. printf("great success for var %s! \n", target);
  184. goto done;
  185. }
  186. }
  187. printf("failed to find variable %s! \n", target);
  188. return -13;
  189. done:;
  190. free(cstring.buf);
  191. free(data.buf);
  192. free(hdr);
  193. return 0;
  194. }