WMCTF 2023 Writeup

发布时间 2023-08-22 09:24:01作者: s11nk

WMCTF 2023 Writeup

人刚分手,打个 CTF 拿 3 个二血,1 个三血,本来还挺高兴的,只有一血有奖励。好好好,都欺负我是吧。

ezAndroid

userName是一个类似RC4的东西,直接复制出来跑,password是变表 AES,在 .init_array 修改了 AES 的 sbox。

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "aes.h"
uint8_t byte_A138[10];

void sub_60A0(unsigned char *result, unsigned char *a2) {
    unsigned int i; // [xsp+18h] [xbp-18h]


    for (i = 0;; ++i) {
        result[i] = a2[i + 28] ^ a2[i % 0x1C];
        if (i == 8)
            break;
    }
}

char *__fastcall swap(char *a1, char *a2) {
    char *result; // rax
    char v3; // [rsp+1h] [rbp-19h]

    v3 = *a1;
    *a1 = *a2;
    result = a2;
    *a2 = v3;
    return result;
}

void sub_57D0(char *a1, char *a2, int a3, char *key, int a5) {
    int k; // [rsp+14h] [rbp-24Ch]
    int v7; // [rsp+18h] [rbp-248h]
    int v8; // [rsp+18h] [rbp-248h]
    int i; // [rsp+48h] [rbp-218h]
    int j; // [rsp+48h] [rbp-218h]
    int v12; // [rsp+48h] [rbp-218h]
    char v13[520]; // [rsp+50h] [rbp-210h] BYREF
    unsigned __int64 v14; // [rsp+258h] [rbp-8h]

    v7 = 0;
    for (i = 0; i < 256; ++i) {
        v13[i + 256] = i;
        v13[i] = key[i % a5];
    }
    for (j = 0; j < 256; ++j) {
        v7 = ((unsigned __int8) v13[j] + (unsigned __int8) v13[j + 256] + v7) % 256;
        swap(&v13[j + 256], &v13[v7 + 256]);
    }
    v8 = 0;
    v12 = 0;
    for (k = 0; k < a3; ++k) {
        v12 = (v12 + 1) % 256;
        v8 = ((unsigned __int8) v13[v12 + 256] + v8) % 256;
        swap(&v13[v12 + 256], &v13[v8 + 256]);
        a2[k] = k ^ v13[((unsigned __int8) v13[v8 + 256] + (unsigned __int8) v13[v12 + 256]) % 256 + 256] ^ a1[k];
    }
}

int main() {
    unsigned char byte_7EBB[67] = {
            0x3D, 0x15, 0x9E, 0xC9, 0xC5, 0x85, 0x5D, 0xDA, 0x5B, 0x33, 0xBF, 0x90, 0xAD, 0xEE, 0xE5, 0x16,
            0x00, 0x10, 0xF7, 0x29, 0xAC, 0xB4, 0x2D, 0x99, 0x31, 0x4B, 0x45, 0x0B, 0x0C, 0x27, 0xAD, 0xFD,
            0xF0, 0xB3, 0x6A, 0xE2, 0x5B, 0x7C, 0xA4, 0x71, 0xB5, 0x81, 0x74, 0x33, 0xCB, 0x57, 0x7C, 0x8B,
            0x11, 0x7C, 0x38, 0x8C, 0x97, 0xA0, 0x51, 0x57, 0x14, 0x1A, 0x6A, 0x1A, 0xF2, 0x69, 0x59, 0x9F,
            0xF6, 0x31, 0x8A
    };
    char key[9];
    sub_60A0((unsigned char *) key, byte_7EBB);
    size_t keyLen = strlen(key);

    uint8_t v1[10];
    *(uint16_t *) &v1[8] = 0x43AB;
    *(uint64_t *) v1 = 0xC1BDEB7EE66497E9LL;
    for (int i = 0; i < 10; ++i)
        byte_A138[i] = v1[i];
    char *userName = malloc(17);
    memcpy(userName + 10, "123456", 7);
    sub_57D0((char *) byte_A138, userName, 10, key, (int) keyLen);
    printf("%s\n", userName);
    unsigned char xmmword_7E1A[16] = {
            0x2B, 0xC8, 0x20, 0x8B, 0x5C, 0x0D, 0xA7, 0x9B, 0x2A, 0x51, 0x3A, 0xD2, 0x71, 0x71, 0xCA, 0x50
    };
    struct AES_ctx aesCtx;
    AES_init_ctx(&aesCtx, (uint8_t*)userName);
    AES_ECB_decrypt(&aesCtx, xmmword_7E1A);
    printf("%s\n", xmmword_7E1A);
    // WMCTF{Re_1s_eaSy_eZ_Rc4_@nd_AES!}
    return 0;
}

