因为现有的作品均有大大小小的问题,想自己开发一个自己用着比较舒服的框架,提供分析效率。
因为想收集大家出现的反馈,继续发展壮大,我会持续维护一阵子,来提升自己的开发实力。
答案是不开源,会给出so文件,以及详细的使用方法,以及入门的例子,但是不开源不代表不维护
文章结尾会放上微信技术交流群,来讨论trace相关问题,以及逆向相关问题
这一篇算是对我git上的注入模块的部分的一个填坑,也是某搜题软件的第二篇的填坑,我最终目的是开发出无痕注入 、hook 、trace 、debug。
欢迎大家贴数据对比速度,当然后期有更多的优化空间,目前先不考虑改善(够用了)
已经手动trace过某团、某音、某Q,均无崩溃完成trace完成
但是在我测试中也有无法trace无法使用的情况,比如某宝,但是根据trace
进行了针对性patch后也可以继续运行完成
所以我希望大家发掘更多样本进行开发修复
文本例子(可以结尾下载trace文件,自己搜索查看)
并且支持通过得到的Jmethod id 反查是哪个类型
*为正在读取的内存内容
以及一些方便算法还原的打印
这个目前不全,后面会加上所有调用参数等的打印
有人会问有啥用
append一些函数就能逮到了
后面会和上面的功能一起维护好,打印所有入参,相当于给所有函数都inlinehook了一遍
此次的案例以我上一篇trace的案例作为教学,所有相关文件都会放在git仓库里
运行后得到0x9deeed68
继续运行
ptr(0x9deeed68).add(16).readPointer();
得到
由于读写内存的权限问题,需要刷入指定面具模块
可以在github仓库中找到
推动git上的so到/data/local/tmp/test.so
关闭setlinux
刷入面具模块(在仓库里)
在过掉msao后(见上一篇帖子开头)
attach输入
之后主动调用拿到结果:
去手机里拉到/data/user/0/com.fenbi.android.leo/log.txt
trace文件
基于qbdi框架的魔改和拓展,so没有任何加固混淆
欢迎反编译交流学习,会逐渐补充原理部分
防止读取到无效内存的地址,缓存maps相关的内存区段,防止进行非法读写
在跳转指令执行的时候(拦截B,BL的指令)
首先先dladdr找到调用的模块的落地文件,使用elf解析工具,把所有符号+偏移地址都缓存到自己的内存里,
如果匹配就可以替换
JNI打印就是在此基础上进行优化的打印
方案1 速度很慢
方案2 自己实现了一些搜索的策略 贪婪搜索策略 不再是窗口策略
方案3 管道
方案4 错误捕获
还有4种方案
其实就是遇到SVC指令的时候 读取他的X8拿到系统调用号,拿到调用名称并且打印
后面如果想打印更多返回值 就可以像// 对 openat 系统调用进行专门处理 这里一样增加更多case
但是目前只有openat被处理了,后面更新也会更新这里
仓库地址:
499K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6B7K9i4q4A6N6e0t1H3x3U0u0Q4x3V1k6$3L8g2)9J5k6s2c8J5j5h3y4W2i4K6u0V1M7X3g2D9k6h3q4K6k6b7`.`.
欢迎使用,求个star~ 有问题可以提issue,有问题我会补充到文档里
交流群:(纯技术交流,无广告,欢迎大佬们来交朋友)

