Skip to content

Commit 3398484

Browse files
committed
Android Native 函数 Hook 技术介绍
1 parent 76261f1 commit 3398484

16 files changed

+934
-1
lines changed

app/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@ android {
6868
}
6969
buildFeatures {
7070
compose = true
71+
prefab = true
7172
}
7273
ndkVersion = "27.1.12297006"
7374
}
7475

7576
dependencies {
7677
implementation(project(":vmp"))
78+
implementation(libs.libsu.core)
79+
implementation(libs.libsu.service)
80+
implementation(libs.libsu.nio)
81+
implementation(libs.shadowhook)
7782
implementation(libs.androidx.core.ktx)
7883
implementation(libs.androidx.lifecycle.runtime.ktx)
7984
implementation(libs.androidx.activity.compose)

app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@
1818
android:theme="@style/Theme.AndroidExample"
1919
tools:ignore="HardcodedDebugMode"
2020
tools:targetApi="31">
21+
<activity
22+
android:name=".root.RootActivity"
23+
android:exported="false" />
24+
<activity
25+
android:name=".hook.HookActivity"
26+
android:exported="false" />
2127
<activity
2228
android:name=".hotfix.HotFixActivity"
2329
android:exported="false" />
2430
<activity
2531
android:name=".plugin.PluginActivity"
26-
android:exported="true"/>
32+
android:exported="true" />
2733
<activity
2834
android:name=".classloader.ClassLoaderActivity"
2935
android:exported="false" />

app/src/main/cpp/CMakeLists.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,34 @@ target_link_libraries(
342342
# 链接 LibTomCrypt
343343
libtomcrypt
344344
)
345+
346+
## CyrusStudioHook ##########################################################################################
347+
348+
find_package(shadowhook REQUIRED CONFIG)
349+
350+
add_library( # 设置库的名称
351+
cyrus_studio_hook
352+
353+
# 设置库的类型
354+
SHARED
355+
356+
# 设置源文件路径
357+
cyrus_studio_hook.cpp
358+
)
359+
360+
# 为 cyrus_studio_hook 动态库启用字符串加密
361+
target_compile_options(
362+
cyrus_studio_hook
363+
PRIVATE
364+
-mllvm -sobf)
365+
366+
# 抹除符号
367+
set_target_properties(aes PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/hide.map")
368+
369+
target_link_libraries(
370+
cyrus_studio_hook
371+
# 链接 log 库
372+
${log-lib}
373+
# 链接 shadowhook
374+
shadowhook::shadowhook
375+
)
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#include <unistd.h>
2+
#include <android/log.h>
3+
#include <jni.h>
4+
#include <string>
5+
#include <stddef.h>
6+
#include "shadowhook.h"
7+
8+
#include "dex/dex_file.h"
9+
#include "dex/art_method.h"
10+
#include "dex/class_accessor.h"
11+
12+
using namespace cyurs;
13+
14+
#define LOG_TAG "cyrus_studio_hook"
15+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
16+
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
17+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
18+
19+
20+
int g_sdkLevel = 0;
21+
22+
__attribute__ ((constructor)) void init() {
23+
// api level
24+
g_sdkLevel = android_get_device_api_level();
25+
}
26+
27+
// 原始 execve 函数指针
28+
int (*orig_execve)(const char *__file, char *const *__argv, char *const *__envp);
29+
30+
// 替代 execve 实现
31+
int my_execve(const char *__file, char *const *__argv, char *const *__envp) {
32+
33+
LOGI("execve called: %s", __file);
34+
35+
if (__file && strstr(__file, "dex2oat")) {
36+
LOGW("Blocked dex2oat execution: %s", __file);
37+
// 返回失败,模拟 dex2oat 调用失败
38+
return -1;
39+
}
40+
41+
// 调用原始 execve
42+
return orig_execve(__file, __argv, __envp);
43+
}
44+
45+
extern "C"
46+
JNIEXPORT void JNICALL
47+
Java_com_cyrus_example_hook_CyrusStudioHook_hookExecve(JNIEnv *, jclass) {
48+
void *handle = shadowhook_hook_sym_name(
49+
"libc.so", // 函数所在模块
50+
"execve", // 要 hook 的符号名
51+
reinterpret_cast<void *>(my_execve),
52+
reinterpret_cast<void **>(&orig_execve)
53+
);
54+
55+
if (handle != nullptr) {
56+
LOGI("Successfully hooked execve");
57+
} else {
58+
LOGW("Failed to hook execve");
59+
}
60+
}
61+
62+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
63+
extern char **environ;
64+
65+
extern "C"
66+
JNIEXPORT jboolean JNICALL
67+
Java_com_cyrus_example_hook_CyrusStudioHook_dex2oat(JNIEnv *env, jclass,
68+
jstring dexPath_,
69+
jstring oatPath_) {
70+
const char *dexPath = env->GetStringUTFChars(dexPath_, nullptr);
71+
const char *oatPath = env->GetStringUTFChars(oatPath_, nullptr);
72+
73+
const char *dex2oatPath = "/system/bin/dex2oat";
74+
75+
char dexArg[256];
76+
char oatArg[256];
77+
char isaArg[] = "--instruction-set=arm64";
78+
snprintf(dexArg, sizeof(dexArg), "--dex-file=%s", dexPath);
79+
snprintf(oatArg, sizeof(oatArg), "--oat-file=%s", oatPath);
80+
81+
const char *argv[] = {
82+
dex2oatPath,
83+
dexArg,
84+
oatArg,
85+
isaArg,
86+
"--runtime-arg", "-Xbootclasspath:/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar",
87+
"--boot-image=/apex/com.android.art/javalib/boot.art",
88+
nullptr
89+
};
90+
91+
LOGI("Calling dex2oat with: %s %s %s", dexArg, oatArg, isaArg);
92+
93+
int ret = execve(dex2oatPath, (char *const *) argv, environ);
94+
95+
LOGE("execve called with ret: %s", ret);
96+
97+
env->ReleaseStringUTFChars(dexPath_, dexPath);
98+
env->ReleaseStringUTFChars(oatPath_, oatPath);
99+
100+
return false; // execve 不返回,除非失败
101+
}
102+
103+
104+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105+
106+
void *(*orig_LoadMethod)(void *, void *, void *, void *, void *);
107+
108+
void *my_LoadMethod(void *linker, void *dex_file, void *method, void *klass_handle, void *dst) {
109+
110+
// DexFile
111+
std::string location;
112+
uint8_t *begin = nullptr;
113+
uint64_t dexSize = 0;
114+
if (g_sdkLevel >= 35) {
115+
auto *dexFileV35 = (V35::DexFile *) dex_file;
116+
location = dexFileV35->location_;
117+
begin = (uint8_t *) dexFileV35->begin_;
118+
dexSize = dexFileV35->header_->file_size_;
119+
} else if (g_sdkLevel >= __ANDROID_API_P__) {
120+
auto *dexFileV28 = (V28::DexFile *) dex_file;
121+
location = dexFileV28->location_;
122+
begin = (uint8_t *) dexFileV28->begin_;
123+
dexSize = dexFileV28->size_ == 0 ? dexFileV28->header_->file_size_ : dexFileV28->size_;
124+
} else {
125+
auto *dexFileV21 = (V21::DexFile *) dex_file;
126+
location = dexFileV21->location_;
127+
begin = (uint8_t *) dexFileV21->begin_;
128+
dexSize = dexFileV21->size_ == 0 ? dexFileV21->header_->file_size_ : dexFileV21->size_;
129+
}
130+
131+
// 打印 DexFile 信息
132+
LOGI("[pid=%d][API=%d] enter my_LoadMethod:\n DexFile Base = %p\n DexFile Size = %zu bytes\n DexFile Location= %s",
133+
getpid(), g_sdkLevel, begin, dexSize, location.c_str());
134+
135+
// 调用原始函数,使 ArtMethod 数据填充完成
136+
void *result = orig_LoadMethod(linker, dex_file, method, klass_handle, dst);
137+
138+
// ArtMethod
139+
uint32_t dex_code_item_offset_ = -1;
140+
uint32_t dex_method_index_;
141+
if (g_sdkLevel >= 31) {
142+
auto *dstV31 = (V31::ArtMethod *) dst;
143+
auto classAccessor_method = reinterpret_cast<Method &>(method);
144+
dex_code_item_offset_ = classAccessor_method.code_off_;
145+
dex_method_index_ = dstV31->dex_method_index_;
146+
} else {
147+
auto *dstV28 = (V28::ArtMethod *) dst;
148+
dex_code_item_offset_ = dstV28->dex_code_item_offset_;
149+
dex_method_index_ = dstV28->dex_method_index_;
150+
}
151+
152+
// 打印 Method 信息
153+
LOGI("[pid=%d][API=%d] enter my_LoadMethod:\n"
154+
" ArtMethod.dex_code_item_offset_ = 0x%x\n"
155+
" ArtMethod.dex_method_index_ = %d",
156+
getpid(), g_sdkLevel, dex_code_item_offset_, dex_method_index_);
157+
158+
return result;
159+
}
160+
161+
162+
extern "C"
163+
JNIEXPORT void JNICALL
164+
Java_com_cyrus_example_hook_CyrusStudioHook_hookLoadMethod(JNIEnv *, jclass) {
165+
void *handle = shadowhook_hook_sym_name(
166+
"libart.so",
167+
"_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_13ClassAccessor6MethodENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE", // 要 hook 的符号名
168+
reinterpret_cast<void *>(my_LoadMethod),
169+
reinterpret_cast<void **>(&orig_LoadMethod)
170+
);
171+
172+
if (handle != nullptr) {
173+
LOGI("Successfully hooked LoadMethod");
174+
} else {
175+
LOGW("Failed to hook LoadMethod");
176+
}
177+
}

app/src/main/cpp/dex/art_method.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#ifndef CYRUS_ART_METHOD_H
2+
#define CYRUS_ART_METHOD_H
3+
4+
#include <stdint.h>
5+
#include <string>
6+
7+
namespace cyurs {
8+
9+
// android9+
10+
namespace V28 {
11+
class ArtMethod {
12+
public:
13+
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
14+
// The class we are a part of.
15+
uint8_t declaring_class_;
16+
17+
// Access flags; low 16 bits are defined by spec.
18+
// Getting and setting this flag needs to be atomic when concurrency is
19+
// possible, e.g. after this method's class is linked. Such as when setting
20+
// verifier flags and single-implementation flag.
21+
uint32_t access_flags_;
22+
23+
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
24+
25+
// Offset to the CodeItem.
26+
uint32_t dex_code_item_offset_;
27+
28+
// Index into method_ids of the dex file associated with this method.
29+
uint32_t dex_method_index_;
30+
31+
/* End of dex file fields. */
32+
33+
// Entry within a dispatch table for this method. For static/direct methods the index is into
34+
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
35+
// ifTable.
36+
uint16_t method_index_;
37+
38+
union {
39+
// Non-abstract methods: The hotness we measure for this method. Not atomic,
40+
// as we allow missing increments: if the method is hot, we will see it eventually.
41+
uint16_t hotness_count_;
42+
// Abstract methods: IMT index (bitwise negated) or zero if it was not cached.
43+
// The negation is needed to distinguish zero index and missing cached entry.
44+
uint16_t imt_index_;
45+
};
46+
};
47+
48+
} //namespace V28
49+
50+
51+
// android 12 开始去掉了 dex_code_item_offset_ 字段
52+
namespace V31 {
53+
54+
class ArtMethod {
55+
public:
56+
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
57+
// The class we are a part of.
58+
uint8_t declaring_class_;
59+
60+
// Access flags; low 16 bits are defined by spec.
61+
// Getting and setting this flag needs to be atomic when concurrency is
62+
// possible, e.g. after this method's class is linked. Such as when setting
63+
// verifier flags and single-implementation flag.
64+
uint32_t access_flags_;
65+
66+
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
67+
68+
// Index into method_ids of the dex file associated with this method.
69+
uint32_t dex_method_index_;
70+
71+
/* End of dex file fields. */
72+
73+
// Entry within a dispatch table for this method. For static/direct methods the index is into
74+
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
75+
// ifTable.
76+
uint16_t method_index_;
77+
};
78+
79+
} //namespace V35
80+
81+
};//namespace cyrus
82+
83+
#endif //CYRUS_ART_METHOD_H

app/src/main/cpp/dex/class_accessor.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef CYRUS_METHOD_H
2+
#define CYRUS_METHOD_H
3+
4+
#include <stdint.h>
5+
#include <string>
6+
7+
8+
namespace cyurs {
9+
10+
class BaseItem {
11+
public:
12+
// Internal data pointer for reading.
13+
const void* dex_file_;
14+
const uint8_t* ptr_pos_ = nullptr;
15+
const uint8_t* hiddenapi_ptr_pos_ = nullptr;
16+
uint32_t index_ = 0u;
17+
uint32_t access_flags_ = 0u;
18+
uint32_t hiddenapi_flags_ = 0u;
19+
};
20+
21+
// A decoded version of the method of a class_data_item.
22+
class Method : public BaseItem {
23+
public:
24+
bool is_static_or_direct_ = true;
25+
uint32_t code_off_ = 0u;
26+
};
27+
28+
};//namespace cyrus
29+
30+
#endif //CYRUS_METHOD_H

0 commit comments

Comments
 (0)