frida小技巧之string与byte转化

一、分析app逻辑代码

1.运行程序,输入任意符号,点verify看下报错。

2.反编译代码,搜索报错字符并查看相关方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void verifyClick(View v) {
try {
//进行网络验证
InputStream in = new URL("<http://broken.license.server.com/query?license=>" + ((EditText) findViewById(R.id.text_license)).getText().toString()).openConnection().getInputStream();
StringBuilder responseBuilder = new StringBuilder();
byte[] b = new byte[0];
while (in.read(b) > 0) {
responseBuilder.append(b);
}
String response = responseBuilder.toString();
// 返回的响应为"LICENSEKEYOK"
if (response.equals("LICENSEKEYOK")) {
// 通过MainActivity.xor()方法生成activatedKey
String activatedKey = new String(MainActivity.xor(getMac().getBytes(), response.getBytes()));
// 生成的activatedKey写入到SharedPreferences文件中
SharedPreferences.Editor editor = getApplicationContext().getSharedPreferences("preferences", 0).edit();
editor.putString("KEY", activatedKey);
editor.commit();
new AlertDialog.Builder(this).setTitle("Activation successful").setMessage("Activation successful").setIcon(17301543).show();
return;
}
new AlertDialog.Builder(this).setTitle("Invalid license!").setMessage("Invalid license!").setIcon(17301543).show();
} catch (Exception e) {
new AlertDialog.Builder(this).setTitle("Error occured").setMessage("Server unreachable").setNeutralButton("OK", (DialogInterface.OnClickListener) null).setIcon(17301543).show();
}
}

3.步入到生成activatedKey的方法,算法比较简单。

1
2
3
4
5
6
7
public static byte[] xor(byte[] val, byte[] key) {
byte[] o = new byte[val.length];
for (int i = 0; i < val.length; i++) {
o[i] = (byte) (val[i] ^ key[i % key.length]);
}
return o;
}

4.生成activatedKey后,点击“premium content”按钮,会调用MainActivity中的方法,并将“Mac”与”key”传入MainActivity

1
2
3
4
5
6
public void showPremium(View view) {
Intent i = new Intent(this, MainActivity.class);
i.putExtra("MAC", getMac());
i.putExtra("KEY", getKey());
startActivity(i);
}

其中getMac与getKey方法如下:

1
2
3
4
5
6
7
8
9
10
11
private String getKey() {
return getApplicationContext().getSharedPreferences("preferences", 0).getString("KEY", "");
}

private String getMac() {
try {
return ((WifiManager) getApplicationContext().getSystemService("wifi")).getConnectionInfo().getMacAddress();
} catch (Exception e) {
return "";
}
}

5.在MainActivity中通过调用setringFromJNI函数,计算出最终结果。

1
2
3
4
5
6
7
8
9
10
11
12
public void onCreate(Bundle savedInstanceState) {
String key = getIntent().getStringExtra("KEY");
String mac = getIntent().getStringExtra("MAC");
if (key == "" || mac == "") {
key = "";
mac = "";
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 调用setringFromJNI函数,计算结果。
((TextView) findViewById(R.id.sample_text)).setText(stringFromJNI(key, mac));
}

6.梳理下流程,整个过程比较条理。

通过网络验证—>生成activatedKey—>获取mac值—>通过setringFromJNI计算得出最终结果。

二、实现:

虽然有更简单的实现方法,但这里是通过new一个实例,然后直接调用java方法,并调用native函数返回目标值,有些知识点还是值的学习的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function hook() {

Java.perform(function () {

function stringToBytes(str) {
var javaString = Java.use('java.lang.String');
var bytes = [];
bytes = javaString.$new(str).getBytes();
return bytes;
}

function bytesToString(bytes) {
var javaString = Java.use('java.lang.String');
return javaString.$new(bytes);
}

var LauncherActivity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
var MainActivity = Java.use('de.fraunhofer.sit.premiumapp.MainActivity');

LauncherActivity.verifyClick.implementation = function (view) {
this.showPremium(view);
}

LauncherActivity.getKey.implementation = function (arg) {

send("hook start .........")
// 设置响应值为“LICENSEKEYOK”
var response = "LICENSEKEYOK";

// 得到mac值
var mac = this.getMac();
send("mac的值为:" + mac);
var macByte = stringToBytes(mac);

// 得到key值
var mac = this.getKey();
var responseString = response;
send("response的值为:" + responseString)
var responseByte = stringToBytes(responseString)

// 实例化MainActivity
var instance = MainActivity.$new();
// 调用MainActivity.xor方法
var keyByte = instance.xor(macByte, responseByte);
send("key的值为:" + keyByte);
var keyString = bytesToString(keyByte);
return (keyString);

};
MainActivity.stringFromJNI.implementation = function (arg1, arg2) {

var value = this.stringFromJNI(arg1, arg2);
send("flag----->" + value);
return value;
}
});
}
setImmediate(hook, 0);

运行结果:

1
2
3
4
message: {'type': 'send', 'payload': 'mac的值为:02:00:00:00:00:00'} data: None
message: {'type': 'send', 'payload': 'response的值为:LICENSEKEYOK'} data: None
message: {'type': 'send', 'payload': 'key的值为:[object Object]'} data: None
message: {'type': 'send', 'payload': 'flag----->AHE17{pr3mium4ctiv4ted}'} data: None
  • byte与string互相转换的小技巧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function stringToBytes(str){
    var javaString = Java.use('java.lang.String');
    var bytes = [];
    bytes = javaString.$new(str).getBytes();
    return bytes;
    }

    function bytesToString(bytes){
    var javaString = Java.use('java.lang.String');
    return javaString.$new(bytes);
    }