吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1102|回复: 7
收起左侧

[Android 原创] 【2024春节】解题之Android中级题 详细过程

  [复制链接]
Botton 发表于 2024-7-28 10:03
本帖最后由 Botton 于 2024-7-28 10:35 编辑

0x01、前言
最近在学安卓逆向,正好把春节的Android中级题给做一做


0x02、过程
先把apk丢到GDA里面看一下
包名:com.zj.wuaipojie2024_2 入口:com.zj.wuaipojie2024_2.MainActivity
1.jpg
然后用jadx看下MainActivity下面有什么东西,发现有个叫checkPassword的方法
2.png
用frIDA来hook看一下
[JavaScript] 纯文本查看 复制代码
Java.perform(function () {
        let MainActivity = Java.use("com.zj.wuaipojie2024_2.MainActivity");
        MainActivity["checkPassword"].implementation = function (str) {
                let ret = this["checkPassword"](str);
                console.log(str,ret);
                return ret;
        };
});

随便滑一个密码,可以发现方法被调用了,手势的索引是
0 1 2
3 4 5
6 7 8
传入的str就是我们的密码

3.jpg
那么看一下checkPassword面进行了哪些操作
checkPassword方法先是从apk的assets目录下拿到classes.dex,复制了一份到app内部空间的data目录下,命名为1.dex,然后加载这个1.dex,调用com.zj.wuaipojie2024_2.C类下的isValidate方法
这里看一下app的运行日志,adb logcat | grep com.zj.wuaipojie2024_2.MainActivity
这里用cmder来执行命令,因为grep是linux下的命令
4.png
可以发现有这么一条日志
Suppressed: java.io.IOException: Failed to open dex files from /data/user/0/com.zj.wuaipojie2024_2/app_data/1.dex because: Failure to verify dex file '/data/user/0/com.zj.wuaipojie2024_2/app_data/1.dex': Bad checksum (c607ea12, expected 22dcea4c)
查一下关于dex的资料 https://source.android.com/docs/core/runtime/dex-format?hl=zh-cn
我们把这个1.dex下载来用010 Editor打开看看,模板选dex
5.png
可以看到checksumsignature是红色的,也就是因为checksum的值错误让app以为文件损坏导致1.dex没有成功加载
写一份脚本修复一下
[Python] 纯文本查看 复制代码
import hashlib,zlib

def repairChecksum(dex_file):
    self = open(dex_file, "rb+")
    self.seek(8)
    # checksum是uint32,小端存储
    sourceData = self.read(4)[::-1]
    self.seek(12)
    checkdata = self.read()
    checksum = format(zlib.adler32(checkdata), 'x')
    print ("头部原checksum:",sourceData.hex())
    print ("计算checksum:",checksum)
    if sourceData.hex() == checksum:
        print('checksum校验正常')
    else:
        self.seek(8)
        # 小端写入
        self.write(bytes.fromhex(checksum)[::-1])
        print('checksum效验异常,修复成功')

def repairSignature(dex_file):
    self = open(dex_file, "rb+")
    self.seek(12)
    sourceData = self.read(20)
    self.seek(32)
    sigdata = self.read()
    sha1 = hashlib.sha1()
    sha1.update(sigdata)
    sha0 = sha1.hexdigest()
    print ("计算signature:",sha0)
    print ("现在signature:",sourceData.hex())
    if sourceData.hex() == sha0:
        print('SHA1效验正常')
    else:
        self.seek(12)
        self.write(bytes.fromhex(sha0))
        print('SHA1效验异常,修复成功')

if __name__ == '__main__':
    repairSignature('1.dex')
    repairChecksum('1.dex')

查一下app的安装路径 adb shell pm path com.zj.wuaipojie2024_2
package:/data/app/com.zj.wuaipojie2024_2-KFnHRFDetvCIbKXjR_4LTQ==/base.apk
这里用mt管理器把修复后的1.dex替换到base.apkassets目录下的classes.dex
因为调用checkPassword法执行时会从这拿原来的classes.dex复制一份到app内部空间的data目录下,不能直接替换data目录下的1.dex,不然又被没修复的classes.dex覆盖掉了
然后我们重新运行app看一下日志
6.png
可以发现日志不一样了,没有之前那个加载dex的报错
分析一下com.zj.wuaipojie2024_2这个类下的执行过程
7.png
isValidate方法中调用了getStaticMethod方法,getStaticMethod方法中调用了read方法和fix方法
getStaticMethod中先用read方法中读入app运行目录data下的decode.dex,传入到fix方法后生成了2.dex,然后加载2.dexcom.zj.wuaipojie2024_2.A类下的d方法最后再将2.dex删除
将修复后的1.dex复制一份到data目录下,命名为decode.dex
8.jpg
记得给文件权限
现在要想办法拿到2.dex 这里我通过frida来hook,阻止2.dex被删除
[JavaScript] 纯文本查看 复制代码
Java.perform(function () {
    var deleteFile = Java.use("java.io.File").delete;
        deleteFile.implementation = function () {
                console.log("delete file: " + this);
                return
        };
});

2.dex丢到jadx里看一下
9.png
可以看到d方法已经正常了,转换成python代码执行得到密码为048531267
[Python] 纯文本查看 复制代码
def calculate_str():
    stringBuffer = []
    i = 0
    fixed_string = "0485312670fb07047ebd2f19b91e1c5f"

    while len(stringBuffer) < 9 and i < 40:
        substring = fixed_string[i:i + 1]
        if substring not in stringBuffer:
            stringBuffer.append(substring)
        i += 1

    computed_str = "".join(stringBuffer).upper()

    print(computed_str)

