首页 > 编程语言 >pyc逆向以及DASCTF里的ezpython复现

pyc逆向以及DASCTF里的ezpython复现

时间:2023-11-28 22:46:37浏览次数:57  
标签:pyc min float DASCTF num str inf ezpython dis

pyc文件结构分析

pyc文件是Python在解释执行源代码时生成的字节码文件,可以直接由Python虚拟机执行。重点了解了下文件头,还不太了解其他部分怎么出题。
参考python pyc 加花指令

pyc文件头

参考
深入理解 python 虚拟机:pyc 文件结构
Python代码保护 | pyc 混淆从入门到工具实现
magic number + 源代码文件信息 + PyCodeObject

  • Magic Number  4字节
  • Bit Field      4字节
  • 最后修改日期    4字节
  • 文件大小      4字节
  • CodeObject

magic number
可以直接从python库中找到。具体的路径是:Python根目录\Lib_bootstrap_external.py文件中。如果不清楚路径的话可以通过图中的命令查询。
image
因为magic number生成的方法在importlib库中的util文件中,通过util中的记录可以找到magic number是在同文件夹下的_bootstrap_external.py中存放
关于magic number更详细的信息可以参考Python Uncompyle6 反编译工具使用 与 Magic Number 详解

Bit Field一般全0(至少python 3.11中是这样),最后修改日期和文件大小对反汇编无影响。修复文件头关键是修复Magic Number。值得注意,一旦能进行反汇编,不论是否完全,都说明此时修复的magic number正确,反汇编不完全是后面字节码的问题

pyc逆向

鉴于有好用的工具,优先考虑使用工具反编译出源码(而非硬看字节码)常见的工具有uncompyle6, pycdc。二者的使用此处不再说明,不过更推荐pycdc,它支持更高Python版本(目前已支持到3.11),而且pycdc工程还提供了pycdas用于反汇编将pyc文件转换成字节码,贴个地址吧。pycdc | github

DASCTF2023 11月月赛ezpython

提示了用python3.11编译,因此uncompyle不支持,只能用pycdc。用010editor打开后发现文件头被覆盖成key:yuanshen,找了py3.11最新版本的magic number填上后可以反编译出源码,但是不完全。pycdc还原出的源码如下:

点击查看代码
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.11

import pyDes

def adjust_length(str):
    if len(str) < 8:
        str = str.ljust(8, '0')
    elif len(str) > 8:
        str = str[:8]
    return str


def yuanshen(array, start, end):
    num = len(array)
    dis = [
        float('inf')] * num
    tree = [
        False] * num
    parent = [
        -1] * num
    dis[start] = 0
# WARNING: Decompyle incomplete


def qidong(input, key, IV):
    cipher = pyDes.des(key, pyDes.CBC, IV, pad = None, padmode = pyDes.PAD_PKCS5)
    encrypted_data = cipher.encrypt(input)
    encrypted_hex_list = encrypted_data()
    return encrypted_hex_list


def main():
    data = [
        159,
        41,
        201,
        125,
        67,
        60,
        44,
        34,
        203,
        56,
        116,
        186,
        13,
        71,
        125,
        30,
        84,
        123,
        109,
        54,
        106,
        56,
        17,
        124,
        87,
        236,
        25,
        12,
        80,
        178,
        165,
        123]
    key = input('请输入key: ')
    if len(key) != 8:
        print('wrong key lenth!')
        exit()
    flag = input('请输入flag: ')
    array = [
        [
            0,
            float('inf'),
            float('inf'),
            1,
            3,
            4,
            float('inf'),
            float('inf'),
            float('inf')],
        [
            float('inf'),
            0,
            float('inf'),
            float('inf'),
            float('inf'),
            2,
            float('inf'),
            4,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            0,
            8,
            1,
            float('inf'),
            float('inf'),
            float('inf'),
            1],
        [
            1,
            float('inf'),
            8,
            0,
            3,
            5,
            1,
            2,
            float('inf')],
        [
            3,
            float('inf'),
            1,
            3,
            0,
            float('inf'),
            1,
            5,
            3],
        [
            4,
            2,
            float('inf'),
            5,
            float('inf'),
            0,
            float('inf'),
            1,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            float('inf'),
            1,
            1,
            float('inf'),
            0,
            float('inf'),
            5],
        [
            float('inf'),
            4,
            float('inf'),
            2,
            5,
            1,
            5,
            0,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            1,
            float('inf'),
            3,
            float('inf'),
            float('inf'),
            float('inf'),
            0]]
    t = yuanshen(array, 1, 8)
    IV = (lambda .0: pass# WARNING: Decompyle incomplete
)(t())
    IV = adjust_length(IV)
    check = qidong(flag, key, IV)
    if check == data:
        print('yes,yes,yes!!')
        return None
    ''.join('bad,bad,bad!!')

