蜀道山CTF<最高的山最长的河>出题记录

出这道题的最开始感觉就是,因为现在逆向的形式好多,我最开始学习的时候,经常因为很多工具,或者手段完全不知道,就很懵逼,很多师傅都出了各种类型的,我就想着给以前的"自己"出一道正常exe,慢慢调的题,为了不那么简单,我就选择了C++(究极混淆,可能比rust好点),让大家无聊了,慢慢调着玩,哈哈,轻喷~

哈哈,出这道题的时候,那个时候BLG被干烂了,给我郁郁了一阵,就取了这个名字

其中

这里我把第一行给改了,大家随便找个正常exe改了就行,下面那里单纯是我写着玩的,哈哈,出题人的小乐趣

相信大家进来就可以找到主函数了,可能有些师傅会困惑,中间有个trycatch的地方,那个就是我拿来迷惑大家的,哈哈,然后你输入的长度不对,可能也会导致报错,可能也算另类的"反调试"?

这里为了方便,我就直接放源码吧,师傅们也可以拿去学习或者改一下出题也行

(以前学习很多都是白嫖很多师傅们的资源学习,传承一下下)

#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
using namespace std;
const std::string base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const vector<unsigned long> enc ={165,100,159,4,57,183,166,23,34,205,38,77,125,16,130,219,133,219,39,57,66,60,30,165,34,205,38,77,125,16,130,219,214,55,104,128,177,249,21,25,68,24,66,36,143,120,162,44,};
const uint32_t key[] = { 'L', 'Z', 'S', 'D', 'S' };
//LZSDS{how_how_how_how_how_ow_ow_ow!}
std::vector<uint32_t> sub1234(const std::vector<uint32_t>& a) {
    std::vector<uint32_t> b;
    for (uint32_t i : a) {
        b.push_back((i >> (3 * 8)) & 0xFF);
        b.push_back((i >> (2 * 8)) & 0xFF);
        b.push_back((i >> (1 * 8)) & 0xFF);
        b.push_back((i >> (0 * 8)) & 0xFF);
    }
    if (b.size()!=48 ){
        throw std::runtime_error("!!!what will happen?");
    }
    return b;
}

std::pair<uint32_t, uint32_t> tea(uint32_t v0, uint32_t v1) {
    const uint32_t delta = 0x9E3779B9;
    uint32_t sum = delta * 32;
    for (int i = 0; i < 32; ++i) {
        v1 -= ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]);
        v0 -= ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]);
        sum -= delta;
    }
    return std::make_pair(v0 ^ key[4], v1 ^ key[4]);

}

std::vector<uint32_t> to_tea(const std::vector<uint32_t>& a) {
    std::vector<uint32_t> result = a;
    std::vector<uint32_t> b;
    for (size_t i = 0; i < a.size() - 3; i += 4) {
        b.push_back((static_cast<uint32_t>(result[i + 3]) << (3 * 8)) +
            (static_cast<uint32_t>(result[i + 1]) << (2 * 8)) +
            (static_cast<uint32_t>(result[i + 2]) << (1 * 8)) +
            static_cast<uint32_t>(result[i]));
    }

    for (size_t i = 0; i < b.size() - 1; i += 2) {
        auto [v0, v1] = tea(b[i], b[i + 1]);

        b[i] = v0;
        b[i + 1] = v1;
    }
    return sub1234(b);
}

std::vector<uint32_t> encode2(const std::string& str) {
    std::vector<uint32_t> a(str.begin(), str.end());
    std::vector<uint32_t> b;
    std::vector<uint32_t> h;
    for (size_t i = 0; i < a.size(); i += 3) {
        b.push_back(a[i] & 0b111111);
        b.push_back(a[i+1] & 0b111111);
        b.push_back(a[i+2] & 0b111111);
        b.push_back((((a[i]>>6) & 0b11)<<4)+(((a[i+1]>>6) & 0b11)<<2)+((a[i+2]>>6) & 0b11));
    }//魔改tea

    for (int i = 0; i < b.size(); ++i) {
        h.push_back(base64_table[b[i]]);
        //        cout<<char(h[i]);
    }
    cout<<endl;
    std::vector<uint32_t> encrypted = to_tea(h);
    //    for (uint32_t i : encrypted){
    //        cout<<i<<',';
    //    }
    return encrypted;
}
int main() {
    std::string in_put;
    while (true) {
        cout<<"input your flag:\n";
        std::cin >> in_put;
        if (in_put == "end") {
            break;
        }
        try{
            vector<uint32_t> ans = encode2(in_put);
            for (int i = 0; i < enc.size(); ++i) {
                if (ans[i]!=enc[i]){
                    std::cout << "No!!!!" << std::endl;
                    exit(0);
                }
            }
            cout<<"yoxi! you are right!!!" << std::endl;
            return 0;
        }
        catch (const std::exception& e){
            cout<<"wow ! look where you are!"<<endl;
            cout<<"!!!what will happen?"<<endl;
            cout<<"come on!"<<endl;
            string flag;
            cin>>flag;
            cout<<"wrong!!"<<endl;
        }
    }
    return 0;
}