替换一下对应的 sbox 和 rsbox

unsigned char sbox[256] = {
        0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82,
        0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2,
        0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62,
        0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2,
        0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42,
        0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2,
        0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22,
        0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92,
        0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02,
        0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72,
        0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2,
        0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52,
        0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2,
        0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32,
        0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2,
        0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12
};

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

RightBack

pyc 花指令,静态分析发现规律,使用下面的脚本正则匹配去花

import re

r = re.compile(b'\x6e\x00\x6e\x04.{4}\x6e\x02.{2}', re.DOTALL)
with open('./RightBack.bin', 'rb') as pyc:
    code = pyc.read()
    ret_arr = r.findall(code)
    for ret in ret_arr:
        code = code.replace(ret, b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09')
    open('./a.pyc', 'wb').write(code)

pycdc 反编译的结果大概如下,有很多错误,但是无所谓,可以看到关键逻辑了。

# Source Generated with Decompyle++
# File: a.pyc (Python 3.9)

import struct


def T(num, round):
    numArr = bytearray(struct.pack('<I', num))
    for i in range(4): numArr[i] = Sbox[numArr[i]]
    return struct.unpack('<I', numArr)[0] ^ Rcon[round]


def p1(s, key):
    j = 0
    k = []
    for i in range(256): s.append(i)
    k.append(key[i % len(key)])
    for i in range(256): j = (j + s[i] + ord(k[i])) % 256
    s[i] = s[j]
    s[j] = s[i]


def p2(key):
    w = [0] * 44
    for i in range(4): w[i] = struct.unpack('<I', key[i * 4:i * 4 + 4])[0]
    cnt = 0
    for i in range(4, 44, 1): if
    i % 4 == 0: w[i] = w[i - 4] ^ T(w[i - 1], cnt)
    cnt += 1
    continuew[i] = w[i - 4] ^ w[i - 1]
    return w


def p3(s, p):
    i = j = 0
    for z in range(len(p)): i = (i + 1) % 256j = (j + s[i]) % 256
    s[i] = s[j]
    s[j] = s[i]
    p[z] ^= s[(s[i] + s[j]) % 256]
    return p


def F1(part1, part2):
    global REG
    REG = {'EAX': 0, 'EBX': 0, 'ECX': 0, 'EDX': 0, 'R8': 0, 'CNT': 0, 'EIP': 0}
    REG['EAX'] = part1
    REG['EBX'] = part2


def F2(v1, v2, v3):
    if v1 == 1:
        REG[reg_table[str(v2)]] = extendKey[REG[reg_table[str(v3)]]]
    elif v1 == 2:
        REG[reg_table[str(v2)]] = REG[reg_table[str(v3)]]
    elif v1 == 3:
        REG[reg_table[str(v2)]] = v3
    REG['EIP'] += 4


def F3(v1, v2, v3):
    if v1 == 1:
        REG[reg_table[str(v2)]] = REG[reg_table[str(v2)]] + extendKey[REG[reg_table[str(v3)]]] & 0xFFFFFFFFL
    elif v1 == 2:
        REG[reg_table[str(v2)]] = REG[reg_table[str(v2)]] + REG[reg_table[str(v3)]] & 0xFFFFFFFFL
    elif v1 == 3:
        REG[reg_table[str(v2)]] = REG[reg_table[str(v2)]] + v3 & 0xFFFFFFFFL
    REG['EIP'] += 4


def F4(v1, v2):
    REG[reg_table[str(v1)]] ^= REG[reg_table[str(v2)]]
    REG['EIP'] += 3


def F5(v1, v2):
    REG[reg_table[str(v1)]] &= v2
    REG['EIP'] += 3


def F6(v1, v2, v3):
    if v1 == 1:
        REG[reg_table[str(v2)]] -= extendKey[v3]
    elif v1 == 2:
        REG[reg_table[str(v2)]] -= REG[reg_table[str(v3)]]
    elif v1 == 3:
        REG[reg_table[str(v2)]] -= v3
    REG['EIP'] += 4


def F7(v1, v2):
    REG[reg_table[str(v1)]] |= REG[reg_table[str(v2)]]
    REG['EIP'] += 3


def F8(v1, v2):
    REG[reg_table[str(v1)]] = REG[reg_table[str(v1)]] >> REG[reg_table[str(v2)]] & 0xFFFFFFFFL
    REG['EIP'] += 3


def F9(v1, v2):
    REG[reg_table[str(v1)]] = REG[reg_table[str(v1)]] << REG[reg_table[str(v2)]] & 0xFFFFFFFFL
    REG['EIP'] += 3


def FA(v1, v2, v3):
    if v1 == 1:
        REG[reg_table[str(v2)]] *= extendKey[v3]
    elif v1 == 2:
        REG[reg_table[str(v2)]] *= REG[reg_table[str(v3)]]
    elif v1 == 3:
        REG[reg_table[str(v2)]] *= v3
    REG['EIP'] += 4


def FB():
    REG['R8'] = REG['CNT'] == 21
    REG['EIP'] += 1


def WC():
    if not REG['R8']:
        REG['EIP'] = 16
    else:
        REG['EIP'] += 1


def VM(part1, part2):
    F1(part1, part2)
    EIP = REG['EIP']
    if opcode[EIP] == 80: F2(opcode[EIP + 1], opcode[EIP + 2], opcode[EIP + 3])
    continue
    if opcode[EIP] == 29: F3(opcode[EIP + 1], opcode[EIP + 2], opcode[EIP + 3])
    continue
    if opcode[EIP] == 113: F4(opcode[EIP + 1], opcode[EIP + 2])
    continue
    if opcode[EIP] == 114: F5(opcode[EIP + 1], opcode[EIP + 2])
    continue
    if opcode[EIP] == 150: F6(opcode[EIP + 1], opcode[EIP + 2], opcode[EIP + 3])
    continue
    if opcode[EIP] == 87: F7(opcode[EIP + 1], opcode[EIP + 2])
    continue
    if opcode[EIP] == 116: F8(opcode[EIP + 1], opcode[EIP + 2])
    continue
    if opcode[EIP] == 41: F9(opcode[EIP + 1], opcode[EIP + 2])
    continue
    if opcode[EIP] == 220: FA(opcode[EIP + 1], opcode[EIP + 2], opcode[EIP + 3])
    continue
    if opcode[EIP] == 7: FB()
    continue
    if opcode[EIP] == 153: WC()
    continue


def Have():
    Hello = '                                                                                                                                                                                                                                                \n||   / |  / /                                                                     \n||  /  | / /  ___     //  ___      ___      _   __      ___       __  ___  ___    \n|| / /||/ / //___) ) // //   ) ) //   ) ) // ) )  ) ) //___) )     / /   //   ) ) \n||/ / |  / //       // //       //   / / // / /  / / //           / /   //   / /  \n|  /  | / ((____   // ((____   ((___/ / // / /  / / ((____       / /   ((___/ /   \n                                                                                            \n                                                                                            \n||   / |  / / /|    //| |     //   ) ) /__  ___/ //   / / ___      ___      ___      ___    \n||  /  | / / //|   // | |    //          / /    //___   //   ) ) //   ) ) //   ) ) //   ) ) \n|| / /||/ / // |  //  | |   //          / /    / ___     ___/ / //   / /   ___/ /   __ / /  \n||/ / |  / //  | //   | |  //          / /    //       / ____/ //   / /  / ____/       ) )  \n|  /  | / //   |//    | | ((____/ /   / /    //       / /____ ((___/ /  / /____  ((___/ /     \n    '
    print(Hello)
    return input('RightBack: ').encode()


def Fun(right):
    if len(right) != 64: print('XD')
    exit()
    back = b''
    for i in range(0, len(right), 8): part1 = struct.unpack('>I', right[i + 0:i + 4])[0]
    part2 = struct.unpack('>I', right[i + 4:i + 8])[0]
    if i != 0:    part1 ^= struct.unpack('>I', back[i - 8:i - 4])[0]
    part2 ^= struct.unpack('>I', back[i - 4:i])[0]
    VM(part1, part2)
    back += struct.pack('>I', REG['EAX'])
    back += struct.pack('>I', REG['EBX'])
    return back


if __name__ == '__main__':
    REG = {}
    EIP = 0
    reg_table = {'1': 'EAX', '2': 'EBX', '3': 'ECX', '4': 'EDX', '5': 'R8', '6': 'CNT', '7': 'EIP'}
    Sbox = [82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255,
            135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250,
            195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134,
            104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87,
            167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143,
            202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206,
            240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26,
            113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219,
            192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81,
            127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200,
            235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125]
    Rcon = [16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 0x80000000L, 452984832,
            905969664]
    s = []
    key = 'CalmDownBelieveU'
    p1(s, key)
    key = [61, 15, 58, 65, 177, 180, 182, 248, 192, 143, 37, 238, 50, 29, 215, 190]
    key = bytes(p3(s, key))
    extendKey = p2(bytes(key))
    opcode = [69, 136, 121, 24, 179, 67, 209, 20, 27, 169, 205, 146, 212, 160, 124, 49, 20, 155, 157, 253, 52, 71, 174,
              164, 134, 60, 184, 203, 131, 210, 57, 151, 77, 241, 61, 6, 13, 52, 235, 37, 100, 178, 8, 238, 205, 27,
              194, 159, 230, 165, 211, 221, 100, 217, 111, 202, 185, 207, 226, 50, 88, 4, 58, 73, 10, 92, 24, 230, 246,
              245, 21, 110, 182, 151, 85, 28, 181, 191, 185, 236, 92, 98, 222, 85, 228, 14, 235, 93, 77, 161, 61, 140,
              222, 74, 124, 13, 211, 75, 134, 235, 164, 228, 235, 16, 29, 41, 49, 105, 188, 51, 232, 65, 209, 165, 35,
              182, 248, 245, 69, 18, 152, 71, 223, 85, 114]
    opcode = p3(s, opcode)
    right = Have()
    back = Fun(right)
    data1 = [228, 244, 207, 251, 194, 124, 252, 61, 198, 145, 97, 98, 89, 25, 92, 208, 155, 38, 34, 225, 98, 206, 234,
             245, 223, 54, 214, 137, 35, 86, 180, 66, 223, 234, 90, 136, 5, 189, 166, 117, 111, 222, 39, 156, 163, 173,
             36, 174, 47, 144, 15, 160, 45, 239, 211, 11, 190, 181, 24, 164, 234, 114, 174, 27]
    data1 = bytes(p3(s, data1))
    data2 = [165, 83, 203, 51, 99, 164, 30, 91, 230, 64, 181, 55, 190, 47, 125, 240, 186, 173, 116, 47, 89, 64, 68, 215,
             124, 138, 34, 175, 60, 136, 77, 216, 250, 127, 14, 14, 66, 168, 198, 247, 252, 189, 243, 239, 25, 63, 143,
             7, 177, 13, 99, 226, 100, 6, 207, 77, 46, 136, 251, 123, 225, 27, 76, 183]
    data2 = bytes(p3(s, data2))
    data3 = [95, 219, 46, 178, 111, 141, 17, 168, 254, 60, 68, 59, 41, 183, 182, 118, 3, 47, 150, 240, 140, 159, 110,
             238]
    data3 = bytes(p3(s, data3))
    if back == data2:
        print(bytes(data1).decode())
    else:
        print(bytes(data3).decode())

结合 xpython trace的结果,虚拟机比较简单(写的反汇编脚本丢了,就不再写一遍了),直接给出解密代码

#include <stdio.h>
#include <stdint.h>

static uint32_t extendKey[44] = {1835819331, 1853321028, 1768711490, 1432712805, 2177920767, 4020699579, 2261476601, 3551400604, 711874531, 3318306392, 1124217505, 2427199549, 3099853672, 2098025776, 1041196945, 2929936300, 246748610, 1941455090, 1303848803, 3809763535, 1395557789, 546751855, 1830937100, 2385871555, 2516030638, 3043054017, 3628118989, 1450520846, 1825094265, 3651791800, 32069749, 1469868411, 919887482, 4017993154, 4002737591, 3104343244, 4134211933, 420914335, 4152510760, 1317719524, 1990496755, 1873950060, 2553314372, 3602559392};

typedef struct {
    uint32_t p1;
    uint32_t p2;
} uint32_pair;

uint32_t ror32(uint32_t v, uint32_t r) {
    return (v >> r) | (v << (32 - r));
}

uint32_pair decrypt(uint32_t p1, uint32_t p2) {
    for (int i = 21; i > 0; --i) {
        p2 -= extendKey[i * 2 + 1];
        p2 = ror32(p2, p1 & 31);
        p2 ^= p1;
        p1 -= extendKey[i * 2];
        p1 = ror32(p1, p2 & 31);
        p1 ^= p2;
    }
    p2 -= extendKey[1];
    p1 -= extendKey[0];
    uint32_pair ret = {p1, p2};
    return ret;
}

int main() {
    uint8_t flag[] = {4, 58, 242, 54, 86, 177, 154, 252, 247, 30, 33, 220, 219, 143, 142, 148, 77, 52, 231, 157, 156, 82, 12, 110, 251, 250, 213, 253, 50, 249, 120, 44, 187, 190, 57, 193, 217, 133, 117, 182, 40, 248, 204, 120, 164, 228, 133, 146, 14, 189, 114, 197, 175, 135, 145, 42, 139, 241, 239, 150, 22, 96, 209, 18, 0};
    uint32_pair last;
    for (int i = 0; i < 64; i += 8) {
        uint32_t p1 = (flag[i + 0] << 24 | flag[i + 1] << 16 | flag[i + 2] << 8 | flag[i + 3]);
        uint32_t p2 = (flag[i + 4] << 24 | flag[i + 5] << 16 | flag[i + 6] << 8 | flag[i + 7]);
        uint32_pair cur = decrypt(p1, p2);
        if (i != 0) {
            cur.p1 ^= last.p1;
            cur.p2 ^= last.p2;
        }
        flag[i + 3] = cur.p1 & 0xff;
        flag[i + 2] = (cur.p1 >> 8) & 0xff;
        flag[i + 1] = (cur.p1 >> 16) & 0xff;
        flag[i + 0] = (cur.p1 >> 24) & 0xff;
        flag[i + 7] = cur.p2 & 0xff;
        flag[i + 6] = (cur.p2 >> 8) & 0xff;
        flag[i + 5] = (cur.p2 >> 16) & 0xff;
        flag[i + 4] = (cur.p2 >> 24) & 0xff;
        last.p1 = p1;
        last.p2 = p2;
    }
    printf("%s", flag);
    return 0;
}

gohunt

一堆加密,xxtea、xor、变表的base58、变表的base64,直接给解密脚本啦

import base64
import string


def b64_decode(val: str):
    b64_std_table = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
    my_b64_table = get_b64_table()
    table = str.maketrans(my_b64_table, b64_std_table)
    val = val.translate(table)
    return base64.b64decode(val)
def get_b64_table():
    r_b64table = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2E, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x17, 0x1B,
                  0x30, 0x29, 0x0F, 0x24, 0x13, 0x3D, 0x2D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x2A, 0x18, 0x35,
                  0x32, 0x1A, 0x3A, 0x3F, 0x04, 0x25, 0x38, 0x36, 0x1E, 0x20, 0x00, 0x34, 0x2C, 0x02, 0x0B, 0x1C, 0x0A,
                  0x26, 0x33, 0x3B, 0x06, 0x39, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0x28, 0x31, 0x14, 0x0C,
                  0x1D, 0x11, 0x37, 0x08, 0x27, 0x01, 0x15, 0x07, 0x10, 0x0D, 0x19, 0x22, 0x23, 0x3C, 0x3E, 0x12, 0x16,
                  0x1F, 0x03, 0x05, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                  0xFF]
    b64table = []
    for i in range(64):
        b64table.append(r_b64table.index(i))
    return ''.join(chr(_) for _ in b64table)

