吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9183|回复: 16
收起左侧

[Android 原创] 从OWASP CrackMe学Android逆向(一)

  [复制链接]
Se8s0n 发表于 2019-11-5 10:12

准备环境、工具

Pixel(Magisk+Xposed/EdXposed)
这里要安利一下Magisk,它现在有个超强的Magisk Hide功能,能隐藏root状态,不过我还没研究过它的原理,如果后面有机会的话会稍微整理一下的。

UnCrackable-Level1

进入到存有UnCrackable的文件目录下,adb install UnCrackable-Level1.apk把APK安装到手机上。打开应用之后,发现应用存在root检测,一旦发现了root状态,点击OK之后应用就会被kill。

可以反编译apk之后patch root检测部分的内容,重打包之后安装到手机里面绕过root检测,也可以用Magisk自带的Magisk Hide绕过。Magisk Hide设置方法如下:


当然第二张图里会不一样,我们需要选中UnCrackable-Level1.apk。

随便输个内容,按下VERIFY,发现出现了个弹窗,要我们再试一下。OK,可以看出我们应该从反编译apk开始入手,找找它这个验证的代码逻辑,看看能不能找到正确的字符串,或者看看有没有方法绕过这样的条件判断。

如果代码量过大的话,我们可以通过adb shell dumpsys activity top | findstr ACTIVITY定位要找的界面的代码。像下面这样,就能定位到界面

不过用JEB看一下,发现这个代码量真的不是很大....用JEB打开MainActivity之后看到一个verify的函数没有经过混淆,看代码逻辑,判断的条件由a.a()函数传递,返回的是一个boolean类型的值。如果Level1真要让我们弹出Success的话,就没什么意思了,那就跟进a.a()函数里面看看。

发现a.a()中调用了加密的算法,之后将用户输入的值也就是arg5与加密后的结果进行比对,若比对成功的话,就说明我们输入的是正确的。明显这个crackme的意图不是要我们patch源代码然后得到弹窗Success, 而是获得flag, 也就是密文5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=解密后的结果。

CTF解题法

可以使用动态和静态两种方法获得明文, 静态的方法参照文章OWASP Android Crackme Level 1, 文章的作者使用了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解法

感觉上面的方法太巧妙了, 下面会补充一个没那么巧妙且常用的方法——Xposed hook, 我一开始找到函数a.a(), 但是因为参数类型填写不正确的原因, 加上该函数实际上返回的是byte []类型的数据, 所以不能得到正确的flag, Xposed模块param.getResult()的返回值又是String类型的数据,所以在这里我们需要处理数据转换的问题。具体的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);       
        }    
    }
}

安装Xposed模块并重启之后,随便输入之后提交,得到flagI want to believe

Frida解题法

还可以按照wp Level1 Writeup使用Frida hook代码获取flag。

Frida使用的过程中不能和Magisk Hide一起使用, 不然会出现Failed to spawn: unable to access zygote64 while preparing for app launch; try disabling Magisk Hide in case it is active的报错。所以我们需要先关掉Magisk Hide(Magisk Manager > Settings >Magisk > Magisk Hide (关掉)), 并且还要在Frida脚本里面绕过root的检测。

因为之前没有接触过Frida, 也算是用这个实例来学习Frida的使用, 如果跟我一样不是很熟悉Frida, 可以配合Frida的官方文档一起食用, 文档中有Frida的js API使用说明, 下面的代码主要涉及的是调用Java函数的部分。作者在这里写的绕过root和debug的思路我觉得值得学习和思考, 有种听君一席话, 胜读十年书的感觉。当然, 这样讲还是有点夸张的, 不过真的很值得学习。

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的使用方法, 安装的话直接pip install frida-tools, 还有下载适合自己设备的frida-server的版本, push进设备, 加上权限就行。下面是对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:

结语

其实在使用Frida绕过应用检测的时候我想到一个问题, 我认为重载java.lang.System.exit的方法只能绕过Java层调用的exit函数, 而不能绕过native层的函数。Java层,测试后已知hookjava.lang.System.exit是不能阻止应用被系统层强制退出的。而native层的, 还没遇到一个这样的demo可以用来测试, 但是我猜测native层的还是需要hook native层的exit函数。如果跳出了这两个层级, 直接对系统层的exit进行注入, 会导致系统崩溃吗?如我的思考或文章出现了错误,希望各位大佬不吝赐教。

最后,附上CrackMe的下载链接

免费评分

参与人数 3威望 +2 吾爱币 +14 热心值 +3 收起 理由
鼠小天 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
流清 + 1 + 1 感谢分享,学到很多
qtfreet00 + 2 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| Se8s0n 发表于 2019-11-28 22:01
SrEGS8Y4SJAGEZi 发表于 2019-11-20 15:32
最近在看这个,但是遇到了问题
我用jadx看java代码时看到调用sg.vantagepoint.uncrackable1.a 是直接调用 ...

很抱歉现在才回复你。我看了一下你问题的描述,有点不够清晰, 可能理解会出错,我尝试理解是这样的:为什么不hook sg.vantagepoint.uncrackable1.a.a, 而选择hook sg.vantagepoint.a.a?

上述问题的答案:
我们选择hook的函数,有一种是为了获取该函数的参数或者返回值,还有一种情况是为了那个函数不按照它原本的代码执行,而按照你想要方式执行(你对这个函数进行改写)。第一种情况也就是你所描述的这个,为了获取加密前的结果,我们选择hook sg.vantagepoint.a.a,获取加密的密钥var_0和密文var_1,之后再调用函数进行解密,所以在所有的函数中选择sg.vantagepoint.a.a是比较合理的,能达成我们解密的目的。而第二种改写的情况,就是文章中sysexit.exit.overload("int"),我们通过重写java层的exit函数,阻止应用检测到root或debug的状态后闪退。

具体的情况可以按照你想实现的功能去选择你想要hook的函数,希望能帮到你。
情话布墨 发表于 2019-12-4 10:54
楼主你好,有个不太明白的地方  我是一个安卓很白的一个人,   在“进入到存有UnCrackable的文件目录下,adb install UnCrackable-Level1.apk把APK安装到手机上。打开应用之后,发现应用存在root检测,一旦发现了root状态,点击OK之后应用就会被kill。”中有一句 adb install UnCrackable-Level1.apk把APK安装到手机上 这个我不太懂,难道不是直接下载安装就好了么  为什么要用命令 这个命令在什么工具上用?
hs_f 发表于 2019-11-5 15:14
田野的守望者 发表于 2019-11-6 09:30
帮忙顶一下
etnet.top 发表于 2019-11-6 09:54
牛逼啊 大佬
表弟用毛线 发表于 2019-11-6 16:34
第一次在吾爱看得懂这是什么代码
Trunks 发表于 2019-11-6 22:34
感谢楼主,准备学。
q1a23 发表于 2019-11-7 02:56
支持一下,发贴不容易
wapj2467 发表于 2019-11-8 09:47
看来我要学的还有很多很多,
jiejie1405 发表于 2019-11-8 10:37
最近对这个感兴趣,谢谢分享
pojieit 发表于 2019-11-11 14:57
学习了

感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 20:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表