{"id":15047124,"url":"https://github.com/zeex/subhook","last_synced_at":"2025-04-12T18:53:51.812Z","repository":{"id":6352703,"uuid":"7589481","full_name":"Zeex/subhook","owner":"Zeex","description":"Simple hooking library for C/C++ (x86 only, 32/64-bit, no dependencies)","archived":false,"fork":false,"pushed_at":"2023-03-27T15:58:59.000Z","size":180,"stargazers_count":801,"open_issues_count":22,"forks_count":124,"subscribers_count":42,"default_branch":"master","last_synced_at":"2024-09-29T23:23:41.301Z","etag":null,"topics":["c","cmake","cplusplus","hooking","x86"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"hatena/Hatena-Textbook","license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zeex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2013-01-13T15:17:50.000Z","updated_at":"2024-09-26T08:09:11.000Z","dependencies_parsed_at":"2022-08-06T19:15:31.008Z","dependency_job_id":"4864b431-1d76-491f-b15f-00ef07e678d9","html_url":"https://github.com/Zeex/subhook","commit_stats":{"total_commits":226,"total_committers":15,"mean_commits":"15.066666666666666","dds":0.08849557522123896,"last_synced_commit":"e935959d2f9cc642bcbb5e7759b2b1e7196b0947"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zeex%2Fsubhook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zeex%2Fsubhook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zeex%2Fsubhook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zeex%2Fsubhook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zeex","download_url":"https://codeload.github.com/Zeex/subhook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219855413,"owners_count":16556096,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cmake","cplusplus","hooking","x86"],"created_at":"2024-09-24T20:54:37.063Z","updated_at":"2024-10-12T16:01:00.340Z","avatar_url":"https://github.com/Zeex.png","language":"C","readme":"[![Build Status][build_status]][build]\n[![Build Status - Windows][build_status_win]][build_win]\n\nSubHook is a simple library for [hooking][wikipedia_hooking] arbitrary functions\nat run time. It's written in C and also provides an optional C++ wrapper API.\n\nThe library was originally developed to intercept a bunch of API calls in the\n[SA-MP server](https://www.sa-mp.com), which is a Windows/Linux 32-bit app, in\norder to extend a plugin that I wrote for it. Since then, it has been adapted\nto better support x86_64, macOS, and more common use-cases, thanks to the\n[contributors][github_contributors].\n\nInstallation\n------------\n\nEasy method:\n\n1. Copy the source and header files to your project and include\n   [`subhook.c`](subhook.c) in your build.\n2. On Windows only: Define `SUBHOOK_STATIC` before including `subhook.h`.\n\nWith CMake:\n\n1. Copy the subhook repo to your project tree.\n2. Call `add_subdirectory(path/to/subhook)` in your CMakeLists.txt.\n3. Optional: configure how the library is built by setting these varaible prior\n   to `add_subdirectory(...)`:\n\n   * `SUBHOOK_STATIC` - Build as static library (`OFF` by default)\n   * `SUBHOOK_INSTALL` - Enable installation and packaging of targets/files\n     with CPack (`OFF` by default)\n   * `SUBHOOK_TESTS` - Enable tests (`ON` by default)\n   * `SUBHOOK_FORCE_32BIT` - Configure for compiling 32-bit binaries on 64-bit\n     systems (default is `OFF`)\n\nUse of CMake is not mandatory, the library can be built without it (no extra\nbuild configuration is required).\n\nExamples\n--------\n\nIn the following examples `foo` is some function or a function pointer that\ntakes a single argument of type `int` and uses the same calling convention\nas `my_foo` (depends on compiler).\n\n### Basic usage\n\n```c\n#include \u003cstdio.h\u003e\n#include \u003csubhook.h\u003e\n\nsubhook_t foo_hook;\n\nvoid my_foo(int x) {\n  /* Remove the hook so that you can call the original function. */\n  subhook_remove(foo_hook);\n\n  printf(\"foo(%d) called\\n\", x);\n  foo(x);\n\n  /* Install the hook back to intercept further calls. */\n  subhook_install(foo_hook);\n}\n\nint main() {\n  /* Create a hook that will redirect all foo() calls to to my_foo(). */\n  foo_hook = subhook_new((void *)foo, (void *)my_foo, 0);\n\n  /* Install it. */\n  subhook_install(foo_hook);\n\n  foo(123);\n\n  /* Remove the hook and free memory when you're done. */\n  subhook_remove(foo_hook);\n  subhook_free(foo_hook);\n}\n```\n\n### Trampolines\n\nUsing trampolines allows you to jump to the original code without removing\nand re-installing hooks every time your function gets called.\n\n```c\ntypedef void (*foo_func)(int x);\n\nvoid my_foo(int x) {\n  printf(\"foo(%d) called\\n\", x);\n\n  /* Call foo() via trampoline. */\n  ((foo_func)subhook_get_trampoline(foo_hook))(x);\n}\n\nint main() {\n   /* Same code as in the previous example. */\n}\n```\n\nPlease note that subhook has a very simple length disassmebler engine (LDE)\nthat works only with most common prologue instructions like push, mov, call,\netc. When it encounters an unknown instruction subhook_get_trampoline() will\nreturn NULL. You can delegate instruction decoding to a custom disassembler\nof your choice via `subhook_set_disasm_handler()`.\n\n### C++\n\n```c++\n#include \u003ciostream\u003e\n#include \u003csubhook.h\u003e\n\nsubhook::Hook foo_hook;\nsubhook::Hook foo_hook_tr;\n\ntypedef void (*foo_func)(int x);\n\nvoid my_foo(int x) {\n  // ScopedHookRemove removes the specified hook and automatically re-installs\n  // it when the object goes out of scope (thanks to C++ destructors).\n  subhook::ScopedHookRemove remove(\u0026foo_hook);\n\n  std::cout \u003c\u003c \"foo(\" \u003c\u003c x \u003c\u003c \") called\" \u003c\u003c std::endl;\n  foo(x + 1);\n}\n\nvoid my_foo_tr(int x) {\n  std::cout \u003c\u003c \"foo(\" \u003c\u003c x \u003c\u003c \") called\" \u003c\u003c std::endl;\n\n  // Call the original function via trampoline.\n  ((foo_func)foo_hook_tr.GetTrampoline())(x + 1);\n}\n\nint main() {\n  foo_hook.Install((void *)foo, (void *)my_foo);\n  foo_hook_tr.Install((void *)foo, (void *)my_foo_tr);\n}\n```\n\nKnown issues/limitations\n------------------------\n\n* `subhook_get_trampoline()` may return NULL because only a small subset of\n  x86 instructions is supported by the disassembler in this library (just\n  common prologue instructions). As a workaround you can plug in a more\n  advanced instruction length decoder using `subhook_set_disasm_handler()`.\n\n* If a target function (the function you are hooking) is less than N bytes\n  in length, for example if it's a short 2-byte jump to a nearby location\n  (sometimes compilers generate code like this), then you will not be able\n  to hook it.\n\n  N is 5 by default: 1 byte for jmp opcode + 4 bytes for offset. But if you\n  enable the use of 64-bit offsets in 64-bit mode N becomes 14 (see the\n  definition of `subhook_jmp64`).\n\n  On x64_64, another cause could be that the function contains instructions\n  referencing memory that is too far away from the trampline code buffer's\n  address `trampoline_addr`, such as `cmp dword ptr [some_32bit_addr], rax`\n  (i.e. RIP-relative addressing) where the offset between `some_32bit_addr`\n  and `trampoline_addr` cannot fit into 32 bits, and therefore we cannot\n  update the memory address referenced in the original code (we need to do\n  that because because it's relative).\n\n* Some systems protect executable code form being modified at runtime, which\n  will not allow you to install hooks, or don't allow to mark heap-allocated\n  memory as executable, which prevents the use of trampolines.\n\n  For example, on Fedora you can have such problems because of SELinux (though\n  you can disable it or exclude your files).\n\nLicense\n-------\n\nLicensed under the 2-clause BSD license.\n\n[build]: https://travis-ci.org/Zeex/subhook\n[build_status]: https://travis-ci.org/Zeex/subhook.svg?branch=master\n[build_win]: https://ci.appveyor.com/project/Zeex/subhook/branch/master\n[build_status_win]: https://ci.appveyor.com/api/projects/status/q5sp0p8ahuqfh8e4/branch/master?svg=true\n[wikipedia_hooking]: https://en.wikipedia.org/wiki/Hooking\n[github_contributors]: https://github.com/Zeex/subhook/graphs/contributors\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeex%2Fsubhook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeex%2Fsubhook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeex%2Fsubhook/lists"}