1.环境
夜深模拟器6.5.0.3
ApkIDE3.5.0
UnCrackable-Level1.apk
2.安装APK
adb install UnCrackable-Level1.apk
运行apk
运行提示检测root权限
开始干掉root检测
3.反编译APK
使用ApkIDE打开APK
查看java代码
在onCreate()方法中,使用c中的a、b、c来检测root
a、b、c3个方法来检测root
4.patch root
修改3个方法的返回,使其一直为false
c的smali代码
当检测到不是root权限,就返回v0 我们就让他总是返回v0
const/4 v0, 0x0
这样就返回v0了
把a b c3个方法都改了
保存smali代码 重新编译apk
5.解题
删除原来的apk 安装新编译的apk
没有root检测了
随便输入内容
有个弹窗,尝试搜索字符串 看看有什么逻辑可绕过
如果代码量过大的话,我们可以通过adb shell dumpsys activity top | findstr ACTIVITY
定位要找的界面的代码。
用jd-gui打开MainActivity
发现a.a()中调用了加密的算法,之后将用户输入的值也就是arg5与加密后的结果进行比对,若比对成功的话,就说明我们输入的是正确的。明显这个crackme的意图不是要我们patch源代码然后得到弹窗Success, 而是获得flag, 也就是密文5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=
解密后的结果。
CTF解题法
可以使用动态和静态两种方法获得明文, 用openssl和硬编码在源码中的密钥8d127684cbc37c17616d806cf50473cc解密5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=。
通过代码我们可以知道AES算法使用了ECB加密模式, 且在CTF中AES-128加密算法的密钥一般为32位, 输入命令echo 5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc= | openssl enc -aes-128-ecb -base64 -d -nopad -K 8d127684cbc37c17616d806cf50473cc
可获取flagI want to believe。
Xposed解法
不太理解 先把代码贴上
package com.example.unlock;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class HookMain implements IXposedHookLoadPackage {
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if(!lpparam.packageName.equals("owasp.mstg.uncrackable1")) //过滤包名
return;
XposedBridge.log("Loaded app: " + lpparam.packageName); //Hook a方法
try {
XposedHelpers.findAndHookMethod("sg.vantagepoint.a.a", lpparam.classLoader, "a", byte [].class, byte [].class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
}
protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
// 转换数据类型
String flag = new String((byte []) param.getResult());
// 在这里使用Log.i()可能会输出失败, 最好还是用XposedBridge.log
XposedBridge.log("SECRET: " + flag);
}
});
} catch (Throwable e){
XposedBridge.log("hook failed");
XposedBridge.log(e);
}
}
}
Frida解题法
Frida的官方文档:https://frida.re/docs/javascript-api/#java
Java.perform(function () {
send("Starting hooks OWASP uncrackable1...");
/*
hook java.lang.System.exit, 使该函数只用来输出下面的字符串
避免了应用的检测机制导致应用退出, 使用该方法绕过Java层的root/debug检测
*/
var sysexit = Java.use("java.lang.System");
sysexit.exit.overload("int").implementation = function(var_0) {
send("java.lang.System.exit(I)V // We avoid exiting the application :)");
};
var aes_decrypt = Java.use("sg.vantagepoint.a.a");
aes_decrypt.a.overload("[B","[B").implementation = function(var_0,var_1) {
send("sg.vantagepoint.a.a.a([B[B)[B doFinal(enc) // AES/ECB/PKCS7Padding");
send("Key : " + var_0);
send("Encrypted : " + var_1);
/*
重载解密函数, 并获取其返回值, 因其类型为byte [],
js在调用Java方法之后只能返回一个对象, 而不是返回一个byte类型的数组
*/
var ret = this.a.overload("[B","[B").call(this,var_0,var_1);
send("Decrypted : " + ret);
var flag = "";
//将char类型转换为String类型
for (var i=0; i < ret.length; i++){
flag += String.fromCharCode(ret[i]);
}
send("Decrypted flag: " + flag);
return ret; //[B
};
var mainactivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
mainactivity.onStart.overload().implementation = function() {
send("MainActivity.onStart() HIT!!!");
var ret = this.onStart.overload().call(this);
};
//var mainactivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
mainactivity.onCreate.overload("android.os.Bundle").implementation = function(var_0) {
send("MainActivity.onCreate() HIT!!!");
var ret = this.onCreate.overload("android.os.Bundle").call(this,var_0);
};
var activity = Java.use("android.app.Activity");
activity.onCreate.overload("android.os.Bundle").implementation = function(var_0) {
send("Activity HIT!!!");
var ret = this.onCreate.overload("android.os.Bundle").call(this,var_0);
};
send("Hooks installed.");
});
下面是对frida-server进行的操作的操作:
$ adb shell
$ su
# cd /data/local/tmp # 进入frida-server的目录下
# ./frida-server &
之后重新开一个cmd窗口, 进入.js脚本, 也就是hook的脚本所在的目录下执行frida -U owasp.mstg.uncrackable1 -l cracker.js
(我的hook文件名为cracker.js), 就能获取flag:
参考文章:
https://nankeen.me/posts/owasp-android-level1/
https://www.52pojie.cn/thread-1048786-1-1.html
https://enovella.github.io/android/reverse/2017/05/18/android-owasp-crackmes-level-1.html