HKCERT CTF2025-Reverse

本文最后更新于 2025年12月25日 凌晨

jar

可以看到用了 sm4 加密,main 函数只是在比对密文

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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class Main {
private static final String KEY_SEED = "happ";
private static final byte[] KEY = Sm4.deriveKeyFromSeed("happ");
private static final String TARGET_CIPHER_HEX = "21c2692a4775c413356a31fc55c38f6218bed9d46c45bd0eb777be9334c999d7";

public Main() {
}

public static void main(String[] var0) throws Exception {
BufferedReader var1 = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Input flag: ");
String var2 = var1.readLine();
if (var2 != null) {
if (var2.startsWith("flag{") && var2.endsWith("}")) {
byte[] var3 = Sm4.encrypt(KEY, var2.getBytes(StandardCharsets.UTF_8));
String var4 = Sm4.toHex(var3);
if (var4.equals("21c2692a4775c413356a31fc55c38f6218bed9d46c45bd0eb777be9334c999d7")) {
System.out.println("Correct");
} else {
System.out.println("Wrong");
}

} else {
System.out.println("Wrong");
}
}
}
}

sm4 密钥生成方式依赖 deriveKeyFromSeed,确定的 16 字节

1
2
3
4
5
6
7
8
public static byte[] deriveKeyFromSeed(String str) {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
byte[] bArr = new byte[16];
for (int i = 0; i < 16; i++) {
bArr[i] = (byte) (((bytes[i % bytes.length] & 255) + (i * 17) + 35) & 255);
}
return bArr;
}

ECB 模式加密,sbox 修改和变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static final byte[] SBOX = {-42, -112, -23, -2, -52, -31, 61, -73, 22, -74, 20, -62, 40, -5, 44, 5, 43, 103, -102, 118, 42, -66, 4, -61, -86, 68, 19, 38, 73, -122, 6, -103, -100, 66, 80, -12, -111, -17, -104, 122, 51, 84, 11, 67, -19, -49, -84, 98, -28, -77, 28, -87, -55, 8, -24, -107, Byte.MIN_VALUE, -33, -108, -6, 117, -113, 63, -90, 71, 7, -89, -4, -13, 115, 23, -70, -125, 89, 60, 25, -26, -123, 79, -88, 104, 107, -127, -78, 113, 100, -38, -117, -8, -21, 15, 75, 112, 86, -99, 53, 30, 36, 14, 94, 99, 88, -47, -94, 37, 34, 124, 59, 1, 33, 120, -121, -44, 0, 70, 87, -97, -45, 39, 82, 76, 54, 2, -25, -96, -60, -56, -98, -22, -65, -118, -46, 64, -57, 56, -75, -93, -9, -14, -50, -7, 97, 21, -95, -32, -82, 93, -92, -101, 52, 26, 85, -83, -109, 50, 48, -11, -116, -79, -29, 29, -10, -30, 46, -126, 102, -54, 96, -64, 41, 35, -85, 13, 83, 78, 111, -43, -37, 55, 69, -34, -3, -114, 47, 3, -1, 106, 114, 109, 108, 91, 81, -115, 27, -81, -110, -69, -35, -68, Byte.MAX_VALUE, 17, -39, 92, 65, 31, 16, 90, -40, 10, -63, 49, -120, -91, -51, 123, -67, 45, 116, -48, 18, -72, -27, -76, -80, -119, 105, -105, 74, 12, -106, 119, 126, 101, -71, -15, 9, -59, 110, -58, -124, 24, -16, 125, -20, 58, -36, 77, 32, 121, -18, 95, 62, -41, -53, 57, 72};

/* renamed from: FK */
private static final int[] f0FK = {-1548633402, 1453994832, 1736282519, -1301273892};

/* renamed from: CK */
private static final int[] f1CK = {462357, 472066609, 943670861, 1415275113, 1886879365, -1936483679, -1464879427, -993275175, -521670923, -66909679, 404694573, 876298825, 1347903077, 1819507329, -2003855715, -1532251463, -1060647211, -589042959, -117504499, 337322537, 808926789, 1280531041, 1752135293, -2071227751, -1599623499, -1128019247, -656414995, -184876535, 269950501, 741554753, 1213159005, 1684763257};
private static final byte[] SBOX_P = new byte[256];

static {
for (int i = 0; i < 256; i++) {
SBOX_P[i] = (byte) rotl8(SBOX[(i ^ 167) & 255] & 255, i & 3);
}
}


private static int sboxTransform(int i) {
return SBOX_P[(i ^ 60) & 255] & 255;
}

解密逻辑如下

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
from __future__ import annotations

SBOX = [-42, -112, -23, -2, -52, -31, 61, -73, 22, -74, 20, -62, 40, -5, 44, 5, 43, 103, -102, 118, 42, -66, 4, -61, -86, 68, 19, 38, 73, -122, 6, -103, -100, 66, 80, -12, -111, -17, -104, 122, 51, 84, 11, 67, -19, -49, -84, 98, -28, -77, 28, -87, -55, 8, -24, -107, -128, -33, -108, -6, 117, -113, 63, -90, 71, 7, -89, -4, -13, 115, 23, -70, -125, 89, 60, 25, -26, -123, 79, -88, 104, 107, -127, -78, 113, 100, -38, -117, -8, -21, 15, 75, 112, 86, -99, 53, 30, 36, 14, 94, 99, 88, -47, -94, 37, 34, 124, 59, 1, 33, 120, -121, -44, 0, 70, 87, -97, -45, 39, 82, 76, 54, 2, -25, -96, -60, -56, -98, -22, -65, -118, -46, 64, -57, 56, -75, -93, -9, -14, -50, -7, 97, 21, -95, -32, -82, 93, -92, -101, 52, 26, 85, -83, -109, 50, 48, -11, -116, -79, -29, 29, -10, -30, 46, -126, 102, -54, 96, -64, 41, 35, -85, 13, 83, 78, 111, -43, -37, 55, 69, -34, -3, -114, 47, 3, -1, 106, 114, 109, 108, 91, 81, -115, 27, -81, -110, -69, -35, -68, 127, 17, -39, 92, 65, 31, 16, 90, -40, 10, -63, 49, -120, -91, -51, 123, -67, 45, 116, -48, 18, -72, -27, -76, -80, -119, 105, -105, 74, 12, -106, 119, 126, 101, -71, -15, 9, -59, 110, -58, -124, 24, -16, 125, -20, 58, -36, 77, 32, 121, -18, 95, 62, -41, -53, 57, 72]
SBOX = [(x + 256) & 0xFF for x in SBOX]

FK = [(-1548633402 + (1 << 32)) & 0xFFFFFFFF, 1453994832, 1736282519, (-1301273892 + (1 << 32)) & 0xFFFFFFFF]
CK = [462357, 472066609, 943670861, 1415275113, 1886879365, (-1936483679 + (1 << 32)) & 0xFFFFFFFF, (-1464879427 + (1 << 32)) & 0xFFFFFFFF, (-993275175 + (1 << 32)) & 0xFFFFFFFF, (-521670923 + (1 << 32)) & 0xFFFFFFFF, (-66909679 + (1 << 32)) & 0xFFFFFFFF, 404694573, 876298825, 1347903077, 1819507329, (-2003855715 + (1 << 32)) & 0xFFFFFFFF, (-1532251463 + (1 << 32)) & 0xFFFFFFFF, (-1060647211 + (1 << 32)) & 0xFFFFFFFF, (-589042959 + (1 << 32)) & 0xFFFFFFFF, (-117504499 + (1 << 32)) & 0xFFFFFFFF, 337322537, 808926789, 1280531041, 1752135293, (-2071227751 + (1 << 32)) & 0xFFFFFFFF, (-1599623499 + (1 << 32)) & 0xFFFFFFFF, (-1128019247 + (1 << 32)) & 0xFFFFFFFF, (-656414995 + (1 << 32)) & 0xFFFFFFFF, (-184876535 + (1 << 32)) & 0xFFFFFFFF, 269950501, 741554753, 1213159005, 1684763257]

def rotl8(val, n):
n = n & 7
return ((val << n) | (val >> (8 - n))) & 0xFF

SBOX_P = [0] * 256
for i in range(256):
SBOX_P[i] = rotl8(SBOX[(i ^ 167) & 0xFF], i & 3)

def rotl(val, n):
return ((val << n) | (val >> (32 - n))) & 0xFFFFFFFF

def sbox_transform(x):
return SBOX_P[(x ^ 60) & 0xFF]

def tau(x):
b0 = sbox_transform((x >> 24) & 0xFF)
b1 = sbox_transform((x >> 16) & 0xFF)
b2 = sbox_transform((x >> 8) & 0xFF)
b3 = sbox_transform(x & 0xFF)
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3

def T(x):
t = tau(x)
return (t ^ rotl(t, 2) ^ rotl(t, 10) ^ rotl(t, 18) ^ rotl(t, 24)) & 0xFFFFFFFF

def TPrime(x):
t = tau(x)
return (t ^ rotl(t, 13) ^ rotl(t, 23)) & 0xFFFFFFFF

def bytes_to_int(b, off):
return ((b[off] & 0xFF) << 24) | ((b[off+1] & 0xFF) << 16) | ((b[off+2] & 0xFF) << 8) | (b[off+3] & 0xFF)

def int_to_bytes(val):
return bytes([(val >> 24) & 0xFF, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF])

def derive_key_from_seed(seed: str) -> bytes:
seed_bytes = seed.encode('utf-8')
key = bytearray(16)
for i in range(16):
key[i] = ((seed_bytes[i % len(seed_bytes)] & 0xFF) + (i * 17) + 35) & 0xFF
return bytes(key)

def expand_key(key: bytes) -> list:
k = [bytes_to_int(key, 0), bytes_to_int(key, 4), bytes_to_int(key, 8), bytes_to_int(key, 12)]
tmp = [0] * 36
for i in range(4):
tmp[i] = k[i] ^ FK[i]
rk = [0] * 32
for i in range(32):
tmp[i + 4] = (tmp[i] ^ TPrime(tmp[i+1] ^ tmp[i+2] ^ tmp[i+3] ^ CK[i])) & 0xFFFFFFFF
rk[i] = tmp[i + 4]
return rk

