Frida安卓基础

463 阅读9分钟

| 基于Kali Linux | htop 查看当前活跃进程 | jnettop 实时查看系统网络负载工具 | 源码阅读:cs.android.com | 源码阅读:aospxref.com | 源码阅读:androidxref.com | 源码阅读:www.androidos.net.cn/sourcecode

目录

1. 刷机

Android 源码编译和刷机ROOT

2. Shell 常用命令

  • cat 查看文本文件内容: cat 1.txt
  • touch 创建空文件 : touch a.txt
  • echo > 覆盖写操作: echo "afra55" > a.txt
  • echo >> 扩展写操作
  • grep 过滤符合条件的输出: cat a.txt | grep afra
  • ps -e 打印全部进程,查看手机的全部进程
  • ps 打印当前进程
  • netstat 查看对应APP进程的IP、端口、协议等网络信息: netstat -alpe grep org.myapp
  • lsof 查看对应进程打开的文件: lsof -p 21212 -l
  • top 查看当前活跃进程,查看手机当前运行的进程

3. adb 常用命令

描述命令
查看当前处于前台的Activityadb shell dumpsys activity top
打印当前焦点窗口adb shell dumpsys window | grep mCurrentFocus
打印包信息,四大组件MIME权限等adb shell dumpsys 包名
查看APPP的数据库信息adb shell dumpsys dbinfo 包名
截图保存到手机adb shell screencap -p 路径
截图保存到电脑adb exec-out screencap -p > screen.png
录制屏幕adb shell screenrecord /sdcard/filename.mp4
在焦点输入框内输入文字,无法输入中文adb shell input test 文字
列出所有按照的APK包名adb shell pm list packages
安装手机里的APK文件,uninstall 包名用于卸载adb shell pm install 安装包路径
启动APP的Activity, -D是DEBUG模式adb shell am start-activity -D -N 包名/类名
启动主Activityadb shell monkey -p 包名 -c android.intent.category.LAUNCHER 1
查看手机系统架构adb shell getprop ro.product.cpu.abi
查看手机型号adb shell getprop ro.product.model
查看电池信息adb shell dumpsys battery
查看屏幕分辨率adb shell wm size
查看屏幕密度adb shell wm density
查看android_idadb shell settings get secure android_id
adb获取蓝牙MAC地址adb shell settings get secure bluetooth_address
adb获取IMEIadb shell "service call iphonesubinfo 1 | toybox cut -d \"'\" -f2 | toybox grep -Eo '[0-9]' | toybox xargs | toybox sed 's/\ //g'"
清除应用数据&缓存adb shell pm clear 包名
获取设备IP地址adb shell ip route | awk '{print $9}'
启用设备的网络 adb 功能adb tcpip 5555
网络连接设备adb connect DEVICE_IP:5555
截图adb shell screencap -p /sdcard/screenshot.png
查看应用安装路径, 该路径下的apk可以pull下来使用adb shell pm path 包名
强制停止应用adb shell am force-stop 包名
模拟按键输入,见附表一adb shell input keyevent 按键代码
向上滑动手势解锁adb shell input swipe 300 1000 300 500
发送广播,见附表二adb shell am broadcast -a <action> [-d ] [-t <mime_type>] [-c <category>]
查看实时资源占用情况,见附表四adb shell top

附表一:按键代码

keycode含义
3HOME 键
4返回键
5打开拨号应用
6挂断电话
24增加音量
25降低音量
26电源键
27拍照(需要在相机应用里)
64打开浏览器
82菜单键
85播放/暂停
86停止播放
87播放下一首
88播放上一首
122移动光标到行首或列表顶部
123移动光标到行末或列表底部
126恢复播放
127暂停播放
164静音
176打开系统设置
187切换应用
207打开联系人
208打开日历
209打开音乐
210打开计算器
220降低屏幕亮度
221提高屏幕亮度
223系统休眠
224点亮屏幕
231打开语音助手
276如果没有 wakelock 则让系统休眠

