DASCTF 2025下半年赛-Reverse

本文最后更新于 2025年12月7日 下午

ezmac

加密逻辑,简单异或

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
__int64 __fastcall sub_10000045C(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5)
{
return sub_100000634(a1, a2, a3, a4, a5, 57);
}

__int64 __fastcall sub_100000634(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, char a6)
{
unsigned __int8 *v6; // x21
char v7; // w3
unsigned __int8 *v8; // x21
int v9; // t1
unsigned __int8 v11; // w3
unsigned __int8 *v12; // x21

while ( 1 )
{
v9 = *v6;
v8 = v6 + 1;
v7 = v9;
if ( !v9 )
break;
v11 = v7 ^ a6++;
v12 = v8 - 1;
*v12 = v11;
v6 = v12 + 1;
}
return sub_100000654();
}

解密脚本

1
2
3
4
5
6
7
data = [0x7D, 0x7B, 0x68, 0x7F, 0x69, 0x78, 0x44, 0x78, 0x72, 0x21, 0x74, 0x76, 0x75, 0x22, 0x26, 0x7B, 0x7C, 0x7E, 0x78, 0x7A, 0x2E, 0x2D, 0x7F, 0x2D]
res = []
for i,b in enumerate(data):
key = 57 + i
orig = b ^ key
res.append(orig)
print(''.join(chr(x) for x in res))

flag

1
DASCTF{83c720da35436cc0}

androidfile

Java 层生成两个随机字符串 s、s1 作为 AES 的 key 和 iv,再进行 RSA 公钥加密,调用 native 层

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.dasctf.androidfile;

import R0.c;
import android.content.res.Resources;
import android.os.Build.VERSION;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.J;
import androidx.activity.K;
import androidx.activity.p;
import androidx.activity.q;
import androidx.activity.r;
import androidx.activity.s;
import androidx.activity.t;
import androidx.activity.u;
import c0.a;
import f.g;
import f.h;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MainActivity extends h {
public TextView A;
public Button y;
public TextView z;

static {
System.loadLibrary("androidfile");
}

public MainActivity() {
a a0 = new a(this);
this.d.b.f("androidx:appcompat", a0);
this.i(new g(this));
}

public static String A() {
StringBuffer stringBuffer0 = new StringBuffer();
Random random0 = new Random();
for(int v = 0; v < 16; ++v) {
stringBuffer0.append(((char)"0123456bcdefghijklmnopqrstuvwEFGHIJKLMNOPQRSTUVWXYZ".charAt(random0.nextInt(51))));
}

return stringBuffer0.toString();
}

public static String C(String s, String s1, String s2) {
Cipher cipher0 = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher0.init(1, new SecretKeySpec(s1.getBytes(), "AES"), new IvParameterSpec(s2.getBytes()));
return Base64.encodeToString(cipher0.doFinal(s.getBytes("UTF-8")), 0);
}

public static String D(String s) {
X509EncodedKeySpec x509EncodedKeySpec0 = new X509EncodedKeySpec(Base64.decode("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ3AfAR+HoKn8iQaFT8xjSLkJf+uHuX5dSH/gsLSAlqIkVeADHx7okRAfl5U2sCe0A/2SY9sDurGOLHTYcmHAuECAwEAAQ==", 0));
PublicKey publicKey0 = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec0);
Cipher cipher0 = Cipher.getInstance("RSA");
cipher0.init(1, publicKey0);
return Base64.encodeToString(cipher0.doFinal(s.getBytes()), 0);
}

private native String a_p(String arg1) {
}

