ty1937 发表于 2024-2-27 15:21

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

本帖最后由 ty1937 于 2024-2-27 15:35 编辑

# 一: 闲言

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



# 二:开始

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




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

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





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



```

    public boolean checkPassword(String str) {

      try {

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

            byte[] bArr = new byte;

            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加载有问题,发现了报错



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




那就上 010Editor ,拖入后





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



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

(https://ctf-wiki.org/android/basic_operating_mechanism/java_layer/dex/dex/#dex_5)



dex 头部中 checksum 和signature的介绍





那直接使用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())

```





重新填入后:



重新使用jadx-gtui 打开





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





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



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






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



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




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



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



# 四:分析代码逻辑



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



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





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

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

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







那么我们现在使用代码,把这个 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.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") = i3;

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

            byteBuffer.position(intValue);

            byteBuffer.put(encodeClassData);

            byteBuffer.position(32);

            byte[] bArr = new byte;

            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数组






生成的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";

    }



```






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




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





密码正常,成功跳转




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



这显示不是正确的flag。



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

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



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

打开B.d 方法



```

public class B {

    public static String d(String str) {

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

    }

}

```

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





# 五:结束

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

fnckyon2014 发表于 2024-2-27 16:15

{:1_893:} 666,这个强

z2ctian 发表于 2024-2-27 16:43

大佬太强了

tony118 发表于 2024-2-27 17:18

思路清楚{:1_921:}

zhenzhuxuebao 发表于 2024-2-27 20:30

牛啊哥{:17_1069:}有理有据

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
这个的确很复杂,小白不得行了

所以你看懂了没{:1_918:}

geekchina 发表于 2024-2-29 21:30

牛人,这水平可以当老师

shiyuping2008 发表于 2024-3-1 08:42

不错不错
页: [1] 2 3
查看完整版本: 2024春节解题{Android 中级题} 解题思路