-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1b1bb44
commit 87c9624
Showing
58 changed files
with
8,470 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
/build | ||
/.cxx | ||
/.gradle | ||
# Prerequisites | ||
*.d | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
# fake-linker | ||
Modify Android linker to provide loading module and hook function | ||
![NPM](https://img.shields.io/badge/License-Apache2-blue)[![文档](https://img.shields.io/badge/Document-Chinese-green.svg)](README_CN.md) | ||
|
||
Modify Android linker to provide loading module and hook function. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# fake-linker | ||
|
||
## 项目描述 | ||
|
||
进程内动态查找和修改 `Linker` 数据,对外提供基于 `LD_PRELOAD` 模式加载 `PLT hook` 模块和各种操作 `soinfo`、`namespace`的功能。原理分析请查看[Android 动态修改Linker实现LD_PRELOAD全局库PLT Hook](https://sanfengandroid.github.io/2021/01/10/Android-%E5%8A%A8%E6%80%81%E4%BF%AE%E6%94%B9Linker%E5%AE%9E%E7%8E%B0LD-PRELOAD%E5%85%A8%E5%B1%80%E5%BA%93PLT-Hook/) | ||
|
||
## 支持的Android版本 | ||
|
||
支持版本:`Android 5.0 ~ Android 11+`,支持架构:`x86`,`x86_64`,`arm`,`arm64` | ||
|
||
## 构建 | ||
|
||
1. 源码构建 | ||
> 将其作为 `Android Library` 添加到 Android 项目中,主模块添加其依赖,修改[`build.gradle`](build.gradle)中的 `buildApi` 变量编译指定Api等级 | ||
2. 使用构建库 | ||
> 将下载的 `native` 库导入到 Android 项目中的jniLibs.srcDirs | ||
## Hook 模块开发 | ||
|
||
1. 复制导出头文件(源码在 `cpp` 目录下的 [export](src/main/cpp/export) 目录)到 `Hook` 模块 | ||
2. 自定义实现[`linker_export.h`](src/main/cpp/export/linker_export.h)中的 `fake_load_library_init` 导出函数 | ||
3. 调用各种实现方法,查看[`linker_export.h`](src/main/cpp/export/linker_export.h)中的 `RemoteInvokeInterface` 定义 | ||
4. 正常编写Hook方法如: `open`,`dlopen`,`dlsym`方法等等 | ||
5. Hook模块区分Android7.0以下(无命名空间,`soinfo handle`),Android7.0及以上(有命名空间,`soinfo handle`) | ||
|
||
## Java层初始化 | ||
|
||
1. 正确安装库到指定位置,将 `fake-linker` 和 `Hook` 模块安装到应用有权限访问的路径,如: `/data/local/tmp`,自身使用则可调用系统方法直接加载 | ||
模块已经集成了安装可执行文件,Java层调用 [FileInstaller](src/main/java/com/sanfengandroid/fakelinker/FileInstaller.java) 类下的方法安装,内部已经处理了各个不同的平台架构 | ||
2. 通过`FileInstaller`设置对于不同Hook需要设置不同的 `Selinux` 和 `uid, gid` 文件属性 | ||
3. 加载并初始化 `fake-linker` 模块,调用[`FakeLinker.initFakeLinker`](src/main/java/com/sanfengandroid/fakelinker/FakeLinker.java)方法,内部会加载Hook模块并回调 `fake_load_library_init` 方法完成模块初始化 | ||
|
||
## 其它描述 | ||
|
||
1. 项目不同于直接设置 `LD_PRELOAD` 环境变量,直接设置通常无法拦截 `dlsym` 方法,因为一旦拦截则需要自己实现查找符号且高版本还有`caller`地址限制,而该模块通过中转模块 `fake-linker` 提供调用`dlsym`方法,因此Hook模块可以拦截`dlsym`,且提供更多 `Linker` 相关功能 | ||
2. Android各个版本`Linker`都有对应的修改,因此模块依赖手机的 `api` 等级,不同等级加载对应模块即可,内部使用`NDK`版本为最新版,该版本已经删除 `Api-25`,代码内是支持 `Api-25` 需要切换低版本`NDK`来编译,需要自己适配代码 | ||
3. 其它更多使用请等待后续另一仓库 | ||
|
||
## 注意事项 | ||
|
||
1. Hook系统进程时请做好备份删除工作,避免因为出错导致系统进程死亡无法开机的情况 | ||
2. 根据模块加载时机,重定位已经加载的模块 | ||
## 使用示例 | ||
1. 将`Hook`模块设置为全局库 `remote->CallCommonFunction(kCFAddSoinfoToGlobal, kSPAddress, nullptr, kSPNull, nullptr, &error_code);` | ||
2. 重定位一些已经加载完成的模块 `remote->CallCommonFunction(kCFCallManualRelinks, kSPAddress, nullptr, kSPNames, libs, &error_code);` | ||
```c++ | ||
static const RemoteInvokeInterface *remote; | ||
static jint HookJniRegisterNatives(JNIEnv *env, jclass c, const JNINativeMethod *methods, jint nMethods) { | ||
LOG("start register native function %p", __builtin_return_address(0)); | ||
jint ret = original_functions->RegisterNatives(env, c, methods, nMethods); | ||
if (ret != JNI_ERR && !original_functions->ExceptionCheck(env)) { | ||
std::string cls = JNIHelper::GetClassName(env, c); | ||
for (int i = 0; i < nMethods; ++i) { | ||
LOG("native register class: %s, method name: %s, function signature: %s, register address: %p", cls.c_str(), methods[i].name, methods[i].signature, methods[i].fnPtr); | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
static void InitHook() { | ||
int error_code; | ||
// 将本模块添加到全局模块中,这会影响之后加载的所有模块 | ||
remote->CallCommonFunction(kCFAddSoinfoToGlobal, kSPAddress, nullptr, kSPNull, nullptr, &error_code); | ||
if (error_code != kErrorNo) { | ||
LOGE("init global soinfo error, error code: %x", error_code); | ||
return; | ||
} | ||
VarLengthObject<const char *> *libs; | ||
// 重新解析以下模块的导入表,因为以下模块在我们还未加载前就已经加载完成了,所有重新链接使其符号链接到我们的Hook方法 | ||
// java代码也主要使用是以下几个库,重链接也意味着Hook的java的核心导入函数 | ||
libs = VaArgsToVarLengthObject<const char *>(5, "libjavacore.so", "libnativehelper.so", "libnativeloader.so", "libart.so", "libopenjdk.so"); | ||
remote->CallCommonFunction(kCFCallManualRelinks, kSPAddress, nullptr, kSPNames, libs, &error_code); | ||
// Hook JNI 接口,监听RegisterNatives方法 | ||
remote_->HookJniNative(offsetof(JNINativeInterface, RegisterNatives), (void *)HookJniRegisterNatives, nullptr); | ||
} | ||
C_API void fake_load_library_init(JNIEnv *env, void *fake_soinfo, const RemoteInvokeInterface *interface, | ||
const char *cache_path, const char *config_path, const char *_process_name){ | ||
remote = interface; | ||
InitHook(); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
* Copyright (c) 2020 XpFilter by beich. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
plugins { | ||
id 'com.android.library' | ||
} | ||
|
||
def buildApi = rootProject.ext.buildApi | ||
def linkerModuleName = 'fake-linker' | ||
def hookInstallModuleName = 'hook-install' | ||
def mVersionCode = 1000 | ||
def mVersionName = "fake-linker-1.0" | ||
|
||
android { | ||
compileSdkVersion 30 | ||
buildToolsVersion "30.0.3" | ||
|
||
defaultConfig { | ||
minSdkVersion 21 | ||
targetSdkVersion 30 | ||
versionCode mVersionCode | ||
versionName mVersionName | ||
|
||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||
consumerProguardFiles "consumer-rules.pro" | ||
|
||
externalNativeBuild { | ||
cmake { | ||
cppFlags "" | ||
cFlags "" | ||
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64' | ||
arguments "-DANDROID_PLATFORM=${buildApi}", "-DLINKER_MODULE_NAME=${linkerModuleName}", | ||
"-DHOOK_INSTALL_MODULE_NAME=${hookInstallModuleName}", | ||
"-DHOOK_LOG_LEVEL=${rootProject.ext.logLevel}", | ||
"-DMODULE_VERSION=${mVersionCode}", | ||
"-DMODULE_VERSION_NAME=\"${mVersionName}\"" | ||
|
||
} | ||
} | ||
|
||
buildConfigField 'String', 'LINKER_MODULE_NAME', "\"${linkerModuleName}\"" | ||
buildConfigField 'String', 'HOOK_INSTALL_MODULE_NAME', "\"${hookInstallModuleName}\"" | ||
} | ||
|
||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_8 | ||
targetCompatibility JavaVersion.VERSION_1_8 | ||
} | ||
sourceSets{ | ||
release{ | ||
jniLibs.srcDirs = ["src/main/libs/release"] | ||
} | ||
debug{ | ||
jniLibs.srcDirs = ["src/main/libs/debug"] | ||
} | ||
} | ||
|
||
externalNativeBuild { | ||
cmake { | ||
path "src/main/cpp/CMakeLists.txt" | ||
} | ||
} | ||
ndkVersion '22.0.7026061' | ||
} | ||
|
||
tasks.whenTaskAdded { task -> | ||
if (task.name.startsWith('merge') && task.name.endsWith('JniLibFolders')) { | ||
task.doLast { | ||
it.outputs.files.files.each { File dir -> | ||
dir.eachFileRecurse { | ||
if (it.name == "lib${linkerModuleName}-${buildApi}.so") { | ||
println '找到当前正在编译的fake-linker模块版本,需要删除' | ||
it.delete() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
dependencies { | ||
|
||
implementation 'androidx.appcompat:appcompat:1.2.0' | ||
implementation 'com.google.android.material:material:1.2.1' | ||
testImplementation 'junit:junit:4.13.1' | ||
implementation 'eu.chainfire:libsuperuser:1.0.0.201811281328' | ||
androidTestImplementation 'androidx.test.ext:junit:1.1.2' | ||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="utf-8"?><!-- | ||
~ Copyright (c) 2020 XpFilter by beich. | ||
~ | ||
~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
~ you may not use this file except in compliance with the License. | ||
~ You may obtain a copy of the License at | ||
~ | ||
~ http://www.apache.org/licenses/LICENSE-2.0 | ||
~ | ||
~ Unless required by applicable law or agreed to in writing, software | ||
~ distributed under the License is distributed on an "AS IS" BASIS, | ||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
~ See the License for the specific language governing permissions and | ||
~ limitations under the License. | ||
--> | ||
|
||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.sanfengandroid.fakelinker"> | ||
</manifest> |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# For more information about using CMake with Android Studio, read the | ||
# documentation: https://d.android.com/studio/projects/add-native-code.html | ||
|
||
# Sets the minimum version of CMake required to build the native library. | ||
|
||
cmake_minimum_required(VERSION 3.4.1) | ||
|
||
# Creates and names a library, sets it as either STATIC | ||
# or SHARED, and provides the relative paths to its source code. | ||
# You can define multiple libraries, and CMake builds them for you. | ||
# Gradle automatically packages shared libraries with your APK. | ||
string(TOLOWER ${CMAKE_BUILD_TYPE} THIS_BUILD_TYPE) | ||
|
||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Os -Oz -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -Werror") | ||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -Oz -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden -Werror") | ||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") | ||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror") | ||
|
||
if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a" OR ${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") | ||
add_definitions(-DUSE_RELA) | ||
else () | ||
add_definitions(-D__work_around_b_24465209__) | ||
endif () | ||
|
||
if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a" OR ${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") | ||
SET(NEON_SRC linker/linker_gnu_hash_neon.cpp) | ||
else () | ||
SET(NEON_SRC) | ||
endif () | ||
|
||
include_directories(common) | ||
include_directories(export) | ||
|
||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../libs/${THIS_BUILD_TYPE}/${CMAKE_ANDROID_ARCH_ABI}) | ||
message(WARNING "当前版本:${ANDROID_PLATFORM},输出目录:${CMAKE_LIBRARY_OUTPUT_DIRECTORY}, 编译类型:${THIS_BUILD_TYPE},模块名:${LINKER_MODULE_NAME},安装模块${HOOK_INSTALL_MODULE_NAME}") | ||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/module_config.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/common/module_config.h") | ||
|
||
add_library( # Sets the name of the library. | ||
${LINKER_MODULE_NAME}-${ANDROID_PLATFORM} | ||
|
||
# Sets the library as a shared library. | ||
SHARED | ||
|
||
# Provides a relative path to your source file(s). | ||
linker_main.cpp | ||
|
||
linker/fake_linker.cpp | ||
linker/linker_globals.cpp | ||
linker/linker_namespaces.cpp | ||
linker/linker_soinfo.cpp | ||
linker/linker_util.cpp | ||
linker/linker_common_types.cpp | ||
linker/linker_block_allocator.cpp | ||
linker/local_block_allocator.cpp | ||
linker/linker_export.cpp | ||
${NEON_SRC} | ||
|
||
# JNI Hook | ||
linker/art/hook_jni_native_interface_impl.cpp | ||
# JNI Common | ||
common/jni_helper.cpp | ||
common/maps_util.cpp | ||
) | ||
|
||
add_executable( | ||
${HOOK_INSTALL_MODULE_NAME} | ||
|
||
installer/hook_installer.cpp | ||
) | ||
|
||
# Searches for a specified prebuilt library and stores the path as a | ||
# variable. Because CMake includes system libraries in the search path by | ||
# default, you only need to specify the name of the public NDK library | ||
# you want to add. CMake verifies that the library exists before | ||
# completing its build. | ||
|
||
find_library( # Sets the name of the path variable. | ||
log-lib | ||
|
||
# Specifies the name of the NDK library that | ||
# you want CMake to locate. | ||
log) | ||
|
||
|
||
# Specifies libraries CMake should link to your target library. You | ||
# can link multiple libraries, such as libraries you define in this | ||
# build script, prebuilt third-party libraries, or system libraries. | ||
|
||
target_link_libraries( # Specifies the target library. | ||
${LINKER_MODULE_NAME}-${ANDROID_PLATFORM} | ||
|
||
# Links the target library to the log library | ||
# included in the NDK. | ||
${log-lib}) | ||
# 设置 CMAKE_LIBRARY_OUTPUT_DIRECTORY 不生效,不知为何,设置RUNTIME_OUTPUT_DIRECTORY生效 | ||
set_target_properties(${LINKER_MODULE_NAME}-${ANDROID_PLATFORM} PROPERTIES | ||
RUNTIME_OUTPUT_DIRECTORY | ||
${CMAKE_CURRENT_SOURCE_DIR}/../libs/${THIS_BUILD_TYPE}/${CMAKE_ANDROID_ARCH_ABI}) | ||
|
||
target_link_libraries( | ||
${HOOK_INSTALL_MODULE_NAME} | ||
${log-lib}) | ||
|
||
set_target_properties(${HOOK_INSTALL_MODULE_NAME} PROPERTIES | ||
RUNTIME_OUTPUT_DIRECTORY | ||
${CMAKE_CURRENT_SOURCE_DIR}/../assets/${CMAKE_ANDROID_ARCH_ABI}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// Created by beich on 2020/11/6. | ||
// | ||
#pragma once | ||
|
||
#include <android/log.h> | ||
|
||
extern int g_log_level; | ||
#define LOG_TAG "HookLog" | ||
#define _PRINT(v, format, ...) \ | ||
do { \ | ||
if(g_log_level < (v)) __android_log_print(v, LOG_TAG, format, ##__VA_ARGS__); \ | ||
}while(0) | ||
|
||
#define LOGV(format, ...) _PRINT(ANDROID_LOG_VERBOSE, format, ##__VA_ARGS__) | ||
#define LOGD(format, ...) _PRINT(ANDROID_LOG_DEBUG, format, ##__VA_ARGS__) | ||
#define LOGI(format, ...) _PRINT(ANDROID_LOG_INFO, format, ##__VA_ARGS__) | ||
#define LOGW(format, ...) _PRINT(ANDROID_LOG_WARN, format, ##__VA_ARGS__) | ||
#define LOGE(format, ...) _PRINT(ANDROID_LOG_ERROR, format, ##__VA_ARGS__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// Created by lenovo-s on 2019/4/15. | ||
// | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
|
||
#define LINE_MAX 4096 | ||
|
||
typedef void *gpointer; | ||
typedef intptr_t gssize; | ||
typedef uintptr_t gsize; | ||
typedef uint64_t gaddress; | ||
#define GSIZE_TO_POINTER(s) ((gpointer) (gsize) (s)) | ||
#define GPOINTER_TO_SIZE(p) ((gsize) (p)) |
Oops, something went wrong.