这恐怕是学习Frida最详细的笔记(一)




转载自Sakura的博客:https://eternalsakura13.com/2020/07/04/fridajava

致谢

本篇文章学到的内容来自且彻底来自r0ysue的知识星球,推荐一下(这个男人啥都会,还能陪你在线撩骚)。
python



Frida环境

https://github.com/frida/fridaandroid

pyenv

python全版本随机切换,这里提供macOS上的配置方法c++

1brew update
2brew install pyenv
3echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.bash_profile
 1下载一个3.8.2,下载真的很慢,要慢慢等
2pyenv install 3.8.2
3
4pyenv versions
5sakura@sakuradeMacBook-Pro:~$ pyenv versions
6  system
73.8.2 (set by /Users/sakura/.python-version)
8切换到咱们装的
9pyenv local 3.8.2
10python -V
11pip -V
12本来系统自带的
13python local system
14python -V

另外当你须要临时禁用pyenv的时候
git



把这个注释了而后另开终端就行了。github



关于卸载某个python版本web

1Uninstalling Python Versions
2As time goes on, you will accumulate Python versions in your $(pyenv root)/versions directory.
3
4To remove old Python versions, pyenv uninstall command to automate the removal process.
5
6Alternatively, simply rm -rf the directory of the version you want to remove. You can find the directory of a particular Python version with the pyenv prefix command, e.g. pyenv prefix 2.6.8.

frida安装

若是直接按下述安装则会直接安装frida和frida-tools的最新版本。sql

1pip install frida-tools
2frida --version
3frida-ps --version

咱们也能够自由安装旧版本的frida,例如12.8.0shell

1pyenv install 3.7.7
2pyenv local 3.7.7
3pip install frida==12.8.0
4pip install frida-tools==5.3.0

老版本frida和对应关系
对应关系很好找
数据库



安装objection

1pyenv local 3.8.2
2pip install objection
3objection -h
1pyenv local 3.7.7
2pip install objection==1.8.4
3objection -h

frida使用

下载frida-server并解压,在这里下载frida-server-12.8.0

先adb shell,而后切换到root权限,把以前push进来的frida server改个名字叫fs
而后运行frida

1adb push /Users/sakura/Desktop/lab/alpha/tools/android/frida-server-12.8.0-android-arm64 /data/local/tmp
2chmod +x fs
3./fs

若是要监听端口,就

1./fs -l 0.0.0.0:8888

frida开发环境搭建

  1. 安装

1git clone https://github.com/oleavr/frida-agent-example.git
2cd frida-agent-example/
3npm install
  1. 使用vscode打开此工程,在agent文件夹下编写js,会有智能提示。

  2. npm run watch会监控代码修改自动编译生成js文件

  3. python脚本或者cli加载_agent.js
    frida -U -f com.example.android --no-pause -l _agent.js

下面是测试脚本

s1.js

