吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3765|回复: 21
收起左侧

[CTF] 【2025春节】解题领红包之五 Windows高级题 小白解题全流程

  [复制链接]
Tkazer 发表于 2025-2-18 18:07
本帖最后由 Tkazer 于 2025-2-18 18:13 编辑

吾爱2025-Windows逆向高级题-5

CTF逆向小白解题全流程,过程中可能有些点有问题,请大佬们多多指教。

考点:异步消息执行,变种tea展开、变种MD5、时间戳、Flag分段检查

解题过程

这一段是获取两个编辑框的内容,即uid和flag,然后flag要符合异或的那一系列条件,实际格式是flag{...}。

1.png

跟到这边发现有一系列函数,main_program里面的执行验证按钮后主流程,execute是main_program里面通过不同消息来执行不同命令的函数。(都是自命名的函数,仅代表个人想法)

2.png

execute函数

其他消息:将flag括号内数据进行unhex(如1122字符串直接转成0x11,0x22数据)

0x35消息:获取当前半小时整点时间戳数据。

3.png

0x55消息:通过利用变种MD5+Salt将解密完数据的前十六字节计算得到4字节数值。

4.png

0x25消息:unhex后数据进行解密(Decrypt函数)。

5.png

main_program函数

第一部分

获取flag括号内数据通过消息分发执行execute的unhex消息,然后再执行execute的Decrypt函数,解密unhex后的数据,将解密完的数据长度赋值给v12。

6.png

第二部分

将解密完数据的前16字节进行custom_MD5,得到4字节数据,然后判断解密后数据第17个字节开始四个字节是否和计算得到的4字节数据相等。

如果相等就再次判断v12,即解密后数据长度,判断是否等于20。

再调用execute的时间戳获取消息,得到8字节时间戳数据。

最后再检查解密后数据前8字节是否等于时间戳数据,以及第九个字节往后8字节是否等于编辑框输入的uid。

结论

输入的flag得是被和Decrypt相对于的加密函数进行加密后的数据,加密前格式:{半时整点时间戳(8字节),uid(8字节),Custom_MD5(前面十六字节)(4字节),0x04填充(四个字节)}

最后一部分填充会在下面Decrypt函数里面说明来由。

7.png

Decrypt函数(sub_7FF7FAC92C40)

要求unhex后数据长度要是8的倍数,且利用一系列计算得到v12这个数据,参与内部解密的Key生成,最后还要求解密完的数据符合一系列条件验证。

8.png

解密后数据条件验证

从这部分逻辑代码可以分析,他是将最后v8指向最后一个数据,然后v9赋值最后一个数据,然后v8循环递减,直到当前v8指向v8开始往前的第v9个指针结束,然后最后解密后数据长度=当前长度-v9。

已知解密后前面已经占用了20字节(时间戳+uid+md5),在main_program也已知解密后数据长度要等于20,所以可以知道这边v9必须等于4,所以v8等于4,最后这边一共占用4个字节,即{4,4,4,4},这样经过这边的验证最后的size才会等于20。

9.png

dec函数

将unhex后数据按8字节分块进行tea的解密,tea加密的Key由上一层传入的v12通过RC4得到,且每次解密Key都会变化(固定变化),直接动调就可以拿到几次解密用到的Key值。

10.png

下面一系列解密就是tea的解密,不过是展开,可以数出一共是12轮,且Delta直接可以通过两次sum的值相减得到(由于tea解密这边应该是加上sum,ida伪代码展示是减,但是实际计算后数值一样),B979379E就是tea解密用到的Delta。

11.png

所以就可以通过动调得到的几次Key和Delta写出tea的加密代码。

uint32_t key1[] =
{
    0xD7851B65,
    0x473457C1,
    0x1231F787,
    0x9ACD6D9A
};
uint32_t key2[] =
{
    0xB728E994,
    0x1746382E,
    0xC52D865C,
    0x10778A6E
};
uint32_t key3[] =
{
    0x7459F437,
    0x90D1E5D,
    0x779375B2,
    0xEFCB8541
};

void tea_encrypt(uint32_t v[2], const uint32_t k[4])
{
    uint32_t v0 = v[0], v1 = v[1], sum = 0;

    uint32_t delta = 0xB979379E;

    for (uint32_t i = 0; i < 12; i++)
    {
        sum += delta;
        v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    }

    v[0] = v0;
    v[1] = v1;
}

主解题流程

通过用c++实现这部分代码,获取时间戳数据(8字节)。

12.png

然后将uid转为8字节字节数据拼接到时间戳字节后面。

MD5值暂时填充4个0x00,将MD5值和4个0x04字节拼接上。

将完整数据进行tea_encrypt,再用flag{}包裹填入编辑框进行验证。