@Override // f.h
public final void onCreate(Bundle bundle0) {
r r0;
super.onCreate(bundle0);
K k0 = new K(0, 0, J.a);
K k1 = new K(p.a, p.b, J.a);
View view0 = this.getWindow().getDecorView();
c.d(view0, "window.decorView");
Resources resources0 = view0.getResources();
c.d(resources0, "view.resources");
boolean z = ((Boolean)J.a.b(resources0)).booleanValue();
Resources resources1 = view0.getResources();
c.d(resources1, "view.resources");
boolean z1 = ((Boolean)J.a.b(resources1)).booleanValue();
int v = Build.VERSION.SDK_INT;
if(v >= 30) {
r0 = __alloc_invoke__ u, Object(); // Mismatched object-allocation/constructor-invocation, likely indicating an inlined constructor call
}
else if(v >= 29) {
r0 = __alloc_invoke__ t, Object(); // Mismatched object-allocation/constructor-invocation, likely indicating an inlined constructor call
}
else if(v >= 28) {
r0 = __alloc_invoke__ s, Object(); // Mismatched object-allocation/constructor-invocation, likely indicating an inlined constructor call
}
else if(v >= 26) {
r0 = __alloc_invoke__ r, Object(); // Mismatched object-allocation/constructor-invocation, likely indicating an inlined constructor call
}
else {
r0 = __alloc_invoke__ q, Object(); // Mismatched object-allocation/constructor-invocation, likely indicating an inlined constructor call
}

Window window0 = this.getWindow();
c.d(window0, "window");
r0.C0(k0, k1, window0, view0, z, z1);
Window window1 = this.getWindow();
c.d(window1, "window");
r0.d(window1);
this.setContentView(0x7F0B001C); // layout:activity_main
this.y = (Button)this.findViewById(0x7F080130); // id:mybutton1
this.z = (TextView)this.findViewById(0x7F0800AC); // id:edit_text_1
this.A = (TextView)this.findViewById(0x7F0800AD); // id:edit_text_2
new String("MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAncB8BH4egqfyJBoVPzGNIuQl/64e5fl1If+CwtICWoiRV4AMfHuiREB+XlTawJ7QD/ZJj2wO6sY4sdNhyYcC4QIDAQABAkEAh81Gdg+kcFHoD9AsbkRX/atuUtcwXkYL4gK2LMThpdEFHIO7Scr+SYfwqmm/LMtkbojEGEnNoIfmoLvGfhXaAQIhANDWo8OSMSQFnvh129cFiVfYKlS4ec24ixvFD8fUD4SRAiEAwWBuZ3kox1n21AsTAxom+E3z5KUUOSUjPXvG6tZBgVECIDOP2y0tSi6/qIll6BqFxmxG9eSnC4PMfaQkmonXBOHRAiBmJUPsUGmj8/eXxknCp7vSCYs9SZ3HGcDlp05Jmed8IQIhAJnE1PNe9lC5OazgRYhSG6bGCTbfFHT6OuwCVIxRSx4P");
String s = MainActivity.A();
String s1 = MainActivity.A();
this.y.setOnClickListener(new i0.a(this, s, "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ3AfAR+HoKn8iQaFT8xjSLkJf+uHuX5dSH/gsLSAlqIkVeADHx7okRAfl5U2sCe0A/2SY9sDurGOLHTYcmHAuECAwEAAQ==", s1));
}
}

给了数据

1
2
3
4
5
6
7
8
9
10
11
12
EvB2udc3ofALSbCxeH5j4O2QZjfyZ151Nj3tOBVpt+99XXudbbzYknID0CxFcVO5+Vf16SjxzVbCuOizTIm3TVXXprsM1IlyjzJnTIUc8s4cFIX+clb1zN5PqUm11Z9LDlUMGYu+fa0fZqB5o7EMXWJvl+uKOsk/K3zzrnU0Rdpn/Ylm0ZBBDqpaNDYeXkGM52Uj6NxOhRRMaW2VcH/u4rNg7y7/X6OKa68G2TstGohwelnKpzgp4eFBNxn2<-encrypt

input->UBUSWb+1P3Z/aokV67e5xQ7eaHoEj3JAeC0XA1RckTWdWZYCB/+D7qC3Hao74goX

