Miku
4503 字
23 分钟
NepCTF WP
Realme
有两处反调试和 smc
异常反调
peb 反调
然后就能得到正确执行流
这里直接取 ksa 后的 sbox 了
#include <stdio.h>
void dec(unsigned char *sbox, unsigned char *input, unsigned int length){ int v3; char v4; char tmp; int v7; int v8;
v8 = 0; v7 = 0; for (int i = 0; i < length; ++i) { v8 = (v8 + 1) % 256; v7 = (v7 + v8 * sbox[v8]) % 256; tmp = sbox[v8]; sbox[v8] = sbox[v7]; sbox[v7] = tmp; v3 = (sbox[v7] + sbox[v8]) % 256; if (i % 2) v4 = input[i] - sbox[v3]; else v4 = input[i] + sbox[v3]; input[i] = v4; }}
int main(){ unsigned char sbox[256] = {0x35, 0xE9, 0x55, 0xD7, 0x6E, 0x5F, 0x47, 0x3D, 0xCC, 0x9D, 0xD2, 0x6B, 0xEB, 0x52, 0x97, 0x19, 0xD5, 0xC5, 0x80, 0xB, 0x64, 0x2B, 0xCD, 0xF6, 0x95, 0xB1, 0x31, 0x34, 0x31, 0x8, 0x43, 0xBE, 0x8C, 0x86, 0x16, 0x70, 0xFE, 0x36, 0x11, 0xFD, 0xA7, 0xB9, 0x55, 0xA0, 0x4E, 0x40, 0xDA, 0x8, 0x1F, 0x4B, 0xA2, 0x4C, 0x50, 0x47, 0x15, 0xCE, 0xC3, 0x8D, 0xB5, 0x0, 0xFB, 0x43, 0x7, 0x32, 0x1D, 0x5E, 0xDC, 0x4D, 0xF5, 0x19, 0x98, 0xF, 0x8D, 0xB0, 0xEC, 0x48, 0xAB, 0x92, 0x15, 0xD6, 0xDA, 0x6F, 0x1B, 0x85, 0x45, 0x4, 0x84, 0x8A, 0x5B, 0xE, 0x66, 0xB6, 0xA0, 0x1E, 0x2A, 0x6D, 0x3C, 0x8F, 0x26, 0xC7, 0x90, 0x89, 0xDC, 0x8B, 0x87, 0xE0, 0x82, 0x57, 0xCE, 0x66, 0x13, 0x4B, 0x49, 0x6A, 0x1F, 0x1A, 0x9, 0x32, 0x8E, 0x36, 0xAD, 0x65, 0x58, 0xBC, 0xD4, 0x5E, 0xD0, 0x2C, 0x68, 0xBF, 0xBD, 0xA1, 0x45, 0x17, 0x16, 0x5, 0x9A, 0x4C, 0xFC, 0xB, 0xB9, 0x49, 0xDB, 0x6F, 0x37, 0x27, 0x30, 0x51, 0x69, 0x61, 0xD5, 0x75, 0xD3, 0x74, 0xEB, 0x4F, 0x23, 0x54, 0xC, 0x1C, 0x70, 0xDE, 0xE9, 0x7F, 0x62, 0x25, 0xF4, 0x84, 0x3E, 0x2F, 0x76, 0x3, 0x7A, 0x79, 0x5F, 0xCA, 0x1, 0x7, 0x41, 0x57, 0xC4, 0x97, 0x4, 0x33, 0x6C, 0x42, 0x4E, 0x38, 0xE, 0xE7, 0x93, 0xE2, 0x64, 0x3F, 0xB7, 0x5C, 0x5D, 0xE5, 0x59, 0x8C, 0x6D, 0xED, 0x34, 0x85, 0xDF, 0x62, 0x91, 0x9, 0x94, 0xB3, 0x5, 0x2E, 0x18, 0xD8, 0xBF, 0x7E, 0xAC, 0xAE, 0x9E, 0xD6, 0xC1, 0x3B, 0x54, 0x72, 0x22, 0x5C, 0xE7, 0xD0, 0x6B, 0x25, 0xFE, 0xFF, 0xFB, 0x3B, 0x2D, 0x7C, 0x65, 0x5A, 0xCD, 0xF0, 0xBD, 0x67, 0x74, 0x17, 0x2, 0x42, 0x2C, 0x2E, 0x5A, 0xA7, 0xD1, 0x73, 0x94, 0xAF, 0x89, 0x6}; unsigned char enc[] = {0x50, 0x59, 0xA2, 0x94, 0x2E, 0x8E, 0x5C, 0x95, 0x79, 0x16, 0xE5, 0x36, 0x60, 0xC7, 0xE8, 0x6, 0x33, 0x78, 0xF0, 0xD0, 0x36, 0xC8, 0x73, 0x1B, 0x65, 0x40, 0xB5, 0xD4, 0xE8, 0x9C, 0x65, 0xF4, 0xBA, 0x62, 0xD0}; dec(sbox, enc, sizeof(enc)); printf("%s\n", enc);}//NepCTF{Y0u_FiN1sH_Th1s_E3sy_Smc!!!}
Crackme
主程序逻辑很清晰,获取 name 的 sign 然后 再获取 name 后面加 Showmaker11 的 sign
然后把第一个 sign 做 key,第二个 sign 做密文进行 aes 加密然后验证
sign 和 aes 均来自 libcrypt.dll
dll 被混淆了,还进行了字符串加密
通过解密后的字符串得到此题 aes128 的源码
并且发现被内联的函数都被放在了一起
最终发现只在mixColumns
函数中加了一个异或
#include <stdint.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <windows.h>#include <stdint.h>
typedef struct{ uint32_t eK[44], dK[44]; // encKey, decKey int Nr; // 10 rounds} AesKey;
#define BLOCKSIZE 16 // AES-128分组长度为16字节
// uint8_t y[4] -> uint32_t x#define LOAD32H(x, y) \ do \ { \ (x) = ((uint32_t)((y)[0] & 0xff) << 24) | ((uint32_t)((y)[1] & 0xff) << 16) | \ ((uint32_t)((y)[2] & 0xff) << 8) | ((uint32_t)((y)[3] & 0xff)); \ } while (0)
// uint32_t x -> uint8_t y[4]#define STORE32H(x, y) \ do \ { \ (y)[0] = (uint8_t)(((x) >> 24) & 0xff); \ (y)[1] = (uint8_t)(((x) >> 16) & 0xff); \ (y)[2] = (uint8_t)(((x) >> 8) & 0xff); \ (y)[3] = (uint8_t)((x) & 0xff); \ } while (0)
// 从uint32_t x中提取从低位开始的第n个字节#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)
// 密钥扩展中的SubWord(RotWord(temp),字节替换然后循环左移1位#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \ ((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))
// uint32_t x循环左移n位#define ROF32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))// uint32_t x循环右移n位#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
// AES-128轮常量,无符号长整型static const uint32_t rcon[10] = { 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL};// S盒unsigned char S[256] = { 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};
// 逆S盒unsigned char inv_S[256] = { 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D};
/* copy in[16] to state[4][4] */int loadStateArray(uint8_t (*state)[4], const uint8_t *in){ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[j][i] = *in++; } } return 0;}
/* copy state[4][4] to out[16] */int storeStateArray(uint8_t (*state)[4], uint8_t *out){ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { *out++ = state[j][i]; } } return 0;}
// 密钥扩展,只接受16字初始密钥int keyExpansion(const uint8_t *key, uint32_t keyLen, AesKey *aesKey){
if (NULL == key || NULL == aesKey) { printf("keyExpansion param is NULL\n"); return -1; }
if (keyLen != 16) { printf("keyExpansion keyLen = %d, Not support.\n", keyLen); return -1; }
uint32_t *w = aesKey->eK; // 加密密钥 uint32_t *v = aesKey->dK; // 解密密钥
// 扩展密钥长度44=4*(10+1)个字,原始密钥128位,4个32位字,Nb*(Nr+1)
/* W[0-3],前4个字为原始密钥 */ for (int i = 0; i < 4; ++i) { LOAD32H(w[i], key + 4 * i); }
/* W[4-43] */ // temp=w[i-1];tmp=SubWord(RotWord(temp))xor Rcon[i/4] xor w[i-Nk] for (int i = 0; i < 10; ++i) { w[4] = w[0] ^ MIX(w[3]) ^ rcon[i]; w[5] = w[1] ^ w[4]; w[6] = w[2] ^ w[5]; w[7] = w[3] ^ w[6]; w += 4; }
w = aesKey->eK + 44 - 4; // 解密密钥矩阵为加密密钥矩阵的倒序,方便使用,把ek的11个矩阵倒序排列分配给dk作为解密密钥 // 即dk[0-3]=ek[41-44], dk[4-7]=ek[37-40]... dk[41-44]=ek[0-3] for (int j = 0; j < 11; ++j) { for (int i = 0; i < 4; ++i) { v[i] = w[i]; } w -= 4; v += 4; }
return 0;}
// 轮密钥加int addRoundKey(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], 3 - i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */ state[i][j] ^= k[i][j]; } }
return 0;}
// 字节替换int subBytes(uint8_t (*state)[4]){ /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[i][j] = S[state[i][j]]; // 直接使用原始字节作为S盒数据下标 } }
return 0;}
// 逆字节替换int invSubBytes(uint8_t (*state)[4]){ /* i: row, j: col */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[i][j] = inv_S[state[i][j]]; } } return 0;}
// 行移位int shiftRows(uint8_t (*state)[4]){ uint32_t block[4] = {0};
/* i: row */ for (int i = 0; i < 4; ++i) { // 便于行循环移位,先把一行4字节拼成uint_32结构,移位后再转成独立的4个字节uint8_t LOAD32H(block[i], state[i]); block[i] = ROF32(block[i], 8 * i); // block[i]循环左移8*i位,如第0行左移0位 STORE32H(block[i], state[i]); } return 0;}
// 逆行移位int invShiftRows(uint8_t (*state)[4]){ uint32_t block[4] = {0};
/* i: row */ for (int i = 0; i < 4; ++i) { LOAD32H(block[i], state[i]); block[i] = ROR32(block[i], 8 * i); STORE32H(block[i], state[i]); }
return 0;}
/* Galois Field (256) Multiplication of two Bytes */// 两字节的伽罗华域乘法运算uint8_t GMul(uint8_t u, uint8_t v){ uint8_t p = 0;
for (int i = 0; i < 8; ++i) { if (u & 0x01) { p ^= v; }
int flag = (v & 0x80); v <<= 1; if (flag) { v ^= 0x1B; }
u >>= 1; }
return p;}
// 列混合int mixColumns(uint8_t (*state)[4]){ uint8_t tmp[4][4]; uint8_t M[4][4] = {{0x02, 0x03, 0x01, 0x01}, {0x01, 0x02, 0x03, 0x01}, {0x01, 0x01, 0x02, 0x03}, {0x03, 0x01, 0x01, 0x02}};
/* copy state[4][4] to tmp[4][4] */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { tmp[i][j] = state[i][j]; } }
for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { // 伽罗华域加法和乘法 // state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j]) // ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]); state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j]) ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]) ^ 0x55; } }
return 0;}
// 逆列混合int invMixColumns(uint8_t (*state)[4]){ uint8_t tmp[4][4]; uint8_t M[4][4] = {{0x0E, 0x0B, 0x0D, 0x09}, {0x09, 0x0E, 0x0B, 0x0D}, {0x0D, 0x09, 0x0E, 0x0B}, {0x0B, 0x0D, 0x09, 0x0E}}; // 使用列混合矩阵的逆矩阵
/* copy state[4][4] to tmp[4][4] */ for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { tmp[i][j] = state[i][j]; } }
for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j]) ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]) ^ 0x55; } }
return 0;}
// AES-128加密接口,输入key应为16字节长度,输入长度应该是16字节整倍数,// 这样输出长度与输入长度相同,函数调用外部为输出数据分配内存int aesEncrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *pt, uint8_t *ct, uint32_t len){ AesKey aesKey; uint8_t *pos = ct; const uint32_t *rk = aesKey.eK; // 加密密钥指针 uint8_t out[BLOCKSIZE] = {0}; uint8_t actualKey[16] = {0}; uint8_t state[4][4] = {0};
if (NULL == key || NULL == pt || NULL == ct) { 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); // 密钥扩展
// 使用ECB模式循环加密多个分组长度的数据 for (int i = 0; i < len; i += BLOCKSIZE) { // 把16字节的明文转换为4x4状态矩阵来进行处理 loadStateArray(state, pt); // 轮密钥加 addRoundKey(state, rk);
for (int j = 1; j < 10; ++j) { rk += 4; subBytes(state); // 字节替换 shiftRows(state); // 行移位 mixColumns(state); // 列混合 addRoundKey(state, rk); // 轮密钥加 }
subBytes(state); // 字节替换 shiftRows(state); // 行移位 // 此处不进行列混合 addRoundKey(state, rk + 4); // 轮密钥加
// 把4x4状态矩阵转换为uint8_t一维数组输出保存 storeStateArray(state, pos);
pos += BLOCKSIZE; // 加密数据内存指针移动到下一个分组 pt += BLOCKSIZE; // 明文数据指针移动到下一个分组 rk = aesKey.eK; // 恢复rk指针到秘钥初始位置 } return 0;}
// AES128解密, 参数要求同加密int aesDecrypt(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 *rk = aesKey.dK; // 解密密钥指针 uint8_t out[BLOCKSIZE] = {0}; uint8_t actualKey[16] = {0}; uint8_t state[4][4] = {0};
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) { // 把16字节的密文转换为4x4状态矩阵来进行处理 loadStateArray(state, ct); // 轮密钥加,同加密 addRoundKey(state, rk);
for (int j = 1; j < 10; ++j) { rk += 4; invShiftRows(state); // 逆行移位 invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒 addRoundKey(state, rk); // 轮密钥加,同加密 invMixColumns(state); // 逆列混合 }
invShiftRows(state); // 逆行移位 invSubBytes(state); // 逆字节替换 // 此处没有逆列混合 addRoundKey(state, rk + 4); // 轮密钥加,同加密
storeStateArray(state, pos); // 保存明文数据 pos += BLOCKSIZE; // 输出数据内存指针移位分组长度 ct += BLOCKSIZE; // 输入数据内存指针移位分组长度 rk = aesKey.dK; // 恢复rk指针到秘钥初始位置 } return 0;}
void sign(unsigned char *in, int len, unsigned char *out){ HINSTANCE hAes = LoadLibrary("libcrypto.dll"); auto encrypt = (void (*)(unsigned char *, int, unsigned char *))GetProcAddress(hAes, "sign"); if (!encrypt) { printf("GetProcAddress failed\n"); }
encrypt(in, len, out);}
int main(){ uint8_t key[16] = {0x24, 0x25, 0x8A, 0x5B, 0x6A, 0x20, 0x62, 0x5D, 0xD2, 0x71, 0x64, 0x32, 0xFD, 0xE7, 0x5E, 0xC4}; uint8_t pt[16] = {0xca, 0x02, 0xa1, 0xd4, 0x73, 0x4b, 0x36, 0x8f, 0x8a, 0x1d, 0x78, 0x5e, 0xdf, 0x13, 0xa4, 0xe2}; uint8_t ct[16] = {0x22, 0xEB, 0xB5, 0x40, 0x91, 0x62, 0x9C, 0xF7, 0xE2, 0x13, 0xFF, 0xA8, 0x8C, 0x54, 0xD9, 0x80}; // 外部申请输出数据内存,用于加密后的数据 uint8_t plain[16] = {0}; unsigned char out[16] = {0}; int len = 0; char raw[][100] = {"lixShowmaker11", "zhangqinShowmaker11", "yingchenShowmaker11", "esShowmaker11", "wangguiyingShowmaker11", "zhangqiangShowmaker11", "liuxiuyingShowmaker11", "iqShowmaker11", "lilizhangShowmaker11", "liuhuanShowmaker11", "liuylShowmaker11", "zhangliangShowmaker11", "wangfShowmaker11", "lixzShowmaker11", "liliShowmaker11", "lixiuhuaShowmaker11", "tliShowmaker11", "rmShowmaker11", "vxShowmaker11", "liujShowmaker11", "xiulanchenShowmaker11", "mhuangShowmaker11", "shuhuawangShowmaker11", "jianguoliShowmaker11", "kailiuShowmaker11", "zhiqiangzhangShowmaker11", "zgShowmaker11", "bliShowmaker11", "lwShowmaker11", "ioShowmaker11", "zbShowmaker11", "litShowmaker11", "chenqiangShowmaker11", "zhangguirongShowmaker11", "xiangliShowmaker11", "taowangShowmaker11", "manageShowmaker11", "mxShowmaker11", "lihShowmaker11", "loShowmaker11", "xmliShowmaker11", "jnShowmaker11", "hmzhangShowmaker11", "zhaoweiShowmaker11", "yumeiwangShowmaker11", "shuyingliShowmaker11", "slzhangShowmaker11", "binzhangShowmaker11", "zhangsShowmaker11", "keShowmaker11", "jingzhangShowmaker11", "aiShowmaker11", "pliuShowmaker11", "cmzhangShowmaker11", "guirongliShowmaker11", "mwangShowmaker11", "lixiangShowmaker11", "cliShowmaker11", "gfzhangShowmaker11", "alShowmaker11", "yangliShowmaker11", "lzhangShowmaker11", "anShowmaker11", "xlyangShowmaker11", "zhangyzShowmaker11", "waShowmaker11", "ldmShowmaker11", "zhanghmShowmaker11", "osShowmaker11", "lmaShowmaker11", "hoShowmaker11", "gwShowmaker11", "qiangchenShowmaker11", "wangguihuaShowmaker11", "jfwangShowmaker11", "gfliShowmaker11", "xywangShowmaker11", "drShowmaker11", "pgShowmaker11", "hxliShowmaker11", "yyangShowmaker11", "hsShowmaker11", "ymliuShowmaker11", "eeShowmaker11", "wjfShowmaker11", "fangchenShowmaker11", "chenxShowmaker11", "yxzShowmaker11", "chenhongShowmaker11", "adminrootShowmaker11", "qiangwangShowmaker11", "linliShowmaker11", "wangxiufangShowmaker11", "csShowmaker11", "liuyulanShowmaker11", "guizhenliuShowmaker11", "ryShowmaker11", "boyangShowmaker11", "haiyanliShowmaker11", "guilanliShowmaker11", "inkeyShowmaker11"}; char raw1[][100] = {"lix", "zhangqin", "yingchen", "es", "wangguiying", "zhangqiang", "liuxiuying", "iq", "lilizhang", "liuhuan", "liuyl", "zhangliang", "wangf", "lixz", "lili", "lixiuhua", "tli", "rm", "vx", "liuj", "xiulanchen", "mhuang", "shuhuawang", "jianguoli", "kailiu", "zhiqiangzhang", "zg", "bli", "lw", "io", "zb", "lit", "chenqiang", "zhangguirong", "xiangli", "taowang", "manage", "mx", "lih", "lo", "xmli", "jn", "hmzhang", "zhaowei", "yumeiwang", "shuyingli", "slzhang", "binzhang", "zhangs", "ke", "jingzhang", "ai", "pliu", "cmzhang", "guirongli", "mwang", "lixiang", "cli", "gfzhang", "al", "yangli", "lzhang", "an", "xlyang", "zhangyz", "wa", "ldm", "zhanghm", "os", "lma", "ho", "gw", "qiangchen", "wangguihua", "jfwang", "gfli", "xywang", "dr", "pg", "hxli", "yyang", "hs", "ymliu", "ee", "wjf", "fangchen", "chenx", "yxz", "chenhong", "adminroot", "qiangwang", "linli", "wangxiufang", "cs", "liuyulan", "guizhenliu", "ry", "boyang", "haiyanli", "guilanli"}; for (int i = 0; i < 100; i++) { len = strlen(raw[i]); sign(raw[i], len, out); sign(raw1[i], strlen(raw1[i]), key); memset(plain, 0, 16); aesDecrypt(key, 16, out, plain, 16); for (int j = 0; j < 16; j++) { printf("%02x", plain[j]); } printf("\n"); }
return 0;}//NepCTF{8e31cd81-4f34-8d31-f2eb-fa6a2d2319cb}
QRS
绝大多数字符串均被加密
解密函数
直接在解密函数处下断点,找到返回的 failed 后再向上回溯即可
最终在 0x14002B2D0 找到处理函数
在 0x14000FCE0 找到加密函数
变种 xtea
#include <stdio.h>#include <stdint.h>
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]){ unsigned int i; uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x68547369; for (i = 0; i < num_rounds; i++) { v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); sum += delta; v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); } v[0] = v0; v[1] = v1;}
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]){ unsigned int i; uint32_t v0 = v[0], v1 = v[1], delta = 0x68547369, sum = delta * num_rounds; for (i = 0; i < num_rounds; i++) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum -= delta; v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); } v[0] = v0; v[1] = v1;}
int main(){ uint32_t k[4] = {0x1234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210}; uint32_t enc[8] = {0x83EA621, 0xC745973C, 0xE3B77AE8, 0xCDEE8146, 0x7DC86B96, 0x6B8C9D3B, 0x79B14342, 0x2ECF0F0D}; unsigned int r = 48; for (int i = 0; i < 4; i++) { decipher(r, &enc[2 * i], k); } for (int i = 0; i < 32; i++) { printf("%c", *((char *)enc + i)); } return 0;}//a4747f82be106d3f8c4d747c744d7ee5
SpeedMino-Warlock
根据提示找到ethangreen-dev/lovely-injector at v0.8.0
发现 version.dll 其实是一个 mod 的注入器
这是源码中应用 patch 的部分
通过字符串直接定位到关键位置
在 dll 的这个函数下断点
在加载 main.lua 的时候跳转到这个位置
可以看到刚才源代码中的 hash
导出被修改的源码
找到被修改的部分
直接加载编译后的 lua 脚本进行验证
尝试了一堆反编译工具,最终找到weaweawe01/luajit_decompile at 1.0可以进行反编译
slot1 = function(slot0, slot1) slot2 = 0 + slot0 * 384 + slot1
while true do slot3 = 2
while true do if slot2 < slot3 * slot3 and (slot2 - 1) % 5 ~= 0 then return slot2 end
if slot2 % slot3 == 0 then break end
slot3 = slot3 + 1 end
slot2 = slot2 + 1 end
return 383end
slot2 = { 36506, 88774, 438822, 666001, 109051, 2124674, 2767204, 2610244, 3272321, 8662979, 5131975, 1034228, 6712315, 12760199, 20058866, 6854477, 6126314, 3871829, 13815442, 15034520, 38884726, 10742135, 26199509, 45230422, 46725610, 7627278, 21052590, 68432885, 48719842, 94974872, 55215219, 113201828, 86070272, 153897959, 136736481, 88243015, 108020790, 71691707, 157143593, 22399026, 41588356, 220309217, 86844145, 129645965, 240111657, 257783827, 92900284, 52969902, 27539801, 275411785, 364406385, 253200306, 389008554, 359199685, 337450915}
return (function (slot0) slot1 = 1 slot2 = { 359, 383 }
if #uv0 ~= #slot0 then return false end
for slot6 = 1, #slot0 do if (64 + slot0[slot6])^5 % (slot2[slot6] * slot2[slot6 + 1]) ~= uv0[slot6] then return false end
slot2[slot6 + 2] = uv1(slot6, slot0[slot6]) end
return trueend)(slot0)
不可逆的验证 那我们直接爆破
local uv0 = { 36506, 88774, 438822, 666001, 109051, 2124674, 2767204, 2610244, 3272321, 8662979, 5131975, 1034228, 6712315, 12760199, 20058866, 6854477, 6126314, 3871829, 13815442, 15034520, 38884726, 10742135, 26199509, 45230422, 46725610, 7627278, 21052590, 68432885, 48719842, 94974872, 55215219, 113201828, 86070272, 153897959, 136736481, 88243015, 108020790, 71691707, 157143593, 22399026, 41588356, 220309217, 86844145, 129645965, 240111657, 257783827, 92900284, 52969902, 27539801, 275411785, 364406385, 253200306, 389008554, 359199685, 337450915}
local function uv1(i, val) local slot2 = i * 384 + val while true do local slot3 = 2 while true do if slot2 < slot3 * slot3 and (slot2 - 1) % 5 ~= 0 then return slot2 end if slot2 % slot3 == 0 then break end slot3 = slot3 + 1 end slot2 = slot2 + 1 endend
local function solve() local slot2 = {359, 383} local slot0 = {}
for i = 1, #uv0 do local found = false for x = 0, 383 do local val = (64 + x)^5 % (slot2[i] * slot2[i+1]) if val == uv0[i] then slot0[i] = x slot2[i + 2] = uv1(i, x) found = true break end end if not found then error("Failed to find solution at index " .. i) end end
return slot0end
local result = solve()for i, v in ipairs(result) do io.write(v) if i < #result then io.write(", ") endendio.write("\n")
运行得到密文
230, 210, 245, 223, 98, 97, 151, 114, 63, 90, 139, 78, 204, 42, 227, 68, 202, 55, 242, 163, 103, 108, 231, 197, 142, 255, 180, 98, 123, 0, 184, 145, 164, 171, 247, 41, 92, 21, 89, 100, 22, 125, 87, 98, 10, 142, 70, 107, 101, 142, 60, 65, 168, 214, 243
再根据 main.lua 的 rc4 解密
#include <stdio.h>#include <string.h>unsigned char sbox[256] = {0};void swap(unsigned char *a, unsigned char *b){ unsigned char tmp = *a; *a = *b; *b = tmp;}void init_sbox(unsigned char key[]){ for (unsigned int i = 0; i < 256; i++) sbox[i] = i; unsigned int keyLen = strlen((char *)key); unsigned char Ttable[256] = {0}; for (int i = 0; i < 256; i++) Ttable[i] = key[i % keyLen]; for (int j = 0, i = 0; i < 256; i++) { j = (j + sbox[i] + key[i % keyLen]) % 256; swap(&sbox[i], &sbox[j]); }}void RC4(unsigned char data[]){ unsigned char k, i = 0, j = 0, t; unsigned int dataLen = 55; for (unsigned h = 0; h < dataLen; h++) { i = (i + 1) % 256; j = (j + sbox[i]) % 256; swap(&sbox[i], &sbox[j]); t = (sbox[i] + sbox[j]) % 256; k = sbox[t]; data[h] = (data[h] - k + 256) % 256; }}
int main(){ unsigned char key[] = {"Speedmino Created By MrZ and modified by zxc"}; unsigned char enc[] = {230, 210, 245, 223, 98, 97, 151, 114, 63, 90, 139, 78, 204, 42, 227, 68, 202, 55, 242, 163, 103, 108, 231, 197, 142, 255, 180, 98, 123, 0, 184, 145, 164, 171, 247, 41, 92, 21, 89, 100, 22, 125, 87, 98, 10, 142, 70, 107, 101, 142, 60, 65, 168, 214, 243}; init_sbox(key); RC4(enc); printf("%s", enc);
return 0;}
//NepCTF{Y0u_c4n_M0dDing_LOVE2D_g@mE_By_l0vely_iNjector!}