WMCTF 2023 Writeup
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_
看到generateShellcodes
和 mprotect
好确定有脏东西!估计肯定是有svc
调用 mincore , 但是 libapp 都打开了这部搜索一下 5000
? 直接找到了,我还绕什么。
直接 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)