Frida学习笔记

准备工作

使用kali
时间改成中国时区
dpkg-reconfigure tzdata
加入中文字体
apt upgrde
如果报错,解决方法: 下载最新key添加到keylist

wget -q -O - https://archive.kali.org/archive-key.asc | apt-key add

apt install xfonts-intl-chinese
apt install ttf-wqy-microhei
可以装一些好用的小工具;
htop
jnettop
安装py环境
pyenv

配置代理

ipconfig查看ip

nano /etc/proxychains.conf  
注释poxy—_dns  
最后改成sock5 ip最后改成1,端口1080

将机器装入adb,且为了方便加入环境变量

wget https://dl.google.com/android/repository/platform-tools-latest-linux.zip  
7z x platform-tools-latest-linux.zip  
root@kali:~/Downloads# cat ~/.bashrc |grep export
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
root@kali:~/Downloads# cd platform-tools/
root@kali:~/Downloads/platform-tools# cd ..
export PATH="/root//Downloads/platform-tools:$PATH"

Frida开发环境

目前最新正确的Frida环境搭建方法:

  1. git clone https://github.com/oleavr/frida-agent-example.git
  2. cd frida-agent-example/
  3. npm install
  4. 使用VSCode等IDE打开此工程,在agent下编写typescript,会有智能提示。
  5. npm run watch会监控代码修改自动编译生成js文件
  6. frida -U -f com.example.android --no-pause -l _agent.js

安装frida server

wget https://github.com/frida/frida/releases/download/12.8.0/frida-server-12.8.0-android-arm64.xz

特定版本

想要使用基于特定frida版本的objection,只需先安装好特定版本的frida和frida-tools(星球里搜“特定版本”有对应关系),再去objection的release里找那个日期之后一点点的版本。比如以frida 12.8.0版本举例:
pip install frida==12.8.0
pip install frida-tools==5.3.0
pip install objection==1.8.4
按照这个顺序,在装objection的时候,就会直接Requirement already satisfied,不会再去下载新的frida来安装了。

编写脚本注入

编写0530.js脚本

function main(){
    Java.perform(function(){
        console.log("Inside Frida Java Perform!")
    })
}


setImmediate(main)
root@kali:~/Downloads/frida-agent-example/agent# frida -U -n com.android.providers.calendar -l 0530.js
     ____
    / _  |   Frida 12.8.0 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://www.frida.re/docs/home/
Attaching...                                                            
Inside Frida Java Perform!
[Google Pixel XL::com.android.providers.calendar]->  

wifi adb

apkmirror wifi adb
可以设置打开远程adb
QtScrcpy可以投屏
然后通过wifi的frida就可以通过

手机端启动server  
./fridaserver -l 0.0.0.0:8888//0.0.0.0.0为任意ip  
案例如下:  
marlin:/data/local/tmp # ./fs1280arm64 -l 0.0.0.0:8888  

手机端就可以查看与注入

frida-ps -H 手机ip:端口  
案例如下:  
root@kali:~/Desktop# frida-ps H 192.168.15.98:8888
 PID  Name
----  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 905  (sd-pam)
 596  ModemManager
 562  NetworkManager
1109  Thunar
 617  Xorg
 559  accounts-daemon
2836  adb
1151  agent
 616  agetty
1056  at-spi-bus-launcher
1075  at-spi2-registryd
1355  bash
3210  bash
3714  bash
3922  bash
4927  bash
3631  code
3673  code


注入就是:  

frida H 192.168.15.98:8888 -f 包名 -l 脚本 --no-pause
-f为新开进程方式

Objection环境、自动化分析和插件

objection是一个基于Frida开发的命令行工具,它可以很方便的Hook Java函数和类,并输出参数,调用栈,返回值
关于objection版本安装尽量选择frida版本时间稍后的版本
手机端启动frida-server

root@kali:~# objection -N -h 192.168.15.98 -p 8888 -g com.android.settings explore
Using networked device @`192.168.15.98:8888`
Agent injected and responds ok!

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.8.4

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions
com.android.settings on (google: 8.1.0) [net] # frida                                                                     
--------------------  -----------
Frida Version         12.8.0
Process Architecture  arm64
Process Platform      linux
Debugger Attached     False
Script Runtime        DUK
Script Filename       /script1.js
Frida Heap Size       17.0 MiB
--------------------  -----------
com.android.settings on (google: 8.1.0) [net] # env                                                                       

Name                    Path
----------------------  -----------------------------------------------------------
cacheDirectory          /data/user_de/0/com.android.settings/cache
codeCacheDirectory      /data/user_de/0/com.android.settings/code_cache
externalCacheDirectory  /storage/emulated/0/Android/data/com.android.settings/cache
filesDirectory          /data/user_de/0/com.android.settings/files
obbDir                  /storage/emulated/0/Android/obb/com.android.settings
packageCodePath         /system/priv-app/SettingsGoogle/SettingsGoogle.apk

内存漫游、hook anywhere、抓包

参考连接:
https://www.anquanke.com/post/id/197657
Frida只是提供了各种API供我们调用,在此基础之上可以实现具体的功能,比如禁用证书绑定之类的脚本,就是使用Frida的各种API来组合编写而成。于是有大佬将各种常见、常用的功能整合进一个工具,供我们直接在命令行中使用,这个工具便是objection。
objection功能强大,命令众多,而且不用写一行代码,便可实现诸如内存搜索、类和模块搜索、方法hook打印参数返回值调用栈等常用功能,是一个非常方便的,逆向必备、内存漫游神器。

获取基本信息

首先介绍几个基本操作:
键入命令之后,回车执行;
help:不知道当前命令的效果是什么,在当前命令前加help比如,help env,回车之后会出现当前命令的解释信息;
按空格:不知道输入什么就按空格,会有提示出来,上下选择之后再按空格选中,又会有新的提示出来;
jobs:作业系统很好用,建议一定要掌握,可以同时运行多项(hook)作业;
我们以安卓内置应用“设置”为例,来示范一下基本的用法。
在手机上启动frida-server,并且点击启动“设置”图标,手机进入设置的界面,首先查看一下“设置”应用的包名。

内存漫游

提取内存信息
查看内存中加载的库
运行命令memory list modules

om.android.settings on (google: 8.1.0) [net] # memory list modules                                                       
Save the output by adding `--json modules.json` to this command
Name                                             Base          Size                  Path
-----------------------------------------------  ------------  --------------------  --------------------------------------------------------------------
app_process64                                    0x5d907e9000  32768 (32.0 KiB)      /system/bin/app_process64
libandroid_runtime.so                            0x786e55a000  1990656 (1.9 MiB)     /system/lib64/libandroid_runtime.so
libbinder.so                                     0x786c975000  557056 (544.0 KiB)    /system/lib64/libbinder.so
libcutils.so                                     0x786c526000  81920 (80.0 KiB)      /system/lib64/libcutils.so
libhwbinder.so                                   0x786f343000  163840 (160.0 KiB)    /system/lib64/libhwbinder.so
liblog.so                                        0x786e4da000  102400 (100.0 KiB)    /system/lib64/liblog.so
libnativeloader.so                               0x786cfa2000  28672 (28.0 KiB)      /system/lib64/libnativeloader.so
libutils.so                                      0x786bf5d000  131072 (128.0 KiB)    /system/lib64/libutils.so
libwilhelm.so                                    0x786f203000  274432 (268.0 KiB)    /system/lib64/libwilhelm.so
libc++.so                                        0x786c58a000  983040 (960.0 KiB)    /system/lib64/libc++.so
libc.so                                          0x786ed5f000  892928 (872.0 KiB)    /system/lib64/libc.so
libm.so                                          0x786eb42000  233472 (228.0 KiB)    /system/lib64/libm.so
libdl.so                                         0x786deaf000  20480 (20.0 KiB)      /system/lib64/libdl.so
libmemtrack.so                                   0x786c048000  282624 (276.0 KiB)    /system/lib64/libmemtrack.so
libandroidfw.so                                  0x786dcc4000  339968 (332.0 KiB)    /system/lib64/libandroidfw.so
libappfuse.so                                    0x786d12f000  57344 (56.0 KiB)      /system/lib64/libappfuse.so
libbase.so                                       0x786cf2b000  73728 (72.0 KiB)      /system/lib64/libbase.so
libcrypto.so                                     0x786d14f000  1155072 (1.1 MiB)     /system/lib64/libcrypto.so
libnativehelper.so                               0x786ddd2000  36864 (36.0 KiB)      /system/lib64/libnativehelper.so
libdebuggerd_client.so                           0x786c739000  28672 (28.0 KiB)      /system/lib64/libdebuggerd_client.so
libui.so                                         0x786eb87000  147456 (144.0 KiB)    /system/lib64/libui.so
libgraphicsenv.so                                0x786ed1a000  16384 (16.0 KiB)      /system/lib64/libgraphicsenv.so
libgui.so                                        0x786cc43000  622592 (608.0 KiB)    /system/lib64/libgui.so
libsensor.so                                     0x786cb85000  94208 (92.0 KiB)      /system/lib64/libsensor.so
libinput.so                                      0x786d00b000  184320 (180.0 KiB)    /system/lib64/libinput.so
libcamera_client.so                              0x786dd52000  327680 (320.0 KiB)    /system/lib64/libcamera_client.so
libcamera_metadata.so                            0x786f45a000  45056 (44.0 KiB)      /system/lib64/libcamera_metadata.so
libskia.so                                       0x786d3cf000  8298496 (7.9 MiB)     /system/lib64/libskia.so
libsqlite.so                                     0x786ca02000  1146880 (1.1 MiB)     /system/lib64/libsqlite.so
libEGL.so                                        0x786e50e000  192512 (188.0 KiB)    /system/lib64/libEGL.so
libGLESv1_CM.so               

