selph
selph
发布于 2023-11-30 / 117 阅读
0
0

[技能兴鲁2023 决赛 职工组]re-ezalgorithm

附件给了个exe,是pyinstaller打包的结果,没操作过这种程序,赛场上也没做出来,现在再来做一做(输在了没准备工具上)

环境准备

解包需要两个工具

  1. pyinstxtractor.py:exe -> pyc

  2. uncompyle6:pyc -> py

安装:

git clone https://github.com/extremecoders-re/pyinstxtractor.git
#通过清华镜像源,下载快
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uncompyle

如果python版本高于3.8,需要去做一些修改:

Windows下,修改uncompyle​包的脚本,在python的lib目录中,推荐用everything去搜索该文件(C:\Users\selph\AppData\Local\Programs\Python\Python311\Lib\site-packages\uncompyle6\bin\uncompile.py​)

搜索该关键词requires​,然后找到这些二元组,把我们自己用的python版本添加进去,保存即可正常使用

image

代码提取

exe->pyc:

.\pyinstxtractor-ng.exe filename.exe

pyc->py:

uncompyle6.exe -o . filename.pyc

逆向分析

拿到的是混淆过的脚本:

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)]
# Embedded file name: 1111.py


def f():
    sss = '这是什么算法!'


def OOO0OOooooooO000OOOOO000OOooooooO(OOOOOooooooO000OOOOO000OOooooooO):
    OOOOOooooooO000OOOOO000OOooooooO = [chr(x) for x in OOOOOooooooO000OOOOO000OOooooooO]
    for OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO in range(len(OOOOOooooooO000OOOOO000OOooooooO)):
        if OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO].isupper() == True:
            OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO] = chr((ord(OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]) - 52) % 26 + 65)
        else:
            if OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO].islower() == True:
                OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO] = chr((ord(OOOOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]) - 84) % 26 + 97)
            return [ord(x) for x in ''.join(OOOOOooooooO000OOOOO000OOooooooO)]


def OOO0OOOooooooO000OOOOO000OOooooooO(OOOOO0000OOOooooooO0000OOOOO000OOooooooO, OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO):
    j = 0
    OOOO0OOOooooooO000OOOOO000OOooooooO = ''
    for OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO in range(len(OOOOO0000OOOooooooO0000OOOOO000OOooooooO)):
        if OOOOO0000OOOooooooO0000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO].isalpha():
            OOOO0OOOooooooO0000OOOOO000OOooooooO = ord(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO[(j % len(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO))].upper()) - 65
            if OOOOO0000OOOooooooO0000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO].isupper():
                OOOO0OOOooooooO000OOOOO000OOooooooO += chr((ord(OOOOO0000OOOooooooO0000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]) - 39 - OOOO0OOOooooooO0000OOOOO000OOooooooO) % 26 + 65)
            else:
                OOOO0OOOooooooO000OOOOO000OOooooooO += chr((ord(OOOOO0000OOOooooooO0000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]) - 71 - OOOO0OOOooooooO0000OOOOO000OOooooooO) % 26 + 97)
            j += 1
        else:
            OOOO0OOOooooooO000OOOOO000OOooooooO += OOOOO0000OOOooooooO0000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]
    else:
        return OOOO0OOOooooooO000OOOOO000OOooooooO


def OOOOOooooooO000OOoooooooooooooooOOOOOOOOOO000OOooooooO(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO):
    s = len(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO)
    if s != 5:
        return 0
    for OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO in range(s):
        if OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO].islower() == False:
            return 0
        return 1


def check(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO):
    if OOOOOooooooO000OOoooooooooooooooOOOOOOOOOO000OOooooooO(OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO) == 0:
        return 0
    OOO = OOO0OOooooooO000OOOOO000OOooooooO([82, 115, 121, 114, 116, 127, 124, 104, 104, 105, 104, 106, 106, 105, 122, 104, 105, 124, 116, 122, 105, 104, 124, 103, 122, 103, 106, 106, 103, 122, 106, 103, 105, 106, 106, 115, 105, 106, 103, 115, 97, 113, 125])
    arr = 'Aixit{815r1736-128j-d184-0734-eg67sed34nad}'
    OOOO0OOOooooooO000OOOOO000OOooooooO = OOO0OOOooooooO000OOOOO000OOooooooO(arr, OOOOOooooO0OOoooooooooooooooOOOOOOOOOO000OOooooooO)
    for OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO in range(len(OOOO0OOOooooooO000OOOOO000OOooooooO)):
        OOOOOooooO000OOoooooooooooooooOOOOOOOOOO000OOooooooO = ord(OOOO0OOOooooooO000OOOOO000OOooooooO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO])
        if OOOOOooooO000OOoooooooooooooooOOOOOOOOOO000OOooooooO | 68 != OOO[OOOOOooooOoooooooooooooooOOOOOOOOOO000OOooooooO]:
            return 0
        return 1


