当前位置: 代码迷 >> 综合 >> 一篇文章带你领悟 Frida 的精髓(基于安卓8.1)
  详细解决方案

一篇文章带你领悟 Frida 的精髓(基于安卓8.1)

热度:19   发布时间:2024-01-31 00:03:49.0

 

转载(一篇文章带你领悟Frida的精髓(基于安卓8.1)):https://www.freebuf.com/articles/system/190565.html

《Frida操作手册》:https://github.com/hookmaster/frida-all-in-one

Frida github 地址:https://github.com/dweinstein/awesome-frida

 

 

前言

 

前阵子受《Xposed模块编写的那些事》这篇文章的帮助很大,感觉有必要写一篇文章来回馈freebuf社区。现在最火爆的又是frida,该框架从Java层hook到Native层hook无所不能,虽然持久化还是要依靠Xposed和hookzz等开发框架,但是 frida 的动态和灵活对逆向以及自动化逆向的帮助非常巨大。

 

 

frida是啥?

 

首先,frida 是啥,github目录 Awesome Frida ( https://github.com/dweinstein/awesome-frida )这样介绍 frida 的:

Frida is Greasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript into native apps that run on Windows, Mac, Linux, iOS and Android. Frida is an open source software.

frida是平台原生appGreasemonkey,说的专业一点,就是一种动态插桩工具,可以插入一些代码到原生app的内存空间去,(动态地监视和修改其行为),这些原生平台可以是WinMacLinuxAndroid或者iOS。而且frida还是开源的。

Greasemonkey可能大家不明白,它其实就是firefox的一套插件体系,使用它编写的脚本可以直接改变firefox对网页的编排方式,实现想要的任何功能。而且这套插件还是外挂的,非常灵活机动。

frida也是一样的道理。

 

 

frida为什么这么火?

 

动静态修改内存实现作弊一直是刚需,比如金山游侠,本质上frida做的跟它是一件事情。原则上是可以用frida把金山游侠,包括CheatEngine等“外挂”做出来的。

当然,现在已经不是直接修改内存就可以高枕无忧的年代了。大家也不要这样做,做外挂可是违法行为。

在逆向的工作上也是一样的道理,使用frida可以“看到”平时看不到的东西。出于编译型语言的特性,机器码在CPU和内存上执行的过程中,其内部数据的交互和跳转,对用户来讲是看不见的。当然如果手上有源码,甚至哪怕有带调试符号的可执行文件包,也可以使用gbdlldb等调试器连上去看。

那如果没有呢?如果是纯黑盒呢?又要对app进行逆向和动态调试、甚至自动化分析以及规模化收集信息的话,我们需要的是细粒度的流程控制和代码级的可定制体系,以及不断对调试进行动态纠正和可编程调试的框架,这就是frida

frida使用的是pythonJavaScript等“胶水语言”也是它火爆的一个原因,可以迅速将逆向过程自动化,以及整合到现有的架构和体系中去,为你们发布“威胁情报”、“数据平台”甚至“AI风控”等产品打好基础。

 

 

frida 实操环境

 

frida 分客户端环境和服务端环境。在客户端我们可以编写 Python 代码,用于连接远程设备,提交要注入的代码到远程,接受服务端的发来的消息等。在服务端,我们需要用 Javascript 代码注入到目标进程,操作内存数据,给客户端发送消息等操作。我们也可以把客户端理解成控制端,服务端理解成被控端。
假如我们要用PC来对Android设备上的某个进程进行操作,那么PC就是客户端,而Android设备就是服务端。

 

准备 frida 服务端环境

服务端在Android平台测试。服务端环境准备步骤如下:

根据自己的平台下载frida服务端并解压:https://github.com/frida/frida/releases

执行以下命令将服务端推到手机的/data/local/tmp目录:adb push frida-server /data/local/tmp/frida-server

执行以下命令修改frida-server文件权限:adb shell chmod 777 /data/local/tmp/frida-server

注:Windows系统执行命令可以在CMD中进行;Linux和MacOS执行命令可以在终端中进行。adb是Android一个调试工具,具体安装方法不是本文的重点。

 

准备 客户端环境

在 PC 上安装 Python 的运行环境,安装完成后执行下面的命令安装 frida:pip install frida

frida 命令帮助:

kali@kali:~$ frida -h
Usage: frida [options] targetOptions:--version             show program's version number and exit-h, --help            show this help message and exit-D ID, --device=ID    connect to device with the given ID-U, --usb             connect to USB device-R, --remote          connect to remote frida-server-H HOST, --host=HOST  connect to remote frida-server on HOST-f FILE, --file=FILE  spawn FILE-F, --attach-frontmostattach to frontmost application-n NAME, --attach-name=NAMEattach to NAME-p PID, --attach-pid=PIDattach to PID--stdio=inherit|pipe  stdio behavior when spawning (defaults to “inherit”)--aux=option          set aux option when spawning, such as “uid=(int)42”(supported types are: string, bool, int)--runtime=duk|v8      script runtime to use--debug               enable the Node.js compatible script debugger--squelch-crash       if enabled, will not dump crash report to console-O FILE, --options-file=FILEtext file containing additional command line options-l SCRIPT, --load=SCRIPTload SCRIPT-P PARAMETERS_JSON, --parameters=PARAMETERS_JSONparameters as JSON, same as Gadget-C CMODULE, --cmodule=CMODULEload CMODULE-c CODESHARE_URI, --codeshare=CODESHARE_URIload CODESHARE_URI-e CODE, --eval=CODE  evaluate CODE-q                    quiet mode (no prompt) and quit after -l and -e--no-pause            automatically start main thread after startup-o LOGFILE, --output=LOGFILEoutput to log file--exit-on-error       exit with code 1 after encountering any exception inthe SCRIPT
kali@kali:~$ 

如果将一个脚本注入到Android目标进程:frida -U -l myhook.js com.xxx.xxxx

参数解释:

  • -U 指定对USB设备操作
  • -l 指定加载一个Javascript脚本
  • 最后指定一个进程名,如果想指定进程 pid,用 -p 选项。正在运行的进程可以用 frida-ps -U 命令查看

frida 运行过程中,执行 %resume 重新注入,执行 %reload 来重新加载脚本;执行 exit 结束脚本注入

 

kali linux 的原因是工具很全面,否则 python 和 node 的各种权限、环境和依赖实在是烦。

主机:

Host:Macbook Air CPU: i5 Memory:8GSystem:Kali Linux 2018.4 (Native,非虚拟机)

客户端:

client:Nexus 6 shamu CPU:Snapdragon 805 Mem:3GSystem:lineage-15.1-20181123-NIGHTLY-shamu,android 8.1

用 lineage 因为它有便利的网络ADB调试,可以省掉一个usb数据线连接的过程。(虽然真实的原因是没钱买新设备,Nexus 6官方只支持到7.1.1,想上8.1只有lineage一个选择。)记得需要刷进去一个lineage的 su包,获取root权限,frida是需要在root权限下运行的。

首先到官网下载一个platform-tools的linux版本——SDK Platform-Tools for Linux,下载解压之后可以直接运行里面的二进制文件,当然也可以把路径加到环境里去。这样adbfastboot命令就有了。

然后再将 frida-server(下载地址: https://github.com/frida/frida/releases )下来,拷贝到安卓机器里去,使用 root用户跑起来,保持 adb 的连接不要断开。

$ ./adb root                               # might be required
$ ./adb push frida-server /data/local/tmp/
$ ./adb shell "chmod 755 /data/local/tmp/frida-server"
$ ./adb shell "/data/local/tmp/frida-server &"

最后在 kali linux 里安装好 frida 即可,在 kali 里安装 frida 真是太简单了,一句话命令即可,保证不出错。(可能会需要先安装 pip,也是一句话命令:curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

pip install frida-tools

然后用 frida-ps -U 命令连上去,就可以看到正在运行的进程了。

 

 

基本能力 1:hook参数、修改结果

 

先自己写个 app

package com.roysue.demo02;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;public class MainActivity extends AppCompatActivity {@Overrideprotected 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,30);}}void fun(int x , int y ){Log.d("Sum" , String.valueOf(x+y));}}