如果觉得信息太多,可以:
cat .objection/objection.log
查看库的导出函数
运行命令memory list exports libssl.so,效果如下

function  _ZNSt3__16__treeINS_12__value_typeIN7android8String16ElEENS_19__map_value_compareIS3_S4_NS_4lessIS3_EELb1EEENS_9allocatorIS4_EEE25__emplace_unique_key_argsIS3_JRKNS_21piecewise_construct_tENS_5tupleIJRKS3_EEENSG_IJEEEEEENS_4pairINS_15__tree_iteratorIS4_PNS_11__tree_nodeIS4_PvEElEEbEERKT_DpOT0_                                                      0x786c9d44e8
function  _ZNK7android6VectorINS_12ProcessState12handle_entryEE12do_constructEPvm                                                                                                                                                                                                                                                                                     0x786c9add70
function  _ZNK7android6VectorIPNS_7BBinderEE8do_splatEPvPKvm                                                                                                                                                                                                                                                                                                          0x786c9b6cc8
function  _ZN7android6binder5ValueC2ERKS1_                                                                                                                                                                                                                                                                                                                            0x786c9d868c
variable  _ZZ17internal_type_ptrIiEPKvvE6marker                                                                                                                                                                                                                                                                                                                       0x786c9fc398

当结果太多,终端无法全部显示的时候,可以将结果导出到文件中,然后使用其他软件查看内容

# memory list exports libart.so --json /root/libart.json  
Writing exports as json to /root/libart.json...
Wrote exports to: /root/libart.json  

提取整个(或部分)内存
命令是memory dump all from_base。
搜索整个内存
命令是memory search --string --offsets-only
在堆上搜索实例
我们查看AOSP源码关于设置里显示系统设置的部分,发现存在着DisplaySettings类,可以在堆上搜索是否存在着该类的实例。首先在手机上点击进入“显示”设置,然后运行以下命令,并得到相应的实例地址:

