androidleearn
一段dex文件的smali代码分析
1 | 修饰符 /* 私有 静态 不可修改*/ 方法名 参数 |
这是一个方法从开始时到结束
拦窗(开屏广告)去除
法一:修改弹窗时间(class.dex)
法二:activity的切换
直接找androidmainfest.xml
1 | //必须声明可视化界面的activity,必须用mainfest.xml中的<activity>表示所有的的activity(声明、注册、定义),系统并不会识别和运行任何未生命的activity |
2. io重定向
io重定向定义是在读A文件时指向b文件
主要干的是1.禁止访问文件2.文件只读3.路径替换
具体应用:1.过签名检测2.风控对抗(检测文件打开次数)3.对root和xposed的检测(文件不可取)
《安卓逆向这档事》六、校验的N次方-签名校验对抗、PM代{过}{滤}理、IO重定向 - 吾爱破解 - 52pojie.cn
1 | using namespace std; |
1 | sget-object p10, Lcom/zj/wuaipojie/util/ContextUtils;->INSTANCE:Lcom/zj/wuaipojie/util/ContextUtils; |
先找到check_crc方法的位置(dex文件方法名搜索),之后找到调用,在在最开始的方法下站下上面两个代码下面的哪个,然后在
数据目录一中新建files文件夹再将原包转移过来,之后命名为base.apk
android的so入门
先介绍一下静态库与动态库(静态库是直接装载到代码中,使得程序的内存变得很大,动态库是运行时加载)
在windows中的静态库是.lib,动态库.dll
在linux的静态库时.a,动态库是.so
在java中调用c/c++的lib库() ->nativec++的工程, 有两种方法
1 | static{ |
so(lib库)的代码在main->cpp->native-lib.cpp,在旁边的txt文件是编译后结果(有源文件、库名,还有其他库能不能用这个库(shared表示能))而find
loadlibrary函数发现在程序中是找不到源码的,是在android源码中定义的
首先是loadlibrary函数,接着执行loadlibrary0,通过映射得到新类,然后传入名字,之后接着是findlibrary函数找到库的地址,之后判断这个结果是否为空,之后是nativeload函数为c/c++函数,为静态绑定函数
静态绑定是程序编译过程中,把函数(方法或过程)与响应的代码结合的过程(前面是路径名后面是类名)
从java层到native层默认传两个参数JNIEnv * env与jclass
关心path与lib,最后是检验JNI_onload的符号
最先调用是elf文件的initarry函数()
so会在linker中装载,call_arry会调用initarry的所有函数

c/c++中的工具ndk(原生工具),java是sdk
反射调用时参数首先是调用的静态函数,调用的方法,调用方法的参数
jnitrace(基于frida),pip install jnitrace(动态跟踪)
jnitrace -l libndk.so xxx.ndkn
frida
1 | adb shell |
1 | cd /data/local/tmp |
1 | frida-ps -U #检测连接,再开个窗口(检测服务是否正常) |
注入模式
frida -f 启动程序比frida -n进程早的多
spawn模式:将启动APP的权利交由Frida来控制,即使程序已经启动,在使用Frida注入程序时还是会重启App
优点:hook时间早,可以在程序刚启动时就执行
比如:
JNI_Omload
Application.attch
Activity.Oncreate
反调试代码
root检验
anti-frida
缺点是:有些APP检测spawn行为
例如
anti-debug
anti-frida
ptrace检测
接着是脚本注入代码
1 | frida -U -f 包名 -l hook.js |
-f 是在启动一个新进程并附加
-n是在原有的进程中附加
attch模式:在APP已经启动的情况下,Frida通过ptrace注入程序从而执行Hook操作
优点:稳定
缺点:早期函数 hook 不到
例如:
- Application.attach
- JNI_OnLoad
- native 初始化
- 早期检测函数
这些可能已经执行完了。
注入代码
1 | frida -U -n 包名 -l hook.js |
常用的API
模板
1 | Java.perform(function() { |
1.java.performance:Frida的context函数(js代码在执行前,会有个预加载的过程,目的是建立当前js代码的执行环境,而这个执行环境就是上下文,上下文有三种首先是全局上下文,一旦代码被载入,引擎最先进入的就是这个环境。接着是函数执行上下文:当执行一个函数时,运行函数体中的代码,eval上下文,在eval函数中运行的代码)(另一种上下文是函数上下文)
2.Frida在Android提供的Java API如 Java.use()、Java.perform() 等)
实际上是在native层调用ART/Dalvik(类似于一个虚拟机)的内部接口,实现Java反射等功能
3.var <class_reference> = Java.use("<package_name>.<class>");
Java.use,该函数以类名作为参数。
4.
在选定类中,可以使用hook语法范围要hook挂钩的方法,并指定要挂勾的arguments``<args>表示传递给函数的参数。
java hook
1.hook普通的方法,打印参数和修改返回值,替换参数
1 | function hookTest1(){ |
java中的this关键字表示对当前对象的引用,可以构造函数,由无参构造函数调用有参构造函数(构造函数指的是方法名与类名相同的方法)
2.Hook静态方法和字段
1 | //静态方法的主动调用 |
1 | function hookTest5(){ |
3.主动调用
非静态方法
1 | function hooktest(){ |
1 | function hookTest10(){ |
4.hook内部类
1 | function hookTest6(){ |
nativehook
首先是so加载流程
System.loadlibrary()/System.load() ->android_dlopen_exit()/dlopen() ->
do_dlopen() ->findlibrary() ->call_constructors() -> init() ->init_arry() ->
jni_onloadf()
什么是android_dlopen_exit()/dlopen() :”dynamic link“动态装载库,windows存在dll文件的动态加载类型,在linux中有so文件,dlopen就是重要的函数
以下方法去获取Frida中特定函数的地址
使用FridaAPI :Module.enumerateExports()
使用Frida API:Module.getExportByName().
使用Frida API: Module.findExportByName()
计算偏移量并将add()其映射到Module.getAddress()地址 [其实相当于绝对地址 = base address + offset]
使用Frida API: Module.enumerateImports()
native的基本Hook打印
1 | function hookTest2() { |
动态注册函数
1 | function hookTest6(){ |
frida的检测与对抗
文件/端口检测
frida默认端口(默认 27042 和 27043),可以端口转发
修改文件名
1 | ./fsl -l 0.0.0.0:6666 #启动frida-server,监听所有网络接口(0.0.0是指所有网卡)的6666端口 |
使用脚本定位
1 | function hook_dlopen() { var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); |
检测map
什么是map:
/proc/self/maps是一个特殊的文件,它包含了当前进程的内存映射信息。当你打开这个文件时,它会显示一个列表,其中包含了进程中每个内存区域的详细信息。这些信息包含:
1.起始地址(Start Adress)
2.结束地址(End Address)
3.权限(如可读、可写、可执行)
4.共享/私有标志(Shared or Private)
5.关联的文件或设备(如果内存区域是文件映射的)
6.内存区域的偏移量
7.内存区域的类型(如匿名映射、文件映射,设备映射)
当注入frida后,map文件就存在frida-agent-64.so,frida-agent-32.so等文件
检测逻辑
1 | bool check_maps() { |
1 | // 定义一个函数anti_maps,用于阻止特定字符串的搜索匹配,避免检测到敏感内容如"Frida"或"REJECT" |
status(线程)检测
在/proc/pid/task目录下,可以通过查看不同进程的县城子目录,从而获取调试时的信息
在某些app中就会去读取 /proc/stask/线程ID/status 文件,如果监测是frida产生的则进行反调试,。例如:gmain/gdbus/gum-js-loop/pool-frida等
1.gmain:frida使用的Glib库,其主事循环被称为GMainLoop。在Frida中的gmain表示GMainLoop线程
2.gdbbus:GDBus 是 Glib 提供的一个用于 D-Bus 通信的库。在 Frida 中,gdbus 表示 GDBus 相关的线程。
3.gum-js-loop:Gum是Frida的运行时引擎,用于执行注入的JavaScript代码。gum-js-loop 表示 Gum 引擎执行 JavaScript 代码的线程。
4.pool-frida:Frida中的某些功能可用于线程池来处理任务,pool-frida表示Frida中的线程池
5.linjector: 是一种用于 Android 设备的开源工具,它允许用户在运行时向 Android 应用程序注入动态链接库(DLL)文件。通过注入 DLL 文件,用户可以修改应用程序的行为、调试应用程序、监视函数调用等,这在逆向工程、安全研究和动态分析中是非常有用的。
frida可以随时附加,所以检测要覆盖APP的全周期,或者至少是敏感函数执行前
检测逻辑
1 | bool check_status() { |
anti脚本
1 | function replace_str() { |
inline hook 检测
通过Frida查看一个函数hook之前和之后的机器码,以此来判断是否被Frida的inlinehook注入
1 | #include <jni.h> |
1 |
|
