LogCat 及 Log 的一些思考

0x00 TOC

  • 原理
  • smali注入
  • 手机查看log并导出
  • so打log
  • 发布(release)版屏蔽Log输出
  • 查看内核日志
  • 参考链接

#####0x01 原理

常见的一些打Log的语句

1
2
3
4
5
6
Log.i(TAG, "");
Log.d(TAG, "");
Log.e(TAG, "");
Log.v(TAG, "");
Log.w(TAG, "");
Log.wtf(TAG, "");

Log给开发者开放了6种级别(分别对应info,debug,error,verbose,warning,assert),隐藏了两种级别:

1
2
F — Fatal
S — Silent (highest priority, on which nothing is ever printed)

通过阅读Log的代码,里面说明了通过println_native(LOG_ID_MAIN, Priority, tag, msg);代码进行输出了日志。并声明了LOG_ID_MAIN、LOG_ID_RADIO、LOG_ID_EVENTS、LOG_ID_SYSTEM、LOG_ID_CRASH五个缓冲区。

println_native()的代码在frameworks/base/core/jni/android_util_log.cpp,其中判断了msg是否为空,如果为空,抛出空指针异常。

其中levels_t是一个结构体,其中包括了那六种级别,其中assert,查阅代码可得知,如果利用Log.wtf()方法,就会打印一个标志成ASSERT的错误。

并通过以下函数进行了打印操作。即__android_log_buf_write。在__android_log_buf_write函数里通过调用write_to_log函数进行打印。

1
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);

然后write_to_log是怎样的情况呢,在文件45行有以下信息。

1
2
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);

然后追溯到__write_to_log_init这个函数里,这个函数其中有一些打开文件等的操作,然后__write_to_log_kernel这个就写入到文件里。

具体底层Log设备Logger机制,就不再阐述,如果细研下去,就是另一篇文章了,并且嵌入式的同学还学到了ioctl函数,这需要一些Linux驱动方面的知识。

LogCat是如何获取Log的?通过logcat.cpp,可以知道,定义了一个Log文件目录,即#define LOG_FILE_DIR "/dev/log/"。具体读取log的过程,可以参考田海立的文章-解读Android LOG机制的实现:(5)获取LOG的应用程序LogCat

如何去修改logcat的显示颜色呢,可以通过android studio的设置,也可以安装一个logcat-color直接改变颜色,通过以下命令。

1
$ logcat-color -e | egrep '(Tag1|Tag2)'

0x02 smali注入

通过以下命令,进行apktool反编译

1
apktool d xxx.apk

在合适位置插入以下语句,其中v0为寄存器,尽量不要随意添加寄存器。

1
invoke-static {v0, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

然后进行打包

1
apktool b xxx -o xxx.apk

打包完安装需要签名,由于已经有了android.keystore ,这里使用jarsigner进行签名。

1
jarsigner -verbose -keystore android.keystore -signedjar android_signed.apk app.apk android.keystore

运行即可看到Log信息。

0x03 手机查看log并导出

如何在手机上读取其他应用的log并可以导出呢,谷歌在4.1以后禁止了相关权限,改为了signature|system|development权限。就算有android.PREMISSION.READ_LOGS,也读取不到其他应用的log了。只能root以后查看。

在谷歌Android Developer论坛里也有相关讨论。在4.1之后,禁止了去阅读其他应用log的权限。

并且在google play商店上也有几款手机上查看log的软件,需要root权限。比如CatLog - Logcat Reader!aLogcat (free) - logcat 等。aLogCat也开源了,地址在GitHub上

0x04 so打log

如何在so文件,即jni开发中里打log呢

这里如何配置NDK就不再叙述。详细可参考ndk官网

需要在cpp文件中添加以下语句:

1
2
3
#include <android/log.h>
//打印相关信息
__android_log_write(ANDROID_LOG_DEBUG,"Tag","Java_com_sxau_ndkdemo_MainActivity_getStringFromC");

然后在build.gradle文件里,修改成以下片段:

1
2
3
4
ndk {
moduleName "JniDemo"
ldLibs("log", "z", "m")
}

引入liblog.h,或者通过自定义Android.mk进行导入。

可以通过一些宏定义定义:

1
2
#define LOG_TAG "ANDROID_LAB"  
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

0x05 发布(release)版屏蔽Log输出

一种方法是通过添加一个Log辅助类,配置级别,或者通过变量控制显示。

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
public class Log {
public static int logLevel = Log.VERBOSE;

public static void i(String tag, String msg) {
if (logLevel <= Log.INFO)
android.util.Log.i(tag, msg);
}

public static void e(String tag, String msg) {
if (logLevel <= Log.ERROR)
android.util.Log.e(tag, msg);
}

public static void d(String tag, String msg) {
if (logLevel <= Log.DEBUG)
android.util.Log.d(tag, msg);
}

public static void v(String tag, String msg) {
if (logLevel <= Log.VERBOSE)
android.util.Log.v(tag, msg);
}

public static void w(String tag, String msg) {
if (logLevel <= Log.WARN)
android.util.Log.w(tag, msg);
}
}

使用时直接使用这个Log类打印方法。

release版屏蔽log输出,另外一种方法是,可以通过ProGuard的方式,将log语句删除。

ProGuard是Android SDK的一部分。只需要开启即可。

在android studio中,编辑build.gradle文件,配置如下代码:

1
2
3
4
5
6
7
8
9
android {
...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

后来gradle的runProguard更名为minifyEnabled,所以直接改为true即可。

1
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

注意这段位置,本来按默认的配置一直没有消除成功,直到看了一篇文章以后,改为了proguard-android-optimize.txt,才屏蔽输出成功。即:

1
2
3
4
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

修改Proguard的配置文件proguard-rules.pro,添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}

-assumenosideeffects class java.io.PrintStream{
public void println(%);
public void println(**);
}

即结果。然后打包签名输出就不会有log日志了。

0x06 查看内核日志

1
2
3
4
./adb shell
su
dmesg
cat /proc/kmsg

dmesg是内核中的一个命令,可以查看内核日志,当然,也可以用cat/proc/kmsg。两者不同的是,dmesg只读取缓冲区中的内核日志,而cat /proc/kmsg则可以原始的、完整的日志文件。

0x07 参考链接

Google API

Android LogCat Security

Stackoverflow

android系统源代码情景分析

如何在JNI编程中使用logCat

Author

admin

Posted on

2016-06-21

Updated on

2016-06-23

Licensed under

Comments