# android heap search instances com.android.settings.DisplaySettings                                                                                                                             
Using exsiting matches for com.android.settings.DisplaySettings. Use --fresh flag for new instances.
Handle    Class                                 toString()
--------  ------------------------------------  -----------------------------------------
0x252a    com.android.settings.DisplaySettings  DisplaySettings{69d91ee #0 id=0x7f0a0231}

调用实例的方法
查看源码得知com.android.settings.DisplaySettings类有着getPreferenceScreenResId()方法(后文也会介绍在objection中直接打印类的所有方法的命令),这样就可以直接调用该实例的getPreferenceScreenResId()方法,用excute命令。

# android heap execute 0x2526 getPreferenceScreenResId                  
Handle 0x2526 is to class com.android.settings.DisplaySettings
Executing method: getPreferenceScreenResId()
2132082764

可见结果被直接打印了出来。
在实例上执行js代码
也可以在找到的实例上直接编写js脚本,输入android heap evaluate 0x2526命令后,会进入一个迷你编辑器环境,输入console.log(“evaluate result:”+clazz.getPreferenceScreenResId())这串脚本,按ESC退出编辑器,然后按回车,即会开始执行这串脚本,输出结果。

# android heap evaluate 0x2526                                          
(The handle at `0x2526` will be available as the `clazz` variable.)

console.log("evaluate result:"+clazz.getPreferenceScreenResId()) 

JavaScript capture complete. Evaluating...
Handle 0x2526 is to class com.android.settings.DisplaySettings
evaluate result:2132082764

这个功能其实非常厉害,可以即时编写、出结果、即时调试自己的代码,不用再编写→注入→操作→看结果→再调整,而是直接出结果。
直接启动activity
直接上代码,想要进入显示设置,可以在任意界面直接运行以下代码进入显示设置:
可以使用android hooking list命令来查看当前可用的activities,然后使用上述命令进行调起。

com.android.settings on (google: 8.1.0) [net] # android hooking list activities                                           
com.android.settings.ActivityPicker
com.android.settings.AirplaneModeVoiceActivity
com.android.settings.AllowBindAppWidgetActivity
com.android.settings.AppWidgetPickActivity
com.android.settings.BandMode
com.android.settings.ConfirmDeviceCredentialActivity
com.android.settings.CreateShortcut
com.android.settings.CredentialStorage
com.android.settings.CryptKeeper$FadeToBlack

om.android.settings on (google: 8.1.0) [net] # android intent launch_activity com.android.settings.wifi.WifiSettings     
(agent) Starting activity com.android.settings.wifi.WifiSettings...
(agent) Activity successfully asked to start.

Frida学习笔记_第1张图片
直接启动service
也可以先使用android hooking list services查看可供开启的服务,然后使用android intent launch_service com.android.settings.bluetooth.BluetoothPairingService命令来开启服务。

Frida hook anywhere

在学习Frida的时候,遇到的第一个问题就是,无法找到正确的类及子类,无法定位到实现功能的准确的方法,无法正确的构造参数、继而进入正确的重载,这时候可以使用Frida进行动态调试,来确定以上具体的名称和写法,最后写出正确的hook代码。

objection(内存漫游)

列出内存中所有的类

# android hooking list classes

sun.util.logging.LoggingSupport
sun.util.logging.LoggingSupport$1
sun.util.logging.LoggingSupport$2
sun.util.logging.PlatformLogger
sun.util.logging.PlatformLogger$1
sun.util.logging.PlatformLogger$JavaLoggerProxy
sun.util.logging.PlatformLogger$Level
sun.util.logging.PlatformLogger$LoggerProxy
void

Found 11885 classes

内存中搜索所有的类
在内存中所有已加载的类中搜索包含特定关键词的类。

# android hooking search classes display                                                                                                                                                         
[Landroid.hardware.display.WifiDisplay;
[Landroid.icu.impl.ICUCurrencyDisplayInfoProvider$ICUCurrencyDisplayInfo$CurrencySink$EntrypointTable;
[Landroid.icu.impl.LocaleDisplayNamesImpl$CapitalizationContextUsage;
[Landroid.icu.impl.LocaleDisplayNamesImpl$DataTableType;
[Landroid.icu.number.NumberFormatter$DecimalSeparatorDisplay;
[Landroid.icu.number.NumberFormatter$SignDisplay;
[Landroid.icu.text.DisplayContext$Type;
[Landroid.icu.text.DisplayContext;
[Landroid.icu.text.LocaleDisplayNames$DialectHandling;
[Landroid.view.Display$Mode;
[Landroid.view.Display;
android.app.Vr2dDisplayProperties
android.hardware.display.AmbientBrightnessDayStats
android.hardware.display.AmbientBrightnessDayStats$1
android.hardware.display.BrightnessChangeEvent
com.android.settings.wfd.WifiDisplaySettings$SummaryProvider
com.android.settings.wfd.WifiDisplaySettings$SummaryProvider$1
com.android.settingslib.display.BrightnessUtils
com.android.settingslib.display.DisplayDensityUtils
com.google.android.gles_jni.EGLDisplayImpl
javax.microedition.khronos.egl.EGLDisplay

Found 144 classes

内存中搜索所有的方法
在内存中所有已加载的类的方法中搜索包含特定关键词的方法,上文中可以发现,内存中已加载的类就已经高达11885个了,那么他们的方法一定是类的个数的数倍,整个过程会相当庞大和耗时.

android hooking search methods display
当搜索到了比较关心的类之后,就可以直接查看它有哪些方法,比如我们想要查看com.android.settings.DisplaySettings类有哪些方法:

# android hooking list class_methods com.android.settings.DisplaySettings                                                                                                                        
private static java.util.List com.android.settings.DisplaySettings.buildPreferenceControllers(android.content.Context,com.android.settingslib.core.lifecycle.Lifecycle)
protected int com.android.settings.DisplaySettings.getPreferenceScreenResId()
protected java.lang.String com.android.settings.DisplaySettings.getLogTag()
protected java.util.List com.android.settings.DisplaySettings.createPreferenceControllers(android.content.Context)
public int com.android.settings.DisplaySettings.getHelpResource()
public int com.android.settings.DisplaySettings.getMetricsCategory()
static java.util.List com.android.settings.DisplaySettings.access$000(android.content.Context,com.android.settingslib.core.lifecycle.Lifecycle)

Found 7 method(s)

列出的方法与源码相比对之后,发现是一模一样的。

直接生成hook代码
上文中在列出类的方法时,还直接把参数也提供了,也就是说我们可以直接动手写hook了,既然上述写hook的要素已经全部都有了,objection这个“自动化”工具,当然可以直接生成代码。

# android hooking generate  simple  com.android.settings.DisplaySettings                                                                                                                         

Java.perform(function() {
    var clazz = Java.use('com.android.settings.DisplaySettings');
    clazz.getHelpResource.implementation = function() {

        //

        return clazz.getHelpResource.apply(this, arguments);
    }
});


Java.perform(function() {
    var clazz = Java.use('com.android.settings.DisplaySettings');
    clazz.getLogTag.implementation = function() {

        //

        return clazz.getLogTag.apply(this, arguments);
    }
});


Java.perform(function() {
    var clazz = Java.use('com.android.settings.DisplaySettings');
    clazz.getPreferenceScreenResId.implementation = function() {

        //

        return clazz.getPreferenceScreenResId.apply(this, arguments);
    }
});

objection(hook)

上述操作均是基于在内存中直接枚举搜索,已经可以获取到大量有用的静态信息,以下几个方法,可以获取到执行时动态的信息,当然、同样地,不用写一行代码。
hook类的所有方法
我们以手机连接蓝牙耳机播放音乐为例为例,看看手机蓝牙接口的动态信息。首先我们将手机连接上我的蓝牙耳机——一加蓝牙耳机OnePlus Bullets Wireless 2,并可以正常播放音乐;然后我们按照上文的方法,搜索一下与蓝牙相关的类,搜到一个高度可疑的类:android.bluetooth.BluetoothDevice。运行以下命令,hook这个类:

# android hooking watch class android.bluetooth.BluetoothDevice
om.android.settings on (google: 8.1.0) [net] # android hooking search classes bluetooth                                  
android.bluetooth.BluetoothA2dp
android.bluetooth.BluetoothA2dp$1
android.bluetooth.BluetoothA2dp$2
android.bluetooth.BluetoothAdapter
android.bluetooth.BluetoothAdapter$1
android.bluetooth.BluetoothDevice$1
android.bluetooth.BluetoothDevice$2
android.bluetooth.BluetoothHeadset
android.bluetooth.BluetoothHeadset$1
android.bluetooth.BluetoothHeadset$2
android.bluetooth.BluetoothHeadset$3
android.bluetooth.BluetoothManager
android.bluetooth.BluetoothProfile
android.bluetooth.BluetoothProfile$ServiceListener
android.bluetooth.IBluetooth
android.bluetooth.IBluetooth$Stub
android.bluetooth.IBluetooth$Stub$Proxy
android.bluetooth.IBluetoothA2dp
android.bluetooth.IBluetoothA2dp$Stub
android.bluetooth.IBluetoothA2dp$Stub$Proxy
android.bluetooth.IBluetoothGatt
android.bluetooth.IBluetoothGatt$Stub
android.bluetooth.IBluetoothHeadset
android.bluetooth.IBluetoothHeadset$Stub
android.bluetooth.IBluetoothHeadset$Stub$Proxy
android.bluetooth.IBluetoothManager
android.bluetooth.IBluetoothManager$Stub
android.bluetooth.IBluetoothManager$Stub$Proxy
android.bluetooth.IBluetoothManagerCallback
android.bluetooth.IBluetoothManagerCallback$Stub
android.bluetooth.IBluetoothProfileServiceConnection
android.bluetooth.IBluetoothProfileServiceConnection$Stub
android.bluetooth.IBluetoothStateChangeCallback
android.bluetooth.IBluetoothStateChangeCallback$Stub
com.android.settings.Settings$BluetoothSettingsActivity

com.android.settings on (google: 8.1.0) [net] # android hooking watch class android.bluetooth.BluetoothDevice             
(agent) Hooking android.bluetooth.BluetoothDevice.-get0()
(agent) Hooking android.bluetooth.BluetoothDevice.-set0(android.bluetooth.IBluetooth)
(agent) Hooking android.bluetooth.BluetoothDevice.convertPinToBytes(java.lang.String)
(agent) Hooking android.bluetooth.BluetoothDevice.getService()
(agent) Hooking android.bluetooth.BluetoothDevice.cancelBondProcess()
(agent) Hooking android.bluetooth.BluetoothDevice.cancelPairingUserInput()
(agent) Hooking android.bluetooth.BluetoothDevice.connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)
(agent) Hooking android.bluetooth.BluetoothDevice.connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int)
(agent) Hooking android.bluetooth.BluetoothDevice.connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int)
(agent) Hooking android.bluetooth.BluetoothDevice.connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler)
(agent) Hooking android.bluetooth.BluetoothDevice.connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, boolean, int, android.os.Handler)
(agent) Hooking android.bluetooth.BluetoothDevice.createBond()
(agent) Hooking android.bluetooth.BluetoothDevice.createBond(int)
(agent) Hooking android.bluetooth.BluetoothDevice.createBondOutOfBand(int, android.bluetooth.OobData)
(agent) Hooking android.bluetooth.BluetoothDevice.createInsecureL2capSocket(int)
(agent) Hooking android.bluetooth.BluetoothDevice.createInsecureRfcommSocket(int)
(agent) Hooking android.bluetooth.BluetoothDevice.createInsecureRfcommSocketToServiceRecord(java.util.UUID)
(agent) Hooking android.bluetooth.BluetoothDevice.createL2capSocket(int)
(agent) Hooking android.bluetooth.BluetoothDevice.createRfcommSocket(int)
(agent) Hooking android.bluetooth.BluetoothDevice.createRfcommSocketToServiceRecord(java.util.UUID)
(agent) Hooking android.bluetooth.BluetoothDevice.createScoSocket()
(agent) Hooking android.bluetooth.BluetoothDevice.describeContents()
(agent) Hooking android.bluetooth.BluetoothDevice.equals(java.lang.Object)
(agent) Hooking android.bluetooth.BluetoothDevice.fetchUuidsWithSdp()
(agent) Hooking android.bluetooth.BluetoothDevice.getAddress()
(agent) Hooking android.bluetooth.BluetoothDevice.getAlias()
(agent) Hooking android.bluetooth.BluetoothDevice.getAliasName()
(agent) Hooking android.bluetooth.BluetoothDevice.getBatteryLevel()
(agent) Hooking android.bluetooth.BluetoothDevice.getBluetoothClass()
(agent) Hooking android.bluetooth.BluetoothDevice.getBondState()
(agent) Hooking android.bluetooth.BluetoothDevice.getMessageAccessPermission()
(agent) Hooking android.bluetooth.BluetoothDevice.getName()
(agent) Hooking android.bluetooth.BluetoothDevice.getPhonebookAccessPermission()
(agent) Hooking android.bluetooth.BluetoothDevice.getSimAccessPermission()
(agent) Hooking android.bluetooth.BluetoothDevice.getType()
(agent) Hooking android.bluetooth.BluetoothDevice.getUuids()
(agent) Hooking android.bluetooth.BluetoothDevice.hashCode()
(agent) Hooking android.bluetooth.BluetoothDevice.isBluetoothDock()
(agent) Hooking android.bluetooth.BluetoothDevice.isBluetoothEnabled()
(agent) Hooking android.bluetooth.BluetoothDevice.isBondingInitiatedLocally()
(agent) Hooking android.bluetooth.BluetoothDevice.isConnected()
(agent) Hooking android.bluetooth.BluetoothDevice.isEncrypted()
(agent) Hooking android.bluetooth.BluetoothDevice.removeBond()
(agent) Hooking android.bluetooth.BluetoothDevice.sdpSearch(android.os.ParcelUuid)
(agent) Hooking android.bluetooth.BluetoothDevice.setAlias(java.lang.String)
(agent) Hooking android.bluetooth.BluetoothDevice.setDeviceOutOfBandData([B, [B)
(agent) Hooking android.bluetooth.BluetoothDevice.setMessageAccessPermission(int)
(agent) Hooking android.bluetooth.BluetoothDevice.setPairingConfirmation(boolean)
(agent) Hooking android.bluetooth.BluetoothDevice.setPasskey(int)
(agent) Hooking android.bluetooth.BluetoothDevice.setPhonebookAccessPermission(int)
(agent) Hooking android.bluetooth.BluetoothDevice.setPin([B)
(agent) Hooking android.bluetooth.BluetoothDevice.setRemoteOutOfBandData()
(agent) Hooking android.bluetooth.BluetoothDevice.setSimAccessPermission(int)
(agent) Hooking android.bluetooth.BluetoothDevice.toString()
(agent) Hooking android.bluetooth.BluetoothDevice.writeToParcel(android.os.Parcel, int)
(agent) Registering job pld7w7pk25n. Type: watch-class for: android.bluetooth.BluetoothDevice
com.android.settings on (google: 8.1.0) [net] # jobs list                                                                 
Job ID         Hooks  Type
-----------  -------  --------------------------------------------------
pld7w7pk25n       55  watch-class for: android.bluetooth.BluetoothDevice
com.android.settings on (google: 8.1.0) [net] #  

使用jobs list命令可以看到objection为我们创建的Hooks数为57,也就是将android.bluetooth.BluetoothDevice类下的所有方法都hook了。
这时候我们在设置→声音→媒体播放到上进行操作,在蓝牙耳机与“此设备”之间切换时,会命中这些hook之后,此时objection就会将方法打印出来,会将类似这样的信息“吐”出来:

com.android.settings on (google: 9) [usb] # (agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.isConnected()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getAliasName()                                                                                                                                                               
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getAlias()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getName()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.equals(java.lang.Object)
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.isConnected()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getService()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getAliasName()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getAlias()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.getName()
(agent) [h0u5g7uclo] Called android.bluetooth.BluetoothDevice.equals(java.lang.Object)

可以看到我们的切换操作,调用到了android.bluetooth.BluetoothDevice类中的多个方法。

hook方法的参数、返回值和调用栈
在这些方法中,我们对哪些方法感兴趣,就可以查看哪些个方法的参数、返回值和调用栈,比如想看getName()方法,则运行以下命令:

android hooking watch class_method android.bluetooth.BluetoothDevice.getName --dump-args --dump-return --dump-backtrace

注意最后加上的三个选项–dump-args --dump-return --dump-backtrace,为我们成功打印出来了我们想要看的信息,其实返回值Return Value就是getName()方法的返回值,我的蓝牙耳机的型号名字OnePlus Bullets Wireless 2;从调用栈可以反查如何一步一步调用到getName()这个方法的;虽然这个方法没有参数,大家可以再找个有参数的试一下。

hook方法的所有重载
objection的help中指出,在hook给出的单个方法的时候,会hook它的所有重载。

# help android hooking watch class_method                                                                                                                                                        
Command: android hooking watch class_method

Usage: android hooking watch class_method  
       (optional: --dump-args) (optional: --dump-backtrace)                                                                                                                                                                                
       (optional: --dump-return)                                                                                                                                                                                                           

Hooks a specified class method and reports on invocations, together with                                                                                                                                                                   
the number of arguments that method was called with. This command will                                                                                                                                                                     
also hook all of the methods available overloads unless a specific                                                                                                                                                                         
overload is specified.                                                                                                                                                                                                                     

If the --include-backtrace flag is provided, a full stack trace that                                                                                                                                                                       
lead to the methods invocation will also be dumped. This would aid in                                                                                                                                                                      
discovering who called the original method.                                                                                                                                                                                                

Examples:                                                                                                                                                                                                                                  
   android hooking watch class_method com.example.test.login                                                                                                                                                                               
   android hooking watch class_method com.example.test.helper.executeQuery                                                                                                                                                                 
   android hooking watch class_method com.example.test.helper.executeQuery "java.lang.String,java.lang.String"                                                                                                                             
   android hooking watch class_method com.example.test.helper.executeQuery --dump-backtrace                                                                                                                                                
   android hooking watch class_method com.example.test.login --dump-args --dump-return

那我们可以用File类的构造器来试一下效果。

# android hooking watch class_method java.io.File.$init --dump-args

可以看到objection为我们hook了File构造器的所有重载,一共是6个。在设置界面随意进出几个子设置界面,可以看到命中很多次该方法的不同重载,每次参数的值也都不同,
插件使用,直接加载路径

com.android.settings on (google: 8.1.0) [net] # plugin load /root/Desktop/Wallbre
aker 

就可以使用插件,查看查看各种变量及信息。

com.android.settings on (google: 8.1.0) [net] # plugin wallbreaker classdump --f
ullname android.bluetooth.BluetoothDevice                                       

package android.bluetooth

class BluetoothDevice {

	/* static fields */
	static int ACCESS_ALLOWED; => 1
	static int ACCESS_REJECTED; => 2
	static int ACCESS_UNKNOWN; => 0
	static java.lang.String ACTION_ACL_CONNECTED; => android.bluetooth.device.action.ACL_CONNECTED
	static java.lang.String ACTION_ACL_DISCONNECTED; => android.bluetooth.device.action.ACL_DISCONNECTED
	static java.lang.String ACTION_ACL_DISCONNECT_REQUESTED; => android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED
	static java.lang.String ACTION_ALIAS_CHANGED; => android.bluetooth.device.action.ALIAS_CHANGED
	static java.lang.String ACTION_BATTERY_LEVEL_CHANGED; => android.bluetooth.device.action.BATTERY_LEVEL_CHANGED
	static java.lang.String ACTION_BOND_STATE_CHANGED; => android.bluetooth.device.action.BOND_STATE_CHANGED
	static java.lang.String ACTION_CLASS_CHANGED; => android.bluetooth.device.action.CLASS_CHANGED
	static java.lang.String ACTION_CONNECTION_ACCESS_CANCEL; => android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL
	static java.lang.String ACTION_CONNECTION_ACCESS_REPLY; => android.bluetooth.device.action.CONNECTION_ACCESS_REPLY
	static java.lang.String ACTION_CONNECTION_ACCESS_REQUEST; => android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST
	static java.lang.String ACTION_DISAPPEARED; => android.bluetooth.device.action.DISAPPEARED
	static java.lang.String ACTION_FOUND; => android.bluetooth.device.action.FOUND
	static java.lang.String ACTION_MAS_INSTANCE; => android.bluetooth.device.action.MAS_INSTANCE
	static java.lang.String ACTION_NAME_CHANGED; => android.bluetooth.device.action.NAME_CHANGED
	static java.lang.String ACTION_NAME_FAILED; => android.bluetooth.device.action.NAME_FAILED
	static java.lang.String ACTION_PAIRING_CANCEL; => android.bluetooth.device.action.PAIRING_CANCEL
	static java.lang.String ACTION_PAIRING_REQUEST; => android.bluetooth.device.action.PAIRING_REQUEST
	static java.lang.String ACTION_SDP_RECORD; => android.bluetooth.device.action.SDP_RECORD
	static java.lang.String ACTION_UUID; => android.bluetooth.device.action.UUID
	static int BATTERY_LEVEL_UNKNOWN; => -1
	static int BOND_BONDED; => 12
	static int BOND_BONDING; => 11
	static int BOND_NONE; => 10
	static int BOND_SUCCESS; => 0
	static int CONNECTION_ACCESS_NO; => 2
	static int CONNECTION_ACCESS_YES; => 1
	static int CONNECTION_STATE_CONNECTED; => 1
	static int CONNECTION_STATE_DISCONNECTED; => 0
	static int CONNECTION_STATE_ENCRYPTED_BREDR; => 2
	static int CONNECTION_STATE_ENCRYPTED_LE; => 4
	static android.os.Parcelable$Creator CREATOR; => [0x2272]: android.bluetooth.BluetoothDevice$2@7a81e5
	static boolean DBG; => false
	static int DEVICE_TYPE_CLASSIC; => 1
	static int DEVICE_TYPE_DUAL; => 3
	static int DEVICE_TYPE_LE; => 2
	static int DEVICE_TYPE_UNKNOWN; => 0
	static int ERROR; => -2147483648
	static java.lang.String EXTRA_ACCESS_REQUEST_TYPE; => android.bluetooth.device.extra.ACCESS_REQUEST_TYPE
	static java.lang.String EXTRA_ALWAYS_ALLOWED; => android.bluetooth.device.extra.ALWAYS_ALLOWED
	static java.lang.String EXTRA_BATTERY_LEVEL; => android.bluetooth.device.extra.BATTERY_LEVEL
	static java.lang.String EXTRA_BOND_STATE; => android.bluetooth.device.extra.BOND_STATE
	static java.lang.String EXTRA_CLASS; => android.bluetooth.device.extra.CLASS
	static java.lang.String EXTRA_CLASS_NAME; => android.bluetooth.device.extra.CLASS_NAME
	static java.lang.String EXTRA_CONNECTION_ACCESS_RESULT; => android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT
	static java.lang.String EXTRA_DEVICE; => android.bluetooth.device.extra.DEVICE
	static java.lang.String EXTRA_MAS_INSTANCE; => android.bluetooth.device.extra.MAS_INSTANCE
	static java.lang.String EXTRA_NAME; => android.bluetooth.device.extra.NAME
	static java.lang.String EXTRA_PACKAGE_NAME; => android.bluetooth.device.extra.PACKAGE_NAME
	static java.lang.String EXTRA_PAIRING_KEY; => android.bluetooth.device.extra.PAIRING_KEY
	static java.lang.String EXTRA_PAIRING_VARIANT; => android.bluetooth.device.extra.PAIRING_VARIANT
	static java.lang.String EXTRA_PREVIOUS_BOND_STATE; => android.bluetooth.device.extra.PREVIOUS_BOND_STATE
	static java.lang.String EXTRA_REASON; => android.bluetooth.device.extra.REASON
	static java.lang.String EXTRA_RSSI; => android.bluetooth.device.extra.RSSI
	static java.lang.String EXTRA_SDP_RECORD; => android.bluetooth.device.extra.SDP_RECORD
	static java.lang.String EXTRA_SDP_SEARCH_STATUS; => android.bluetooth.device.extra.SDP_SEARCH_STATUS
	static java.lang.String EXTRA_UUID; => android.bluetooth.device.extra.UUID
	static int PAIRING_VARIANT_CONSENT; => 3
	static int PAIRING_VARIANT_DISPLAY_PASSKEY; => 4
	static int PAIRING_VARIANT_DISPLAY_PIN; => 5
	static int PAIRING_VARIANT_OOB_CONSENT; => 6
	static int PAIRING_VARIANT_PASSKEY; => 1
	static int PAIRING_VARIANT_PASSKEY_CONFIRMATION; => 2
	static int PAIRING_VARIANT_PIN; => 0
	static int PAIRING_VARIANT_PIN_16_DIGITS; => 7
	static int PHY_LE_1M; => 1
	static int PHY_LE_1M_MASK; => 1
	static int PHY_LE_2M; => 2
	static int PHY_LE_2M_MASK; => 2
	static int PHY_LE_CODED; => 3
	static int PHY_LE_CODED_MASK; => 4
	static int PHY_OPTION_NO_PREFERRED; => 0
	static int PHY_OPTION_S2; => 1
	static int PHY_OPTION_S8; => 2
	static int REQUEST_TYPE_MESSAGE_ACCESS; => 3
	static int REQUEST_TYPE_PHONEBOOK_ACCESS; => 2
	static int REQUEST_TYPE_PROFILE_CONNECTION; => 1
	static int REQUEST_TYPE_SIM_ACCESS; => 4
	static java.lang.String TAG; => BluetoothDevice
	static int TRANSPORT_AUTO; => 0
	static int TRANSPORT_BREDR; => 1
	static int TRANSPORT_LE; => 2
	static int UNBOND_REASON_AUTH_CANCELED; => 3
	static int UNBOND_REASON_AUTH_FAILED; => 1
	static int UNBOND_REASON_AUTH_REJECTED; => 2
	static int UNBOND_REASON_AUTH_TIMEOUT; => 6
	static int UNBOND_REASON_DISCOVERY_IN_PROGRESS; => 5
	static int UNBOND_REASON_REMOTE_AUTH_CANCELED; => 8
	static int UNBOND_REASON_REMOTE_DEVICE_DOWN; => 4
	static int UNBOND_REASON_REMOVED; => 9
	static int UNBOND_REASON_REPEATED_ATTEMPTS; => 7
	static android.bluetooth.IBluetoothManagerCallback mStateChangeCallback; => [0x2c1a]: android.bluetooth.BluetoothDevice$1@3db44ba
	static android.bluetooth.IBluetooth sService; => null

	/* instance fields */
	java.lang.String mAddress;

	/* constructor methods */
	android.bluetooth.BluetoothDevice(java.lang.String);

	/* static methods */
	static android.bluetooth.IBluetooth -get0();
	static android.bluetooth.IBluetooth -set0(android.bluetooth.IBluetooth);
	static [B convertPinToBytes(java.lang.String);
	static android.bluetooth.IBluetooth getService();

	/* instance methods */
	boolean cancelBondProcess();
	boolean cancelPairingUserInput();
	android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
	android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
	android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
	android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
	android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, boolean, int, android.os.Handler);
	boolean createBond();
	boolean createBond(int);
	boolean createBondOutOfBand(int, android.bluetooth.OobData);
	android.bluetooth.BluetoothSocket createInsecureL2capSocket(int);
	android.bluetooth.BluetoothSocket createInsecureRfcommSocket(int);
	android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID);
	android.bluetooth.BluetoothSocket createL2capSocket(int);
	android.bluetooth.BluetoothSocket createRfcommSocket(int);
	android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID);
	android.bluetooth.BluetoothSocket createScoSocket();
	int describeContents();
	boolean equals(java.lang.Object);
	boolean fetchUuidsWithSdp();
	java.lang.String getAddress();
	java.lang.String getAlias();
	java.lang.String getAliasName();
	int getBatteryLevel();
	android.bluetooth.BluetoothClass getBluetoothClass();
	int getBondState();
	int getMessageAccessPermission();
	java.lang.String getName();
	int getPhonebookAccessPermission();
	int getSimAccessPermission();
	int getType();
	[Landroid.os.ParcelUuid; getUuids();
	int hashCode();
	boolean isBluetoothDock();
	boolean isBluetoothEnabled();
	boolean isBondingInitiatedLocally();
	boolean isConnected();
	boolean isEncrypted();
	boolean removeBond();
	boolean sdpSearch(android.os.ParcelUuid);
	boolean setAlias(java.lang.String);
	boolean setDeviceOutOfBandData(B[], B[]);
	boolean setMessageAccessPermission(int);
	boolean setPairingConfirmation(boolean);
	boolean setPasskey(int);
	boolean setPhonebookAccessPermission(int);
	boolean setPin(B[]);
	boolean setRemoteOutOfBandData();
	boolean setSimAccessPermission(int);
	java.lang.String toString();
	void writeToParcel(android.os.Parcel, int);

}

使用FRIDA-DEXDump脱壳

安装测试软件,加载FRIDA-DEXDump插件,查看有该包有什么类

root@kali:~/Desktop# objection -N -h 192.168.1.10 -p 8888 -g com.cz.babySister explore
Using networked device @`192.168.1.10:8888`
Agent injected and responds ok!

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.8.4

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions
com.cz.babySister on (google: 8.1.0) [net] # plugin                             
Unknown or ambiguous command: `plugin`. Try `help plugin`.
com.cz.babySister on (google: 8.1.0) [net] # plugin load /root/Desktop/FRIDA-DEX
Dump/frida_dexdump                                                              
Loaded plugin: dexdump
com.cz.babySister on (google: 8.1.0) [net] # android hooking search classes com.
cz.babySister                                                                   
com.cz.babySister.activity.BaseActivity
com.cz.babySister.activity.LoginActivity
com.cz.babySister.activity.MainActivity
com.cz.babySister.activity.N
com.cz.babySister.activity.O
com.cz.babySister.activity.P
com.cz.babySister.activity.Q
com.cz.babySister.activity.RegisterActivity
com.cz.babySister.activity.S
com.cz.babySister.activity.T
com.cz.babySister.activity.WelcomeActivity
com.cz.babySister.activity.a
com.cz.babySister.activity.k
com.cz.babySister.activity.l
com.cz.babySister.activity.m
com.cz.babySister.activity.n
com.cz.babySister.activity.o
com.cz.babySister.activity.oa
com.cz.babySister.application.MyApplication
com.cz.babySister.application.a
com.cz.babySister.interfaces.CitySelectLiener
com.cz.babySister.interfaces.JiFenInterFaces

Found 22 classes

查看积分所在类的函数

com.cz.babySister on (google: 8.1.0) [net] # android hooking list class_methods 
com.cz.babySister.interfaces.JiFenInterFaces                                    
public abstract void com.cz.babySister.interfaces.JiFenInterFaces.getJiFen(float)

Found 1 method(s)

om.cz.babySister on (google: 8.1.0) [net] # plugin dexdump search              
[DEXDump] Found: DexAddr=0x777f4ee01c, DexSize=0x636b10
[DEXDump] Found: DexAddr=0x777fbb8000, DexSize=0xba28
[DEXDump] Found: DexAddr=0x7781800000, DexSize=0xba28
[DEXDump] Found: DexAddr=0x7781dc4f85, DexSize=0x11c
[DEXDump] Found: DexAddr=0x7781f2abde, DexSize=0x11c
[DEXDump] Found: DexAddr=0x778212901c, DexSize=0x3339c
[DEXDump] Found: DexAddr=0x7782cb101c, DexSize=0xba28
[DEXDump] Found: DexAddr=0x778fddf080, DexSize=0x559638
[DEXDump] Found: DexAddr=0x781f5bd000, DexSize=0x580
[DEXDump] Found: DexAddr=0x781f691000, DexSize=0x1ec
com.cz.babySister on (google: 8.1.0) [net] # plugin dexdump dump                
[DEXDump]: DexSize=0x636b10, DexMd5=231500141df1e99cee87d4efbc2913a2, SavePath=/root/Desktop/com.cz.babySister/0x777f4ee01c.dex
[DEXDump]: DexSize=0xba28, DexMd5=a2fa46881e6a15401a35e782d91a5c30, SavePath=/root/Desktop/com.cz.babySister/0x777fbb8000.dex
[DEXDump]: Skip duplicate dex 0x7781800000
[DEXDump]: DexSize=0x11c, DexMd5=f1771b68f5f9b168b79ff59ae2daabe4, SavePath=/root/Desktop/com.cz.babySister/0x7781dc4f85.dex
[DEXDump]: Skip duplicate dex 0x7781f2abde
[DEXDump]: DexSize=0x3339c, DexMd5=b4b87d2c326c2cbd92b15ca9d453a565, SavePath=/root/Desktop/com.cz.babySister/0x778212901c.dex
[DEXDump]: DexSize=0xba28, DexMd5=e5a76d0e8fc1e7b36ffa744bfa47a183, SavePath=/root/Desktop/com.cz.babySister/0x7782cb101c.dex
[Except] - Error: access violation accessing 0x778fe00000
    at frida/runtime/core.js:144
    at frida/runtime/message-dispatcher.js:15
    at c (frida/runtime/message-dispatcher.js:25): {'addr': '0x778fddf080', 'size': 5609016}
[DEXDump]: DexSize=0x580, DexMd5=1dd45877a3cd479a9e54953d0b0b029c, SavePath=/root/Desktop/com.cz.babySister/0x781f5bd000.dex
[DEXDump]: DexSize=0x1ec, DexMd5=a5b3b0c4946613971682701397840f5f, SavePath=/root/Desktop/com.cz.babySister/0x781f691000.dex

就报存下来dump文件了。

Frida上手和逆向三段

  • 基本操作:参数,调用栈,返回值
  • 方法重载,参数构造,动静态处理
  • 主动调用,忽略内部细节,直接返回结果
  • 逆向三段:hook,invoke,rpc

demo

源程序

package com.example.lesson4one;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            fun(50,80);

        }

    }


    int fun(int x,int y){
        Log.d("kanxue",String.valueOf((x+y)));
        return x+y;
    }
}

注入脚本

function main(){
    Java.perform(function(){
        Java.use("com.example.lesson4one.MainActivity").fun.implementation =function(arg1,arg2){
            var result=this.fun(arg1,arg2)
            console.log("arg1,arg2,result",arg1,arg2,result)
            return result;
        }
    })
}


setImmediate(main)
root@kali:~/Downloads/frida-agent-example/agent# frida -U com.example.lesson4one -l lesson4-2.js
     ____
    / _  |   Frida 12.8.0 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://www.frida.re/docs/home/
                                                                                
[Google Pixel XL::com.example.lesson4one]-> arg1,arg2,result 50 80 130
arg1,arg2,result 50 80 130
arg1,arg2,result 50 80 130
arg1,arg2,result 50 80 130
arg1,arg2,result 50 80 130
[Google Pixel XL::com.example.lesson4one]-> arg1,arg2,result 50 80 130

修改脚本

var result=this.fun(100,200)

返回结果

rg1,arg2,result 50 80 300
arg1,arg2,result 50 80 300
arg1,arg2,result 50 80 300
arg1,arg2,result 50 80 300

以下一句话打印调用栈

console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

关于重载

如果在代码中加入

   String fun(String x ){
        total +=x ;
        return x.toLowerCase();

    }

    int fun(int x ,int y){
        Log.d("kanxue", String.valueOf((x+y)));
        return x+y;
    }

hook时如下会报错,提示重载

root@kali:~/Downloads/frida-agent-example/agent# frida -U com.example.lesson4one -l lesson4-2.js
                                                                                
Error: fun(): has more than one overload, use .overload() to choose from:
        .overload('java.lang.String')
        .overload('int', 'int')
    at throwOverloadError (frida/node_modules/frida-java-bridge/lib/class-factory.js:1020)
    at frida/node_modules/frida-java-bridge/lib/class-factory.js:707
    at /lesson4-2.js:7
    at frida/node_modules/frida-java-bridge/lib/vm.js:11
    at E (frida/node_modules/frida-java-bridge/index.js:346)
    at frida/node_modules/frida-java-bridge/index.js:298
    at frida/node_modules/frida-java-bridge/lib/vm.js:11
    at frida/node_modules/frida-java-bridge/index.js:278
    at main (/lesson4-2.js:8)

需要按照提示加入

overload(‘int’, ‘int’)

function main(){
    Java.perform(function(){
        Java.use("com.example.lesson4one.MainActivity").fun.overload('int', 'int').implementation =function(arg1,arg2){
            var result=this.fun(100,200)
            
            console.log("arg1,arg2,result",arg1,arg2,result)
            return result;
        }
    })
}


setImmediate(main)

此时就可以hook成功。

root@kali:~/Downloads/frida-agent-example/agent# frida -U com.example.lesson4one -l lesson4-2.js
     ____

 
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300
java.lang.Throwable
        at com.example.lesson4one.MainActivity.fun(Native Method)
        at com.example.lesson4one.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:6999)
        at android.app.Activity.performCreate(Activity.java:6990)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

arg1,arg2,result 50 80 300

关于未调用的动态函数

如在android程序中

    String secret(){
        return total;
    }

使用如下代码主动调用
静态函数可以直接调用

假如secret2是静态函数
Java.use("com.example.lesson4one.MainActivity").secret2()

动态函数要找到实例,然后再通过实例调用

function invoke(){
    Java.perform(function(){
        Java.choose("com.example.lesson4one.MainActivity",{
            onMatch:function(instance){
                console.log("found insttance ",instance);
                console.log("invoke instance.secret ",instance.secret());
            },onComplete:function(){console.log("search completed !")}
        })
    })
}

setTimeout(invoke,2000)

Frida构造数组、对象、Map和类参数

数组

源码


        Log.d("SimpleArray", "onCreate: SImpleArray");
        char arr[][] = new char[4][]; // 创建一个4行的二维数组
        arr[0] = new char[] { '春', '眠', '不', '觉', '晓' }; // 为每一行赋值
        arr[1] = new char[] { '处', '处', '闻', '啼', '鸟' };
        arr[2] = new char[] { '夜', '来', '风', '雨', '声' };
        arr[3] = new char[] { '花', '落', '知', '多', '少' };
        Log.d("SimpleArray", "-----横版-----");
        for (int i = 0; i < 4; i++) { // 循环4行
            //Log.d("SimpleArraysToString", Arrays.toString(arr[i]));
            //Log.d("SimpleStringBytes", Arrays.toString (Arrays.toString (arr[i]).getBytes()));
            for (int j = 0; j < 5; j++) { // 循环5列
                Log.d("SimpleArray", Character.toString(arr[i][j])); // 输出数组中的元素
            }
            if (i % 2 == 0) {
                Log.d("SimpleArray", ",");// 如果是一、三句,输出逗号
            } else {
                Log.d("SimpleArray", "。");// 如果是二、四句,输出句号
            }
        }

要对Character.toString用fridahook,观察值

function main(){
    Java.perform(function(){
        Java.use("java.lang.Character").toString.overload('char').implementation =function(x)
        {
            var result=this.toString(x)
            console.log("x,result",x,result)
            return result

        }
    })
}


setImmediate(main)

结果如下

root@kali:~/Downloads/frida-agent-example/agent# frida -U -f com.kanxue.lesson5 -l lesson5.js --no-pause

Spawned `com.kanxue.lesson5`. Resuming main thread!                     
[Google Pixel XL::com.kanxue.lesson5]-> x,result 春 春
x,result 眠 眠
x,result 不 不
x,result 觉 觉
x,result 晓 晓
x,result 处 处
x,result 处 处
x,result 闻 闻                                                                   

再如要打印一整行,然后hook改变结果,改变如下代码生成的结果

Log.d("SimpleArraysToString", Arrays.toString(arr[i]));

脚本如下

function main(){
    Java.perform(function(){
        Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x)
        {
            var result=this.toString(x);
            console.log("x,result",x,result);
            return result;
        }

    })
}

结果如下:

[Google Pixel XL::com.kanxue.lesson5]-> x,result [object Object] [春, 眠, 不, 觉, 晓]
x,result [object Object] [春, 眠, 不, 觉, 晓]
x,result [object Object] [处, 处, 闻, 啼, 鸟]
x,result [object Object] [处, 处, 闻, 啼, 鸟]
x,result [object Object] [夜, 来, 风, 雨, 声]
x,result [object Object] [夜, 来, 风, 雨, 声]
x,result [object Object] [花, 落, 知, 多, 少]
x,result [object Object] [花, 落, 知, 多, 少]                                   

可以看到参数为[object Object] ,事实上知道他是Char C的数组。
可以使用JSON.stringfy(x)打印,或者使用roysue工具r0gson,如下
https://bbs.pediy.com/thread-259186.htm
脚本

function main(){
    Java.perform(function(){
        Java.openClassFile("/data/local/tmp/r0gson.dex").load();
        const gson = Java.use('com.r0ysue.gson.Gson');
        //console.log(gson.$new().toJson(xxx));

        Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x)
        {
            var result=this.toString(x);
            console.log("x,result",gson.$new().toJson(x),result);
            return result;
        }

    })
}


setImmediate(main)
[Google Pixel XL::com.kanxue.lesson5]-> x,result ["春","眠","不","觉","晓"] [春, 眠, 不, 觉, 晓]
x,result ["处","处","闻","啼","鸟"] [处, 处, 闻, 啼, 鸟]
x,result ["夜","来","风","雨","声"] [夜, 来, 风, 雨, 声]
x,result ["花","落","知","多","少"] [花, 落, 知, 多, 少]
x,result ["春","眠","不","觉","晓"] [春, 眠, 不, 觉, 晓]
x,result ["处","处","闻","啼","鸟"] [处, 处, 闻, 啼, 鸟]
x,result ["夜","来","风","雨","声"] [夜, 来, 风, 雨, 声]
x,result ["花","落","知","多","少"] [花, 落, 知, 多, 少]

构造数组

可以使用frida API Java.array构造

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

        Java.openClassFile("/data/local/tmp/r0gson.dex").load();
        const gson = Java.use('com.r0ysue.gson.Gson');
        //console.log(gson.$new().toJson(xxx));

        Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x)
        {
            var charArray=Java.array('char',['一', '行', '白', '鹭', '上', '青', '天']);
            var result=this.toString(charArray);
            console.log("x,result",gson.$new().toJson(charArray),result);

            

            return Java.use("java.lang.String").$new("我连安全在搞啥都不知道");
        }

    })
}


