5

[NepnepCTF2025]Crackme

校验的时候会先检验输入是不是16位小写字母a-f和数字组成的字符串的hex,后面的加密函数混淆非常多

crackme.exe会调用libcrypto.dll里面的函数

第一次MD5函数是对用户名,作为密钥,第二次是对用户名+"Showmaker11",作为最后判断的密文

加密结果和标准的不一样,可能是常数不一样,在MD5函数里面的return处断点能够找到加密后的数据

image

aes加密还给了函数名还行吧

image

这样找到了S盒

image

这里和标准的不一样,多异或了0x55

(好ai)
AES 加密流程推测

  1. 密钥扩展(Key Expansion)
    dword_68155030 可能是 轮常量(Rcon)。

代码片段:

v75 = dword_68155030[v131] ^ v118 ^ v124 ^ (byte_68154000[v135] << 8) ^ byte_68154000[v135 >> 24];
*(v144 + 16) = v75;  // 存储扩展后的密钥

符合 AES 密钥扩展逻辑。

  1. 字节替换(SubBytes)
    byte_68154000 可能是 S-Box,用于字节替换:
LOBYTE(n626523479_1) = byte_68154000[*v65[0]];
*v65[0] = n626523479_1;
  1. 行移位(ShiftRows)
    代码中有 循环左移操作(ROL4),可能是 ShiftRows:
LODWORD(v132) = __ROL4__(v34, 8 * v138);
  1. 列混淆(MixColumns)
    部分代码涉及 GF(2^8) 乘法,可能是 MixColumns:
v127 = v152 ^ v53 ^ v111 ^ v42 ^ 0x55;  // 多项式乘法
  1. 轮密钥加(AddRoundKey)
    xor 操作 可能是轮密钥加:
*v133[0] = v141 ^ v139;

不愧是aes,脚本太难写了

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

#define Nb 4
#define Nr 10
#define Nk 4

// 使用行优先存储(16字节数组)
typedef uint8_t state_t[16];


unsigned char sbox[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
};

unsigned char rsbox[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
};
static uint8_t xtime(uint8_t x) {
	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}

static uint8_t Multiply(uint8_t x, uint8_t y) {
	return (((y & 1) * x) ^
		((y >> 1 & 1) * xtime(x)) ^
		((y >> 2 & 1) * xtime(xtime(x))) ^
		((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
		((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))));
}

// 逆向字节替换
static void InvSubBytes(state_t state) {
	for (int i = 0; i < 16; ++i) {
		state[i] = rsbox[state[i]];
	}
}

// 密钥扩展
static void KeyExpansion(const uint8_t* Key, uint32_t* RoundKey) {
	// 初始密钥拷贝
	for (int i = 0; i < Nk; ++i) {
		RoundKey[i] = ((uint32_t)Key[4*i]<<24) | ((uint32_t)Key[4*i+1]<<16) | 
		((uint32_t)Key[4*i+2]<<8) | (uint32_t)Key[4*i+3];
	}
	
	const uint32_t Rcon[10] = {
		0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
		0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000
	};
	
	// 密钥扩展
	for (int i = Nk; i < Nb*(Nr+1); ++i) {
		uint32_t temp = RoundKey[i-1];
		
		if (i % Nk == 0) {
			temp = (temp << 8) | (temp >> 24); 
			temp = (sbox[(temp >> 24) & 0xFF] << 24) |
			(sbox[(temp >> 16) & 0xFF] << 16) |
			(sbox[(temp >> 8)  & 0xFF] << 8)  |
			sbox[temp & 0xFF];
			temp ^= Rcon[(i/Nk)-1]; 
		}
		
		RoundKey[i] = RoundKey[i-Nk] ^ temp;
	}
}

// 轮密钥加
static void AddRoundKey(uint8_t round, state_t state, const uint32_t* RoundKey) {
	for (int i = 0; i < 4; ++i) {
		uint32_t kw = RoundKey[round*4 + i];
		state[4*i]   ^= (kw >> 24) & 0xFF;
		state[4*i+1] ^= (kw >> 16) & 0xFF;
		state[4*i+2] ^= (kw >> 8)  & 0xFF;
		state[4*i+3] ^= kw & 0xFF;
	}
}

// 逆向行移位
static void InvShiftRows(state_t state) {
	uint8_t temp;
	
	// 第1行循环右移1字节
	temp = state[13];
	state[13] = state[9];
	state[9] = state[5];
	state[5] = state[1];
	state[1] = temp;
	
	// 第2行交换两个字节
	temp = state[2];
	state[2] = state[10];
	state[10] = temp;
	temp = state[6];
	state[6] = state[14];
	state[14] = temp;
	
	// 第3行循环右移3字节
	temp = state[3];
	state[3] = state[7];
	state[7] = state[11];
	state[11] = state[15];
	state[15] = temp;
}

// 逆向列混淆
static void InvMixColumns(state_t state) {
	uint8_t tmp[4][4];
	
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < 4; ++j) {
			tmp[i][j] = state[i*4 + j] ^ 0x55;
		}
	}
	
	for (int i = 0; i < 4; ++i) {
		state[i*4]   = Multiply(tmp[i][0], 0x0e) ^ Multiply(tmp[i][1], 0x0b) ^ Multiply(tmp[i][2], 0x0d) ^ Multiply(tmp[i][3], 0x09);
		state[i*4+1] = Multiply(tmp[i][0], 0x09) ^ Multiply(tmp[i][1], 0x0e) ^ Multiply(tmp[i][2], 0x0b) ^ Multiply(tmp[i][3], 0x0d);
		state[i*4+2] = Multiply(tmp[i][0], 0x0d) ^ Multiply(tmp[i][1], 0x09) ^ Multiply(tmp[i][2], 0x0e) ^ Multiply(tmp[i][3], 0x0b);
		state[i*4+3] = Multiply(tmp[i][0], 0x0b) ^ Multiply(tmp[i][1], 0x0d) ^ Multiply(tmp[i][2], 0x09) ^ Multiply(tmp[i][3], 0x0e);
	}
}

// 解密函数
static void InvCipher(state_t state, const uint32_t* RoundKey) {
	// 初始轮密钥加
	AddRoundKey(Nr, state, RoundKey);
	
	// 9轮完整解密
	for (int round = Nr-1; round > 0; --round) {
		InvShiftRows(state);
		InvSubBytes(state);
		AddRoundKey(round, state, RoundKey);
		InvMixColumns(state);
	}
	
	// 最后一轮
	InvShiftRows(state);
	InvSubBytes(state);
	AddRoundKey(0, state, RoundKey);
}

// ECB解密
void AES_ECB_decrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, size_t length) {
	if (!input || !key || !output || length % 16 != 0) return;
	
	uint32_t RoundKey[Nb*(Nr+1)];
	KeyExpansion(key, RoundKey);
	
	for (size_t i = 0; i < length; i += 16) {
		state_t state;
		memcpy(state, input+i, 16);
		InvCipher(state, RoundKey);
		memcpy(output+i, state, 16);
	}
}

int main() {
	uint8_t key[16] = {
		0x24, 0x25, 0x8a, 0x5b, 0x6a, 0x20, 0x62, 0x5d,
		0xd2, 0x71, 0x64, 0x32, 0xfd, 0xe7, 0x5e, 0xc4
	};
	
	uint8_t ciphertext[16] = {
		0x22, 0xeb, 0xb5, 0x40, 0x91, 0x62, 0x9c, 0xf7,
		0xe2, 0x13, 0xff, 0xa8, 0x8c, 0x54, 0xd9, 0x80
	};
	
	uint8_t decrypted[16];
	
	AES_ECB_decrypt(ciphertext, key, decrypted, sizeof(ciphertext));
	
	printf("解密结果: ");
	for (int i = 0; i < sizeof(decrypted); i++) {
		printf("%02x", decrypted[i]);
	}
	
	return 0;
}

在InvMixColumns函数里面是最后异或结果,所以解密的时候应该先异或回去再逆向列混淆

在网页看到有100个用户名,天塌了

直接利用libcrypto.dll的sign函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <windows.h>  // 用于动态加载 DLL

#define Nb 4
#define Nr 10
#define Nk 4

typedef uint8_t state_t[16];

#define Nb 4
#define Nr 10
#define Nk 4

// 使用行优先存储(16字节数组)
typedef uint8_t state_t[16];


unsigned char sbox[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
};

unsigned char rsbox[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
};
static uint8_t xtime(uint8_t x) {
	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}

static uint8_t Multiply(uint8_t x, uint8_t y) {
	return (((y & 1) * x) ^
		((y >> 1 & 1) * xtime(x)) ^
		((y >> 2 & 1) * xtime(xtime(x))) ^
		((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
		((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))));
}

// 逆向字节替换
static void InvSubBytes(state_t state) {
	for (int i = 0; i < 16; ++i) {
		state[i] = rsbox[state[i]];
	}
}

// 密钥扩展
static void KeyExpansion(const uint8_t* Key, uint32_t* RoundKey) {
	// 初始密钥拷贝
	for (int i = 0; i < Nk; ++i) {
		RoundKey[i] = ((uint32_t)Key[4*i]<<24) | ((uint32_t)Key[4*i+1]<<16) | 
		((uint32_t)Key[4*i+2]<<8) | (uint32_t)Key[4*i+3];
	}
	
	const uint32_t Rcon[10] = {
		0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
		0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000
	};
	
	// 密钥扩展
	for (int i = Nk; i < Nb*(Nr+1); ++i) {
		uint32_t temp = RoundKey[i-1];
		
		if (i % Nk == 0) {
			temp = (temp << 8) | (temp >> 24); 
			temp = (sbox[(temp >> 24) & 0xFF] << 24) |
			(sbox[(temp >> 16) & 0xFF] << 16) |
			(sbox[(temp >> 8)  & 0xFF] << 8)  |
			sbox[temp & 0xFF];
			temp ^= Rcon[(i/Nk)-1]; 
		}
		
		RoundKey[i] = RoundKey[i-Nk] ^ temp;
	}
}

// 轮密钥加
static void AddRoundKey(uint8_t round, state_t state, const uint32_t* RoundKey) {
	for (int i = 0; i < 4; ++i) {
		uint32_t kw = RoundKey[round*4 + i];
		state[4*i]   ^= (kw >> 24) & 0xFF;
		state[4*i+1] ^= (kw >> 16) & 0xFF;
		state[4*i+2] ^= (kw >> 8)  & 0xFF;
		state[4*i+3] ^= kw & 0xFF;
	}
}

// 逆向行移位
static void InvShiftRows(state_t state) {
	uint8_t temp;
	
	// 第1行循环右移1字节
	temp = state[13];
	state[13] = state[9];
	state[9] = state[5];
	state[5] = state[1];
	state[1] = temp;
	
	// 第2行交换两个字节
	temp = state[2];
	state[2] = state[10];
	state[10] = temp;
	temp = state[6];
	state[6] = state[14];
	state[14] = temp;
	
	// 第3行循环右移3字节
	temp = state[3];
	state[3] = state[7];
	state[7] = state[11];
	state[11] = state[15];
	state[15] = temp;
}

// 逆向列混淆
static void InvMixColumns(state_t state) {
	uint8_t tmp[4][4];
	
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < 4; ++j) {
			tmp[i][j] = state[i*4 + j] ^ 0x55;
		}
	}
	
	for (int i = 0; i < 4; ++i) {
		state[i*4]   = Multiply(tmp[i][0], 0x0e) ^ Multiply(tmp[i][1], 0x0b) ^ Multiply(tmp[i][2], 0x0d) ^ Multiply(tmp[i][3], 0x09);
		state[i*4+1] = Multiply(tmp[i][0], 0x09) ^ Multiply(tmp[i][1], 0x0e) ^ Multiply(tmp[i][2], 0x0b) ^ Multiply(tmp[i][3], 0x0d);
		state[i*4+2] = Multiply(tmp[i][0], 0x0d) ^ Multiply(tmp[i][1], 0x09) ^ Multiply(tmp[i][2], 0x0e) ^ Multiply(tmp[i][3], 0x0b);
		state[i*4+3] = Multiply(tmp[i][0], 0x0b) ^ Multiply(tmp[i][1], 0x0d) ^ Multiply(tmp[i][2], 0x09) ^ Multiply(tmp[i][3], 0x0e);
	}
}

// 解密函数
static void InvCipher(state_t state, const uint32_t* RoundKey) {
	// 初始轮密钥加
	AddRoundKey(Nr, state, RoundKey);
	
	// 9轮完整解密
	for (int round = Nr-1; round > 0; --round) {
		InvShiftRows(state);
		InvSubBytes(state);
		AddRoundKey(round, state, RoundKey);
		InvMixColumns(state);
	}
	
	// 最后一轮
	InvShiftRows(state);
	InvSubBytes(state);
	AddRoundKey(0, state, RoundKey);
}

// ECB解密
void AES_ECB_decrypt(const uint8_t* input, const uint8_t* key, uint8_t* output, size_t length) {
	if (!input || !key || !output || length % 16 != 0) return;
	
	uint32_t RoundKey[Nb*(Nr+1)];
	KeyExpansion(key, RoundKey);
	
	for (size_t i = 0; i < length; i += 16) {
		state_t state;
		memcpy(state, input+i, 16);
		InvCipher(state, RoundKey);
		memcpy(output+i, state, 16);
	}
}

// ... (保留你原有的 AES 解密相关代码,包括 sbox, rsbox, xtime, Multiply, InvSubBytes, KeyExpansion 等)

// 定义 sign 函数的类型
typedef void (*sign_func_t)(const char*, int, uint8_t*);

// 动态加载 libcrypto.dll 并调用 sign 函数
void call_libcrypto_sign(const char* input, int len, uint8_t* output) {
	HMODULE hLib = LoadLibraryA("libcrypto.dll");
	if (!hLib) {
		fprintf(stderr, "Failed to load libcrypto.dll\n");
		exit(1);
	}
	
	sign_func_t sign_func = (sign_func_t)GetProcAddress(hLib, "sign");
	if (!sign_func) {
		fprintf(stderr, "Failed to find 'sign' function in libcrypto.dll\n");
		FreeLibrary(hLib);
		exit(1);
	}
	
	// 调用 sign 函数
	sign_func(input, len, output);
	
	FreeLibrary(hLib);
}

// 生成 key 和 ciphertext
void generate_key_and_ciphertext(const char* username, uint8_t* key, uint8_t* ciphertext) {
	// key = sign(username)
	call_libcrypto_sign(username, strlen(username), key);
	
	// ciphertext = sign(username + "Showmaker11")
	char buffer[256];
	snprintf(buffer, sizeof(buffer), "%sShowmaker11", username);
	call_libcrypto_sign(buffer, strlen(buffer), ciphertext);
}

// 解密所有用户名
void decrypt_all_usernames(const char** usernames, int count) {
	for (int i = 0; i < count; i++) {
		uint8_t key[16];
		uint8_t ciphertext[16];
		uint8_t decrypted[16];
		
		// 生成 key 和 ciphertext
		generate_key_and_ciphertext(usernames[i], key, ciphertext);
		
		// 解密
		AES_ECB_decrypt(ciphertext, key, decrypted, 16);
		
		// 输出结果
		printf("Username: %s\n", usernames[i]);
		printf("Decrypted: ");
		for (int j = 0; j < 16; j++) {
			printf("%02x", decrypted[j]);
		}
		printf("\n\n");
	}
}

int main() {
	// 示例用户名列表(替换成你的 100 个用户名)
	const char* usernames[] = {
		"chengang",
		"lijuanwang",
		"ligh",
		"zhangpeng",
		"zf",
		"zhangliang",
		"chenyuying",
		"ljh",
		"xzchen",
		"jingyang",
		"fy",
		"chenguiying",
		"ma",
		"wangyuying",
		"vm",
		"yb",
		"xliu",
		"ej",
		"lgr",
		"rn",
		"lil",
		"lingli",
		"ny",
		"wanggl",
		"ug",
		"wangguirong",
		"huazhang",
		"hongyang",
		"lxy",
		"liul",
		"gw",
		"rz",
		"nazhang",
		"liuqiang",
		"ck",
		"zgl",
		"superadmin",
		"ve",
		"admin1",
		"mw",
		"wanggz",
		"chenchen",
		"flwang",
		"yuanfang",
		"jgwang",
		"shulanwang",
		"jn",
		"wzhao",
		"ychen",
		"cgy",
		"wangxin",
		"zhangxue",
		"zhangxin",
		"zhoum",
		"chengwang",
		"taowang",
		"tingtingli",
		"wangna",
		"zhangsz",
		"tn",
		"tc",
		"ba",
		"qz",
		"fengli",
		"lizhou",
		"guizhizhang",
		"zhoujing",
		"to",
		"liugang",
		"gb",
		"jianzhang",
		"vd",
		"hwang",
		"xiuyinghuang",
		"tf",
		"tw",
		"wangyz",
		"jyang",
		"ei",
		"lyli",
		"jliu",
		"xywang",
		"liuling",
		"pengwang",
		"yumeiwang",
		"hd",
		"chenjuan",
		"wangfy",
		"sli",
		"zhangbo",
		"zhangyy",
		"yywang",
		"vg",
		"liming",
		"gli",
		"chenjun",
		"zhoumin",
		"minchen",
		"zhangning",
		"jgliu",
		"inkey"
	};
	
	int num_usernames = sizeof(usernames) / sizeof(usernames[0]);
	decrypt_all_usernames(usernames, num_usernames);
	
	return 0;
}

手动填写网页就可以了

image

[NepnepCTF2025]SpeedMino

游戏启动之后flag一直在计算,ce修改游戏速度(不要太太太大,会卡死)加快计算,而且得分也是和时间的关系最大

image

image

[NepnepCTF2025]MoewBle喵泡

MeowBle_Data中level2有part1和part2

image

这个游戏能用il2cppdumper去dump

image

看到有die函数和health变量,但是我将die函数nop之后如果player受伤就会被罚站,但是看到有人是成功的,那就是我改炸了

image

分析.NET程序的结构,只有CE的7.5版本以上能够使用这个功能

image

使用lookup instances查找实例,实例窗口默认比较小,所以可以拉一下,修改health的值,这样角色就无敌了,玩通关(有隐藏关卡)就可以

image

科乐美秘技,即输入“↑ ↑ ↓ ↓ ← → ← → B A”来获得隐藏技能或彩蛋,这里可以在暂停的时候触发GM,获取flag7

image

或者调用方法,在点击右上角设置,也能触发GM面板

image

最后终点在起就能通关了,flag共9段

flag:NepCTF{94721248-773d-0b25-0e2d-db9cac299389}

round-1/rev01(没做完)

VM,C++写的,opcode是文件里面的,通过interpret函数解释字节码

[NewStar CTF 2024]easygui

sub_140001490有反调试,直接修改跳转

image

加密和比较函数都挺明显的

加密:对输入的字节进行查表替换,每四个字节一组循环右移3位,rc4加密

查表替换

image

循环右移

image

rc4加密

image

rc4解密可以利用程序的自解密,也可以直接算,是标准的

image

动调提取v29,位运算,查表替换

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

// 加密函数
void encrypt(uint8_t *data, int len) {
	for (int k = 0; k < len; k += 4) {	
		
		uint8_t a = data[k];
		uint8_t b = data[k+1];
		uint8_t c = data[k+2];
		uint8_t d = data[k+3];
		
		data[k+3] = ((a >> 5) & 0x7) | ((d << 3) & 0xf8);
		data[k+2] = ((d >> 5) & 0x7) | ((c << 3) & 0xf8);
		data[k+1] = ((c >> 5) & 0x7) | ((b << 3) & 0xf8);
		data[k]   = ((b >> 5) & 0x7) | ((a << 3) & 0xf8);
	}
}