import base58
import xxtea

flag = base58.b58decode('YMQHsYFQu7kkTqu3Xmt1ruYUDLU8uaMoPpsfjqYF4TQMMKtw5KF7cpWrkWpk3', alphabet=b'nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj')
flag = bytearray(flag)
xor_key = b64_decode('7Ik1Sjk6edoyu6MDdvoTe3==')
for i in range(len(flag)):
    flag[i] ^= xor_key[i % len(xor_key)]
xxtea_key = b64_decode('g6CdeI4xgd@7ojkrp6nDdM==')
flag = xxtea.decrypt(flag, xxtea_key, padding=False)
print(flag)

babyAnti2.0

出题人还我相册!(自己没备份好,呜呜

除了 内存陷阱部分,好像和今年 VNCTF 2023 的 1.0 版本一样,mincore hook 半天,不好使。 ida 打开字符串搜索 FfiTrampoline_ 看到generateShellcodesmprotect 好确定有脏东西!估计肯定是有svc 调用 mincore , 但是 libapp 都打开了这部搜索一下 5000? 直接找到了,我还绕什么。

image

image

直接 hook 一下,脚本有点乱,做题的时候写的,hook了一堆。

const android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
console.log(android_dlopen_ext);
if (android_dlopen_ext != null) {
    Interceptor.attach(android_dlopen_ext, {
        onEnter: function (args) {
            var soName = args[0].readCString();
            if (soName.indexOf("libanticheat.so") != -1) {
                this.hook = true;
            }
        },
        onLeave: function (retval) {
            if (this.hook) {
                hook_func();
            };
        }
    });
}


function hook_pthread_create() {
    var pt_create_func = Module.findExportByName(null, 'pthread_create');
    var detect_frida_loop_addr = null;
    console.log('pt_create_func:', pt_create_func);

    Interceptor.attach(pt_create_func, {
        onEnter: function () {
            if (detect_frida_loop_addr == null) {
                var base_addr = Module.getBaseAddress('libanticheat.so');
                if (base_addr != null) {
                    detect_frida_loop_addr = base_addr.add(0x5EA48)
                    console.log('this.context.x2: ', detect_frida_loop_addr, this.context.x2);
                    if (this.context.x2.compare(detect_frida_loop_addr) == 0) {
                        hook_anti_frida_replace(this.context.x2);
                    }
                }

            }

        },
        onLeave: function (retval) {
            // console.log('retval',retval);
        }
    })


}

function hook_anti_frida_replace(addr) {
    console.log('replace anti_addr :', addr);
    Interceptor.replace(addr, new NativeCallback(function (a1) {
        console.log('replace success');
        return;
    }, 'pointer', []));

}
Java.perform(function () {
    var antiCheatPlugin = Java.use("com.WMCTF2023.anti_cheat.AntiCheatPlugin");
    antiCheatPlugin["b"].implementation = function (params) {
        console.log("b is called");
        return true;
    }
})

setImmediate(hook_pthread_create());
function hook_func() {
    var base_addr = Module.getBaseAddress("libanticheat.so");

    var is_device_rooted_hidden = base_addr.add(0x5CF6C);
    var is_device_modified_hidden = base_addr.add(0x5CFCC);
    var is_device_injected_hidden = base_addr.add(0x5D0C8);

    var is_device_rooted = base_addr.add(0x5CF38);
    var is_device_modified = base_addr.add(0x5CF98);
    var is_device_injected = base_addr.add(0x5CFFC);
    var memtrap = base_addr.add(0x5ECC0);
    var init_memtrap = base_addr.add(0x5EC74);

    var mincore = Module.findExportByName(null, 'mincore');



    Interceptor.attach(init_memtrap, {
        onEnter: function (args) {
            console.log(`init_memtrap called ${args[0]}`);
        },
        onLeave: function (retval) {
            console.log(`init_memtrap retval ${retval}`);

        }
    });
    // WMCTF{We1c0me_t0_Th3_W0r1d_0f_MemTr4p#^-^}
    var aa = null
    Interceptor.attach(memtrap, {
        onEnter: function (args) {
            // console.log(`memtrap called ${args[0]}`);
            if (aa == null) {
                aa = Interceptor.attach(mincore, {
                    onEnter: function (args) {
                        // console.log(`mincore called`);
                        this.vec = args[2];
                    },
                    onLeave: function (retval) {
                        // console.log(`mincore before modify retval ${this.vec.readU8()}, ${retval}`);
                        this.vec.writeU8(0);
                        // console.log(`mincore after modify retval ${this.vec.readU8()}, ${retval}`);
                    }
                });

                var base_addr_app = Module.getBaseAddress("libapp.so");
                Interceptor.attach(base_addr_app.add(0x314F08), {
                    onEnter: function (args) {
                        console.log(`cmp called ${this.context.x2}`);
                        this.context.x2 = 5000;
                    },
                })
            }
        },
        onLeave: function (retval) {
            // console.log(`memtrap called ${retval}`);

        }
    });

    Interceptor.attach(is_device_rooted, {
        onLeave: function (retval) {
            // console.log('is_device_rooted called');
            retval.replace(0);
        }
    });

    Interceptor.attach(is_device_modified, {
        onLeave: function (retval) {
            // console.log('is_device_modified called');
            retval.replace(0);
        }
    });

    Interceptor.attach(is_device_injected, {
        onLeave: function (retval) {
            // console.log('is_device_modified called');
            retval.replace(0);
        }
    });

    // ---- 应该是没用,最开始看到就都 hook了  ---------
    Interceptor.attach(is_device_rooted_hidden, {
        onLeave: function (retval) {
            console.log('is_device_rooted_hidden called');
            retval.replace(0);
        }
    });

    Interceptor.attach(is_device_modified_hidden, {
        onLeave: function (retval) {
            console.log('is_device_modified_hidden called');
            retval.replace(0);
        }
    });

    Interceptor.attach(is_device_injected_hidden, {
        onLeave: function (retval) {
            console.log('is_device_modified_hidden called');
            retval.replace(0);
        }
    });
}

ezIos

第一次做IOS逆向,感觉不是很难,但是混淆有的烦人,看到调用的外面的函数好像不是很多,打算unicorn 模拟执行。 但是 __dyld_get_image_xxx的三个函数不知道干啥的,卡住了,刚好当时学弟叫我,就去讲题了。第二天,比赛结束之后。问了一下 REtard 爷,这三个函数的作用才出。 整体来说就是 rc4 加密输入,然后 xtea 解密密文,二者对比。代码如下

from capstone import *
import cle
from unicorn import *
from unicorn.arm64_const import *
import struct

cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM)

uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
game = cle.Loader('./UnCrackable Level 3')
gameSelf = game.all_objects[0]

for segment in gameSelf.segments:
    uc.mem_map(segment.vaddr, segment.memsize)
    print(f'{hex(segment.vaddr)} -> {hex(segment.vaddr + segment.memsize)}')
    if segment.filesize > 0:
        uc.mem_write(segment.vaddr, game.memory.load(segment.vaddr, segment.memsize))

uc.mem_map(0x100100000, 0x1000)
uc.mem_write(0x100100088, b'12345678')

str_base_addr = 0x10000

flag = []
en_flag = []


def hook_code(mu: Uc, address, size, user_data):
    global str_base_addr, flag, en_flag
    if address == 0x10000D6BC:  # __dyld_get_image_header
        mu.reg_write(UC_ARM64_REG_X0, gameSelf.mapped_base)
        lr = mu.reg_read(UC_ARM64_REG_LR)
        mu.reg_write(UC_ARM64_REG_PC, lr)
    if address == 0x10000D6C8:  # __dyld_get_image_name
        mu.reg_write(UC_ARM64_REG_X0, 0)
        lr = mu.reg_read(UC_ARM64_REG_LR)
        mu.reg_write(UC_ARM64_REG_PC, lr)
    if address == 0x10000D6D4:  # __dyld_get_image_vmaddr_slide
        mu.reg_write(UC_ARM64_REG_X0, 0)
        lr = mu.reg_read(UC_ARM64_REG_LR)
        mu.reg_write(UC_ARM64_REG_PC, lr)
    if address == 0x10000D95C:  # _strdup
        str_addr = mu.reg_read(UC_ARM64_REG_X0)
        # copy 字符串
        i = 0
        while True:
            c = mu.mem_read(str_addr + i, 1)
            mu.mem_write(str_base_addr + i, bytes(c))
            if c == b'\x00':
                break
            i += 1
        mu.reg_write(UC_ARM64_REG_X0, str_base_addr)
        str_base_addr += i
        lr = mu.reg_read(UC_ARM64_REG_LR)
        mu.reg_write(UC_ARM64_REG_PC, lr)
    if address == 0x10000D78C:  # free,不处理
        lr = mu.reg_read(UC_ARM64_REG_LR)
        mu.reg_write(UC_ARM64_REG_PC, lr)
    if address == 0x1000057C8: # tea 结果
        m_addr = mu.reg_read(UC_ARM64_REG_X0)
        en_flag += list(mu.mem_read(m_addr, 4 * 2))
        print(en_flag)
    if address == 0x100009D8C:  # 对比密文处
        flag.append(mu.reg_read(UC_ARM64_REG_W9))
        mu.reg_write(UC_ARM64_REG_W8, 0)
        print(''.join(chr(_) for _ in flag))


uc.reg_write(UC_ARM64_REG_SP, 0x1000)
uc.mem_write(0x2000, bytes([38, 163, 101, 125, 73, 104, 238, 103, 140, 208, 62, 41, 193, 123, 94, 196, 93, 49, 133, 130, 122, 41, 50, 159, 93, 49, 133, 130, 122, 41, 50, 159]))
uc.reg_write(UC_ARM64_REG_X0, 0x2000)
uc.hook_add(UC_HOOK_CODE, hook_code)
uc.emu_start(0x1000091e4, 0x10000A998)