setImmediate(main)

结果如下:

root@kali:~/Downloads/frida-agent-example/agent# frida -U -f com.kanxue.lesson5 -l lesson5.js --no-pause
     ____
    / _  |   Frida 12.8.0 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://www.frida.re/docs/home/
Spawned `com.kanxue.lesson5`. Resuming main thread!                     
[Google Pixel XL::com.kanxue.lesson5]-> x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]                           
x,result ["一","行","白","鹭","上","青","天"] [一, 行, 白, 鹭, 上, 青, 天]

另外如果想hook以下代码,就是一个数组转为字符串,然后在getBytes打印。

Log.d("SimpleStringBytes", Arrays.toString (Arrays.toString (arr[i]).getBytes()));

frida脚本为:

 Java.use("java.util.Arrays").toString.overload('[B').implementation=function(x)
        {
        
            var result=this.toString(x);
            console.log("x,result",gson.$new().toJson(x),result);

            

            return result;
        }

强制类型转换

首先定义一个父类Water,和子类Juice
代码如下:

package com.kanxue.lesson5;

import android.util.Log;

public class Water { // 水 类
    public static String flow(Water W) { // 水 的方法
        // SomeSentence
        Log.d("2Object", "water flow: I`m flowing");
        return "water flow: I`m flowing";
    }