uint8_t v29[] = {
	0x31, 0x74, 0x54, 0x20, 0x03, 0x53, 0x78, 0x70, 0x3A, 0x35,
	0x65, 0x42, 0x04, 0x6B, 0x1F, 0x43,
	0x06, 0x37, 0x00, 0x76, 0x21, 0x08, 0x0B, 0x13, 0x52, 0x4B,
	0x2F, 0x1A, 0x59, 0x2C, 0x56, 0x51,
	0x7F, 0x3B, 0x0E, 0x05, 0x26, 0x15, 0x25, 0x63, 0x64, 0x7A,
	0x3C, 0x29, 0x41, 0x2A, 0x12, 0x17,
	0x2E, 0x39, 0x57, 0x3D, 0x66, 0x33, 0x44, 0x6C, 0x6F, 0x47,
	0x16, 0x71, 0x5F, 0x1C, 0x14, 0x5A,
	0x0C, 0x4F, 0x01, 0x30, 0x1B, 0x68, 0x0F, 0x62, 0x3F, 0x18,
	0x69, 0x6D, 0x7E, 0x5D, 0x6A, 0x28,
	0x22, 0x5B, 0x55, 0x72, 0x09, 0x5E, 0x02, 0x3E, 0x50, 0x7B,
	0x46, 0x45, 0x38, 0x10, 0x48, 0x79,
	0x60, 0x36, 0x61, 0x6E, 0x2D, 0x49, 0x7C, 0x2B, 0x34, 0x27,
	0x11, 0x7D, 0x0D, 0x0A, 0x77, 0x73,
	0x58, 0x5C, 0x4C, 0x32, 0x4D, 0x1E, 0x24, 0x40, 0x67, 0x4A,
	0x4E, 0x1D, 0x07, 0x75, 0x19, 0x23
};

char find_char(uint8_t value) {
	for (int i = 0; i < 128; i++) {
		if (v29[i] == value) {
			return (char)i;
		}
	}
	return '?'; // 没找到
}

int main() {
	uint8_t Src[] = {
		0x6F,0x81,0xA6,0xC5, 0x63,0xAC,0x4B,0xC7, 0x8F,0x29,
		0x87,0xA4, 0x27,0xAA,0xA6,0x69, 0x4F,0x27,0xAE,0xEC,
		0x27,0x2E,0xE7,0xA9, 0x69,0x87,0x2E,0xE5, 0x2F,0x24,
		0xE6,0x6F, 0x44,0x87,0xA9,0x89, 0x4F,0x26,0x47,0x21,
		0xAB,0x01,0xA7,0xAE
	};
	int len = sizeof(Src)/sizeof(Src[0]);

	encrypt(Src, len);

	for (int i = 0; i < len; i++) {
		printf("%c", find_char(Src[i]));
	}
	
	return 0;
}

[HDCTF 2023]easy_asm

image

很明显主要是异或加密,cl是0x10,al就是密文

#include<stdio.h>
int main(){
	int a[]={0x4E,0x6F,0x74,0x20,0x65,0x71,0x75,0x61,0x6C,0x21,0x24,
		0x45,0x71,0x75,0x61,0x6C,0x21,0x24,0x58,0x54,0x53,0x44,
		0x56,0x6B,0x5A,0x65,0x63,0x64,0x4F,0x71,0x4F,0x75,0x23,
		0x63,0x69,0x4F,0x71,0x43,0x7D,0x6D,0x24,};
	for(int i=0;i<41;i++){
		printf("%c",a[i]^0x10);
	}
}

flag周围带有乱码

[LitCTF 2023]程序和人有一个能跑就行了

直接反编译看到是rc4加密,用程序解密的话得到的是fake flag

看图有两份密文,但是反编译只能看到一份,并且是错的

image

更换密文

image

[NewStar CTF 2024]ezrust

动调发现输入,之后又是cmp,所以enc函数只能是在中间

image

看到loverust字符串

在loverust上面下一个硬件断点,会直接段在这里(好厉害)

image

a3是输入

异或结果不对

image

v7=7,v6=8,所以这里对key的索引应该是逆序的

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

int main()
{
	unsigned char v4[27]={0};
	v4[0] = 18;
	v4[1] = 31;
	v4[2] = 20;
	v4[3] = 21;
	v4[4] = 30;
	v4[5] = 15;
	v4[6] = 95;
	v4[7] = 57;
	v4[8] = 43;
	v4[9] = 51;
	v4[10] = 7;
	v4[11] = 65;
	v4[12] = 58;
	v4[13] = 79;
	v4[14] = 95;
	v4[15] = 3;
	v4[16] = 16;
	v4[17] = 44;
	v4[18] = 53;
	v4[19] = 6;
	v4[20] = 58;
	v4[21] = 4;
	v4[22] = 26;
	v4[23] = 31;
	v4[24] = 0;
	v4[25] = 14;
	char key[]="tsurevol";
	for(int i=0;i<27;i++){
		printf("%c",v4[i]^key[i%8]);
	}
}
posted @ 2025-08-03 23:34  zzz222666  阅读(22)  评论(0)    收藏  举报