原创 2022 KCTF 看雪学苑
出题团队简介
赛题设计思路
题目介绍
算法
使用AES算法和修改的Base64算法(以下简称Base64)验证用户名和序列号是否正确。
(1) 算法参考KCTF2021春季赛-千里寻根实现。
(2) 验证算法部分是一段完整的shellcode代码,将其分片成3部分后,做了混淆和校验。
(3) 如果对输入的序列号解密失败,或者解密结果与用户名不匹配,则提示失败。
使用CRC32算法保护算法部分的内存完整性
(1) 在解密shellcode中,随机插入5份CRC32检查,计算出当前EIP前后一段内存范围内的校验值。
(2) 使用5份校验值分别对Base64映射表的一部分做异或,得到加密后的Base64映射表。
(3) CrackMe一开始使用该加密后的Base64映射表,只有运行时得到正确的CRC32值,才可以恢复正确的Base64映射表。
(4) 该校验主要用于反调试,在校验范围内的内存如果有被修改(比如下了int3断点),那么Base64映射表的结果将是错误的。
赛题解析
本赛题解析由看雪论坛专家 ThTsOd 给出:
搜索字符串,发现有一些Failed和一个Succeed,在开头下断点。
发现41ADCC是个比较长度0x10,得到加密数据的位置在0x8995f1
对0x8995f1下硬件断点,发现断在奇怪的地方。
// KenGen.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
//#pragma pack(1)
//typedef struct tagShareData
//{
// uint64_t g_lpCtf;
// uint8_t g_szName[17];
// uint8_t g_szSerial[32];
// uint8_t g_szBase64Table[0x100];
// uint64_t g_qwDecSuccess;
// uint64_t g_qwStackSize;
// uint8_t g_Stack[0x6008];
// uint8_t g_szDec[16];
// uint8_t tmp[24];
//}SHAREDATA, * PSHAREDATA;
//
//
//SHAREDATA g_sd;
uint8_t g_sd[0x6179] = { 0 };
#define g_sd_g_lpCtf (*(uint64_t*)(g_sd))
#define g_sd_g_szName ((uint8_t*)(g_sd+8))
#define g_sd_g_szSerial ((uint8_t*)(g_sd+25))
#define g_sd_g_szBase64Table ((uint8_t*)(g_sd+57))
#define g_sd_g_qwDecSuccess (*(uint64_t*)(g_sd+313))
#define g_sd_g_qwStackSize (*(uint64_t*)(g_sd+321))
#define g_sd_g_Stack ((uint8_t*)(g_sd+329))
#define g_sd_g_szDec ((uint8_t*)(g_sd+24913))
#define g_sd_tmp ((uint8_t*)(g_sd+24929))
#define LEFTMOVE(X,D) (( (X) >> (16 - ((D) ) )) | ( (X) << ( (D) )))
#define RIGHTMOVE(X,D) (( (X) << (16 - ((D) ) )) | ( (X) >> ( (D) )))
#define XOR_NUMBER 0x8160C68FF6C4875E^0x090A0B004654434B
int hex2string(unsigned char* hex, int size, char* buf)
{
int i = 0, j = 0;
unsigned int val = 0;
for (; i < size; ++i)
{
val = hex[i] >> 4;
if (val < 10)
buf[j + 0] = '0' + val;
else
buf[j + 0] = 'A' + val - 10;
val = hex[i] & 0x0f;
if (val < 10)
buf[j + 1] = '0' + val;
else
buf[j + 1] = 'A' + val - 10;
j += 2;
}
buf[j] = 0;
return j;
}
void DecM(LPBYTE g_uhSerial, LPBYTE dec, BYTE x1, BYTE x2)
{
unsigned short int d[8] = { 0 };
for (int i = 0; i <= 7; i++)
{
d[i] = (((unsigned char*)(g_uhSerial))[(x2 ^ (x1 + 2 * i)) % 16] << 8) + ((unsigned char*)(g_uhSerial))[(x2 ^ (x1 + 2 * i + 1)) % 16];
}
unsigned short int A = d[0] ^ d[1];
unsigned short int B = d[2] + d[3];
unsigned short int C = d[4] - d[5];
//这里面的操作是取d[6] ^ d[7]二进制中1的个数赋值给D
unsigned short int D = d[6] ^ d[7];
D = (D & 0x55555555) + ((D >> 1) & 0x55555555);
D = (D & 0x33333333) + ((D >> 2) & 0x33333333);
D = (D & 0x0F0F0F0F) + ((D >> 4) & 0x0F0F0F0F);
D = (D & 0x00FF00FF) + ((D >> 8) & 0x00FF00FF);
D = (D & 0x0000FFFF) + ((D >> 16) & 0x0000FFFF);
unsigned short int S = (A & B) | ((~A) & C);
d[6] ^= S;
d[7] ^= S;
d[6] = LEFTMOVE(d[6], D);
d[7] = LEFTMOVE(d[7], D);
unsigned short int R = (unsigned short int)(((unsigned int)A * S) >> D) + 24;
d[4] += R; d[5] += R;
unsigned short int U = R ^ C;
d[2] += U; d[3] -= U;
unsigned short int V = (R & S) | (S & U) | (U & R);
d[0] ^= V; d[1] ^= V;
for (int i = 0; i <= 7; i++)
{
*(unsigned short int*)((char*)(dec) + 2 * i) = d[i];
}
}
void EncM(LPBYTE g_uhSerial, LPBYTE enc, BYTE x1, BYTE x2)
{
unsigned short int X[8] = { 0 };
for (int i = 0; i <= 7; i++)
{
X[i] = *(unsigned short int*)((char*)(g_uhSerial) + 2 * i);
}
unsigned short int d[8] = { 0 };
unsigned short int A, B, C, D, S, R, U, V;
C = X[4] - X[5];
B = X[2] + X[3];
A = X[0] ^ X[1];
//这里面的操作是取X[6] ^ X[7]二进制中1的个数赋值给D
D = X[6] ^ X[7];
D = (D & 0x55555555) + ((D >> 1) & 0x55555555);
D = (D & 0x33333333) + ((D >> 2) & 0x33333333);
D = (D & 0x0F0F0F0F) + ((D >> 4) & 0x0F0F0F0F);
D = (D & 0x00FF00FF) + ((D >> 8) & 0x00FF00FF);
D = (D & 0x0000FFFF) + ((D >> 16) & 0x0000FFFF);
S = (A & B) | ((~A) & C);
R = (unsigned short int)(((unsigned int)A * S) >> D) + 24;
U = R ^ C;
V = (R & S) | (S & U) | (U & R);
d[0] = X[0] ^ V; d[1] = X[1] ^ V;
d[2] = X[2] - U; d[3] = X[3] + U;
d[4] = X[4] - R; d[5] = X[5] - R;
d[6] = RIGHTMOVE(X[6], D) ^ S; d[7] = RIGHTMOVE(X[7], D) ^ S;
for (int i = 0; i <= 7; i++)
{
((unsigned char*)(enc))[(x2 ^ (x1 + 2 * i)) % 16] = (d[i] >> 8);
((unsigned char*)(enc))[(x2 ^ (x1 + 2 * i + 1)) % 16] = (d[i]);
}
}
bool KC_base64_encode(uint8_t* pIn, uint8_t* pOut)
{
//定义base64编码表
//0O/+KuChaNiUB1FAckL4Hot9zlWEMpm2TG5Sb6Ixdeq8YJPjygXQsRvwVZr73Dnf
unsigned char base64_table[64] = {
0x30, 0x4F, 0x2F, 0x2B, 0x4B, 0x75, 0x43, 0x68, 0x61, 0x4E, 0x69, 0x55, 0x42, 0x31, 0x46, 0x41,
0x63, 0x6B, 0x4C, 0x34, 0x48, 0x6F, 0x74, 0x39, 0x7A, 0x6C, 0x57, 0x45, 0x4D, 0x70, 0x6D, 0x32,
0x54, 0x47, 0x35, 0x53, 0x62, 0x36, 0x49, 0x78, 0x64, 0x65, 0x71, 0x38, 0x59, 0x4A, 0x50, 0x6A,
0x79, 0x67, 0x58, 0x51, 0x73, 0x52, 0x76, 0x77, 0x56, 0x5A, 0x72, 0x37, 0x33, 0x44, 0x6E, 0x66
};
uint32_t nRound = 24 / 3;
uint32_t nEnc = 0;
uint32_t nDec = 0;
//以3个8位字符为一组进行编码
while (nRound-- > 0)
{
uint32_t idx;
idx = ((pIn[nDec + 2] & 0xf) << 2) | (pIn[nDec] & 0x3);
pOut[nEnc] = base64_table[idx];
idx = (pIn[nDec + 1] & 0x3c) | ((pIn[nDec] & 0xc) >> 2);
pOut[nEnc + 1] = base64_table[idx];
idx = ((pIn[nDec + 1] & 0x3) << 4) | (pIn[nDec] >> 4);
pOut[nEnc + 2] = base64_table[idx];
idx = ((pIn[nDec + 1] & 0xc0) >> 2) | (pIn[nDec + 2] >> 4);
pOut[nEnc + 3] = base64_table[idx];
nDec += 3;
nEnc += 4;
}
return true;
}
void KC_base64_decode(uint8_t* pIn, uint8_t* pOut)
{
uint32_t nRound = 32 / 4;
uint32_t nEnc = 0;
uint32_t nDec = 0;
//以4个字符为一位进行解码
while (nRound-- > 0)
{
uint8_t tmp0 = g_sd_g_szBase64Table[pIn[nEnc]];
uint8_t tmp1 = g_sd_g_szBase64Table[pIn[nEnc + 1]];
uint8_t tmp2 = g_sd_g_szBase64Table[pIn[nEnc + 2]];
uint8_t tmp3 = g_sd_g_szBase64Table[pIn[nEnc + 3]];
if ((tmp0 & 0xC0) != 0 || (tmp1 & 0xC0) != 0 || (tmp2 & 0xC0) != 0 || (tmp3 & 0xC0) != 0)
{
//走这里就是解密出错了
return;
}
g_sd_g_qwDecSuccess = g_sd_g_qwDecSuccess | (tmp0 & 0xC0);
g_sd_g_qwDecSuccess = g_sd_g_qwDecSuccess | (tmp1 & 0xC0);
g_sd_g_qwDecSuccess = g_sd_g_qwDecSuccess | (tmp2 & 0xC0);
g_sd_g_qwDecSuccess = g_sd_g_qwDecSuccess | (tmp3 & 0xC0);
pOut[nDec] =
(((uint8_t)tmp2) << 4)
| ((((uint8_t)tmp1) & 0x3) << 2)
| (((uint8_t)tmp0) & 0x3);
pOut[nDec + 1] =
((((uint8_t)tmp3) & 0x30) << 2)
| (((uint8_t)tmp1) & 0x3c)
| (((uint8_t)tmp2) >> 4);
pOut[nDec + 2] =
(((uint8_t)tmp3) << 4)
| (((uint8_t)tmp0) >> 2);
nEnc += 4;
nDec += 3;
}
return;
}
uint64_t EncQWORD(uint64_t input)
{
uint64_t result = 0;
uint64_t xor_val = input ^ XOR_NUMBER;
PBYTE pResult = (PBYTE)&result;
PBYTE pXorVal = (PBYTE)&xor_val;
pResult[0] = pXorVal[0] ^ pXorVal[1] ^ pXorVal[2] ^ pXorVal[3];
pResult[1] = pXorVal[0] ^ pXorVal[1] ^ pXorVal[2];
pResult[2] = pXorVal[0] ^ pXorVal[2];
pResult[3] = pXorVal[0];
pResult[4] = pXorVal[4] ^ pXorVal[5] ^ pXorVal[6] ^ pXorVal[7];
pResult[5] = pXorVal[4] ^ pXorVal[5] ^ pXorVal[6];
pResult[6] = pXorVal[4] ^ pXorVal[6];
pResult[7] = pXorVal[4];
return result;
}
uint64_t DecQWORD(uint64_t input)
{
uint64_t result = 0;
PBYTE pResult = (PBYTE)&result;
PBYTE pInputVal = (PBYTE)&input;
pResult[2] = pInputVal[2] ^ pInputVal[3];
pResult[1] = pInputVal[1] ^ pInputVal[3] ^ pResult[2];
pResult[3] = pInputVal[0] ^ pInputVal[3] ^ pResult[1] ^ pResult[2];
pResult[0] = pInputVal[3];
pResult[6] = pInputVal[6] ^ pInputVal[7];
pResult[5] = pInputVal[5] ^ pInputVal[7] ^ pResult[6];
pResult[7] = pInputVal[4] ^ pInputVal[7] ^ pResult[5] ^ pResult[6];
pResult[4] = pInputVal[7];
result ^= XOR_NUMBER;
return result;
}
void Enc(LPBYTE Serial, LPBYTE enc)
{
BYTE tmp[24] = { 0 };
BYTE tmp2[32] = { 0 };
EncM(Serial, tmp+8, 3, 8);
*(uint64_t*)tmp = EncQWORD(*(uint64_t*)Serial);
KC_base64_encode(tmp, tmp2);
EncM(tmp2, enc, 11, 7);
EncM(tmp2+16, enc+16, 5, 14);
}
void Dec()
{
BYTE tmp[24] = { 0 };
DecM(g_sd_g_szSerial, g_sd_g_szSerial, 11, 7);
DecM(g_sd_g_szSerial + 16, g_sd_g_szSerial + 16, 5, 14);
KC_base64_decode(g_sd_g_szSerial, tmp);
*(uint64_t*)tmp = DecQWORD(*(uint64_t*)tmp);
memcpy(g_sd_g_szDec, tmp + 8, 16);
DecM(g_sd_g_szDec, g_sd_g_szDec, 3, 8);
g_sd_g_qwDecSuccess = g_sd_g_qwDecSuccess | (*(uint64_t*)g_sd_g_szDec ^ *(uint64_t*)tmp);
}
int main()
{
BYTE szInput[20] = { 0 };// { 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10 };
BYTE szEnc[32] = { 0 };
char szShow[200] = { 0 };
for (int i = 0; i < 16; i++)
{
szInput[i] = 0x10 - i;
}
printf("input name:");
scanf("%16s", &szInput);
hex2string(szInput, 16, szShow);
printf("\r\n\r\ng_userName : \r\n%s\r\n\r\n", szShow);
Enc(szInput, szEnc);
hex2string(szEnc, 32, szShow);
printf("\r\n\r\nEnc : \r\n%s\r\n\r\n", szShow);
memcpy(g_sd_g_szName, szInput, 16);
memcpy(g_sd_g_szSerial, szEnc, 32);
uint8_t table[256] = {
0x95, 0xE2, 0x80, 0xC6, 0xEA, 0xC3, 0xD5, 0x8D, 0x9E, 0xC5, 0xB3, 0x62, 0x64, 0x4D, 0x76, 0xBA, //0x00-0x0F
0x92, 0xFD, 0xDE, 0x7F, 0x42, 0x72, 0x81, 0xAD, 0x79, 0x54, 0x73, 0x85, 0x86, 0x5E, 0xF1, 0x84, //0x10-0x1F
0x6A, 0xF5, 0x63, 0xD8, 0xFE, 0xA8, 0xC0, 0xC8, 0x4F, 0xC9, 0xC7, 0x03, 0x7B, 0xE5, 0xDF, 0x02, //0x20-0x2F
0x00, 0x0D, 0x1F, 0x3C, 0x13, 0x22, 0x25, 0x3B, 0x2B, 0x17, 0xAA, 0xA0, 0xF6, 0x97, 0x59, 0x58, //0x30-0x3F
0x6D, 0x0F, 0x0C, 0x06, 0x3D, 0x1B, 0x0E, 0x21, 0x14, 0x26, 0x2D, 0x04, 0x12, 0x1C, 0x09, 0x01, //0x40-0x4F
0x2E, 0x33, 0x35, 0x23, 0x20, 0x0B, 0x38, 0x1A, 0x32, 0x2C, 0x39, 0x7C, 0xD1, 0xF2, 0x5C, 0x75, //0x50-0x5F
0xA1, 0x08, 0x24, 0x10, 0x28, 0x29, 0x3F, 0x31, 0x07, 0x0A, 0x2F, 0x11, 0x19, 0x1E, 0x3E, 0x15, //0x60-0x6F
0x1D, 0x2A, 0x3A, 0x34, 0x16, 0x05, 0x36, 0x37, 0x27, 0x30, 0x18, 0x6C, 0x4A, 0x7A, 0x44, 0x98, //0x70-0x7F
0x96, 0x69, 0xC4, 0xEB, 0xCC, 0x49, 0xBE, 0xB5, 0x48, 0x71, 0x94, 0xE1, 0xA3, 0xB1, 0x78, 0xFA, //0x80-0x8F
0x53, 0x46, 0x40, 0xCB, 0xBC, 0x47, 0x83, 0xC1, 0xEE, 0xF9, 0xE8, 0x61, 0xA9, 0xFB, 0xC2, 0xD2, //0x90-0x9F
0x4C, 0x55, 0xDA, 0xF7, 0x7E, 0xD9, 0x8F, 0xAC, 0xE3, 0x52, 0x60, 0x9B, 0xE9, 0x56, 0x9C, 0x89, //0xA0-0xAF
0x57, 0xB4, 0x51, 0x7D, 0xB0, 0x74, 0x8E, 0xA2, 0x9D, 0xED, 0xB6, 0xE0, 0x5F, 0xFC, 0x4B, 0x6E, //0xB0-0xBF
0xA5, 0x41, 0xD0, 0xA7, 0xBB, 0xF0, 0x8C, 0x91, 0x65, 0xB9, 0xD4, 0xE6, 0x87, 0xB8, 0xBF, 0xF8, //0xC0-0xCF
0xEC, 0x9F, 0x9A, 0xD3, 0x6F, 0x93, 0x5D, 0x66, 0x88, 0x43, 0x5B, 0x50, 0xF3, 0x82, 0xB7, 0xCE, //0xD0-0xDF
0x67, 0xE7, 0xF4, 0xFF, 0xAF, 0xCD, 0xD6, 0xDC, 0xAB, 0x68, 0x5A, 0x8A, 0xDD, 0xEF, 0xE4, 0xBD, //0xE0-0xEF
0x4E, 0xA4, 0x77, 0xB2, 0x45, 0xA6, 0x99, 0x70, 0xDB, 0x6B, 0xD7, 0x90, 0xCF, 0xAE, 0x8B, 0xCA //0xF0-0xFF
};
memcpy(g_sd_g_szBase64Table, table, sizeof(table));
Dec();
hex2string(g_sd_g_szDec, 16, szShow);
printf("\r\n\r\nDec : %lld (<-- 必须要为0)\r\n%s\r\n\r\n",g_sd_g_qwDecSuccess, szShow);
system("pause");
}
第六题《废土末世》正在进行中,
*点击图片查看大图
球分享
球点赞
球在看