    public String still(Water W) { // 水 的方法
        // SomeSentence
        Log.d("2Object", "water still: still water runs deep!");
        return "water still: still water runs deep!";
    }
}



package com.kanxue.lesson5;

import android.util.Log;

public class Juice extends Water { // 果汁 类 继承了水类

    public String fillEnergy(){
        Log.d("2Object", "Juice: i`m fillingEnergy!");
        return "Juice: i`m fillingEnergy!";
    }

    public static void main() {

        Water w1 = new Water();
        flow(w1) ; //

        Juice J = new Juice(); // 实例化果汁类对象
        flow(J) ; // 调用水的方法  向上转型 J → W

        Water w2 = new Juice();
        ((Juice) w2).fillEnergy();
    }
}

脚本

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


        var Waterhandle=null;
        Java.choose("com.kanxue.lesson5.Water",{
            onMatch:function(instance){
                console.log("found instance:",instance);
                console.log("instance call still:",instance.still(instance));
                Waterhandle=instance;
            },onComplete:function(){console.log("search is completed!")}
        })

    })
}


setImmediate(main)

结果

root@kali:~/Downloads/frida-agent-example/agent# frida -U com.kanxue.lesson5 -l 
                                                      
found instance: com.kanxue.lesson5.Water@53ef60e
instance call still: water still: still water runs deep!
found instance: com.kanxue.lesson5.Water@79b382f
instance call still: water still: still water runs deep!
search is completed!
[Google Pixel XL::com.kanxue.lesson5]->  

