Small Trace 工具发布 - 一个基于 QDBI 与 Frida 的简易追踪工具
随着软件逆向与安全研究的发展,越来越多的开发者和研究人员开始关注代码追踪。尽管市面上已有不少工具,但大多数都未开源,或者无法提供定制化的追踪功能。在此背景下,我决定利用几天时间开发一款简单的代码追踪工具。借助前辈们的工作,结合 QDBI 与 Frida 的 Gum 模块,我构建了 Small Trace。这款工具虽然简单,但能有效地帮助用户观察内存读写、函数调用等关键操作。
看着周围越来越多的人在使用代码追踪工具,我也开始使用前辈们开发的许多追踪工具,然而这些工具都没有开源,且许多定制打印功能无法满足我的需求。因此,我决定自行动手,花了几天时间写了一个简单的追踪工具。这个工具的主要目的是进行内存读写、函数调用等的简单打印,可以帮助开发者在调试过程中快速定位问题。
Small Trace目前使用了QDBI与Frida的Gum模块实现,未来计划加入基于ptrace的追踪功能,进一步提升工具的灵活性与功能。
Small Trace可以打印内存读取、函数调用等信息,帮助开发者更好地理解程序的运行流程。例如,在追踪一个内存读取操作时,可能会得到如下的输出:
如上所示,工具能够打印内存读取时的数据值、数据地址,以及调用的具体函数信息,包括函数调用的内容。这对于分析内存相关问题、定位加密解密逻辑尤为有用。
获取文件
你将得到两个文件:libqdbi.so 和 CalvinTrace.js。
加载动态库
你需要让目标程序加载我们的动态库 libqdbi.so。你可以使用任何方法加载此库,但我推荐使用如下方法:
这样,你就可以开始进行内存跟踪了,轻松查看加密解密操作的位置,方便定位问题。
这里一定要调用一次 ,或者 让程序自己跑,跑到 你 hook 的地方。
如何获取 保存的日志呢?
日志当然会自动的保存在 /data/data/包名/qdbi_log.txt 咯
现在 实战我写的小demo
目标代码
一个简单的 base64 解码
编译后 打开 apk 发现so的名称为 libcheckqdbi.so
我要trace的方法 为 Java_io_calvin_checkqdbi_MainActivity_checkQDBI
现在我要 先将 trace so push 到 手机 data/local/tmp 目录下 赋予权限
然后填写数据到frida 脚本
这次使用 符号 进行 hook
填写好后 测试
你可以用adb 看到日志
表示程序 正常 hook
这时候只需要主动调用 这个方法Java_io_calvin_checkqdbi_MainActivity_checkQDBI 就行了
马上在日志里就会显示
那么在apk的私有目录里就会有我记录下的日志
可以辅助我在ida 分析时 观测到内存的数据 至少 可以推断大概的内容 这里base64解出来的内容 就是 hello this is test !!!!
Small Trace 工具虽简单,却能帮助开发者在调试过程中获取关于内存读写、函数调用的关键数据。通过结合 QDBI 和 Frida,我们可以快速实现对内存操作的追踪。未来,我还计划加入基于 ptrace 的跟踪功能,使得这个工具更加灵活与强大。希望这个工具能为从事逆向分析与调试的朋友们提供一些帮助。
项目的地址 : 20cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6z5K9g2c8A6j5h3&6q4M7W2S2A6L8X3M7$3y4U0k6Q4x3V1k6e0L8h3q4D9L8q4)9J5k6q4c8J5j5h3y4W2
如果你对 签名校验 环境检测 以及移动安全 有兴趣 可以加我一起交流学习 我打算建一个学习交流群

