NCTF Reverse 部分 WP
SafeProgram
主要加密逻辑在encode
里面,其实就是SM4,而且没有任何魔改
图中的 table 对应 SM4 的 sbox
但跑了几个脚本,发现输出并不正确,怀疑题目在进入 main 函数前修改了某些东西
搜索 key 的交叉引用,发现sub_7FF7317B1480
中修改了 key ,并对 SM4 的 sbox 进行了一些交换
写个脚本把 key 和 sbox 跑出来
#include <bits/stdc++.h>
int main() {
unsigned char key_[] = {
0xDF, 0xD2, 0xC5, 0xD7, 0xA3, 0xA5, 0xFF, 0xF2, 0xE5, 0xF7
};
unsigned char sbox[] = { 0xD6,0x90,0xE9,0xFE,0xCC,0xE1,0x3D,0xB7,0x16,0xB6,0x14,0xC2,0x28,0xFB,0x2C,0x5,0x2B,0x67,0x9A,0x76,0x2A,0xBE,0x4,0xC3,0xAA,0x44,0x13,0x26,0x49,0x86,0x6,0x99,0x9C,0x42,0x50,0xF4,0x91,0xEF,0x98,0x7A,0x33,0x54,0xB,0x43,0xED,0xCF,0xAC,0x62,0xE4,0xB3,0x1C,0xA9,0xC9,0x8,0xE8,0x95,0x80,0xDF,0x94,0xFA,0x75,0x8F,0x3F,0xA6,0x47,0x7,0xA7,0xFC,0xF3,0x73,0x17,0xBA,0x83,0x59,0x3C,0x19,0xE6,0x85,0x4F,0xA8,0x68,0x6B,0x81,0xB2,0x71,0x64,0xDA,0x8B,0xF8,0xEB,0xF,0x4B,0x70,0x56,0x9D,0x35,0x1E,0x24,0xE,0x5E,0x63,0x58,0xD1,0xA2,0x25,0x22,0x7C,0x3B,0x1,0x21,0x78,0x87,0xD4,0x0,0x46,0x57,0x9F,0xD3,0x27,0x52,0x4C,0x36,0x2,0xE7,0xA0,0xC4,0xC8,0x9E,0xEA,0xBF,0x8A,0xD2,0x40,0xC7,0x38,0xB5,0xA3,0xF7,0xF2,0xCE,0xF9,0x61,0x15,0xA1,0xE0,0xAE,0x5D,0xA4,0x9B,0x34,0x1A,0x55,0xAD,0x93,0x32,0x30,0xF5,0x8C,0xB1,0xE3,0x1D,0xF6,0xE2,0x2E,0x82,0x66,0xCA,0x60,0xC0,0x29,0x23,0xAB,0xD,0x53,0x4E,0x6F,0xD5,0xDB,0x37,0x45,0xDE,0xFD,0x8E,0x2F,0x3,0xFF,0x6A,0x72,0x6D,0x6C,0x5B,0x51,0x8D,0x1B,0xAF,0x92,0xBB,0xDD,0xBC,0x7F,0x11,0xD9,0x5C,0x41,0x1F,0x10,0x5A,0xD8,0xA,0xC1,0x31,0x88,0xA5,0xCD,0x7B,0xBD,0x2D,0x74,0xD0,0x12,0xB8,0xE5,0xB4,0xB0,0x89,0x69,0x97,0x4A,0xC,0x96,0x77,0x7E,0x65,0xB9,0xF1,0x9,0xC5,0x6E,0xC6,0x84,0x18,0xF0,0x7D,0xEC,0x3A,0xDC,0x4D,0x20,0x79,0xEE,0x5F,0x3E,0xD7,0xCB,0x39,0x48 };
for (size_t i = 0; i < 10; i++) {
key_[i] ^= 0x91;
}
for (size_t i = 0; i < 10; i++) {
std::swap(sbox[0], sbox[key_[i]]);
}
printf("key: ");
for (size_t i = 0; i < 10; i++) {
printf("0x%02x,", key_[i]);
}
printf("\n");
printf("sbox: ");
for (size_t i = 0; i < 256; i++) {
printf("0x%02x,", sbox[i]);
}
printf("\n");
}
/*
key:
0x4e,0x43,0x54,0x46,0x32,0x34,0x6e,0x63,0x74,0x66
sbox: 0xd1,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,0xe4,0xb3,0x17,0xa9,0x1c,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,0x47,0x07,0xa7,0x4f,0xf3,0x73,0x71,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0xd6,0xa8,0x68,0x6b,0x81,0xb2,0xfc,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,0x1e,0x24,0x0e,0x78,0x63,0x58,0x9f,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0xc9,0x87,0xd4,0x00,0x46,0x57,0x5e,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
*/
之后写出魔改后的 SM4 的 exp
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
// SM4算法参数定义
const unsigned int FK[4] = { 0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC };
const unsigned int CK[32] = {
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};
// S盒
// 这个题目提前置换了sbox
const unsigned char SBOX[] = {
0xd1,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x5,0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x4,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x6,0x99,0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0xb,0x43,0xed,0xcf,0xac,0x62,0xe4,0xb3,0x17,0xa9,0x1c,0x8,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,0x47,0x7,0xa7,0x4f,0xf3,0x73,0x71,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0xd6,0xa8,0x68,0x6b,0x81,0xb2,0xfc,0x64,0xda,0x8b,0xf8,0xeb,0xf,0x4b,0x70,0x56,0x9d,0x35,0x1e,0x24,0xe,0x78,0x63,0x58,0x9f,0xa2,0x25,0x22,0x7c,0x3b,0x1,0x21,0xc9,0x87,0xd4,0x0,0x46,0x57,0x5e,0xd3,0x27,0x52,0x4c,0x36,0x2,0xe7,0xa0,0xc4,0xc8,0x9e,0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0xd,0x53,0x4e,0x6f,0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x3,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,0xa,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,0x89,0x69,0x97,0x4a,0xc,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x9,0xc5,0x6e,0xc6,0x84,0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
/*
原来的sbox
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
*/
};
// 循环左移
#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
// 非线性变换τ
unsigned int tau(unsigned int word) {
unsigned int result = 0;
result |= SBOX[(word >> 24) & 0xFF] << 24;
result |= SBOX[(word >> 16) & 0xFF] << 16;
result |= SBOX[(word >> 8) & 0xFF] << 8;
result |= SBOX[word & 0xFF];
return result;
}
// 线性变换L
unsigned int L(unsigned int word) {
return word ^ ROTL(word, 2) ^ ROTL(word, 10) ^ ROTL(word, 18) ^ ROTL(word, 24);
}
// 密钥扩展算法
void key_expansion(const unsigned char key[16], unsigned int rk[32]) {
unsigned int k[36];
// 初始化密钥
k[0] = (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3];
k[1] = (key[4] << 24) | (key[5] << 16) | (key[6] << 8) | key[7];
k[2] = (key[8] << 24) | (key[9] << 16) | (key[10] << 8) | key[11];
k[3] = (key[12] << 24) | (key[13] << 16) | (key[14] << 8) | key[15];
for (int i = 0; i < 4; i++) {
k[i] ^= FK[i];
}
// 生成轮密钥
for (int i = 0; i < 32; i++) {
unsigned int tmp = k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i];
tmp = tau(tmp);
tmp = tmp ^ ROTL(tmp, 13) ^ ROTL(tmp, 23);
k[i + 4] = k[i] ^ tmp;
rk[i] = k[i + 4];
}
}
// SM4解密单个分组
void sm4_decrypt_block(const unsigned int rk[32], const unsigned char input[16], unsigned char output[16]) {
unsigned int X[36];
// 反序输入
X[0] = (input[0] << 24) | (input[1] << 16) | (input[2] << 8) | input[3];
X[1] = (input[4] << 24) | (input[5] << 16) | (input[6] << 8) | input[7];
X[2] = (input[8] << 24) | (input[9] << 16) | (input[10] << 8) | input[11];
X[3] = (input[12] << 24) | (input[13] << 16) | (input[14] << 8) | input[15];
// 32轮迭代(逆序使用轮密钥)
for (int i = 0; i < 32; i++) {
unsigned int tmp = X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[31 - i]; // 注意这里使用逆序密钥
tmp = tau(tmp);
tmp = L(tmp);
X[i + 4] = X[i] ^ tmp;
}
// 反序输出
unsigned int Y0 = X[35];
unsigned int Y1 = X[34];
unsigned int Y2 = X[33];
unsigned int Y3 = X[32];
output[0] = (Y0 >> 24) & 0xFF;
output[1] = (Y0 >> 16) & 0xFF;
output[2] = (Y0 >> 8) & 0xFF;
output[3] = Y0 & 0xFF;
output[4] = (Y1 >> 24) & 0xFF;
output[5] = (Y1 >> 16) & 0xFF;
output[6] = (Y1 >> 8) & 0xFF;
output[7] = Y1 & 0xFF;
output[8] = (Y2 >> 24) & 0xFF;
output[9] = (Y2 >> 16) & 0xFF;
output[10] = (Y2 >> 8) & 0xFF;
output[11] = Y2 & 0xFF;
output[12] = (Y3 >> 24) & 0xFF;
output[13] = (Y3 >> 16) & 0xFF;
output[14] = (Y3 >> 8) & 0xFF;
output[15] = Y3 & 0xFF;
}
int main() {
// key被异或了0x91
unsigned char key[16] = { 0x4e,0x43,0x54,0x46,0x32,0x34,0x6e,0x63,0x74,0x66,0x4e,0x43,0x54,0x46,0x32,0x34 };
unsigned char ciphertext1[] = {
0xFB,0x97,0x3C,0x3B,0xF1,0x99,0x12,0xDF,0x13,0x30,0xF7,0xD8,0x7F,0xEB,0xA0,0x6C
};
unsigned char ciphertext2[] = {
0x14,0x5B,0xA6,0x2A,0xA8,0x5,0xA5,0xF3,0x76,0xBE,0xC9,0x1,0xF9,0x36,0x7B,0x46
};
// 生成轮密钥
unsigned int rk[32];
key_expansion(key, rk);
// 解密
unsigned char plaintext1[16];
sm4_decrypt_block(rk, ciphertext1, plaintext1);
unsigned char plaintext2[16];
sm4_decrypt_block(rk, ciphertext2, plaintext2);
// 输出结果
printf("NCTF{");
for (int i = 0; i < 16; i++) {
printf("%c", plaintext1[i]);
}
for (int i = 0; i < 16; i++) {
printf("%c", plaintext2[i]);
}
printf("}");
cout << endl;
return 0;
}
/*
NCTF{58cb925e0cd823c0d0b54fd06b820b7e}
*/
ezDos
16位 Dos 程序,还塞了花,IDA 不能直接反编译,老老实实看汇编了
需要注意的是,中间 call 的几个函数会修改返回地址,有几条指令被跳过了
分析一下很容易就能看出来这是魔改的 rc4,主要修改了以下几个地方:
- 生成倒置的 sbox,即 sbox 内容为255->0
- 打乱 sbox 的过程中,对 key 进行了循环左移
- 对异或的密钥流每一位 +1
exp如下
#include <vector>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
vector<unsigned char> rc4(const vector<unsigned char>& key, const vector<unsigned char>& data) {
unsigned char S[256];
int i, j = 0;
// 1. 初始化 S 盒(KSA 密钥调度算法)
for (i = 0; i < 256; ++i)
S[i] = 255 - i;
for (i = 0; i < 256; ++i) {
int tmp = (key[i % key.size()] << 3) | (key[i % key.size()] >> 5);
j = (j + S[i] + tmp) % 256;
swap(S[i], S[j]);
}
// 2. 生成密钥流并加密(PRGA 伪随机生成算法)
vector<unsigned char> result;
i = j = 0;
for (unsigned char c : data) {
i = (i + 1) % 256;
j = (j + S[i]) % 256;
swap(S[i], S[j]);
unsigned char k = S[(S[i] + S[j]) % 256];
result.push_back(c ^ (k + 1));
}
return result;
}
int main() {
vector<unsigned char> key = { 0x4E,0x43,0x54,0x66,0x32,0x30,0x32,0x34,0x6E,0x63,0x74,0x46 };
vector<unsigned char> plain_bytes = { 0x7C,0x3E,0xD,0x3C,0x88,0x54,0x83,0xE,0x3B,0xB8,0x99,0x1B,0x9B,0xE5,0x23,0x43,0xC5,0x80,0x45,0x5B,0x9A,0x29,0x24,0x38,0xA9,0x5C,0xCB,0x7A,0xE5,0x93,0x73,0xE,0x70,0x6D,0x7C,0x31,0x2B,0x8C };
vector<unsigned char> decrypted = rc4(key, plain_bytes);
string decrypted_str(decrypted.begin(), decrypted.end());
cout << "解密结果: " << decrypted_str << endl;
return 0;
}
/*
NCTF{Y0u_4r3_Assemb1y_M4st3r_5d0b497e}
*/