原理上很简单,就是间隔一秒在控制台输出一下fun(50,30)函数的结果,fun()这个函数的作用是求和。那最终结果在控制台如下所示。

$ adb logcat |grep Sum
11-26 21:26:23.234  3245  3245 D Sum     : 80
11-26 21:26:24.234  3245  3245 D Sum     : 80
11-26 21:26:25.235  3245  3245 D Sum     : 80
11-26 21:26:26.235  3245  3245 D Sum     : 80
11-26 21:26:27.236  3245  3245 D Sum     : 80
11-26 21:26:28.237  3245  3245 D Sum     : 80
11-26 21:26:29.237  3245  3245 D Sum     : 80

图示:

现在我们来写一段js代码,并用 frida-server 将这段代码加载到 com.roysue.demo02 中去,执行其中的 hook 函数。

s1.js 代码:

console.log("Script loaded successfully ");
Java.perform(function x() {console.log("Inside java perform function");//定位类var my_class = Java.use("com.roysue.demo02.MainActivity");console.log("Java.Use.Successfully!");//定位类成功!//在这里更改类的方法的实现(implementation)my_class.fun.implementation = function(x,y){//打印替换前的参数console.log( "original call: fun("+ x + ", " + y + ")");//把参数替换成2和5,依旧调用原函数var ret_value = this.fun(2, 5);return ret_value;}
});

