BeginCTF2024 RE WP 部分复现

文章讲述了在一场CTF挑战中,遇到的编码混淆和解密过程,涉及XXTEA加密算法,通过分析源代码和加密逻辑,逐步揭示了加密密钥和解密步骤。
摘要由CSDN通过智能技术生成
8. arc

上面一托混淆,左边似乎是三个东西相乘

单独取出最左边一托打印,可以得到大数组

接下来要解密,原代码非常混乱,我们先整理一下,简单去混淆

print
(
    all
    (
        [
            [data][a][d] == e 
            for a, b in enumerate
            (
                [
                    [int(a) for a in bin(sum((ord(b) << 6 ^ 4102 - a ^ c for c in b'beginCTF'))).replace('0b', '')] 
                    for a, b in enumerate
                     (
                        input('[+]Flag: ')
                     )
                ]
            ) 
            for d,e in iter #迭代器
            (
                (enumerate(b))
            )
        ]
    )
)

a应该是行数索引,b是输入的字符,经过bin(sum((ord(b) << 6 ^ 4102 - a ^ c for c in b'beginCTF')))转化成一个二进制数,应该要和大数组里对应行里的一行数字相同

data = [[1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0], [1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0], [1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0], [1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0], [1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0], [1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0], [1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0], [1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0], [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0], [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0], [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0], [1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0], [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0], [1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0], [1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0], [1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0], [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0], [1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0], [1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0]]
# 本文作者:Yuro@Yuro的博客
# 本文链接:https://yurogod.github.io/ctf/events/BeginCTF-2024/
# 版权声明:本站所有文章除特别声明外,均采用 (CC)BY-NC-SA 许可协议。转载请注明出处!
for i, lst in enumerate(data): #i是索引,lst是子序列(每一行)
    for j in range(32, 128): #爆破有效字符
        n = int("".join([str(x) for x in lst]), 2) #把每一行的数字连起来变成二进制,再转换为十进制
        if sum((j << 6 ^ 4102 - i ^ key for key in b'beginCTF')) == n: 
            print(chr(j), end="")
            break
#begin{Y@u_aRe_g00d_aT_play1ng_witH_sNake3}
9. stick game

源码里搜一下score

改一下,没吊用

中间有一坨,后来才知道是ob混淆

Obfuscator.io Deobfuscator

用网站去混淆

救赎之道,就在其中 begin{y0u_re4l1y_g07_1337427_74e96c14c0c79977b52a15ba559bcf30}

(似乎也可以在case6那里改分数)

10. superguesser

start函数往下翻,有一坨赋值,把数据按C键转成代码,去掉花(可能是花搞不清楚)——也并不能还原伪代码,不过可以大致看看

while这里进行了异或,可能是加密逻辑

if这里应该是判断比对密文

照理来说应该动调把加密逻辑调出来,但是太麻烦,不如先根据密文猜测

动调取一下密文,寻找memcmp函数调用的位置,找cmp指令

67行if下个断点,一路F7

进去,rdx,rcx寄存器里好像有字符串,但是没到cmp,往下走几步

拼接成了字符串

密文和flag头单字节异或一下

结合伪代码中的51

可以猜测每一位异或从51递增的数

#superguesser wp
enc = [0x51, 0x51, 0x52, 0x5F, 0x59, 0x43, 0x5D, 0x5F, 0x59, 0x49, 
  0x5A, 0x59, 0x56, 0x2E, 0x26, 0x1D, 0x2A, 0x37, 0x1A, 0x27, 
  0x29, 0x17, 0x28, 0x24, 0x2A, 0x38, 0x25, 0x21, 0x3D, 0x0F, 
  0x32, 0x3A, 0x3C, 0x3D, 0x36, 0x33, 0x2A]

exm = 'begin{'
flag = ''
#for i in range(6):
#    print(enc[i]^ord(exm[i]))
for i in range(len(enc)):
    flag += chr(enc[i]^(i+51))
print(flag)
#begin{debugging_is_an_anathor_choice}
11. not main

具体的原理在官方WP里讲了,看不太懂,就记录一下大致流程吧

主函数看起来是个tea,但是题目都告诉我们not main,说明这个不对

找一找有个Hander函数,一般是异常处理时跳转的函数

有个xxtea

应该是key

然后就是调密文,动调调不了,有反调试

请教dalao后得知关键代码在__scrt_common_main_sehinitterm

这个函数调用了这两个地址之间的所有函数,参数是两个函数指针

一个个看,点进第三个可以看到两个疑似密文的地址,点进第四个则是触发Handler的部分

第四个sub_51010实际上就是反调试的位置,也是Handler的调用位置

可以发现,一旦检测到被调试,Handler就不会触发

第三个sub_51000则是:

不知道哪一个是密文,X查一下引用

unk_48503C没有其他引用,unk_485018引用分别在Handler和main,有可能是main里的假密文

再看一下汇编,这里把offset unk_48503Coffset unk_485018的地址进行了异或。

另一处Handler内的unk_485018引用则再次进行了这个操作,也就是说等于没异或

所以如果真逻辑在Handler内,unk_485018就是没用的数据

真正的密文就是unk_48503C

由于TEA系加密每次需要的原始数据是2个32位无符号整数,所以取数据时注意一下

也就是8个数,两个一组,加密四轮

密钥是true

抄个XXTEA脚本跑一下——乱码

正确的流程应该是main先进入析构函数终进行一次 tea 加密,然后触发 veh 异常调用 veh 处理函数在其中进行一次 xxtea 加密

只不过tea加密用的是另一套密钥fake

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2)+(y>>3^z<<4))^((sum^y)+(key[(p&3)^e]^z)))
void btea(uint32_t* v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)
    {
        rounds = 6 + 52 / n;	//这里可以说是预定义值,n=2是rounds=32
        sum = 0;
        z = v[n - 1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p = 0; p < n - 1; p++)        //注意这里的p是从0~n-1
            {
                y = v[p + 1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n - 1] += MX;        //这里的MX中传入的p=n-1
        } while (--rounds);
    }
    else if (n < -1)
    {
        n = -n;
        rounds = 6 + 52 / n;
        sum = rounds * DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--)    //注意这里的p是从n-1~0,和上面是反过来的
            {
                z = v[p - 1];
                y = v[p] -= MX;
            }
            z = v[n - 1];
            y = v[0] -= MX;    //这里的MX中传入的 p=0
            sum -= DELTA;
        } while (--rounds);
    }
}
void detea(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;
    uint32_t delta = 0x9e3779b9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    }
    v[0] = v0; v[1] = v1;
}
int main()
{
    uint32_t v[8] = { 0xCFBE0F1B, 0x05F3083F, 0x4220E43B, 0x3383AFEE, 0xFA3237CE, 0xECADA66E, 0xA8D47CA7, 0xEFC51077 };
    uint32_t k[4] = { 116,114,117,101 };//true
    uint32_t fk[4] = { 0x66,0x61,0x6B,0x65 };//fake
    int i, n = 8;
    btea(v, -n, k);//负号是解密
    for (i = 0; i < 8; i += 2)
        detea(v + i, fk);
    printf("%s", v);//begin{not_main_is_matter!}
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值