在MD5生成代码处,断点在箭头处,即可得到MD5四字节数据。

13.png

最终再重复上面步骤即可得到flag。

完整代码

#include <iostream>
#include <Windows.h>

void tea_encrypt(uint32_t v[2], const uint32_t k[4])
{
    uint32_t v0 = v[0], v1 = v[1], sum = 0;

    uint32_t delta = 0xB979379E;

    for (uint32_t i = 0; i < 12; i++)
    {
        sum += delta;
        v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    }

    v[0] = v0;
    v[1] = v1;
}

/*
输入格式:
flag
{
tea_enc
(
    timestamp 8字节
    uid 8字节
    md5 4字节
    0x04*4 4字节填充
)
}
*/

int main()
{
    // 动调得到的三个Key
    uint32_t key1[] =
    {
        0xD7851B65,
        0x473457C1,
        0x1231F787,
        0x9ACD6D9A
    };
    uint32_t key2[] =
    {
        0xB728E994,
        0x1746382E,
        0xC52D865C,
        0x10778A6E
    };
    uint32_t key3[] =
    {
        0x7459F437,
        0x90D1E5D,
        0x779375B2,
        0xEFCB8541
    };

    uint8_t timestamp_bytes[8]{};
    // 8字节下的UID
    uint8_t uid[]{ 0x50, 0x04, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00 };
    // 动调得到MD5四个字节,加上最后4个0x04填充
    uint8_t md5_and_pad[]{ 0xD2, 0x63, 0xE4, 0xE6, 0x04, 0x04, 0x04, 0x04 };

    // 半时整点时间戳计算
    FILETIME time{};
    DWORD64 timestamp{};

    GetSystemTimeAsFileTime(&time);

    memcpy((void*)(×tamp), (void*)(&time), 8);
    timestamp = 1800 * ((timestamp / 0x989680 - 0x2B6109100LL) / 0x708);
    memcpy((void*)(timestamp_bytes), (void*)(×tamp), 8);

    // 加密数据
    tea_encrypt((uint32_t*)timestamp_bytes, (uint32_t*)key1);
    tea_encrypt((uint32_t*)uid, (uint32_t*)key2);
    tea_encrypt((uint32_t*)md5_and_pad, (uint32_t*)key3);

    printf("flag{");
    for (int i = 0; i < 8; i++)
    {
        printf("%02X", timestamp_bytes[i]);
    }
    for (int i = 0; i < 8; i++)
    {
        printf("%02X", uid[i]);
    }
    for (int i = 0; i < 8; i++)
    {
        printf("%02X", md5_and_pad[i]);
    }
    printf("}");
    return 0;
}

心得

动调调试分析程序主体流程很重要,要先了解大概执行框架才能逐步往下层分析,且上层一些代码条件有助于下层的分析。

然后踩了一个严重的坑就是IDA伪代码里面的变量值和实际值一些情况下是不一样的,之前写题没在意那么多,这次很多地方都发现有这种问题,卡了我分析好久。所以关键代码段最好用汇编逐步分析,看实际数据的变化。

免费评分

参与人数 14吾爱币 +13 热心值 +13 收起 理由
52PJ658 + 1 我很赞同!
baiseyuyi + 1 + 1 用心讨论,共获提升!
xiaokeaihia + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
ck6102 + 1 + 1 热心回复!
ion1umd + 1 + 1 我很赞同!
liu1238 + 1 + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yur0 + 1 太强啦
bacikal + 1 + 1 我很赞同!
nanaqilin + 1 + 1 我很赞同!
jackyyue_cn + 1 + 1 用心讨论,共获提升!
skyes + 1 + 1 用心讨论,共获提升!
小菜鸟一枚 + 1 + 1 用心讨论,共获提升!
wgz001 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

nanaqilin 发表于 2025-2-19 13:32
我也深有体会,由其是打断点捕获变量值的时候,我的总结的做法是,定位变量的寄存器或者内存偏移地址,然后在汇编处找到合适的地方打断点,等断下来后,切回到IDA伪代码中进行查看
ion1umd 发表于 2025-2-23 14:36
同感,之前用ida做别的逆向题看伪代码时也会遇到那种直接把不可见字符插入伪代码的,而且ida还不会告诉你那是不可见字符
KeviseBY 发表于 2025-2-20 08:01
SWLang9696 发表于 2025-2-20 10:12
感谢分享,向大佬学习
Knm 发表于 2025-2-20 12:53
感谢分享,向大佬学习
wenjun612 发表于 2025-2-20 21:46
向大佬学习
es1231 发表于 2025-2-23 15:48
向大佬学习
Json852 发表于 2025-2-24 15:25
做了第一题就不知道了
feiye8 发表于 2025-2-24 20:36
感谢分享,向大佬学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-5-26 09:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表