一
过掉Frida检测
使用魔改frida过检测(文件已打包)
魔改frida已经随文件打包,大家可以去github支持下作者。
[!tip] 注意,遇到有frida检测的样本尽量要-f挂起,如果-F可能造成手机卡死重启,耽误调试进度。
function hook_open() {
// https://blog.csdn.net/a656343072/article/details/40539889
/*
函数原型:int open( const char * pathname, int oflags);
int open( const char * pathname,int oflags, mode_t mode);
mode仅当创建新文件时才使用,用于指定文件的访问权限。
pathname 是待打开/创建文件的路径名;
oflags用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。
O_RDONLY 只读模式
O_WRONLY 只写模式
O_RDWR 读写模式
以上三者是互斥的,即不可以同时使用。
*/
var open_addr = Module.findExportByName("libc.so","open")
var io_map = Memory.allocUtf8String("/proc/13585/maps");
Interceptor.attach(open_addr, {
onEnter: function (args) {
console.log('targetFunction called from:n' +
Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('n') + 'n');
if(args[0].readCString().indexOf("/proc/")!=-1 && args[0].readCString().indexOf("maps")!=-1){
args[0] = io_map
// ptr(args[0]).writePointer(Memory.allocUtf8String(args[0].readCString().replaceAll(/d+/g,"1")))
// args[0] = Memory.allocUtf8String("/proc/1/maps")
// Memory.protect(ptr(args[0]), args[0].readCString().length, 'rwx');
// var value_new_str = Memory.allocUtf8String("/proc/1/maps")
// console.log("args0="+args[0].readCString())
// ptr(args[0]).writeByteArray([0x2f,0x70,0x72,0x6f,0x63,0x2f,0x32,0x2f,0x6d,0x61,0x70,0x73,0x0])
// console.log("args0="+args[0].readCString())
}
this.pathname = args[0]
this.oflags = args[1]
this.mode = args[2]
},
onLeave: function (retval) {
console.log("retval="+retval+"---"+"pathname="+this.pathname.readCString()+"---oflags="+this.oflags)
if(this.pathname.readCString().indexOf("libmsaoaidsec")!=-1){ // 过了惠头条
retval.replace(0xffffffff)
}
console.log("open pathname="+this.pathname.readCString()+"---oflags="+this.oflags)
if(this.pathname.readCString().indexOf("/proc/")!=-1 && this.pathname.readCString().indexOf("maps")!=-1){
retval.replace(0x0)
}
}
})
}
retval=0x7b---pathname=/proc/13205/status---oflags=0x0
open pathname=/proc/13205/status---oflags=0x0
targetFunction called from:
0x7bfdefd314 libmonochrome_64.so!0x3c0b314
0x7bfdefd314 libmonochrome_64.so!0x3c0b314
0x7bfdefd314 libmonochrome_64.so!0x3c0b314
[!tip] 逆向工程中搜集信息是一个非常重要的环节,不要吝惜你的谷歌不用。 有很多大佬已经给你铺好了路。 这句话写给你们,也写给我自己,这真的很重要。
使用线程法定位anti位置(目前通用,不排除日后新的框架出现导致复杂情况出现)
[!note] 在寻找frida的各种方法中,我们分为两条路线: 一种是以anti-frida-su.js为主的,去满足正常环境的要求,去各种抹除掉frida注入后的种种痕迹,但是这是致命的,因为frida有无数种特征,你无论如何是抹除不完的,不如重新按照frida写一个自己的HOOK工具。但是幸运的是,有无数大佬为我们铺路,例如非虫大佬的11个patch,能够过掉市场上百分之70的检测,但这是远远不够的。 CRC检测的出现让魔改的frida也无法招架得住。例如,对libart.so的prettymethod的方法的不可规避的注入,让frida无处遁形。我们该怎么办? 非虫大佬已经给出了答案:修改hook pretymthod的hook时机,这样能过掉百分之5左右的样本(为了节约用户硬件资源,启屏后就会关闭),但是大部分样本还是通过开启线程进行crc循环检测 第一种方法变得更加曲折起来,必须要配合魔改rom以及linux内核来进行进一步隐藏。
[!note] 请大家思考,frida的检测粒度一般在什么级别,我们先做假设,假 如我是一个开发人员,我在进行界面跳转,再或者数据请求后,加几句话,对23946端口的检测(frida检测一个例子,当然没有那么简单),那么frida检测我们可以认为是语句级别的粒度,因为只有几行代码,我们也无法避免,因为我们要进行操作。 接上部分,我们公司有安全开发人员,给了我一个函数,我不用考虑别的,直接调用即可,那frida检测我们可以认为是函数级别的粒度 如果我们公司没有开发人员怎么办,当然是外包啦,引入一个安全公司的so,打钱打钱。
normal find thread func offset libshell-super.com.showstartfans.activity.so 0x765f7450d0 360656 580d0
else if(so_name.indexOf("libshell-super.com.showstartfans.activity.so")>-1&& offset==360656){
}
二
进行抓包,并过掉抓包检测
[!tip]
var jclazz = null;
var jobj = null;
function getObjClassName(obj) {
if (!jclazz) {
var jclazz = Java.use("java.lang.Class");
}
if (!jobj) {
var jobj = Java.use("java.lang.Object");
}
return jclazz.getName.call(jobj.getClass.call(obj));
}
function watch(obj, mtdName) {
var listener_name = getObjClassName(obj);
var target = Java.use(listener_name);
if (!target || !mtdName in target) {
return;
}
// send("[WatchEvent] hooking " + mtdName + ": " + listener_name);
target[mtdName].overloads.forEach(function (overload) {
overload.implementation = function () {
//send("[WatchEvent] " + mtdName + ": " + getObjClassName(this));
console.log("[WatchEvent] " + mtdName + ": " + getObjClassName(this))
return this[mtdName].apply(this, arguments);
};
})
}
function OnClickListener() {
Java.perform(function () {
//以spawn启动进程的模式来attach的话
Java.use("android.view.View").setOnClickListener.implementation = function (listener) {
if (listener != null) {
watch(listener, 'onClick');
}
return this.setOnClickListener(listener);
};
//如果frida以attach的模式进行attch的话
Java.choose("android.view.View$ListenerInfo", {
onMatch: function (instance) {
instance = instance.mOnClickListener.value;
if (instance) {
console.log("mOnClickListener name is :" + getObjClassName(instance));
watch(instance, 'onClick');
}
},
onComplete: function () {
}
})
})
}
setImmediate(OnClickListener);
[22041216C::秀动 ]-> [WatchEvent] onClick: com.showstartfans.activity.activitys.login.XDLoginActivity
[!tip] 建议大家自己开发一个okhttp的应用跟一下,有助于文章理解
if (!x0.g()) {
builder.proxy(Proxy.NO_PROXY);
}
NetworkCapabilities networkCapabilities;
return (network == null || connectivityManager == null || (networkCapabilities = connectivityManager.getNetworkCapabilities(network)) == null || !networkCapabilities.hasTransport(4)) ? false : true;
Java.perform(function () {
var x0Class = Java.use("i.a0.a.n.x0");
// 确保方法存在
if (x0Class.i.overloads.length > 0) {
// Hook 所有重载版本(如果有多个的话)
x0Class.i.overloads.forEach(function (overload) {
overload.implementation = function () {
console.log("i.a0.a.n.x0.i method hooked");
// 返回 false
return false;
};
});
}
});
三
确定分析加密内容
showstart_net.so
[!note] 值得讨论的点:这个so是rust配合开发的, 下单接口的请求体和相应体都是加密的,就在上面的so中实现,没有找到任何aes的特征,但是找到了rust库的aes包的引用。 推测原因1:rust数据结构和标准的c不一样,就连字符串结尾都不是以 结尾的。 导致反编译出的so贼乱。 解决办法:开发rust aes 并提取特征点写出插件。
看雪ID:mb_qzwrkwda
https://bbs.kanxue.com/user-home-967562.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):记录一次秀动APP的逆向