湾区杯初赛WriteUp&复现

和X2c的nixware、Ky4niT3师傅参加了线上初赛,但是寄了。第一次组队参赛,还是太菜了。Re方向解了一道,然后和师傅们拼凑出了一道。基本就是签到题靠AI,难题没思路,见识还是太少了

WriteUp

hardtest

  • 附件程序用IDA打开,shift+F12在字符串表通过关键字“flag:”可以找到main函数位置

    字符串表

  • 以下是优化后的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
    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
    __int64 __fastcall main(int a1, char **a2, char **a3)
    {
    unsigned int seed; // eax
    unsigned __int64 scale1; // rax
    void *temp1; // rsp
    unsigned __int64 scale2; // rax
    void *temp2; // rsp
    char temp; // al
    _QWORD buf[2]; // [rsp+8h] [rbp-F0h] BYREF
    __int64 i_2; // [rsp+18h] [rbp-E0h]
    __int64 v12; // [rsp+20h] [rbp-D8h]
    char input; // [rsp+29h] [rbp-CFh] BYREF
    char judge; // [rsp+2Ah] [rbp-CEh]
    char randNum; // [rsp+2Bh] [rbp-CDh]
    int j; // [rsp+2Ch] [rbp-CCh]
    int i; // [rsp+30h] [rbp-C8h]
    int length; // [rsp+34h] [rbp-C4h]
    __int64 v19; // [rsp+38h] [rbp-C0h]
    __int64 Buf; // [rsp+40h] [rbp-B8h]
    __int64 v21; // [rsp+48h] [rbp-B0h]
    _QWORD *Enc; // [rsp+50h] [rbp-A8h]
    char flag[104]; // [rsp+58h] [rbp-A0h] BYREF
    unsigned __int64 v24; // [rsp+C0h] [rbp-38h]

    v24 = __readfsqword(0x28u);
    setbuf(stdin, 0LL);
    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
    seed = time(0LL);
    srand(seed);
    randNum = rand() % 255 + 1;
    printf("input your number(1-255): ");
    if ( __isoc99_scanf("%d", &input) == 1 && randNum == input )
    {
    while ( getchar() != 10 )
    ;
    printf("flag: ");
    fgets(flag, 100, stdin); // 获取flag输入
    flag[strcspn(flag, "\n")] = 0;
    length = strlen(flag);
    v19 = length - 1LL;
    i_2 = length;
    v12 = 0LL;
    buf[0] = length;
    buf[1] = 0LL;
    scale1 = 16 * ((length + 15LL) / 0x10uLL); // 在动态分配栈上的内存
    while ( buf != (buf - (scale1 & 0xFFFFFFFFFFFFF000LL)) )
    ;
    temp1 = alloca(scale1 & 0xFFF);
    if ( (scale1 & 0xFFF) != 0 )
    *(&buf[-1] + (scale1 & 0xFFF)) = *(&buf[-1] + (scale1 & 0xFFF));
    Buf = buf; // Buf指向分配的内存
    encrypt1(flag, buf); // 对flag进行加密,加密结果实际上由Buf记录,buf是一个通用的缓存空间
    v21 = length - 1LL;
    scale2 = 16 * ((length + 15LL) / 0x10uLL); // 再次动态分配栈上的内存
    while ( buf != (buf - (scale2 & 0xFFFFFFFFFFFFF000LL)) )
    ;
    temp2 = alloca(scale2 & 0xFFF);
    if ( (scale2 & 0xFFF) != 0 )
    *(&buf[-1] + (scale2 & 0xFFF)) = *(&buf[-1] + (scale2 & 0xFFF));
    Enc = buf; // Enc指向分配的内存
    for ( i = 0; i < length; ++i )
    {
    temp = encrypt2(*(Buf + i)); // 对上一轮加密的结果再进行一次加密,加密结果由Enc记录
    *(Enc + i) = temp;
    }
    judge = 1;
    for ( j = 0; j < length; ++j )
    {
    if ( *(Enc + j) != byte_2120[j] ) // 判断加密结果是否正确
    {
    judge = 0;
    break;
    }
    }
    if ( judge )
    puts("right");
    return 0LL;
    }
    else
    {
    puts("error");
    return 1LL;
    }
    }

    __int64 __fastcall encrypt1(const char *flag, __int64 buf)
    {
    __int64 count; // rax
    signed int i; // [rsp+14h] [rbp-1Ch]
    signed int length; // [rsp+18h] [rbp-18h]

    length = strlen(flag);
    for ( i = 0; ; ++i )
    {
    count = i;
    if ( i >= length )
    break;
    *(i + buf) = rol(flag[i], (i % 7) + 1); // return (flag[i] << ((i%7)+1)) | (flag[i] >> (8 - ((i%7)+1)))
    }
    return count;
    }

    __int64 __fastcall encrypt2(char Buf_i)
    {
    unsigned __int8 v1; // al
    unsigned __int8 v3; // [rsp+15h] [rbp-3h]

    v1 = rol(Buf_i ^ 0x5Au, 3); // return (a1^0x5Au << 3) | (a1^0x5Au >> (8 - 3));
    v3 = sub_1313((16 * ((3 * (v1 >> 4)) & 0xF)) | (5 * (v1 & 0xF)) & 0xFu);// AI说是模幂运算函数,计算a1^(255) mod 257
    return byte_2020[ror(v3, 2)]; // return (v3 >> 2) | (v3 << (8 - 2));此处建立了映射关系
    }
  • 笔者加解密这一块太菜了,没有数学头脑,只能依靠AI解密了(因为涉及数论,笔者还没学过)

    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
    def rol(b, n):
    n %= 8
    return ((b << n) | (b >> (8 - n))) & 0xFF

    def ror(b, n):
    n %= 8
    return ((b >> n) | (b << (8 - n))) & 0xFF

    def mod_inv(a, mod=257):
    # 计算a在模257下的逆元,使用费马小定理
    return pow(a, 255, mod)

    byte_2020 = [
    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
    ]

    # 构建反向映射
    rev_2020 = {}
    for idx, val in enumerate(byte_2020):
    rev_2020[val] = idx

    enc_data = [
    0x97, 0xD5, 0x60, 0x43, 0xB4, 0x10, 0x43, 0x73, 0x0F, 0xDA,
    0x43, 0xCD, 0xD3, 0xE8, 0x73, 0x4A, 0x94, 0xC3, 0xCD, 0x71,
    0xBD, 0xDC, 0x97, 0x1A
    ]

    # 逆向encrypt2
    enc1_out = []
    for i, c in enumerate(enc_data):
    idx = rev_2020[c] # 得到索引(即v3循环右移2位后的值)
    v3 = rol(idx, 2) # 循环左移2位得到v3(原循环右移2位的逆)
    # 如果v3为0?但原代码中a1不为0才计算,这里假设v3不为0
    if v3 == 0:
    x = 0
    else:
    x = mod_inv(v3, 257)
    A = (x >> 4) & 0xF
    B = x & 0xF
    high = (11 * A) & 0xF
    low = (13 * B) & 0xF
    v1 = (high << 4) | low
    tmp = ror(v1, 3) # 循环右移3位(原循环左移3位的逆)
    enc1_out.append(tmp ^ 0x5A)

    # 逆向encrypt1
    flag = []
    for i, c in enumerate(enc1_out):
    n = (i % 7) + 1
    flag_char = ror(c, n) # 循环右移n位(原循环左移n位的逆)
    flag.append(flag_char)

    # 转换为字符串
    flag_str = ''.join(chr(b) for b in flag)
    print(flag_str)