获取的RSA公钥和私钥
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ3AfAR+HoKn8iQaFT8xjSLkJf+uHuX5 dSH/gsLSAlqIkVeADHx7okRAfl5U2sCe0A/2SY9sDurGOLHTYcmHAuECAwEAAQ\=\=
-----END PUBLIC KEY-----

-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAncB8BH4egqfyJBoVPzGNIuQl/64e5fl1If+CwtICWoiRV4AMfHuiREB+XlTawJ7QD/ZJj2wO6sY4sdNh yYcC4QIDAQABAkEAh81Gdg+kcFHoD9AsbkRX/atuUtcwXkYL4gK2LMThpdEFHIO7 Scr+SYfwqmm/LMtkbojEGEnNoIfmoLvGfhXaAQIhANDWo8OSMSQFnvh129cFiVfY KlS4ec24ixvFD8fUD4SRAiEAwWBuZ3kox1n21AsTAxom+E3z5KUUOSUjPXvG6tZB gVECIDOP2y0tSi6/qIll6BqFxmxG9eSnC4PMfaQkmonXBOHRAiBmJUPsUGmj8/eX xknCp7vSCYs9SZ3HGcDlp05Jmed8IQIhAJnE1PNe9lC5OazgRYhSG6bGCTbfFHT6 OuwCVIxRSx4P
-----END PRIVATE KEY-----

so 里面是标准 RC4 加密

1
2
3
4
enkey_QMz2qirA80LJiOs30Efl00JsrIv+ZdrM9iB74P/nCWOrzEemEOaq2lN1/V5/rOAoTgBanJO/Acpo
okhVIOVdsA==
eniv_hKH/M/v8zwVICeWlc652BZk2eA/c2g0cLpBwvWBVlphiwBBasdn9HPWk7sb/IaRh8eppZrToUwz6
f1eomFJkEQ=

解得 RSA 加密后的 key 和 iv,最后用私钥进行解密密文

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
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES
from Crypto.Util.number import bytes_to_long, long_to_bytes
from Crypto.Util.Padding import unpad

RSA_PRIV_KEY = """-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAncB8BH4egqfyJBoV
PzGNIuQl/64e5fl1If+CwtICWoiRV4AMfHuiREB+XlTawJ7QD/ZJj2wO6sY4sdNh
yYcC4QIDAQABAkEAh81Gdg+kcFHoD9AsbkRX/atuUtcwXkYL4gK2LMThpdEFHIO7
Scr+SYfwqmm/LMtkbojEGEnNoIfmoLvGfhXaAQIhANDWo8OSMSQFnvh129cFiVfY
KlS4ec24ixvFD8fUD4SRAiEAwWBuZ3kox1n21AsTAxom+E3z5KUUOSUjPXvG6tZB
gVECIDOP2y0tSi6/qIll6BqFxmxG9eSnC4PMfaQkmonXBOHRAiBmJUPsUGmj8/eX
xknCp7vSCYs9SZ3HGcDlp05Jmed8IQIhAJnE1PNe9lC5OazgRYhSG6bGCTbfFHT6
OuwCVIxRSx4P
-----END PRIVATE KEY-----"""

RSA_ENC_KEY_B64 = "QMz2qirA80LJiOs30Efl00JsrIv+ZdrM9iB74P/nCWOrzEemEOaq2lN1/V5/rOAoTgBanJO/AcpookhVIOVdsA=="
RSA_ENC_IV_B64 = "hKH/M/v8zwVICeWlc652BZk2eA/c2g0cLpBwvWBVlphiwBBasdn9HPWk7sb/IaRh8eppZrToUwz6f1eomFJkEQ="
TARGET_FLAG_B64 = "UBUSWb+1P3Z/aokV67e5xQ7eaHoEj3JAeC0XA1RckTWdWZYCB/+D7qC3Hao74goX"

def safe_b64decode(d):
d = d.strip().replace("\n","").replace("\r","")
m = len(d) % 4
if m:
d += "=" * (4 - m)
return base64.b64decode(d)