如果想强制转化子类为父类,在frida中可以如下操作
该操作是将子类Juice转化为water并且调用其的动态方法still

 var JuiceHandle = null ;
        Java.choose("com.kanxue.lesson5.Juice",{
            onMatch:function(instance){
                console.log("found instance :",instance);
                console.log("filling energy,",instance.fillEnergy());
                JuiceHandle= instance;
            },onComplete:function(){"Search Completed!"}
        })
        var WaterHandle = Java.cast(JuiceHandle ,Java.use("com.kanxue.lesson5.Water"));
        console.log("Water invoke still ", WaterHandle.still(WaterHandle));

结果

found instance : com.kanxue.lesson5.Juice@b8f6e3c
filling energy, Juice: i`m fillingEnergy!
found instance : com.kanxue.lesson5.Juice@e1067c5
filling energy, Juice: i`m fillingEnergy!
found instance : com.kanxue.lesson5.Juice@3f8651a
filling energy, Juice: i`m fillingEnergy!
found instance : com.kanxue.lesson5.Juice@bad284b
filling energy, Juice: i`m fillingEnergy!
Water invoke still  water still: still water runs deep!

父类转子类是不行的

接口interface

如下,代码,milk类实现了liquid接口

package com.kanxue.lesson5;

public interface liquid {
    public String flow();
}