minigame

  • 微信小程序逆向,第一次遇到这个类型的题

  • wedecode解包附件,输出结果在wedecode-main/OUTPUT/default里

    解包结果

    没有这方面的经验,让AI分析项目,发现utils/validator.wasm里有检验逻辑。wasm是一种二进制文件,可以转成wat阅读(类似汇编)。使用在线工具wasm2wat demo把wasm转成wat:

    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
    (module
    (type $t0 (func (param i32) (result i32)))
    (type $t1 (func))
    (func $c (export "c") (type $t0) (param $p0 i32) (result i32)
    (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
    (if $I6
    (i32.ne
    (block $B0 (result i32)
    (block $B1
    (block $B2
    (br_if $B2
    (i32.eqz
    (i32.and
    (local.tee $l3
    (local.get $p0))
    (i32.const 3))))
    (drop
    (br_if $B0
    (i32.const 0)
    (i32.eqz
    (i32.load8_u
    (local.get $p0)))))
    (loop $L3
    (br_if $B2
    (i32.eqz
    (i32.and
    (local.tee $p0
    (i32.add
    (local.get $p0)
    (i32.const 1)))
    (i32.const 3))))
    (br_if $L3
    (i32.load8_u
    (local.get $p0))))
    (br $B1))
    (loop $L4
    (local.set $p0
    (i32.add
    (local.tee $l1
    (local.get $p0))
    (i32.const 4)))
    (br_if $L4
    (i32.eq
    (i32.and
    (i32.or
    (i32.sub
    (i32.const 16843008)
    (local.tee $l4
    (i32.load
    (local.get $l1))))
    (local.get $l4))
    (i32.const -2139062144))
    (i32.const -2139062144))))
    (loop $L5
    (local.set $l1
    (i32.add
    (local.tee $p0
    (local.get $l1))
    (i32.const 1)))
    (br_if $L5
    (i32.load8_u
    (local.get $p0)))))
    (i32.sub
    (local.get $p0)
    (local.get $l3)))
    (i32.const 38))
    (then
    (return
    (i32.const 0))))
    (loop $L7
    (block $B8
    (local.set $l1
    (i32.eq
    (local.tee $p0
    (i32.xor
    (i32.load8_u offset=1024
    (local.get $l2))
    (i32.load8_s
    (i32.add
    (local.get $l2)
    (local.get $l3)))))
    (i32.const 153)))
    (br_if $B8
    (i32.ne
    (local.get $p0)
    (i32.const 153)))
    (br_if $L7
    (i32.ne
    (local.tee $l2
    (i32.add
    (local.get $l2)
    (i32.const 1)))
    (i32.const 38)))))
    (local.get $l1))
    (func $b (export "b") (type $t1))
    (memory $a (export "a") 258 258)
    (data $d0 (i32.const 1024) "\ff\f5\f8\fe\e2\ff\f8\fc\a9\fb\ab\ae\fa\ad\ac\a8\fa\ae\ab\a1\a1\af\ae\f8\ac\af\ae\fc\a1\fa\a8\fb\fb\ad\fc\ac\aa\e4"))

  • 由AI分析知,逻辑主要是把输入的内容与密文异或,异或的结果要等于153。等价于将输入的内容和153异或要等于密文。解密就是拿153异或密文。exp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    enc = [
    0xff, 0xf5, 0xf8, 0xfe, 0xe2, 0xff, 0xf8, 0xfc, 0xa9, 0xfb,
    0xab, 0xae, 0xfa, 0xad, 0xac, 0xa8, 0xfa, 0xae, 0xab, 0xa1,
    0xa1, 0xaf, 0xae, 0xf8, 0xac, 0xaf, 0xae, 0xfc, 0xa1, 0xfa,
    0xa8, 0xfb, 0xfb, 0xad, 0xfc, 0xac, 0xaa, 0xe4
    ]

    flag = ''.join([chr(data ^ 153) for data in enc])
    print(flag)

复现

strangapp

  • apk用JEB打开会发现MainActivity全是nop:

    MainActivity

  • 拖进GDA里看一下,会发现apk加了壳

    GDA分析

    所以第一步先脱壳

  • 没辙了,题目环境卡的好死,配了一下午环境没配出能运行apk的,改天再试😡

作者

SydzI

发布于

2025-10-03

更新于

2025-11-05

许可协议

评论