NCTF Reverse 部分 WP

NCTF Reverse 部分 WP

azusa 2 2025-03-23

NCTF Reverse 部分 WP

SafeProgram

image-20250323222359173

主要加密逻辑在encode里面,其实就是SM4,而且没有任何魔改

图中的 table 对应 SM4 的 sbox

image-20250323223051665

但跑了几个脚本,发现输出并不正确,怀疑题目在进入 main 函数前修改了某些东西

搜索 key 的交叉引用,发现sub_7FF7317B1480中修改了 key ,并对 SM4 的 sbox 进行了一些交换

image-20250323223309694

写个脚本把 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

image-20250323230028432

16位 Dos 程序,还塞了花,IDA 不能直接反编译,老老实实看汇编了

image-20250323230250193

image-20250323232950460

image-20250323233041789

需要注意的是,中间 call 的几个函数会修改返回地址,有几条指令被跳过了

分析一下很容易就能看出来这是魔改的 rc4,主要修改了以下几个地方:

  1. 生成倒置的 sbox,即 sbox 内容为255->0
  2. 打乱 sbox 的过程中,对 key 进行了循环左移
  3. 对异或的密钥流每一位 +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}
*/