连接多台设备还是很简单的,使用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.
简单的登陆页面如下图:
admin账户不能用来登陆,这个只是在前台进行的校验。对账户密码进行base64编码发送给服务器。
功能代码:
packagecom.example.myapplication;importandroidx.appcompat.app.AppCompatActivity;
importandroid.os.Bundle;
importandroid.util.Base64;
importandroid.view.View;
importandroid.widget.EditText;
importandroid.widget.TextView;
publicclass MainActivity extends AppCompatActivity {
EditTextusername_et;
EditTextpassword_et;
TextViewmessage_tv;
// Used to loadthe 'native-lib'libraryonapplication 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(newView.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("Sendingtotheserver:"+ Base64.encodeToString((username_et.getText().toString() + ":"+ password_et.getText().toString()).getBytes(), Base64.DEFAULT));
}
});
}
/**
* A native method that isimplemented bythe 'native-lib'native library,
* which ispackaged with thisapplication.
*/
public native String stringFromJNI();
}
布局代码:
<?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.
(1)实现能够让admin登陆,绕过前台的校验。 (2)将setText中文本发送给电脑端,通过电脑端对数据进行修改,然后把修改后的内容再次发送给手机。也就是互联互通。
python代码(python3.8):
importtime
importfrida
importsys
importbase64def my_message_handler(message, payload):
print(message)
print(payload)
ifmessage["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)
withopen("conn.js") asf:
script= session.create_script(f.read())
script.on("message", my_message_handler) # 注册消息处理函数
script.load()
sys.stdin.read()
conn.js代码:
Java.perform(function () {
vartv_class = Java.use("android.widget.TextView");
tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
varstring_to_send = x.toString();
varstring_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(); //收到数据之后,再执行下去
varstring= Java.use("java.lang.String");
string_to_recv = string.$new(string_to_recv);//将收到的数据转化为String类型
returnthis.setText(string_to_recv);
}
});
又很多数据通过JavaScript处理起来可能比较麻烦,那么可以考虑把数据发送给PC端,在PC端用python对数据处理就容易多了,处理完成之后再把数据发送回去。
在 Frida API使用(1)对RPC进行了介绍。在文章中把js和python代码写在了一个文件中,最好是把他们分开写,这里不再举例。
这个定义还是很重要的: rpc.exportsis empty objectthat 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的内容,欢迎关注我的微信公众号:无情剑客.