1.了解常见frida检测 2.了解frida持久化hook
1.教程Demo(更新) 2.jadx-gui 3.VS Code
在上面的检测对抗中,我们hook了libc.so中的fread、strstr、open等系统函数,但是如果app不讲武德,自实现这些函数,阁下又该如何应对? 在用户空间和内核空间之间,有一个叫做Syscall(系统调用, system call)的中间层,是连接用户态和内核态的桥梁。这样即提高了内核的安全型,也便于移植,只需实现同一套接口即可。Linux系统,用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作。SVC(软件中断指令)指令 :在ARM架构的系统中,svc
是一条特殊的指令,它允许用户态的程序发起一个系统调用。当这条指令被执行时,CPU会从用户态切换到内核态,从而允许内核处理这个请求。
Linux操作系统是一个巨大的图书馆,而syscall
就是这个图书馆的前台服务窗口。当一个应用程序(比如一个读者)需要借阅书籍(获取系统资源或服务)时,它不能直接进入图书馆的内部书架去拿书,因为那样可能会造成混乱和损坏。所以,读者需要通过前台服务窗口,也就是syscall
,来请求它想要的书籍。
svc
就像是图书馆前台服务窗口的内部电话。当读者通过前台窗口提出请求时,前台工作人员会通过内部电话(svc
)来联系图书馆的内部工作人员,请求他们找到并提供所需的书籍。在Linux系统中,当一个程序通过syscall
请求服务时,实际上是通过svc
这条指令通知内核,然后由内核来处理这些请求。Frida-Sigaction-Seccomp实现对Android APP系统调用的拦截 分享一个Android通用svc跟踪以及hook方案——Frida-Seccomp 基于seccomp+sigaction的Android通用svc hook方案 [原创]SVC的TraceHook沙箱的实现&无痕Hook实现思路 [原创]Seccomp技术在Android应用中的滥用与防护 [原创]批量检测android app的so中是否有svc调用
首先当我们长按开机键(电源按钮)开机,此时会引导芯片开始从固化到ROM中的预设代码处执行,然后加载引导程序到RAM。然后启动加载的引导程序,引导程序主要做一些基本的检查,包括RAM的检查,初始化硬件的参数。
到达内核层的流程后,这里初始化一些进程管理、内存管理、加载各种Driver等相关操作,如Camera Driver、Binder Driver 等。下一步就是内核线程,如软中断线程、内核守护线程。下面一层就是Native层,这里额外提一点知识,层于层之间是不可以直接通信的,所以需要一种中间状态来通信。Native层和Kernel层之间通信用的是syscall,Native层和Java层之间的通信是JNI。
在Native层会初始化init进程,也就是用户组进程的祖先进程。init中加载配置文件init.rc,init.rc中孵化出ueventd、logd、healthd、installd、lmkd等用户守护进程。开机动画启动等操作。核心的一步是孵化出Zygote进程,此进程是所有APP的父进程,这也是Xposed注入的核心,同时也是Android的第一个Java进程(虚拟机进程)。
进入框架层后,加载zygote init类,注册zygote socket套接字,通过此套接字来做进程通信,并加载虚拟机、类、系统资源等。zygote第一个孵化的进程是system_server进程,负责启动和管理整个Java Framework,包含ActivityManager、PowerManager等服务。
应用层的所有APP都是从zygote孵化而来
anti脚本
自定义strstr
Frida的Gadget是一个共享库,用于免root注入hook脚本。官方文档 思路:将APK解包后,通过修改smali代码或patch so文件的方式植入frida-gadget,然后重新打包安装。 优点:免ROOT、能过掉一部分检测机制 缺点:重打包可能会遇到解决不了的签名校验、hook时机需要把握
注意的问题:objection patchapk
命令基本上是其他几个系统命令的补充,可尽可能地自动化修补过程。当然,需要先安装并启用这些命令。它们是:
ps:这几个环境工具,aapt、jarsigner都是Android Studio自带的,所以在配置好as的环境即可,abd的环境配置网上搜一下就行,apktool则需要额外配置,我会上传到课件当中
另外会遇到的问题,patchapk的功能在patch的时候会下载对应版本的gadget的so,但是网络问题异常慢,所以建议根据链接去下载好,然后放到这个路径下并重命名
方法一: 思路:可以patch /data/app/pkgname/lib/arm64(or arm)目录下的so文件,apk安装后会将so文件解压到该目录并在运行时加载,修改该目录下的文件不会触发签名校验。 Patch SO的原理可以参考Android平台感染ELF文件实现模块注入 优点:绕过签名校验、root检测和部分ptrace保护。 缺点:需要root、高版本系统下,当manifest中的android:extractNativeLibs为false时,lib目录文件可能不会被加载,而是直接映射apk中的so文件、可能会有so完整性校验 使用方法
然后提取patch后是so文件放到对应的so目录下
方法二: 思路:基于magisk模块方案注入frida-gadget,实现加载和hook。寒冰师傅的FridaManager 优点:无需重打包、灵活性较强 缺点:需要过root检测,magsik检测
方法三: 思路:基于jshook封装好的fridainject框架实现hookJsHook
原理:修改aosp源代码,在fork子进程的时候注入frida-gadgetubuntu 20.04系统AOSP(Android 11)集成Frida AOSP Android 10内置FridaGadget实践01 AOSP Android 10内置FridaGadget实践02(完) |
1.检测方法签名信息,frida在hook方法的时候会把java方法转为native方法 2.Frida在attach进程注入SO时会显式地校验ELF_magic字段,不对则直接报错退出进程,可以手动在内存中抹掉SO的magic,达到反调试的效果。检测点
3.Frida源码中多次调用somain结构体,但它在调用前不会判断是否为空,只要手动置空后Frida一附加就会崩溃检测点
4.通常inline hook第一条指令是mov 常数到寄存器,然后第二条是一个br 寄存器指令。检查第二条指令高16位是不是0xd61f,就可以判断目标函数是否被inline hook了!
5.还可以去hook加固壳,现在很多加固厂商都antifrida了,从壳中的代码去分析检测思路
反思
百度云 阿里云 哔哩哔哩 教程开源地址
PS:解压密码都是52pj,阿里云由于不能分享压缩包,所以下载exe文件,双击自
小菜花的frida-gadget持久化方案汇总 FridaManager:Frida脚本持久化解决方案 Linux系统调用(syscall)原理 小菜花的frida-svc-interceptor [原创]小日本也有大安全——记一次不寻常的手游反调试,反hook分析与绕过 [原创]非root环境下frida持久化的两种方式及脚本 多种姿势花样使用Frida注入
bool
anti_anti_maps() {
const
int
buf_size = 512;
char
buf[buf_size];
int
fd;
fd = my_openat(AT_FDCWD,
"/proc/self/maps"
, O_RDONLY | O_CLOEXEC, 0);
if
(fd != -1) {
while
((read_line(fd, buf, buf_size)) > 0) {
if
(
strstr
(buf,
"frida"
) ||
strstr
(buf,
"gadget"
)) {
close(fd);
return
true
;
}
}
close(fd);
}
else
{
}
return
false
;
}
ENTRY(my_openat)
mov x8, __NR_openat
svc #0
cmn x0, #(MAX_ERRNO + 1)
cneg x0, x0, hi
b.hi __set_errno_internal
ret
END(my_openat)
bool
anti_anti_maps() {
const
int
buf_size = 512;
char
buf[buf_size];
int
fd;
fd = my_openat(AT_FDCWD,
"/proc/self/maps"
, O_RDONLY | O_CLOEXEC, 0);
if
(fd != -1) {
while
((read_line(fd, buf, buf_size)) > 0) {
if
(
strstr
(buf,
"frida"
) ||
strstr
(buf,
"gadget"
)) {
close(fd);
return
true
;
}
}
close(fd);
}
else
{
}
return
false
;
}
ENTRY(my_openat)
mov x8, __NR_openat
svc #0
cmn x0, #(MAX_ERRNO + 1)
cneg x0, x0, hi
b.hi __set_errno_internal
ret
END(my_openat)
function
anti_svc(){
let target_code_hex;
let call_number_openat;
let arch = Process.arch;
if
(
"arm"
=== arch){
target_code_hex =
"00 00 00 EF"
;
call_number_openat = 322;
}
else
if
(
"arm64"
=== arch){
target_code_hex =
"01 00 00 D4"
;
call_number_openat = 56;
}
else
{
console.log(
"arch not support!"
);
}
if
(arch){
console.log(
"\nthe_arch = "
+ arch);
Process.enumerateRanges(
'r--'
).forEach(
function
(range) {
if
(!range.file || !range.file.path){
return
;
}
let path = range.file.path;
if
((!path.startsWith(
"/data/app/"
)) || (!path.endsWith(
".so"
))){
return
;
}
let baseAddress = Module.getBaseAddress(path);
let soNameList = path.split(
"/"
);
let soName = soNameList[soNameList.length - 1];
console.log(
"\npath = "
+ path +
" , baseAddress = "
+ baseAddress +
" , rangeAddress = "
+ range.base +
" , size = "
+ range.size);
Memory.scan(range.base, range.size, target_code_hex, {
onMatch:
function
(match){
let code_address = match;
let code_address_str = code_address.toString();
if
(code_address_str.endsWith(
"0"
) || code_address_str.endsWith(
"4"
) ||
code_address_str.endsWith(
"8"
) || code_address_str.endsWith(
"c"
)){
console.log(
"--------------------------"
);
let call_number = 0;
if
(
"arm"
=== arch){
call_number = (code_address.sub(0x4).readS32()) & 0xFFF;
}
else
if
(
"arm64"
=== arch){
call_number = (code_address.sub(0x4).readS32() >> 5) & 0xFFFF;
}
else
{
console.log(
"the arch get call_number not support!"
);
}
console.log(
"find svc : so_name = "
+ soName +
" , address = "
+ code_address +
" , call_number = "
+ call_number +
" , offset = "
+ code_address.sub(baseAddress));
if
(call_number_openat === call_number){
let target_hook_addr = code_address;
let target_hook_addr_offset = target_hook_addr.sub(baseAddress);
console.log(
"find svc openat , start inlinehook by frida!"
);
Interceptor.attach(target_hook_addr, {
onEnter:
function
(args){
console.log(
"\nonEnter_"
+ target_hook_addr_offset +
" , __NR_openat , args[1] = "
+
args[1].readCString());
this
.new_addr = Memory.allocUtf8String(
"/data/user/0/com.zj.wuaipojie/maps"
);
args[1] =
this
.new_addr;
console.log(
"onEnter_"
+ target_hook_addr_offset +
" , __NR_openat , args[1] = "
+
args[1].readCString());
},
onLeave:
function
(retval){
console.log(
"onLeave_"
+ target_hook_addr_offset +
" , __NR_openat , retval = "
+ retval)
}
});
}
}
},
onComplete:
function
() {}
});
});
}
}
function
anti_svc(){
let target_code_hex;
let call_number_openat;
let arch = Process.arch;
if
(
"arm"
=== arch){
target_code_hex =
"00 00 00 EF"
;
call_number_openat = 322;
}
else
if
(
"arm64"
=== arch){
target_code_hex =
"01 00 00 D4"
;
call_number_openat = 56;
}
else
{
console.log(
"arch not support!"
);
}
if
(arch){
console.log(
"\nthe_arch = "
+ arch);
Process.enumerateRanges(
'r--'
).forEach(
function
(range) {
if
(!range.file || !range.file.path){
return
;
}
let path = range.file.path;
if
((!path.startsWith(
"/data/app/"
)) || (!path.endsWith(
".so"
))){
return
;
}
let baseAddress = Module.getBaseAddress(path);
let soNameList = path.split(
"/"
);
let soName = soNameList[soNameList.length - 1];
console.log(
"\npath = "
+ path +
" , baseAddress = "
+ baseAddress +
" , rangeAddress = "
+ range.base +
" , size = "
+ range.size);
Memory.scan(range.base, range.size, target_code_hex, {
onMatch:
function
(match){
let code_address = match;
let code_address_str = code_address.toString();
if
(code_address_str.endsWith(
"0"
) || code_address_str.endsWith(
"4"
) ||
code_address_str.endsWith(
"8"
) || code_address_str.endsWith(
"c"
)){
console.log(
"--------------------------"
);
let call_number = 0;
if
(
"arm"
=== arch){
call_number = (code_address.sub(0x4).readS32()) & 0xFFF;
}
else
if
(
"arm64"
=== arch){
call_number = (code_address.sub(0x4).readS32() >> 5) & 0xFFFF;
}
else
{
console.log(
"the arch get call_number not support!"
);
}
console.log(
"find svc : so_name = "
+ soName +
" , address = "
+ code_address +
" , call_number = "
+ call_number +
" , offset = "
+ code_address.sub(baseAddress));
if
(call_number_openat === call_number){
let target_hook_addr = code_address;
let target_hook_addr_offset = target_hook_addr.sub(baseAddress);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2024-7-25 12:06
被正己编辑
,原因: