tea 加密解密算法(面向ctf-reverse使用,光速学会tea逆向套路)

一,算法特征

tea算法的主要特征表现在sum和delta变量,以及3行核心加密中出现的右移4左移5,两行各有3个小括号互相异或

在题目中看到这些特征时就应该警醒这是tea相关算法

delta的值一般为0x9E3779B9(-0x61C88647),但题目中往往会改变它的值,并不影响算法的逆向


二,加解密脚本

(一)tea加密脚本

注释写的很详细,可以辅助了解原理

void tea_enc(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是明文的左、右半部分
    uint32_t sum = 0;               // sum用作加密过程中的一个累加变量
    uint32_t delta = 0xd33b470;     //作为sum每次累加的变化值,题目中往往会修改此值
    for (int i = 0; i < 32; i++) {  // tea加密进行32轮
        //以下3行是核心加密过程,题目中往往会对部分细节做出修改(但由于异或的对称性质,根本不需要记,写解密函数时照抄就行了)
        sum += delta;
        v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
    }
    // v0和v1只是加密的临时变量,因此加密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}

(二)tea解密脚本 

void tea_dec(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是密文的左、右半部分
    uint32_t delta = 0xd33b470;     //作为sum每次累加的变化值,题目中往往会修改此值
    uint32_t sum = 32 * delta;      //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta
    for (int i = 0; i < 32; i++) {  // tea加密进行32轮
        //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
    // 因此解密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}

(三)tea解题实例

下面用两道比较经典的tea算法实战例题来搞清楚这个脚本如何套用,建议自己先尝试一下然后再看wp,两题都看一下,因为tea算法的题目经常对明文形式、输出格式等做变换,全部例举出来很废精力,还是要学会自己举一反三

省略了密钥k和密文input的获取过程,仔细分析main函数中调用tea解密的部分,理解如何套用编写好的tea解密函数

重点抓准——无论明文是什么形式、有多长,解密时一定是每次传入两个uint32_t(32位无符号整数)

(1)例题1->[MoeCTF 2022]ezTea

链接:NSSCTF | 在线CTF平台

#include <stdint.h>
#include <stdio.h>
void tea_dec(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是密文的左、右半部分
    uint32_t delta = 0xd33b470;     //作为sum每次累加的变化值,题目中往往会修改此值
    uint32_t sum = 32 * delta;      //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta
    for (int i = 0; i < 32; i++) {  // tea加密进行32轮
        //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
    // 解密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}

int main() {
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {1, 2, 3, 4};
    // v为要加解密的数据,两个32位无符号整数
    //但是稍微难一点点的都不会直接加密两个uint32_t,除非签到题。像这里的例子就是给了32个uint8_t
    //(常见的题目还有给几个uint32_t的,其实无非是拆开写成0x17、0x65...和连着写成0x1765的区别)
    //在后面的循环里每次传两组,每组4个组成uint32_t用于tea算法
    int8_t input[33] = {0x17, 0x65, 0x54, 0x89, 0xed, 0x65, 0x46, 0x32, 0x3d, 0x58, 0xa9, 0xfd, 0xe2, 0x5e, 0x61, 0x97,
                        0xe4, 0x60, 0xf1, 0x91, 0x73, 0xe9, 0xe9, 0xa2, 0x59, 0xcb, 0x9a, 0x99, 0xec, 0xb1, 0xe1, 0x7d};

    for (int i = 0; i < 32; i += 8) {
        //每组4个组成uint32_t用于tea算法,tea算法每次加解密操作的v一定是两个uint_32,至于怎么传入两个uint_32,题目有各种呈现方式,需要做题者自行分析
        uint32_t v[2] = {*(uint32_t*)&input[i], *(uint32_t*)&input[i + 4]};
        tea_dec(v, k);
        
        // tea输出字符的固定算法,外层循环两次是因为明文分为左半和右半两个uint32_t
        // 内层循环4次是因为一个字符占1个字节即8位,每次&0xff可以摘下最后1字节打印出对应ASCII字符,然后>>8准备下一字节
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 4; k++) {
                printf("%c", v[j] & 0xff);
                v[j] >>= 8;
            }
        }
    }
    return 0;
}

(2)例题2->[GWCTF 2019]xxor

链接:NSSCTF | 在线CTF平台

#include <stdint.h>
#include <stdio.h>
void tea_dec(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是密文的左、右半部分
    uint32_t delta = 1166789954;    //作为sum每次累加的变化值,题目中往往会修改此值
    uint32_t sum = 64 * delta;      //题目中的tea加密轮数为64轮,因此解密时要做出对应的修改,最终sum是64倍的delta
    for (int i = 0; i < 64; i++) {  // tea加密进行64轮
        //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
        v1 -= (v0 + sum + 20) ^ ((v0 << 6) + k[2]) ^ ((v0 >> 9) + k[3]) ^ 0x10;
        v0 -= (v1 + sum + 11) ^ ((v1 << 6) + k[0]) ^ ((v1 >> 9) + k[1]) ^ 0x20;
        sum -= delta;
    }
    // 解密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}

int main() {
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {2, 2, 3, 4};
    // v为要加解密的数据,两个32位无符号整数
    //但是稍微难一点点的都不会直接加密两个uint32_t,除非签到题。像这里的例子就是给了6个uint32_t
    //但是在加解密时一定是拆开进行,每次传入两个uint32_t

    uint32_t v[6];//根据题目解出密文是这些(z3求方程未知数的解)
    //这里给数组v的元素赋值时不能写成{-548868226, 550153460, ...}这种形式
    //否则会报类型转换的错误,但是单独给每个元素赋值就可以,不清楚原因,但是也算学到一招,避免以后遇到这种问题没法解决
    v[0] = -548868226;
    v[1] = 550153460;
    v[2] = 3774025685;
    v[3] = 1548802262;
    v[4] = 2652626477;
    v[5] = -2064448480;
    for(int i = 0; i <= 2; i++){//循环3次,每次传入两个uint32_t解密得到明文
        tea_dec(&v[2 * i], k);
    }

    for (int i = 0; i < 6; i++) {//根据题目提示,打印出明文的十六进制形式
        printf("%X", v[i]);//结果:666C61677B72655F69735F6772656174217D
    }

    //然后你可以找解密网站进行十六进制转字符串,也可以开个python脚本,直接运行下面这两行代码进行十六进制转字符串
    /*
    hex_data = '666C61677B72655F69735F6772656174217D'
    print(bytes.fromhex(hex_data).decode())
    */
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值