在Android逆向:frida学习(1)中已经介绍了frida的安装与基本使用方法,并且以一道2015年的SECCON CTF例题展示了如何使用它的hook功能。下面还是以一道CTF题目来对frida的使用进行进一步说明
题目分析
首先看一下Mainifest文件,发现有两个activity
在LaucherActivity中,关键代码如下:
public void verifyClick(View arg16) {
String v6 = this.findViewById(0x7F0B005D).getText().toString();
try {
InputStream v5 = new URL("http://broken.license.server.com/query?license=" + v6).openConnection().getInputStream();
StringBuilder v9 = new StringBuilder();
byte[] v1 = new byte[0];
while(v5.read(v1) > 0) {
v9.append(v1);
}
String v8 = v9.toString();
if(v8.equals("LICENSEKEYOK")) {
String v0 = new String(MainActivity.xor(this.getMac().getBytes(), v8.getBytes()));
SharedPreferences$Editor v4 = this.getApplicationContext().getSharedPreferences("preferences", 0).edit();
v4.putString("KEY", v0);
v4.commit();
new Builder(((Context)this)).setTitle("Activation successful").setMessage("Activation successful").setIcon(0x1080027).show();
return;
}
new Builder(((Context)this)).setTitle("Invalid license!").setMessage("Invalid license!").setIcon(0x1080027).show();
}
catch(Exception v3) {
new Builder(((Context)this)).setTitle("Error occured").setMessage("Server unreachable").setNeutralButton("OK", null).setIcon(0x1080027).show();
}
}
该函数会向一个不存在的url发送我们填写的数据,只有收到’LICENSEKEYOK
'才能继续
函数先利用该字符串与Mac地址进行亦或得到KEY,并且在启动MainActivity的时候会把KEY和MAC地址传过去
public void showPremium(View arg4) {
Intent v0 = new Intent(((Context)this), MainActivity.class);
v0.putExtra("MAC", this.getMac());
v0.putExtra("KEY", this.getKey());
this.startActivity(v0);
}
根据MainActivity中的onCreate(),大体可以判断获得flag需要把MAC地址和KEY当做参数传给stringFromJNI()
protected void onCreate(Bundle arg6) {
String v0 = this.getIntent().getStringExtra("KEY");
String v1 = this.getIntent().getStringExtra("MAC");
if(v0 == "" || v1 == "") {
v0 = "";
v1 = "";
}
super.onCreate(arg6);
this.setContentView(0x7F04001B);
this.findViewById(0x7F0B0060).setText(this.stringFromJNI(v0, v1));
}
当然了,向之前的rps那题一样,本题也可以去逆向so文件,不过本文还是讲使用frida的hook来解题
解题思路
通过上面的分析,我们的解题思路如下:
- 调用程序中的getMac()获得Mac地址
- 把Mac地址和’
LICENSEKEYOK
'当做参数传给xor()计算出KEY - 把getkey()给hook了,让它返回值为刚才计算的KEY
- 调用showPremium(),这样就能获得flag
- 把上面1-4步hook verifyClick()
解题流程
按照Android逆向:frida学习(1)中的操作配置好frida-server,然后运行下面的脚本
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var LauncherActivity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
var MainActivity = Java.use('de.fraunhofer.sit.premiumapp.MainActivity');
var mac = "";
var mac_bytes = [];
var key = "";
LauncherActivity.verifyClick.implementation = function(v){
send("Hook LauncherActivity.verifyClick.");
mac = this.getMac();
send("mac is: " + mac.toString());
for (var i = 0; i < mac.length; ++i) {
var code = mac.charCodeAt(i);
mac_bytes = mac_bytes.concat([code]);
}
var resp = "LICENSEKEYOK";
var resp_bytes = [];
for (var i = 0; i < resp.length; ++i) {
var code = resp.charCodeAt(i);
resp_bytes = resp_bytes.concat([code]);
}
var key_bytes = MainActivity.xor(mac_bytes, resp_bytes);
for (var i = 0; i < key_bytes.length; ++i) {
key += (String.fromCharCode(key_bytes[i]));
}
send("key is: " + key.toString());
}
LauncherActivity.getKey.implementation = function () {
return key;
}
});
"""
process = frida.get_usb_device().attach('de.fraunhofer.sit.premiumapp')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。类似python中的ord()
fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串。类似python中的chr()
concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
收到KEY
点击PREMIUM CONTENT
获得flag