main()

提示有字节码不能识别,需要手动修复。找到对应位置看pycdas反汇编出的字节码可以慢慢修出来。最重要的是修复关键函数“yuanshen”,个人修复的结果如下

点击查看代码
# Source Generated with Decompyle++
# File: ezpython.pyc (Python 3.11)

import pyDes

def adjust_length(str):
    if len(str) < 8:
        str = str.ljust(8, '0')
    elif len(str) > 8:
        str = str[:8]
    return str


def yuanshen(array, start, end)://缺失函数
    num = len(array)
    dis = [float('inf')] * num
    tree = [False] * num
    parent = [-1] * num
    dis[start] = 0
    for _ in range(num):
        min_dis=float('inf')
        min_index=-1
        for v in range(num):
            if(tree[v]):
                continue
            if(dis[v]>=min_dis):
                continue
            else:
                min_dis=dis[v]
                min_index=v
        if(min_index!=-1):
            tree[min_index]=True
            for v in range(num):
                if(tree[v]):
                    continue
                if(array[min_index][v]!=float('inf')):
                    if(dis[min_index]+array[min_index][v]<dis[v]):
                        dis[v]=dis[min_index]+array[min_index][v]
                        parent[v]=min_index
                    else:
                        continue
                else:
                    continue
    if(dis[end]==float('inf')):
        return 0
    else:
        t=[]
        current=end
        while((current!=-1)):
            t.append(current)
            current=parent[current]
        
        
        return t[::-1]

def qidong(input, key, IV):
    cipher = pyDes.des(key, pyDes.CBC, IV, pad = None, padmode = pyDes.PAD_PKCS5)
    encrypted_data = cipher.encrypt(input)
    encrypted_hex_list = encrypted_data()
    return encrypted_hex_list


def main():
    data = [
        159,
        41,
        201,
        125,
        67,
        60,
        44,
        34,
        203,
        56,
        116,
        186,
        13,
        71,
        125,
        30,
        84,
        123,
        109,
        54,
        106,
        56,
        17,
        124,
        87,
        236,
        25,
        12,
        80,
        178,
        165,
        123]
    key = input('请输入key: ')
    if len(key) != 8:
        print('wrong key lenth!')
        exit()
    flag = input('请输入flag: ')
    array = [
        [
            0,
            float('inf'),
            float('inf'),
            1,
            3,
            4,
            float('inf'),
            float('inf'),
            float('inf')],
        [
            float('inf'),
            0,
            float('inf'),
            float('inf'),
            float('inf'),
            2,
            float('inf'),
            4,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            0,
            8,
            1,
            float('inf'),
            float('inf'),
            float('inf'),
            1],
        [
            1,
            float('inf'),
            8,
            0,
            3,
            5,
            1,
            2,
            float('inf')],
        [
            3,
            float('inf'),
            1,
            3,
            0,
            float('inf'),
            1,
            5,
            3],
        [
            4,
            2,
            float('inf'),
            5,
            float('inf'),
            0,
            float('inf'),
            1,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            float('inf'),
            1,
            1,
            float('inf'),
            0,
            float('inf'),
            5],
        [
            float('inf'),
            4,
            float('inf'),
            2,
            5,
            1,
            5,
            0,
            float('inf')],
        [
            float('inf'),
            float('inf'),
            1,
            float('inf'),
            3,
            float('inf'),
            float('inf'),
            float('inf'),
            0]]
    t = yuanshen(array, 1, 8)
    IV=""//修复时额外添加
    for i in t:
        IV.join(str(i))//缺失部分
    IV = adjust_length(IV)
    check = qidong(flag, key, IV)
    if check == data:
        print('yes,yes,yes!!')
        return None
    ''.join('bad,bad,bad!!')

main()

再贴下官方wp给出的源码吧
点击查看代码
# 初始化变量
num = len(array)
dis = float('inf')
tree = [False] * num
parent = [-1] * num

# 设置起始位置
start[0] = 0//疑似写错?

# 遍历范围
for _ in range(num):
    min_dis = float('inf')
    min_index = -1

    # 遍历节点
    for v in range(num):
        # 如果节点未被访问
        if not tree[v]:
            # 计算距离
            dist_v = dis[v]
            
            # 如果距离小于当前最小距离
            if dist_v < min_dis:
                min_dis = dist_v
                min_index = v

    # 标记节点为已访问
    tree[min_index] = True

    # 更新距离和父节点
    for v in range(num):
        if not tree[v]:
            new_dis = dis[min_index] + array[min_index][v]

            if new_dis < dis[v]:
                dis[v] = new_dis
                parent[v] = min_index

