https://github.com/bytedance/android-inline-hook

:fire: ShadowHook is an Android inline hook library which supports thumb, arm32 and arm64.

https://github.com/bytedance/android-inline-hook

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (13.0%) to scientific vocabulary

Keywords

android androidinlinehook arm arm64 hook inline inlinehook jni ndk security thumb
Last synced: 5 months ago · JSON representation

Repository

:fire: ShadowHook is an Android inline hook library which supports thumb, arm32 and arm64.

Basic Info
Statistics
  • Stars: 2,016
  • Watchers: 47
  • Forks: 337
  • Open Issues: 10
  • Releases: 10
Topics
android androidinlinehook arm arm64 hook inline inlinehook jni ndk security thumb
Created about 4 years ago · Last pushed 7 months ago
Metadata Files
Readme Contributing License Code of conduct Security

README.md

shadowhook

简体中文

shadowhook is an Android inline hook library. Its goals are:

  • Stability - Can be stably used in production apps.
  • Compatibility - Always maintains backward compatibility of API and ABI in new versions.
  • Performance - Continuously reduces API call overhead and additional runtime overhead introduced by hooks.
  • Functionality - Besides basic hook functionality, provides general solutions for "hook-related" issues.

If you need an Android PLT hook library, try ByteHook.

Features

  • Supports armeabi-v7a and arm64-v8a.
  • Supports Android 4.1 - 16 (API level 16 - 36).
  • Supports hook and intercept.
  • Supports specifying hook and intercept target locations via "address" or "library name + function name".
  • Automatically completes hook and intercept for "newly loaded ELFs", with optional callbacks after execution.
  • Automatically prevents recursive circular calls between proxy functions.
  • Supports hook and intercept operation recording, which can be exported at any time.
  • Supports registering callbacks before and after linker calls .init + .init_array and .fini + .fini_array of newly loaded ELFs.
  • Supports bypassing linker namespace restrictions to query symbol addresses in .dynsym and .symtab of all ELFs in the process.
  • Compatible with CFI unwind and FP unwind in hook proxy functions and interceptor functions.
  • Licensed under the MIT license.

Documentation

shadowhook Manual

[!CAUTION] The "Quick Start" below will get your DEMO running. However, shadowhook is not just a few hook APIs. To use shadowhook stably in production apps and fully leverage its capabilities, please be sure to read the "shadowhook Manual".

Quick Start

1. Add dependencies in build.gradle

shadowhook is published on Maven Central. To use native dependencies, shadowhook uses the Prefab package format, which is supported from Android Gradle Plugin 4.0.

Gradle allprojects { repositories { mavenCentral() } }

```Gradle android { buildFeatures { prefab true } }

dependencies { implementation 'com.bytedance.android:shadowhook:x.y.z' } ```

Replace x.y.z with the version number. It's recommended to use the latest release version.

Note: shadowhook uses prefab package schema v2, which is the default configuration from Android Gradle Plugin 7.1.0. If you're using a version of Android Gradle Plugin prior to 7.1.0, add the following configuration to gradle.properties:

android.prefabVersion=2.0.0

2. Add dependencies in CMakeLists.txt or Android.mk

CMakeLists.txt

```CMake find_package(shadowhook REQUIRED CONFIG)

addlibrary(mylib SHARED mylib.c) targetlink_libraries(mylib shadowhook::shadowhook) ```

Android.mk

``` include $(CLEARVARS) LOCALMODULE := mylib LOCALSRCFILES := mylib.c LOCALSHAREDLIBRARIES += shadowhook include $(BUILDSHAREDLIBRARY)

$(call import-module,prefab/shadowhook) ```

3. Specify one or more ABIs you need

Gradle android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' } } }

4. Add packaging options

shadowhook includes two .so files: libshadowhook.so and libshadowhook_nothing.so.

If you're using shadowhook in an SDK project, you need to avoid packaging libshadowhook.so and libshadowhook_nothing.so into your AAR to prevent duplicate .so file issues when packaging the app.

Gradle android { packagingOptions { exclude '**/libshadowhook.so' exclude '**/libshadowhook_nothing.so' } }

On the other hand, if you're using shadowhook in an APP project, you may need to add some options to handle conflicts caused by duplicate .so files. However, this may cause the APP to use an incorrect version of shadowhook.

Gradle android { packagingOptions { pickFirst '**/libshadowhook.so' pickFirst '**/libshadowhook_nothing.so' } }

