吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3729|回复: 27
收起左侧

[Android CTF] 2024春节解题{Android 中级题} 解题思路

  [复制链接]
ty1937 发表于 2024-2-27 15:21
本帖最后由 ty1937 于 2024-2-27 15:35 编辑

一: 闲言

52论坛又开启一年一度的解题得红包的活动,大年初七,闲着没事,就把题下载下来解题了一波,现在活动结束了,可以分享解题思路,我就分享一下我的思路,俗话说的好,条条大路通罗马,解题思路也不止一条,欢迎大佬们把其他解题思路讨论分享一波。

二:开始

下载好apk后,装入手机运行了一波

1.png

引入眼帘的就是一个解锁,

那么直接拖入 jadx-gui 看看代码

2.png

发现收到密码后,进入了 checkPassword 方法


    public boolean checkPassword(String str) {

        try {

            InputStream open = getAssets().open("classes.dex");

            byte[] bArr = new byte[open.available()];

            open.read(bArr);

            File file = new File(getDir("data", 0), "1.dex");

            FileOutputStream fileOutputStream = new FileOutputStream(file);

            fileOutputStream.write(bArr);

            fileOutputStream.close();

            open.close();

            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));

            if (str2 == null || !str2.startsWith("唉!")) {

                return false;

            }

            this.tvText.setText(str2);

            this.myunlock.setVisibility(8);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

代码逻辑也很清晰,将assets目录下的classes.dex 打开,读取写入到软件data目录app_data下名字1.dex 文件,然后加载该dex,再使用反射的调用的方式,调用 com.zj.wuaipojie2024_2.C 类 ,方法 isValidate ,并传入了int数组。

三:发现问题

在随便输入一组密码后,在日志中发现了dex加载有问题,发现了报错

3.png

说dex 的checksum 有问题,将classes.dex 拖入jadx-gui中提示报错,打不开。
4.png

那就上 010Editor ,拖入后

5.png

发现 checksum 和sha1 signature 的值确实被篡改过,那么我们使用代码计算出来,重新填回就行了。

在这片文章有对dex文件的介绍

dex文件的介绍

dex 头部中 checksum 和signature的介绍

6.png

那直接使用python 计算出正确的值即可


def repairChecksum(dex_file):

  self = open(dex_file, "rb")

  self.seek(8)

  sourceData = self.read(4)

  self.seek(12)

  checkdata = self.read()

  checksum =  format(zlib.adler32(checkdata), 'x')

  print ("头部原checksum:",sourceData )

  print ("计算checksum:",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())

7.png

重新填入后:

8.png

重新使用jadx-gtui 打开

9.png

没问题,将修好的dex放回apk中,重启应用,重新输入密码,发现了新的报错:

10.png

这个报错,说明有1.dex已经被成功加载进去。

经过排查代码发现报错点在这里

11.png
12.png

也就是读取这个decode.dex 转为ByteBuffer时出的问题,我们进到这个目录查看文件。

那么现在将1.dex 也移动到这个目录。

13.png

重新启动APP,发现还是有问题,那是因为移动的decode.dex 的用户组没有修改,所以不能被加载,我们使用命令修改用户组。

14.png

再次启动APP,输入密码,发现没有报错,那证明代码中。

四:分析代码逻辑

那么现在我们就来仔细读读代码逻辑

apk 首次加载1.dex调用 类C\ isValidate 方法,传入int数组

15.png

问:isValidate 方法里面又做了什么?

答:简述就是根据decode.dex 生成了2.dex,然后加载2.dex,然后反射调用

com.zj.wuaipojie2024_2.A 方法 d ,并传入字符串,显而易见这个str 就是 九宫格密码

16.png

那么我们现在使用代码,把这个 2.dex 生成出来看看代码逻辑。