package com.kanxue.lesson5;

import android.util.Log;

public class milk implements liquid {
    public String flow(){
        Log.d("3interface", "flowing : interface ");
        return "nihao";
    };
    public static void main() {
        milk m = new milk();
        m.flow();
    }
}

我们可以用到Java.registerClass(spec),其可以根据接口创建一个类。

Java.registerClass(spec): create a new Java class and return a wrapper for it, where spec is an object containing:

name: String specifying the name of the class.
superClass: (optional) Super-class. Omit to inherit from java.lang.Object.
implements: (optional) Array of interfaces implemented by this class.
fields: (optional) Object specifying the name and type of each field to expose.
methods: (optional) Object specifying methods to implement.

实现了一个beer,beer类实现了接口liquid

function main(){
    Java.perform(function(){
        var beer = Java.registerClass({
            name: 'com.kanxue.lesson5.beer',
            implements: [Java.use('com.kanxue.lesson5.liquid')],
            methods: {
                flow: function(){
                    console.log("flow beer");
                    return "test good";
                }

            }
          });
          console.log("beer.flow",beer.$new().flow());


        

    })
}

枚举

枚举相当于一个受限制的类。
在android代码中实现了枚举

package com.kanxue.lesson5;

import android.util.Log;

enum Signal {
    GREEN, YELLOW, RED
}
public class TrafficLight {
    public static Signal color = Signal.RED;
    public static void main() {
        //Log.d("4enum", "enum "+ color.getClass().getName().toString());
        Log.d("4enum", "enum "+ color);
        switch (color) {
            case RED:
                color = Signal.GREEN;
                break;
            case YELLOW:
                color = Signal.RED;
                break;
            case GREEN:
                color = Signal.YELLOW;
                break;
        }
    }


}

frida脚本查找匹配枚举

Java.choose("com.kanxue.lesson5.Signal",{
            onMatch:function(instance){
                console.log("found instance",instance);
                console.log("invoke getDeclaringClass",instance.getDeclaringClass());
            },onComplete:function(){
                {"Search Completed!"};
            }
        })

    })

结果

found instance GREEN
invoke getDeclaringClass class com.kanxue.lesson5.Signal
found instance YELLOW
invoke getDeclaringClass class com.kanxue.lesson5.Signal
found instance RED
invoke getDeclaringClass class com.kanxue.lesson5.Signal

关于stl打印。hook变量

