-
-
[原创]KCTF 2025 第四题 血色试炼wp
-
发表于: 2025-8-21 20:39 4734
-
由于动调没有起来,故从TlsCallback_0开始分析
注意到有两个很明显的key,以及base64的Table
ThisEncAndDecKey和tweakKey_2024521

而且这里还有两个动态加载,下面肯定有很难读的反调试
注意到两个很大的数组,都是256长度

到这里其实已经开始猜是不是AES了,从后面看来是猜对了
继续往下看看到一堆不太看得懂的LoadLibraryW,感觉是hook相关代码,但是没影响我IDA调试

从资源段读取数据,发现核心的解密函数

其实是个蛮简单的反调试,主体在以下部分,主要靠syscall 0x19进行反调试

总共应该是有3处,将恶意跳转改成强制jmp就解决了
然后就开始了漫长的逆向之路
开头就是当头一棒的Base64

但是很显然,在线网站解出来的和题目给的不一样~~~
其实看着很复杂,但是魔改的核心其实很少很少,就只有下面这一小块

也就是大伙熟悉的==相关流程,稍微对输入字符进行了一点点的位移,呈现在keyGen上面就是如下所示
这块是我觉得最折磨的部分,因为对AES不熟悉,对着标准AES一点点扣出来的

但是捏,其实就只魔改了一个函数,也就是addRoundKey,其他其实一点都没改,invMixColumns看着很唬人,但是实际上没有任何魔改

所以对应的代码如下
这块其实我没分析出来,但是有蛮力,通过替换资源段的方式,让代码运行过程中自吐了最大4轮所需要的xor Key

对应到我的代码上,写的很丑))

最后我们回到主函数,简单读读就能明白原来是加密了4轮

