安卓逆向刷题笔记

本文会持续更新

BUU-findit

题目链接:https://buuoj.cn/challenges#findit

xml里找到函数入口

image.png

可看到Java层逻辑

image.png

直接看到有两行关键代码

1
2
3
4
5
6
7
new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i]
new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1]

找到两个关键字符串!
ThisIsTheFlagHome

pvkq{m164675262033l4m49lnp7p9mnk28k75}

pvkq{m164675262033l4m49lnp7p9mnk28k75}这玩意一眼凯撒位移吧,十位解密之后得到flag

flag{c164675262033b4c49bdf7f9cda28a75}

BUU-简单注册器

题目链接:https://buuoj.cn/challenges#%E7%AE%80%E5%8D%95%E6%B3%A8%E5%86%8C%E5%99%A8

xml里找到函数入口

image.png

点进去看主要逻辑

image.png

发现输入的判断条件

1
2
3
if (xx.length() != 32 || xx.charAt(31) != 'a' || xx.charAt(1) != 'b' || (xx.charAt(0) + xx.charAt(2)) - 48 != 56) {
flag = 0;
}

可以直接按照以上判断条件输入符合的32位字符,点击按钮出flag,或者运行以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();

x[2] = (char) ((x[2] + x[3]) - 50);

x[4] = (char) ((x[2] + x[5]) - 48);

x[30] = (char) ((x[31] + x[9]) - 48);

x[14] = (char) ((x[27] + x[28]) - 97);

for (int i = 0; i < 16; i++) {

char a = x[31 - i];

x[31 - i] = x[i];

x[i] = a;

}
//可改成C代码后加个printf("%s",x);,即可打印出flag

总的来说,该代码生成的flag与用户输入无关,只要输入满足条件,程序就会对固定的字符串(x = "dd2940c04462b4dd7c450528835cca15")进行处理,因此直接运行代码或构造符合条件的输入均可得到正确flag

攻防世界-mobile easy-so

题目链接:https://adworld.xctf.org.cn/media/file/task/456c1dab04b24036ba1d6e32a08dc882.apk

xml里找到函数入口

image.png

点进去查看主要逻辑

image.png

发现逻辑校验关键函数CheckString,进去之后发现是调了so层的cyberpeace函数

image.png

在IDA里反编译so文件,分析关键函数逻辑

image.png

主要逻辑就是将获取到的字符串进行一系列变换和f72c5a36569418a20907b55be5bf95ad作比较,所以要写一段逆运算的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void reverse_operations(const char *input) {
size_t len = strlen(input);
char *output = (char *)malloc(len + 1);
strcpy(output, input);

// 反向执行字符交换
if (len >= 3) {
for (size_t i = 2; i < len; i += 2) {
char temp = output[i];
output[i] = output[i + 1];
output[i + 1] = temp;
}
}

// 反向交换前两个字符
if (len >= 2) {
char temp = output[0];
output[0] = output[1];
output[1] = temp;
}

// 反向执行每隔 16 个字符的交换
if (len >= 2) {
size_t half_len = len >> 1;
for (size_t i = 0; i < half_len; i++) {
char temp = output[i];
output[i] = output[half_len + i];
output[half_len + i] = temp;
}
}

// 输出结果
printf("Original string: %s/n", output);
free(output);
}

int main() {
const char *flag = "f72c5a36569418a20907b55be5bf95ad";
reverse_operations(flag);
return 0;
}

//===>Original string: 90705bb55efb59da7fc2a5636549812a

包上flag{}包裹,flag{90705bb55efb59da7fc2a5636549812a}

攻防世界-mobile 基础Android

题目链接:https://adworld.xctf.org.cn/media/file/task/6a0484a135bb44ba8fdcf829b5d9865b.apk

xml里找到函数入口

image.png

点进去分析主要逻辑:通过checkPassword函数对比字符串,成功后显示Good,Please go on!并进入MainActivity2

image.png

分析checkPassword函数逻辑

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
根据代码逻辑,字符的转换是:

pass[len] = (char) (((255 - len) - 100) - pass[len]);

我们需要使 `pass[len]` 转换后等于 `'0'`,即 ASCII 值为 48。
设 `x` 为原字符的 ASCII 值,可以得到方程:

(255 - len - 100) - x = 48`

简化后为:

x = 255 - len - 100 - 48
x = 107 - len`

因此,原字符的 ASCII 值应为 `107 - len`。根据 `len` 从 0 到 11,得到的字符应为:

- `len = 0` -> `107` -> `'k'`
- `len = 1` -> `106` -> `'j'`
- `len = 2` -> `105` -> `'i'`
- `len = 3` -> `104` -> `'h'`
- `len = 4` -> `103` -> `'g'`
- `len = 5` -> `102` -> `'f'`
- `len = 6` -> `101` -> `'e'`
- `len = 7` -> `100` -> `'d'`
- `len = 8` -> `99` -> `'c'`
- `len = 9` -> `98` -> `'b'`
- `len = 10` -> `97` -> `'a'`
- `len = 11` -> `96` -> '`' (反引号)

所以,一个有效的字符串是 "kjihgfedcba`",长度为 12

输入后进入下一界面

Screenshot_2025-02-23-21-51-43-34_8e9213dd12c701caf40fa4cb254e198a[1].jpg

根据代码逻辑此时来到了MainActivity2,分析逻辑

image.png

可以看到发送了一个广播,那么在xml布局文件里能看到被定义的广播接收器

image.png

看到关键字符串:android.is.very.fun,输入后即可得到flag

Screenshot_2025-02-23-21-55-58-11_8e9213dd12c701caf40fa4cb254e198a[1].jpg

flag{08067-wlecome}

PS:android:name="android.is.very.fun" 是自定义的广播动作,当其他组件发送一个包含 android.is.very.fun 动作的意图时,这个接收器就会被触发,所以与实际发送内容是无关的

NewStarCTF 2023 Week2-AndroDbgme

题目链接:

这道题jadx打开会炸,高版本jeb反编译是正常的,分析MainActivity主要逻辑

0e31b555efdd7a8e8565ce2d0eafa170.png

发现只要动调就会出flag

20cb06382ad821b6d28742caa5532561.png

那么在xml里的application加上一条件android:debuggable=”true”使app支持动调

Screenshot_2025-02-26-23-25-26-25_9e8df3d0c7c1f50248b6ee043a653d26[1].jpg

保存后需要自签名一下

Screenshot_2025-02-26-23-24-53-33_9e8df3d0c7c1f50248b6ee043a653d26[1].jpg

之后安装新的app

Screenshot_2025-02-26-23-25-00-82_9e8df3d0c7c1f50248b6ee043a653d26[1].jpg

jadx双击进程即可启动动调

image.png

image.png

此时在app里任意输入点击按钮即可得到flag

Screenshot_2025-02-26-22-50-02-73_7ca81305321c94afdc4b991336a8af21[1].jpg

flag{Let_1t_run_@t_f1rs7_m@ybe_th3_b3st}