本文最后更新于:2025年11月23日 下午
安卓日志来源一般有自身 app 或者第三方 SDK,自己的比较容易归一化处理,优秀的 SDK 也会提供日志策略接口,但有一些喜欢直接用安卓的标准日志接口或者不能设置日志策略,这种就比较讨厌了,处理起来也比较麻烦。有两种办法:混淆时全部去掉或者 hook 系统日志接口。
编译期“阉割”:混淆处理
编译期“阉割”:配置R8/Proguard 日志优化规则,直接把日志方法当空气处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| # 要全局去掉-dontoptimize # 去掉 SDK 的 debug/info 日志 -assumenosideeffects class com.xxx.sdk.internal.SdkLog { public static int d(...); public static int i(...); # 不写 e(...),保留错误日志 }
# 去除全部log -assumenosideeffects class android.util.Log { public static *** d(...); public static *** v(...); public static *** i(...); public static *** e(...); public static *** w(...); }
# 去除System.out.println(); -assumenosideeffects class java.io.PrintStream { public void println(...); }
|
这样的打击范围太大,出现问题时无法收集日志。
hook系统日志打印接口
下面介绍使用字节跳动的bhook和腾讯的xlog来处理日志:前者用来hook日志接口,后者用来储存日志。
日志打印原理:
Android app层或framework层,通过调用Log/Slog/Rlog中的方法打印日志,接着通过JNI会调用到native层android_util_Log_println_native接口,最终通过liblog的__android_log_buf_write方法进行日志打印。
1 2 3 4 5 6 7 8 9 10 11
| __android_log_buf_write
-->__android_log_write_log_message
-->get_logger_function()
-->__android_log_logd_logger
-->write_to_log
-->LogdWrite
|
如果只需要拦截app层或framework层,只需要拦截android_util_Log_println_native 所在的libandroid_runtime.so 即可,android_util_Log_println_native 调用了__android_log_buf_write 方法。
实践
参考:bhook/doc/quickstart.zh-CN.md at main · bytedance/bhook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #include <jni.h> #include <string>
#include <bytehook.h> #include "xlogger/android_xlog.h" #define TAG "wesley-jni"
static bool g_consoleLogOpen = false; static bool g_saveToFile = false;
int android_log_buf_write_proxy(int bufID, int prio, const char *tag, const char *text) { BYTEHOOK_STACK_SCOPE(); int ret = 0; char tag_buffer[50]; if (g_consoleLogOpen) { snprintf(tag_buffer, sizeof(tag_buffer), "wesley-%s", tag); ret = BYTEHOOK_CALL_PREV(android_log_buf_write_proxy, bufID, prio, tag_buffer, text); } if (g_saveToFile) { if (prio >= ANDROID_LOG_ERROR) { XLOGE(tag, "%s", text); } else { XLOGI(tag, "%s", text); } } return ret; }
bool hook_log_allow_filter(const char *caller_path_name, void *arg) {
if(nullptr != strstr(caller_path_name, "libandroid_runtime.so")) return true;
return false; }
bytehook_stub_t stub_of_android_log_buf_write = nullptr; int hook_logcat() {
stub_of_android_log_buf_write = bytehook_hook_partial( hook_log_allow_filter, nullptr, nullptr, "__android_log_buf_write", reinterpret_cast<void *>(android_log_buf_write_proxy), nullptr, nullptr); if (stub_of_android_log_buf_write == nullptr) { return -1; } return 0; }
void unhook_logcat() { if (stub_of_android_log_buf_write != nullptr) { bytehook_unhook(stub_of_android_log_buf_write); stub_of_android_log_buf_write = nullptr; } }
extern "C" JNIEXPORT void JNICALL Java_com_wesley_android_c_toolkit_NativeHelper_00024Companion_unhookLogcat(JNIEnv *env, jobject thiz) { XLOGE(TAG, "unhook_logcat"); unhook_logcat(); }
extern "C" JNIEXPORT jboolean JNICALL Java_com_wesley_android_c_toolkit_NativeHelper_00024Companion_hookLogcat(JNIEnv *env, jobject thiz, jboolean console_log_open, jboolean save_to_file) { XLOGE(TAG,"hook_logcat"); g_consoleLogOpen = (console_log_open == JNI_TRUE); g_saveToFile = (save_to_file == JNI_TRUE); if (hook_logcat() == 0) { return JNI_TRUE; } return JNI_FALSE; }
|
app 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.wesley.android.c.toolkit
import android.util.Log import com.bytedance.android.bytehook.ByteHook
class NativeHelper {
companion object { init { val ret = ByteHook.init() Log.e("ByteHook", "init ret: $ret") System.loadLibrary("toolkit") } external fun hookLogcat(consoleLogOpen: Boolean = true, saveToFile: Boolean = true): Boolean external fun unhookLogcat()
} }
|
完整代码参考:wesley666/AndroidToolkit: AndroidToolkit
运行 app 后可以看到日志 tag 加上了前缀 wesley,测试环境:小米 13,安卓 15。
参考
Android logd日志简介及典型案例分析
深入理解安卓日志系统(logcat / liblog / logd
Android帝国之日志系统–logd、logcat
Android logd日志原理 - Gityuan博客
Android日志是如何输出的:Log.d经历了什么