# 构建路径
current = end
t = []
while current != -1:
    t.append(current)
    current = parent[current]

# 反转路径
t = t[::-1]

# 返回路径
return t

此处不得不赞美gpt,修复好后把代码丢进去就能识别出是求图中起始点到终点最短路径的dijkstra算法
image

无向图是在array中用邻接矩阵的方式表示
image
可以根据邻接矩阵画图直接找最短路径:15736428
image
以上两图都转自官方wp
最后得出key是yuanshen,IV是15736428,用cyberchef即可解出flag。
hint:在des的cbc模式下,IV只影响前八位的取值,而flag的前七位已知,因此只需要爆破一位即可

花指令干扰pyc反编译

下篇更,光看了原理还没上手做

标签:pyc,min,float,DASCTF,num,str,inf,ezpython,dis
From: https://www.cnblogs.com/y0hv2y/p/17863148.html

相关文章

  • 2023版IDEA或PyCharm关闭时卡在Closing project
    当关闭IDEA或PyCharm时,提示“Closingproject”,并卡住很久。 原因之一:当项目的依赖文件没有下载或加载完成时,就会触发Closingproject。 方法一:菜单->File->Setting->Appearance&Behavior->SystemSettings->HTTPProxy->勾选“Manualproxyconfiguration......
  • pycham配置GitHub环境【一文了解window上GitHub的基本操作】
    基础用户设置【包含用户登录、密钥生成】网络配置外观->系统设置->https代理->检查连接我这里测试网址是GitHub,连接成功即可后续操作【不成功别找我,我也不知道】git安装【如果安装,请忽略】github登录凭证两者即可,我这里演示用token按照他的生成就好了,不是很难......
  • 【git】pycharm上推送代码到云仓
    1、pycharm中安装插件,并且重启2、出现shareprojectongitee,如果是初识提交,会有addfilesforinitialcommit修改代码后,提交......
  • 【git】pycharm上拉取云仓代码
    前言当我们在github上看到别人写的项目,想拉到本地学习下。如何用pycharm把git仓库的代码拉取到本地电脑呢?环境准备:1.本地电脑已经安装了git2.已经注册过github账号3.pycharmpycharm配置先自己注册github账号,本地安装git环境,打开Pycharm-File-Settings-Versioncontr......
  • pycharm贴心大tips
    一、自定义文件头模版【1】打开settingsFile-->Settings【2】PythonScriptsEditor-->FileandCodeTemplates-->PythonScript【3】参数说明${PROJECT_NAME}-当前Project名称;${NAME}-在创建文件的对话框中指定的文件名;${USER}-当前用户名;......
  • pycharm常用快捷键大全
    常用快捷键大全【1】基本编辑快捷键Ctrl+Space:基本的代码完成(类、方法、属性)Ctrl+Alt+Space:快速导入任意类Ctrl+Shift+Enter:语句完成Ctrl+P:参数信息(在方法中调用参数)Ctrl+Q:快速查看文档F1:外部文档Shift+F1:外部文档,进入web文档主页Ctrl+......
  • [二]开发工具Pycharm
    [二]开发工具Pycharm【1】官网https://www.jetbrains.com.cn/【2】下载Pycharm(1)选择Pycharm(2)下载下载安装包作为开发者来说,我们选择专业版进行下载但是需要注意的是,专业版是需要花钱的,并且可以支持免费试用30天但是对于开源社区贡献者和学生免费,如果是这类人,可以......
  • 【五】pycharm贴心大tips
    【一】自定义文件头模版【1】打开settingsFile-->Settings【2】PythonScriptsEditor-->FileandCodeTemplates-->PythonScript【3】参数说明${PROJECT_NAME}-当前Project名称;${NAME}-在创建文件的对话框中指定的文件名;${USER}-当前用户名;......
  • 【四】pycharm快捷键大全
    常用快捷键大全【1】基本编辑快捷键Ctrl+Space:基本的代码完成(类、方法、属性)Ctrl+Alt+Space:快速导入任意类Ctrl+Shift+Enter:语句完成Ctrl+P:参数信息(在方法中调用参数)Ctrl+Q:快速查看文档F1:外部文档Shift+F1:外部文档,进入web文档主页Ctrl+......
  • 【python入门之pycharm篇】--如何安装pycharm以及如何安装python解释器
    【一】Python解释器下载【1】Python官网详细方面可见下方链接了解pythonhttps://www.python.org【2】Python各版本解释器官网https://www.python.org/downloads/【二】Windows系统安装Python解释器【1】下载Python版本解释器现在已经更新到了3.13版本的Python解释器......