因为现有的作品均有大大小小的问题,想自己开发一个自己用着比较舒服的框架,提供分析效率。
因为想收集大家出现的反馈,继续发展壮大,我会持续维护一阵子,来提升自己的开发实力。
答案是不开源,会给出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被处理了,后面更新也会更新这里
仓库地址:
https://github.com/jiqiu2022/vm-trace-release
欢迎使用,求个star~ 有问题可以提issue,有问题我会补充到文档里
交流群:(纯技术交流,无广告,欢迎大佬们来交朋友)
加好友拉你进群 现在超过200没法二维码加了

预告:
接下来闲下来会完善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
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
;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-4-14 14:25
被棕熊编辑
,原因: