Post

陇剑杯

陇剑杯

手生,练手

Lesscommon

注意x + y == (x ^ y) + ((x & y) << 1)

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall one_rond_enc(context *context_struct, unsigned __int8 *p_input_i, _BYTE *p_output_i)
{
  interim_0 = v0 + key[0];
  interim_1 = v1 + key[1];
  for ( i = 1; i <= 12 ++i ) // 12 iters
  {
    interim_0 = interim_1 ^ ROL4(key[2*i] + interim_0, interim_1);
    interim_1 = interim_0 ^ ROL4(key[2*i] + interim_1, interim_0);
  }
}
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

unsigned int cipher[12] = {
    0xF3AB6F4C, 0xF6E27813, 0x991C9D86, 0x10CC85DE, 0x05EE28E8, 0x344B2192, 0x3C172843, 0x51735B56, 
    0x0F1D8A9F, 0x562C3497, 0x48699F42, 0xF58AD5A3
};

unsigned int key[26] = {
    0xE29075B4, 0xBBF28559, 0x01CEA3AA, 0xC32F06A9, 0x322BCBD2, 0x4B6947FD, 0xC65A9676, 0xE6EC21DE, 
    0xFC0D4679, 0xFBAD7BE4, 0x1E6A2032, 0x47150799, 0xFCA5733F, 0xD7D2FD1C, 0xA957923D, 0x94A4D5C6, 
    0xD8EBABA8, 0x7321CBE0, 0x5A0E0417, 0xA9F74244, 0x4616ADBD, 0xFB0925C4, 0x168F9ACC, 0xBE6AF2E2, 
    0xDB033BA9, 0xFFA2830E
};

uint32_t ROR4(uint32_t value, uint32_t count){
    count %= 32;
    return (value >> count) | (value << (32 - count));
}

int main(){
    for (int i = 0;i < 12;i++){
        uint32_t v0 = cipher[2 * i];
        uint32_t v1 = cipher[2 * i + 1];
        for (int i = 12; i >= 1 ;--i ) // 12 iters
        {
            v1 = ROR4(v1 ^ v0, v0) - key[2*i + 1];
            v0 = ROR4(v0 ^ v1, v1) - key[2*i];
        }
            v0 -=  key[0];
            v1 -=  key[1];

            cipher[2 * i] = v0;
            cipher[2 * i + 1] = v1;
    }

    printf("%s", cipher);
    
    return 0;
}
// flag{8c1fe64f-1085-48bb-947e-7635abc40f4c}
PrecedenceOperatorDescriptionAssociativity
1() [] -> . ++ -- (postfix)Function call, array subscript, member access, postfix increment/decrementLeft to Right
2++ -- (prefix) + - (unary) ! ~ * (dereference) & (address-of) sizeof _AlignofUnary operators, prefix increment/decrementRight to Left
3* / %Multiplication, division, modulusLeft to Right
4+ -Addition, subtractionLeft to Right
5<< >>Bitwise left shift, right shiftLeft to Right
6< <= > >=Relational operatorsLeft to Right
7== !=Equality operatorsLeft to Right
8&Bitwise ANDLeft to Right
9^Bitwise XOR (exclusive OR)Left to Right
10\|Bitwise OR (inclusive OR)Left to Right
11&&Logical ANDLeft to Right
12\|\|Logical ORLeft to Right
13?:Ternary conditional operatorRight to Left
14= += -= *= /= %= <<= >>= &= ^= \|=Assignment operatorsRight to Left
15,Comma operatorLeft to Right

Prover

用到了四个等式和四个校验和做约束z3求解, 注意Concat接收俩都要是BitVec, z3加和运算在有限域上, BitVec需要扩展.

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
from z3 import *

def ROL4(x, n):
    # Correctly handle 32-bit rotation for BitVecs
    return (x << n) | (LShR(x, 32 - n))

def popcount8(x):
    # This function works as-is, Z3's operators handle the integer constants
    return (x & 1) + ((x>>1) & 1) + ((x>>2) & 1) + ((x>>3) & 1) + ((x>>4) & 1) + ((x>>5) & 1) + ((x>>6) & 1) + ((x>>7) & 1)

s = Solver()

V0 = BitVec('V0', 32)
V1 = BitVec('V1', 32)
V2 = BitVec('V2', 32)
V3 = BitVec('V3', 32)
V4 = BitVec('V4', 32)
V5 = BitVec('V5', 32)

s.add(V0 == 0x1B744424)
s.add((V1 & 0xFF) == 0x4E)
s.add((V5 & 0xFFFF0000) == 0)
s.add((V5 & 0xFF00) == 0xD500)

# Equation1
t = ROL4(V0, 5)
term1 = ROL4(V1, 11)
term2 = V4 ^ 0xDEADBEEF
term3 = (V2 - 0x61C88647) ^ t
s.add(term1 + term2 + term3 == 0xC356FD3A)

# Equation2
termA = ROL4(V3, 17)
termB = 0x27D4EB2D * V3
termC = V2 + 0x7F4A7C15
termD = ROL4(V5, 13) + 0x85EBCA6B * V1
v13 = termB ^ termC ^ termD
s.add(termA + v13 == 0xB5CCF033)

# Loop part
a0 = V3 ^ V0 ^ 0x13579BDF
b0 = ROL4(V2, 7) + V1

# n2=0
v9_0 = ROL4(0x9E3779B9 - (0x7A143595 * b0), 5)
b1 = ROL4(b0, 11) ^ b0 ^ v9_0 ^ a0

# n2=1
v9_1 = ROL4(0x9E3779B8 - (0x7A143595 * b1), 10)
b2 = ROL4(b1, 11) ^ b1 ^ v9_1 ^ b0

# Constraints on b1 and b2
s.add(b1 + b2 == 0x2FD99233)
s.add(ROL4(b2, 11) + ROL4(b1, 3) == 0x2BAF2082)

# Define bytes from V as a list for easier processing
All_Bytes = [Extract(i*8+7, i*8, V) for V in [V0,V1,V2,V3,V4] for i in range(4)]
All_Bytes.extend([Extract(7,0,V5), Extract(15,8,V5)]) # B20, B21

B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, B21 = All_Bytes

# Add known bytes
s.add(B0 == 0x24)
s.add(B1 == 0x44)
s.add(B2 == 0x74)
s.add(B3 == 0x1B)
s.add(B4 == 0x4E)
s.add(B21 == 0xD5)

# ** FIX APPLIED HERE **
# The integer 0 is replaced with BitVecVal(0, ) for zero-extension.
zero_ext = lambda b: Concat(BitVecVal(0, 8), b)

# Checksum3
sum_val = Sum([zero_ext(b) for b in All_Bytes])
checksum_3 = Extract(15,0, sum_val)
s.add(checksum_3 == 0x913)

# Checksum2
xor_val = All_Bytes[0]
for i in range(1, len(All_Bytes)):
    xor_val = xor_val ^ All_Bytes[i]
s.add(xor_val == 0x55)

# ** AND FIX APPLIED HERE **
# Checksum1
weighted_sum = Sum([zero_ext(All_Bytes[i]) * (i + 1) for i in range(len(All_Bytes))])
checksum_1 = Extract(7,0, weighted_sum)
s.add(checksum_1 == 0x43)

# Checksum
pop_sum = Sum([popcount8(b) for b in All_Bytes])
checksum = Extract(7,0, pop_sum)
s.add(checksum == 0x50)

if s.check() == sat:
    m = s.model()
    print("Solution found!")
    print("V0 =", hex(m[V0].as_long()))
    print("V1 =", hex(m[V1].as_long()))
    print("V2 =", hex(m[V2].as_long()))
    print("V3 =", hex(m[V3].as_long()))
    print("V4 =", hex(m[V4].as_long()))
    print("V5 =", hex(m[V5].as_long()))
else:
    print("No solution")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hex_bytes= ['0x24', '0x44', '0x74', '0x1b', '0x4e', '0x84', '0x0f', '0xab', '0xf2', '0xdc', '0x30', '0x4e', '0x1f', '0xc4', '0x22', '0xa0', '0x55', '0x0b', '0xfe', '0x1a', '0x52', '0xd5']
hex_bytes = [int(s, 16) for s in hex_bytes]
key_1 = [0x03, 0x05, 0x09, 0x0B, 0x0D]
key_2 = [0xA5, 0x5C, 0xC3, 0x96, 0x3E, 0xD7, 0x21]
_ROR_ = lambda b, i: ((b >> (i)) | (b << (8 - i))) & 0xff

plain = []

for i in range(22):
    c1 = _ROR_(hex_bytes[i], (i % 5))
    c2 = (c1 ^ key_2[i % 7]) & 0xff
    temp = (c2 - (19 * i + 79)) & 0xff
    # 求乘法逆元
    c3 = (temp * pow(key_1[i % 5], -1, 0x100)) & 0xff
    plain.append(chr(c3 & 0xff))

print(''.join(plain))
# flag{7ac1d3e59f0b2468}

Dragon

main反编译不全, 看到jmp rax寄存器混淆:

image-20250909145909729

alt + b搜特征码去掉前三个混淆看到xxtea:

image-20250909153747926

image-20250909154659247

image-20250909154734433

image-20250909151408344

写脚本解密:

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
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DELTA 0x87654321

void XXTEA_decrypt(uint32_t *v, int n, const uint32_t key[4]) {
    if (n < 2) return;  // At least two elements
    uint32_t y, z, sum, e;
    uint32_t p, q = 0x2a; // !
    sum = q * DELTA;
    y = v[0];
    do {
        e = (sum >> 2) & 3;
        for (p = n - 1; p > 0; p--) {
            z = v[p - 1];
            v[p] -= ((z >> 5) ^ (y << 4)) + ((y >> 5) ^ (z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z));
            y = v[p];
        }
        z = v[n - 1];
        v[0] -= ((z >> 5) ^ (y << 4)) + ((y >> 5) ^ (z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z));
        y = v[0];
        sum -= DELTA;
    } while (--q > 0);
}

int main()
{
    uint32_t key[] = {0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210};
    uint8_t cipher[] =
    {
        0xCE, 0xD6, 0xB4, 0x0E, 0x8B, 0xDE, 0x1D, 0x52, 0xFD, 0x24, 
        0xED, 0x21, 0x26, 0xEC, 0x10, 0xBA, 0x1C, 0x93, 0x39, 0x33, 
        0x7D, 0x0E, 0xDC, 0x46, 0x44, 0x9F, 0x46, 0xCC, 0x79, 0x70, 
        0xBA, 0x64, 0x77, 0x79, 0x77, 0x64, 0x98, 0x1C, 0x15, 0xB2, 
        0xA1, 0x5A, 0xCC, 0xDB
    };
    
    for (uint32_t index = 0; index < 4; ++index )
    {
        key[index] = key[index] ^ 0x13579BDF;
        key[index] = (key[index] >> 25) | (key[index] << 7);
    }

    int cipher_len_bytes = sizeof(cipher);
    int n = cipher_len_bytes / sizeof(uint32_t);

    XXTEA_decrypt((uint32_t *)cipher, n, (const uint32_t *)key);

    // Output
    printf("Decrypted data (as chars, %zu bytes):\n", cipher_len_bytes);
    for (size_t i = 0; i < cipher_len_bytes; i++)
    {
        if (cipher[i] >= 32 && cipher[i] <= 126) {
             printf("%c", cipher[i]);
        } else {
             printf("."); 
        }
    }
    return 0;
}
// flag{cbee3251-9cff-4542-bf15-337bb8df7f3f}
This post is licensed under CC BY 4.0 by the author.