5-Android中级题
5-Android中级题

5-Android中级题

分析

image-20240214124338290

GestureUnlock密码界面,isError(密码)做验证回调

image-20240214124706995

DexClassLoader加载了一个外置dex来判断和解密文本

image-20240214125047762

image-20240214125152971

不像是正常的东西,getStaticMethod方法中还有对dex的修复

Class[] uClassArray = new Class[]{Context.class,String.class,int[].class};
Object[] objArray = new Object[]{this,p0,this.getResources().getIntArray(R$array.A_offset)};// this,密码,数据
isValidate(Context p0,String p1,int[] p2)


C.getStaticMethod(p0, p2, "com.zj.wuaipojie2024_2.A", "d", uClassArray).invoke(null, objArray) 
private static Method getStaticMethod(Context p0,int[] p1,String p2,String p3,Class[] p4){
   String str = null;
   try{
      File uFile = C.fix(C.read(p0), p1[0], p1[1], p1[2], p0);//修复dex
      File dir = p0.getDir("fixed", 0);
      uFile.delete();
      new File(dir, uFile.getName()).delete();
      return new DexClassLoader(uFile.getAbsolutePath(), dir.getAbsolutePath(), str, p0.getClass().getClassLoader()).loadClass(p2).getDeclaredMethod(p3, p4);
   }catch(java.lang.Exception e6){
      e6.printStackTrace();
      return str;
   }
}
private static ByteBuffer read(Context p0){
   ByteBuffer uByteBuffer = null;
   try{
      File uFile = new File(p0.getDir("data", 0), "decode.dex");
      if (!uFile.exists()) {
         return uByteBuffer;
      }
      FileInputStream uFileInputSt = new FileInputStream(uFile);
      byte[] uobyteArray = new byte[uFileInputSt.available()];
      uFileInputSt.read(uobyteArray);
      uFileInputSt.close();
      return ByteBuffer.wrap(uobyteArray);
   }catch(java.lang.Exception e0){
      return e0;
   }
}

看来还是得看解密后的dex

题解

1. 分析进入的checkPassword函数

image-20240214131113845

hook checkPassword,看看入参是啥

setImmediate(function () {
    Java.perform(function () {
        var targetClass = decodeURIComponent("com.zj.wuaipojie2024%5f2.MainActivity");
        var methodName = "checkPassword";
        var gclass = Java.use(targetClass);
        gclass[methodName].overload("java.lang.String").implementation = function (arg0) {
            console.log("checkPassword(java.lang.String)" + "\n\targ0 = " + arg0);
            var i = this[methodName](arg0);
            console.log("return " + i);
            return i;
        };
    });
});
checkPassword(java.lang.String)
        arg0 = 012345678
return false


# 即九宫格
# 0 1 2 
# 3 4 5 
# 6 7 8

此处应有暴力解!!!!!

2. 分析解密函数getStaticMethod

重新审视getStaticMethod方法,发现fix仅与传入的 int[] p2有关

this.getResources().getIntArray(R$array.A_offset)

image-20240214133445408

image-20240214133654087

int[] p2 = [0,3,7908]

要不,修好的文件直接拦截删除?

但是!!!! 这dex载不进去啊

image-20240221092237132

索性直接JEB导出gradle项目,自己解看看了

3. 手动构建解密

先MT修复dex,使用JEB打开,导出com.zj.wuaipojie2024_2.*的java

新建一个Android项目,导入java文件,如下

image-20240221092527089

运行,发现decode.dex不存在

apk搜索,发现只有com.zj.wuaipojie2024_2.C#read有使用,盲猜就是这个assets/classes.dex

改名,放到对应的路径/data/data/包名/app_data/decode.dex

删除getStaticMethod方法中的删除操作

image-20240221092932505

运行!!!

image-20240221093001892

2.dex成功导出!!!

已知,isValidate调用com.zj.wuaipojie2024_2.A.d(Context,密码)

// 2.dex        com.zj.wuaipojie2024_2.A.d

public static String d(Context context, String str) {
    MainActivity.sSS(str);//frida检测
    String signInfo = Utils.getSignInfo(context);//签名校验
    if (signInfo == null || !signInfo.equals("fe4f4cec5de8e8cf2fca60a4e61f67bcd3036117")) {
        return "";
    }
    StringBuffer stringBuffer = new StringBuffer();
    int i = 0;
    while (stringBuffer.length() < 9 && i < 40) {
        int i2 = i + 1;
        String substring = "0485312670fb07047ebd2f19b91e1c5f".substring(i, i2);
        if (!stringBuffer.toString().contains(substring)) {
            stringBuffer.append(substring);
        }
        i = i2;
    }

    //stringBuffer.toString().toUpperCase()     锁屏密码 048531267
    return !str.equals(stringBuffer.toString().toUpperCase()) ? "" : "唉!哪有什么亿载沉睡的玄天帝,不过是一位被诅咒束缚的旧日之尊,在灯枯之际挣扎的南柯一梦罢了。有缘人,这份机缘就赠予你了。坐标在B.d";
}


//感觉这个B.d还是不对,可能还得解密
public static String d(String str) {
    return "?" + str + "?";
}

这时候就得灵机一动了,这玩意卡了我好几天,在查看dex时发现有如下字样

image-20240221112248243

说明B.d方法肯定是没解出来,fix是根据int[3]数组进行修复的,这想起了另一个数组B_offset = [1,1,8108],解密得

public static String d(String str) {
    return "机缘是{" + Utils.md5(Utils.getSha1("password+你的uid".getBytes())) + "}";
}

安卓代码

package com.ql.test;

import static com.zj.wuaipojie2024_2.C.isValidate;

import android.os.Bundle;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

import com.zj.wuaipojie2024_2.Utils;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(v -> test());
        System.out.println(test2("048531267"));
        System.out.println(test3("048531267", "838695"));
    }


    public void test() {
        try {
            int[] A_offset = new int[3];
            A_offset[0] = 0;
            A_offset[1] = 3;
            A_offset[2] = 7908;
            isValidate(this, "123456", A_offset);

            A_offset[0] = 1;
            A_offset[1] = 1;
            A_offset[2] = 8108;
            isValidate(this, "123456", A_offset);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String test2(String str) {
        StringBuffer stringBuffer = new StringBuffer();
        int i = 0;
        while (stringBuffer.length() < 9 && i < 40) {
            int i2 = i + 1;
            String substring = "0485312670fb07047ebd2f19b91e1c5f".substring(i, i2);
            if (!stringBuffer.toString().contains(substring)) {
                stringBuffer.append(substring);
            }
            i = i2;
        }
        System.out.println("锁屏密码:" + stringBuffer.toString().toUpperCase());
        return !str.equals(stringBuffer.toString().toUpperCase()) ? "" : "唉!哪有什么亿载沉睡的玄天帝,不过是一位被诅咒束缚的旧日之尊,在灯枯之际挣扎的南柯一梦罢了。有缘人,这份机缘就赠予你了。坐标在B.d";

    }

    public String test3(String password, String uid) {
        try {
            return "机缘是{" + Utils.md5(Utils.getSha1((password + uid).getBytes())) + "}";
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
}

花絮

sSS方法是个frida检测,有简单的/proc/self/maps检测

image-20240214141329738

密码的样子

image-20240221105838572

Android中级题-题解/材料