def raw_rsa_decrypt(enc, key):
c = bytes_to_long(safe_b64decode(enc))
m = long_to_bytes(pow(c, key.d, key.n))
return m[-16:] if len(m) >= 16 else m.rjust(16, b'\x00')

k = RSA.import_key(RSA_PRIV_KEY)
aes_k = raw_rsa_decrypt(RSA_ENC_KEY_B64, k)
aes_iv = raw_rsa_decrypt(RSA_ENC_IV_B64, k)

cipher = AES.new(aes_k, AES.MODE_CBC, aes_iv)
pt = cipher.decrypt(safe_b64decode(TARGET_FLAG_B64))
flag = unpad(pt, 16).decode("utf-8")

print(flag)

拿到 flag

1
DASCTF{android_encrypto_file_and_plains}

login

client 端发送了三个字段,account,key,password

并且 sub_49DD 和 sub_4125 函数这里进行了 AES-128 CBC 加密

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
unsigned __int64 __fastcall sub_49DD(__int64 a1, unsigned int a2, __int64 a3, _QWORD *a4, __int64 a5)
{
__int64 *v5; // rax
__int64 v6; // rdx
_QWORD *v7; // rax
__int64 v8; // rdx
int v13; // [rsp+24h] [rbp-3Ch]
int i; // [rsp+34h] [rbp-2Ch]
__int64 v15; // [rsp+38h] [rbp-28h]
__int64 v16; // [rsp+40h] [rbp-20h] BYREF
__int64 v17; // [rsp+48h] [rbp-18h]
unsigned __int64 v18; // [rsp+58h] [rbp-8h]

v18 = __readfsqword(0x28u);
v15 = sub_48ED(a1, a2, 16);
v13 = sub_49A4(a2, 16);
for ( i = 0; i < v13 / 16; ++i )
{
v16 = 0;
v17 = 0;
v5 = (16 * i + v15);
v6 = v5[1];
v16 = *v5;
v17 = v6;
sub_4125(a3, 16, &v16, 16 * i + a5, 16, a4);
v7 = (16 * i + a5);
v8 = v7[1];
*a4 = *v7;
a4[1] = v8;
}
return __readfsqword(0x28u) ^ v18;
}

__int64 __fastcall sub_4125(const void *a1, unsigned int a2, __int64 a3, __int64 a4, unsigned int a5, __int64 a6)
{
int v6; // eax
__int64 v8; // [rsp+18h] [rbp-1E8h]
unsigned int v10; // [rsp+30h] [rbp-1D0h]
int v11; // [rsp+34h] [rbp-1CCh]
unsigned int i; // [rsp+38h] [rbp-1C8h]
int j; // [rsp+3Ch] [rbp-1C4h]
__int64 v14; // [rsp+40h] [rbp-1C0h]
_BYTE *v15; // [rsp+48h] [rbp-1B8h]
_QWORD v16[2]; // [rsp+50h] [rbp-1B0h] BYREF
_BYTE v17[368]; // [rsp+60h] [rbp-1A0h] BYREF
__int64 v18; // [rsp+1D0h] [rbp-30h]
__int64 v19; // [rsp+1D8h] [rbp-28h]
_QWORD dest[4]; // [rsp+1E0h] [rbp-20h] BYREF

v8 = a3;
dest[3] = __readfsqword(0x28u);
v10 = 0;
v11 = 0;
while ( a5 > v10 )
{
while ( v11 <= 15 )
{
v6 = v11++;
*(v10++ + a3) ^= *(v6 + a6);
}
v11 = 0;
}
v14 = a4;
v15 = v17;
v18 = 0;
v19 = 0;
dest[0] = 0;
dest[1] = 0;
v16[0] = 0;
v16[1] = 0;
if ( a1 && a3 && a4 )
{
if ( a2 <= 0x10 )
{
if ( (a5 & 0xF) != 0 )
{
puts("inLen is invalid.");
return 0xFFFFFFFFLL;
}
else
{
memcpy(dest, a1, a2);
sub_33F6(dest, 16, v17);
for ( i = 0; a5 > i; i += 16 )
{
sub_3323(v16, v8);
sub_36C5(v16, v17);
for ( j = 1; j <= 9; ++j )
{
v15 += 16;
sub_37D2(v16);
sub_38D8(v16);
sub_3CB1(v16);
sub_36C5(v16, v15);
}
sub_37D2(v16);
sub_38D8(v16);
sub_36C5(v16, v15 + 16);
sub_338C(v16, v14);
v14 += 16;
v8 += 16;
v15 = v17;
}
return 0;
}
}
else
{
puts("keyLen must be 16.");
return 0xFFFFFFFFLL;
}
}
else
{
puts("param err.");
return 0xFFFFFFFFLL;
}
}

server 这边收到的数据依次进行校验,account -> byte_C1A0,key -> byte_C0A0,password -> byte_CA20

account 和 key 验证之后调用 sub_73B2 函数进行了 RSA 解密,这里拿到 RSA 私钥也就是 off_C2F0 ,然后对这两个字段的数据进行解密

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii

mod_hex = (
"9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb13"
"09b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4"
"c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224"
"d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5"
"fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741f"
"ad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bb"
"d81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2cc"
"fdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe2"
"4d3"
)
exp_hex = "10001"
priv_hex = (
"28c7df24a5798679db2a44979275f5f3179db180d91335702942fb1b70e985de"
"825da90f2eb65d20ddf8be1d9d4e15bc1d84e95795ff8c0c28ce3c33fde054f6"
"e82a4f4cc22597b350c9c62ccc0188bd4152a701a3601558f22aa9fae8b9fdac"
"6c2bc09b1637f71e0511805e04b203c4fdb2b36ad232fe819b06ed4e57c74f39"
"fd9b72623c16ff2100f148f622bf12876260c4859672360dc0da3da6b45c5c8c"
"6215ccda072765840c213fba11a91d6bf598a8a8065797566c8950a34ea0a072"
"a9ed0c38bdc58662f186ec578ca55d5098443fd566cc722ace9c4e89afc4e302"
"c8a4870e11a003b935f4a102695bfd64bb0fa74dcc372682e2b24ff45a1a69"
)

acct_hex = (
"1638e0eb936140b5527033292cbefcd73b55cfc7fb79df51ae3768a0dd9c84ae"
"4580e47a5133b425f4c93eac97e4b1aa0b4cd30589d004f6d0d19fcbc709e86c"
"c2996b433d29f650b69987a466f05bef7f69945860dcc44742a511f3621385c8"
"9fbd4d73153615789634b25cfc3151a4115bc30c96979e5f965290f36a863e33"
"78b5cfc9ba31438c4bae22b23ef815edf7cf1771803bd392a5072b468900b75f"
"5a4377d1daf3d6f7b7b6850d1a4a4134f2f65840efaa9b83d31083051df0fc80"
"a786529159484f62bbb9524f68285f48c7ab8e03bdfeca1a6025aaed9f9728b3"
"90689c0c963920c728eb5695fcb9413f9f4e06d3b93db40e26d6275c84e6126a"
)

key_hex = (
"373a2a27b38fd778c716728ebb95be89a0a057109119a08d5ce49261ebb0e077"
"6d254a40c4d21bd2463e61608771de401eed13ac6660d996bea8c8b82bdd0eaf"
"56c38466776eba31f7b2219230b654a77ec0af395a01c31c139a4f6b7b8ba845"
"192096165dd7acd0331e79dbe434ed8c9a66581d26f69e5faa295f66010076b9"
"1a6dd61db7abd325f8bd25d928debcc02e5555ff81f7ae3e548e3e4659a37f5d"
"3d3c39fbcad1b583e42fb04fa328ebb77e7841f45b711e77ee23e11989db2c0e"
"06b8191a456d56bd1a7d42c47fdfdf1179228b57c6efca9b9b6a7d22682e5b67"
"c7c46a877fb677f5f317b4823fcdc812f0362be27c0f5453037148ed30127b26"
)