5. Initialize

shadowhook supports three modes (shared, multi, unique). You can try the unique mode first.

```Java import com.bytedance.shadowhook.ShadowHook;

public class MySdk { public void init() { ShadowHook.init(new ShadowHook.ConfigBuilder() .setMode(ShadowHook.Mode.UNIQUE) .build()); } } ```

6. hook and unhook

Hook applies to the entire function. You need to write a proxy function that receives arguments and passes return values in the same way as the original function, which typically means the proxy function needs to be defined with the same type as the original function (including: number of parameters, parameter order, parameter types, return value type). When the hook is successful, when the hooked function is executed, the proxy function will be executed first, and in the proxy function, you can decide whether to call the original function.

Example: hook the art::ArtMethod::Invoke() function in libart.so.

```C void *orig = NULL; void *stub = NULL;

// Type definition of the hooked function typedef void (*artmethodinvokefunctypet)(void *, void *, uint32t *, uint32t, void *, const char *);

// Proxy function void artmethodinvokeproxy(void *thiz, void *thread, uint32t *args, uint32t argssize, void *result, const char *shorty) { // do something ((artmethodinvokefunctypet)orig)(thiz, thread, args, argssize, result, shorty); // do something }

void dohook() { stub = shadowhookhooksymname( "libart.so", "ZN3art9ArtMethod6InvokeEPNS6ThreadEPjjPNS6JValueEPKc", (void *)artmethodinvoke_proxy, (void **)&orig);

if(stub == NULL) {
    int err_num = shadowhook_get_errno();
    const char *err_msg = shadowhook_to_errmsg(err_num);
    LOG("hook error %d - %s", err_num, err_msg);
}

}

void dounhook() { int result = shadowhookunhook(stub);

if(result != 0) {
    int err_num = shadowhook_get_errno();
    const char *err_msg = shadowhook_to_errmsg(err_num);
    LOG("unhook error %d - %s", err_num, err_msg);
}

} ```

  • _ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc is the function symbol name of art::ArtMethod::Invoke in libart.so after C++ Name Mangler processing, which can be viewed using readelf. C functions do not have the concept of Name Mangler.
  • The symbol name of art::ArtMethod::Invoke is different in versions before Android M. This example only applies to Android M and later versions. If you want to achieve better Android version compatibility, you need to handle the differences in function symbol names yourself.

7. intercept and unintercept

Intercept applies to instructions. It can be the first instruction of a function or a certain instruction in the middle of a function. You need to write an interceptor function. When the intercept is successful, when the intercepted instruction is executed, the interceptor function will be executed first. In the interceptor function, you can read and modify the values of registers. After the interceptor function returns, the intercepted instruction will continue to be executed. Intercept is similar to the breakpoint debugging function of a debugger.

Example: intercept a certain instruction in the art::ArtMethod::Invoke() function in libart.so.

```C void *stub;

if defined(aarch64)

void artmethodinvokeinterceptor(shadowhookcpucontext_t *ctx, void *data) { // When x19 equals 0, modify the values of x20 and x21 if (ctx->regs[19] == 0) { ctx->regs[20] = 1; ctx->regs[21] = 1000; LOG("interceptor: found x19 == 0"); }

// When q0 equals 0, modify the values of q0, q1, q2, q3
if (ctx->vregs[0].q == 0) {
    ctx->vregs[0].q = 1;
    ctx->vregs[1].q = 0;
    ctx->vregs[2].q = 0;
    ctx->vregs[3].q = 0;
    LOG("interceptor: found q0 == 0");
}

}

void dointercept(void) { // Query the address of art::ArtMethod::Invoke void *handle = shadowhookdlopen("libart.so"); if (handle == NULL) { LOG("handle not found"); return; } void *symaddr = shadowhookdlsym(handle, "ZN3art9ArtMethod6InvokeEPNS6ThreadEPjjPNS6JValueEPKc"); shadowhookdlclose(handle); if (sym_addr == NULL) { LOG("symbol not found"); return; }

// Locate the address of a certain instruction in art::ArtMethod::Invoke
void *instr_addr = (void *)((uintptr_t)sym_addr + 20);

stub = shadowhook_intercept_instr_addr(
           instr_addr,
           artmethod_invoke_interceptor,
           NULL,
           SHADOWHOOK_INTERCEPT_WITH_FPSIMD_READ_WRITE);

if(stub == NULL) {
    int err_num = shadowhook_get_errno();
    const char *err_msg = shadowhook_to_errmsg(err_num);
    LOG("intercept failed: %d - %s", err_num, err_msg);
}

}

void dounintercept() { int result = shadowhookunintercept(stub);

if (result != 0) {
    int err_num = shadowhook_get_errno();
    const char *err_msg = shadowhook_to_errmsg(err_num);
    LOG("unintercept failed: %d - %s", err_num, err_msg);
}

}

endif

```

  • To simplify the example code, instr_addr is fixed as sym_addr + 20. In real scenarios, memory scanning and other methods are generally used to determine the address of the instruction that needs to be intercepted.
  • Since aarch32 and aarch64 have different registers and the instructions of the same function are different, the intercept logic generally needs to be written separately. Here only includes example code for aarch64.