本来以为是3轮,但是加密完感觉长度不对,于是改成了4轮,长度就对上了
按照每层加密的值排布
AES_ECB.hpp是标准AES,里面没有任何代码改动
int base64_encode(const uint8_t* input, int in_len, char* output) { int out_len = 0; for (int i = 0; i < in_len; i += 3) { uint32_t buf = 0; int remain = in_len - i; buf |= input[i]; if (remain > 1) buf |= input[i + 1] << 8; if (remain > 2) buf |= input[i + 2] << 16; if (remain > 2) { output[out_len++] = base64_table[(buf) & 0x3F]; output[out_len++] = base64_table[(buf >> 6) & 0x3F]; output[out_len++] = base64_table[(buf >> 12) & 0x3F]; output[out_len++] = base64_table[(buf >> 18) & 0x3F]; } else { output[out_len++] = base64_table[(buf >> 0) & 0x3F]; output[out_len++] = (remain > 1) ? base64_table[(buf >> 6) & 0x3F]: base64_table[((buf >> 6) & 0x3) << 4]; output[out_len++] = (remain > 1) ? base64_table[((buf >> 12) & 0xF)<<2] : '='; output[out_len++] = '='; } } output[out_len] = '\0'; return out_len;}int base64_encode(const uint8_t* input, int in_len, char* output) { int out_len = 0; for (int i = 0; i < in_len; i += 3) { uint32_t buf = 0; int remain = in_len - i; buf |= input[i]; if (remain > 1) buf |= input[i + 1] << 8; if (remain > 2) buf |= input[i + 2] << 16; if (remain > 2) { output[out_len++] = base64_table[(buf) & 0x3F]; output[out_len++] = base64_table[(buf >> 6) & 0x3F]; output[out_len++] = base64_table[(buf >> 12) & 0x3F]; output[out_len++] = base64_table[(buf >> 18) & 0x3F]; } else { output[out_len++] = base64_table[(buf >> 0) & 0x3F]; output[out_len++] = (remain > 1) ? base64_table[(buf >> 6) & 0x3F]: base64_table[((buf >> 6) & 0x3) << 4]; output[out_len++] = (remain > 1) ? base64_table[((buf >> 12) & 0xF)<<2] : '='; output[out_len++] = '='; } } output[out_len] = '\0'; return out_len;}int addRoundKey_self(uint8_t (*state)[4], const uint32_t *key){ uint8_t k[4][4]; /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { k[i][j] = (uint8_t) BYTE(key[j], i); state[i][j] ^= k[i][j]; } } return 0;}int addRoundKey_self(uint8_t (*state)[4], const uint32_t *key){ uint8_t k[4][4]; /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { k[i][j] = (uint8_t) BYTE(key[j], i); state[i][j] ^= k[i][j]; } } return 0;}KCTFvCF5nc2wC5/sVISvceXVkY==8g+JjodOZ4bcaurrKobFVzxuSGA2uDFJFyXqAlnTlKI=l/SxsR0BCjRZTmc7XWscrMv38iYuoiUECvi9Tuvi+ZiJZmdR5EcvVnTZOZvrnOrwtSzQkyqcvZLgkwDltPF9RpInibA5fTpH/bJni2yvLzTKao2uL5eLZ5QIxPj8bsYWe48ZohC5/Jw3cNNAaX8/rA==KCTFvCF5nc2wC5/sVISvceXVkY==8g+JjodOZ4bcaurrKobFVzxuSGA2uDFJFyXqAlnTlKI=l/SxsR0BCjRZTmc7XWscrMv38iYuoiUECvi9Tuvi+ZiJZmdR5EcvVnTZOZvrnOrwtSzQkyqcvZLgkwDltPF9RpInibA5fTpH/bJni2yvLzTKao2uL5eLZ5QIxPj8bsYWe48ZohC5/Jw3cNNAaX8/rA==#include <iostream>#include <vector>#include <string>#include <cctype>#include <time.h>using namespace std;#include "DES-AES/AES_ECB.hpp"#include "data.h"uint32_t buf[8] = {};#define BOOL boolint invMixColumns_self(uint8_t (*state)[4]){ uint8_t tmp[4][4]; uint8_t tmp2[4][4]; for (int i = 0; i < 4; ++i ) { uint8_t v3 = buf1[state[0][i]]; uint8_t v4 = buf2[state[1][i]]; uint8_t v5 = buf3[state[2][i]]; tmp2[0][i] = v3 ^ v4 ^ v5 ^ buf4[state[3][i]]; uint8_t v6 = buf4[state[0][i]]; uint8_t v7 = buf1[state[1][i]]; uint8_t v8 = buf2[state[2][i]]; tmp2[1][i] = buf3[state[3][i]] ^ v6 ^ v7 ^ v8; uint8_t v9 = buf3[state[0][i]]; uint8_t v10 = buf4[state[1][i]]; uint8_t v11 = buf1[state[2][i]]; tmp2[2][i] = v9 ^ v10 ^ v11 ^ buf2[state[3][i]]; uint8_t v12 = buf2[state[0][i]]; uint8_t v13 = buf3[state[1][i]]; uint8_t v14 = buf4[state[2][i]]; tmp2[3][i] = v14 ^ buf1[state[3][i]] ^ v12 ^ v13; } for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[i][j] = tmp2[i][j]; } } return 0;}int addRoundKey_self(uint8_t (*state)[4], const uint32_t *key){ uint8_t k[4][4]; /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { k[i][j] = (uint8_t) BYTE(key[j], i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */ state[i][j] ^= k[i][j]; } } return 0;}int aesDecrypt_kctf(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len){ AesKey aesKey; uint8_t *pos = pt; const uint32_t *ek = (uint32_t *)aes_key_ek; const uint32_t *rk = aesKey.dK; //解密密钥指针 uint8_t actualKey[16] = {0}; uint8_t state[4][4] = {0}; uint8_t xorKey[17] = { 0xA0, 0x81, 0x3E, 0x4D, 0x5B, 0x6A, 0xD4, 0xC2, 0x7B, 0xA5, 0x46, 0x20, 0x82, 0xB5, 0x1C, 0xEF}; uint8_t xorKey2[17] = { 0x43, 0x51, 0x8B, 0xC3, 0x5E, 0x57, 0xCC, 0x04, 0xF0, 0xBC, 0x43, 0x85, 0x1E, 0x9C, 0xBB, 0x51}; uint8_t xorKey3[17] = { 0xED, 0x59, 0x82, 0xE8, 0x1C, 0x04, 0xAC, 0x0B, 0xE4, 0xBA, 0xC2, 0xBA, 0x53, 0x27, 0xF0, 0x1B}; uint8_t xorKey4[17] = { 0xA1, 0x35, 0x01, 0xDF, 0xBB, 0x6F, 0xD2, 0x83, 0xEB, 0x49, 0xB5, 0x4A, 0xF7, 0xE5, 0x40, 0x4B}; if (NULL == key || NULL == ct || NULL == pt) { printf("param err.\n"); return -1; } if (keyLen > 16) { printf("keyLen must be 16.\n"); return -1; } if (len % BLOCKSIZE) { printf("inLen is invalid.\n"); return -1; } memcpy(actualKey, key, keyLen); keyExpansion(actualKey, 16, &aesKey); //密钥扩展,同加密 for (int i = 0; i < len; i += BLOCKSIZE) { loadStateArray(state, ct); addRoundKey_self(state, &ek[40]); rk = &ek[36]; for (int j = 1; j < 10; ++j) { invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒 invShiftRows(state); // 逆行移位 addRoundKey_self(state, rk); // 轮密钥加,同加密 rk -= 4; invMixColumns(state); // 逆列混合 } invSubBytes(state); // 逆字节替换 invShiftRows(state); // 逆行移位 addRoundKey_self(state, rk); // 轮密钥加,同加密 storeStateArray(state, pos); // 保存明文数据 if (i==0) { for (int j=0; j<16; j++) { pos[j] ^= xorKey[j]; } }else if (i== 16) { for (int j=0; j<16; j++) { pos[j] ^= xorKey2[j]; } } else if (i== 32) { for (int j=0; j<16; j++) { pos[j] ^= xorKey3[j]; } } else if (i== 48) { for (int j=0; j<16; j++) { pos[j] ^= xorKey4[j]; } } pos += BLOCKSIZE; // 输出数据内存指针移位分组长度 ct += BLOCKSIZE; // 输入数据内存指针移位分组长度 rk = aesKey.dK; // 恢复rk指针到秘钥初始位置 } return 0;}int aesEncrypt_kctf(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len){ AesKey aesKey; uint8_t *pos = pt; const uint32_t *ek = (uint32_t *)aes_key_ek; const uint32_t *rk = aesKey.dK; //解密密钥指针 uint8_t actualKey[16] = {0}; uint8_t state[4][4] = {0}; uint8_t xorKey[17] = { 0xA0, 0x81, 0x3E, 0x4D, 0x5B, 0x6A, 0xD4, 0xC2, 0x7B, 0xA5, 0x46, 0x20, 0x82, 0xB5, 0x1C, 0xEF}; uint8_t xorKey2[17] = { 0x43, 0x51, 0x8B, 0xC3, 0x5E, 0x57, 0xCC, 0x04, 0xF0, 0xBC, 0x43, 0x85, 0x1E, 0x9C, 0xBB, 0x51}; uint8_t xorKey3[17] = { 0xED, 0x59, 0x82, 0xE8, 0x1C, 0x04, 0xAC, 0x0B, 0xE4, 0xBA, 0xC2, 0xBA, 0x53, 0x27, 0xF0, 0x1B}; uint8_t xorKey4[17] = { 0xA1, 0x35, 0x01, 0xDF, 0xBB, 0x6F, 0xD2, 0x83, 0xEB, 0x49, 0xB5, 0x4A, 0xF7, 0xE5, 0x40, 0x4B}; if (NULL == key || NULL == ct || NULL == pt) { printf("param err.\n"); return -1; } if (keyLen > 16) { printf("keyLen must be 16.\n"); return -1; } if (len % BLOCKSIZE) { printf("inLen is invalid.\n"); return -1; } memcpy(actualKey, key, keyLen); keyExpansion(actualKey, 16, &aesKey); //密钥扩展,同加密 for (int i = 0; i < len; i += BLOCKSIZE) { uint8_t new_ct[17] = {0}; if (i==0) { for (int j=0; j<16; j++) { new_ct[j] = ct[j] ^ xorKey[j]; } }else if (i== 16) { for (int j=0; j<16; j++) { new_ct[j] = ct[j] ^ xorKey2[j]; } } else if (i== 32) { for (int j=0; j<16; j++) { new_ct[j] = ct[j] ^ xorKey3[j]; } } else if (i== 48) { for (int j=0; j<16; j++) { new_ct[j] = ct[j] ^ xorKey4[j]; } } // 把16字节的密文转换为4x4状态矩阵来进行处理 loadStateArray(state, new_ct); // 轮密钥加,同加密 addRoundKey_self(state, ek); rk = &ek[4]; for (int j = 1; j < 10; ++j) { subBytes(state); // 逆字节替换,这两步顺序可以颠倒 shiftRows(state); // 逆行移位 mixColumns(state); // 逆列混合 addRoundKey_self(state, rk); // 轮密钥加,同加密 rk += 4; } subBytes(state); // 逆字节替换 shiftRows(state); // 逆行移位 // 此处没有逆列混合 addRoundKey_self(state, rk); // 轮密钥加,同加密 storeStateArray(state, pos); // 保存明文数据 pos += BLOCKSIZE; // 输出数据内存指针移位分组长度 ct += BLOCKSIZE; // 输入数据内存指针移位分组长度 } return 0;}constexpr char base64_table[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789+/";int get_by_table(char a) { for (int i=0;i<64;i++) { if (a == base64_table[i]) return i; } return -1;}// Base64 编码int base64_encode(const uint8_t* input, int in_len, char* output) { int out_len = 0; for (int i = 0; i < in_len; i += 3) { uint32_t buf = 0; int remain = in_len - i; buf |= input[i]; if (remain > 1) buf |= input[i + 1] << 8; if (remain > 2) buf |= input[i + 2] << 16; if (remain > 2) { output[out_len++] = base64_table[(buf) & 0x3F]; output[out_len++] = base64_table[(buf >> 6) & 0x3F]; output[out_len++] = base64_table[(buf >> 12) & 0x3F]; output[out_len++] = base64_table[(buf >> 18) & 0x3F]; } else { output[out_len++] = base64_table[(buf >> 0) & 0x3F]; output[out_len++] = (remain > 1) ? base64_table[(buf >> 6) & 0x3F]: base64_table[((buf >> 6) & 0x3) << 4]; output[out_len++] = (remain > 1) ? base64_table[((buf >> 12) & 0xF)<<2] : '='; output[out_len++] = '='; } } output[out_len] = '\0'; return out_len;}// Base64 解码int base64_decode(const char* input, uint8_t* output) { int in_len = 0; while (input[in_len] && input[in_len] != '\n') in_len++; int out_len = 0; for (int i = 0; i < in_len; i += 4) { int n[4]; for (int j = 0; j < 4; j++) { if (input[i + j] == '=') n[j] = -1; else n[j] = get_by_table(input[i + j]); } uint32_t buf = 0; buf |= (n[0] & 0x3F); buf |= (n[1] & 0x3F) << 6; if (n[2] >= 0) buf |= (n[2] & 0x3F) << 12; if (n[3] >= 0) buf |= (n[3] & 0x3F) << 18; if (n[2] >= 0 && n[3] >= 0) { output[out_len++] = (buf) & 0xFF; output[out_len++] = (buf >> 8) & 0xFF; output[out_len++] = (buf >> 16) & 0xFF; } else if (n[2] >= 0) { output[out_len++] = (buf) & 0xFF; output[out_len++] = ((buf & 0xFFF) + ((buf >> 2) & 0xF000)) >> 8; } else{ output[out_len++] = ((buf) & 0x3F) + ((buf >> 4) & 0xC0); } } return out_len;}int run() { uint8_t aes_key[] = "ThisEncAndDecKey"; uint8_t aes_pt[0x100] = {0}; uint8_t aes_test_enc[0x100] = {0}; const char test_base64[256] = "M3JIlKQv/ztrqDQc94MSRI=="; char test_base64_enc[0x100] = {0}; uint8_t test_base64_dec[0x100] = {0}; base64_decode(test_base64, test_base64_dec); base64_encode(test_base64_dec, 16, test_base64_enc); for (int i = 0; i < 16; ++i) { if (test_base64_enc[i] != test_base64[i]) { printf("Error! index: 0x%02x\n", test_base64_enc[i]); } } aesDecrypt_kctf(aes_key, 16, test_base64_dec, aes_pt, 16); printf("%s\n", aes_pt); aesEncrypt_kctf(aes_key, 16, aes_pt, aes_test_enc, 16); for (int i = 0; i < 16; ++i) { if (aes_test_enc[i] != test_base64_dec[i]) { printf("Error! index: 0x%02x\n", i); } } uint8_t flag[17] = "KCTF"; uint8_t enc_flag_aes1[0x100] = {0}; char enc_flag_str1[0x100] = {0}; aesEncrypt_kctf(aes_key, 16, flag, enc_flag_aes1, 16); base64_encode(enc_flag_aes1, 16, enc_flag_str1); printf("%s\n", enc_flag_str1); uint8_t enc_flag_aes2[0x100] = {0}; char enc_flag_str2[0x100] = {0}; aesEncrypt_kctf(aes_key, 16, (uint8_t*)enc_flag_str1, enc_flag_aes2, 32); base64_encode(enc_flag_aes2, 32, enc_flag_str2); printf("%s\n", enc_flag_str2); uint8_t enc_flag_aes3[0x100] = {0}; char enc_flag_str3[0x100] = {0}; aesEncrypt_kctf(aes_key, 16, (uint8_t*)enc_flag_str2, enc_flag_aes3, 48); base64_encode(enc_flag_aes3, 48, enc_flag_str3); printf("%s\n", enc_flag_str3); uint8_t enc_flag_aes4[0x100] = {0}; char enc_flag_str4[0x100] = {0}; aesEncrypt_kctf(aes_key, 16, (uint8_t*)enc_flag_str3, enc_flag_aes4, 64); base64_encode(enc_flag_aes4, 64, enc_flag_str4); printf("%s\n", enc_flag_str4); return 0LL;}int main() { run(); return 0;}#include <iostream>#include <vector>#include <string>#include <cctype>#include <time.h>using namespace std;#include "DES-AES/AES_ECB.hpp"#include "data.h"uint32_t buf[8] = {};#define BOOL boolint invMixColumns_self(uint8_t (*state)[4]){ uint8_t tmp[4][4]; uint8_t tmp2[4][4]; for (int i = 0; i < 4; ++i ) { uint8_t v3 = buf1[state[0][i]]; uint8_t v4 = buf2[state[1][i]]; uint8_t v5 = buf3[state[2][i]]; tmp2[0][i] = v3 ^ v4 ^ v5 ^ buf4[state[3][i]]; uint8_t v6 = buf4[state[0][i]]; uint8_t v7 = buf1[state[1][i]]; uint8_t v8 = buf2[state[2][i]]; tmp2[1][i] = buf3[state[3][i]] ^ v6 ^ v7 ^ v8; uint8_t v9 = buf3[state[0][i]]; uint8_t v10 = buf4[state[1][i]]; uint8_t v11 = buf1[state[2][i]]; tmp2[2][i] = v9 ^ v10 ^ v11 ^ buf2[state[3][i]]; uint8_t v12 = buf2[state[0][i]]; uint8_t v13 = buf3[state[1][i]]; uint8_t v14 = buf4[state[2][i]]; tmp2[3][i] = v14 ^ buf1[state[3][i]] ^ v12 ^ v13; } for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[i][j] = tmp2[i][j]; } } return 0;}int addRoundKey_self(uint8_t (*state)[4], const uint32_t *key){ uint8_t k[4][4]; /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { k[i][j] = (uint8_t) BYTE(key[j], i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */ state[i][j] ^= k[i][j]; } } return 0;}int aesDecrypt_kctf(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len){ AesKey aesKey; uint8_t *pos = pt; const uint32_t *ek = (uint32_t *)aes_key_ek; const uint32_t *rk = aesKey.dK; //解密密钥指针 uint8_t actualKey[16] = {0}; uint8_t state[4][4] = {0}; uint8_t xorKey[17] = { 0xA0, 0x81, 0x3E, 0x4D, 0x5B, 0x6A, 0xD4, 0xC2, 0x7B, 0xA5, 0x46, 0x20, 0x82, 0xB5, 0x1C, 0xEF}; uint8_t xorKey2[17] = { 0x43, 0x51, 0x8B, 0xC3, 0x5E, 0x57, 0xCC, 0x04, 0xF0, 0xBC, 0x43, 0x85, 0x1E, 0x9C, 0xBB, 0x51}; uint8_t xorKey3[17] = { 0xED, 0x59, 0x82, 0xE8, 0x1C, 0x04, 0xAC, 0x0B, 0xE4, 0xBA, 0xC2, 0xBA, 0x53, 0x27, 0xF0, 0x1B}; uint8_t xorKey4[17] = { 0xA1, 0x35, 0x01, 0xDF, 0xBB, 0x6F, 0xD2, 0x83, 0xEB, 0x49, 0xB5, 0x4A, 0xF7, 0xE5, 0x40, 0x4B}; if (NULL == key || NULL == ct || NULL == pt) { printf("param err.\n"); return -1; } if (keyLen > 16) { printf("keyLen must be 16.\n"); return -1; } if (len % BLOCKSIZE) { printf("inLen is invalid.\n"); return -1; } memcpy(actualKey, key, keyLen); keyExpansion(actualKey, 16, &aesKey); //密钥扩展,同加密 for (int i = 0; i < len; i += BLOCKSIZE) { loadStateArray(state, ct); addRoundKey_self(state, &ek[40]); rk = &ek[36]; for (int j = 1; j < 10; ++j) { invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒 invShiftRows(state); // 逆行移位 addRoundKey_self(state, rk); // 轮密钥加,同加密 rk -= 4; invMixColumns(state); // 逆列混合 } invSubBytes(state); // 逆字节替换 invShiftRows(state); // 逆行移位 addRoundKey_self(state, rk); // 轮密钥加,同加密 storeStateArray(state, pos); // 保存明文数据 if (i==0) { for (int j=0; j<16; j++) { pos[j] ^= xorKey[j]; } }else if (i== 16) { for (int j=0; j<16; j++) { pos[j] ^= xorKey2[j]; } } else if (i== 32) { for (int j=0; j<16; j++) { pos[j] ^= xorKey3[j]; } } else if (i== 48) { for (int j=0; j<16; j++) { pos[j] ^= xorKey4[j]; } }[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!