acct_bytes = binascii.unhexlify(acct_hex)
key_bytes = binascii.unhexlify(key_hex)

mod = int(mod_hex, 16)
exp = int(exp_hex, 16)
priv = int(priv_hex, 16)

rsa = RSA.construct((mod, exp, priv))
cipher = PKCS1_OAEP.new(rsa)

aes_iv = cipher.decrypt(acct_bytes)
aes_key = cipher.decrypt(key_bytes)

print("AES key:", aes_key)
print("AES iv:", aes_iv)

解得 key 和 iv

1
2
AES key: b'qqwweerrttyyuuii'
AES iv: b'aassddffgghhjjll'

根据拿到的 key 和 iv 解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Cipher import AES

key = b'qqwweerrttyyuuii'
iv = b'aassddffgghhjjll'

raw_bytes = bytes([
0xAD, 0xD1, 0xD1, 0x19, 0x60, 0xC2, 0x2D, 0x91, 0x66, 0xDA,
0xC3, 0xC2, 0x67, 0x25, 0xC8, 0x19, 0x09, 0x17, 0x6B, 0x23,
0x8E, 0x30, 0x03, 0xAA, 0x57, 0xAA, 0xCB, 0xA0, 0xA2, 0x26,
0xB7, 0xC3, 0x1C, 0x22, 0x0B, 0x8D, 0x20, 0x9C, 0xB4, 0x95,
0xB5, 0x5D, 0xB4, 0xE2, 0x7D, 0x4E, 0x43, 0x8E
])

cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(raw_bytes)
print(decrypted.decode('utf-8', errors='ignore'))

得到 flag

1
DASCTF{dqmaxfwkm921kr21m;df1m1dqmlk1d12d1}

androidfff

flutter 框架的 apk,blutter 解包后 IDA 加载 libapp.so 导入符号表(结束之后 p3c0wn 师傅给我说这个可以直接看 asm\untitled3\main.dart 文件,确实这样不需要载入符号表了更方便^~^)