private static ByteBuffer read() {

        try {

            File file = new File("C:\\Users\\Administrator\\Desktop\\1.dex");

            if (!file.exists()) {

                return null;

            }

            FileInputStream fileInputStream = new FileInputStream(file);

            byte[] bArr = new byte[fileInputStream.available()];

            fileInputStream.read(bArr);

            ByteBuffer wrap = ByteBuffer.wrap(bArr);

            fileInputStream.close();

            return wrap;

        } catch (Exception unused) {

            unused.printStackTrace();

            return null;

        }

    }

    private static File fix(ByteBuffer byteBuffer, int i, int i2, int i3) throws Exception {

        try {

            File dir = new File("C:\\Users\\Administrator\\Desktop");

            int intValue = D.getClassDefData(byteBuffer, i).get("class_data_off").intValue();

            HashMap<String, int[][]> classData = D.getClassData(byteBuffer, intValue);

            classData.get("direct_methods")[i2][2] = i3;

            byte[] encodeClassData = D.encodeClassData(classData);

            byteBuffer.position(intValue);

            byteBuffer.put(encodeClassData);

            byteBuffer.position(32);

            byte[] bArr = new byte[byteBuffer.capacity() - 32];

            byteBuffer.get(bArr);

            byte[] sha1 = Utils.getSha1(bArr);

            byteBuffer.position(12);

            byteBuffer.put(sha1);

            int checksum = Utils.checksum(byteBuffer);

            byteBuffer.position(8);

            byteBuffer.putInt(Integer.reverseBytes(checksum));

            byte[] array = byteBuffer.array();

            File file = new File(dir, "2.dex");

            FileOutputStream fileOutputStream = new FileOutputStream(file);

            fileOutputStream.write(array);

            fileOutputStream.close();

            return file;

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }

ByteBuffer read = read();

File fix = fix(read, 0, 3, 7908);

fix方法3个int值就是一开始传入的int数组

17.png
18.png

生成的2.dex直接拖入jadx-gui,直接到A-d 方法


    public static String d(Context context, String str) {

        MainActivity.sSS(str);

        String signInfo = Utils.getSignInfo(context);

        if (signInfo == null || !signInfo.equals(C.SIGNATURE)) {

            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;

        }

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

    }

19.png

可以看到,把密码进行了对比,那我们把代码运行一次,看看密码时多少。

20.png

可以看到正确的九宫格密码是:048531267

QQ截图20240227152917.png

密码正常,成功跳转

21.png

这个是发现 2.dex 类B 方法d

22.png

这显示不是正确的flag。

这时细心的小伙伴就发现,上面的数组还有一组数组D- 1,1,8108,那

我们再用这组数组生成一个新的 3.dex

将3.dex 拖入jadx-gui查看代码:

打开B.d 方法

24.png


public class B {

    public static String d(String str) {

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

    }

}

那么这个算法生成出来的结果就是flag

五:结束

自此这道题就算解题成功,200cb到手。 本地难度适中,掌握dex的结构和代码分析能力就能很快解题。

dex.zip

26.09 KB, 下载次数: 15, 下载积分: 吾爱币 -1 CB

dex

免费评分

参与人数 8威望 +2 吾爱币 +107 热心值 +8 收起 理由
sanbuqianjin + 1 + 1 谢谢@Thanks!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xilinly + 1 谢谢@Thanks!
IIce_ + 2 + 1 谢谢@Thanks!
ytfh1131 + 1 + 1 谢谢@Thanks!
hellozl + 1 + 1 这个思路清晰!
云天逵 + 1 谢谢@Thanks!
正己 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

fnckyon2014 发表于 2024-2-27 16:15
666,这个强
z2ctian 发表于 2024-2-27 16:43
tony118 发表于 2024-2-27 17:18
zhenzhuxuebao 发表于 2024-2-27 20:30
牛啊哥有理有据
ssk147359 发表于 2024-2-28 10:42

大佬太强了
wendao_lx 发表于 2024-2-28 15:08
这个的确很复杂,小白不得行了
 楼主| ty1937 发表于 2024-2-28 16:30
wendao_lx 发表于 2024-2-28 15:08
这个的确很复杂,小白不得行了

所以你看懂了没
geekchina 发表于 2024-2-29 21:30
牛人,这水平可以当老师
shiyuping2008 发表于 2024-3-1 08:42
不错不错
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-21 22:13

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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