本文为看雪论坛精华文章
看雪论坛作者ID:随风而行aa
一
前言
二
相关介绍
dex整体加固:这种方法往往通过动态加载的形式,交换Application的执行,一般我们可以通过hook方法,找到dex_file的起始地址或大小,进行脱取,也可以通过定制Room方法对关键的函数进行插桩,代表有fdex2、Frida_Dump
函数抽取:这种方法往往通过将函数代码抽取放入so文件中,执行时再从so文件读取还原,我们一般可以通过被动调用延时Dump的方法,或主动调用ArtMethod中invoke函数,触发每一个函数,然后进行回填,代表有youpk和fart
VMP:通过定制的指令集进行解释,这时往往需要手工分析,找到指令的映射表,然后进行一步步解释
Java_完整包名_类名_方法名:静态注册
JNI_Onload: 动态注册
动态注册加载so文件两种方式:
(1)System.loadLibrary("native-lib");
(2)System.load(so文件绝对路径)
(1) 空格键:切换文本视图与图表视图
(2) ESC:返回上一个操作地址
(3) G:搜索地址和符号
(4) N:对符号进行重命名
(5) 冒号键:常规注释
(6) 分号键:可重复注释
(7) Alt+M:添加标签
(8) Ctrl+M:查看标签
(9) Ctrl+S:查看节的信息
(10) X:查看交叉应用
(11) F5:查看伪代码
(12) Alt+T:搜索文本
(13) Alt+B:搜索十六进制
(14) 代码数据切换
C-->代码/D-->数据/A-->ascii字符串/U-->解析成未定义的内容
(15) 拷贝伪C代码到反汇编窗口:右键>copy to -assembly
(16) IDA可以修改so的hex操作数来修改so文件,右键点击“edit”进行修改,
然后右键点击“edit-patchrogram”提交
java层:dex加壳技术、混淆技术
so层:so加壳技术、ollvm高级混淆技术
adb shell ps 显示当前的注册信息(adb shell ps | find)| grep
adb forward tcp:8700 jdwp:3924
adb shell am start -D -n My.XuanAo.LiuYao/.main(包名加进程)
注意:Android stdio不能下断点,这是由于Android stdio 版本过高引起的
1.创建模拟器(最好使用真机)
2.在IDA里面找到android_server(dbgsrv目录)
3.把android_server文件放到手机/data/local/tmp
adb push 文件名 /data/local/tmp
4.打开一个cmd窗口:运行android_server
1)adb shell 连接手机
2)给一个最高权限:su
3)来到/data/local/tmp:cd /data/local/tmp
4)给androi_server一个最高的权限:chmod 777 android_server
5)查看android_server是否拥有权限:ls -l
6)运行andorid_server: ./android_server(端口号默认是:23946)
补充:运行andorid_server并且修改端口号:./android_server -p端口号
5.端口转发:
adb forward tcp:端口号 tcp:端口号(之前转发的端口号是什么,这里就是什么)
6.打开DDMS:观察程序的端口号
7.挂起程序:
adb shell am start -D -n 包名/类名
例子:adb shell am start -D -n com.example.javandk1/.MainActivity
补充:此时观察DDMS,被调试的程序前面有一个红色的虫子;
8.IDA里面勾选三项
1)打开ida,选择debugger -第二项-Remote ARMlinux(第四项)
2)添加hostname和portt:
hostname:主机号(默认127.0.0.1)
port:端口号(之前android_server运行时的端口号或者端口转发的端口号)
3)出来进程列表:选择要调试的程序(可以ctrl+f,搜索包名)
4)进来后,勾选三项:
Suspend on process entry point程序入口点 断下
Suspend on thread start/exit线程的退出或启动 断下
Suspend on library load/unload库的加载和卸载 断下
补充:可以直接在这里F9(左上角有一个三角形)运行程序,然后放手;
也可以,直接执行第九步,然后IDA运行程序
9.挂载、释放(放手)
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=端口号(是ddms里显示的端口8600)
补充:此时观察DDMS,被调试的程序前面有一个绿色的虫子;
./gdbserver 0.0.0.0:23946 --attach 11021
gdb-multiach
set arch arm
set arm fallback-mode thumb
target remote 172.31.99.61:23946
F8下一跳
F7步入
C 进入下一个断点
ctrl+shift+pageup 显示上一行状态
ctrl+shift+pagedown 显示下一行状态
如果没有调用,我们可以使用frida主动调用
java层:
android.debuggable=false
so层:
通过检测ptrace值的变化
String[] stringArray = new String[]{"/system/app/Superuser.apk","/sbin/su","/system/bin/su","/system/xbin/su","/data/local/xbin/su","/data/local/bin/su","/system/sd/xbin/su","/system/bin/failsafe/su","/data/local/su","/su/bin/su"};
//遍历数组路径
//执行su
String[] stringArray1 = new String[]{"/system/xbin/which","su"};
process = Runtime.getRuntime().exec(stringArray1);
//遍历到说明含root,遍历不到说明为含root
cat /system/build.prop | grep ro.build.tags
which su
File file = new File("/system/app/Superuser.apk");
if (file.exists())
{
Log.i(LOG_TAG, "/system/app/Superuser.apk exist");
return true;
}
//检测Magisk 是否安装包名为 com.topjohnwu.magisk
which busybox
String[] strCmd = new String[] {"busybox","df"};
ArrayList<String> execResult = executeCommand(strCmd);
if (execResult != null){
Log.i(LOG_TAG,"成功");
return true;
}
else{
Log.i(LOG_TAG,"失败");
return false;
}
Boolean writeFlag = writeFile("/data/su_test", fileContent);
if (writeFlag) {
Log.i(LOG_TAG, "write ok");
} else {
Log.i(LOG_TAG, "write failed");
}
String strRead = readFile("/data/su_test");
Log.i(LOG_TAG, "strRead=" + strRead);
if (fileContent.equals(strRead)) {
return true;
} else {
return false;
}
getprop ro.build.type
getprop ro.build.tags
遍历连接手机所有端口发送D-bus消息,如果返回"REJECT"这个特征则认为存在frida-server
直接调用openat的syscall的检测在text节表中搜索frida-gadget*.so / frida-agent*.so字符串,避免了hook libc来anti-anti的方法
内存中存在frida rpc字符串,认为有frida-server
/*
* Mini-portscan to detect frida-server on any local port.
*/
for(i = 0 ; i <= 65535 ; i++) {
sock = socket(AF_INET , SOCK_STREAM , 0);
sa.sin_port = htons(i);
if (connect(sock , (struct sockaddr*)&sa , sizeof sa) != -1) {
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "FRIDA DETECTION [1]: Open Port: %d", i);
memset(res, 0 , 7);
// send a D-Bus AUTH message. Expected answer is “REJECT"
send(sock, "\x00", 1, NULL);
send(sock, "AUTH\r\n", 6, NULL);
usleep(100);
if (ret = recv(sock, res, 6, MSG_DONTWAIT) != -1) {
if (strcmp(res, "REJECT") == 0) {
/* Frida server detected. Do something… */
}
}
}
close(sock);
}
char line[512];
FILE* fp;
fp = fopen("/proc/self/maps", "r");
if (fp) {
while (fgets(line, 512, fp)) {
if (strstr(line, "frida")) {
/* Evil library is loaded. Do something… */
}
}
fclose(fp);
} else {
/* Error opening /proc/self/maps. If this happens, something is off. */
}
}
三
动静态分析实例分析
(1)启动frida_server
(2)直接frida-dexdump -U -d -f 包名 -o 路径名
(1)模拟器检测
(2)动态调试检测
(3)Root检测
(4)Frida检测
mprop模块
xposed模块
定制Room
./mprop ro.debuggable 1
getprop ro.debuggable
start;stop
adb forward tcp:39026 tcp:39026
绝对地址 = 相对地址(native函数)+ 基地址(so文件)
ps |grep tong
cat /proc/25196/status
a.运行android_server
b.转发端口
c.打开ddms,查看进程状态
d.挂起程序 adb shell am start -D -n 包名/.MainActivity
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8609
四
其他防护过反调试
str2----->d.a----->i3
这里检测windows下bluestacks模拟器的文件夹是否存在,存在i3值即增加
这里使用GLES20着色器来检测Bluestacks模拟器和Translator模拟器
上面部分代码就是从sdk等方面检测各个模拟器,我们可以看见我们这里的夜神模拟器nox
我们可以发现检测root的原理很简单:
(1)列举所有su存在的集合
(2)遍历集合,执行su,看是否成功
(3)成功返回ture
(1)函数的返回值为result
(2)v2值给result
(3)connect函数是否>=0决定最后程序的返回值为true还是false
(4)addr值可以发现就是结构体的首地址,即为0xA2690002
(5)我们需要考虑二进制文件中的大小端序的问题,需要将值进一步的转变,将8位分为两半0xA269和0x0002,然后变为十进制,前面变为0x69A2,即27042
(6)所以我们可以发现connect使用该值就是其端口号27042
总结,APP检测Frida是否运行在27042的套接字上,如果允许在上面就返回true,从而可以判断程序在使用frida,然后关闭程序
function test() {
Java.perform(function () {
var class_obj = Java.use("a.a.a.a.a");
class_obj.R.implementation = function () {
var result = this.R();
console.log("result:"+result);
return false;
}
})
}
function main(params) {
test()
}
setImmediate(main)
五
过反调试的APP漏洞挖掘
function hookentry() {
Java.perform(function () {
var class_obj = Java.use("c.b.a.e");
class_obj.a.implementation = function (arg0) {
var result = this.a(arg0);
console.log("arg0_a:"+arg0);
console.log("result_a:"+result)
return result;
}
class_obj.b.implementation = function (arg0) {
var result = this.b(arg0);
console.log("arg0_b:"+arg0);
console.log("result_b:"+result)
return result;
}
})
}
const SECRET = 'amazing';
const SECRET_LENGTH = SECRET.length;
const operate = (input) => {
let result = "";
for (let i in input) {
result += String.fromCharCode(input.charCodeAt(i)^SECRET.charCodeAt(i%SECRET_LENGTH));
}
return result;
}
const decrypt = (encodedInput) => {
let input = Buffer.from(encodedInput, 'base64').toString();
let dec = operate(input);
console.log(dec);
return dec;
}
decrypt("Gk8UCQwcCQAABFhTTBQHHhIJS0JFEQwSCR4BFQVPW1gaCAFDEA==")
adb shell ps -ef | grep damn
adb logcat | grep 8992
六
实验总结
https://bbs.pediy.com/thread-225717.htm
https://nszdhd1.github.io/2020/03/09/Magisk%E6%A3%80%E6%B5%8B/
https://mabin004.github.io/2018/07/31/Mac%E4%B8%8A%E7%BC%96%E8%AF%91Frida/
https://www.jianshu.com/p/f679cb404524
https://bbs.pediy.com/thread-269862.htm#msg_header_h3_4
https://bbs.pediy.com/thread-270269.htm
https://juejin.cn/post/6844903733248131079#heading-5
https://bbs.pediy.com/thread-268586.htm
https://blog.51cto.com/u_15308480/3140020
看雪ID:随风而行aa
https://bbs.pediy.com/user-home-905443.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!