文章目录
- 总结
- 基础
- Android基础
- 工具
- 定位关键代码
- 页面activity定位
- 数据包参数定位
- 堆栈追踪
- 编写
- 反调
- 脱壳
- 好用的脚本
- 过ssl证书校验抓包
- 反调的脚本
- 打印堆栈
- bilibili反调的脚本
总结
暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。
这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到关键函数。
基础
Android基础
开始学习Android逆向,需要先掌握一些Android开发常见的知识点:
1、需要了解一个apk的目录架构,什么文件放在什么位置,将apk当作zip解压可以看到xml文件(声明入口,权限,服务等)、lib文件(so文件)、asserts文件等
2、知道一些常见的函数,(请求的url函数,处理json的函数等),便于后续hook,可以边学边记录,
工具
jadx:反编译工具,得到的java代码较清晰,但是可能会有部分反编译不了
GDA:反编译工具,相对于jadx可能稍微不太好读,但是该工具编译快,且能够直接调用frida,更加方便
frida:编写hook脚本,相对于xpose更加方便调试,但是持久化比xpose麻烦。
模拟器,或者是root的手机。
定位关键代码
在反编译完代码需要的是找到我们需要分析的功能点。
页面activity定位
首先打开我们功能点所在的页面,比如我想查看我的个人记录处的代码逻辑,我就需要打开对应的页面,然后使用下面的命令,查看表层的activity名称,然后去源代码中寻找对应的activity。
查看表层activity的名称
adb shell "dumpsys activity top | grep --color=always ACTIVITY"
数据包参数定位
找到对应功能的数据包,然后根据里面的参数去代码中搜索
比如:这里我想要找这个app是如何触发升级的,于是我拦截他的数据包,然后看到其中的参数app_version,于是我去代码中搜索app_version,如果太多,也可以换参数vm。
堆栈追踪
假如我们需要找的功能,调用了一处函数,但是我们又不知道在哪里调用的,这个时候我们可以打印堆栈。frida脚本放结尾。
我们找到了tostring函数,然后打印堆栈发现a方法调用了tostring
============================= 调用堆栈开始 =======================
com.netease.commonupgrade.CommonUpgradeResponse.toString(Native Method)
com.netease.commonupgrade.CommonUpgradeResponse.a(Native Method)
com.netease.commonupgrade.CommonUpgradeImpl.a(SourceFile:114)
com.netease.commonupgrade.CommonUpgradeImpl.a(SourceFile:49)
com.netease.service.pris.v4.CheckUpdateTransaction.a(SourceFile:158)
com.netease.framework.task.AsyncTransaction.run(SourceFile:52)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
java.lang.Thread.run(Thread.java:919)
============================= 调用堆栈结束 ==========================
编写
编写frida脚本,刚开始的时候我们可以多看看其他人的脚本,然后去frida官网查询相关的函数。
对于一些简单的分析,直接用GDA就可以,选中对应的函数,右键hook方法,或者自定义脚本然后将代码复制出来。
//GDA复制的代码可以直接使用:
Java.perform(function() {
var targetClass='com.netease.commonupgrade.CommonUpgradeResponse';
var methodName='a';
var gclass = Java.use(targetClass);
gclass[methodName].overload('int').implementation = function(arg0) {
console.log('\nGDA[Hook a(int)]'+'\n\targ0 = '+arg0);
var i=this[methodName](arg0);
console.log('\treturn '+i);
printStack();
return i;
}
})
反调
对于反调试,一些常见的手段就是检测相关的特征。
检测文件:检测在/data/local/tmp文件夹下是否存在名称为frida,ida的调试文件等。检测文件的权限是否为x(第二个较少)。
端口检查: 检测常用的调试端口是否打开
ptrace占用: 检测父进程
TracePid:检测调试的变量
常用的绕过手段。
将检测的文件重定向
改名称,端口
将相关代码nop
一些检测的路径。
“/data/data/” + ProName + “/maps”;
“/data/data/” + ProName + “/fmaps”;
“/data/data/” + ProName + “/task”;
“/data/data/” + ProName + “/exe”;
“/data/data/” + ProName + “/mounts”;
"/data/data/ + ProName + /status;
脱壳
这个工具脱壳比较好,一些企业壳也可以脱,如爱加密,360的企业壳。脱的不是完整的但是分析代码已经差不多了。
https://www.52pojie.cn/thread-1781276-1-1.html
踩坑:
1.Android14的手机有问题,读取不到脚本文件
2.一个脚本不行,换其它的试试,(上次虽然是360的壳,但是我没有脱成功,用通用的脚本脱成功了)
好用的脚本
过ssl证书校验抓包
做渗透需要抓包,但是一些app有证书校验,可以采用下面的脚本+burp抓包:
将证书放到脚本里面的路径,然后有反调的需要先过反调
setTimeout(function(){
Java.perform(function (){
console.log("");
console.log("[.] Cert Pinning Bypass/Re-Pinning");
var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
var FileInputStream = Java.use("java.io.FileInputStream");
var BufferedInputStream = Java.use("java.io.BufferedInputStream");
var X509Certificate = Java.use("java.security.cert.X509Certificate");
var KeyStore = Java.use("java.security.KeyStore");
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
var SSLContext = Java.use("javax.net.ssl.SSLContext");
// Load CAs from an InputStream
console.log("[+] Loading our CA...")
var cf = CertificateFactory.getInstance("X.509");
try {
var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
//***********************证书路径*******************************
}
catch(err) {
console.log("[o] " + err);
}
var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
var ca = cf.generateCertificate(bufferedInputStream);
bufferedInputStream.close();
var certInfo = Java.cast(ca, X509Certificate);
console.log("[o] Our CA Info: " + certInfo.getSubjectDN());
// Create a KeyStore containing our trusted CAs
console.log("[+] Creating a KeyStore for our CA...");
var keyStoreType = KeyStore.getDefaultType();
var keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
console.log("[+] Our TrustManager is ready...");
console.log("[+] Hijacking SSLContext methods now...")
console.log("[-] Waiting for the app to invoke SSLContext.init()...")
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {
console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
console.log("[+] SSLContext initialized with our custom TrustManager!");
}
});
},0);
反调的脚本
这个是我在github上找到的,然后加了很多注释,根据需要修改。
//检测:
// /proc/25896/maps
// /proc/25896/status TracerPID Check
//
//
var ProName = ProcessName();
const currentPid = Process.id;
console.log(currentPid);
function ProcessName() {
//libc.so是系统的文件,里面加载着打开,读取,关闭等操作文件的c函数,jni在调用so层时会用到打开,读取等。(下面还用到了pthread_creeate)
var openPtr = Module.getExportByName('libc.so', 'open');
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
var readPtr = Module.getExportByName('libc.so', 'read');
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', 'int']);
var closePtr = Module.getExportByName('libc.so', 'close');
var close = new NativeFunction(closePtr, 'int', ['int']);
var path = Memory.allocUtf8String('/proc/self/cmdline');
//父进程名检测
var fd = open(path, 0);
if (fd != -1) {
var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000);
close(fd);
result = ptr(buffer).readCString();
return result;
}
return -1;
}
//假设一个libxyz.so文件,追踪libxyz.so库,自己修改
//var ourlib = "libexec.so";
//获取pthread_create函数的地址
//var p_pthread_create = Module.findExportByName("libc.so", "pthread_create");
//var pthread_create = new NativeFunction(p_pthread_create, "int", ["pointer", "pointer", "pointer", "pointer"]);
//拦截和替换 pthread_create 函数
/*
Interceptor.replace(p_pthread_create, new NativeCallback(function(ptr0, ptr1, ptr2, ptr3) {
//函数原型
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
//thread:一个指向 pthread_t 类型的指针,用于存储新创建线程的标识符。
//attr:一个指向 pthread_attr_t 类型的指针,用于指定新线程的属性(例如堆栈大小、调度策略等),通常可以传入 NULL 使用默认属性。
//start_routine:一个函数指针,指向新线程将要执行的函数,该函数必须具有 void * 类型的参数并返回 void * 类型的指针。
//arg:传递给 start_routine 函数的参数。
var ret = ptr(0);
if (gmn(ptr0) == ourlib) {
console.log("Thread Created ptr0 : ", gmn(ptr0), Mod, ptr0.sub(Mod));
}
if (gmn(ptr1) == ourlib) {
var Mod = Module.findBaseAddress(ourlib)
console.log("Thread Created ptr1 : ", gmn(ptr1), Mod, ptr1.sub(Mod));
Interceptor.attach(Mod.add(ptr1.sub(Mod)), {
onEnter: function(args) {
console.log("New Thread Func", ptr1.sub(Mod), "arg : ", args[0], args[1]);
},
onLeave: function(retval) {
console.log("New Thread Func Return : ", retval);
}
});
}
if (gmn(ptr2) == ourlib) {
var Mod = Module.findBaseAddress(ourlib)
console.log("Thread Created ptr2 : ", gmn(ptr2), Mod, ptr2.sub(Mod));
Interceptor.attach(Mod.add(ptr2.sub(Mod)), {
onEnter: function(args) {
console.log("New Thread Func", ptr2.sub(Mod), "arg : ", args[0], args[1]);
},
onLeave: function(retval) {
console.log("New Thread Func Return : ", retval);
}
});
}
if (gmn(ptr3) == ourlib) {
var Mod = Module.findBaseAddress(ourlib)
console.log("Thread Created ptr3 : ", gmn(ptr3), Mod, ptr3.sub(Mod));
Interceptor.attach(Mod.add(ptr3.sub(Mod)), {
onEnter: function(args) {
console.log("New Thread Func", ptr3.sub(Mod), "arg : ", args[0], args[1]);
},
onLeave: function(retval) {
console.log("New Thread Func Return : ", retval);
}
});
}
if (ptr1.isNull() && ptr3.isNull()) {
console.warn("loading fake pthread_create");
// return -1 if you not want to create that thread
return pthread_create(ptr0, ptr1, ptr2, ptr3);
// return -1;
} else {
return pthread_create(ptr0, ptr1, ptr2, ptr3);;
}
}, "int", ["pointer", "pointer", "pointer", "pointer"]));
function gmn(fnPtr) {
if (fnPtr != null) {
try {
//获取函数所在模块的名字
return Process.getModuleByAddress(fnPtr).name;
} catch (e) {console.error(e);}
}
}*/
// 检测frida
/*
inet_aton
作用: 将一个 IPv4 地址的字符串表示转换为网络字节序的整数表示。
参数: addrs 是 IPv4 地址的字符串指针;structure 是一个 in_addr 结构体的指针,用于存放转换后的结果。
返回值: 成功返回 1,失败返回 0。
popen
作用: 打开一个管道,通过调用 shell 命令来执行一个进程,并返回一个标准 I/O 流。
参数: path 是要执行的 shell 命令的字符串;type 是打开模式(如 "r" 或 "w")。
返回值: 成功返回指向文件流的指针,失败返回 NULL。
symlink
作用: 创建一个符号链接。
参数: target 是要链接到的目标路径;path 是要创建的符号链接路径。
返回值: 成功返回 0,失败返回 -1。
symlinkat
作用: 在指定的文件描述符(目录)下创建一个符号链接。
参数: target 是要链接到的目标路径;fd 是目录文件描述符;path 是要创建的符号链接路径。
返回值: 成功返回 0,失败返回 -1。
inet_addr
作用: 将一个点分十进制的 IPv4 地址转换为网络字节序的整数表示。
参数: path 是点分十进制 IPv4 地址的字符串。
返回值: 成功返回一个无符号 32 位整数表示的 IP 地址,失败返回 INADDR_NONE。
socket
作用: 创建一个套接字。
参数: domain 是地址族(如 AF_INET 表示 IPv4);type 是套接字类型(如 SOCK_STREAM 表示流套接字);proto 是协议(如 0 表示自动选择合适的协议)。
返回值: 成功返回一个文件描述符,表示创建的套接字;失败返回 -1。
connect
作用: 建立与远程套接字的连接。
参数: fd 是套接字的文件描述符;addr 是指向目标地址结构的指针;len 是地址结构的长度。
返回值: 成功返回 0,失败返回 -1。
send
作用: 发送数据到套接字。
参数: socksfd 是套接字的文件描述符;msg 是指向要发送数据的缓冲区的指针;slen 是要发送的数据的长度;flag 是附加标志。
返回值: 成功返回发送的字节数,失败返回 -1。
sendto
作用: 发送数据到指定的套接字地址。
参数: socksfd 是套接字的文件描述符;msg 是指向要发送数据的缓冲区的指针;slen 是要发送的数据的长度;flag 是附加标志;daddr 是目标地址的指针;dlen 是目标地址的长度。
返回值: 成功返回发送的字节数,失败返回 -1。
open
作用: 打开或创建一个文件或其他资源。
参数: pathname 是要打开的文件路径;flag 是打开标志(如 O_RDONLY 表示只读)。
返回值: 成功返回文件描述符,失败返回 -1。
read
作用: 从文件描述符读取数据。
参数: fd 是文件描述符;buffer 是用于接收数据的缓冲区的指针;count 是要读取的最大字节数。
返回值: 成功返回实际读取的字节数,可能会少于请求的字节数;失败返回 -1。
*/
var inet_atonPtr = Module.findExportByName(null, "inet_aton");
console.log(`inet_atonPtr: ${inet_atonPtr}`);
var inet_aton = new NativeFunction(inet_atonPtr, 'int', ['pointer', 'pointer']);
Interceptor.replace(inet_atonPtr, new NativeCallback(function(addrs, structure) {
var retval = inet_aton(addrs, structure);
//console.log(1);
//console.log("inet_aton : ", addrs.readCString())
return retval;
}, 'int', ['pointer', 'pointer']))
var popenPtr = Module.findExportByName("libc.so", "popen");
var popen = new NativeFunction(popenPtr, 'pointer', ['pointer', 'pointer']);
Interceptor.replace(popenPtr, new NativeCallback(function(path, type) {
var retval = popen(path, type);
//console.log(2);
//console.log("popen : ", path.readCString());
return retval;
}, 'pointer', ['pointer', 'pointer']))
var symlinkPtr = Module.findExportByName("libc.so", "symlink");
var symlink = new NativeFunction(symlinkPtr, 'int', ['pointer', 'pointer']);
Interceptor.replace(symlinkPtr, new NativeCallback(function(target, path) {
var retval = symlink(target, path);
//console.log(3);
//console.log("symlink: ", target.readCString(), path.readCString());
return retval;
}, 'int', ['pointer', 'pointer']))
var symlinkatPtr = Module.findExportByName("libc.so", "symlinkat");
var symlinkat = new NativeFunction(symlinkatPtr, 'int', ['pointer', 'int', 'pointer']);
Interceptor.replace(symlinkatPtr, new NativeCallback(function(target, fd, path) {
var retval = symlinkat(target, fd, path);
//console.log(4);
//console.log("symlinkat : ", target.readCString(), path.readCString());
return retval;
}, 'int', ['pointer', 'int', 'pointer']))
var inet_addrPtr = Module.findExportByName("libc.so", "inet_addr");
var inet_addr = new NativeFunction(inet_addrPtr, 'int', ['int']);
Interceptor.replace(inet_addrPtr, new NativeCallback(function(path) {
var retval = inet_addr(path);
//console.log(5);
//console.log("inet_addr : ", path.readCString())
return retval;
}, 'int', ['int']))
var socketPtr = Module.findExportByName("libc.so", "socket");
var socket = new NativeFunction(socketPtr, 'int', ['int', 'int', 'int']);
Interceptor.replace(socketPtr, new NativeCallback(function(domain, type, proto) {
var retval = socket(domain, type, proto);
//console.log(6);
//console.warn("socket : ", domain, type, proto, "Return : ", retval)
return retval;
}, 'int', ['int', 'int', 'int']))
var connectPtr = Module.findExportByName("libc.so", "connect");
var connect = new NativeFunction(connectPtr, 'int', ['int', 'pointer', 'int']);
Interceptor.replace(connectPtr, new NativeCallback(function(fd, addr, len) {
var retval = connect(fd, addr, len);
var family = addr.readU16();
var port = addr.add(2).readU16();
//port = ((port & 0xff) << 8) | (port >> 8);
//console.log(7);
//console.warn("Connect : ", family, "Port : ", port, "Return : ", retval);
return retval;
}, 'int', ['int', 'pointer', 'int']))
var sendPtr = Module.findExportByName("libc.so", "send");
var send2 = new NativeFunction(sendPtr, 'int', ['int', 'pointer', 'int', 'int']);
Interceptor.replace(sendPtr, new NativeCallback(function(socksfd, msg, slen, flag, daddr, dlen) {
var retval = send2(socksfd, msg, slen, flag);
//console.log(8);
//console.log("send : ", socksfd, msg.readCString(), slen, flag);
return retval;
}, 'int', ['int', 'pointer', 'int', 'int']))
var sendtoPtr = Module.findExportByName("libc.so", "sendto");
var sendto = new NativeFunction(sendtoPtr, 'int', ['int', 'pointer', 'int', 'int', 'pointer', 'int']);
Interceptor.replace(sendtoPtr, new NativeCallback(function(socksfd, msg, slen, flag, daddr, dlen) {
var retval = sendto(socksfd, msg, slen, flag, daddr, dlen);
//console.log(9);
//console.log("sendto : ",socksfd,msg.readCString(),slen,flag,daddr,dlen);
return retval;
}, 'int', ['int', 'pointer', 'int', 'int', 'pointer', 'int']))
const openPtr = Module.getExportByName('libc.so', 'open');
const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
var readPtr = Module.findExportByName("libc.so", "read");
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', "int"]);
//这一段就是hookopen函数,将记录了文件是否被调试的文件进行了重定向
//ProName:进程名
var FakeMaps = "/data/data/" + ProName + "/maps";
var FOpenMaps = "/data/data/" + ProName + "/fmaps";
var FakeTask = "/data/data/" + ProName + "/task";
var FakeExE = "/data/data/" + ProName + "/exe";
var FakeMounts = "/data/data/" + ProName + "/mounts";
var FakeStatus = "/data/data/" + ProName + "/status";
var MapsFile = new File(FakeMaps, "w");
var TaskFile = new File(FakeTask, "w");
var ExEFile = new File(FakeExE, "w");
var FMapsFile = new File(FOpenMaps, "w");
var FMountFile = new File(FakeMounts, "w");
var StatusFile = new File(FakeStatus, "w");
var MapsBuffer = Memory.alloc(512);
var TaskBuffer = Memory.alloc(512);
var ExEBuffer = Memory.alloc(512);
var FopenBuffer = Memory.alloc(512);
var MountBuffer = Memory.alloc(512);
var StatusBuffer = Memory.alloc(512);
var Open64MapsBuffer = Memory.alloc(512);
//openptr在前面申明了,是open函数的地址
Interceptor.replace(openPtr, new NativeCallback(function(pathname, flag) {
//pathname = "/proc/" + currentPid + "/maps";
var FD = open(pathname, flag);
var ch = pathname.readCString();
//如果路径是/proc/xxx/maps
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("maps") >= 0) {
console.log("open : ", pathname.readCString(),11111)
//parseInt:将字符串转换成整数
while (parseInt(read(FD, MapsBuffer, 512)) !== 0) {
//读取打开的文件的字符串
var MBuffer = MapsBuffer.readCString();
//将所有调试文件的名字替换
//console.log(1);
MBuffer = MBuffer.replaceAll("/data/local/tmp/re.frida.server/frida-agent-64.so", "FakingMaps");
MBuffer = MBuffer.replaceAll("re.frida.server", "FakingMaps");
MBuffer = MBuffer.replaceAll("re.frida", "FakingMaps");
MBuffer = MBuffer.replaceAll("re.", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida.", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-agent-64.so", "FakingMaps");
MBuffer = MBuffer.replaceAll("rida-agent-64.so", "FakingMaps");
MBuffer = MBuffer.replaceAll("agent-64.so", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-agent-32.so", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-helper-32", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-helper", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-agent", "FakingMaps");
MBuffer = MBuffer.replaceAll("pool-frida", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-", "FakingMaps");
MBuffer = MBuffer.replaceAll("/data/local/tmp", "/data");
MBuffer = MBuffer.replaceAll("server", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida-server", "FakingMaps");
MBuffer = MBuffer.replaceAll("linjector", "FakingMaps");
MBuffer = MBuffer.replaceAll("gum-js-loop", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida_agent_main", "FakingMaps");
MBuffer = MBuffer.replaceAll("gmain", "FakingMaps");
MBuffer = MBuffer.replaceAll("frida", "FakingMaps");
MBuffer = MBuffer.replaceAll("magisk", "FakingMaps");
MBuffer = MBuffer.replaceAll(".magisk", "FakingMaps");
MBuffer = MBuffer.replaceAll("/sbin/.magisk", "FakingMaps");
MBuffer = MBuffer.replaceAll("libriru", "FakingMaps");
MBuffer = MBuffer.replaceAll("xposed", "FakingMaps");
MapsFile.write(MBuffer);
//console.log("MBuffer : ",MBuffer);
}
var filename = Memory.allocUtf8String(FakeMaps);
return open(filename, flag);
}
//如果路径是/proc/xxx/task,检测正在运行的任务
if (ch.indexOf("/proc") >= 0 && ch.indexOf("task") >= 0) {
// console.log("open : ", pathname.readCString())
while (parseInt(read(FD, TaskBuffer, 512)) !== 0) {
var buffer = TaskBuffer.readCString();
buffer = buffer.replaceAll("re.frida.server", "FakingTask");
buffer = buffer.replaceAll("frida-agent-64.so", "FakingTask");
buffer = buffer.replaceAll("rida-agent-64.so", "FakingTask");
buffer = buffer.replaceAll("agent-64.so", "FakingTask");
buffer = buffer.replaceAll("frida-agent-32.so", "FakingTask");
buffer = buffer.replaceAll("frida-helper-32", "FakingTask");
buffer = buffer.replaceAll("frida-helper", "FakingTask");
buffer = buffer.replaceAll("frida-agent", "FakingTask");
buffer = buffer.replaceAll("pool-frida", "FakingTask");
buffer = buffer.replaceAll("frida", "FakingTask");
buffer = buffer.replaceAll("/data/local/tmp", "/data");
buffer = buffer.replaceAll("server", "FakingTask");
buffer = buffer.replaceAll("frida-server", "FakingTask");
buffer = buffer.replaceAll("linjector", "FakingTask");
buffer = buffer.replaceAll("gum-js-loop", "FakingTask");
buffer = buffer.replaceAll("frida_agent_main", "FakingTask");
buffer = buffer.replaceAll("gmain", "FakingTask");
buffer = buffer.replaceAll("magisk", "FakingTask");
buffer = buffer.replaceAll(".magisk", "FakingTask");
buffer = buffer.replaceAll("/sbin/.magisk", "FakingTask");
buffer = buffer.replaceAll("libriru", "FakingTask");
buffer = buffer.replaceAll("xposed", "FakingTask");
buffer = buffer.replaceAll("pool-spawner", "FakingTask");
buffer = buffer.replaceAll("gdbus", "FakingTask");
TaskFile.write(buffer);
// console.log(buffer);
}
var filename2 = Memory.allocUtf8String(FakeTask);
return open(filename2, flag);
}
//如果路径是/proc/xxx/mounts
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("mounts") >= 0) {
console.log("open : ", pathname.readCString())
while (parseInt(read(FD, MountBuffer, 512)) !== 0) {
var MNTBuffer = MountBuffer.readCString();
MNTBuffer = MNTBuffer.replaceAll("magisk", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("/sbin/.magisk", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("libriru", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("mirror", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("system_root", "StaySafeStayHappy");
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy")
FMountFile.write(MNTBuffer);
// console.log("MNTBuffer : ",MNTBuffer);
}
var mountname = Memory.allocUtf8String(FakeMounts);
return open(mountname, flag);
}
if (ch.indexOf("/proc/") >=0 && ch.indexOf("status") >=0) {
console.log("open : ", pathname.readCString())
while (parseInt(read(FD, StatusBuffer, 512)) !== 0) {
var PStatus = StatusBuffer.readCString();
if (PStatus.indexOf("TracerPid:") > -1) {
StatusBuffer.writeUtf8String("TracerPid:\t0");
console.log("Bypassing TracerPID Check");
}
StatusFile.write(PStatus);
}
var statusname = Memory.allocUtf8String(FakeStatus);
return open(statusname, flag);
}
if (ch.indexOf("/proc") >= 0 && ch.indexOf("exe") >= 0) {
console.log("open : ", pathname.readCString())
while (parseInt(read(FD, ExEBuffer, 512)) !== 0) {
var buffer = ExEBuffer.readCString();
// console.warn(buffer)
buffer = buffer.replaceAll("frida-agent-64.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent-32.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("re.frida.server", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper-32", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper", "StaySafeStayHappy");
buffer = buffer.replaceAll("pool-frida", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida", "StaySafeStayHappy");
buffer = buffer.replaceAll("/data/local/tmp", "/data");
buffer = buffer.replaceAll("frida-server", "StaySafeStayHappy");
buffer = buffer.replaceAll("linjector", "StaySafeStayHappy");
buffer = buffer.replaceAll("gum-js-loop", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida_agent_main", "StaySafeStayHappy");
buffer = buffer.replaceAll("gmain", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent", "StaySafeStayHappy");
ExEFile.write(buffer);
}
var filename3 = Memory.allocUtf8String(FakeExE);
return open(filename3, flag);
}
return FD;
}, 'int', ['pointer', 'int']))
var fgetsPtr = Module.findExportByName("libc.so", "fgets");
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.replace(fgetsPtr, new NativeCallback(function(buf, size, fp) {
//var retval = fgets(buf, size, fp);
var buffer = buf.readCString();
buffer = buffer.replaceAll("re.frida.server", "FakingGets");
buffer = buffer.replaceAll("frida-agent-64.so", "FakingGets");
buffer = buffer.replaceAll("rida-agent-64.so", "FakingGets");
buffer = buffer.replaceAll("agent-64.so", "FakingGets");
buffer = buffer.replaceAll("frida-agent-32.so", "FakingGets");
buffer = buffer.replaceAll("frida-helper-32", "FakingGets");
buffer = buffer.replaceAll("frida-helper", "FakingGets");
buffer = buffer.replaceAll("frida-agent", "FakingGets");
buffer = buffer.replaceAll("pool-frida", "FakingGets");
buffer = buffer.replaceAll("frida", "FakingGets");
buffer = buffer.replaceAll("/data/local/tmp", "/data");
buffer = buffer.replaceAll("server", "FakingGets");
buffer = buffer.replaceAll("frida-server", "FakingGets");
buffer = buffer.replaceAll("linjector", "FakingGets");
buffer = buffer.replaceAll("gum-js-loop", "FakingGets");
buffer = buffer.replaceAll("frida_agent_main", "FakingGets");
buffer = buffer.replaceAll("gmain", "FakingGets");
buffer = buffer.replaceAll("magisk", "FakingGets");
buffer = buffer.replaceAll(".magisk", "FakingGets");
buffer = buffer.replaceAll("/sbin/.magisk", "FakingGets");
buffer = buffer.replaceAll("libriru", "FakingGets");
buffer = buffer.replaceAll("xposed", "FakingGets");
buf.writeUtf8String(buffer);
// console.log(buf.readCString());
return fgets(buf, size, fp);
}, 'pointer', ['pointer', 'int', 'pointer']))
var readlinkPtr = Module.findExportByName("libc.so", "readlink");
var readlink = new NativeFunction(readlinkPtr, 'int', ['pointer', 'pointer', 'int']);
Interceptor.replace(readlinkPtr, new NativeCallback(function(pathname, buffer, bufsize) {
var retval = readlink(pathname, buffer, bufsize);
//从内存中读取字符串,判断是否含有特征字符
if(buffer.readCString().indexOf("frida")!==-1 ||
buffer.readCString().indexOf("gum-js-loop")!==-1||
buffer.readCString().indexOf("gmain")!==-1 ||
buffer.readCString().indexOf("linjector")!==-1 ||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
buffer.readCString().indexOf("pool-frida")!==-1 ||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
buffer.readCString().indexOf("frida-agent")!==-1 ||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
){
console.log(buffer.readCString(), "Check in readlink");
buffer.writeUtf8String("/system/framework/services.jar");
return readlink(pathname, buffer, bufsize);
}
// console.log("readlink : ", pathname.readCString(), buffer.readCString());
return retval;
}, 'int', ['pointer', 'pointer', 'int']))
var readlinkatPtr = Module.findExportByName("libc.so", "readlinkat");
var readlinkat = new NativeFunction(readlinkatPtr, 'int', ['int', 'pointer', 'pointer', 'int']);
Interceptor.replace(readlinkatPtr, new NativeCallback(function(dirfd, pathname, buffer, bufsize) {
var retval = readlinkat(dirfd, pathname, buffer, bufsize);
if(buffer.readCString().indexOf("frida")!==-1 ||
buffer.readCString().indexOf("gum-js-loop")!==-1||
buffer.readCString().indexOf("gmain")!==-1 ||
buffer.readCString().indexOf("linjector")!==-1 ||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
buffer.readCString().indexOf("pool-frida")!==-1 ||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
buffer.readCString().indexOf("frida-agent")!==-1 ||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
){
console.log(buffer.readCString(), "Check in readlinkat");
buffer.writeUtf8String("/system/framework/services.jar");
return readlinkat(dirfd, pathname, buffer, bufsize);
}
// console.log("readlinkat : ", pathname.readCString(), buffer.readCString());
return retval;
}, 'int', ['int', 'pointer', 'pointer', 'int']))
/*
Interceptor.attach(Module.findExportByName(null, "strstr"),{
onEnter: function(args){
this.frida = false;
console.log(3);
var str1 = args[0].readCString();
var str2 = args[1].readCString();
if(str1.indexOf("frida")!==-1 || str2.indexOf("frida")!==-1 ||
str1.indexOf("gum-js-loop")!==-1 || str2.indexOf("gum-js-loop")!==-1 ||
str1.indexOf("gmain")!==-1 || str2.indexOf("gmain")!==-1 ||
str1.indexOf("linjector")!==-1 || str2.indexOf("linjector")!==-1 ||
str1.indexOf("/data/local/tmp")!==-1 || str2.indexOf("/data/local/tmp")!==-1 ||
str1.indexOf("pool-frida")!==-1 || str2.indexOf("pool-frida")!==-1 ||
str1.indexOf("frida_agent_main")!==-1 || str2.indexOf("frida_agent_main")!==-1 ||
str1.indexOf("re.frida.server")!==-1 || str2.indexOf("re.frida.server")!==-1 ||
str1.indexOf("frida-agent")!==-1 || str2.indexOf("frida-agent")!==-1 ||
str1.indexOf("pool-spawner")!==-1 || str2.indexOf("pool-spawner")!==-1 ||
str1.indexOf("frida-agent-64.so")!==-1 || str2.indexOf("frida-agent-64.so")!==-1 ||
str1.indexOf("frida-agent-32.so")!==-1 || str2.indexOf("frida-agent-32.so")!==-1 ||
str1.indexOf("frida-helper-32.so")!==-1 || str2.indexOf("frida-helper-32.so")!==-1 ||
str1.indexOf("frida-helper-64.so")!==-1 || str2.indexOf("frida-helper-64.so")!==-1 ||
str1.indexOf("/sbin/.magisk")!==-1 || str2.indexOf("/sbin/.magisk")!==-1 ||
str1.indexOf("libriru")!==-1 || str2.indexOf("libriru")!==-1 ||
str1.indexOf("magisk")!==-1 || str2.indexOf("magisk")!==-1
){
this.frida = true;
console.log("检测到:strstr : ",str1,str2);
}
},
onLeave: function(retval){
if (this.frida) {
retval.replace(ptr("0x0"));
}
}
});
*/
//Enabling it might give crash on some apps
/*
Interceptor.attach(Module.findExportByName("libc.so", "read"), {
onEnter: function(args) {
try {
var buffer = args[1].readCString();
if (buffer.indexOf("frida") >= 0) {
buffer = buffer.replaceAll("re.frida.server", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent-64.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("rida-agent-64.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("agent-64.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent-32.so", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper-32", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent", "StaySafeStayHappy");
buffer = buffer.replaceAll("pool-frida", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida", "StaySafeStayHappy");
buffer = buffer.replaceAll("/data/local/tmp", "/data");
buffer = buffer.replaceAll("server", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida-server", "StaySafeStayHappy");
buffer = buffer.replaceAll("linjector", "StaySafeStayHappy");
buffer = buffer.replaceAll("gum-js-loop", "StaySafeStayHappy");
buffer = buffer.replaceAll("frida_agent_main", "StaySafeStayHappy");
buffer = buffer.replaceAll("gmain", "StaySafeStayHappy");
buffer = buffer.replaceAll("magisk", "StaySafeStayHappy");
buffer = buffer.replaceAll(".magisk", "StaySafeStayHappy");
buffer = buffer.replaceAll("/sbin/.magisk", "StaySafeStayHappy");
buffer = buffer.replaceAll("libriru", "StaySafeStayHappy");
buffer = buffer.replaceAll("xposed", "StaySafeStayHappy");
args[1].writeUtf8String(buffer);
}
} catch (e) {
//console.error(e);
}
}
});
*/
/*
var memcpyPtr = Module.findExportByName("libc.so", "memcpy");
var memcpy = new NativeFunction(memcpyPtr, 'pointer', ['pointer', 'pointer', 'int']);
Interceptor.replace(memcpyPtr, new NativeCallback(function(dest, src, len) {
var retval = memcpy(dest, src, len);
if(dest.readCString() != null && src.readCString() != null && (dest.readCString().indexOf("frida")>=0 || src.readCString().indexOf("frida")>=0) )
{
//console.warn("memcpy : ",dest.readCString(),src.readCString());
var buffer = dest.readCString();
var buffer2 = src.readCString();
buffer = buffer.replaceAll("re.frida.server","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent-64.so","StaySafeStayHappy");
buffer = buffer.replaceAll("rida-agent-64.so","StaySafeStayHappy");
buffer = buffer.replaceAll("agent-64.so","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent-32.so","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper-32","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-helper","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-agent","StaySafeStayHappy");
buffer = buffer.replaceAll("pool-frida","StaySafeStayHappy");
buffer = buffer.replaceAll("frida","StaySafeStayHappy");
buffer = buffer.replaceAll("/data/local/tmp","/data");
buffer = buffer.replaceAll("server","StaySafeStayHappy");
buffer = buffer.replaceAll("frida-server","StaySafeStayHappy");
buffer = buffer.replaceAll("linjector","StaySafeStayHappy");
buffer = buffer.replaceAll("gum-js-loop","StaySafeStayHappy");
buffer = buffer.replaceAll("frida_agent_main","StaySafeStayHappy");
buffer = buffer.replaceAll("gmain","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("re.frida.server","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-agent-64.so","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("rida-agent-64.so","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("agent-64.so","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-agent-32.so","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-helper-32","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-helper","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-agent","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("pool-frida","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("/data/local/tmp","/data");
buffer2 = buffer2.replaceAll("server","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida-server","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("linjector","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("gum-js-loop","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("frida_agent_main","StaySafeStayHappy");
buffer2 = buffer2.replaceAll("gmain","StaySafeStayHappy");
dest.writeUtf8String(buffer);
src.writeUtf8String(buffer2);
// console.log(buffer,buffer2);
return memcpy(dest, src, len);
}
return retval;
}, 'pointer', ['pointer', 'pointer', 'int']))
*/
打印堆栈
将函数加在代码中,然后在hook的函数中调用printStack();即可。
// 日志打印和调用堆栈输出函数
// 日志输出
function LOG(sth)
{
console.log(sth);
}
// 打印调用堆栈的函数
function printStack()
{
Java.perform
(
function()
{
var Exception = Java.use("java.lang.Exception"); //加载Java类 java.lang.Exception
var ins = Exception.$new("Exception");// Exception构造方法调用
var straces = ins.getStackTrace();// 调用Exception对象的getStackTrace方法
if (straces != undefined && straces != null)
{
var strace = straces.toString();
var replaceStr = strace.replace(/,/g, "\n");
LOG("============================= 调用堆栈开始 =======================\n");
LOG(replaceStr);
LOG("============================= 调用堆栈结束 ==========================\n");
Exception.$dispose();
}
}
);
}
bilibili反调的脚本
在网上找到的,可以绕过bilibili的反调。
function create_fake_pthread_create() {
const fake_pthread_create = Memory.alloc(4096)
Memory.protect(fake_pthread_create, 4096, "rwx")
Memory.patchCode(fake_pthread_create, 4096, code => {
const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) })
cw.putRet()
})
return fake_pthread_create
}
function hook_dlsym() {
var count = 0
console.log("=== HOOKING dlsym ===")
var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"),
{
onEnter: function (args) {
const name = ptr(args[1]).readCString()
console.log("[dlsym]", name)
if (name == "pthread_create") {
count++
}
},
onLeave: function(retval) {
if (count == 1) {
retval.replace(fake_pthread_create)
}
else if (count == 2) {
retval.replace(fake_pthread_create)
// 完成2次替换, 停止hook dlsym
interceptor.detach()
}
}
}
)
return Interceptor
}
function hook_dlopen() {
var interceptor = 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();
console.log("[LOAD]", path)
if (path.indexOf("libmsaoaidsec.so") > -1) {
hook_dlsym()
}
}
}
}
)
return interceptor
}
// 创建虚假pthread_create
var fake_pthread_create = create_fake_pthread_create()
var dlopen_interceptor = hook_dlopen()