在源程序如下:

        Map mapr0ysue = new HashMap<>(); // 创建Map集合对象
        mapr0ysue.put("ISBN 978-7-5677-8742-1", "Android项目开发实战入门"); // 向Map集合中添加元素
        mapr0ysue.put("ISBN 978-7-5677-8741-4", "C语言项目开发实战入门");
        mapr0ysue.put("ISBN 978-7-5677-9097-1", "PHP项目开发实战入门");
        mapr0ysue.put("ISBN 978-7-5677-8740-7", "Java项目开发实战入门");

        //Log.d("5map", "key值toString"+mapr0ysue.toString());




        Set set = mapr0ysue.keySet(); // 构建Map集合中所有key的Set集合
        Iterator it = set.iterator(); // 创建Iterator迭代器
        Log.d("5map", "key值:");
        while (it.hasNext()) { // 遍历并输出Map集合中的key值
            try {
                Thread.sleep(2000);
                Log.d("5map", it.next()+"  ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


frida代码如下:

function hashmap888(){
    Java.perform(function(){
        Java.choose("java.util.HashMap",{
            onMatch:function(instance){
                if(instance.toString().indexOf("ISBN")!=-1)
                {

                    console.log("found HashMap:",instance);
                console.log("HashMap toString:",instance.toString());
                }
                

            },onComplete:function(){
                console.log("Search Completed!");
            }
        })

    })
}

setImmediate(hashmap888,2000)

结果:

found HashMap: {ISBN 978-7-5677-9097-1=PHP项目开发实战入门, ISBN 978-7-5677-8742-1=Android项目开发实战入门, ISBN 978-7-5677-8741-4=C语言项目开发实战入门, ISBN 978-7-5677-8740-7=Java项目开发实战入门}
HashMap toString: {ISBN 978-7-5677-9097-1=PHP项目开发实战入门, ISBN 978-7-5677-8742-1=Android项目开发实战入门, ISBN 978-7-5677-8741-4=C语言项目开发实战入门, ISBN 978-7-5677-8740-7=Java项目开发实战入门}
Search Completed!

Frida综合情景案例

demo1

如图找到判断LoginActivity.a(obj, obj).equals(obj2)调用错误代码处

   public void onClick(View view) {
                String obj = editText.getText().toString();
                String obj2 = editText2.getText().toString();
                if (TextUtils.isEmpty(obj) || TextUtils.isEmpty(obj2)) {
                    Toast.makeText(LoginActivity.this.mContext, "username or password is empty.", 1).show();
                } else if (LoginActivity.a(obj, obj).equals(obj2)) {
                    LoginActivity.this.startActivity(new Intent(LoginActivity.this.mContext, FridaActivity1.class));
                    LoginActivity.this.finishActivity(0);
                } else {
                    Toast.makeText(LoginActivity.this.mContext, "Login failed.", 1).show();
                }
            }
        });
        

进行fridahook

function main(){
    Java.perform(function(){
        Java.use("com.example.androiddemo.Activity.LoginActivity").a.overload('java.lang.String', 'java.lang.String').implementation=function(x,y){
            var result=this.a(x,y);
            console.log("x,y,result:",x,y,result);
            return result;
        }
    })
}

setImmediate(main)

随便输出aaaaa,结果如下:

[Google Pixel XL::com.example.androiddemo]-> x,y,result: aaaaaa aaaaaa 4dca528a2131b62a29686c0cf67a15926faef3cfc24becfc49f635a5bfec7519

所以后面这个result就是4dca528a2131b62a29686c0cf67a15926faef3cfc24becfc49f635a5bfec7519
使用adb 输入此段字符串

adb shell  
input text 4dca528a2131b62a29686c0cf67a15926faef3cfc24becfc49f635a5bfec7519

可见进入界面,但是点击下一关,check failed
Frida学习笔记_第2张图片
根据字符串Check Failed查找
附进有check success函数


    public void CheckSuccess() {
    }

根据代码找到第一关

ublic class FridaActivity1 extends BaseFridaActivity {
    private static final char[] table = {'L', 'K', 'N', 'M', 'O', 'Q', 'P', 'R', 'S', 'A', 'T', 'B', 'C', 'E', 'D', 'F', 'G', 'H', 'I', 'J', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'o', 'd', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'e', 'f', 'g', 'h', 'j', 'i', 'k', 'l', 'm', 'n', 'y', 'z', '0', '1', '2', '3', '4', '6', '5', '7', '8', '9', '+', '/'};

    @Override // com.example.androiddemo.Activity.BaseFridaActivity
    public String getNextCheckTitle() {
        return "当前第1关";
    }

    @Override // com.example.androiddemo.Activity.BaseFridaActivity
    public void onCheck() {
        try {
            if (a(b("请输入密码:")).equals("R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=")) {
                CheckSuccess();
                startActivity(new Intent(this, FridaActivity2.class));
                finishActivity(0);
                return;
            }
            super.CheckFailed();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

所以根据代码可以,直接让a方法返回上述代码equal值。
脚本如下:

function main(){
    Java.perform(function(){
        Java.use("com.example.androiddemo.Activity.FridaActivity1").a.implementation=function(x){
            var result="R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
            console.log("result:",result);
            return result;
        }

        
    })
}

setImmediate(main)

进入第二关
Frida学习笔记_第3张图片
第二关关键代码如下,两者都为否才成功,所以就需要调用静动态函数即可


    private static void setStatic_bool_var() {
        static_bool_var = true;
    }

    private void setBool_var() {
        this.bool_var = true;
    }

    @Override // com.example.androiddemo.Activity.BaseFridaActivity
    public void onCheck() {
        if (!static_bool_var || !this.bool_var) {
            super.CheckFailed();
            return;
        }
        CheckSuccess();

代码如下:

function second(){
    Java.perform(function(){
        //static
        Java.use("com.example.androiddemo.Activity.FridaActivity2").setStatic_bool_var();
        //dynamic
        Java.choose("com.example.androiddemo.Activity.FridaActivity2",
        {onMatch:function(instance){
            console.log("found instance");
            instance.setBool_var();
            
        },onComplete:function(){console.log("search completed !")}

        })

    })
}

setImmediate(second)

如下注入进去。
Frida学习笔记_第4张图片
第三关需要将3个变量都设置为真,才能进入下关。这三个为动静态方法和跟函数同名的变量
与函数同名变量开头前加下划线

 public void onCheck() {
        if (!static_bool_var || !this.bool_var || !this.same_name_bool_var) {
            super.CheckFailed();
            return;
        }
function thrid(){
    Java.perform(
        function(){
            Java.use("com.example.androiddemo.Activity.FridaActivity3").static_bool_var.value=true;
            


            Java.choose("com.example.androiddemo.Activity.FridaActivity3",
        {onMatch:function(instance){
            console.log("found instance");
            instance.bool_var.value=true;
            instance._same_name_bool_var.value=true;
            
        },onComplete:function(){console.log("search completed !")}

        })



    })
}

setImmediate(thrid)

第四关为内部类函数,全部要设置为true,才能进入下一关

public class FridaActivity4 extends BaseFridaActivity {
    @Override // com.example.androiddemo.Activity.BaseFridaActivity
    public String getNextCheckTitle() {
        return "当前第4关";
    }

    private static class InnerClasses {
        public static boolean check1() {
            return false;
        }

        public static boolean check2() {
            return false;
        }

        public static boolean check3() {
            return false;
        }

        public static boolean check4() {
            return false;
        }

        public static boolean check5() {
            return false;
        }

        public static boolean check6() {
            return false;
        }

        private InnerClasses() {
        }
    }

    @Override // com.example.androiddemo.Activity.BaseFridaActivity
    public void onCheck() {
        if (!InnerClasses.check1() || !InnerClasses.check2() || !InnerClasses.check3() || !InnerClasses.check4() || !InnerClasses.check5() || !InnerClasses.check6()) {
            super.CheckFailed();
            return;
        }
        CheckSuccess();
        startActivity(new Intent(this, FridaActivity5.class));
        finishActivity(0);
    }
}

frida代码如下:

function fourth(){
    Java.perform(function(){
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check1.implementation=function(){
            return true;
        }
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check2.implementation=function(){
            return true;
        }
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check3.implementation=function(){
            return true;
        }
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check4.implementation=function(){
            return true;
        }
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check5.implementation=function(){
            return true;
        }
        Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses").check6.implementation=function(){return true;}
    
}
    )}


setImmediate(fourth)

如果函数太多可以使用枚举:

function forth2(){
    Java.perform(function(){
        var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses" ;
        var InnerClass = Java.use(class_name);
        var all_methods = InnerClass.class.getDeclaredMethods();
        //console.log(all_methods);
        for(var i = 0;i

第5个,看到关键函数如下,根据需要定位到getDynamicDexCheck的check为真跳转正误,getDynamicDexCheck返回值是一个DynamicDexCheck实例,实例有check方法。所以需要从定位到getDynamicDexCheck开始,而getDynamicDexCheck用到了DynamicDexCheck,DynamicDexCheck根据代码由com.example.androiddemo.Dynamic.DynamicCheck类实例化,又因为此类并没有导入加载,所以需要先换到com.example.androiddemo.Dynamic.DynamicCheck的clossloader然后去找cheeck。
首先需要定位到getDynamicDexCheck,没法直接跳转到,进行hook观察所在的类

public void onCheck() {
        if (getDynamicDexCheck() == null) {
            Toast.makeText(this, "onClick loaddex Failed!", 1).show();
        } else if (getDynamicDexCheck().check()) {
            CheckSuccess();
            startActivity(new Intent(this, FridaActivity6.class));
            finishActivity(0);
        } else {
            super.CheckFailed();
        }
    }

脚本如下:


function fifth()
{
    Java.perform(function(){
        Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
            onMatch:function(instance){
                console.log("found instance getDynamicDexCheck:",instance.getDynamicDexCheck().$className);
            },onComplete:function(){console.log("search completed !")}
        })
    })
}

setImmediate(fifth)

结果如下:

found instance getDynamicDexCheck: com.example.androiddemo.Dynamic.DynamicCheck

然后添加脚本去改变check的值

    function fifth(){
        Java.perform(function(){
            Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
                onMatch:function(instance){
                    console.log("found instance getDynamicDexCheck :",instance.getDynamicDexCheck().$className);
                },onComplete:function(){console.log("search complete!")}
            })
            Java.enumerateClassLoaders({
                onMatch:function(loader){
                    try {
                        if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
                            console.log("Succefully found loader!",loader);
                            Java.classFactory.loader = loader;
                        }
                    } catch (error) {
                        console.log("found error "+error)
                        
                    }
                },onComplete:function(){"enum completed!"}
            })
            Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function(){return true};
        })
    }
    
setImmediate(fifth)

第六关
比较简单,脚本如下:

function sixth (){
    Java.perform(function(){
        Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class0").check.implementation = function (){return true };
        Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class1").check.implementation = function (){return true };
        Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class2").check.implementation = function (){return true };

        
    })
    
}

也可以使用枚举:

function sixth2(){
    Java.perform(function(){
        Java.enumerateLoadedClasses({
            onMatch:function(name,handle){
                if(name.toString().indexOf("com.example.androiddemo.Activity.Frida6.Frida6")>=0){
                    console.log("name",name)
                    Java.use(name).check.implementation = function(){return true}
                }
            },onComplete(){}
        })

    })

}

综合案例、hook时机、制作dex、算法还原思路

关于单独打包.java文件,可以使用

root@kali:~/AndroidStudioProjects/lesson9/app/build/intermediates/javac/debug/cl
asses/com/kanxue/lesson9# /root/Android/Sdk/build-tools/30.0.3/d8 reverseA.class
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
root@kali:~/AndroidStudioProjects/lesson9/app/build/intermediates/javac/debug/classes/com/kanxue/lesson9# ls
BuildConfig.class  classes.dex  MainActivity.class  reverseA.class

参考资料

看雪视频

你可能感兴趣的:(Android,Frida,移动安全)