def decrypt_block(block: bytes, rk: list) -> bytes:
x = [0] * 36
x[0] = bytes_to_int(block, 0)
x[1] = bytes_to_int(block, 4)
x[2] = bytes_to_int(block, 8)
x[3] = bytes_to_int(block, 12)
for i in range(32):
x[i + 4] = (x[i] ^ T(x[i+1] ^ x[i+2] ^ x[i+3] ^ rk[31 - i])) & 0xFFFFFFFF
return int_to_bytes(x[35]) + int_to_bytes(x[34]) + int_to_bytes(x[33]) + int_to_bytes(x[32])

def pkcs7_unpad(data: bytes) -> bytes:
pad_len = data[-1]
return data[:-pad_len]

def decrypt(key: bytes, cipher: bytes) -> bytes:
rk = expand_key(key)
plain = bytearray()
for i in range(0, len(cipher), 16):
plain.extend(decrypt_block(cipher[i:i+16], rk))
return pkcs7_unpad(bytes(plain))

KEY_SEED = "happ"
TARGET_CIPHER_HEX = "21c2692a4775c413356a31fc55c38f6218bed9d46c45bd0eb777be9334c999d7"

key = derive_key_from_seed(KEY_SEED)
cipher = bytes.fromhex(TARGET_CIPHER_HEX)
plaintext = decrypt(key, cipher)
print("flag:", plaintext.decode('utf-8'))

拿到 flag

1
flag{Have_A_Nice_Dayyyy}

JN

java 层用了 RC4,校验的 flag 中间的 8 个字节

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
    static {
MainActivity.Companion = new Companion(null);
System.loadLibrary("xsran");
MainActivity.UNKNOWN_KEY = new byte[]{1, 35, 69, 103, (byte)0x89, -85, -51, -17, -2, -36, -70, -104, 0x76, 84, 50, 16};
MainActivity.JAVA_CIPHER = new byte[]{-58, 23, -12, -12, -74, 92, -50, (byte)0x90};
}

public final boolean J_Validate(byte[] arg2) {
Intrinsics.checkNotNullParameter(arg2, "input");
return Arrays.equals(this.unknownEncrypt(arg2, MainActivity.UNKNOWN_KEY), MainActivity.JAVA_CIPHER);
}

public final native boolean N_Valildate(byte[] arg1) {
}

@Override // androidx.activity.ComponentActivity
protected void onCreate(Bundle arg4) {
super.onCreate(arg4);
ComponentActivityKt.setContent$default(((ComponentActivity)this), null, ((Function2)ComposableLambdaKt.composableLambdaInstance(-1003124788, true, new MainActivity.onCreate.1(this))), 1, null);
this.startService(new Intent(((Context)this), MySimpleService.class));
}

public final byte[] unknownEncrypt(byte[] arg8, byte[] arg9) {
Intrinsics.checkNotNullParameter(arg8, "input");
Intrinsics.checkNotNullParameter(arg9, "key");
int[] v1 = new int[0x100];
int v2 = 0;
int v3;
for(v3 = 0; v3 < 0x100; ++v3) {
v1[v3] = v3;
}

int v3_1 = 0;
int v4 = 0;
while(v3_1 < 0x100) {
int v5 = v1[v3_1];
v4 = v4 + v5 + (arg9[v3_1 % arg9.length] & 0xFF) & 0xFF;
v1[v3_1] = v1[v4];
v1[v4] = v5;
++v3_1;
}

byte[] v9 = new byte[arg8.length];
int v3_2 = 0;
int v4_1 = 0;
while(v2 < arg8.length) {
v3_2 = v3_2 + 1 & 0xFF;
int v5_1 = v1[v3_2];
v4_1 = v4_1 + v5_1 & 0xFF;
v1[v3_2] = v1[v4_1];
v1[v4_1] = v5_1;
v9[v2] = (byte)(v1[v1[v3_2] + v5_1 & 0xFF] ^ arg8[v2]);
++v2;
}

return v9;
}

public final boolean validate(String arg6) {
Intrinsics.checkNotNullParameter(arg6, "flag");
if(arg6.length() != 22) {
return false;
}

String v6 = arg6.substring(5, 21);
Intrinsics.checkNotNullExpressionValue(v6, "substring(...)");
String v3 = v6.substring(0, 8);
Intrinsics.checkNotNullExpressionValue(v3, "substring(...)");
String v6_1 = v6.substring(8, 16);
Intrinsics.checkNotNullExpressionValue(v6_1, "substring(...)");
byte[] v0 = v3.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(v0, "getBytes(...)");
byte[] v6_2 = v6_1.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(v6_2, "getBytes(...)");
return (this.J_Validate(v0)) && (this.N_Valildate(v6_2));
}
}

so 层为 tea 系列加密

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
bool __fastcall Java_com_challenge_xsran_MainActivity_N_1Valildate(__int64 a1, __int64 a2, __int64 a3)
{
unsigned int v3; // eax
__int64 v4; // rsi
int v5; // r9d
unsigned int v6; // r10d
unsigned int v7; // r11d
_QWORD v9[2]; // [rsp+8h] [rbp-10h] BYREF

v9[1] = __readfsqword(0x28u);
(*(*a1 + 1600LL))(a1, a3, 0, 8, v9);
v3 = v9[0];
v4 = HIDWORD(v9[0]);
v5 = -32;
v6 = -1640531527;
do
{
v7 = v4;
v4 = (v6 >> 2) & 3;
v3 += (((v7 >> 5) ^ (4 * v7)) + ((v7 >> 3) ^ (16 * v7))) ^ ((v6 ^ v7) + (v7 ^ dword_590[v4]));
LODWORD(v4) = v7 + ((((v3 >> 5) ^ (4 * v3)) + ((v3 >> 3) ^ (16 * v3))) ^ ((v6 ^ v3) + (v3 ^ dword_590[v4 ^ 1])));
v6 -= 1640531527;
++v5;
}
while ( v5 );
return (v3 ^ 0x6421ACBE | v4 ^ 0xFA7CB432) == 0;
}


__int64 __fastcall sub_dec(unsigned int *a1, int n2, const unsigned int *a3)
{
__int64 result; // rax
unsigned int v5; // r14d
unsigned int v6; // r10d
unsigned int v7; // r12d
unsigned int v8; // edx
__int64 v9; // r15
unsigned int v10; // ecx

result = (52 / n2);
v5 = -1640531527 * result - 1253254570;
if ( -1640531527 * result != 1253254570 )
{
v6 = *a1;
do
{
v7 = (v5 >> 2) & 3;
v8 = v6;
v9 = (n2 - 1);
v10 = v6;
if ( n2 >= 2 )
{
do
{
v10 = a1[v9]
- ((((a1[(v9 - 1)] >> 5) ^ (4 * v10)) + ((v10 >> 3) ^ (16 * a1[(v9 - 1)])))
^ ((v5 ^ v10) + (a3[v7 ^ v9 & 3] ^ a1[(v9 - 1)])));
a1[v9] = v10;
}
while ( v9-- > 1 );
v6 = *a1;
v8 = v10;
}
result = v7;
v6 -= (((a1[n2 - 1] >> 5) ^ (4 * v8)) + ((v8 >> 3) ^ (16 * a1[n2 - 1]))) ^ ((v5 ^ v8) + (a3[v7] ^ a1[n2 - 1]));
*a1 = v6;
v5 += 1640531527;
}
while ( v5 );
}
return result;
}


dword_590 dd 3C2D1E0Fh, 78695A4Bh, 0B4A59687h, 0F0E1D2C3h

合并解密

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
MASK32 = 0xFFFFFFFF
DELTA = 0x9E3779B9

K = [0x3C2D1E0F, 0x78695A4B, 0xB4A59687, 0xF0E1D2C3]
TARGET_V0 = 0x6421ACBE
TARGET_V1 = 0xFA7CB432

def E(x: int) -> int:
x &= MASK32
return ((((x << 4) & MASK32) ^ (x >> 3)) + (((x << 2) & MASK32) ^ (x >> 5))) & MASK32

def round_f(x: int, sum_: int, k: int) -> int:
x &= MASK32
sum_ &= MASK32
k &= MASK32
return ((((k ^ x) + (x ^ sum_)) & MASK32) ^ E(x)) & MASK32

def native_decrypt(v0: int, v1: int) -> bytes:
v0 &= MASK32
v1 &= MASK32
sum_ = (DELTA * 32) & MASK32
for _ in range(32):
idx = (sum_ >> 2) & 3
v1 = (v1 - round_f(v0, sum_, K[idx ^ 1])) & MASK32
v0 = (v0 - round_f(v1, sum_, K[idx])) & MASK32
sum_ = (sum_ - DELTA) & MASK32
return v0.to_bytes(4, "little") + v1.to_bytes(4, "little")

part2 = native_decrypt(TARGET_V0, TARGET_V1).decode("latin1")

UNKNOWN_KEY = bytes.fromhex("0123456789abcdeffedcba9876543210")
JAVA_CIPHER = bytes.fromhex("c617f4f4b65cce90")

def rc4(data: bytes, key: bytes) -> bytes:
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]

i = j = 0
out = bytearray()
for b in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
out.append(b ^ S[(S[i] + S[j]) & 0xFF])
return bytes(out)

part1 = rc4(JAVA_CIPHER, UNKNOWN_KEY).decode("ascii")

mid16 = part1 + part2
flag = "flag{" + mid16 + "}"
print("flag :", flag)

得到 flag

1
flag{kokodayo~OoO~OoO}

onebyone

输入限制 userInput.length() == 24,否则直接退出

calculate 把 24 字符分成 3 组,每组 8 字符拼成 t_in,然后对 t_in 做 64 轮 t = (t<<1) ^ 0x72F9E1EBA0EA3693 操作,返回 3 个 64-bit 值 t_out,processInput() 把每个 t_out 按小端序拆成 8 字节,合计 24 字节 arr,再传入 jiami

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
public class MainActivity extends AppCompatActivity {
public ActivityMainBinding binding;

public native int[] jiami(int[] iArr);

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

@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
this.binding = inflate;
setContentView(inflate.mo270getRoot());
final EditText input = (EditText) findViewById(R.id.edit1);
Button calculateButton = (Button) findViewById(R.id.button1);
calculateButton.setOnClickListener(new View.OnClickListener() { // from class: com.example.onebyone.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
MainActivity.this.processInput(input.getText().toString());
}
});
}

public final void processInput(String userInput) {
int state = 0;
long[] result = null;
int[] arr = null;
int[] result1 = null;
int[] result2 = {206, 176, 51, 89, 115, 30, 199, 248, 5, 103, 255, 154, 27, 21, 228, 69, 190, 160, 235, 131, 5, 16, 112, 22};
while (true) {
switch (state) {
case 0:
if (userInput.length() != 24) {
state = 1;
break;
} else {
state = 2;
break;
}
case 1:
Toast.makeText(getApplicationContext(), "长度错误", 0).show();
System.exit(0);
return;
case 2:
result = calculate(userInput);
state = 3;
break;
case 3:
arr = new int[24];
state = 4;
break;
case 4:
for (int i = 0; i < 3; i++) {
long value = result[i];
for (int j = 0; j < 8; j++) {
arr[(i * 8) + j] = (int) ((value >> (j * 8)) & 255);
}
}
state = 5;
break;
case 5:
result1 = jiami(arr);
state = 6;
break;
case 6:
if (Arrays.equals(result1, result2)) {
state = 7;
break;
} else {
state = 8;
break;
}
case 7:
Toast.makeText(getApplicationContext(), "正确!", 0).show();
return;
case 8:
Toast.makeText(getApplicationContext(), "错误!", 0).show();
return;
default:
return;
}
}
}

public final long[] calculate(String input) {
int state = 0;
long[] arr4 = new long[3];
long[] a = new long[3];
int i = 0;
int j = 0;
long t = 0;
while (true) {
switch (state) {
case 0:
if (i < input.length()) {
state = 1;
} else {
state = 2;
}
case 1:
String group = input.substring(i, Math.min(i + 8, input.length()));
arr4[i / 8] = Long.parseLong(stringToHex(group).substring(0), 16);
i += 8;
state = 0;
case 2:
j = 0;
state = 3;
case 3:
if (j < 3) {
state = 4;
} else {
state = 7;
}
case 4:
t = arr4[j];
i = 0;
state = 5;
case 5:
if (i < 64) {
state = 6;
} else {
a[j] = t;
j++;
state = 3;
}
case 6:
if ((t & Long.MIN_VALUE) == Long.MIN_VALUE) {
t = ((Long.MAX_VALUE & t) * 2) ^ 8284901391658006163L;
} else {
t *= 2;
}
i++;
state = 5;
case 7:
return a;
default:
return a;
}
}
}

public static String stringToHex(String str) {
StringBuilder hex = new StringBuilder();
int state = 0;
int idx = 0;
char[] chars = str.toCharArray();
while (true) {
switch (state) {
case 0:
if (idx < chars.length) {
state = 1;
} else {
state = 2;
}
case 1:
hex.append(String.format("%02x", Integer.valueOf(chars[idx])));
idx++;
state = 0;
case 2:
return hex.toString();
default:
return hex.toString();
}
}
}
}

so 层为 RC4 小改加密,key 是 hswSss]e

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
__int64 __fastcall Java_com_example_onebyone_MainActivity_jiami(_JNIEnv *a1, __int64 a2, __int64 a3)
{
int i; // [rsp+14h] [rbp-8Ch]
__int64 v5; // [rsp+18h] [rbp-88h]
void *ptr; // [rsp+28h] [rbp-78h]
signed int ArrayLength; // [rsp+34h] [rbp-6Ch]
__int64 IntArrayElements; // [rsp+38h] [rbp-68h]
__int64 v11; // [rsp+68h] [rbp-38h] BYREF
_DWORD v12[10]; // [rsp+70h] [rbp-30h] BYREF
unsigned __int64 v13; // [rsp+98h] [rbp-8h]

v13 = __readfsqword(0x28u);
v12[0] = sub_DB0('h');
v12[1] = sub_DB0('s');
v12[2] = sub_DB0('w');
v12[3] = sub_DB0('S');
v12[4] = sub_DB0('s');
v12[5] = sub_DB0('s');
v12[6] = sub_DB0(']');
v12[7] = sub_DB0('e');
sub_DD0(v12, &v11, 8);
IntArrayElements = _JNIEnv::GetIntArrayElements(a1, a3, 0);
if ( !IntArrayElements )
return 0;
ArrayLength = _JNIEnv::GetArrayLength(a1, a3);
ptr = malloc(4LL * ArrayLength);
if ( ptr )
{
if ( (unsigned int)time(0) == 305419896 )
{
free(ptr);
return 0;
}
else
{
sub_F10(&v11, 8, IntArrayElements, ArrayLength, ptr);
v5 = _JNIEnv::NewIntArray(a1, ArrayLength);
if ( v5 )
{
_JNIEnv::SetIntArrayRegion(a1, v5, 0, ArrayLength, ptr);
for ( i = 0; i < 8; ++i )
*((_BYTE *)&v12[-2] + i) = 0;
free(ptr);
_JNIEnv::ReleaseIntArrayElements(a1, a3, IntArrayElements, 0);
return v5;
}
else
{
free(ptr);
_JNIEnv::ReleaseIntArrayElements(a1, a3, IntArrayElements, 0);
return 0;
}
}
}
else
{
_JNIEnv::ReleaseIntArrayElements(a1, a3, IntArrayElements, 0);
return 0;
}
}

PRGA 这里 *a3 = (*a3 + 2) % 256,不是标准的 i = (i + 1) % 256,keystream 索引 S[(S[i] + S[j] + 2) % 256] 多了 +2

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall sub_1280(__int64 a1, unsigned int a2, int *a3, int *a4)
{
char v5; // [rsp+Fh] [rbp-25h]

*a3 = (*a3 + 2) % 256;
*a4 = (*(a1 + *a3) + *a4) % 256;
v5 = *(a1 + *a3);
*(a1 + *a3) = *(a1 + *a4);
*(a1 + *a4) = v5;
return *(a1 + (*(a1 + *a4) + *(a1 + *a3) + 2) % 256) ^ a2;
}

解密脚本如下:

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
from __future__ import annotations

poly = 0x72f9e1eba0ea3693
mask64 = (1 << 64) - 1

ciphertext = [
206, 176, 51, 89, 115, 30, 199, 248,
5, 103, 255, 154, 27, 21, 228, 69,
190, 160, 235, 131, 5, 16, 112, 22,
]

key = b"hswSss]e"


def rc4_variant(key: bytes, data: bytes) -> bytes:
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + key[i % len(key)]) & 0xff
s[i], s[j] = s[j], s[i]

out = bytearray(len(data))
i = 0
j = 0
for n, b in enumerate(data):
i = (i + 2) & 0xff
j = (j + s[i]) & 0xff
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j] + 2) & 0xff]
out[n] = b ^ k
return bytes(out)


def gf_mul_x_step(t: int) -> int:
msb = (t >> 63) & 1
t = ((t << 1) & mask64)
if msb:
t ^= poly
return t


def gf_mul_x64(t: int) -> int:
for _ in range(64):
t = gf_mul_x_step(t)
return t


def gf_div_x_step(t: int) -> int:
msb = t & 1
u = t ^ (poly if msb else 0)
u >>= 1
return u | (msb << 63)


def gf_div_x64(t: int) -> int:
for _ in range(64):
t = gf_div_x_step(t)
return t


ct = bytes(ciphertext)
arr = rc4_variant(key, ct)

parts = []
for i in range(3):
chunk = arr[i * 8:(i + 1) * 8]
t_out = int.from_bytes(chunk, "little")
t_in = gf_div_x64(t_out)
raw = t_in.to_bytes(8, "big")
parts.append(raw.decode("ascii"))

flag = "".join(parts)
print(flag)

最终 Flag

1
flag{345623095654755648}

Wm

wasm,jeb 可以反编译看大部分伪代码

AES-128 ECB 模式加密,f2 是 SBOX,f3 是 AddRoundKey,f4 是 MixColumns

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
int check(int* param0, int* param1) {
int result;
int* ptr0;
int v0;
int v1 = __g0 - 144;

__g0 -= 144;
if((unsigned int)param1 <= 48) {
int v2 = v1 + 128;
while(ptr0 != 8) {
int v3 = __f2((int)(unsigned char)ptr0, (int)*(char*)(ptr0 + 260), (int)*(char*)(ptr0 + 0x100));
*(char*)((int)ptr0 + v2) = (unsigned char)v3;
ptr0 = (int*)((char*)ptr0 + 1);
}
ptr0 = (int*)0x8;
do {
int v4 = __f2((int)(unsigned char)ptr0, (int)*(char*)(ptr0 + 260), (int)*(char*)(ptr0 + 0x100));
*(char*)((int)ptr0 + v2) = (unsigned char)v4;
ptr0 = (int*)((char*)ptr0 + 1);
}
while(ptr0 != 16);
int v5 = v1 + 64;
int* ptr1 = (int*)((int)param1 & 0x30) + 4;
while(param1 != v0) {
*(char*)(v0 + v5) = *(char*)((int)param0 + v0);
++v0;
}
ptr0 = param1 >= ptr1 ? param1: ptr1;
param0 = (int*)((int)ptr1 - (int)param1);
while(param1 != ptr0) {
*(char*)((int)param1 + v5) = (unsigned char)param0;
param1 = (int*)((char*)param1 + 1);
}
if(ptr1 == 32) {
param1 = NULL;
do {
result = (int)param1 + v5;
int* ptr2 = (int*)((int)param1 + v1);
param0 = NULL;
int v6 = __g0 - 192;
__g0 -= 192;
while(param0 != 16) {
*(char*)((int*)((int)param0 + v6) + 44) = *(char*)((int)param0 + result);
param0 = (int*)((char*)param0 + 1);
}
param0 = NULL;
do {
*(char*)((int)param0 + v6) = *(char*)((int)param0 + v2);
param0 = (int*)((char*)param0 + 1);
}
while(param0 != 16);
int v7 = v6 - 16;
v0 = 1;
do {
ptr1 = (int*)(v0 * 17);
param0 = NULL;
ptr0 = (int*)(v0 * 16);
result = (int)(int*)(v0 * 16) + v7;
while(param0 != 16) {
*(char*)((int)(int*)((int)param0 + (int)ptr0) + v6) = (unsigned char)(int*)((int)(int*)((unsigned int)*(char*)((int)param0 + result) ^ (int)ptr1) ^ (int)param0);
param0 = (int*)((char*)param0 + 1);
}
++v0;
}
while(v0 != 11);
__f3(v6, v6 + 176);
param0 = (int*)0x1;
do {
result = v6 + 176;
ptr1 = NULL;
do {
char* ptr3 = (char*)(result + (int)ptr1);
*ptr3 = *(char*)((int*)*ptr3 + 264);
ptr1 = (int*)((char*)ptr1 + 1);
}
while(ptr1 != 16);
ptr1 = (int*)*(char*)(result + 1);
*(char*)(result + 1) = *(char*)(result + 5);
ptr0 = (int*)*(char*)(result + 9);
*(char*)(result + 9) = *(char*)(result + 13);
*(char*)(result + 5) = (unsigned char)ptr0;
*(char*)(result + 13) = (unsigned char)ptr1;
ptr0 = (int*)*(char*)(result + 2);
*(char*)(result + 2) = *(char*)(result + 10);
*(char*)(result + 10) = (unsigned char)ptr0;
ptr0 = (int*)*(char*)(result + 6);
*(char*)(result + 6) = *(char*)(result + 14);
*(char*)(result + 14) = (unsigned char)ptr0;
ptr0 = (int*)*(char*)(result + 15);
*(char*)(result + 15) = *(char*)(result + 11);
*(char*)(result + 11) = *(char*)(result + 7);
*(char*)(result + 7) = *(char*)(result + 3);
*(char*)(result + 3) = (unsigned char)ptr0;
if(param0 != 10) {
__f4(result);
__f4(result + 4);
__f4(result + 8);
__f4(result + 12);
}
__f3((int)(int*)((int)param0 * 16) + v6, result);
param0 = (int*)((char*)param0 + 1);
}
while(param0 != 11);
param0 = NULL;
do {
*(char*)((int)param0 + (int)ptr2) = *(char*)((int*)((int)param0 + v6) + 44);
param0 = (int*)((char*)param0 + 1);
}
while(param0 != 16);
__g0 = v6 + 192;
param1 += 4;
}
while((unsigned int)param1 < 32);
param1 = NULL;
loc_5000033E:
do {
param0 = param1;
if(param1 != 32) {
param1 = (int*)((char*)param0 + 1);
if((unsigned int)*(char*)((int)param0 + v1) == (unsigned int)*(char*)(param0 + 328)) {
goto loc_5000033E;
}
}
break;
}
while(1);
result = (int)((unsigned int)param0 > 31);
}
}

__g0 = v1 + 144;
return result;
}


int __f2(int param0, int param1, int param2) {
return (unsigned int)((unsigned char)param2 * 233 + (unsigned char)(param0 ^ param1));
}



void __f3(int param0, int param1) {
int v0;

while(v0 != 16) {
char* ptr0 = (char*)(param0 + v0);
*ptr0 = (unsigned char)((unsigned int)*(char*)(param1 + v0) ^ (unsigned int)*ptr0);
++v0;
}
}



void __f4(char* param0) {
int v0 = (int)*(param0 + 1);
int v1 = (unsigned int)*param0;
int v2 = (int)*(param0 + 1) ^ v1;
int v3 = __f5((int)*(param0 + 1) ^ v1);
int v4 = (unsigned int)*(param0 + 2);
int v5 = (unsigned int)*(param0 + 2) ^ v2;
int v6 = (unsigned int)*(param0 + 3);
int v7 = ((unsigned int)*(param0 + 2) ^ v2) ^ v6;

*param0 = (unsigned char)((v3 ^ v1) ^ v7);
int v8 = __f5(v0 ^ v4);
*(param0 + 1) = (unsigned char)((v0 ^ v8) ^ v7);
int v9 = __f5(v4 ^ v6);
*(param0 + 2) = (unsigned char)((v6 ^ v9) ^ v2);
int v10 = __f5(v1 ^ v6);
*(param0 + 3) = (unsigned char)(v5 ^ v10);
}



.code:50000418 __f5 proc
.code:50000418
.code:50000418 0 [0] local.get $L0
.code:5000041A 0 [1] i32.extend8_s
.code:5000041B 0 [1] i32.const 7
.code:5000041D 0 [2] i32.shr_u
.code:5000041E 0 [1] i32.const 1Bh
.code:50000420 0 [2] i32.and
.code:50000421 0 [1] local.get $L0
.code:50000423 0 [2] i32.const 1
.code:50000425 0 [3] i32.shl
.code:50000426 0 [2] i32.xor
.code:50000427 0 [1] i32.const FFh
.code:5000042A 0 [2] i32.and
.code:5000042B 0 [1] end
.code:5000042B
.code:5000042B __f5 endp

在 data 段中找到加密数据,从 0x64D 开始 到 0x66C 的 32 字节为加密的密文

解密脚本如下:

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
DATA_HEX = (
"8080584bc6165e9095b536aa5e54fe9aabcd17395e82f14a63b72c90d5087fe6"
"39262d21a831359f6a5b3d71a48df12c90d89327a0031daaf78ef8f5c6fe289a"
"eda7c97c6c65ad966effbfab2b826b4f5e9d799942cc5fc05d48dab8b17de82f"
"53d97640413400fa08618ce973b975de098b5ab77aa6eb013091e46310160295"
"8ab5f0a1191769df1fa358250a66c5f20bf91ad5c8c762afe6ec807b4aa5a988"
"975649b605cd1e4d9efd24673e0743293adb15867870cad21cb4e24e84045181"
"ba686050135c7e069889f638cbcfbe23bd926d37d78f14f3360caeb03f20f452"
"e0227f7446fcee9cb2872e4511e7d1d02a64ef3c1259ac543b6f0de3dc9b47c4"
"bba2c24b3383d4cec144ddb3940f7285d6fbd357e5bc18321bc37755ea0ee14c"
"963cd1732facfec20056e126349ae12fb54fa386fb87f8910a9b00fb0f8d2477"
)

DATA = bytes.fromhex(DATA_HEX)


def xtime(x):
return (((x & 0xFF) << 1) ^ (0x1B if x & 0x80 else 0)) & 0xFF


def add_round_key(s, k):
for i in range(16):
s[i] ^= k[i]


def sub_bytes(s, box):
for i in range(16):
s[i] = box[s[i]]


def inv_sub_bytes(s, box):
for i in range(16):
s[i] = box[s[i]]


def shift_rows(s):
s[1], s[5], s[9], s[13] = s[5], s[9], s[13], s[1]
s[2], s[6], s[10], s[14] = s[10], s[14], s[2], s[6]
s[3], s[7], s[11], s[15] = s[15], s[3], s[7], s[11]


def inv_shift_rows(s):
s[1], s[5], s[9], s[13] = s[13], s[1], s[5], s[9]
s[2], s[6], s[10], s[14] = s[10], s[14], s[2], s[6]
s[3], s[7], s[11], s[15] = s[7], s[11], s[15], s[3]


def mix_single_column(c):
a0, a1, a2, a3 = c
t = a0 ^ a1 ^ a2 ^ a3
u = a0
c[0] = (a0 ^ t ^ xtime(a0 ^ a1)) & 0xFF
c[1] = (a1 ^ t ^ xtime(a1 ^ a2)) & 0xFF
c[2] = (a2 ^ t ^ xtime(a2 ^ a3)) & 0xFF
c[3] = (a3 ^ t ^ xtime(a3 ^ u)) & 0xFF


def mix_columns(s):
for i in range(4):
col = bytearray(s[i * 4 : i * 4 + 4])
mix_single_column(col)
s[i * 4 : i * 4 + 4] = col


def inv_mix_columns(s):
mix_columns(s)
mix_columns(s)
mix_columns(s)


def derive_round_keys(k0):
keys = [bytes(k0)]
prev = bytearray(k0)
for r in range(1, 11):
c = (r * 17) & 0xFF
cur = bytes((prev[i] ^ c ^ i) & 0xFF for i in range(16))
keys.append(cur)
prev = bytearray(cur)
return keys


def decrypt_block(ct, keys, inv_sbox):
s = bytearray(ct)
for r in range(10, 0, -1):
add_round_key(s, keys[r])
if r != 10:
inv_mix_columns(s)
inv_shift_rows(s)
inv_sub_bytes(s, inv_sbox)
add_round_key(s, keys[0])
return bytes(s)


a = DATA[0:16]
b = DATA[16:32]
sbox = DATA[32:288]
expected = DATA[288:320]

inv = bytearray(256)
for i, v in enumerate(sbox):
inv[v] = i
inv_sbox = bytes(inv)

k0 = bytes((((i * -23) + (a[i] ^ b[i])) & 0xFF) for i in range(16))
keys = derive_round_keys(k0)

pt = decrypt_block(expected[:16], keys, inv_sbox) + decrypt_block(expected[16:], keys, inv_sbox)
pad = pt[-1]
print(pt[:-pad].decode("utf-8", errors="replace"))

得到 flag

1
flag{One_Easy_Wasm_Chall}

eert

二叉树

buildTree 这里是在构建树数组下标从0开始,左子节点是 2i+1,右子节点是 2i+2

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
__int64 __fastcall buildTree(__int64 a1)
{
__int64 v1; // rbx
TreeNode *v2; // rbx
__int64 v3; // rbx
__int64 v4; // rbx
char v6; // [rsp+1Fh] [rbp-61h]
int i; // [rsp+20h] [rbp-60h]
int i_2; // [rsp+24h] [rbp-5Ch]
int i_1; // [rsp+2Ch] [rbp-54h]
__int64 v10; // [rsp+30h] [rbp-50h] BYREF
__int64 v11; // [rsp+38h] [rbp-48h] BYREF
_QWORD v12[2]; // [rsp+40h] [rbp-40h] BYREF
_BYTE v13[24]; // [rsp+50h] [rbp-30h] BYREF
unsigned __int64 v14; // [rsp+68h] [rbp-18h]

v14 = __readfsqword(0x28u);
if ( std::string::empty(a1) )
return 0;
std::vector<TreeNode *>::vector(v13);
v12[1] = a1;
v10 = std::string::begin(a1);
v11 = std::string::end(a1);
while ( __gnu_cxx::operator!=<char const*,std::string>(&v10, &v11) )
{
v6 = *__gnu_cxx::__normal_iterator<char const*,std::string>::operator*(&v10);
v2 = operator new(0x18u);
TreeNode::TreeNode(v2, v6);
v12[0] = v2;
std::vector<TreeNode *>::push_back(v13, v12);
__gnu_cxx::__normal_iterator<char const*,std::string>::operator++(&v10);
}
i_2 = std::vector<TreeNode *>::size(v13);
for ( i = 0; i < i_2; ++i )
{
i_1 = 2 * (i + 1);
if ( 2 * i + 1 < i_2 )
{
v3 = *std::vector<TreeNode *>::operator[](v13, 2 * i + 1);
*(*std::vector<TreeNode *>::operator[](v13, i) + 8LL) = v3;
}
if ( i_1 < i_2 )
{
v4 = *std::vector<TreeNode *>::operator[](v13, i_1);
*(*std::vector<TreeNode *>::operator[](v13, i) + 16LL) = v4;
}
}
v1 = *std::vector<TreeNode *>::operator[](v13, 0);
std::vector<TreeNode *>::~vector(v13);
return v1;
}

preorder 先序遍历(根-左-右)结果存到 tmp1

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall preorder(TreeNode **a1)
{
__int64 result; // rax

if ( a1 )
{
std::string::operator+=(&tmp1[abi:cxx11], *a1);
preorder(a1[1]);
return preorder(a1[2]);
}
return result;
}

inorder 中序遍历(左-根-右)结果存到 tmp2

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall inorder(TreeNode **a1)
{
__int64 result; // rax

if ( a1 )
{
inorder(a1[1]);
std::string::operator+=(&tmp2[abi:cxx11], *a1);
return inorder(a1[2]);
}
return result;
}

先序遍历结果异或 key=7,然后自定义 Base64 编码,最后比较 ans1,中序遍历结果异或 key=8,然后用自定义 Base64 编码,最后比较 ans2

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
unsigned __int64 __fastcall __static_initialization_and_destruction_0(int a1, int n0xFFFF)
{
char v3; // [rsp+17h] [rbp-19h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-18h]

v4 = __readfsqword(0x28u);
if ( a1 == 1 && n0xFFFF == 0xFFFF )
{
std::ios_base::Init::Init(&std::__ioinit);
__cxa_atexit(&std::ios_base::Init::~Init, &std::__ioinit, &_dso_handle);
std::allocator<char>::allocator(&v3);
std::string::basic_string<std::allocator<char>>(
&CUSTOM_BASE64_TABLE,
"ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/",
&v3);
std::allocator<char>::~allocator(&v3);
__cxa_atexit(&std::string::~string, &CUSTOM_BASE64_TABLE, &_dso_handle);
std::allocator<char>::allocator(&v3);
std::string::basic_string<std::allocator<char>>(&tmp1[abi:cxx11], &unk_60E1, &v3);
std::allocator<char>::~allocator(&v3);
__cxa_atexit(&std::string::~string, &tmp1[abi:cxx11], &_dso_handle);
std::allocator<char>::allocator(&v3);
std::string::basic_string<std::allocator<char>>(&tmp2[abi:cxx11], &unk_60E1, &v3);
std::allocator<char>::~allocator(&v3);
__cxa_atexit(&std::string::~string, &tmp2[abi:cxx11], &_dso_handle);
std::allocator<char>::allocator(&v3);
std::string::basic_string<std::allocator<char>>(&ans1[abi:cxx11], "PTevaTqjNg5pa2GOxBSbcRJ0KiWgR2YY", &v3);
std::allocator<char>::~allocator(&v3);
__cxa_atexit(&std::string::~string, &ans1[abi:cxx11], &_dso_handle);
std::allocator<char>::allocator(&v3);
std::string::basic_string<std::allocator<char>>(&ans2[abi:cxx11], "WEmmcQCKV2abyU94RRmvOih5L2uJy1uL", &v3);
std::allocator<char>::~allocator(&v3);
__cxa_atexit(&std::string::~string, &ans2[abi:cxx11], &_dso_handle);
}
return v4 - __readfsqword(0x28u);
}

还原二叉树

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
TBL = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
MAP = {c:i for i,c in enumerate(TBL)}

ans1 = "PTevaTqjNg5pa2GOxBSbcRJ0KiWgR2YY"
ans2 = "WEmmcQCKV2abyU94RRmvOih5L2uJy1uL"

def cb64_decode(s: str) -> bytes:
out, val, bits = bytearray(), 0, 0
for ch in s:
if ch == '=': break
val = (val << 6) | MAP[ch]
bits += 6
while bits >= 8:
bits -= 8
out.append((val >> bits) & 0xFF)
val &= (1 << bits) - 1
return bytes(out)

def decrypt(s: str, k: int) -> str:
return bytes(b ^ k for b in cb64_decode(s)).decode('latin-1')

pre = decrypt(ans1, 7)
ino = decrypt(ans2, 8)

pos = {ch:i for i,ch in enumerate(ino)}
n = len(pre)

L = [-1]*n
R = [-1]*n
pi = 0

def build(inL: int, inR: int) -> int:
global pi
if inL >= inR: return -1
root = pi
pi += 1
mid = pos[pre[root]]
L[root] = build(inL, mid)
R[root] = build(mid+1, inR)
return root

root = build(0, n)

arr = [''] * n
def fill(node: int, idx: int):
if node == -1 or idx >= n: return
arr[idx] = pre[node]
fill(L[node], 2*idx+1)
fill(R[node], 2*idx+2)

fill(root, 0)
flag_inner = ''.join(arr)
print("flag{"+flag_inner+"}")

abc

给的 bc 字节码文件,clang 编译一下 clang a.bc -o a.out

RC4 加密,修改了一些地方,初始化 Sbox 时每轮取 key 的索引是 (5 * j + 3) % n23,

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
__int64 __fastcall sub_2a4c(__int64 a1, char *_sub_b361, unsigned __int64 n23)
{
__int64 result; // rax
char v4; // [rsp+Ah] [rbp-26h]
int j; // [rsp+Ch] [rbp-24h]
char v6; // [rsp+13h] [rbp-1Dh]
unsigned __int8 v7; // [rsp+13h] [rbp-1Dh]
int i; // [rsp+14h] [rbp-1Ch]

for ( i = 0; i < 256; ++i )
{
*(a1 + i) = i;
result = (i + 1);
}
v6 = 0;
for ( j = 0; j < 256; ++j )
{
v7 = _sub_b361[(5 * j + 3) % n23] + *(a1 + j) + v6;
v4 = *(a1 + j);
*(a1 + j) = *(a1 + v7);
*(a1 + v7) = v4;
v6 = j + v7;
result = (j + 1);
}
return result;
}

生成 keystream S[i + S[S[j]+S[i]]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
unsigned __int64 __fastcall sub_1a4c(__int64 a1, _BYTE *ptr, _BYTE *s1, size_t size)
{
unsigned __int64 i_1; // rax
char v5; // [rsp+3h] [rbp-31h]
unsigned __int64 i; // [rsp+4h] [rbp-30h]
unsigned __int8 v7; // [rsp+12h] [rbp-22h]
unsigned __int8 v8; // [rsp+13h] [rbp-21h]

v8 = 0;
v7 = 0;
for ( i = 0; ; ++i )
{
i_1 = i;
if ( i >= size )
break;
++v8;
v7 += v8 + *(a1 + v8);
v5 = *(a1 + v8);
*(a1 + v8) = *(a1 + v7);
*(a1 + v7) = v5;
s1[i] = *(a1 + (v8 + *(a1 + (*(a1 + v7) + *(a1 + v8))))) ^ ptr[i];
}
return i_1;
}

解密如下

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
key = b"ab#_var1an&_k3y_f0r_???"

cipher = bytes.fromhex(
"c2f6bfa99abe24dc680cf460d7faca2c"
"58ca3808892f40245d879792a25bfa81"
"ba598070b6648253115d"
)

def ksa_variant(key: bytes):
S = list(range(256))
j = 0
keylen = len(key)
for i in range(256):
key_index = (i * 5 + 3) % keylen
j = (j + S[i] + key[key_index]) & 0xFF
S[i], S[j] = S[j], S[i]
j = (j + i) & 0xFF
return S

def prga_variant(S, n: int) -> bytes:
i = 0
j = 0
out = bytearray()
for _ in range(n):
i = (i + 1) & 0xFF
j = (j + S[i] + i) & 0xFF
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) & 0xFF
a = S[t]
ks = S[(a + i) & 0xFF]
out.append(ks)
return bytes(out)

S = ksa_variant(key)
ks = prga_variant(S, len(cipher))
plain = bytes(c ^ k for c, k in zip(cipher, ks))

print(plain)

拿到 flag

1
flag{Thi3_i3_7he_s0_c@11ed_abcd}

ezc

只有 20 种 seed,输入 XOR 后并与 cipher 比较

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // ebx
__pid_t v4; // eax
void *v5; // rsp
void *v7; // rsp
_QWORD v8[7]; // [rsp+8h] [rbp-4A0h] BYREF
unsigned __int64 j; // [rsp+40h] [rbp-468h]
size_t v10; // [rsp+48h] [rbp-460h]
unsigned __int64 i; // [rsp+50h] [rbp-458h]
__int64 v12; // [rsp+58h] [rbp-450h]
_QWORD *v13; // [rsp+60h] [rbp-448h]
__int64 v14; // [rsp+68h] [rbp-440h]
void *s1; // [rsp+70h] [rbp-438h]
char s[1032]; // [rsp+78h] [rbp-430h] BYREF
unsigned __int64 v17; // [rsp+480h] [rbp-28h]

v17 = __readfsqword(0x28u);
v3 = time(0);
v4 = getpid();
srand((v4 ^ v3) % 0x14);
v12 = 35;
v8[4] = 36;
v8[5] = 0;
v8[2] = 36;
v8[3] = 0;
v5 = alloca(48);
v13 = v8;
for ( i = 0; i < 0x24; ++i )
*((_BYTE *)v13 + i) = rand();
printf("Enter your guess (exactly %zu bytes): ", 0x24u);
if ( fgets(s, 1024, stdin) )
{
v10 = strlen(s);
if ( v10 && s[v10 - 1] == 10 )
s[--v10] = 0;
if ( v10 == 36 )
{
v14 = 35;
v8[0] = 36;
v8[1] = 0;
v7 = alloca(48);
s1 = v8;
for ( j = 0; j < 0x24; ++j )
*((_BYTE *)s1 + j) = *((_BYTE *)v13 + j) ^ s[j];
if ( !memcmp(s1, &cipher, 0x24u) )
puts("Correct! Your input is the plaintext.");
else
puts("Incorrect.");
return 0;
}
else
{
printf("Wrong length: expected %zu, got %zu\n", 0x24u, v10);
return 1;
}
}
else
{
fwrite("No input\n", 1u, 9u, stderr);
return 1;
}
}


cipher = 0x1F, 0xC9, 0xED, 0x29, 0xA6, 0xFE, 0x44, 0xEE, 0x82, 0x45, 0xE9, 0xD8, 0x7F, 0x42, 0x10, 0xE0, 0xBB, 0x4B, 0xD0, 0x05, 0x4C, 0x76, 0x90, 0xCB, 0x48, 0x9C, 0x7A, 0xA9, 0xF0, 0x33, 0x55, 0x25, 0x64, 0x88, 0x3D, 0xF7

在 wsl 运行 c 文件爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char cipher[] = {0x1F, 0xC9, 0xED, 0x29, 0xA6, 0xFE, 0x44, 0xEE, 0x82, 0x45, 0xE9, 0xD8, 0x7F, 0x42, 0x10, 0xE0, 0xBB, 0x4B, 0xD0, 0x05, 0x4C, 0x76, 0x90, 0xCB, 0x48, 0x9C, 0x7A, 0xA9, 0xF0, 0x33, 0x55, 0x25, 0x64, 0x88, 0x3D, 0xF7};

int main() {
unsigned char key[36], plain[37];
for (int seed = 0; seed < 20; seed++) {
srand(seed);
for (int i = 0; i < 36; i++)
key[i] = rand() & 0xFF;
for (int i = 0; i < 36; i++)
plain[i] = cipher[i] ^ key[i];
plain[36] = 0;
int ok = 1;
for (int i = 0; i < 36; i++)
if (plain[i] < 32 || plain[i] > 126) { ok = 0; break; }
if (ok)
printf("seed=%d: %s\n", seed, plain);
}
return 0;
}

seed=16 的时候成功

1
flag{c4deea31-5d10-4b6c-8c45-6afac715eea3}

box

这题有反调试,密文在 byte_14000C0A0 中

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
  while ( 1 )
{
v4 = (95 * v3 + 29);
if ( v4 == 124 )
goto LABEL_37;
if ( (95 * v3 + 29) > 0x7Cu )
{
if ( v4 == 219 )
goto LABEL_38;
if ( (95 * v3 + 29) > 0xDBu )
{
if ( v4 == 240 )
goto LABEL_33;
if ( v4 != 248 )
return 0;
}
else
{
if ( v4 == 182 )
goto LABEL_13;
if ( v4 == 211 )
goto LABEL_16;
if ( v4 != 153 )
return 0;
LABEL_9:
if ( sub_1400017E0(v3, v1, v2) )
{
v10 = -103;
v9 = 0x24F5B91F7D51F399LL;
}
else
{
sub_1400018E0(&v9);
}
}
qmemcpy(v11, Buffer, 0x2Au);
goto LABEL_12;
}
if ( v4 == 58 )
goto LABEL_41;
if ( (95 * v3 + 29) > 0x3Au )
{
if ( v4 != 87 )
{
if ( v4 != 116 )
return 0;
LABEL_15:
sub_140001E70(v11, 42);
LABEL_16:
v2 = 1;
v3 = 11;
LODWORD(v1) = 0;
goto LABEL_17;
}
LABEL_12:
sub_140001B10(v11, 42);
LABEL_13:
sub_140001B90(v23, &v9, 9);
LABEL_14:
sub_140001D30(v23, v11, 42);
goto LABEL_15;
}
if ( v4 == 29 )
break;
if ( v4 != 50 )
{
if ( v4 != 21 )
return 0;
goto LABEL_14;
}
if ( v1 == 42 )
goto LABEL_33;
LABEL_17:
if ( v11[v1] != byte_14000C0A0[v1] )
{
v2 = 0;
LABEL_33:
if ( v2 )
sub_140001EE0(&unk_14000B090, 26, v2);
else
sub_140001EE0(&unk_14000B080, 7, v2);
putchar(10);
return 0;
}
v1 = (v1 + 1);
}
sub_140001EE0(&unk_14000B0C0, 16, 1);
LABEL_37:
v6 = off_14000B1F0();
if ( fgets(Buffer, 128, v6) )
{
LABEL_38:
v8 = strlen(Buffer);
v0 = v8;
if ( v8 && v11[v8 + 47] == 10 )
{
v11[v8 + 47] = 0;
v0 = v8 - 1;
}
LABEL_41:
if ( v0 != 42 )
{
sub_140001EE0(&unk_14000B080, 7, v2);
putchar(10);
return 1;
}
goto LABEL_9;
}
sub_140001EE0(&unk_14000B0B0, 12, v7);
putchar(10);
return 1;
}


byte_14000C0A0=0xA9, 0xBB, 0x6C, 0xD0, 0x58, 0xE8, 0x45, 0x88, 0xD7, 0x73, 0x5B, 0x6A, 0xEB, 0x12, 0x67, 0x15, 0xEE, 0xFE, 0xDD, 0x44, 0x93, 0x1E, 0x5D, 0xB6, 0xFA, 0x3D, 0xAF, 0x75, 0xD6, 0x29, 0x4F, 0x48, 0x24, 0xDB, 0xC6, 0x30, 0x5A, 0x35, 0xD2, 0xED, 0x79, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

sub_1400018E0 这里是在进行密钥派生,Round Key 在 0x14000C0D0 处:0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f

初始密钥为 0x2D56B6B61328C017

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
__int64 __fastcall sub_1400018E0(__int64 a1)
{
char i; // al
int v3; // eax
__int64 *v4; // r13
__int64 v5; // r12
__int64 result; // rax
int v7; // esi
int v8; // edx
__int64 (__fastcall *v9)(int, int); // r14
unsigned __int8 v10; // al
__int64 v11; // rdx
__int64 *v12; // r13
unsigned int v13; // r15d
__int64 *v14; // r14
unsigned int v15; // esi
unsigned __int8 v16; // al
__int64 (__fastcall *v17)(); // r12
unsigned __int8 v18; // al
__int64 *v19; // rax
__int64 v20; // rdx
int v21; // [rsp+2Ch] [rbp-6Ch]
__int64 v22; // [rsp+30h] [rbp-68h] BYREF
char v23; // [rsp+38h] [rbp-60h]
char v24; // [rsp+39h] [rbp-5Fh] BYREF
__int64 v25; // [rsp+47h] [rbp-51h] BYREF
char v26; // [rsp+4Fh] [rbp-49h]

for ( i = 0; ; i = 4 )
{
while ( 1 )
{
v3 = (77 * i + 35);
if ( v3 > 112 )
goto LABEL_5;
if ( v3 != 35 )
break;
v23 = 37;
v22 = 0x2D56B6B61328C017LL;
v19 = &v22;
do
{
v20 = *v19;
v19 = (v19 + 1);
*(v19 - 1) = byte_14000C0E0[v20];
}
while ( &v24 != v19 );
v21 = 0;
i = 2;
}
++v21;
v22 = v25;
v23 = v26;
LABEL_5:
if ( v21 > 3 )
break;
v12 = &v22;
v13 = 0;
v14 = &v25;
do
{
v15 = v13;
v12 = (v12 + 1);
v14 = (v14 + 1);
++v13;
v16 = off_14000B030();
off_14000B020(v16, *(&v22 + v13 % 9));
off_14000B030();
off_14000B070();
v17 = off_14000B030;
v18 = off_14000B030();
*(v14 - 1) = (v17)(v18, v15);
}
while ( v13 != 9 );
}
v4 = &v22;
v5 = 0;
LOBYTE(result) = 90;
do
{
v7 = result;
v4 = (v4 + 1);
v8 = byte_14000C0E0[off_14000B030()];
*(a1 + v5) = v8;
v9 = off_14000B020;
v10 = off_14000B020(v7, v8);
v11 = v5++;
result = v9(v10, v11);
}
while ( v5 != 9 );
return result;
}

RC4 加密,sub_140001B90 是 KSA,sub_140001D30 是 PRGA,sub_140001E70 最后进行了个异或

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
__int64 __fastcall sub_140001E70(__int64 a1, int a2)
{
__int64 result; // rax
int i; // ebx
__int64 v6; // rdi

result = 17;
for ( i = 0; a2 > i; result = 64 )
{
v6 = i++;
*(a1 + v6) = off_14000B030();
}
return result;
}


__int64 __fastcall sub_140001740(int a1, unsigned int a2)
{
return a1 ^ a2;
}


__int64 __fastcall sub_1400017D0(char a1, char a2)
{
return (4 * (a1 - a2)) | 0xFFFFFFAD;
}


__int64 __fastcall sub_140001750(int a1, unsigned int a2)
{
return a1 & a2;
}


__int64 __fastcall sub_140001760(int a1, unsigned int a2)
{
return a1 | a2;
}


__int64 __fastcall sub_140001730(int a1, int a2)
{
return (a1 - a2);
}


__int64 __fastcall sub_140001770(unsigned __int8 a1, unsigned int a2)
{
if ( a2 )
return (a1 % a2);
return a2;
}


__int64 __fastcall sub_140001780(int a1, char a2)
{
return (a1 << (a2 & 7));
}


__int64 __fastcall sub_140001790(unsigned __int8 a1, char a2)
{
return a1 >> (a2 & 7);
}


char __fastcall sub_1400017A0(char a1, char a2)
{
return __ROL1__(a1, a2);
}


char __fastcall sub_1400017B0(char a1, char a2)
{
return __ROR1__(a1, a2);
}

解密逻辑如下:

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
120
121
122
123
124
125
SBOX = bytes([
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
])

ROUND_KEY = bytes([0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f])

cipher = bytes([
0xa9,0xbb,0x6c,0xd0,0x58,0xe8,0x45,0x88,0xd7,0x73,0x5b,0x6a,0xeb,0x12,0x67,0x15,
0xee,0xfe,0xdd,0x44,0x93,0x1e,0x5d,0xb6,0xfa,0x3d,0xaf,0x75,0xd6,0x29,0x4f,0x48,
0x24,0xdb,0xc6,0x30,0x5a,0x35,0xd2,0xed,0x79,0x43
])

def add_op(a, b): return (a + b) & 0xFF
def xor_op(a, b): return a ^ b
def rol8(a, b): b &= 7; return ((a << b) | (a >> (8 - b))) & 0xFF if b else a

def derive_key():
init = bytes([0x17, 0xc0, 0x28, 0x13, 0xb6, 0xb6, 0x56, 0x2d, 0x25])
raw = [SBOX[b] for b in init]
for iteration in range(4):
rk = ROUND_KEY[iteration]
tmp = [0] * 9
for i in range(9):
v1 = xor_op(raw[i], raw[(i + 8) % 9])
v2 = add_op(v1, raw[(i + 1) % 9])
idx3 = raw[(i + 3) % 9]
v3 = xor_op(v2, SBOX[idx3])
v4 = rol8(v3, (i % 5) + 1)
v5 = xor_op(v4, rk)
tmp[i] = xor_op(v5, i)
raw = tmp
print(f"keyround {iteration}: {bytes(raw).hex()}")

output = [0] * 9
current = 0x5A
for i in range(9):
t1 = xor_op(raw[i], current)
output[i] = SBOX[t1]
t2 = add_op(current, SBOX[t1])
current = add_op(t2, i)
print(f"keyfinal: {bytes(output).hex()}")
return bytes(output)

def rc4_ksa(key):
S = list(range(256))
j = 0
for i in range(256):
t1 = xor_op(i, 0x5A)
t2 = add_op(t1, key[i % len(key)])
t3 = add_op(S[i], t2)
j = add_op(j, t3)
S[i], S[j] = S[j], S[i]
return S

def rc4_prga(S, ct):
S = S[:]
i = j = 0
pt = []
for n, c in enumerate(ct):
i = add_op(i, 1)
j = add_op(j, S[i])
S[i], S[j] = S[j], S[i]
if (~n & 7) == 0:
S[i] = add_op(S[i], S[j])
k = add_op(S[i], S[j])
ks_byte = S[k]
tmp1 = xor_op(0x5A, n)
tmp2 = xor_op(c, tmp1)
pt.append(xor_op(tmp2, ks_byte))
return bytes(pt)

def scramble(data):
out = bytearray(len(data))
for i, b in enumerate(data):
t1 = xor_op(b, 0x3C)
t2 = add_op(t1, i)
t3 = rol8(t2, 3)
t4 = xor_op(t3, (0x37 * i) & 0xFF)
out[i] = t4
return bytes(out)

def ror8(a, b):
b &= 7
return ((a >> b) | (a << (8 - b))) & 0xFF if b else a

def unscramble(data):
out = bytearray(len(data))
for i, b in enumerate(data):
t1 = xor_op(b, (0x37 * i) & 0xFF)
t2 = ror8(t1, 3)
t3 = (t2 - i) & 0xFF
t4 = xor_op(t3, 0x3C)
out[i] = t4
return bytes(out)

def post_transform(data):
out = bytearray(len(data))
for i, b in enumerate(data):
delta = ((91 * i - 89) ^ (i << 4)) & 0xFF
out[i] = xor_op(b, delta)
return bytes(out)

key = derive_key()
print("key:", key.hex())
step1 = post_transform(cipher)
S = rc4_ksa(key)
step2 = rc4_prga(S, step1)
flag = unscramble(step2)
print("flag:", flag)

最后得到结果 key: 9951d71a65ca12ab12,flag 如下

1
flag{Congratulations_On_Cracking_This_Box}

findkey

AES 白盒

运行一下发现密文和明文都给了,并且求解的密钥是 16 个字符

d810 可以去一部分混淆,AES 加密,可以找到 sbox 和故障密文

func_0x09818ae3 这里跟进 src 地址处拿到故障密文

用脚本提取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import idaapi
import idc

start_addr = 0x000000000040A120
end_addr = 0x000000000040A21F

def data(start_addr, end_addr):
data = idaapi.get_bytes(start_addr, end_addr - start_addr + 1)
hex_data = [f"0x{data[i]:02x}" for i in range(len(data))]
lines = [", ".join(hex_data[i:i+16]) for i in range(0, len(hex_data), 16)]
with open("src_data.txt", "w") as f:
f.write("\n".join(lines))

data(start_addr, end_addr)

数据如下

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
src_data=[
0x1f, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x91, 0xd6, 0x18, 0x5b, 0x4e, 0xba, 0xc3, 0x0d, 0x71,
0x56, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0xd8, 0xd6, 0x18, 0x72, 0x4e, 0xba, 0x61, 0x0d, 0x71,
0x34, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x5c, 0xd6, 0x18, 0x43, 0x4e, 0xba, 0x65, 0x0d, 0x71,
0xc9, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x28, 0xd6, 0x18, 0x61, 0x4e, 0xba, 0x50, 0x0d, 0x71,
0xd4, 0x0b, 0xdc, 0x4d, 0x9b, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0x34, 0xba, 0xca, 0x47, 0x71,
0xd4, 0x41, 0xdc, 0x4d, 0x94, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xda, 0xba, 0xca, 0x62, 0x71,
0xd4, 0x36, 0xdc, 0x4d, 0x06, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xe1, 0xba, 0xca, 0xac, 0x71,
0xd4, 0xc2, 0xdc, 0x4d, 0xe7, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xea, 0xba, 0xca, 0x60, 0x71,
0xd4, 0x52, 0x6f, 0x4d, 0x2c, 0x37, 0x80, 0x77, 0x00, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x80,
0xd4, 0x52, 0x09, 0x4d, 0x2c, 0x3a, 0x80, 0x77, 0x40, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x7b,
0xd4, 0x52, 0x62, 0x4d, 0x2c, 0x93, 0x80, 0x77, 0x54, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x36,
0xd4, 0x52, 0xf0, 0x4d, 0x2c, 0x54, 0x80, 0x77, 0x10, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x17,
0xd4, 0x52, 0xdc, 0x1f, 0x2c, 0x84, 0xc8, 0x77, 0xd6, 0x4b, 0x7d, 0x4e, 0x17, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x75, 0x2c, 0x84, 0x76, 0x77, 0xd6, 0x25, 0x7d, 0x4e, 0xcf, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x2b, 0x2c, 0x84, 0x22, 0x77, 0xd6, 0x08, 0x7d, 0x4e, 0x16, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x3e, 0x2c, 0x84, 0x33, 0x77, 0xd6, 0x81, 0x7d, 0x4e, 0x2d, 0xca, 0x0d, 0x71,
]

sbox_data=[
0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72,
0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8,
0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb,
0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3,
0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9,
0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90,
0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06,
0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d,
0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e,
0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5,
0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8,
0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c,
0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
]

使用 dfa 攻击进行求解

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import sys
src_data = [
0x1f, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x91, 0xd6, 0x18, 0x5b, 0x4e, 0xba, 0xc3, 0x0d, 0x71,
0x56, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0xd8, 0xd6, 0x18, 0x72, 0x4e, 0xba, 0x61, 0x0d, 0x71,
0x34, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x5c, 0xd6, 0x18, 0x43, 0x4e, 0xba, 0x65, 0x0d, 0x71,
0xc9, 0x52, 0xdc, 0x4d, 0x2c, 0x84, 0x80, 0x28, 0xd6, 0x18, 0x61, 0x4e, 0xba, 0x50, 0x0d, 0x71,
0xd4, 0x0b, 0xdc, 0x4d, 0x9b, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0x34, 0xba, 0xca, 0x47, 0x71,
0xd4, 0x41, 0xdc, 0x4d, 0x94, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xda, 0xba, 0xca, 0x62, 0x71,
0xd4, 0x36, 0xdc, 0x4d, 0x06, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xe1, 0xba, 0xca, 0xac, 0x71,
0xd4, 0xc2, 0xdc, 0x4d, 0xe7, 0x84, 0x80, 0x77, 0xd6, 0x18, 0x7d, 0xea, 0xba, 0xca, 0x60, 0x71,
0xd4, 0x52, 0x6f, 0x4d, 0x2c, 0x37, 0x80, 0x77, 0x00, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x80,
0xd4, 0x52, 0x09, 0x4d, 0x2c, 0x3a, 0x80, 0x77, 0x40, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x7b,
0xd4, 0x52, 0x62, 0x4d, 0x2c, 0x93, 0x80, 0x77, 0x54, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x36,
0xd4, 0x52, 0xf0, 0x4d, 0x2c, 0x54, 0x80, 0x77, 0x10, 0x18, 0x7d, 0x4e, 0xba, 0xca, 0x0d, 0x17,
0xd4, 0x52, 0xdc, 0x1f, 0x2c, 0x84, 0xc8, 0x77, 0xd6, 0x4b, 0x7d, 0x4e, 0x17, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x75, 0x2c, 0x84, 0x76, 0x77, 0xd6, 0x25, 0x7d, 0x4e, 0xcf, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x2b, 0x2c, 0x84, 0x22, 0x77, 0xd6, 0x08, 0x7d, 0x4e, 0x16, 0xca, 0x0d, 0x71,
0xd4, 0x52, 0xdc, 0x3e, 0x2c, 0x84, 0x33, 0x77, 0xd6, 0x81, 0x7d, 0x4e, 0x2d, 0xca, 0x0d, 0x71,
]

sbox_data = [
0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72,
0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8,
0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb,
0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3,
0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9,
0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90,
0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06,
0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d,
0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e,
0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5,
0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8,
0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c,
0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
]
full_sbox = [0x63] + sbox_data
init_vec = bytes([
0x45, 0xBA, 0x23, 0x7D, 0xEB, 0xC4, 0xC2, 0x0F,
0xDA, 0x39, 0x62, 0x61, 0x93, 0xF6, 0x64, 0xB4,
])

rcon_list = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]


def generate_shift_map():
original_indices = list(range(16))

# 2. 按照 AES 列优先 (Column-Major) 填充到 4x4 矩阵
# 0 4 8 12
# 1 5 9 13
# 2 6 10 14
# 3 7 11 15
rows = [[], [], [], []]
for c in range(4):
for r in range(4):
rows[r].append(original_indices[c * 4 + r])

# 3. 执行 ShiftRows 变换
# Row 0 不变
# Row 1 左移 1
rows[1] = rows[1][1:] + rows[1][:1]
# Row 2 左移 2
rows[2] = rows[2][2:] + rows[2][:2]
# Row 3 左移 3
rows[3] = rows[3][3:] + rows[3][:3]
mapped_indices = []
for c in range(4):
for r in range(4):
mapped_indices.append(rows[r][c])

return mapped_indices

# 生成映射表
index_map = generate_shift_map()
inv_sbox_table = [0] * 256
for i, v in enumerate(full_sbox):
inv_sbox_table[v] = i

def gmul(a, b):
# Galois Field 乘法
p = 0
for _ in range(8):
if b & 1: p ^= a
hi = a & 0x80
a = (a << 1) & 0xFF
if hi: a ^= 0x1B
b >>= 1
return p & 0xFF

def mix_cols(state):
# AES 列混淆
out = [0] * 16
for c in range(4):
p = state[4*c : 4*c+4]
out[4*c+0] = gmul(0x02, p[0]) ^ gmul(0x03, p[1]) ^ p[2] ^ p[3]
out[4*c+1] = p[0] ^ gmul(0x02, p[1]) ^ gmul(0x03, p[2]) ^ p[3]
out[4*c+2] = p[0] ^ p[1] ^ gmul(0x02, p[2]) ^ gmul(0x03, p[3])
out[4*c+3] = gmul(0x03, p[0]) ^ p[1] ^ p[2] ^ gmul(0x02, p[3])
return out

def reverse_keysched(target_key):
w = [None] * 44
for i in range(4):
w[40+i] = target_key[4*i : 4*i+4]
for i in range(43, 3, -1):
if i % 4 != 0:
w[i-4] = bytes(a ^ b for a, b in zip(w[i], w[i-1]))
else:
# RotWord
rot = w[i-1][1:] + w[i-1][:1]
# SubWord
sub = bytes(full_sbox[x] for x in rot)
# XOR Rcon
r_val = rcon_list[i // 4]
xor_res = bytes([sub[0] ^ r_val, sub[1], sub[2], sub[3]])
w[i-4] = bytes(a ^ b for a, b in zip(w[i], xor_res))

return b"".join(w[0:4])

blocks = [src_data[i*16 : (i+1)*16] for i in range(16)]
precalc_mix = []
base_arr = list(init_vec)
for i in range(16):
tmp = base_arr[:]
tmp[i] = 0
precalc_mix.append(mix_cols(tmp))

final_key_array = [0] * 16

# 针对末轮密钥的每一个字节位置 p (0~15)
for p in range(16):
mix_idx = index_map[p]

cands = []
for k_byte in range(256):
prev_key_byte = None
consistent = True
for i in range(16):
# 公式:MixCol[mix_idx] ^ PrevKey[mix_idx] = InvSubBytes( Out[p] ^ LastKey[p] )
out_val = blocks[i][p]
mix_val = precalc_mix[i][mix_idx]

val = inv_sbox_table[out_val ^ k_byte] ^ mix_val

if prev_key_byte is None:
prev_key_byte = val
elif prev_key_byte != val:
consistent = False
break

if consistent:
cands.append(k_byte)

if len(cands) == 1:
final_key_array[p] = cands[0]
else:
print(f"Error at byte {p}: found {len(cands)} candidates")
sys.exit(1)

final_key_bytes = bytes(final_key_array)
master_key = reverse_keysched(final_key_bytes)

print(f"Key: {master_key.hex()}")

from hex 转之后就是 flag

1
flag{8579432268}

easyre

有大量 SIMD 指令,还有反调试,把垃圾指令全部 nop 掉,运行 idapython 脚本

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
import idaapi
import idc
import idautils
import ida_bytes
import ida_ua

def nop_instruction(ea):
length = ida_bytes.get_item_size(ea)
if length > 0:
for i in range(length):
ida_bytes.patch_byte(ea + i, 0x90)
return length

def is_junk_instruction(mnem):
mnem = mnem.lower()
SAFE_LIST = {
"push", "pop", "pushf", "popf", "pushfq", "popfq",
"pusha", "popa", "proc", "pause", "ret", "retn", "call"
}
if mnem in SAFE_LIST:
return False
if mnem.startswith("v") or mnem.startswith("f"):
return True
if mnem.startswith("p"):
if mnem not in SAFE_LIST:
return True
if mnem in {"sqrtsd", "sqrtss", "rcr", "ror"}:
return True
return False

def clean_junk_asm_safe():
selection = idc.read_selection_start(), idc.read_selection_end()
if selection[0] == idc.BADADDR:
return
start_ea = selection[0]
end_ea = selection[1]
print(f"[*] 正在分析区域: {hex(start_ea)} - {hex(end_ea)}")
current_ea = start_ea
count = 0
while current_ea < end_ea:
mnem = idc.print_insn_mnem(current_ea)
if not mnem:
current_ea = idc.next_head(current_ea)
continue
length = ida_bytes.get_item_size(current_ea)
if is_junk_instruction(mnem):
nop_instruction(current_ea)
count += 1
current_ea += length
print(f"[*] 清理完成!已安全移除 {count} 条指令。")
print("[*] 关键 GPR 指令 (push/pop/mov/add/call) 已保留。")
print("[*] 请在伪代码窗口按 F5 刷新。")

if __name__ == "__main__":
clean_junk_asm_safe()

去掉垃圾指令后保存为新程序,tea 系列的加密,102 轮

下断点进行调试,off_140030098 就是存 key 的地方,dump 数据出来

1
0xCD, 0x85, 0x74, 0xC7, 0xF1, 0x31, 0xC4, 0x09, 0x70, 0x6A, 0xD7, 0xA3, 0x37, 0x49, 0x0C, 0x56

sub_7FF769311CB0 是 AES 加密逻辑,动调可以拿到 delta,0x9E3779B9

这里还有一段异或 0x7F 的逻辑,在调试时可以发现最后两个密文没有加密

密文从 data 段的 00007FF769340000 开始,截至到 0027 为第一段密文,后两个字节为第二段密文

解密逻辑如下

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
import struct

XTEA_DELTA = 0x9E3779B9

def decrypt_block(rounds, block, keys):
left, right = block
sum_ = (XTEA_DELTA * rounds) & 0xFFFFFFFF
for _ in range(rounds):
term1 = ((left << 4) ^ (left >> 5)) + left
term2 = sum_ + keys[(sum_ >> 11) & 3]
right = (right - (term1 ^ term2)) & 0xFFFFFFFF
sum_ = (sum_ - XTEA_DELTA) & 0xFFFFFFFF
term3 = ((right << 4) ^ (right >> 5)) + right
term4 = sum_ + keys[sum_ & 3]
left = (left - (term3 ^ term4)) & 0xFFFFFFFF
return left, right

def decrypt_stream(ciphertext, key_raw, rounds=32):
keys = list(struct.unpack("<4I", key_raw))
decrypted = b""
for i in range(0, len(ciphertext) - 7, 8):
chunk = ciphertext[i:i+8]
left, right = struct.unpack("<2I", chunk)
left, right = decrypt_block(rounds, (left, right), keys)
decrypted += struct.pack("<2I", left, right)
return decrypted

def xor_transform(data):
return bytes(b ^ 0x7F for b in data)

def sub_transform(data):
return bytes((b - 0x40) & 0xFF for b in data)

def solve():
key = bytes([
0xCD, 0x85, 0x74, 0xC7,
0xF1, 0x31, 0xC4, 0x09,
0x70, 0x6A, 0xD7, 0xA3,
0x37, 0x49, 0x0C, 0x56
])
ciphertext = bytes([
0xBA, 0x7A, 0xAA, 0x6A, 0x2F, 0x7E, 0xF8, 0x03, 0x2D, 0xB4,
0xAB, 0x92, 0x6B, 0x91, 0x31, 0xDA, 0x95, 0x37, 0x51, 0x13,
0x1F, 0xCE, 0x1C, 0x62, 0x51, 0xBC, 0x3F, 0xB2, 0xB1, 0xB3,
0x54, 0x17, 0xEF, 0x28, 0x93, 0xAE, 0x52, 0xCA, 0xCE, 0xA7
])
leftover = [0xDE, 0xC2]
stage1 = decrypt_stream(ciphertext, key, rounds=102)
stage2 = xor_transform(stage1)
final_output = sub_transform(stage2)
decoded = ""
for byte in leftover:
char = (byte ^ 0x7F) - 0x40
decoded += chr(char & 0xFF)
result = final_output.rstrip(b'\x00').decode(errors='ignore') + decoded
print(f"Flag: {result}")

solve()

HKCERT CTF2025-Reverse
http://example.com/2025/12/25/HKCERT-CTF2025-Reverse/
作者
butt3rf1y
发布于
2025年12月25日
许可协议