Hook框架Frida练习(1)

admin 2022年1月6日01:06:25评论55 views字数 6574阅读21分54秒阅读模式

文章涉及到的知识点:

  • 怎么使用javascript实例化类并调用类方法
  • 怎么在”jscode”中增加自定义javascript方法
  • 怎么较为灵活的hook类方法

基础环境

1
2
3
4
5
Mac Os 10.15.5
Python 3.7
Google Nexus 6P Anroid 6.0.1
Frida官网:https://www.frida.re/
Frida源码:https://github.com/frida

apk的下载地址:whyshouldIpay

安装APK

下载apk安装,先看看是什么功能,简单的使用后,了解到PREMIUM CONETNT内容需要输入License验证后才能查看。PREMIUM CONETNT按钮中的内容应该是答案。

image-20201217204346213

流程分析

使用jadx将apk反编译出来,分析,在AndroidManifest.xml中找到了启动的Activity是LauncherActivity

image-20201217204804697

找到其中的主要代码进行分析

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) {
//第一个验证,将输入的Licese通过网络验证,但这个肯定是通不过的,这是一个可 能需要绕过的点。
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")) {
//当网络验证成功后,生成激活秘钥,并写入到preferences文件中
String activatedKey = new String(MainActivity.xor(getMac().getBytes(), response.getBytes()));
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();
}
}

verifyClick中可以知道生成激活秘钥的算法是MainActivity.xor

1
String activatedKey = new String(MainActivity.xor(getMac().getBytes(), response.getBytes()));

MainActivity中,查看该方法,看上去算起来还是比较麻烦。

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;
}

接下来当程序被激活成功后,点击PREMIUM CONETNT按钮,会调用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);
}

MainActivityonCreate方法中,看到了最终答案生成的native方法stringFromJNI(key, mac)

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void onCreate(Bundle savedInstanceState) {
//获取Intent传递过来的值
String key = getIntent().getStringExtra("KEY");
String mac = getIntent().getStringExtra("MAC");
if (key == "" || mac == "") {
key = "";
mac = "";
}
super.onCreate(savedInstanceState);
setContentView((int) R.layout.activity_main);
//调用native函数,算出答案
((TextView) findViewById(R.id.sample_text)).setText(stringFromJNI(key, mac));
}

大概过程如下:

  1. 输入License,进行验证
  2. 通过网络验证获取返回值“LICENSEKEYOK”后,然后调用MainActivity.xor在本地preferences文件中生成秘钥,激活成功。
  3. 本地获取MAC地址及秘钥Key传入MainActivity得出答案。

对hook点进行分析

  1. 获取getMac()函数的返回值,与“LICENSEKEYOK”字符串进行xor运算得出秘钥Key.

  2. hook getKey方法,让它不从preferences文件读取Key,而是我们自己构造。

  3. hook verifyClick,让它调用showPremium方法

    image-20201229114906268

代码的构造与编写

hook getMac()函数

getMac() 在showPremium函数中调用,showPremium在LauncherActivity类中,所有直接通过this就能直接调用getMac()方法

1
2
3
4
5
6
7
8
9
10
11
12
js_code = '''
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
hook_Activity.showPremium.implementation = function(v){
var Key = this.getKey();
var Mac = this.getMac();
send(Key);
send(Mac);

}
});
'''

完整的Python代码:

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
# -*-coding:utf-8-*-
import frida, sys


def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)


js_code = '''
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
hook_Activity.showPremium.implementation = function(v){
var Key = this.getKey();
var Mac = this.getMac();
send(Key);
send(Mac);

}
});
'''

process = frida.get_usb_device().attach('de.fraunhofer.sit.premiumapp')
script = process.create_script(js_code)
script.on('message', on_message)
script.load()
sys.stdin.read()

image-20201217210711185

计算密钥key

接下来开始真正第一步的hook,将mac值与“LICENSEKEYOK”通过MainActivity.xor获取秘钥Key。那就直接hook getKey方法吧,这样可以自己来构造秘钥Key。
仔细分析,会发现在这一步中可能会遇到下面的问题:

  1. 怎么调用xor方法。
  2. java是强类型语言,javascript是弱类型语言,怎么将javascript参数进行类型转换并传递到java语言中。

怎么将javascript参数进行类型转换并传递到java语言中?其实方法很简单,既然java是强类型语言,那就根据它要求的类型传递对应参数即可,看看它参数的类型。

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;
}

那么,在javascript代码中,先准备一个将字符串类型转换为byte[]类型的方法stringToBytes,再通过实例化MainActivity类的方式调用xor(),然后还需要一个将byte[]回转为String的方法,因为秘钥key是Sting类型的。

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
59
60
61
62
63
64
65
66
67
68
69
70
71
js_code = '''
//字符串转换byte[]的方法
stringToBytes = function(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++ ) {
ch = str.charCodeAt(i);
st = [];

do {
st.push( ch & 0xFF );
ch = ch >> 8;
}
while ( ch );
re = re.concat( st.reverse() );
}
return re;
}
//将byte[]转成String的方法
function byteToString(arr) {
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
//hook 代码
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
var MainActivity = Java.use('de.fraunhofer.sit.premiumapp.MainActivity')
var LicenseStr = "LICENSEKEYOK";
//hook getKey()方法,直接构造密码,而不从preferences读取
hook_Activity.getKey.implementation = function(){
//获取Mac
var Mac = this.getMac();
//实例化MainActivity
var instance = MainActivity.$new();
//类型转换
var MacByte =stringToBytes(Mac);
var LicenseByte = stringToBytes(LicenseStr);

send("MacByte:"+MacByte)
send("LicenseByte:"+LicenseByte)
//调用实例化对象的xor方法
xorResult = instance.xor(MacByte,LicenseByte);
send(xorResult);
//类型回转
var Key = byteToString(xorResult)
send(Key);
return Key;
}
hook_Activity.verifyClick.implementation = function(view){
this.showPremium(view);
}
});
'''

接下来,执行看看,能不能获取秘钥Key。

image-20201225155944497

调用showPremium获取答案

前面2个步骤,可以说是已经完成90%了,接下来只需要在hook一个能够触发showPremium方法的即可。方法就随意了,这里采用hook verifyClick的方式,这样点击app上的VERIFY按钮,触发verifyClick方法去调用showPremium,进而获得最终答案。

1
2
3
hook_Activity.verifyClick.implementation = function(view){
this.showPremium(view);
}

启动脚本,点击app上的VERIFY按钮看看执行结果:

image-20201225160323712

总结

  1. 任意类方法调用。
  2. 任意类方法重实现。
    以及学会怎么构造和使用自定义javascript方法。

参考链接:

https://bbs.pediy.com/thread-227233.htm

FROM :ol4three.com | Author:ol4three

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:06:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Hook框架Frida练习(1)https://cn-sec.com/archives/721052.html

发表评论

匿名网友 填写信息