学习Frida的时候看到小肩膀视频中提到了一个apk,whyshouldipay。这个apk之前做过逆向分析修改,所以正好此处使用Frida来尝试HOOK。
逆向分析
把apk丢到jeb中,从mainfast文件。可以看到首页activity是LauncherActivity。找到首页activity,点击反编译。
从代码中可以看到,其实是做了一次网络验证,但由于时间久远,这个apk貌似是16年的CTF使用,网址现在已经不能使用了。所以需要这里进行修改,利用AK来修改smail代码。重打包安装。
.method public verifyClick(Landroid/view/View;)V
.locals 15
.line 39
.restart local v1 # "b":[B
.restart local v2 # "con":Ljava/net/URLConnection;
.restart local v5 # "in":Ljava/io/InputStream;
.restart local v9 # "responseBuilder":Ljava/lang/StringBuilder;
.restart local v11 # "url":Ljava/net/URL;
:cond_0
const-string v8, "LICENSEKEYOK"
.line 40
#.local v8, "response":Ljava/lang/String;
const-string v12, "LICENSEKEYOK"
invoke-virtual {v8, v12}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v12
if-eqz v12, :cond_1
.line 43
new-instance v0, Ljava/lang/String;
invoke-direct {p0}, Lde/fraunhofer/sit/premiumapp/LauncherActivity;->getMac()Ljava/lang/String;
move-result-object v12
invoke-virtual {v12}, Ljava/lang/String;->getBytes()[B
move-result-object v12
invoke-virtual {v8}, Ljava/lang/String;->getBytes()[B
move-result-object v13
invoke-static {v12, v13}, Lde/fraunhofer/sit/premiumapp/MainActivity;->xor([B[B)[B
move-result-object v12
invoke-direct {v0, v12}, Ljava/lang/String;-><init>([B)V
.line 44
.local v0, "activatedKey":Ljava/lang/String;
invoke-virtual {p0}, Lde/fraunhofer/sit/premiumapp/LauncherActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v12
const-string v13, "preferences"
const/4 v14, 0x0
invoke-virtual {v12, v13, v14}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;
move-result-object v7
.line 45
.local v7, "pref":Landroid/content/SharedPreferences;
invoke-interface {v7}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;
move-result-object v4
.line 46
.local v4, "editor":Landroid/content/SharedPreferences$Editor;
const-string v12, "KEY"
invoke-interface {v4, v12, v0}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;
.line 47
invoke-interface {v4}, Landroid/content/SharedPreferences$Editor;->commit()Z
.line 48
new-instance v12, Landroid/support/v7/app/AlertDialog$Builder;
invoke-direct {v12, p0}, Landroid/support/v7/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
const-string v13, "Activation successful"
.line 49
invoke-virtual {v12, v13}, Landroid/support/v7/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/support/v7/app/AlertDialog$Builder;
move-result-object v12
const-string v13, "Activation successful"
.line 50
invoke-virtual {v12, v13}, Landroid/support/v7/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/support/v7/app/AlertDialog$Builder;
move-result-object v12
const v13, 0x1080027
.line 51
invoke-virtual {v12, v13}, Landroid/support/v7/app/AlertDialog$Builder;->setIcon(I)Landroid/support/v7/app/AlertDialog$Builder;
move-result-object v12
.line 52
invoke-virtual {v12}, Landroid/support/v7/app/AlertDialog$Builder;->show()Landroid/support/v7/app/AlertDialog;
:cond_1
return-void
.end method
修改如上,重新给v8寄存器赋值。并且删除以上全部请求代码,清楚其他try的代码,不然其做HTTP请求验证。完整的verifyClick函数smail代码如上。至此网络验证的给去掉了。
点击verify,就会显示验证成功,然后写入本地数据保存key值。
只不过这样修改,再点击premium的时候就是认证成功的了。
Hook 函数
那么先hook一下getMac和getKey函数,看一下返回是什么值。
import frida, sys
jscode = """
Java.perform(function(){
var lunc = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
lunc.getMac.implementation = function(){
send("HOOK Start....");
var sed = this.getMac();
send(sed);
var ser = this.getKey();
send(ser);
return sed;
}
})
"""
def message(message, data):
if message["type"] == "send":
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('de.fraunhofer.sit.premiumapp')
script = process.create_script(jscode)
script.on("message", message)
script.load()
sys.stdin.read()
在手机上打开Frida。转发端口27042。运行后获取到两个参数,一个是WiFi的Mac,一个是key。
然后按照修改返回参数来直接达到不修改apk的目的。
Hook 直接获得结果
从代码中就可以看到,当返回结果为LICENSEKEYOK时,调用MainActivity类下的xor方法。参数一个是上面的Mac的byte值,一个是LICENSEKEYOK的byte值。
import frida, sys
jscode = """
Java.perform(function(){
//字符串转byte
function stringToBytes(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++ ) {
ch = str.charCodeAt(i); // get char
st = []; // set up "stack"
do {
st.push( ch & 0xFF ); // push byte to stack
ch = ch >> 8; // shift value down by 1 byte
}
while ( ch );
re = re.concat( st.reverse() );
}
return re;
}
//byte转字符串
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;
}
var lunc = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
var main = Java.use('de.fraunhofer.sit.premiumapp.MainActivity');
lunc.getKey.implementation = function(){
send("HOOK Start....");
var sed = this.getMac();
send(sed); //获取mac
var xor = main.xor(stringToBytes(sed), stringToBytes("LICENSEKEYOK")); //生成key
var ser = byteToString(xor);
send(ser);
return ser;
}
})
"""
def message(message, data):
if message["type"] == "send":
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('de.fraunhofer.sit.premiumapp')
script = process.create_script(jscode)
script.on("message", message)
script.load()
sys.stdin.read()
执行如上代码,即可Hook到key函数,然后由xor生成,来返回给getKey函数。返回如下所示。