if __name__ == '__main__':
    print('欢迎来到ctf登陆系统')
    OOOOOooooOooooooooooooOOOOOOOOOO000OOooooooO = input('请输入用户名:')
    OOOOOooooOooooooooooooOOOOOOOO000OOooooooO = input('请输入密码:')
    OOOOOooooOooooooooooooOOOOOOOOOO000OOooooooO += OOOOOooooOooooooooooooOOOOOOOO000OOooooooO
    if check(OOOOOooooOooooooooooooOOOOOOOOOO000OOooooooO) == True:
        print('登录成功!flag的格式为flag{xxxxx-xxxxxx-xxxxx-xxxxxxxxx}')
    else:
        print('登录失败')

重命名一下变量:

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)]
# Embedded file name: 1111.py


def f():
    sss = '这是什么算法!'


def fun1(param1):
    param1 = [chr(x) for x in param1]
    for i in range(len(param1)):
        if param1[i].isupper():
            param1[i] = chr((ord(param1[i]) - 52) % 26 + 65)
        else:
            if param1[i].islower():
                param1[i] = chr((ord(param1[i]) - 84) % 26 + 97)
    return [ord(x) for x in ''.join(param1)]


def fun2(arr, userinput):
    j = 0
    res = ''
    for i in range(len(arr)):
        if arr[i].isalpha():
            index = ord(userinput[(j % len(userinput))].upper()) - 65
            if arr[i].isupper():
                res += chr((ord(arr[i]) - 39 - index) % 26 + 65)
            else:
                res += chr((ord(arr[i]) - 71 - index) % 26 + 97)
            j += 1
        else:
            res += arr[i]
    return res


def fun3(param1):
    s = len(param1)
    if s != 5:
        return 0
    for i in range(s):
        if not param1[i].islower():
            return 0
    return 1


def check(userinput):
    if fun3(userinput) == 0:
        return 0
    target = fun1([82, 115, 121, 114, 116, 127, 124, 104, 104, 105, 104, 106, 106, 105, 122, 104, 105, 124, 116, 122, 105, 104, 124, 103, 122, 103, 106, 106, 103, 122, 106, 103, 105, 106, 106, 115, 105, 106, 103, 115, 97, 113, 125])
    print(target)
    arr = 'Aixit{815r1736-128j-d184-0734-eg67sed34nad}'
    res = fun2(arr, userinput)
    for i in range(len(res)):
        if ord(res[i]) | 68 != target[i]:
            return 0
    return 1


if __name__ == '__main__':
    print('欢迎来到ctf登陆系统')
    buf = input('请输入用户名:')
    buf2 = input('请输入密码:')
    buf += buf2
    if check(buf):
        print('登录成功!flag的格式为flag{xxxxx-xxxxxx-xxxxx-xxxxxxxxx}')
    else:
        print('登录失败')

这里输入的用户名和密码会拼接成为一个key,作为参数进入check函数进行校验

fun3限制用户输入,必须是5个字节,且全都是小写

fun1会固定生成一个数组target

fun2会处理用户输入生成一个数组res

然后把res数组或一个68,然后和target数组比对,如果全相同,则表示验证成功

由于或操作的存在,逆向算法存在一定困难,所以可以采用暴力破解的方式:

import string

res = [69, 102, 108, 101, 103, 127, 124, 117, 117, 118, 117, 119, 119, 118, 109, 117, 118, 124, 103, 109, 118, 117, 124, 116, 109, 116, 119, 119, 116, 109, 119, 116, 118, 119, 119, 102, 118, 119, 116, 102, 110, 100, 125]
arr = 'Aixit{815r1736-128j-d184-0734-eg67sed34nad}'

def fun2(arr, userinput):
    j = 0
    res_ = ''
    for i in range(len(arr)):
        if arr[i].isalpha():
            index = ord(userinput[(j % len(userinput))].upper()) - 65
            if arr[i].isupper():
                res_ += chr((ord(arr[i]) - 39 - index) % 26 + 65)
            else:
                res_ += chr((ord(arr[i]) - 71 - index) % 26 + 97)
            j += 1
        else:
            res_ += arr[i]
    return res_


def brute():
    for i in string.ascii_lowercase:
        for j in string.ascii_lowercase:
            for k in string.ascii_lowercase:
                for l in string.ascii_lowercase:
                    for m in string.ascii_lowercase:
                        res_ = fun2(arr, i + j + k + l + m)
                        for n in range(len(res_)):
                            if ord(res_[n]) | 68 != res[n]:
                                break
                            if n == len(res_) - 1:
                                print(i + j + k + l + m)
                                return

brute()

提取fun1计算结果,和fun2函数

因为输入就5个字符,所以就爆破5个字符的内容,如果前5字符符合了,后面生成的结果也会符合

结果:admin

答案:flag{815r1736-128g-r184-0734-wt67sbr34fnd}


评论