跟进定位到 untitled3_main__FlagCheckerState::ctor_2f7c1c 这里,初始化一个 FlagCheckerState 对象,并向对象内部写入一段字节数组进行校验,这就是密文

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
__int64 __fastcall untitled3_main__FlagCheckerState::ctor_2f7c1c(__int64 a1, __int64 a2)
{
__int64 v2; // x15
__int64 v3; // x22
__int64 v4; // x26
_QWORD *v5; // x27
unsigned __int64 v6; // x28
__int64 v7; // x29
__int64 v8; // x30
__int64 v9; // x29
__int64 v10; // x0
__int64 v11; // x2
__int64 TextEditingControllerStub_2f7e14; // x1
__int64 v13; // x0
__int64 v14; // x0
int v15; // w1
__int64 v16; // x0
__int64 v17; // x3
__int64 ArrayStub_3b8244; // x0
__int64 GrowableArrayStub_3b716c; // x0
__int64 v20; // x1

*(v2 - 16) = v7;
*(v2 - 8) = v8;
v9 = v2 - 16;
v10 = v5[60];
v11 = a2;
*(v2 - 24) = a2;
if ( (v2 - 32) <= *(v4 + 56) )
LODWORD(v10) = StackOverflowSharedWithoutFPURegsStub_3b834c(v10);
*(v11 + 23) = v10;
TextEditingControllerStub_2f7e14 = AllocateTextEditingControllerStub_2f7e14();
v13 = v5[5963];
*(v9 - 16) = TextEditingControllerStub_2f7e14;
*(TextEditingControllerStub_2f7e14 + 39) = v13;
*(TextEditingControllerStub_2f7e14 + 7) = 0;
*(TextEditingControllerStub_2f7e14 + 19) = 0;
*(TextEditingControllerStub_2f7e14 + 27) = 0;
v14 = *(*(v4 + 104) + 2520LL);
if ( v14 == v5[8] )
LODWORD(v14) = InitLateFinalStaticFieldStub_3b6290();
v15 = v14;
v16 = *(v9 - 16);
*(v16 + 15) = v15;
v17 = *(v9 - 8);
*(v17 + 19) = v16;
if ( (*(v16 - 1) & (*(v17 - 1) >> 2) & HIDWORD(v6)) != 0 )
v16 = WriteBarrierWrappersStub_3b69a4();
ArrayStub_3b8244 = AllocateArrayStub_3b8244(v16, v5[275], 52);
*(v9 - 16) = ArrayStub_3b8244;
*(ArrayStub_3b8244 + 15) = 236;
*(ArrayStub_3b8244 + 19) = 230;
*(ArrayStub_3b8244 + 23) = 194;
*(ArrayStub_3b8244 + 27) = 226;
*(ArrayStub_3b8244 + 31) = 204;
*(ArrayStub_3b8244 + 35) = 232;
*(ArrayStub_3b8244 + 39) = 146;
*(ArrayStub_3b8244 + 43) = 168;
*(ArrayStub_3b8244 + 47) = 188;
*(ArrayStub_3b8244 + 51) = 142;
*(ArrayStub_3b8244 + 55) = 140;
*(ArrayStub_3b8244 + 59) = 140;
*(ArrayStub_3b8244 + 63) = 174;
*(ArrayStub_3b8244 + 67) = 128;
*(ArrayStub_3b8244 + 71) = 218;
*(ArrayStub_3b8244 + 75) = 182;
*(ArrayStub_3b8244 + 79) = 130;
*(ArrayStub_3b8244 + 83) = 218;
*(ArrayStub_3b8244 + 87) = 130;
*(ArrayStub_3b8244 + 91) = 186;
*(ArrayStub_3b8244 + 95) = 218;
*(ArrayStub_3b8244 + 99) = 174;
*(ArrayStub_3b8244 + 103) = 166;
*(ArrayStub_3b8244 + 107) = 130;
*(ArrayStub_3b8244 + 111) = 150;
*(ArrayStub_3b8244 + 115) = 158;
GrowableArrayStub_3b716c = AllocateGrowableArrayStub_3b716c();
*(GrowableArrayStub_3b716c + 15) = *(v9 - 16);
*(GrowableArrayStub_3b716c + 11) = 52;
v20 = *(v9 - 8);
*(v20 + 27) = GrowableArrayStub_3b716c;
if ( (*(GrowableArrayStub_3b716c - 1) & (*(v20 - 1) >> 2) & HIDWORD(v6)) != 0 )
WriteBarrierWrappersStub_3b6964();
return v3;
}

这里在进行 xor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 untitled3_main__FlagCheckerState::_anon_closure_29cba4()
{
__int64 v0; // x15
__int64 v1; // x29
__int64 v2; // x30
__int64 v3; // x3
__int64 result; // x0
__int64 v5; // x2

v3 = (*v0 << 32) >> 33;
if ( (*v0 & 1) != 0 )
v3 = *(*v0 + 7LL);
result = (2 * (v3 ^ 0x32));
if ( (v3 ^ 0x32) != result >> 1 )
{
*(v0 - 16) = v1;
*(v0 - 8) = v2;
result = AllocateMintSharedWithoutFPURegsStub_3b84cc();
*(result + 7) = v5;
}
return result;
}

因为 xor 函数中进行了 result = (2 * (v3 ^ 0x32)) 操作,所以对密文除以 2 才是真正的密文

1
DASCTF{fluttr_is_so_easy}

DASCTF 2025下半年赛-Reverse
http://example.com/2025/12/07/DASCTF-2025下半年赛-Reverse/
作者
butt3rf1y
发布于
2025年12月7日
许可协议