附表二:常见广播意图 adb shell am broadcast -a android.intent.action.BOOT_COMPLETED

action触发时机
android.net.conn.CONNECTIVITY_CHANGE网络连接发生变化
android.intent.action.SCREEN_ON屏幕点亮
android.intent.action.SCREEN_OFF屏幕熄灭
android.intent.action.BATTERY_LOW电量低,会弹出电量低提示框
android.intent.action.BATTERY_OKAY电量恢复了
android.intent.action.BOOT_COMPLETED设备启动完毕
android.intent.action.DEVICE_STORAGE_LOW存储空间过低
android.intent.action.DEVICE_STORAGE_OK存储空间恢复
android.intent.action.PACKAGE_ADDED安装了新的应用
android.net.wifi.STATE_CHANGEWiFi 连接状态发生变化
android.net.wifi.WIFI_STATE_CHANGEDWiFi 状态变为启用/关闭/正在启动/正在关闭/未知
android.intent.action.BATTERY_CHANGED电池电量发生变化
android.intent.action.INPUT_METHOD_CHANGED系统输入法发生变化
android.intent.action.ACTION_POWER_CONNECTED外部电源连接
android.intent.action.ACTION_POWER_DISCONNECTED外部电源断开连接
android.intent.action.DREAMING_STARTED系统开始休眠
android.intent.action.DREAMING_STOPPED系统停止休眠
android.intent.action.WALLPAPER_CHANGED壁纸发生变化
android.intent.action.HEADSET_PLUG插入耳机
android.intent.action.MEDIA_UNMOUNTED卸载外部介质
android.intent.action.MEDIA_MOUNTED挂载外部介质
android.os.action.POWER_SAVE_MODE_CHANGED省电模式开启

附表三:系统属性

属性名含义
ro.build.version.sdkSDK 版本
ro.build.version.releaseAndroid 系统版本
ro.build.version.security_patchAndroid 安全补丁程序级别
ro.product.model型号
ro.product.brand品牌
ro.product.name设备名
ro.product.board处理器型号
ro.product.cpu.abilistCPU 支持的 abi 列表
persist.sys.isUsbOtgEnabled是否支持 OTG
dalvik.vm.heapsize每个应用程序的内存上限
ro.sf.lcd_density屏幕密度

附表四:实时资源占用情况表描述

列名含义
PID进程 ID
PR优先级
CPU%当前瞬间占用 CPU 百分比
S进程状态(R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)
#THR线程数
VSSVirtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSSResident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY调度策略优先级,SP_BACKGROUND/SPFOREGROUND
UID进程所有者的用户 ID
NAME进程名

3. Frida 环境

3.1 安装 Frida

github.com/frida/frida

# frida --version
15.2.2

3.2 查看手机系统架构

# adb shell getprop ro.product.cpu.abi
arm64-v8a

3.3 下载与安装的Frida版本对应 frida-server

下载测试机系统架构对应的 frida-server github.com/frida/frida…

frida-server-15.2.2-android-arm64.xz

3.4 讲 frida-server 推送到手机

# 7z x frida-server-15.2.2-android-arm64.tar.xz
# adb push  frida-server-15.2.2-android-arm64 /data/local/tmp/
# adb shell
# su
# cd /data/local/tmp
# chmod 777 frida-server-15.2.2-android-arm64

3.5 启动 frida-server

# adb shell
# su
# cd /data/local/tmp
# ./frida-server-15.2.2-android-arm64

4 Frida 脚本智能提示

基于Node和NPM环境。 安装 nodejs: nodejs.org/en/download… 推荐使用三方包管理器安装:github.com/nodesource/…

4.1 使用Frida示例项目

# git clone https://github.com/oleavr/frida-agent-example
# cd frida-agent-example
# npm install

使用vscode等IDE打开此工程,在agent文件夹下编写JavaScript,会有智能提示。