10.jpg
可以得到提示在B.d
11.jpg
但是这个B.d显然还有问题,需要再修复
看一下fix方法需要的参数,是从checkPassword方法传过来的
String str2 = (String) new DexClassLoader(file.getAbsolutePath(), getDir("dex", 0).getAbsolutePath(), null, getClass().getClassLoader()).loadClass("com.zj.wuaipojie2024_2.C").getDeclaredMethod("isValidate", Context.class, String.class, int[.class).invoke(null, this, str, getResources().getIntArray(R.array.A_offset));
getResources().getIntArray(R.array.A_offset) 得到fix方法中的int i, int i2, int i3
jadx中点进去看一下R.array.A_offset
12.png
发现还有一个叫R.array.D_offset的,那我们把传入fix方法中的参数改成这个试试
[JavaScript] 纯文本查看 复制代码
Java.perform(function () {
        let AppCompatActivity = Java.use("androidx.appcompat.app.AppCompatActivity");
        AppCompatActivity["getResources"].implementation = function () {
                let resources = this["getResources"]();
                resources.getIntArray.implementation = function (resId) {
            let ret = this.getIntArray(resId);
                        console.log(resId,ret);
                        if(resId == 2130903040){
                                return this.getIntArray(0x7f030001);
                        }
                        return ret;
        };
                return resources;
        };
        
        var deleteFile = Java.use("java.io.File").delete;
        deleteFile.implementation = function () {
                console.log("delete file: " + this);
                return
        };
});

把修复后的2.dex下载来丢到jadx中看一下
13.png
可以看到B.d已经显示正常
最后把Utils复制下来稍微修改下即可
[Java] 纯文本查看 复制代码
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

public class Utils {
    public static final String SHA1 = "SHA1";

    public static byte[] toULEB128(int i) {
        int i2 = i >> 28;
        if (i2 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (((i >> 14) & 127) | 128), (byte) (((i >> 21) & 127) | 128), (byte) (i2 & 15)};
        }
        int i3 = i >> 21;
        if (i3 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (((i >> 14) & 127) | 128), (byte) (i3 & 127)};
        }
        int i4 = i >> 14;
        if (i4 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (i4 & 127)};
        }
        int i5 = i >> 7;
        return i5 > 0 ? new byte[]{(byte) ((i & 127) | 128), (byte) (i5 & 127)} : new byte[]{(byte) (i & 127)};
    }

    public static byte[] getSha1(byte[] bArr) {
        try {
            return MessageDigest.getInstance("SHA").digest(bArr);
        } catch (Exception unused) {
            return null;
        }
    }

    public static String md5(byte[] bArr) {
        try {
            String bigInteger = new BigInteger(1, MessageDigest.getInstance("md5").digest(bArr)).toString(16);
            for (int i = 0; i < 32 - bigInteger.length(); i++) {
                bigInteger = "0" + bigInteger;
            }
            return bigInteger;
        } catch (NoSuchAlgorithmException unused) {
            throw new RuntimeException("ops!!");
        }
    }

    public static int checksum(ByteBuffer byteBuffer) {
        byteBuffer.position(12);
        int capacity = byteBuffer.capacity();
        int i = 1;
        int i2 = 0;
        boolean z = false;
        while (byteBuffer.position() < capacity) {
            ArrayList arrayList = new ArrayList();
            int i3 = 0;
            while (true) {
                if (i3 < 1024) {
                    arrayList.add(Integer.valueOf(byteBuffer.get() & 255));
                    if (byteBuffer.position() == byteBuffer.limit()) {
                        z = true;
                        break;
                    }
                    i3++;
                } else {
                    break;
                }
            }
            int[] calculateVar = calculateVar(arrayList, i, i2);
            int i4 = calculateVar[0];
            int i5 = calculateVar[1];
            if (z) {
                return (i5 << 16) + i4;
            }
            i2 = i5;
            i = i4;
        }
        return 0;
    }

    private static int[] calculateVar(ArrayList<Integer> arrayList, int i, int i2) {
        int i3 = 0;
        while (i3 < arrayList.size()) {
            int intValue = (arrayList.get(i3).intValue() + i) % 65521;
            i2 = (i2 + intValue) % 65521;
            i3++;
            i = intValue;
        }
        return new int[]{i, i2};
    }


    public static int[] fromULEB128(ByteBuffer byteBuffer) {
        int i;
        int i2 = byteBuffer.get() & 255;
        if (i2 > 127) {
            int i3 = byteBuffer.get() & 255;
            i2 = (i2 & 127) | ((i3 & 127) << 7);
            if (i3 > 127) {
                int i4 = byteBuffer.get() & 255;
                i2 |= (i4 & 127) << 14;
                if (i4 > 127) {
                    int i5 = byteBuffer.get() & 255;
                    i2 |= (i5 & 127) << 21;
                    if (i5 > 127) {
                        i2 |= (byteBuffer.get() & 255) << 28;
                        i = 5;
                    } else {
                        i = 4;
                    }
                } else {
                    i = 3;
                }
            } else {
                i = 2;
            }
        } else {
            i = 1;
        }
        return new int[]{i2, i};
    }
}

14.png

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
iid6 + 1 + 1 我很赞同!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

tomboy666 发表于 2024-7-28 13:30
厉害看起来挺难解的题
MrYuxuan 发表于 2024-7-28 13:32
lyz19860209 发表于 2024-7-28 13:45
ecco13 发表于 2024-7-28 14:00
感谢分享!
danwangdexiaoyi 发表于 2024-7-28 15:09
好多技术呀,够我学很久了
abc1234567890 发表于 2024-7-28 18:55
牛,用到好多技术,还得继续学习
Feio 发表于 2024-7-29 09:41
正好在看这方面的,学习了。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-24 11:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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