1.环境

夜深模拟器6.5.0.3
ApkIDE3.5.0
UnCrackable-Level1.apk

2.安装APK

adb install UnCrackable-Level1.apk
运行apk
image.png
运行提示检测root权限
开始干掉root检测

3.反编译APK

使用ApkIDE打开APK
image.png
查看java代码
image.png
在onCreate()方法中,使用c中的a、b、c来检测root
image.png
a、b、c3个方法来检测root

4.patch root

修改3个方法的返回,使其一直为false
image.png
c的smali代码

image.png
当检测到不是root权限,就返回v0 我们就让他总是返回v0

  1. const/4 v0, 0x0

这样就返回v0了
把a b c3个方法都改了
保存smali代码 重新编译apk
image.png
image.png

5.解题

删除原来的apk 安装新编译的apk
没有root检测了

随便输入内容
image.png
有个弹窗,尝试搜索字符串 看看有什么逻辑可绕过
如果代码量过大的话,我们可以通过adb shell dumpsys activity top | findstr ACTIVITY定位要找的界面的代码。
image.png
用jd-gui打开MainActivity
image.png
image.png
发现a.a()中调用了加密的算法,之后将用户输入的值也就是arg5与加密后的结果进行比对,若比对成功的话,就说明我们输入的是正确的。明显这个crackme的意图不是要我们patch源代码然后得到弹窗Success, 而是获得flag, 也就是密文5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=解密后的结果。

CTF解题法

可以使用动态和静态两种方法获得明文, 用openssl和硬编码在源码中的密钥8d127684cbc37c17616d806cf50473cc解密5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=。
image.png
通过代码我们可以知道AES算法使用了ECB加密模式, 且在CTF中AES-128加密算法的密钥一般为32位, 输入命令echo 5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc= | openssl enc -aes-128-ecb -base64 -d -nopad -K 8d127684cbc37c17616d806cf50473cc可获取flagI want to believe。

image.png
image.png

Xposed解法

不太理解 先把代码贴上

  1. package com.example.unlock;
  2. import android.util.Log;
  3. import de.robv.android.xposed.IXposedHookLoadPackage;
  4. import de.robv.android.xposed.XC_MethodHook;
  5. import de.robv.android.xposed.XC_MethodReplacement;
  6. import de.robv.android.xposed.XposedBridge;
  7. import de.robv.android.xposed.XposedHelpers;
  8. import de.robv.android.xposed.callbacks.XC_LoadPackage;
  9. import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
  10. public class HookMain implements IXposedHookLoadPackage {
  11. public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
  12. if(!lpparam.packageName.equals("owasp.mstg.uncrackable1")) //过滤包名
  13. return;
  14. XposedBridge.log("Loaded app: " + lpparam.packageName); //Hook a方法
  15. try {
  16. XposedHelpers.findAndHookMethod("sg.vantagepoint.a.a", lpparam.classLoader, "a", byte [].class, byte [].class, new XC_MethodHook() {
  17. @Override
  18. protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
  19. }
  20. protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
  21. // 转换数据类型
  22. String flag = new String((byte []) param.getResult());
  23. // 在这里使用Log.i()可能会输出失败, 最好还是用XposedBridge.log
  24. XposedBridge.log("SECRET: " + flag);
  25. }
  26. });
  27. } catch (Throwable e){
  28. XposedBridge.log("hook failed");
  29. XposedBridge.log(e);
  30. }
  31. }
  32. }

Frida解题法

Frida的官方文档:https://frida.re/docs/javascript-api/#java

  1. Java.perform(function () {
  2. send("Starting hooks OWASP uncrackable1...");
  3. /*
  4. hook java.lang.System.exit, 使该函数只用来输出下面的字符串
  5. 避免了应用的检测机制导致应用退出, 使用该方法绕过Java层的root/debug检测
  6. */
  7. var sysexit = Java.use("java.lang.System");
  8. sysexit.exit.overload("int").implementation = function(var_0) {
  9. send("java.lang.System.exit(I)V // We avoid exiting the application :)");
  10. };
  11. var aes_decrypt = Java.use("sg.vantagepoint.a.a");
  12. aes_decrypt.a.overload("[B","[B").implementation = function(var_0,var_1) {
  13. send("sg.vantagepoint.a.a.a([B[B)[B doFinal(enc) // AES/ECB/PKCS7Padding");
  14. send("Key : " + var_0);
  15. send("Encrypted : " + var_1);
  16. /*
  17. 重载解密函数, 并获取其返回值, 因其类型为byte [],
  18. js在调用Java方法之后只能返回一个对象, 而不是返回一个byte类型的数组
  19. */
  20. var ret = this.a.overload("[B","[B").call(this,var_0,var_1);
  21. send("Decrypted : " + ret);
  22. var flag = "";
  23. //将char类型转换为String类型
  24. for (var i=0; i < ret.length; i++){
  25. flag += String.fromCharCode(ret[i]);
  26. }
  27. send("Decrypted flag: " + flag);
  28. return ret; //[B
  29. };
  30. var mainactivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
  31. mainactivity.onStart.overload().implementation = function() {
  32. send("MainActivity.onStart() HIT!!!");
  33. var ret = this.onStart.overload().call(this);
  34. };
  35. //var mainactivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
  36. mainactivity.onCreate.overload("android.os.Bundle").implementation = function(var_0) {
  37. send("MainActivity.onCreate() HIT!!!");
  38. var ret = this.onCreate.overload("android.os.Bundle").call(this,var_0);
  39. };
  40. var activity = Java.use("android.app.Activity");
  41. activity.onCreate.overload("android.os.Bundle").implementation = function(var_0) {
  42. send("Activity HIT!!!");
  43. var ret = this.onCreate.overload("android.os.Bundle").call(this,var_0);
  44. };
  45. send("Hooks installed.");
  46. });

下面是对frida-server进行的操作的操作:

  1. $ adb shell
  2. $ su
  3. # cd /data/local/tmp # 进入frida-server的目录下
  4. # ./frida-server &

之后重新开一个cmd窗口, 进入.js脚本, 也就是hook的脚本所在的目录下执行frida -U owasp.mstg.uncrackable1 -l cracker.js(我的hook文件名为cracker.js), 就能获取flag:
image.png

参考文章:
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