预告:
接下来闲下来会完善hook相关的事情,以及做一些Linux内核模块开发的分享(隐藏调试信息,改机等)
Call addr: 0x7f3d15b9a0 [libc.so!memmove]
[libc.so!memmove](0xb400007d5ace2da0, 0xb400007db18f7c14, 32)
Call addr: 0x7f3d1582c0 [libc.so!memcmp]
memcmp(0xb400007d5a9dc760, 0xb400007d5ace2da0, 32)
Call addr: 0x7f3d15b9a0 [libc.so!memmove]
[libc.so!memmove](0xb400007d5ace2da0, 0xb400007db18f7c14, 32)
Call addr: 0x7f3d1582c0 [libc.so!memcmp]
memcmp(0xb400007d5a9dc760, 0xb400007d5ace2da0, 32)
memory read at 0x7d1e843ba8, instruction address = 0x7df45647cc, data size = 8, data value = 2a00000000000000
memory read at 0x7d1e843ba8, instruction address = 0x7df45647cc, data size = 8, data value = 2a00000000000000
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function () {
let ReadableNativeMap = Java.use("com.fenbi.android.leo.utils.e");
console.log(getHandle(ReadableNativeMap["zcvsd1wr2t"]))
});
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function () {
let ReadableNativeMap = Java.use("com.fenbi.android.leo.utils.e");
console.log(getHandle(ReadableNativeMap["zcvsd1wr2t"]))
});
DebugSymbol.fromAddress(ptr(0x7de5c78bf4))
{
"address": "0x7de5c78bf4",
"column": 0,
"fileName": "",
"lineNumber": 0,
"moduleName": "libRequestEncoder.so",
"name": "0x61bf4"
}
DebugSymbol.fromAddress(ptr(0x7de5c78bf4))
{
"address": "0x7de5c78bf4",
"column": 0,
"fileName": "",
"lineNumber": 0,
"moduleName": "libRequestEncoder.so",
"name": "0x61bf4"
}
adb shell
su
setenforce 0
function prepareArgs(args) {
if (args === undefined || !Array.isArray(args)) {
args = [];
}
var argNum = args.length;
var argSize = Process.pointerSize * argNum;
var argsPtr = Memory.alloc(argSize);
for (var i = 0; i < argNum; i++) {
var arg = args[i];
var argPtr;
if (!arg){
arg=0
}
if (arg instanceof NativePointer) {
argPtr = arg;
} else if (typeof arg === 'number') {
argPtr = ptr(arg);
} else if (typeof arg === 'string') {
argPtr = Memory.allocUtf8String(arg);
} else if (typeof arg === 'object' && arg.hasOwnProperty('handle')) {
argPtr = arg.handle;
} else if (typeof arg === 'object' && arg instanceof ArrayBuffer) {
var dataPtr = Memory.alloc(arg.byteLength);
Memory.writeByteArray(dataPtr, arg);
argPtr = dataPtr;
} else {
console.error('Unsupported argument type at index ' + i + ':', typeof arg);
throw new TypeError('Unsupported argument type at index ' + i + ': ' + typeof arg);
}
Memory.writePointer(argsPtr.add(i * Process.pointerSize), argPtr);
}
return {
argsPtr: argsPtr,
argNum: argNum
};
}
var dlopenPtr = Module.findExportByName(null, 'dlopen');
var dlopen = new NativeFunction(dlopenPtr, 'pointer', ['pointer', 'int']);
var soPath = "/data/local/tmp/test.so";
var soPathPtr = Memory.allocUtf8String(soPath);
var handle = dlopen(soPathPtr, 2);
var traceaddr = Module.findExportByName("test.so", 'start_trace');
var trace = new NativeFunction(traceaddr, 'pointer', ['pointer', 'pointer', 'uint32','pointer','uint32']);
var aimbase =Module.findBaseAddress("libRequestEncoder.so");
var targetFuncAddr = aimbase.add(0x61bf4);
console.log(handle);
Interceptor.replace(targetFuncAddr, new NativeCallback(function (arg0,arg1,arg2,arg3,arg4,arg5) {
console.log("memory_function called with pointer: " + ptr);
var args =[arg0,arg1,arg2,arg3,arg4,arg5];
var {argsPtr, argNum} = prepareArgs(args);
var argPtr1 = Memory.allocUtf8String("/data/user/0/com.fenbi.android.leo/log.txt");
var res =trace(targetFuncAddr, argsPtr,argNum,argPtr1,6);
return ptr(res);
}, 'pointer', ['pointer','pointer','pointer','pointer','uint32']));
function call(){
Java.perform(function() {
console.log("gan_sign script loaded successfully");
var e = Java.use("com.fenbi.android.leo.utils.e");
var str = "/leo-gateway/android/auth/password";
var str2 = "wdi4n2t8edr";
var intParam = -28673;
var result = e.zcvsd1wr2t(str, str2, intParam);
console.log("input: ", str, str2, intParam);
send(result);
console.log("output: ", result);
});
}
function prepareArgs(args) {
if (args === undefined || !Array.isArray(args)) {
args = [];
}
var argNum = args.length;
var argSize = Process.pointerSize * argNum;
var argsPtr = Memory.alloc(argSize);
for (var i = 0; i < argNum; i++) {
var arg = args[i];
var argPtr;
if (!arg){
arg=0
}
if (arg instanceof NativePointer) {
argPtr = arg;
} else if (typeof arg === 'number') {
argPtr = ptr(arg);
} else if (typeof arg === 'string') {
argPtr = Memory.allocUtf8String(arg);
} else if (typeof arg === 'object' && arg.hasOwnProperty('handle')) {
argPtr = arg.handle;
} else if (typeof arg === 'object' && arg instanceof ArrayBuffer) {
var dataPtr = Memory.alloc(arg.byteLength);
Memory.writeByteArray(dataPtr, arg);
argPtr = dataPtr;
} else {
console.error('Unsupported argument type at index ' + i + ':', typeof arg);
throw new TypeError('Unsupported argument type at index ' + i + ': ' + typeof arg);
}
Memory.writePointer(argsPtr.add(i * Process.pointerSize), argPtr);
}
return {
argsPtr: argsPtr,
argNum: argNum
};
}
var dlopenPtr = Module.findExportByName(null, 'dlopen');
var dlopen = new NativeFunction(dlopenPtr, 'pointer', ['pointer', 'int']);
var soPath = "/data/local/tmp/test.so";
var soPathPtr = Memory.allocUtf8String(soPath);
var handle = dlopen(soPathPtr, 2);
var traceaddr = Module.findExportByName("test.so", 'start_trace');
var trace = new NativeFunction(traceaddr, 'pointer', ['pointer', 'pointer', 'uint32','pointer','uint32']);
var aimbase =Module.findBaseAddress("libRequestEncoder.so");
var targetFuncAddr = aimbase.add(0x61bf4);
console.log(handle);
Interceptor.replace(targetFuncAddr, new NativeCallback(function (arg0,arg1,arg2,arg3,arg4,arg5) {
console.log("memory_function called with pointer: " + ptr);
var args =[arg0,arg1,arg2,arg3,arg4,arg5];
var {argsPtr, argNum} = prepareArgs(args);
var argPtr1 = Memory.allocUtf8String("/data/user/0/com.fenbi.android.leo/log.txt");
var res =trace(targetFuncAddr, argsPtr,argNum,argPtr1,6);
return ptr(res);
}, 'pointer', ['pointer','pointer','pointer','pointer','uint32']));
function call(){
Java.perform(function() {
console.log("gan_sign script loaded successfully");
var e = Java.use("com.fenbi.android.leo.utils.e");
var str = "/leo-gateway/android/auth/password";
var str2 = "wdi4n2t8edr";
var intParam = -28673;
var result = e.zcvsd1wr2t(str, str2, intParam);
console.log("input: ", str, str2, intParam);
send(result);
console.log("output: ", result);
});
}
call()
gan_sign script loaded successfully
memory_function called with pointer: function value() {
[native code]
}
input: /leo-gateway/android/auth/password wdi4n2t8edr -28673
output: d9b6b12a7352587971a9b3b007be672d
message: {'type': 'send', 'payload': 'd9b6b12a7352587971a9b3b007be672d'} data: None
call()
gan_sign script loaded successfully
memory_function called with pointer: function value() {
[native code]
}
input: /leo-gateway/android/auth/password wdi4n2t8edr -28673
output: d9b6b12a7352587971a9b3b007be672d
message: {'type': 'send', 'payload': 'd9b6b12a7352587971a9b3b007be672d'} data: None
void AddrResolver::cacheModules() {
modules.clear();
for (const auto &map: QBDI::getCurrentProcessMaps(true)) {
if (map.name.empty() || map.name.find("[anon]") != std::string::npos) {
continue;
}
if (map.name.find("/") == std::string::npos) {
continue;
}
if ((map.permission & QBDI::PF_READ) == 0) {
continue;
}
auto r = std::find_if(std::begin(modules), std::end(modules),
[&](const Module &m) {
return m.path == map.name;
});
if (r != std::end(modules)) {
r->append(map);
} else {
modules.emplace_back(map);
}
}
}
void AddrResolver::cacheModules() {
modules.clear();
for (const auto &map: QBDI::getCurrentProcessMaps(true)) {
if (map.name.empty() || map.name.find("[anon]") != std::string::npos) {
continue;
}
if (map.name.find("/") == std::string::npos) {
continue;
}
if ((map.permission & QBDI::PF_READ) == 0) {
continue;
}
auto r = std::find_if(std::begin(modules), std::end(modules),
[&](const Module &m) {
return m.path == map.name;
});
if (r != std::end(modules)) {
r->append(map);
} else {
modules.emplace_back(map);
}
}
}
void AddrResolver::loadModules(const std::vector<const Module *> &modules) {
for (const auto &m: modules) {
if (loaded_path.find(m->path) != loaded_path.end()) {
continue;
}
std::unique_ptr externlib = ELFTOOLS::ELF::Parser::parse(m->path);
if (not externlib) {
continue;
}
for (const auto &s: externlib->symbols()) {
QBDI::rword addr = s.value() + m->range.start();
resolv_cache[addr].emplace(s.demangled_name());
}
loaded_path.emplace(m->path);
}
}
void AddrResolver::loadModules(const std::vector<const Module *> &modules) {
for (const auto &m: modules) {
if (loaded_path.find(m->path) != loaded_path.end()) {
continue;
}
std::unique_ptr externlib = ELFTOOLS::ELF::Parser::parse(m->path);
if (not externlib) {
continue;
}
for (const auto &s: externlib->symbols()) {
QBDI::rword addr = s.value() + m->range.start();
resolv_cache[addr].emplace(s.demangled_name());
}
loaded_path.emplace(m->path);
}
}
inline void searchVisibleStringsInMemoryProcessVM(rword addr, size_t size, LogManager *logManager) {
pid_t pid = getpid();
std::vector<uint8_t> buffer(size);
struct iovec local_iov;
local_iov.iov_base = buffer.data();
local_iov.iov_len = size;
struct iovec remote_iov;
remote_iov.iov_base = reinterpret_cast<void *>(addr);
remote_iov.iov_len = size;
ssize_t nread = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
if (nread < 0) {
std::stringstream ss;
ss << "process_vm_readv 失败: " << strerror(errno) << "\n";
logManager->logPrint(ss.str().c_str());
return;
}
logManager->logPrint("内存读取成功。\n");
std::string visibleString;
for (size_t i = 0; i < static_cast<size_t>(nread); ++i) {
if (std::isprint(buffer[i])) {
visibleString += static_cast<char>(buffer[i]);
} else {
if (visibleString.length() >= minStringLength) {
rword stringAddr = addr + i - visibleString.length();
std::stringstream ss;
ss << "地址: 0x" << std::hex << stringAddr
<< ", 字符串: " << visibleString << "\n";
logManager->logPrint(ss.str().c_str());
}
visibleString.clear();
}
}
if (visibleString.length() >= minStringLength) {
rword stringAddr = addr + nread - visibleString.length();
std::stringstream ss;
ss << "地址: 0x" << std::hex << stringAddr
<< ", 字符串: " << visibleString << "\n";
logManager->logPrint(ss.str().c_str());
}
}
inline void searchVisibleStringsInMemoryProcessVM(rword addr, size_t size, LogManager *logManager) {
pid_t pid = getpid();
std::vector<uint8_t> buffer(size);
struct iovec local_iov;
local_iov.iov_base = buffer.data();
local_iov.iov_len = size;
struct iovec remote_iov;
remote_iov.iov_base = reinterpret_cast<void *>(addr);
remote_iov.iov_len = size;
ssize_t nread = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
if (nread < 0) {
std::stringstream ss;
ss << "process_vm_readv 失败: " << strerror(errno) << "\n";
logManager->logPrint(ss.str().c_str());
return;
}
logManager->logPrint("内存读取成功。\n");
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-12-2 21:46
被棕熊编辑
,原因: