众所周知学习代码也是学习,研究研究超星某习通的firda检测和签到
一、环境信息
firda 16.5.6frIDA-tools 13.6.0样本版本信息: 6.6.2

image.png (139.85 KB, 下载次数: 0)
下载附件
样本
2025-9-22 18:31 上传
工具:jadx
小黄鸟
yang神脱壳脚本:https://github.com/lasting-yang/frida_dump
经纬度查询:https://map.yanue.net/
参考文章:
某当劳 Frida 检测
aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3Mva1pQWW1fSXItMzljZzdfWTdJc2UzQQ==
[color=]安卓壳学习记录
aHR0cHM6Ly9iYnMua2FueHVlLmNvbS90aHJlYWQtMjg1ODcwLmh0bQ==
安卓加壳脱壳原理-简单概要 https://www.52pojie.cn/thread-1865310-1-4.html
二、frida过检测
将app简单的提取出来,通过文章的一些信息我们知道了这个企业壳的so文件是
libDexHelper.so,
我们可以简单hook一下dlopen加载过程,看看这一个样本的情况
[JavaScript] 纯文本查看 复制代码function hook_dlopen() {
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
console.log("addr_android_dlopen_ext", android_dlopen_ext);
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr != null && pathptr != undefined) {
var path = ptr(pathptr).readCString();
console.log("android_dlopen_ext:", path);
}
},
onLeave: function (retvel) {
}
})
}
function main() {
hook_dlopen()
}
setImmediate(main)
// frida -U -f com.chaoxing.mobile -l .\xxt_hook.js

image.png (789 KB, 下载次数: 1)
下载附件
so文件启动
2025-9-22 18:32 上传
可以看到一个手机处于一个打开界面卡死,frida进程也被杀死的一个状态,同时也加载出了我们壳特征的so文件,我们知道这个是通过线程检测的,我们去hook线程的加载,这里我们采取用的是clone。
[JavaScript] 纯文本查看 复制代码function hook_clone() {
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function (args) {
if (args[3] != 0) {
// 真正的用户线程函数地址
var addr = args[3].add(96).readPointer() // 读取指针
var so_name = Process.findModuleByAddress(addr).name;
// 获取该 so 在进程里的基址
var so_base = Module.getBaseAddress(so_name);
// 获取相对于 so_base 的偏移
var offset = (addr - so_base);
console.log("===============>", so_name, addr, ptr(offset));
}
},
onLeave: function (retval) {
}
});
}
[C] 纯文本查看 复制代码#include
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
这里有一个96的偏移,可以参考原函数,我们需要拿到的是线程的地址,也就是第四个参数,然后调用

image.png (830.27 KB, 下载次数: 1)
下载附件
线程
2025-9-22 18:32 上传
这里我们可以看到壳的线程创建,这里没有通过so去分析,根据文章直接去测试替换掉线程检测函数,frida也有提供api,举一个例子。
[JavaScript] 纯文本查看 复制代码function frida_Interceptor() {
var add_method = new NativeFunction(Module.findExportByName('libhello.so', 'c_getSum'),
'int',['int','int']);
//输出结果 那结果肯定就是 3
console.log("result:",add_method(1,2));
//这里对原函数的功能进行替换实现
Interceptor.replace(add_method, new NativeCallback(function (a, b) {
//h不论是什么参数都返回123
return 123;
}, 'int', ['int', 'int']));
//再次调用 则返回123
console.log("result:",add_method(1,2));
;
}
这里的话呢,我们直接将返回值和入参都直接置空,就可以实现替换
[JavaScript] 纯文本查看 复制代码function hook_remove(so_name) {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf(so_name) !== -1) {
this.match = true;
}
if (args[0].readCString() != null && args[0].readCString().indexOf("libmsaoaidsec.so") >= 0) {
hook_pth()
}
}
},
onLeave: function (retval) {
if (this.match) {
console.log(so_name + " 加载成功");
var base = Module.findBaseAddress(so_name);
console.log("基址:", base)
if (base === null) {
console.error("!!加载成功,但未找到基址:", so_name);
return;
}
patch_func_nop(base.add(0x4c574));
patch_func_nop(base.add(0x56c10));
patch_func_nop(base.add(0x54584));
patch_func_nop(base.add(0x5c3c4));
console.log("libDexHelper.so文件处理成功")
}
}
});
}
function main() {
hook_remove("libDexHelper.so");
}
setImmediate(main)
这样就实现了过掉frida的线程检测,脱壳部分可以直接参考yang神的项目文档,写的非常清楚,然后将脱壳后的代码放到jadx中进行分析
三、定位修改样例分析
当我们实现脱壳,过掉frida检测时,在测试过程中有出现过root环境的一个检测(概率事件,没截到图,小伙伴遇到了),直接搜索一个文本内容

image.png (173.86 KB, 下载次数: 1)
下载附件
root文本
2025-9-22 18:33 上传

image.png (406.32 KB, 下载次数: 1)
下载附件
su检测
2025-9-22 18:33 上传
还是比较简单的一个root检测。直接firda一把梭
[JavaScript] 纯文本查看 复制代码function java_root() {
Java.perform(function () {
let m = Java.use("ge.m");
m["g"].implementation = function (context, key, defaultValue) {
console.log(`m.g is called: context=${context}, key=${key}, defaultValue=${defaultValue}`);
let result = this["g"](context, key, defaultValue);
if (!result) {
result = true;
return result;
}
return result
};
})
}
接下来就需要和小伙伴配合了,感谢我威哥的配合。

image.png (53.75 KB, 下载次数: 1)
下载附件
课程创建
2025-9-22 18:34 上传
创建两个课程,用来方便测试,发送签到信息。我们先发送普通签到用来测试一个接口的信息。

image.png (153.7 KB, 下载次数: 1)
下载附件
普通接口
2025-9-22 18:34 上传
这有一个seccess的返回值,可以合理猜测一下了,其他的好像都是js文件,内嵌的一个webview??重放处理一下。

image.png (159.09 KB, 下载次数: 1)
下载附件
重放
2025-9-22 18:34 上传

image.png (22.43 KB, 下载次数: 1)
下载附件
重放接口
2025-9-22 18:34 上传
这样我们就可以确定一个基本的接口信息,在后面的我们可以过滤非必要的信息。接下来就梭哈定位签到,我们需要用到一个经纬度的网站。直接发定位签到

image.png (300.88 KB, 下载次数: 0)
下载附件
西湖定位
2025-9-22 18:35 上传
可以看到我目前处于西湖的状态,定位要求在故宫博物馆,抓一下现在的接口信息

image.png (115.65 KB, 下载次数: 0)
下载附件
定位接口
2025-9-22 18:35 上传
可以看到,这个大概就是一个经纬度信息,我们需要把这个换成我们想要的,然后发送过去,搜一下latitude关键词

image.png (1.05 MB, 下载次数: 1)
下载附件
经纬度度检测
2025-9-22 18:35 上传
可以看到搜出来的结果很多,因为这里是作为请求里面的数据,我们可以看到是使用了JSONObject来存储,大概是在这里进行的,然后来调用,这里我测试hook了几个,但是都没有出结果,我想到了去hook JSONObject的put方法,因为put在存储是涉及不同的数据类型,需要重载,这里我们可以看到,使用的是double类型,我们去实现一下。
[JavaScript] 纯文本查看 复制代码function java_root() {
Java.perform(function () {
let m = Java.use("ge.m");
m["g"].implementation = function (context, key, defaultValue) {
console.log(`m.g is called: context=${context}, key=${key}, defaultValue=${defaultValue}`);
let result = this["g"](context, key, defaultValue);
if (!result) {
result = true;
return result;
}
return result
};
})
}
这里不写入主函数,在需要的时候直接调用。

image.png (267.76 KB, 下载次数: 1)
下载附件
定位接口触发
2025-9-22 18:36 上传
当我们点击获取地址时,就会触发这个接口,我们提前将这个函数注入,就可以看到latitude和longitude的写入

image.png (811.41 KB, 下载次数: 1)
下载附件
经纬度写入
2025-9-22 18:36 上传
获取一下经纬度信息,我们加一个判断,把入参给改一下
[JavaScript] 纯文本查看 复制代码function hook_location() {
Java.perform(function () {
let JSONObject = Java.use("org.json.JSONObject");
JSONObject["put"].overload('java.lang.String', 'double').implementation = function (str, obj) {
console.log('double类型入参===>', str, obj)
let result = this["put"](str, obj);
return result;
};
})
}
这样发现我们好像改成功了

image.png (332.48 KB, 下载次数: 0)
下载附件
修改测试
2025-9-22 18:36 上传

image.png (84.68 KB, 下载次数: 1)
下载附件
后台
2025-9-22 18:37 上传
因为我们在抓包的时候发现还有一个地址的名字的传入,这个发现是后台的可以查看的信息,这个需要改一下了,严谨一点,但是这个在put写入的时候就又涉及到数据类型
[JavaScript] 纯文本查看 复制代码function hook_location() {
Java.perform(function () {
let JSONObject = Java.use("org.json.JSONObject");
JSONObject["put"].overload('java.lang.String', 'double').implementation = function (str, obj) {
console.log('double类型入参===>', str, obj)
if (str == "latitude") {
obj = 39.924091367210636
}
if (str == 'longitude') {
obj = 116.4034138534206
}
let result = this["put"](str, obj);
return result;
};
JSONObject["put"].overload('java.lang.String', 'java.lang.Object').implementation = function (str, obj) {
console.log('Object类型入参===>', str, obj)
if (str == "address") {
obj = "故宫博物馆"
}
let obj_result = this["put"](str, obj);
return obj_result;
};
})
}
这样修改一下,我们就成功实现了一个定位签到的修改,以及后台信息的匹配

image.png (158.56 KB, 下载次数: 1)
下载附件
结果1
2025-9-22 18:37 上传

image.png (43.04 KB, 下载次数: 1)
下载附件
结果2
2025-9-22 18:37 上传
四、总结
目前这个属于测试过程,后续也会做成一个xposed模块,继续对拍照,二维码等方面进行处理。毕竟学习学习通代码也是学习。好好学习,天天向上。{:1_932:}读大佬文章觉得自己太菜了,继续学习{:1_923:}[md]```