4.2 使用 npm 包 frida-gum

在系统里装上这个这个npm包(github.com/DefinitelyT… npm i -g @types/frida-gum

5 Frida 注入命令

5.1 USB 注入

查看进程 # frida-ps -U 注入脚本: # frida -U 进程名 -l 脚本路径.js 或直接对当前顶层应用注入脚本: # frida -UF -l 脚本路径.js 加上 -f 参数则将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App. # frida -U -f 进程名 -l hook.js --no-pause 是 Frida 的一个参数,用于在启动时禁止自动暂停目标进程。默认情况下,Frida 会在注入到目标进程后自动暂停该进程,以便用户可以执行进一步的操作,比如在运行时修改内存或进行调试。使用 --no-pause 参数可以禁止这种自动暂停行为,使目标进程继续正常运行。这在某些场景下可能很有用,例如当你只需要监视应用程序的行为而无需进行交互式的调试时。 # frida -U -f 进程名 -l hook.js --no-pause

5.2 网络模式注入

5.2.1 安装 Termux

可以在手机上执行linux命令 github.com/termux/term…

5.2.2 frida-server 使用网络模式进行监听

在手机上执行命令,打开 Termux:

# su
# cd /data/local/tmp/
# ./frida-server-15.2.2-android-arm64 -l 0.0.0.0:8888

或者直接在电脑上执行也行,网络模式一般用来分析那些禁止USB链接的应用。 查看 frida-service 监听的端口: # netstat -tulp | grep frida

tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      14541/frida-server-15.2.2-android-arm64

5.2.3 Frida网络注入

# frida -H 192.168.0.107:8888 -l test.js FridaTest 使用参数 -H 传入测试机的IP和端口,即可进行网络设备注入。

6 Frida脚本基础

test.js

function main(){
    Java.perform(function(){
        console.log("任何对APP的JAVA层的操作,都要写在这个匿名函数里面")

        // 获取对应类的JavaScript对象
        var Example = Java.use('com.example.Example') 

         // new 了一个 Java Stirng 字符串
        var JavaString = Java.use('java.lang.String').$new('122')

        // Hook 重载 类Example 的方法,在该方法调用的时候,会触发匿名函数,可不加overload,fraida报错信息会提示是否应该加上参数信息
        Example.方法名.overload('java.lang.String', 'java.lang.String').implementation = function(str, str2){

            // 传入参数,获取结果,可以自行修改传入参数
            var result = this.方法名(JavaString, JavaString.$new('123'))
            console.log('str, str2, result: ', str, str2, result)
            
            // 打印调用堆栈信息
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
            return result  // 返回结果,可以自行修改返回结果
        }


        // 主动调用类 Example 的静态方法
        var subResult = Example.静态方法名(JavaString.$new("6"), JavaString.$new("7"))

        // 主动调用 Example 的内部方法和变量
        Java.choose('com.example.Example', {
            onMatch:function(instance){
                console.log("变量值", instance.变量名.value)

                // 调用内部方法
                instance.方法名()
            },onComplete:function(){
                console.log("动态调用成功")
            }
        })

        console.log(subResult)
    })
}
setImmediate(main) // 注入后立即执行 main函数
// setTimeout(main, 100) // 注入后,100ms后再执行 main函数

7 RPC 及Python自动化

github.com/frida/frida…

在 js 脚本末尾添加 rpc.exports 用于导出函数,注意导出函数名必须小写:

rpc.exports = {
    callmain:main,
    getfun:getNumber
}

上面导出了2个函数,使得外部可以调用。 loader.py

import frida, sys, time

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

# 获得USB句柄,
devices = frida.get_usb_device()

# 可以添加多个网络设备
# devices = frida.get_usb_device().add_remote_device('192.168.0.107:8888')

# spawn 方式注入
# pid = devices.spawn(["进程名"])
# devices.resume(pid) # 唤起进程
# time.sleep(1) 
# process = devices.attach(pid)

# attach 方式进程注入
process = devices.attach('进程名')

# 加载脚本代码
with open('test.js') as f:
    jscode = f.read()
script = process.create_script(jscode)

# 注册信息打印函数
script.on('message', on_message)
script.load()

command = ""
while 1==1:
    command = input("\nEnter:\n1:exit\n2:call\n3:get\nchoice:")
    if command == "1":
        break
    elif command == "2":
        script.exports.callmain() # 调用脚本方法
    elif command == "3":
        script.exports.getfun()

执行该Python即可。

8 Objection 安装

Objection 可以快速搜索内存,搜索类,Hook方法,打印参数返回值调用栈,不写一行脚本就能进行APP分析。 github.com/sensepost/o…

9 Objection 常用命令

注入后,把要HOOK的功能都触发一次。

9.1 注入

# objection -g 包名 explore

9.2 列出内存所有的类

# android hooking list classes

9.3 搜索包含关键词的类

# android hooking search classes 关键词

9.4 搜索包含关键词的方法

# android hooking search methods 关键词

9.5 查看指定类的所有方法

# android hooking list class_methods 完整类名

9.6 列出注入进程的所有 Activity

# android hooking list activities

9.7 列出注入进程的所有 services

# android hooking list services

9.8 HOOK 指定方法

# android hooking watch class_method 方法名 --dump-args --dump-backtrace --dump-return

9.9 查看当前正在HOOK的作业信息

# jobs list

9.10 移除HOOK

# jobs kill 作业ID

9.11 启动Activity

# android intent launch_activity Activity名

9.12 搜索实例类似 Java.use

# android heap search instance 类名

9.13 执行无参实例方法

# android heap execute 实例Handle 方法名

9.14 执行有参实例方法

# android heap evaluate 实例Handle
clazz.方法名(参数...)

9.15 执行静态方法

TODO

9.16 使用插件Wallbreaker打印相应类的具体内容

需要使用Objection的插件 github.com/hluwa/wallb…,这个插件可以打印类的具体内容,比如静态成员实例成员的值,及所有函数。> 下载&安装:

# git clone https://github.com/hluwa/wallbreaker ~/.objection/plugins/Wallbreaker

注入时使用-P加载插件即可:

# objection  -g 包名 explore -P ~/.objection/plugins/

或者在注入后,再进行加载使用 :

# plugin load /root/.objection/plugins/Wallbreaker/
# plugin wallbreaker objectsearch java.util.HashMap
# plugin wallbreaker objectdump <object-handle>

示例,打印系统主要API接口:

# objection -g com.android.settings explore  
# plugin load /root/.objection/plugins/Wallbreaker/
# plugin wallbreaker classdump android.os.Build

9.17 ZenTracer

有界面的HOOK工具,更方便便捷。 github.com/hluwa/ZenTr…

10 Objection 网络模式

开启测试机的 frida-server 网络模式监听,详见5.2。 使用 -N 参数进行网络模式连接: # objection -N -h 192.168.0.107 -p 8888 -g 包名 explore

11 脱壳

11.1 FRIDA-DEXDump

这个工具可以从内存中搜索和dump符合条件的dex文件,当然VMP壳时脱不了的,只能脱1代壳。

11.1.1 安装插件

github.com/hluwa/frida… 这里作为Objection插件来使用:

# pip3 install frida-dexdump

11.1.2 脱壳&重打包

推荐使用 -d 模式,开启深度搜索模式。

# frida-dexdump -U -f 包名 -d

# frida-dexdump -FU -d

等待一段时间,完成脱壳,记得脱壳前先玩玩APP。

classes.dex
classes02.dex  
classes03.dex  
classes04.dex  
classes05.dex

可以使用gerp定位关键类所在的dex: grep -ril "关键词" ./*.dex

11.1.3 使用 apktool 重打包

github.com/iBotPeaches…

还是MT管理器方便,推荐使用

反编译APK但不反编译 dex, 加-s命令即可:

# apktool d 包名 -s

将反编译后的dex删除。 把脱壳后的class重命名并放入反编译后的文件里:

classes.dex
classes2.dex  
classes3.dex  
classes4.dex  
classes5.dex

重要的一点时,脱壳后的Applicaiton入口变了,需要找到脱壳后的Application,

# grep -ril "Application" ./*.dex
# grep -ril "android.app.Application" ./*.dex
# grep -ril "onCreate()" ./*.dex

找到关键 dex,反编译后查找 extends Application 即可找到真正的Applicaiton。 在清单文件中替换 application 节点的name即可。 重新编译:

# apktool b 反编译的文件夹

重新编译的APK在反编译目录下的dist文件夹。 创建签名文件:

# keytool -genkey -alias 别名 -keyalg RSA -validity 2000 -keystore test.jks

进行签名:

jarsigner -verbose -keystore 签名文件 -signedjar 签名后的.apk 等待签名的.apk 别名

11.2 FRIDA-FART

github.com/hanbingleng…

这个工具用于脱二代抽取型加固壳。

12 Charles 抓包 (ROOT环境)

12.1 下载并安装

www.charlesproxy.com/download/

12.2 手机安装证书

手机配置代理后打开网页chls.pro/ssl,下载并安装用户证书。

12.3 将用户证书变成系统证书

# adb shell
# su
# cd /data/misc/user/0/cacerts-added/  
# mount -o remount,rw /system
# cp * /etc/security/cacerts 
# chmod 777 /etc/security/cacerts/* 
# mount -o remount,ro /system
# reboot

12.4 配置SOCKS5

移除手动配置的代理,安装应用Postern并配置 SOCKS5协议,打开Charles的 SOCKS 协议,端口号要和 HTTP协议不一样。

13 HOOK关键词OkHttp抓包

13.1 OkHttp未被混淆

清空 Objection 日志: # rm ~/.objection/objection.log 注入APP: # objection -g 包名 explore 获取APP已经加载的所有类: # android hooking list classes 退出确保日志已经存储: # exit 复制日志文件到其他目录: # cp ~/.objection/objection.log ./ 过滤网络相关的类, 并保存到文件中, 可以先扫描再使用符号重定向: # cat objection.log | grep -i HttpURLConnection > HttpURLConnection.txt # cat objection.log | grep -i okhttp > okhttp.txt # cat objection.log | grep -i okhttp3 > okhttp3.txt 使用VIM再每行行首添加Hook命令: # :%s/^/android hooking watch class / 保存并退出: # :wq 注入并批量执行HOOK命令: # objection -g 包名 explore -c "./okhttp.txt" 查看相关函数, 退出 Objection,然后再对该函数进行HOOK: # android hooking watch class_method 函数名 --dump-args --dump-backtrace --dump-return 就能找到调用的具体位置, 再对调用函数进行HOOK。

13.2 OkHttp 被混淆了

github.com/siyujie/okh… 该项目使用了OkHttp3框架的特征去验证OkHtt3是否被使用了,而不是直接搜所关键词。 下载项目。 将 okhttpfind.dex 拷贝到 /data/local/tmp/ 目录下,并提升权限。

# adb push okhttpfind.dex /data/local/tmp/
# su
# chmod 777 /data/local/tmp/okhttpfind.dex 

执行命令启动frida -U -l okhttp_poker.js -f com.example.demo --no-pause 可追加 -o [output filepath]保存到文件。 或者打开应用到前台执行注入命令:frida -U -l okhttp_poker.js -F --no-pause。 执行find()方法。 如果提示方法未找到,则修改脚本直接执行 find()。 将 Find Complete 里的内容覆盖到脚本里的对应参数。 再次注入执行hold()方法,就会输出所有的请求信息。

13.3 Frida 动态注入 Dex

通过如下API即可动态加载DEX到内存中,使用我们自己的类和方法:

 Java.openClassFile("/data/local/tmp/HttpLoggingInter").load()

示例:OkHttp3 提供了日志打印库:

 implementation("com.squareup.okhttp3:logging-interceptor")

但分析的APP使用了OkHttp3,而且没有加日志打印库的时候,可以进行dex动态注入,自己写一个dex,来加载APP里没有的类库。 一般OkHttp3是这样加载日志打印拦截器的:

   HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
   interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
   new OkHttpClient.Builder()
                .addNetworkInterceptor(interceptor)
                .build();

写一个空Android项目,添加该类库,并运行起来。 在 app/build/intermediates/apk/debug 找到生成的APK,并解压缩。 通过下面的命令找到类库所在的dex文件。 # grep -ril "HttpLoggingInterceptor" ./*.dex 将其推送至/data/local/tmp目录下: adb push HttpLoggingInterceptor.dex /data/local/tmp/ 编写Frida脚本:

function main(){
    Java.perform(function(){

        // 加载我们的dex文件
        Java.openClassFile("/data/local/tmp/HttpLoggingInterceptor.dex").load();
        var HttpLoggingInterceptor = Java.use("okhttp3.logging.HttpLoggingInterceptor");
        var HttpLoggingInterceptorObj = HttpLoggingInterceptor.$new();
        var Level = Java.use("okhttp3.logging.HttpLoggingInterceptor$Level");
        // 设置枚举值BODY,另OkHttp3打印所有日志
        HttpLoggingInterceptorObj.setLevel(Level.BODY.value);
        var Builder = Java.use("okhttp3.OkHttpClient$Builder");
        Builder.build.implementation = function(){
            this.networkInterceptors().add(HttpLoggingInterceptorObj);
            console.log("add network interceptor");
            return this.build();
        };
        console.log("hook okhttp3 log");
    })
}
setImmediate(main) 

执行命令使用Spawned模式, 从APP启动的时候,就开始HOOK注入: # frida -U -f 进程名 -l 脚本.js --no-pause 这时APP进行网络请求时,即可看到所有信息。

14. HOOK 应用证书文件

保存证书到 sdcar里:

function hook_KeyStore_load() {
    Java.perform(function () {
        var myArray=new Array(1024);
        var i = 0
        for (i = 0; i < myArray.length; i++) {
            myArray[i]= 0x0;
         }
        var buffer = Java.array('byte',myArray);
        var StringClass = Java.use("java.lang.String");

        var KeyStore = Java.use("java.security.KeyStore");
        KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) {
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

            console.log("KeyStore.load1:", arg0);
            this.load(arg0);
        };
        KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

            console.log("KeyStore.load2: filename = ", arg0,',password = ', arg1 ? StringClass.$new(arg1) : null);
            if (arg0){
                var filename  = "/sdcard/Download/"+ String(arg0)
                var file =  Java.use("java.io.File").$new(filename);
                var out = Java.use("java.io.FileOutputStream").$new(file);
                var r;
                while( (r = arg0.read(buffer)) > 0){
                    out.write(buffer,0,r)
                }
                console.log('save_path = ',filename,", cert save success!")
                out.close()
            }
            this.load(arg0, arg1);
        };
        
        console.log("hook_KeyStore_load...");
    });
}
function main(){
    hook_KeyStore_load()
}
setImmediate(main);

导出证书后,可使用 KeyStore Explorer 工具转换证书格式,以便导入到 Charles中。 如果API使用了非标准HTTPS端口,还要手动配置该端口为HTTP数据包。

15 安卓应用层明文协议抓包通杀脚本 r0capture

github.com/r0ysue/r0ca…

16. Java类追踪脚本 r0tracer

  • 根据黑白名单批量追踪类的所有方法
  • 在命中方法后打印出该类或对象的所有域值、参数、调用栈和返回值
  • 极简的文本保存日志机制、易于搜索关键参数
  • 针对加壳应用找不到类时可以切换Classloader