1function main({
2    Java.perform(function x({
3        console.log("sakura")
4    })
5}
6setImmediate(main)

loader.py

 1import time
2import frida
3
4device8 = frida.get_device_manager().add_remote_device("192.168.0.9:8888")
5pid = device8.spawn("com.android.settings")
6device8.resume(pid)
7time.sleep(1)
8session = device8.attach(pid)
9with open("si.js"as f:
10    script = session.create_script(f.read())
11script.load()
12input() #等待输入

解释一下,这个脚本就是先经过frida.get_device_manager().add_remote_device来找到device,而后spawn方式启动settings,而后attach到上面,并执行frida脚本。

FRIDA基础

frida查看当前存在的进程

frida-ps -U查看经过usb链接的android手机上的进程。

 1sakura@sakuradeMacBook-Pro:~$ frida-ps --help
2Usage: frida-ps [options]
3
4Options:
5  --version             show program's version number and exit
6  -h, --help            show this help message and exit
7  -D ID, --device=ID    connect to device with the given ID
8  -U, --usb             connect to USB device
9  -R, --remote          connect to remote frida-server
10  -H HOST, --host=HOST  connect to remote frida-server on HOST
11  -a, --applications    list only applications
12  -i, --installed       include all installed applications
1sakura@sakuradeMacBook-Pro:~$ frida-ps -U
2  PID  Name
3-----  ---------------------------------------------------
4 3640  ATFWD-daemon
5  707  adbd
6  728  adsprpcd
726041  android.hardware.audio@2.0-service
8  741  android.hardware.biometrics.fingerprint@

经过grep过滤就能够找到咱们想要的包名。

frida打印参数和修改返回值

 1package myapplication.example.com.frida_demo;
2
3import android.support.v7.app.AppCompatActivity;
4import android.os.Bundle;
5import android.util.Log;
6
7public class MainActivity extends AppCompatActivity {
8
9    private String total = "@@@###@@@";
10
11    @Override
12    protected void onCreate(Bundle savedInstanceState) {
13        super.onCreate(savedInstanceState);
14        setContentView(R.layout.activity_main);
15
16        while (true){
17
18            try {
19                Thread.sleep(1000);
20            } catch (InterruptedException e) {
21                e.printStackTrace();
22            }
23
24            fun(50,30);
25            Log.d("sakura.string" , fun("LoWeRcAsE Me!!!!!!!!!"));
26        }
27    }
28
29    void fun(int x , int y ){
30        Log.d("sakura.Sum" , String.valueOf(x+y));
31    }
32
33    String fun(String x){
34        total +=x;
35        return x.toLowerCase();
36    }
37
38    String secret(){
39        return total;
40    }
41}
 1function main({
2    console.log("Enter the Script!");
3    Java.perform(function x({
4        console.log("Inside Java perform");
5        var MainActivity = Java.use("myapplication.example.com.frida_demo.MainActivity");
6        // 重载找到指定的函数
7        MainActivity.fun.overload('java.lang.String').implementation = function (str{
8            //打印参数
9            console.log("original call : str:" + str);
10            //修改结果
11            var ret_value = "sakura";
12            return ret_value;
13        };
14    })
15}
16setImmediate(main);
1sakura@sakuradeMacBook-Pro:~$ frida-ps -U | grep frida
28738  frida-helper-32
38897  myapplication.example.com.frida_demo
4
5// -f是经过spawn,也就是重启apk注入js
6sakura@sakuradeMacBook-Pro:~$ frida -U -f myapplication.example.com.frida_demo -l frida_demo.js
7...
8original call : str:LoWeRcAsE Me!!!!!!!!!
912-21 04:46:49.875 9594-9594/myapplication.example.com.frida_demo D/sakura.string: sakura

frida寻找instance,主动调用。

 1function main({
2    console.log("Enter the Script!");
3    Java.perform(function x({
4        console.log("Inside Java perform");
5        var MainActivity = Java.use("myapplication.example.com.frida_demo.MainActivity");
6        //overload 选择被重载的对象
7        MainActivity.fun.overload('java.lang.String').implementation = function (str{
8            //打印参数
9            console.log("original call : str:" + str);
10            //修改结果
11            var ret_value = "sakura";
12            return ret_value;
13        };
14        // 寻找类型为classname的实例
15        Java.choose("myapplication.example.com.frida_demo.MainActivity", {
16            onMatchfunction (x{
17                console.log("find instance :" + x);
18                console.log("result of secret func:" + x.secret());
19            },
20            onCompletefunction ({
21                console.log("end");
22            }
23        });
24    });
25}
26setImmediate(main);

frida rpc

 1function callFun({
2    Java.perform(function fn({
3        console.log("begin");
4        Java.choose("myapplication.example.com.frida_demo.MainActivity", {
5            onMatchfunction (x{
6                console.log("find instance :" + x);
7                console.log("result of fun(string) func:" + x.fun(Java.use("java.lang.String").$new("sakura")));
8            },
9            onCompletefunction ({
10                console.log("end");
11            }
12        })
13    })
14}
15rpc.exports = {
16    callfun: callFun
17};
 1import time
2import frida
3
4device = frida.get_usb_device()
5pid = device.spawn(["myapplication.example.com.frida_demo"])
6device.resume(pid)
7time.sleep(1)
8session = device.attach(pid)
9with open("frida_demo_rpc_call.js"as f:
10    script = session.create_script(f.read())
11
12def my_message_handler(message, payload):
13    print(message)
14    print(payload)
15
16script.on("message", my_message_handler)
17script.load()
18
19script.exports.callfun()
1sakura@sakuradeMacBook-Pro:~/gitsource/frida-agent-example/agent$ python frida_demo_rpc_loader.py 
2begin
3find instance :myapplication.example.com.frida_demo.MainActivity@1d4b09d
4result of fun(string):sakura
5end

frida动态修改

即将手机上的app的内容发送到PC上的frida python程序,而后处理后返回给app,而后app再作后续的流程,核心是理解send/recv函数

 1<TextView
2        android:id="@+id/textView"
3        android:layout_width="wrap_content"
4        android:layout_height="wrap_content"
5        android:text="please input username and password"
6        app:layout_constraintBottom_toBottomOf="parent"
7        app:layout_constraintLeft_toLeftOf="parent"
8        app:layout_constraintRight_toRightOf="parent"
9        app:layout_constraintTop_toTopOf="parent" />

10
11
12    <EditText
13        android:id="@+id/editText"
14        android:layout_width="fill_parent"
15        android:layout_height="40dp"
16        android:hint="username"
17        android:maxLength="20"
18        app:layout_constraintBottom_toBottomOf="parent"
19        app:layout_constraintEnd_toEndOf="parent"
20        app:layout_constraintHorizontal_bias="1.0"
21        app:layout_constraintStart_toStartOf="parent"
22        app:layout_constraintTop_toTopOf="parent"
23        app:layout_constraintVertical_bias="0.095" />

24
25    <EditText
26        android:id="@+id/editText2"
27        android:layout_width="fill_parent"
28        android:layout_height="40dp"
29        android:hint="password"
30        android:maxLength="20"
31        app:layout_constraintBottom_toBottomOf="parent"
32        app:layout_constraintTop_toTopOf="parent"
33        app:layout_constraintVertical_bias="0.239" />

34
35    <Button
36        android:id="@+id/button"
37        android:layout_width="100dp"
38        android:layout_height="35dp"
39        android:layout_gravity="right|center_horizontal"
40        android:text="提交"
41        android:visibility="visible"
42        app:layout_constraintBottom_toBottomOf="parent"
43        app:layout_constraintEnd_toEndOf="parent"
44        app:layout_constraintStart_toStartOf="parent"
45        app:layout_constraintTop_toTopOf="parent"
46        app:layout_constraintVertical_bias="0.745" />

 1public class MainActivity extends AppCompatActivity {
2
3    EditText username_et;
4    EditText password_et;
5    TextView message_tv;
6
7    @Override
8    protected void onCreate(Bundle savedInstanceState) {
9        super.onCreate(savedInstanceState);
10        setContentView(R.layout.activity_main);
11
12        password_et = (EditText) this.findViewById(R.id.editText2);
13        username_et = (EditText) this.findViewById(R.id.editText);
14        message_tv = ((TextView) findViewById(R.id.textView));
15
16        this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
17            @Override
18            public void onClick(View v) {
19
20                if (username_et.getText().toString().compareTo("admin") == 0) {
21                    message_tv.setText("You cannot login as admin");
22                    return;
23                }
24                //hook target
25                message_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT));
26
27            }
28        });
29
30    }
31}

先分析问题,个人最终目标是让message_tv.setText能够"发送"username为admin的base64字符串。
那确定是hook TextView.setText这个函数。

 1console.log("Script loaded successfully ");
2Java.perform(function ({
3    var tv_class = Java.use("android.widget.TextView");
4    tv_class.setText.overload("java.lang.CharSequence").implementation = function (x{
5        var string_to_send = x.toString();
6        var string_to_recv;
7        send(string_to_send); // send data to python code
8        recv(function (received_json_object{
9            string_to_recv = received_json_object.my_data
10            console.log("string_to_recv: " + string_to_recv);
11        }).wait(); //block execution till the message is received
12        var my_string = Java.use("java.lang.String").$new(string_to_recv);
13        this.setText(my_string);
14    }
15});
 1import time
2import frida
3import base64
4
5def my_message_handler(message, payload):
6    print(message)
7    print(payload)
8    if message["type"] == "send":
9        print(message["payload"])
10        data = message["payload"].split(":")[1].strip()
11        print( 'message:', message)
12        #data = data.decode("base64")
13        #data = data
14        data = str(base64.b64decode(data))
15        print( 'data:',data)
16        user, pw = data.split(":")
17        print( 'pw:',pw)
18        #data = ("admin" + ":" + pw).encode("base64")
19        data = str(base64.b64encode(("admin" + ":" + pw).encode()))
20        print( "encoded data:", data)
21        script.post({"my_data": data})  # send JSON object
22        print( "Modified data sent")
23
24device = frida.get_usb_device()
25pid = device.spawn(["myapplication.example.com.frida_demo"])
26device.resume(pid)
27time.sleep(1)
28session = device.attach(pid)
29with open("frida_demo2.js"as f:
30    script = session.create_script(f.read())
31script.on("message", my_message_handler)
32script.load()
33input()
 1sakura@sakuradeMacBook-Pro:~/gitsource/frida-agent-example/agent$ python frida_demo_rpc_loader2.py 
2Script loaded successfully 
3{'type''send''payload''Sending to the server :c2FrdXJhOjEyMzQ1Ng==\n'}
4None
5Sending to the server :c2FrdXJhOjEyMzQ1Ng==
6
7message: {'type''send''payload''Sending to the server :c2FrdXJhOjEyMzQ1Ng==\n'}
8data: b'sakura:123456'
9pw: 123456'
10encoded data: b'
YWRtaW46MTIzNDU2Jw=='
11Modified data sent
12string_to_recv: b'
YWRtaW46MTIzNDU2Jw=='
13

参考连接:https://github.com/Mind0xP/Frida-Python-Binding

API List

  • Java.choose(className: string, callbacks: Java.ChooseCallbacks): void
    经过扫描Java VM的堆来枚举className类的live instance。

  • Java.use(className: string): Java.Wrapper<{}>
    动态为className生成JavaScript Wrapper,能够经过调用$new()来调用构造函数来实例化对象。
    在实例上调用$dispose()以对其进行显式清理,或者等待JavaScript对象被gc。

  • Java.perform(fn: () => void): void
    Function to run while attached to the VM.
    Ensures that the current thread is attached to the VM and calls fn. (This isn't necessary in callbacks from Java.)
    Will defer calling fn if the app's class loader is not available yet. Use Java.performNow() if access to the app's classes is not needed.

  • send(message: any, data?: ArrayBuffer | number[]): void
    任何JSON可序列化的值。
    将JSON序列化后的message发送到您的基于Frida的应用程序,并包含(可选)一些原始二进制数据。
    The latter is useful if you e.g. dumped some memory using NativePointer#readByteArray().

  • recv(callback: MessageCallback): MessageRecvOperation
    Requests callback to be called on the next message received from your Frida-based application.
    This will only give you one message, so you need to call recv() again to receive the next one.

  • wait(): void
    堵塞,直到message已经receive而且callback已经执行完毕并返回

Frida动静态结合分析

Objection

  • 参考这篇文章
    实用FRIDA进阶:内存漫游、hook anywhere、抓包

  • objection
    https://pypi.org/project/objection/

objection启动并注入内存

objection -d -g package_name explore

 1sakura@sakuradeMacBook-Pro:~$ objection -d -g com.android.settings explore
2[debug] Agent path is: /Users/sakura/.pyenv/versions/3.7.7/lib/python3.7/site-packages/objection/agent.js
3[debug] Injecting agent...
4Using USB device `Google Pixel`
5[debug] Attempting to attach to process: `com.android.settings`
6[debug] Process attached!
7Agent injected and responds ok!
8
9     _   _         _   _
10 ___| |_|_|___ ___| |_|_|___ ___
11| . | . | | -_|  _|  _| | . |   |
12|___|___| |___|___|_| |_|___|_|_|
13      |___|(object)inject(ion) v1.8.4
14
15     Runtime Mobile Exploration
16        by: @leonjza from @sensepost
17
18[tab] for command suggestions
19com.android.settings on (google: 8.1.0) [usb] #

objection memory

查看内存中加载的module `memory list modules`
1com.android.settings on (google: 8.1.0) [usb] # memory list modules
2Save the output by adding `--json modules.json` to this command
3Name                                             Base          Size                  Path
4-----------------------------------------------  ------------  --------------------  ---------------------------------------------------------------
5app_process64                                    0x64ce143000  32768 (32.0 KiB)      /system/bin/app_process64
6libandroid_runtime.so                            0x7a90bc3000  1990656 (1.9 MiB)     /system/lib64/libandroid_runtime.so
7libbinder.so                                     0x7a9379f000  557056 (544.0 KiB)    /system/lib64/libbinder.so
查看库的导出函数 `memory list exports libssl.so`
 1com.android.settings on (google: 8.1.0) [usb] # memory list exports libssl.so
2Save the output by adding `--json exports.json` to this command
3Type      Name                                                   Address
4--------  -----------------------------------------------------  ------------
5function  SSL_use_certificate_ASN1                               0x7c8ff006f8
6function  SSL_CTX_set_dos_protection_cb                          0x7c8ff077b8
7function  SSL_SESSION_set_ex_data                                0x7c8ff098f4
8function  SSL_CTX_set_session_psk_dhe_timeout                    0x7c8ff0a754
9function  SSL_CTX_sess_accept                                    0x7c8ff063b8
10function  SSL_select_next_proto                                  0x7c8ff06a74
dump内存空间
  • memory dump all 文件名

  • memory dump from_base 起始地址 字节数 文件名

搜索内存空间

Usage: memory search " " (--string) (--offsets-only)

objection android

内存堆搜索实例 `android heap search instances 类名`

在堆上搜索类的实例

 1sakura@sakuradeMacBook-Pro:~$ objection -g myapplication.example.com.frida_demo explore
2Using USB device `Google Pixel`
3Agent injected and responds ok!
4
5[usb] # android heap search instances myapplication.example.com.frida_demo
6.MainActivity
7Class instance enumeration complete for myapplication.example.com.frida_demo.MainActivity
8Handle    Class                                              toString()
9--------  -------------------------------------------------  ---------------------------------------------------------
100x2102    myapplication.example.com.frida_demo.MainActivity  myapplication.example.com.frida_demo.MainActivity@5b1b0af
调用实例的方法 `android heap execute 实例ID 实例方法`
查看当前可用的activity或者service `android hooking list activities/services`
直接启动activity或者服务 `android intent launch_activity/launch_service activity/服务`

android intent launch_activity com.android.settings.DisplaySettings
这个命令比较有趣的是用在若是有些设计的很差,可能就直接绕过了密码锁屏等直接进去。

1com.android.settings on (google: 8.1.0[usb] # android hooking list services
2com.android.settings.SettingsDumpService
3com.android.settings.TetherService
4com.android.settings.bluetooth.BluetoothPairingService
列出内存中全部的类 `android hooking list classes`
在内存中全部已加载的类中搜索包含特定关键词的类。`android hooking search classes display`
1com.android.settings on (google: 8.1.0) [usb] # android hooking search classes display
2[Landroid.icu.text.DisplayContext$Type;
3[Landroid.icu.text.DisplayContext;
4[Landroid.view.Display$Mode;
5android.hardware.display.DisplayManager
6android.hardware.display.DisplayManager$DisplayListener
7android.hardware.display.DisplayManagerGlobal
内存中搜索指定类的全部方法 `android hooking list class_methods 类名`
1com.android.settings on (google: 8.1.0[usb] # android hooking list class_methods java.nio.charset.Charset
2private static java.nio.charset.Charset java.nio.charset.Charset.lookup(java.lang.String)
3private static java.nio.charset.Charset java.nio.charset.Charset.lookup2(java.lang.String)
4private static java.nio.charset.Charset java.nio.charset.Charset.lookupViaProviders(java.lang.String)
在内存中全部已加载的类的方法中搜索包含特定关键词的方法 `android hooking search methods display`

知道名字开始在内存里搜就颇有用

1com.android.settings on (google: 8.1.0[usb] # android hooking search methods display
2Warningsearching all classes may take some time and in some casescrash the target application.
3Continue[y/N]y
4Found 5529 classessearching methods (this may take some time)...
5android.app.ActionBar.getDisplayOptions
6android.app.ActionBar.setDefaultDisplayHomeAsUpEnabled
7android.app.ActionBar.setDisplayHomeAsUpEnabled
hook类的方法(hook类里的全部方法/具体某个方法)
  • android hooking watch class 类名
    这样就能够hook这个类里面的全部方法,每次调用都会被log出来。

  • android hooking watch class 类名 --dump-args --dump-backtrace --dump-return
    在上面的基础上,额外dump参数,栈回溯,返回值

1android hooking watch class xxx.MainActivity --dump-args --dump-backtrace --dump-return
  • android hooking watch class_method 方法名

1//能够直接hook到全部重载
2android hooking watch class_method xxx.MainActivity.fun --dump-args --dump-backtrace --dump-return

grep trick和文件保存

objection log默认是不能用grep过滤的,可是能够经过objection run xxx | grep yyy的方式,从终端经过管道来过滤。
用法以下

1sakura@sakuradeMacBook-Pro:~$ objection -g com.android.settings run memory list modules | grep libc
2Warning: Output is not to a terminal (fd=1).
3libcutils.so                                     0x7a94a1c000  81920 (80.0 KiB)      /system/lib64/libcutils.so
4libc++.so                                        0x7a9114e000  983040 (960.0 KiB)    /system/lib64/libc++.so
5libc.so                                          0x7a9249d000  892928 (872.0 KiB)    /system/lib64/libc.so
6libcrypto.so                                     0x7a92283000  1155072 (1.1 MiB)     /system/lib64/libcrypto.so

有的命令后面能够经过--json logfile来直接保存结果到文件里。
有的能够经过查看.objection文件里的输出log来查看结果。

1sakura@sakuradeMacBook-Pro:~/.objection$ cat *log | grep -i display
2android.hardware.display.DisplayManager
3android.hardware.display.DisplayManager$DisplayListener
4android.hardware.display.DisplayManagerGlobal

案例学习

案例学习case1:《仿VX数据库原型取证逆向分析》

附件连接
android-backup-extractor工具连接


 1sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn$ java -version
2java version "1.8.0_141"
3
4sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn$ java -jar abe-all.jar unpack 1.ab 1.tar
50123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100%
69097216 bytes written to 1.tar.
7
8...
9sakura@sakuradeMacBook-Pro:~/Desktop/lab/alpha/tools/android/frida_learn/apps/com.example.yaphetshan.tencentwelcome$ ls
10Encryto.db _manifest  a          db

装个夜神模拟器玩

 1sakura@sakuradeMacBook-Pro:/Applications/NoxAppPlayer.app/Contents/MacOS$ ./adb connect 127.0.0.1:62001
2* daemon not running. starting it now on port 5037 *
3adb E  5139 141210 usb_osx.cpp:138] Unable to create an interface plug-in (e00002be)
4* daemon started successfully *
5connected to 127.0.0.1:62001
6sakura@sakuradeMacBook-Pro:/Applications/NoxAppPlayer.app/Contents/MacOS$ ./adb shell
7dream2qltechn:# whoami
8root
9dream2qltechn:# uname -a
10Linux localhost 4.0.9#222 SMP PREEMPT Sat Mar 14 18:24:36 HKT 2020 i686



确定仍是先定位目标字符串Wait a Minute,What was happend?
jadx搜索字符串




重点在a()代码里,实际上是根据明文的name和password,而后aVar.a(a2 + aVar.b(a2, contentValues.getAsString("password"))).substring(0, 7)再作一遍复杂的计算并截取7位当作密码,传入getWritableDatabase去解密demo.db数据库。

因此咱们hook一下getWritableDatabase便可。

1frida-ps -U
2...
35662  com.example.yaphetshan.tencentwelcome
4
5
6objection -d -g com.example.yaphetshan.tencentwelcome explore

看一下源码

1package net.sqlcipher.database;
2...
3public abstract class SQLiteOpenHelper {
4    ...
5    public synchronized SQLiteDatabase getWritableDatabase(char[] cArr) {

也能够objection search一下这个method

1...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2[usb] # android hooking search methods getWritableDatabase
2Warningsearching all classes may take some time and in some casescrash the target application.
3Continue[y/N]y
4Found 4650 classessearching methods (this may take some time)...
5
6android.database.sqlite.SQLiteOpenHelper.getWritableDatabase
7...
8net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase

hook一下这个method

 1[usb] # android hooking watch class_method net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase --dump-args --dump-backtrace --dump-return
2- [incoming message] ------------------
3{
4  "payload""Attempting to watch class \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m and method \u001b[32mgetWritableDatabase\u001b[39m.",
5  "type""send"
6}
7- [./incoming message] ----------------
8(agent) Attempting to watch class net.sqlcipher.database.SQLiteOpenHelper and method getWritableDatabase.
9- [incoming message] ------------------
10{
11  "payload""Hooking \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m.\u001b[92mgetWritableDatabase\u001b[39m(\u001b[31mjava.lang.String\u001b[39m)",
12  "type""send"
13}
14- [./incoming message] ----------------
15(agent) Hooking net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(java.lang.String)
16- [incoming message] ------------------
17{
18  "payload""Hooking \u001b[32mnet.sqlcipher.database.SQLiteOpenHelper\u001b[39m.\u001b[92mgetWritableDatabase\u001b[39m(\u001b[31m[C\u001b[39m)",
19  "type""send"
20}
21- [./incoming message] ----------------
22(agent) Hooking net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase([C)
23- [incoming message] ------------------
24{
25  "payload""Registering job \u001b[94mjytq1qeyllq\u001b[39m. Type: \u001b[92mwatch-method for: net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase\u001b[39m",
26  "type""send"
27}
28- [./incoming message] ----------------
29(agent) Registering job jytq1qeyllq. Type: watch-method for: net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase
30...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2) [usb] #

hook好以后再打开这个apk





 1(agent[1v488x28gcs] Called net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(java.lang.String)
2...
3(agent[1v488x28gcs] Backtrace:
4    net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(Native Method)
5    com.example.yaphetshan.tencentwelcome.MainActivity.a(MainActivity.java:55)
6    com.example.yaphetshan.tencentwelcome.MainActivity.onCreate(MainActivity.java:42)
7    android.app.Activity.performCreate(Activity.java:6692)
8...
9(agent[1v488x28gcs] Arguments net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(ae56f99)
10
11...
12...mple.yaphetshan.tencentwelcome on (samsung: 7.1.2[usb] # jobs list
13Job ID         Hooks  Type
14-----------  -------  -----------------------------------------------------------------------------
151v488x28gcs        2  watch-method fornet.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase

找到参数ae56f99
剩下的就是用这个密码去打开加密的db。



而后base64解密一下就行了。



还有一种策略是主动调用,基于数据流的主动调用分析是很是有意思的。
即本身去调用a函数以触发getWritableDatabase的数据库解密。
先寻找a所在类的实例,而后hook getWritableDatabase,最终主动调用a。
这里幸运的是a没有什么奇奇怪怪的参数须要咱们传入,主动调用这种策略在循环注册等地方可能就会有需求8.

 1 [usb] # android heap search instances com.example.yaphetshan.tencentwelcome.MainActivity
2Class instance enumeration complete for com.example.yaphetshan.tencentwelcome.MainActivity
3Handle    Class                                               toString()
4--------  --------------------------------------------------  ----------------------------------------------------------
50x20078a  com.example.yaphetshan.tencentwelcome.MainActivity  com.example.yaphetshan.tencentwelcome.MainActivity@1528f80
6
7 [usb] # android hooking watch class_method net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase --dump-args --dump-backtrace --dump-return
8
9[usb] # android heap execute 0x20078a a
10
11(agent) [taupgwkum4h] Arguments net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(ae56f99)

案例学习case2:主动调用爆破密码

附件连接



由于直接找Unfortunately,note the right PIN :(找不到,多是把字符串藏在什么资源文件里了。
review代码以后找到校验的核心函数,逻辑就是将input编码一下以后和密码比较,这确定是什么不可逆的加密。

 1    public static boolean verifyPassword(Context context, String input) {
2        if (input.length() != 4) {
3            return false;
4        }
5        byte[] v = encodePassword(input);
6        byte[] p = "09042ec2c2c08c4cbece042681caf1d13984f24a".getBytes();
7        if (v.length != p.length) {
8            return false;
9        }
10        for (int i = 0; i < v.length; i++) {
11            if (v[i] != p[i]) {
12                return false;
13            }
14        }
15        return true;
16    }

这里就爆破一下密码。

1frida-ps -U | grep qualification
27660  org.teamsik.ahe17.qualification.easy
3
4frida -U org.teamsik.ahe17.qualification.easy -l force.js
 1function main({
2    Java.perform(function x({
3        console.log("In Java perform")
4        var verify = Java.use("org.teamsik.ahe17.qualification.Verifier")
5        var stringClass = Java.use("java.lang.String")
6        var p = stringClass.$new("09042ec2c2c08c4cbece042681caf1d13984f24a")
7        var pSign = p.getBytes()
8        // var pStr = stringClass.$new(pSign)
9        // console.log(parseInt(pStr))
10        for (var i = 999; i < 10000; i++){
11            var v = stringClass.$new(String(i))
12            var vSign = verify.encodePassword(v)
13            if (parseInt(stringClass.$new(pSign)) == parseInt(stringClass.$new(vSign))) {
14                console.log("yes: " + v)
15                break
16            }
17            console.log("not :" + v)
18        }
19    })
20}
21setImmediate(main)
1...
2not :9080
3not :9081
4not :9082
5yes: 9083

这里注意parseInt





                关注小白技术社,开启爬虫工程师的app逆向之路。

更多推荐

爬虫工程师的app逆向之路-刷机到板砖

某音的逆向学习视频

饿了么、抖音pojie-flask+frida-rpc




恭喜你完成Frida三部曲的第一部,点个在看告诉你们吧

本文分享自微信公众号 - 小白技术社(xbjss123)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索