然后我们在 kali 主机上使用一段python脚本,将这段 js 脚本“传递”给安卓系统里正在运行的frida-server

loader.py 代码:

import time
import frida# 连接安卓机上的frida-server
device = frida.get_usb_device()
# 启动`demo02`这个app
pid = device.spawn(["com.roysue.demo02"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载s1.js脚本
with open("s1.js") as f:script = session.create_script(f.read())
script.load()# 脚本会持续运行等待输入
raw_input()

然后得保证frida-server正在运行,方法可以是在kali主机输入frida-ps -U命令,如果安卓机上的进程出现了,则frida-server运行良好。

还需要保证selinux是关闭的状态,可以在adb shell里,su -获得root权限之后,输入setenforce 0命令来获得,在Settings→About Phone→SELinux status里看到Permissive,说明selinux关闭成功。

然后在kali主机上输入python loader.js,可以观察到安卓机上com.roysue.demo02这个app马上重启了。然后$ adb logcat|grep Sum里的内容也变了。

说明脚本执行成功了,代码也插到com.roysue.demo02这个包里去,并且成功执行了,s1.js里的代码成功执行了,并且把交互结果传回了kali主机上。

 

 

基本能力 2:参数构造、方法重载、隐藏函数的处理

 

我们现在把 app 的代码稍微写复杂一点点:

package com.roysue.demo02;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;public class MainActivity extends AppCompatActivity {private String total = "@@@###@@@";@Overrideprotected 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,30);Log.d("ROYSUE.string" , fun("LoWeRcAsE Me!!!!!!!!!"));}}void fun(int x , int y ){Log.d("ROYSUE.Sum" , String.valueOf(x+y));}String fun(String x){total +=x;return x.toLowerCase();}String secret(){return total;}
}

app 运行起来后在使用 logcat 打印出来的日志如下:

$ adb logcat |grep ROYSUE
11-26 22:22:35.689  3051  3051 D ROYSUE.Sum: 80
11-26 22:22:35.689  3051  3051 D ROYSUE.string: lowercase me!!!!!!!!!
11-26 22:22:36.695  3051  3051 D ROYSUE.Sum: 80
11-26 22:22:36.696  3051  3051 D ROYSUE.string: lowercase me!!!!!!!!!
11-26 22:22:37.696  3051  3051 D ROYSUE.Sum: 80
11-26 22:22:37.696  3051  3051 D ROYSUE.string: lowercase me!!!!!!!!!
11-26 22:22:38.697  3051  3051 D ROYSUE.Sum: 80
11-26 22:22:38.697  3051  3051 D ROYSUE.string: lowercase me!!!!!!!!!
11-26 22:22:39.697  3051  3051 D ROYSUE.Sum: 80
11-26 22:22:39.698  3051  3051 D ROYSUE.string: lowercase me!!!!!!!!!

图示:

 

可以看到 fun()方法有了重载,在参数是两个 int 的情况下,返回两个int之和。

在参数为String类型之下,则返回字符串的小写形式。

另外,secret()函数为隐藏方法,在app里没有被直接调用。

这时候如果我们直接使用上面的js脚本和loader.js来加载的话,肯定会崩溃。

为了看到崩溃的信息,我们对loader.js做一些处理。

