Frida高级特性

2020-12-14 15:21:01 浏览数 (1)

多台设备

连接多台设备还是很简单的,使用Frida作者oleavr(很多人称他是大胡子,以后就用这个称呼了)为我门提供的python binding功能。当然前提是相应设备的frida-server已经开了。

根据设备id就可以获取相应设备的device。使用的函数是get_device。

互联互通

互联互通是指把app中捕获的内容传输到电脑上,电脑上处理结束后再发回给app继续处理。看似很简单的一个功能,目前却仅有Frida可以实现。后面的这句话我不清楚是否真假,就我所知道的,它是真的,不过通过这句话也能感受到Firda的强大。

预备知识

•recv([type, ]callback): request callback to be called on the next message received from your Frida-based application. Optionally type may be specified to only receive a message where the type field is set to type. This will only give you one message, so you need to call recv() again to receive the next one.•send(message[, data]): send the JavaScript object message to your Frida-based application (it must be serializable to JSON). If you also have some raw binary data that you’d like to send along with it, e.g. you dumped some memory using NativePointer#readByteArray, then you may pass this through the optional data argument. This requires it to either be an ArrayBuffer or an array of integers between 0 and 255.

App

页面

简单的登陆页面如下图:

功能

admin账户不能用来登陆,这个只是在前台进行的校验。对账户密码进行base64编码发送给服务器。

源代码

功能代码:

代码语言:javascript复制
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {


    EditText username_et;
    EditText password_et;
    TextView message_tv;

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      //  super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);

        // Example of a call to a native method
        //TextView tv = findViewById(R.id.sample_text);
        //tv.setText(stringFromJNI());
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        password_et = (EditText) this.findViewById(R.id.password);
        username_et = (EditText) this.findViewById(R.id.username);
        message_tv = ((TextView) findViewById(R.id.textView));

        this.findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (username_et.getText().toString().compareTo("admin") == 0) {
                    message_tv.setText("You cannot login as admin");
                    return;
                }
                //我们hook的目标就在这里
                message_tv.setText("Sending to the server :"   Base64.encodeToString((username_et.getText().toString()   ":"   password_et.getText().toString()).getBytes(), Base64.DEFAULT));

            }
        });

    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

布局代码:

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:layout_width="match_parent">


    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="用户名"
        android:textSize="24sp" />

    <EditText
        android:id="@ id/username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入您的用户名"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="密码"
        android:textSize="24sp" />

    <EditText
        android:id="@ id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入您的密码"/>

   <TextView
        android:id = "@ id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入用户名和密码"
        android:textAlignment="center"
        android:textSize="24sp" />

    <Button
        android:id="@ id/login"
        android:layout_height="60dp"
        android:layout_width="wrap_content"
        android:text="登录"
        android:layout_gravity="center"
        android:textAlignment="center"
        android:textSize="18sp" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

布局类介绍: A ConstraintLayout is a android.view.ViewGroup which allows you to position and size widgets in a flexible way. LinearLayout is a layout that arranges other views either horizontally in a single column or vertically in a single row.

通过hook修改逻辑

(1)实现能够让admin登陆,绕过前台的校验。 (2)将setText中文本发送给电脑端,通过电脑端对数据进行修改,然后把修改后的内容再次发送给手机。也就是互联互通。

python代码(python3.8):

代码语言:javascript复制
import time
import frida
import sys
import base64

def my_message_handler(message, payload):
    print(message)
    print(payload)
    if message["type"] == "send":
        print(message["payload"])
        data = message["payload"].split(":")[1].strip()
        print('message:', message)
        print("burning data:" data)
        data = str(base64.b64decode(data), "utf-8")# 解码
        user, pw = data.split(":") # 提取用户名和密码
        data  =  str(base64.b64encode(("admin"   ":"   pw).encode("utf-8")), "utf-8") # 组成新的组合并编码,这是使用admin登陆
        print("encoded data:", data)
        script.post({"my_data": data})  # 将JSON对象发送回去
        print("Modified data sent")

device = frida.get_usb_device()
pid = device.spawn(["com.example.myapplication"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open("conn.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)  # 注册消息处理函数
script.load()
sys.stdin.read()

conn.js代码:

代码语言:javascript复制
Java.perform(function () {
    var tv_class = Java.use("android.widget.TextView");
    tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
        var string_to_send = x.toString();
        var string_to_recv;
        console.log("send" string_to_send);
        send(string_to_send); // 将数据发送给PC端
        recv(function (received_json_object) {
            string_to_recv = received_json_object.my_data
            console.log("string_to_recv: "   string_to_recv);
        }).wait(); //收到数据之后,再执行下去
        var string = Java.use("java.lang.String");
        string_to_recv = string.$new(string_to_recv);//将收到的数据转化为String类型
        return this.setText(string_to_recv);
    }
});

又很多数据通过JavaScript处理起来可能比较麻烦,那么可以考虑把数据发送给PC端,在PC端用python对数据处理就容易多了,处理完成之后再把数据发送回去。

远程调用(RPC)

在 Frida API使用(1) 对RPC进行了介绍。在文章中把js和python代码写在了一个文件中,最好是把他们分开写,这里不再举例。

这个定义还是很重要的: rpc.exports is empty object that you can either replace or insert into to expose an RPC-style API to your application. The key specifies the method name and the value is your exported function.

写在最后

再前面的apk中,通过hook技术很容易绕过了前台的校验,由此可见,前台的校验是多么的不靠谱.这种校验最好都放在后台,

公众号

更多Frida的内容,欢迎关注我的微信公众号:无情剑客.

0 人点赞