这里我就是自定义base编码过程,没有换表,肯定有很多师傅写了很多换表了,我们换换口味,就是标准表,嘿嘿嘿(中间解法多样,条条大路通罗马)

但是我看见有师傅是还是通过拿到的不一样的表搞出来的,最后那个可能有个多解.

毕竟加密都是对于flag,那样一步到位了(很有耐心的师傅)

这里我是改了base的编码过程,每一个取6位,让最后每个剩下的两个再组成一个数据,得到的base,拿去tea,tea有五个密钥,最后那一个就是异或了结果

然后这是我当时自己写的解密脚本,给师傅们借鉴下

#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
using namespace std;
const vector<unsigned long> enc ={165,100,159,4,57,183,166,23,34,205,38,77,125,16,130,219,133,219,39,57,66,60,30,165,34,205,38,77,125,16,130,219,214,55,104,128,177,249,21,25,68,24,66,36,143,120,162,44,};
const std::string base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned long key[] = { 'L', 'Z', 'S', 'D', 'S' };
std::vector<unsigned long> dsub1234(const std::vector<unsigned long>& a) {
    std::vector<unsigned long> b;
    for (int i = 0; i < a.size(); i += 4) {
            b.push_back(((a[i] << (3 * 8)) ) + ((a[i + 1] << (2 * 8))) + ((a[i + 2] << (1 * 8)) ) + (a[i + 3] & 0xff));
    }
    return b;
}
int find1(int a){
    for (int i = 0; i < base64_table.size(); ++i) {
        if (a==int (base64_table[i])){
//            cout<<i;
            return i;
        }
    }
}
std::vector<unsigned long> sub1234(const std::vector<unsigned long>& a) {
    std::vector<unsigned long> b;
    for (unsigned long i : a) {
        unsigned int t[4]={i&0xFF,((i >> (2 * 8)) & 0xFF),((i >> (1 * 8)) & 0xFF),((i >> (3 * 8)) & 0xFF)};

        b.push_back(find1(t[0]));
        b.push_back(find1(t[1]));
        b.push_back(find1(t[2]));
        b.push_back(find1(t[3]));
    }
    cout<<endl;
    return b;
}
std::pair<unsigned long, unsigned long> tea(unsigned long v0, unsigned long v1) {
    const unsigned long delta = 0x9E3779B9;
    unsigned long sum = 0;
    for (int i = 0; i < 32; ++i) {
        sum += delta;
        v0 += ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]);
        v1 += ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]);
    }
    return std::make_pair(v0 , v1 );
}
std::vector<unsigned long> to_tea(const std::vector<unsigned long>& a) {
    std::vector<unsigned long> b=a;
    for (size_t i = 0; i < b.size() - 1; i += 2) {
        auto [v0, v1] = tea(b[i]^key[4], b[i + 1]^key[4]);
        b[i] = v0;
        b[i + 1] = v1;
    }
    return b;
}

std::vector<unsigned long> decode(vector<unsigned long> str) {
    std::vector<unsigned long> m=dsub1234(str);//转换为八字节数据
    std::vector<unsigned long> encrypted = to_tea(m);//tea解密

    std::vector<unsigned long> a= sub1234(encrypted);//找到base
    std::vector<unsigned long> b;//转换成给tea的参数
    for (size_t i = 0; i < a.size(); i += 4) {
        b.push_back((a[i])+(((a[i+3]>>4)&0b11)<<6));
        b.push_back(a[i+1]+(((a[i+3]>>2)&0b11)<<6));
        b.push_back(a[i+2]+((a[i+3]&0b11)<<6));
    }
    for (unsigned long i : b){
        cout<<char(i);
    }
    cout<<endl;
    return encrypted;
}
int main() {
    decode(enc);
    return 0;
}

这道题其实厉害的师傅,也可以考虑一下爆破,如果patch题目hook一下,三字节爆破一下就出来了,如果不会的师傅也可以去在base阶段采取爆破的做法,爆破感觉比手撕快多了~

最后出题有任何不合理的地方,欢迎师傅们指正,有任何问题也可以私聊我,如果有想和我一起交流re,学习的师傅也很欢迎.(抱拳)

很多师傅没有复现出来,我就写个详解吧

源代码师傅们都看见了

逻辑是base -push-数据整合-totea-数据整理-flag

 点进去一个一个看

第一个啥也不是

第二个

这里进行base编码了

那么后面就该push了

应该这段吧(太久了忘了),具体函数操作,师傅们动调去看看

进来可能很难看,但是你注意for循环,师傅大概就可以理解了

       

大致加密地方都给师傅们找出来了

tea加密改了这个最后输出异或了

base的话

给师傅们改一下大致的id,最主要是结合动调去看

下班!不懂的师傅再call吧~

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

name_name123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值