Feedback

Contributing

License

ShadowHook is licensed under the MIT License.

ShadowHook uses the following third-party source code or libraries:

  • queue.h
    BSD 3-Clause License
    Copyright (c) 1991, 1993 The Regents of the University of California.
  • tree.h
    BSD 2-Clause License
    Copyright (c) 2002 Niels Provos provos@citi.umich.edu
  • linux-syscall-support
    BSD 3-Clause License
    Copyright (c) 2005-2011 Google Inc.
  • xDL
    MIT License
    Copyright (c) 2020-2025 HexHacking Team

Owner

  • Name: Bytedance Inc.
  • Login: bytedance
  • Kind: organization
  • Location: Singapore

GitHub Events

Total
  • Create event: 1
  • Release event: 1
  • Issues event: 20
  • Watch event: 327
  • Issue comment event: 26
  • Push event: 14
  • Pull request event: 4
  • Fork event: 51
Last Year
  • Create event: 1
  • Release event: 1
  • Issues event: 20
  • Watch event: 327
  • Issue comment event: 26
  • Push event: 14
  • Pull request event: 4
  • Fork event: 51

Committers

Last synced: 11 months ago

All Time
  • Total Commits: 89
  • Total Committers: 2
  • Avg Commits per committer: 44.5
  • Development Distribution Score (DDS): 0.045
Past Year
  • Commits: 18
  • Committers: 2
  • Avg Commits per committer: 9.0
  • Development Distribution Score (DDS): 0.111
Top Committers
Name Email Commits
caikelun c****n@g****m 85
xupengying x****y@g****m 4

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 54
  • Total pull requests: 3
  • Average time to close issues: 30 days
  • Average time to close pull requests: about 9 hours
  • Total issue authors: 45
  • Total pull request authors: 3
  • Average comments per issue: 2.35
  • Average comments per pull request: 1.33
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 18
  • Pull requests: 2
  • Average time to close issues: 2 months
  • Average time to close pull requests: about 2 hours
  • Issue authors: 17
  • Pull request authors: 2
  • Average comments per issue: 1.44
  • Average comments per pull request: 1.0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • shuajinanhai (4)
  • supernghia89 (2)
  • c01ns (2)
  • anjiuzhe (2)
  • 9c-x (2)
  • w296488320 (2)
  • qq6r (2)
  • omarsahl (1)
  • zapata-ok (1)
  • SharkFall (1)
  • Yervant7 (1)
  • paycoder (1)
  • ScalletaZ (1)
  • BlackBoxing (1)
  • plakapenka (1)
Pull Request Authors
  • TestPlanB (2)
  • shubhamguptadream11 (1)
  • Mr-JingShi (1)
Top Labels
Issue Labels
bug (36) enhancement (15) invalid (5) good first issue (4) question (1) wontfix (1)
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads: unknown
  • Total dependent packages: 1
  • Total dependent repositories: 3
  • Total versions: 12
repo1.maven.org: com.bytedance.android:shadowhook

ShadowHook is an Android inline hook library which supports thumb, arm32 and arm64.

  • Versions: 12
  • Dependent Packages: 1
  • Dependent Repositories: 3
Rankings
Stargazers count: 12.2%
Dependent repos count: 13.8%
Forks count: 14.7%
Average: 18.4%
Dependent packages count: 33.0%
Last synced: 6 months ago

Dependencies

app/build.gradle maven
  • androidx.appcompat:appcompat 1.5.1 implementation
  • androidx.constraintlayout:constraintlayout 2.1.4 implementation
  • com.google.android.material:material 1.7.0 implementation
build.gradle maven
shadowhook/build.gradle maven
systest/build.gradle maven