2024帕鲁杯reverse复现

查看 98|回复 9
作者:xiaowaaa   
1. 茶:
1.1. 经典查壳
64位无壳软件:


图片.png (200.71 KB, 下载次数: 0)
下载附件
2024-4-29 22:29 上传

1.2. ida 启动


图片.png (154.58 KB, 下载次数: 0)
下载附件
2024-4-29 22:29 上传

main 函数还是比较复杂的,他的流程大概就是输入 flag,然后经过加密,在经过 if 语句判断与密文进行对比,我们使用 ida 的查询加密算法的插件来进行查询一下他的加密算法:


图片.png (50.19 KB, 下载次数: 0)
下载附件
2024-4-29 22:30 上传

他给出的是salsa20加密,但是结果并不是这个加密算法,所以还是要靠自己的经验来观察


图片.png (90.88 KB, 下载次数: 0)
下载附件
2024-4-29 22:30 上传

这个函数就是我们所要进行解密的函数,这个加密其实是 chacha20 加密,经过搜索可以得到 chacha20 加密他需要一个密钥 key,一个偏移量 ponce,其中偏移量是一个 12 位的,但是他给的是 16 位,经过动调发现,他只取了前 12 位,下面给出解密代码(使用 python 的库进行解密,这个 Crypto 是真的难安装)
from Cryptodome.Cipher import ChaCha20
enc_data = bytes.fromhex("f568c48912eed6dc520c7164f44b6378e1d0d3e248914fa8847b405a131f")
key = b"SGludDogSW1wcm92ZSBvZiBTYWxzYTIw"
nonce = b"Is_This_"
cc = ChaCha20.new(key=key, nonce=nonce)
print(cc.decrypt(enc_data))
flag 就是 flag{But_I_Like_ChaCha20_More}
chacha20加密参考文章:链接已丢失,需要补上
2. Pypl
记录一下这个 python 逆向的解决
2.1. 解包
首先可以观察到这个是一个 python 进行打包的程序所以使用 pyinstxtractor 解包。
我刚开始使用的是低于 2.0 的版本,这个解包出来的 pyc 不带后缀,而且也没有自动填补程序头首先将所要解包的程序放在与其 pyinstxtractor 放在一起,然后打开终端运行python pyinstxtractor.py 解包.exe  (格式),执行完毕会看到 successful,而且会生成一个文件夹:


图片.png (35.44 KB, 下载次数: 0)
下载附件
2024-4-29 22:33 上传

上面的文件夹就存放着我们解包的 pyc 文件,我们往他里去找根文件夹名字相同的文件。
然后如果没有 pyc 的后缀,我们手动添加,最后拉入到 010editor 中进行查看文件头。
如果缺少 magic 头,我们就应该重新修改,所以我们还是使用 2.0 以上的版本吧,方便快捷此时我们可以通过一个 dll 文件进行查询他的 python 版本


图片.png (92.61 KB, 下载次数: 0)
下载附件
2024-4-29 22:34 上传

所以这是一个python311的版本贴一下各个版本的magic头
enum PycMagic {
    MAGIC_1_0 = 0x00999902,
    MAGIC_1_1 = 0x00999903, /* Also covers 1.2 */
    MAGIC_1_3 = 0x0A0D2E89,
    MAGIC_1_4 = 0x0A0D1704,
    MAGIC_1_5 = 0x0A0D4E99,
    MAGIC_1_6 = 0x0A0DC4FC,
    MAGIC_2_0 = 0x0A0DC687,
    MAGIC_2_1 = 0x0A0DEB2A,
    MAGIC_2_2 = 0x0A0DED2D,
    MAGIC_2_3 = 0x0A0DF23B,
    MAGIC_2_4 = 0x0A0DF26D,
    MAGIC_2_5 = 0x0A0DF2B3,
    MAGIC_2_6 = 0x0A0DF2D1,
    MAGIC_2_7 = 0x0A0DF303,
    MAGIC_3_0 = 0x0A0D0C3A,
    MAGIC_3_1 = 0x0A0D0C4E,
    MAGIC_3_2 = 0x0A0D0C6C,
    MAGIC_3_3 = 0x0A0D0C9E,
    MAGIC_3_4 = 0x0A0D0CEE,
    MAGIC_3_5 = 0x0A0D0D16,
    MAGIC_3_5_3 = 0x0A0D0D17,
    MAGIC_3_6 = 0x0A0D0D33,
    MAGIC_3_7 = 0x0A0D0D42,
    MAGIC_3_8 = 0x0A0D0D55,
    MAGIC_3_9 = 0x0A0D0D61,
    MAGIC_3_10 = 0x0A0D0D6F,
    MAGIC_3_11 = 0x0A0D0DA7,
    MAGIC_3_12 = 0x0A0D0DCB,
    INVALID = 0,
};
参考资料: Python逆向全版本MagicNumber表
接下来就是准备将 pyc 反编译为 python 代码。
2.2 反编译
这里我尝试使用uncompyle6进行反编译,但是一直报错,我要不知道什么原因导致的,贴个图记录一下错误看看以后可以解决吗:


图片.png (30.12 KB, 下载次数: 0)
下载附件
2024-4-29 22:35 上传

所以我还是使用在线网站将其反编译了
使用这个可以解决得到代码:
#!/usr/bin/env python
# Version: Python 3.11
from Crypto.Util.number import bytes_to_long
def enc(key):
    R = bytes_to_long(b'Welcome To PaluCTF!')
    MOD = 2 ** 418
    R = R ^ R - 60 >> 24
    R = R ^ R - 60
下面解决这个加密,设计异或和位运算所以使用这Z3对其进行解密给一下代码:
from z3 import *
from Cryptodome.Util.number import *
def enc(key):
    R = bytes_to_long(b"Welcome To PaluCTF!")
    MOD = 2**418
    R = R ^ ((R - 60) >> 24)
    R = R ^ ((R - 60)
3. O2
3.1. 经典查壳:
32位elf程序
3.2. IDA启动
可以发现 ELF 的头文件报错了:


图片.png (30.54 KB, 下载次数: 0)
下载附件
2024-4-29 22:37 上传

所以我们找一个 32 位 ELF 文件参考一下:


图片.png (34.1 KB, 下载次数: 0)
下载附件
2024-4-29 22:37 上传

下面是正常的:


图片.png (46.26 KB, 下载次数: 0)
下载附件
2024-4-29 22:37 上传

所以我们修改一下,将偏移 0x0005 处的 01 修改为 02,意思是这是一个64位的程序:

1 32位程序
2 64位程序



图片.png (151.3 KB, 下载次数: 0)
下载附件
2024-4-29 22:38 上传

成功进入主函数但是我的 main 函数不能反编译,显示编译错误,这个我没查出来是什么原因。
网上教程说可以通过动调解决一下,就是找到出错的地址,然后F5反编译一下,然后返回就可以F5了试了试果然可以。
得到了主函数:
__int64 __fastcall main(int a1, char ** a2, char ** a3) {
    __int64 v3; // rax
    std::ostream * v4; // rbx
    __int64 v5; // rax
    _BYTE * v6; // rbp
    char v7; // si
    std::ostream * v8; // rax
    __int64 v9; // rdx
    size_t v10; // rbx
    __int64 v11; // r13
    __int64 v12; // rdx
    __int64 v13; // rbx
    __int64 v14; // rdx
    __int64(__fastcall * v16)(); // rax
    _BYTE v17[32]; // [rsp+0h] [rbp-68h] BYREF
    void * s1; // [rsp+20h] [rbp-48h] BYREF
    size_t n; // [rsp+28h] [rbp-40h]
    v3 = std::operator > ( & std::cout, "Hello! ", a3);
    v4 = (std::ostream * ) std::__ostream_insert > (v3, qword_607AF3F30340, qword_607AF3F30348);
    v5 = * (_QWORD * )( * (_QWORD * ) v4 - 24 LL);
    v6 = * (_BYTE ** )((char * ) v4 + v5 + 240);
    if (!v6)
        std::__throw_bad_cast();
    if (v6[56]) {
        v7 = v6[67];
    } else {
        std::ctype  ::_M_widen_init( * (_QWORD * )((char * ) v4 + v5 + 240));
        v7 = 10;
        v16 = * (__int64(__fastcall ** )())( * (_QWORD * ) v6 + 48 LL);
        if (v16 != std::ctype  ::do_widen)
            v7 = ((__int64(__fastcall * )(_BYTE * , __int64)) v16)(v6, 10 LL);
    }
    v8 = (std::ostream * ) std::ostream::put(v4, v7);
    std::ostream::flush(v8);
    std::operator > ( & std::cout, "Check Flag:", v9);
    std::operator >>  ( & std::cin, & unk_607AF3F30360);
    sub_607AF3F2D5C0(v17, & unk_607AF3F30360, & qword_607AF3F30340);
    sub_607AF3F2D6C0((__int64) & s1, (__int64) v17);
    v10 = n;
    v11 = qword_607AF3F30388;
    v12 = qword_607AF3F30388;
    if (n  0x7FFFFFFF) || v13 > ( & std::cout, "Wrong!", v14);
        goto LABEL_12;
    }
    std::operator > ( & std::cout, "Right!", v14);
    LABEL_12:
        std::string::_M_dispose(v17);
    return 0 LL;
}
观察一下,我们可以发现 sub_607AF3F2D5C0(v17, &unk_607AF3F30360, &qword_607AF3F30340); 就是加密函数
__int64 * __fastcall sub_607AF3F2D5C0(__int64 * a1, char ** a2, _QWORD * a3) {
    __int64 * v3; // rcx
    char * v4; // r12
    __int64 v6; // r15
    int v7; // ebp
    __int64 v8; // rax
    unsigned __int64 v9; // r13
    unsigned __int64 v10; // rax
    int v12; // [rsp+Ch] [rbp-4Ch]
    char * v13; // [rsp+10h] [rbp-48h]
    v3 = a1 + 2;
    *((_BYTE * ) a1 + 16) = 0;
    * a1 = (__int64)(a1 + 2);
    v4 = * a2;
    a1[1] = 0 LL;
    v13 = & a2[1][(_QWORD) v4];
    if (v4 != v13) {
        v6 = 0 LL;
        v7 = 0;
        while (1) {
            v9 = v6 + 1;
            v12 = ( * v4 + * (char * )( * a3 + v7)) % 128;
            v10 = a1 + 2 == v3 ? 15 LL : a1[2];
            if (v10
v12 = (*v4 + *(char )(a3 + v7)) % 128; 这一句就是主要的加密函数可以简单分析一下各个变量的意思
  • v4 就是我们输入的值,也就是 flag(enc)
  • a3 就是传入的 key,我们可以找到他也就是 v12=(enc+key)% 128

    所以我们进行解密:
    enc=bytes.fromhex("364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643")
    key=b'PaluCTF'
    flag=''
    for i in range(len(enc)):
        flag+=chr((enc-key[i%7])%128)
    print(flag)
    解释一下代码,方便下次自己使用。
    bytes.fromhex 是将连续的两个数转换为 hex:
    hex_string = '616263'
    byte_array = bytearray.fromhex(hex_string)
    print(byte_array)
    bytearray(b'abc')
    还有一种解法就是爆破,学习一下大佬的答案:
    from string import printable
    enc = bytearray.fromhex("364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643")
    key = "PaluCTF"
    for i in range(len(enc)):
        for c in printable:
            if (ord(c) + ord(key[i % len(key)])) % 128 == enc:
                print(c, end="")
                break
    这里的 from string import printable :
    # import string library function  
    import string  
    # Storing the sets of punctuation,
    # digits, ascii_letters and whitespace
    # in variable result  
    result = string.printable
    # Printing the set of values  
    print(result)
    输出结果:
    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+, -./:;?@[\]^_`{|}~
    返回一系列可打印的值,所以我们使用双重循环进行爆破就ok了
    答案是 flag{d80a0d76-23af-486e-a0bc-43a463eac552} 剩下两道不是很懂,再分析分析再写吧,帕鲁杯收获还是挺大的,对于加密算法也是又了解到一种。
    修改记录:
  • 20240429: 修正排版

    下载次数, 下载附件

  • liyezhi7742   

    图一个都没有成功贴上哦,建议使用 Markdown 语法并参考帮助修改:
  • 【公告】发帖代码插入以及添加链接教程(有福利)
  • Markdown 插件使用说明
  • 如何实现发帖时图文混排效果

  • yiting8   

    贴图没了,链接也没有打好(下次建议编译成文档上传)
    GunXiOu888   

    建议重新编辑一下哦
    nnelqw   

    重新编辑一下吧
    sundeheng   

    重新编辑下吧,图烂完了
    hellostarrysky   

    排版好乱啊,也都没图
    没头脑和温柚   

    厉害,学习到了!
    xiaowaaa
    OP
      

    友情提示 贴图错误
    没头脑和温柚   


    爱飞的猫 发表于 2024-4-26 02:37
    [md]图一个都没有成功贴上哦,建议使用 Markdown 语法并参考帮助修改:
    - [【公告】发帖代码插入以及添 ...

    抱歉抱歉,我不知道没传上去,马上改
    您需要登录后才可以回帖 登录 | 立即注册