| 基于Kali Linux
| htop
查看当前活跃进程
| jnettop
实时查看系统网络负载工具
| 源码阅读:https://cs.android.com
| 源码阅读:http://aospxref.com
| 源码阅读:http://androidxref.com
| 源码阅读:https://www.androidos.net.cn/sourcecode
1. 刷机
Android 源码编译和刷机ROOT
2. Shell 常用命令
-
cat
查看文本文件内容:cat 1.txt
-
touch
创建空文件 :touch a.txt
-
echo >
覆盖写操作:echo "afra55" > a.txt
-
echo >>
扩展写操作 -
grep
过滤符合条件的输出:cat a.txt | grep afra
-
ps -e
打印全部进程,查看手机的全部进程 -
ps
打印当前进程 -
netstat
查看对应APP进程的IP、端口、协议等网络信息:netstat -alpe grep org.myapp
-
lsof
查看对应进程打开的文件:lsof -p 21212 -l
-
top
查看当前活跃进程,查看手机当前运行的进程
3. adb 常用命令
描述 | 命令 |
---|---|
查看当前处于前台的Activity | adb shell dumpsys activity top |
打印当前焦点窗口 | adb shell dumpsys window | grep mCurrentFocus |
打印包信息,四大组件MIME权限等 | adb shell dumpsys 包名 |
查看APPP的数据库信息 | adb shell dumpsys dbinfo 包名 |
截图保存到手机 | adb shell screencap -p 路径 |
截图保存到电脑 | adb exec-out screencap -p > screen.png |
录制屏幕 | adb shell screenrecord /sdcard/filename.mp4 |
在焦点输入框内输入文字,无法输入中文 | adb shell input test 文字 |
列出所有按照的APK包名 | adb shell pm list packages |
安装手机里的APK文件,uninstall 包名 用于卸载 |
adb shell pm install 安装包路径 |
启动APP的Activity, -D 是DEBUG模式 |
adb shell am start-activity -D -N 包名/类名 |
启动主Activity | adb shell monkey -p 包名 -c android.intent.category.LAUNCHER 1 |
查看手机系统架构 | adb shell getprop ro.product.cpu.abi |
查看手机型号 | adb shell getprop ro.product.model |
查看电池信息 | adb shell dumpsys battery |
查看屏幕分辨率 | adb shell wm size |
查看屏幕密度 | adb shell wm density |
查看android_id
|
adb shell settings get secure android_id |
adb获取蓝牙MAC地址 | adb shell settings get secure bluetooth_address |
adb获取IMEI | adb shell "service call iphonesubinfo 1 | toybox cut -d \"'\" -f2 | toybox grep -Eo '[0-9]' | toybox xargs | toybox sed 's/\ //g'" |
清除应用数据&缓存 | adb shell pm clear 包名 |
获取设备IP地址 | adb shell ip route | awk '{print }' |
启用设备的网络 adb 功能 | adb tcpip 5555 |
网络连接设备 | adb connect DEVICE_IP:5555 |
截图 | adb shell screencap -p /sdcard/screenshot.png |
查看应用安装路径, 该路径下的apk可以pull下来使用 | adb shell pm path 包名 |
强制停止应用 | adb shell am force-stop 包名 |
模拟按键输入,见附表一 | adb shell input keyevent 按键代码 |
向上滑动手势解锁 | adb shell input swipe 300 1000 300 500 |
发送广播,见附表二 | adb shell am broadcast -a <action> [-d ] [-t <mime_type>] [-c <category>] |
查看实时资源占用情况,见附表四 | adb shell top |
附表一:按键代码
keycode | 含义 |
---|---|
3 | HOME 键 |
4 | 返回键 |
5 | 打开拨号应用 |
6 | 挂断电话 |
24 | 增加音量 |
25 | 降低音量 |
26 | 电源键 |
27 | 拍照(需要在相机应用里) |
64 | 打开浏览器 |
82 | 菜单键 |
85 | 播放/暂停 |
86 | 停止播放 |
87 | 播放下一首 |
88 | 播放上一首 |
122 | 移动光标到行首或列表顶部 |
123 | 移动光标到行末或列表底部 |
126 | 恢复播放 |
127 | 暂停播放 |
164 | 静音 |
176 | 打开系统设置 |
187 | 切换应用 |
207 | 打开联系人 |
208 | 打开日历 |
209 | 打开音乐 |
210 | 打开计算器 |
220 | 降低屏幕亮度 |
221 | 提高屏幕亮度 |
223 | 系统休眠 |
224 | 点亮屏幕 |
231 | 打开语音助手 |
276 | 如果没有 wakelock 则让系统休眠 |
附表二:常见广播意图
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED
action | 触发时机 |
---|---|
android.net.conn.CONNECTIVITY_CHANGE | 网络连接发生变化 |
android.intent.action.SCREEN_ON | 屏幕点亮 |
android.intent.action.SCREEN_OFF | 屏幕熄灭 |
android.intent.action.BATTERY_LOW | 电量低,会弹出电量低提示框 |
android.intent.action.BATTERY_OKAY | 电量恢复了 |
android.intent.action.BOOT_COMPLETED | 设备启动完毕 |
android.intent.action.DEVICE_STORAGE_LOW | 存储空间过低 |
android.intent.action.DEVICE_STORAGE_OK | 存储空间恢复 |
android.intent.action.PACKAGE_ADDED | 安装了新的应用 |
android.net.wifi.STATE_CHANGE | WiFi 连接状态发生变化 |
android.net.wifi.WIFI_STATE_CHANGED | WiFi 状态变为启用/关闭/正在启动/正在关闭/未知 |
android.intent.action.BATTERY_CHANGED | 电池电量发生变化 |
android.intent.action.INPUT_METHOD_CHANGED | 系统输入法发生变化 |
android.intent.action.ACTION_POWER_CONNECTED | 外部电源连接 |
android.intent.action.ACTION_POWER_DISCONNECTED | 外部电源断开连接 |
android.intent.action.DREAMING_STARTED | 系统开始休眠 |
android.intent.action.DREAMING_STOPPED | 系统停止休眠 |
android.intent.action.WALLPAPER_CHANGED | 壁纸发生变化 |
android.intent.action.HEADSET_PLUG | 插入耳机 |
android.intent.action.MEDIA_UNMOUNTED | 卸载外部介质 |
android.intent.action.MEDIA_MOUNTED | 挂载外部介质 |
android.os.action.POWER_SAVE_MODE_CHANGED | 省电模式开启 |
附表三:系统属性
属性名 | 含义 |
---|---|
ro.build.version.sdk | SDK 版本 |
ro.build.version.release | Android 系统版本 |
ro.build.version.security_patch | Android 安全补丁程序级别 |
ro.product.model | 型号 |
ro.product.brand | 品牌 |
ro.product.name | 设备名 |
ro.product.board | 处理器型号 |
ro.product.cpu.abilist | CPU 支持的 abi 列表 |
persist.sys.isUsbOtgEnabled | 是否支持 OTG |
dalvik.vm.heapsize | 每个应用程序的内存上限 |
ro.sf.lcd_density | 屏幕密度 |
附表四:实时资源占用情况表描述
列名 | 含义 |
---|---|
PID | 进程 ID |
PR | 优先级 |
CPU% | 当前瞬间占用 CPU 百分比 |
S | 进程状态(R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程) |
#THR | 线程数 |
VSS | Virtual Set Size 虚拟耗用内存(包含共享库占用的内存) |
RSS | Resident Set Size 实际使用物理内存(包含共享库占用的内存) |
PCY | 调度策略优先级,SP_BACKGROUND/SPFOREGROUND |
UID | 进程所有者的用户 ID |
NAME | 进程名 |
3. Frida 环境
3.1 安装 Frida
https://github.com/frida/frida
# frida --version
15.2.2
3.2 查看手机系统架构
# adb shell getprop ro.product.cpu.abi
arm64-v8a
3.3 下载与安装的Frida版本对应 frida-server
下载测试机系统架构对应的 frida-server
https://github.com/frida/frida/releases
frida-server-15.2.2-android-arm64.xz
3.4 讲 frida-server 推送到手机
# 7z x frida-server-15.2.2-android-arm64.tar.xz
# adb push frida-server-15.2.2-android-arm64 /data/local/tmp/
# adb shell
# su
# cd /data/local/tmp
# chmod 777 frida-server-15.2.2-android-arm64
3.5 启动 frida-server
# adb shell
# su
# cd /data/local/tmp
# ./frida-server-15.2.2-android-arm64
4 Frida 脚本智能提示
基于Node和NPM环境。
安装 nodejs: https://nodejs.org/en/download/package-manager
推荐使用三方包管理器安装:https://github.com/nodesource/distributions#installation-instructions
4.1 使用Frida示例项目
# git clone https://github.com/oleavr/frida-agent-example
# cd frida-agent-example
# npm install
使用vscode等IDE打开此工程,在agent
文件夹下编写JavaScript,会有智能提示。
4.2 使用 npm 包 frida-gum
在系统里装上这个这个npm包(https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/frida-gum),可以在任意工程获得frida的代码提示、补全和API查看。
npm i -g @types/frida-gum
5 Frida 注入命令
5.1 USB 注入
查看进程
# frida-ps -U
注入脚本:
# frida -U 进程名 -l 脚本路径.js
或直接对当前顶层应用注入脚本:
# frida -UF -l 脚本路径.js
加上 -f
参数则将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App.
# frida -U -f 进程名 -l hook.js
--no-pause
是 Frida 的一个参数,用于在启动时禁止自动暂停目标进程。默认情况下,Frida 会在注入到目标进程后自动暂停该进程,以便用户可以执行进一步的操作,比如在运行时修改内存或进行调试。使用 --no-pause
参数可以禁止这种自动暂停行为,使目标进程继续正常运行。这在某些场景下可能很有用,例如当你只需要监视应用程序的行为而无需进行交互式的调试时。
# frida -U -f 进程名 -l hook.js --no-pause
5.2 网络模式注入
5.2.1 安装 Termux
可以在手机上执行linux命令
https://github.com/termux/termux-app#installation
5.2.2 frida-server 使用网络模式进行监听
在手机上执行命令,打开 Termux:
# su
# cd /data/local/tmp/
# ./frida-server-15.2.2-android-arm64 -l 0.0.0.0:8888
或者直接在电脑上执行也行,网络模式一般用来分析那些禁止USB链接的应用。
查看 frida-service 监听的端口:
# netstat -tulp | grep frida
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 14541/frida-server-15.2.2-android-arm64
5.2.3 Frida网络注入
# frida -H 192.168.0.107:8888 -l test.js FridaTest
使用参数 -H
传入测试机的IP和端口,即可进行网络设备注入。
6 Frida脚本基础
test.js
function main(){
Java.perform(function(){
console.log("任何对APP的JAVA层的操作,都要写在这个匿名函数里面")
// 获取对应类的JavaScript对象
var Example = Java.use('com.example.Example')
// new 了一个 Java Stirng 字符串
var JavaString = Java.use('java.lang.String').$new('122')
// Hook 重载 类Example 的方法,在该方法调用的时候,会触发匿名函数,可不加overload,fraida报错信息会提示是否应该加上参数信息
Example.方法名.overload('java.lang.String', 'java.lang.String').implementation = function(str, str2){
// 传入参数,获取结果,可以自行修改传入参数
var result = this.方法名(JavaString, JavaString.$new('123'))
console.log('str, str2, result: ', str, str2, result)
// 打印调用堆栈信息
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
return result // 返回结果,可以自行修改返回结果
}
// 主动调用类 Example 的静态方法
var subResult = Example.静态方法名(JavaString.$new("6"), JavaString.$new("7"))
// 主动调用 Example 的内部方法和变量
Java.choose('com.example.Example', {
onMatch:function(instance){
console.log("变量值", instance.变量名.value)
// 调用内部方法
instance.方法名()
},onComplete:function(){
console.log("动态调用成功")
}
})
console.log(subResult)
})
}
setImmediate(main) // 注入后立即执行 main函数
// setTimeout(main, 100) // 注入后,100ms后再执行 main函数
7 RPC 及Python自动化
https://github.com/frida/frida-python
在 js 脚本末尾添加 rpc.exports
用于导出函数,注意导出函数名必须小写:
rpc.exports = {
callmain:main,
getfun:getNumber
}
上面导出了2个函数,使得外部可以调用。
loader.py
import frida, sys, time
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
# 获得USB句柄,
devices = frida.get_usb_device()
# 可以添加多个网络设备
# devices = frida.get_usb_device().add_remote_device('192.168.0.107:8888')
# spawn 方式注入
# pid = devices.spawn(["进程名"])
# devices.resume(pid) # 唤起进程
# time.sleep(1)
# process = devices.attach(pid)
# attach 方式进程注入
process = devices.attach('进程名')
# 加载脚本代码
with open('test.js') as f:
jscode = f.read()
script = process.create_script(jscode)
# 注册信息打印函数
script.on('message', on_message)
script.load()
command = ""
while 1==1:
command = input("\nEnter:\n1:exit\n2:call\n3:get\nchoice:")
if command == "1":
break
elif command == "2":
script.exports.callmain() # 调用脚本方法
elif command == "3":
script.exports.getfun()
执行该Python即可。
8 Objection 安装
Objection 可以快速搜索内存,搜索类,Hook方法,打印参数返回值调用栈,不写一行脚本就能进行APP分析。
https://github.com/sensepost/objection
9 Objection 常用命令
注入后,把要HOOK的功能都触发一次。
9.1 注入
# objection -g 包名 explore
9.2 列出内存所有的类
# android hooking list classes
9.3 搜索包含关键词的类
# android hooking search classes 关键词
9.4 搜索包含关键词的方法
# android hooking search methods 关键词
9.5 查看指定类的所有方法
# android hooking list class_methods 完整类名
9.6 列出注入进程的所有 Activity
# android hooking list activities
9.7 列出注入进程的所有 services
# android hooking list services
9.8 HOOK 指定方法
# android hooking watch class_method 方法名 --dump-args --dump-backtrace --dump-return
9.9 查看当前正在HOOK的作业信息
# jobs list
9.10 移除HOOK
# jobs kill 作业ID
9.11 启动Activity
# android intent launch_activity Activity名
9.12 搜索实例类似 Java.use
# android heap search instance 类名
9.13 执行无参实例方法
# android heap execute 实例Handle 方法名
9.14 执行有参实例方法
# android heap evaluate 实例Handle
clazz.方法名(参数...)
9.15 执行静态方法
TODO
9.16 使用插件Wallbreaker
打印相应类的具体内容
需要使用Objection的插件 https://github.com/hluwa/wallbreaker,这个插件可以打印类的具体内容,比如静态成员实例成员的值,及所有函数。>
下载&安装:
# git clone https://github.com/hluwa/wallbreaker ~/.objection/plugins/Wallbreaker
注入时使用-P加载插件即可:
# objection -g 包名 explore -P ~/.objection/plugins/
或者在注入后,再进行加载使用 :
# plugin load /root/.objection/plugins/Wallbreaker/
# plugin wallbreaker objectsearch java.util.HashMap
# plugin wallbreaker objectdump <object-handle>
示例,打印系统主要API接口:
# objection -g com.android.settings explore
# plugin load /root/.objection/plugins/Wallbreaker/
# plugin wallbreaker classdump android.os.Build
9.17 ZenTracer
有界面的HOOK工具,更方便便捷。
https://github.com/hluwa/ZenTracer
10 Objection 网络模式
开启测试机的 frida-server 网络模式监听,详见5.2。
使用 -N 参数进行网络模式连接:
# objection -N -h 192.168.0.107 -p 8888 -g 包名 explore
11 脱壳
11.1 FRIDA-DEXDump
这个工具可以从内存中搜索和dump符合条件的dex文件,当然VMP壳时脱不了的,只能脱1代壳。
11.1.1 安装插件
https://github.com/hluwa/frida-dexdump#frida-dexdump
这里作为Objection插件来使用:
# pip3 install frida-dexdump
11.1.2 脱壳&重打包
推荐使用 -d
模式,开启深度搜索模式。
# frida-dexdump -U -f 包名 -d
或
# frida-dexdump -FU -d
等待一段时间,完成脱壳,记得脱壳前先玩玩APP。
classes.dex
classes02.dex
classes03.dex
classes04.dex
classes05.dex
可以使用gerp
定位关键类所在的dex:
grep -ril "关键词" ./*.dex
11.1.3 使用 apktool 重打包
https://github.com/iBotPeaches/Apktool/tree/docs
还是
MT管理器
方便,推荐使用
反编译APK但不反编译 dex, 加-s
命令即可:
# apktool d 包名 -s
将反编译后的dex删除。
把脱壳后的class重命名并放入反编译后的文件里:
classes.dex
classes2.dex
classes3.dex
classes4.dex
classes5.dex
重要的一点时,脱壳后的Applicaiton入口变了,需要找到脱壳后的Application,
# grep -ril "Application" ./*.dex
# grep -ril "android.app.Application" ./*.dex
# grep -ril "onCreate()" ./*.dex
找到关键 dex,反编译后查找 extends Application
即可找到真正的Applicaiton。
在清单文件中替换 application 节点的name即可。
重新编译:
# apktool b 反编译的文件夹
重新编译的APK在反编译目录下的dist文件夹。
创建签名文件:
# keytool -genkey -alias 别名 -keyalg RSA -validity 2000 -keystore test.jks
进行签名:
jarsigner -verbose -keystore 签名文件 -signedjar 签名后的.apk 等待签名的.apk 别名
11.2 FRIDA-FART
https://github.com/hanbinglengyue/FART
这个工具用于脱二代抽取型加固壳。
12 Charles 抓包 (ROOT环境)
12.1 下载并安装
https://www.charlesproxy.com/download/
12.2 手机安装证书
手机配置代理后打开网页chls.pro/ssl
,下载并安装用户证书。
12.3 将用户证书变成系统证书
# adb shell
# su
# cd /data/misc/user/0/cacerts-added/
# mount -o remount,rw /system
# cp * /etc/security/cacerts
# chmod 777 /etc/security/cacerts/*
# mount -o remount,ro /system
# reboot
12.4 配置SOCKS5
移除手动配置的代理,安装应用Postern并配置 SOCKS5协议,打开Charles的 SOCKS 协议,端口号要和 HTTP协议不一样。
13 HOOK关键词OkHttp
抓包
13.1 OkHttp未被混淆
清空 Objection 日志:
# rm ~/.objection/objection.log
注入APP:
# objection -g 包名 explore
获取APP已经加载的所有类:
# android hooking list classes
退出确保日志已经存储:
# exit
复制日志文件到其他目录:
# cp ~/.objection/objection.log ./
过滤网络相关的类, 并保存到文件中, 可以先扫描再使用符号重定向:
# cat objection.log | grep -i HttpURLConnection > HttpURLConnection.txt
# cat objection.log | grep -i okhttp > okhttp.txt
# cat objection.log | grep -i okhttp3 > okhttp3.txt
使用VIM再每行行首添加Hook命令:
# :%s/^/android hooking watch class /
保存并退出:
# :wq
注入并批量执行HOOK命令:
# objection -g 包名 explore -c "./okhttp.txt"
查看相关函数, 退出 Objection,然后再对该函数进行HOOK:
# android hooking watch class_method 函数名 --dump-args --dump-backtrace --dump-return
就能找到调用的具体位置, 再对调用函数进行HOOK。
13.2 OkHttp 被混淆了
https://github.com/siyujie/okhttpLogger-Frida
该项目使用了OkHttp3框架的特征去验证OkHtt3是否被使用了,而不是直接搜所关键词。
下载项目。
将 okhttpfind.dex
拷贝到 /data/local/tmp/
目录下,并提升权限。
# adb push okhttpfind.dex /data/local/tmp/
# su
# chmod 777 /data/local/tmp/okhttpfind.dex
执行命令启动frida -U -l okhttp_poker.js -f com.example.demo --no-pause
可追加 -o [output filepath]
保存到文件。
或者打开应用到前台执行注入命令:frida -U -l okhttp_poker.js -F --no-pause
。
执行find()
方法。
如果提示方法未找到,则修改脚本直接执行 find()
。
将 Find Complete
里的内容覆盖到脚本里的对应参数。
再次注入执行hold()
方法,就会输出所有的请求信息。
13.3 Frida 动态注入 Dex
通过如下API即可动态加载DEX到内存中,使用我们自己的类和方法:
Java.openClassFile("/data/local/tmp/HttpLoggingInter").load()
示例:OkHttp3 提供了日志打印库:
implementation("com.squareup.okhttp3:logging-interceptor")
但分析的APP使用了OkHttp3,而且没有加日志打印库的时候,可以进行dex动态注入,自己写一个dex,来加载APP里没有的类库。
一般OkHttp3是这样加载日志打印拦截器的:
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.build();
写一个空Android项目,添加该类库,并运行起来。
在 app/build/intermediates/apk/debug
找到生成的APK,并解压缩。
通过下面的命令找到类库所在的dex文件。
# grep -ril "HttpLoggingInterceptor" ./*.dex
将其推送至/data/local/tmp
目录下:
adb push HttpLoggingInterceptor.dex /data/local/tmp/
编写Frida脚本:
function main(){
Java.perform(function(){
// 加载我们的dex文件
Java.openClassFile("/data/local/tmp/HttpLoggingInterceptor.dex").load();
var HttpLoggingInterceptor = Java.use("okhttp3.logging.HttpLoggingInterceptor");
var HttpLoggingInterceptorObj = HttpLoggingInterceptor.$new();
var Level = Java.use("okhttp3.logging.HttpLoggingInterceptor$Level");
// 设置枚举值BODY,另OkHttp3打印所有日志
HttpLoggingInterceptorObj.setLevel(Level.BODY.value);
var Builder = Java.use("okhttp3.OkHttpClient$Builder");
Builder.build.implementation = function(){
this.networkInterceptors().add(HttpLoggingInterceptorObj);
console.log("add network interceptor");
return this.build();
};
console.log("hook okhttp3 log");
})
}
setImmediate(main)
执行命令使用Spawned模式, 从APP启动的时候,就开始HOOK注入:
# frida -U -f 进程名 -l 脚本.js --no-pause
这时APP进行网络请求时,即可看到所有信息。
14. HOOK 应用证书文件
保存证书到 sdcar里:
function hook_KeyStore_load() {
Java.perform(function () {
var myArray=new Array(1024);
var i = 0
for (i = 0; i < myArray.length; i++) {
myArray[i]= 0x0;
}
var buffer = Java.array('byte',myArray);
var StringClass = Java.use("java.lang.String");
var KeyStore = Java.use("java.security.KeyStore");
KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load1:", arg0);
this.load(arg0);
};
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load2: filename = ", arg0,',password = ', arg1 StringClass.$new(arg1) : null);
if (arg0){
var filename = "/sdcard/Download/"+ String(arg0)
var file = Java.use("java.io.File").$new(filename);
var out = Java.use("java.io.FileOutputStream").$new(file);
var r;
while( (r = arg0.read(buffer)) > 0){
out.write(buffer,0,r)
}
console.log('save_path = ',filename,", cert save success!")
out.close()
}
this.load(arg0, arg1);
};
console.log("hook_KeyStore_load...");
});
}
function main(){
hook_KeyStore_load()
}
setImmediate(main);
导出证书后,可使用 KeyStore Explorer 工具转换证书格式,以便导入到 Charles中。
如果API使用了非标准HTTPS端口,还要手动配置该端口为HTTP数据包。
15 安卓应用层明文协议抓包通杀脚本 r0capture
https://github.com/r0ysue/r0capture
16. Java类追踪脚本 r0tracer
- 根据黑白名单批量追踪类的所有方法
- 在命中方法后打印出该类或对象的所有域值、参数、调用栈和返回值
- 极简的文本保存日志机制、易于搜索关键参数
- 针对加壳应用找不到类时可以切换Classloader