memory read at 0xb4000070d9505f40, instruction address = 0x70d68f75d8, data size = 8, data value = 8b858ed670000000
00000070d9505f00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
*00000070d9505f40 8B 85 8E D6 70 00 00 00 F0 E1 CD AC 71 00 00 B4 | p q |
00000070d9505f50 70 5F 50 D9 70 00 00 B4 B8 7B 8F D6 70 00 00 00 |p_P p { p |
00000070d9505f60 38 C0 ED DD 70 00 00 00 F0 E1 CD AC 71 00 00 B4 |8 p q |
00000070d9505f70 00 60 50 D9 70 00 00 B4 2A 00 00 00 00 00 00 00 | `P p * |
0x00000070d68f75dc 0x255dc blr x8 ;X30=0x70d68f7bb8 -> 0x70d68f75e0
CALL -> [/apex/com.android.art/lib64/libart.so] art::(anonymous namespace)::CheckJNI::NewStringUTF(_JNIEnv*, char const*) (.llvm.17249515741404199542) + 0x435ed4 (size=1808) (target=0x70eba35ed4)
0x00000070eba35ed4 0x435ed4 sub sp, sp, #224
0x00000070eba35ed8 0x435ed8 stp x29, x30, [sp, #128]
memory read at 0xb4000070d9505f40, instruction address = 0x70d68f75d8, data size = 8, data value = 8b858ed670000000
00000070d9505f00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
00000070d9505f30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
*00000070d9505f40 8B 85 8E D6 70 00 00 00 F0 E1 CD AC 71 00 00 B4 | p q |
00000070d9505f50 70 5F 50 D9 70 00 00 B4 B8 7B 8F D6 70 00 00 00 |p_P p { p |
00000070d9505f60 38 C0 ED DD 70 00 00 00 F0 E1 CD AC 71 00 00 B4 |8 p q |
00000070d9505f70 00 60 50 D9 70 00 00 B4 2A 00 00 00 00 00 00 00 | `P p * |
0x00000070d68f75dc 0x255dc blr x8 ;X30=0x70d68f7bb8 -> 0x70d68f75e0
CALL -> [/apex/com.android.art/lib64/libart.so] art::(anonymous namespace)::CheckJNI::NewStringUTF(_JNIEnv*, char const*) (.llvm.17249515741404199542) + 0x435ed4 (size=1808) (target=0x70eba35ed4)
0x00000070eba35ed4 0x435ed4 sub sp, sp, #224
0x00000070eba35ed8 0x435ed8 stp x29, x30, [sp, #128]
adb shell
su
setenforce 0
var TraceSoPath = "/data/local/tmp/libqdbi.so";
var SO_name = "libcheckqdbi.so";
var Symbol = "Java_io_calvin_checkqdbi_MainActivity_checkQDBI";
var so_offset = 0;
var Trace_Mode = 0;
var args = 2;
var TraceSoPath = "/data/local/tmp/libqdbi.so";
var SO_name = "libcheckqdbi.so";
var Symbol = "Java_io_calvin_checkqdbi_MainActivity_checkQDBI";
var so_offset = 0;
var Trace_Mode = 0;
var args = 2;
frida -U -f XXX.XXX.XXX -l CalvinTrace.js
frida -U -f XXX.XXX.XXX -l CalvinTrace.js
std::string base64_decode(const std::string &encoded) {
const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int in_len = encoded.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded[in_] != '=') && is_base64(encoded[in_])) {
char_array_4[i++] = encoded[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++) {
char_array_4[i] = base64_chars.find(char_array_4[i]);
}
char_array_3[0] = (char_array_4[0] << 2) | (char_array_4[1] >> 4);
char_array_3[1] = (char_array_4[1] << 4) | (char_array_4[2] >> 2);
char_array_3[2] = (char_array_4[2] << 6) | char_array_4[3];
for (i = 0; (i < 3); i++) {
ret += char_array_3[i];
}
i = 0;
}
}
if (i) {
for (int j = i; j < 4; j++) {
char_array_4[j] = 0;
}
for (int j = 0; j < 4; j++) {
char_array_4[j] = base64_chars.find(char_array_4[j]);
}
char_array_3[0] = (char_array_4[0] << 2) | (char_array_4[1] >> 4);
char_array_3[1] = (char_array_4[1] << 4) | (char_array_4[2] >> 2);
char_array_3[2] = (char_array_4[2] << 6) | char_array_4[3];
for (int j = 0; (j < i - 1); j++) {
ret += char_array_3[j];
}
}
return ret;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_io_calvin_checkqdbi_MainActivity_checkQDBI(JNIEnv *env, jobject thiz) {
std::string encoded_string = "aGVsbG8gdGhpcyBpcyB0ZXN0ICEhISE=";
std::string decoded_string = base64_decode(encoded_string);
return env->NewStringUTF(decoded_string.c_str());
}
std::string base64_decode(const std::string &encoded) {
const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int in_len = encoded.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded[in_] != '=') && is_base64(encoded[in_])) {
char_array_4[i++] = encoded[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++) {
char_array_4[i] = base64_chars.find(char_array_4[i]);
}
char_array_3[0] = (char_array_4[0] << 2) | (char_array_4[1] >> 4);
char_array_3[1] = (char_array_4[1] << 4) | (char_array_4[2] >> 2);
char_array_3[2] = (char_array_4[2] << 6) | char_array_4[3];
for (i = 0; (i < 3); i++) {
ret += char_array_3[i];
}
i = 0;
}
}
if (i) {
for (int j = i; j < 4; j++) {
char_array_4[j] = 0;
}
for (int j = 0; j < 4; j++) {
char_array_4[j] = base64_chars.find(char_array_4[j]);
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-8-30 20:27
被逆天而行编辑
,原因: