substitute.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. libsubstitute - https://github.com/comex/substitute
  3. This header file itself is in the public domain (or in any jusrisdiction
  4. where the former is ineffective, CC0 1.0).
  5. */
  6. #pragma once
  7. #include <stdlib.h>
  8. #include <stdint.h>
  9. #include <stdbool.h>
  10. #ifdef __cplusplus
  11. extern "C" {
  12. #endif
  13. /* Error codes */
  14. enum {
  15. /* TODO add numbers */
  16. SUBSTITUTE_OK = 0,
  17. /* substitute_hook_functions: can't patch a function because it's too short-
  18. * i.e. there's an unconditional return instruction inside the patch region
  19. * (and not at its end) */
  20. SUBSTITUTE_ERR_FUNC_TOO_SHORT = 1,
  21. /* substitute_hook_functions: can't patch a function because one of the
  22. * instructions within the patch region is one of a few special problematic
  23. * cases - if you get this on real code, the library should probably be
  24. * updated to handle that case properly */
  25. SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START = 2,
  26. /* substitute_hook_functions: can't patch a function because one of the
  27. * instructions within the patch region (other than the last instruction)
  28. * is a call - meaning that a return address within the region (i.e. about
  29. * to point to clobbered code) could be on some thread's stack, where we
  30. * can't easily find and patch it. This check is skipped if
  31. * SUBSTITUTE_NO_THREAD_SAFETY is set. */
  32. SUBSTITUTE_ERR_FUNC_CALLS_AT_START = 3,
  33. /* substitute_hook_functions: can't patch a function because the (somewhat
  34. * cursory) jump analysis found a jump later in the function to within the
  35. * patch region at the beginning */
  36. SUBSTITUTE_ERR_FUNC_JUMPS_TO_START = 4,
  37. /* out of memory */
  38. SUBSTITUTE_ERR_OOM = 5,
  39. /* substitute_hook_functions: mmap, mprotect, vm_copy, or
  40. * vm_remap failure
  41. * substitute_hook_objc_message: vm_remap failure
  42. * Most likely to come up with substitute_hook_functions if the kernel is
  43. * preventing pages from being marked executable. */
  44. SUBSTITUTE_ERR_VM = 6,
  45. /* substitute_hook_functions: not on the main thread, and you did not pass
  46. * SUBSTITUTE_NO_THREAD_SAFETY */
  47. SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD = 7,
  48. /* substitute_hook_functions: when trying to patch the PC of other threads
  49. * (in case they were inside the patched prolog when they were suspended),
  50. * found a PC that was in the patch region but seemingly not at an
  51. * instruction boundary
  52. * The hooks were otherwise completed, but the thread in question will
  53. * probably crash now that its code has changed under it. */
  54. SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD = 8,
  55. /* substitute_hook_functions: destination was out of range, and mmap
  56. * wouldn't give us a trampoline in range */
  57. SUBSTITUTE_ERR_OUT_OF_RANGE = 9,
  58. /* substitute_interpose_imports: couldn't redo relocation for an import
  59. * because the type was unknown */
  60. SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE = 10,
  61. /* substitute_hook_objc_message: no such selector existed in the class's
  62. * inheritance tree */
  63. SUBSTITUTE_ERR_NO_SUCH_SELECTOR = 11,
  64. /* substitute_hook_functions: OS error suspending other threads */
  65. SUBSTITUTE_ERR_ADJUSTING_THREADS = 12,
  66. _SUBSTITUTE_CURRENT_MAX_ERR_PLUS_ONE,
  67. };
  68. /* Get a string representation for a SUBSTITUTE_* error code. */
  69. const char *substitute_strerror(int err);
  70. struct substitute_function_hook {
  71. /* The function to hook. (On ARM, Thumb functions are indicated as usual
  72. * for function pointers.) */
  73. void *function;
  74. /* The replacement function. */
  75. void *replacement;
  76. /* Optional: out *pointer* to function pointer to call old implementation
  77. * (i.e. given 'void (*old_foo)(...);', pass &old_foo) */
  78. void *old_ptr;
  79. /* Currently unused; pass 0. (Protip: When using C {} struct initializer
  80. * syntax, you can just omit this.) */
  81. int options;
  82. };
  83. /* substitute_hook_functions options */
  84. enum {
  85. SUBSTITUTE_NO_THREAD_SAFETY = 1,
  86. };
  87. /* Patch the machine code of the specified functions to redirect them to the
  88. * specified replacements.
  89. *
  90. * After hooking, you can use the function pointer written to 'old_ptr' to call
  91. * the original implementation. (It points to a trampoline that executes the
  92. * original first few instructions, which were written over in the real
  93. * function, then jumps there for the rest.)
  94. *
  95. * This function must be called from the main thread. In return, it attempts
  96. * to be atomic in the face of concurrent calls to the functions being hooked.
  97. * Since there is no way to do that directly, it resorts to pausing all other
  98. * threads while doing its job; and since there is no way to do *that*
  99. * atomically on currently supported platforms, it does so by pausing each
  100. * thread one at a time. If multiple threads each tried to pause each other
  101. * this way, the process would be deadlocked, so mutual exclusion must be
  102. * implicitly provided by running on the main thread.
  103. *
  104. * You can disable the main thread check and all synchronization by passing
  105. * SUBSTITUTE_NO_THREAD_SAFETY.
  106. *
  107. * Why not just use a mutex to prevent deadlock? That would work between
  108. * multiple calls into libsubstitute, but there may be other libraries that
  109. * want to do the same thing and would not know about our mutex. My hope is
  110. * that using the main thread is sufficiently natural that the author of any
  111. * other similar library which cares about atomicity, noticing the same
  112. * concern, would independently come up with the same restriction - at least,
  113. * if they do not find an easier method to avoid deadlocks. Note that all
  114. * existing hooking libraries I know of make no attempt to do any
  115. * synchronization at all; this is fine if hooking is only done during
  116. * initialization while the process is single threaded, but I want to properly
  117. * support dynamic injection. (Note - if there is such an easier method on OS
  118. * X that does not involve spawning a separate process, I'd be curious to hear
  119. * about it.)
  120. *
  121. *
  122. * @hooks see struct substitute_function_hook
  123. * @nhooks number of hooks
  124. * @recordp if non-NULL, on success receives a pointer that can be used to
  125. * cleanly undo the hooks; currently unimplemented, so pass NULL
  126. * @options options - see above
  127. * @return SUBSTITUTE_OK, or any of most of the SUBSTITUTE_ERR_*
  128. */
  129. struct substitute_function_hook_record;
  130. int substitute_hook_functions(const struct substitute_function_hook *hooks,
  131. size_t nhooks,
  132. struct substitute_function_hook_record **recordp,
  133. int options);
  134. #if 1 /* declare dynamic linker-related stuff? */
  135. #ifdef __APPLE__
  136. #include <mach-o/nlist.h>
  137. #ifdef __LP64__
  138. typedef struct nlist_64 substitute_sym;
  139. #else
  140. typedef struct nlist substitute_sym;
  141. #endif
  142. #else
  143. #error No definition for substitute_sym!
  144. #endif
  145. struct substitute_image {
  146. #ifdef __APPLE__
  147. intptr_t slide;
  148. void *dlhandle;
  149. const void *image_header;
  150. #endif
  151. /* possibly private fields... */
  152. };
  153. /* Look up an image currently loaded into the process.
  154. *
  155. * @filename the executable/library path (c.f. dyld(3) on Darwin)
  156. * @return a handle, or NULL if the image wasn't found
  157. */
  158. struct substitute_image *substitute_open_image(const char *filename);
  159. /* Release a handle opened with substitute_open_image.
  160. *
  161. * @handle a banana
  162. */
  163. void substitute_close_image(struct substitute_image *handle);
  164. /* Look up private symbols in an image currently loaded into the process.
  165. *
  166. * @handle handle opened with substitute_open_image
  167. * @names an array of symbol names to search for
  168. * @syms an array of void *, one per name; on return, each entry will be
  169. * filled in with the corresponding symbol address, or NULL if the
  170. * symbol wasn't found
  171. * (on ARM, this will be | 1 for Thumb functions)
  172. * @nsyms number of names
  173. *
  174. * @return SUBSTITUTE_OK (maybe errors in the future)
  175. */
  176. int substitute_find_private_syms(struct substitute_image *handle,
  177. const char **__restrict names,
  178. void **__restrict syms,
  179. size_t nsyms);
  180. /* Get a pointer corresponding to a loaded symbol table entry.
  181. * @handle handle containing the symbol
  182. * @sym symbol
  183. * @return the pointer - on ARM, this can be | 1 for Thumb, like everything
  184. * else
  185. */
  186. void *substitute_sym_to_ptr(struct substitute_image *handle, substitute_sym *sym);
  187. struct substitute_import_hook {
  188. /* The symbol name - this is raw, so C++ symbols are mangled, and on OS X
  189. * most symbols have '_' prepended. */
  190. const char *name;
  191. /* The new import address. */
  192. void *replacement;
  193. /* Optional: out pointer to old value. if there are multiple imports for
  194. * the same symbol, only one address is returned (hopefully they are all
  195. * equal) */
  196. void *old_ptr;
  197. /* Currently unused; pass 0. (Protip: When using C {} struct initializer
  198. * syntax, you can just omit this.) */
  199. int options;
  200. };
  201. /* Directly modify the GOT/PLT entries from a specified image corresponding to
  202. * specified symbols.
  203. *
  204. * This can be used to 'hook' functions or even exported variables. Compared
  205. * to substitute_hook_functions, it has the following advantages:
  206. *
  207. * - It does not require the ability to patch executable code; accordingly, it
  208. * can (from a technical rather than policy perspective) be used in sandboxed
  209. * environments like iOS or PaX MPROTECT.
  210. * - On platforms without RELRO or similar, it is thread safe, as the patches
  211. * are done using atomic instructions.
  212. * - It does not require architecture specific code.
  213. * - It can be used to modify a single library's view of the world without
  214. * affecting the rest of the program.
  215. *
  216. * ...and the following disadvantages:
  217. *
  218. * - It only works for exported functions, and even then will not catch calls
  219. * from a library to its own exported functions.
  220. * - At present, it *only* works for a single importing library at a time.
  221. * Although it is not difficult on most platforms to iterate loaded libraries
  222. * in order to hook all of them, substitute does not currently provide this
  223. * functionality, traversing all libraries' symbol tables may be slow, and in
  224. * any case there is the matter of new importers being loaded after the fact.
  225. *
  226. * @handle handle of the importing library
  227. * @hooks see struct substitute_import_hook
  228. * @nhooks number of hooks
  229. * @recordp if non-NULL, on success receives a pointer that can be used to
  230. * cleanly undo the hooks; currently unimplemented, so pass NULL
  231. * @options options - pass 0
  232. * @return SUBSTITUTE_OK
  233. * SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE
  234. * SUBSTITUTE_ERR_VM - in the future with RELRO on Linux
  235. */
  236. struct substitute_import_hook_record;
  237. int substitute_interpose_imports(const struct substitute_image *handle,
  238. const struct substitute_import_hook *hooks,
  239. size_t nhooks,
  240. struct substitute_import_hook_record **recordp,
  241. int options);
  242. #endif /* 1 */
  243. #if defined(__APPLE__)
  244. #include <objc/runtime.h>
  245. /* Hook a method implementation for a given Objective-C class. By itself, this
  246. * function is thread safe: it is simply a wrapper for the atomic Objective-C
  247. * runtime call class_replaceMethod, plus the superclass-call generation
  248. * functionality described below, and some code to ensure atomicity if the
  249. * method is called while the function is in progress. However, it will race
  250. * with code that modifies class methods without using atomic runtime calls,
  251. * such as Substrate.
  252. *
  253. * @klass the class
  254. * @selector the selector
  255. * @replacement the new implementation (other APIs would call this an
  256. * IMP, but that isn't in general the real type of the
  257. * implementation, so declared as a void * here)
  258. * @old_ptr optional - out pointer to the 'old implementation'.
  259. * If there is no old implementation, a custom IMP is
  260. * returned that delegates to the superclass. This IMP can
  261. * be freed if desired with substitute_free_created_imp.
  262. * @created_imp_ptr optional - out pointer to whether a fake superclass-call
  263. * IMP has been placed in <old_ptr>
  264. *
  265. * @return SUBSTITUTE_OK
  266. * SUBSTITUTE_ERR_NO_SUCH_SELECTOR
  267. */
  268. int substitute_hook_objc_message(Class klass, SEL selector, void *replacement,
  269. void *old_ptr, bool *created_imp_ptr);
  270. void substitute_free_created_imp(IMP imp);
  271. #endif
  272. #ifdef __cplusplus
  273. } /* extern */
  274. #endif