import time
import fridadef my_message_handler(message, payload):  # 定义错误处理print(message)print(payload)# 连接安卓机上的 frida-server
device = frida.get_usb_device()
# 启动`demo02`这个app
pid = device.spawn(["com.example.demo2"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载s1.js脚本
with open("s1.js", encoding='utf-8') as f:script = session.create_script(f.read())script.on("message", my_message_handler)  # 调用错误处理
script.load()# 脚本会持续运行等待输入
input()

再运行$ python loader.py的话,就会看到如下的错误信息返回:

$ python loader.py
Script loaded successfully
Inside java perform function
Java.Use.Successfully!
{u'columnNumber': 1, u'description': u"Error: fun(): has more than one overload, use .overload(<signature>) to choose from:\n\t.overload('java.lang.String')\n\t.overload('int', 'int')", u'fileName': u'frida/node_modules/frida-java/lib/class-factory.js', u'lineNumber': 2233, u'type': u'error', u'stack': u"Error: fun(): has more than one overload, use .overload(<signature>) to choose from:\n\t.overload('java.lang.String')\n\t.overload('int', 'int')\n    at throwOverloadError (frida/node_modules/frida-java/lib/class-factory.js:2233)\n    at frida/node_modules/frida-java/lib/class-factory.js:1468\n    at x (/script1.js:14)\n    at frida/node_modules/frida-java/lib/vm.js:43\n    at M (frida/node_modules/frida-java/index.js:347)\n    at frida/node_modules/frida-java/index.js:299\n    at frida/node_modules/frida-java/lib/vm.js:43\n    at frida/node_modules/frida-java/index.js:279\n    at /script1.js:15"}
None

可以看出是一个 throwOverloadError,这时候就是因为我们没有处理重载,造成的重载处理错误。这个时候就需要我们来处理重载了,在js脚本中处理重载是这样写的:

my_class.fun.overload("int" , "int").implementation = function(x,y){
...
my_class.fun.overload("java.lang.String").implementation = function(x){

其中参数均为两个int的情况下,上一节已经讲过了。参数为String类的时候,由于String类不是Java基本数据类型,而是java.lang.String 类型,所以在替换参数的构造上,需要花点心思。

var string_class = Java.use("java.lang.String"); //获取String类型my_class.fun.overload("java.lang.String").implementation = function(x){console.log("*************************************");var my_string = string_class.$new("My TeSt String#####"); //new一个新字符串console.log("Original arg: " + x);var ret =  this.fun(my_string); // 用新的参数替换旧的参数,然后调用原函数获取结果console.log("Return value: "+ ret);console.log("*************************************");return ret;
};

这样我们对于重载函数的处理就算是ok了。我们到实验里来看下:

$ python loader.py
Script loaded successfully
Inside java perform function
original call: fun(50, 30)
*************************************
Original arg: LoWeRcAsE Me!!!!!!!!!
Return value: my test string#####
*************************************
original call: fun(50, 30)
*************************************
Original arg: LoWeRcAsE Me!!!!!!!!!
Return value: my test string#####
*************************************
original call: fun(50, 30)
*************************************
Original arg: LoWeRcAsE Me!!!!!!!!!
Return value: my test string#####
*************************************

然后logcat打出来的结果也变了。

$ adb logcat |grep ROYSUE
11-26 22:23:29.597  3244  3244 D ROYSUE.Sum: 7
11-26 22:23:29.673  3244  3244 D ROYSUE.string: my test string#####
11-26 22:23:30.689  3244  3244 D ROYSUE.Sum: 7
11-26 22:23:30.730  3244  3244 D ROYSUE.string: my test string#####
11-26 22:23:31.740  3244  3244 D ROYSUE.Sum: 7
11-26 22:23:31.789  3244  3244 D ROYSUE.string: my test string#####
11-26 22:23:32.797  3244  3244 D ROYSUE.Sum: 7
11-26 22:23:32.833  3244  3244 D ROYSUE.string: my test string#####

最后再说一下隐藏方法的调用,frida 对其的处理办法跟 Xposed 是非常像的,Xposed 使用的是XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);方法,直接 findClass

其实 frida 也非常类似,也是使用的直接到内存里去寻找的方法,也就是 Java.choose(className, callbacks)函数,通过类名触发回掉函数。

Java.choose("com.roysue.demo02.MainActivity" , {onMatch : function(instance){ //该类有多少个实例,该回调就会被触发多少次console.log("Found instance: "+instance);console.log("Result of secret func: " + instance.secret());},onComplete:function(){}
});

最终运行效果如下:

$ python loader.py
Script loaded successfully
Inside java perform function
Found instance: com.roysue.demo02.MainActivity@92d5deb
Result of secret func: @@@###@@@
original call: fun(50, 30)
*************************************
Original arg: LoWeRcAsE Me!!!!!!!!!
Return value: my test string#####
*************************************
original call: fun(50, 30)
*************************************
Original arg: LoWeRcAsE Me!!!!!!!!!
Return value: my test string#####
*************************************
original call: fun(50, 30)

这样隐藏方法也被调用起来了。

 

 

中级能力:远程调用

 

上面我们在安卓机器上使用js脚本调用了隐藏函数secret(),它在app内虽然没有被任何地方调用,但是仍然被我们的脚本“找到”并且“调用”了起来

这一小节我们要实现的是,不仅要在跑在安卓机上的js脚本里调用这个函数,还要可以在kali主机上的py脚本里,直接调用这个函数。

也就是使用 frida 提供的 RPC 功能(Remote Procedure Call)。

安卓app不需要有任何修改,这次我们要修改的是js脚本和py脚本。

s3.js 代码:

console.log("Script loaded successfully ");function callSecretFun() { //定义导出函数Java.perform(function () { //找到隐藏函数并且调用Java.choose("com.roysue.demo02.MainActivity", {onMatch: function (instance) {console.log("Found instance: " + instance);console.log("Result of secret func: " + instance.secret());},onComplete: function () { }});});
}
rpc.exports = {// 把callSecretFun函数导出为callsecretfunction符号,// 导出名不可以有大写字母或者下划线callsecretfunction: callSecretFun 
};

然后我们可以在 kali 主机的 py 脚本里直接调用该函数:

loader3.py 代码:

import time
import fridadef my_message_handler(message, payload):print(message)print(payload)device = frida.get_usb_device()
pid = device.spawn(["com.example.demo2"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open("s3.js", encoding='utf-8') as f:script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()command = ""
while 1 == 1:command = input("Enter command:\n\t1: Exit\n\t2: Call secret function\nchoice:")if command == "1":breakelif command == "2":  # 在这里调用script.exports.callsecretfunction()

然后在kali主机上我们就可以看到以下的输出:

$ python loader3.py
Script loaded successfully
Enter command:
1: Exit
2: Call secret function
choice:2
Found instance: com.roysue.demo02.MainActivity@2eacd80
Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!
Enter command:
1: Exit
2: Call secret function
choice:2
Found instance: com.roysue.demo02.MainActivity@2eacd80
Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!
Enter command:
1: Exit
2: Call secret function
choice:2
Found instance: com.roysue.demo02.MainActivity@2eacd80
Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!
Enter command:
1: Exit
2: Call secret function
choice:1

这样我们就实现了在kali主机上直接调用安卓app内部的函数的能力。

 

 

高级能力:互联互通、动态修改

 

最后我们要实现的功能是,我们不仅仅可以在kali主机上调用安卓app里的函数。我们还可以把数据从安卓app里传递到kali主机上,在主机上进行修改,再传递回安卓app里面去。

我们编写这样一个app,其中最核心的地方在于判断用户是否为admin,如果是,则直接返回错误,禁止登陆。如果不是,则把用户和密码上传到服务器上进行验证。

package com.roysue.demo04;import android.support.v7.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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);password_et = (EditText) this.findViewById(R.id.editText2);username_et = (EditText) this.findViewById(R.id.editText);message_tv = ((TextView) findViewById(R.id.textView));this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (username_et.getText().toString().compareTo("admin") == 0) {message_tv.setText("You cannot login as admin");return;}//hook targetmessage_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT));}});}
}

最终跑起来之后,效果就是这样。

我们的目标就是在kali主机上“得到”输入框输入的内容,并且修改其输入的内容,并且“传输”给安卓机器,使其通过验证。也就是说,我们哪怕输入admin的账户和密码,也可以绕过本地校验,进行登陆的操作。

所以最终安卓端的js代码的逻辑就是,截取输入,传输给kali主机,暂停执行,得到kali主机传回的数据之后,继续执行。形成代码如下:

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;send(string_to_send); // 将数据发送给kali主机的python代码recv(function (received_json_object) {string_to_recv = received_json_object.my_dataconsole.log("string_to_recv: " + string_to_recv);}).wait(); //收到数据之后,再执行下去return this.setText(string_to_recv);}
});

kali主机端的流程就是,将接受到的 JSON 数据解析,提取出其中的密码部分,然后将用户名替换成 admin,这样就实现了将admin 和 pw 发送给“服务器”的结果。

import time
import fridadef my_message_handler(message, payload):print messageprint payloadif message["type"] == "send":print message["payload"]data = message["payload"].split(":")[1].strip()print 'message:', messagedata = data.decode("base64")user, pw = data.split(":")data = ("admin" + ":" + pw).encode("base64")print "encoded data:", datascript.post({"my_data": data})  # 将JSON对象发送回去print "Modified data sent"device = frida.get_usb_device()
pid = device.spawn(["com.roysue.demo04"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open("s4.js") as f:script = session.create_script(f.read())
script.on("message", my_message_handler)  # 注册消息处理函数
script.load()
raw_input()

我们只要输入任意用户名(非admin)+密码,非admin的用户名可以绕过compareTo校验,然后frida会帮助我们将用户名改成admin,最终就是 admin:pw 的组合发送到服务器。

$ python loader4.py
Script loaded successfully
{u'type': u'send', u'payload': u'Sending to the server :YWFhYTpiYmJi\n'}
None
Sending to the server :YWFhYTpiYmJimessage: {u'type': u'send', u'payload': u'Sending to the server :YWFhYTpiYmJi\n'}
data: aaaa:bbbb
pw: bbbb
encoded data: YWRtaW46YmJiYg==Modified data sent
string_to_recv: YWRtaW46YmJiYg==

动态修改输入内容就这样实现了。