第二届“奇安信”杯网络安全技能竞赛Reverse | pyre(需要用到反编译工具 pyinstxtractor.py)

2023-12-16 10:20:05

赛题描述

这种exe文件怎么调用py的库?

题目附件:(下载可能会有问题,记得直接跳过下载就可以了)

抱歉无法处理您这个问题哦,您可以换个问题

PyInstaller Extractor 解包

  • 适用场景

制作exe后丢失源代码

  • 前提条件

使用pyinstaller 进行打包, 且未进行加密.

  • 安装

下载v2.0版本的PyInstaller Extractor

这是下载pyinstxtractor.py原地址:https://nchc.dl.sourceforge.net/project/pyinstallerextractor/dist/pyinstxtractor.py

计算机反编译是指通过对他人软件的目标程序(比如可执行程序)进行逆向分析和研究,以推导出他人软件所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,在某些特定情况下可能会推导出源码。

反编译可以作为开发软件时的参考,或者直接用于软件中。

如果找到了一个 Python 3.7 编译的 EXE 文件,则可以使用反编译获取源码,基本过程如下:

  • 将 EXE 文件转换成 PYC 文件;
  • 反编译 PYC 文件。


此过程需要反编译工具 pyinstxtractor.py,可以到 GitHub 官网下载,地址为:GitHub - extremecoders-re/pyinstxtractor: PyInstaller Extractor

图1

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图1:GitHub官网中的 pyinstxtractor.py

反编译的具体步骤为:

1) 使用 pyinstxtractor.py 将 EXE 文件转换成 PYC 文件,在命令行界面中输入下面的命令:

python pyinstxtractor.py helloworld.exe

然后按下 Enter 键,如图2所示。
?

将 EXE 文件转换成 PYC 文件


图2:将 EXE 文件转换成 PYC 文件


解压成功后,同路径下会出现 helloworld.exe_extracted 文件夹,这里面就包含了 PYC 文件。

2) 使用 uncompyle6 将 PYC 文件反编译为 PY 文件。uncompyle6 需要单独安装,安装命令如下:

pip install uncompyle6

安装后,使用 uncompyle6 进行反编译,在命令行界面中输入下面的命令:
uncompyle6 12.5_01.pyc > main.py
按下 Enter 键,如图3所示。
?

使用 uncompyle6 进行反编译


图3:使用 uncompyle6 进行反编译


成功后,生成 main.py,这样就完成了反编译。

正式解题步骤:

  • 使用
  1. 将需解包的exe与下载的pyinstxtractor.py存入同级文件夹

2. 使用命令行输入如下指令, 得到exe的解包

python pyinstxtractor.py {exe路径}
示例: python pyinstxtractor.py CreatFoder.exe

运行后多出一个目录

3. 在解包的文件夹下, 找到主文件进行反编译得到源码.

4. 关于pyc的反编译, 本文使用uncompyle6库, 大家也可以使用下方的在线反编译。

  • 在线反编译工具

在线pyc,pyo,python,py文件反编译,目前支持python1.5到3.6版本的反编译-在线工具

  • 本地库安装
pip install uncompyle6

  • 使用
  1. 在解包的文件夹下, 找到主文件

    2. 在同级目录下使用命令行输入如下指令

    uncompyle6.exe {pyc文件路径} >{py文件输出路径}
    示例: uncompyle6.exe .\CreatFoder.pyc >ppi.py 

    3. 解压完成, 源码get~

  2. 版本差异
  3. 使用PyInstxtractor 2.0以下版本进行解包, 需对目标文件补充magic head(表示python的版本和编译时间), 才能正确进行反编译.

    使用16进制模式查看主文件与主文件目录下的 struct 文件,需要在主文件头插入16个字节与 struct文件保持一致, 再使用uncompyle6进行反编译.

    (注意, 此说明来自网络, 答主并未按此步骤成功操

让我们思考如何优化这段代码:

  • struct文件第一行复制到1文件第一行1更名为1.pyc后在线工具进行编译在线工具

  • check = "flag{"
    c = [
    ? ? 144,
    ? ? ? ? 163,
    ? ? ? ? 158,
    ? ? ? ? 177,
    ? ? ? ? 121,
    ? ? ? ? 39,
    ? ? ? ? 58,
    ? ? ? ? 58,
    ? ? ? ? 91,
    ? ? ? ? 111,
    ? ? ? ? 25,
    ? ? ? ? 158,
    ? ? ? ? 72,
    ? ? ? ? 53,
    ? ? ? ? 152,
    ? ? ? ? 78,
    ? ? ? ? 171,
    ? ? ? ? 12,
    ? ? ? ? 53,
    ? ? ? ? 105,
    ? ? ? ? 45,
    ? ? ? ? 12,
    ? ? ? ? 12,
    ? ? ? ? 53,
    ? ? ? ? 12,
    ? ? ? ? 171,
    ? ? ? ? 111,
    ? ? ? ? 91,
    ? ? ? ? 53,
    ? ? ? ? 152,
    ? ? ? ? 105,
    ? ? ? ? 45,
    ? ? ? ? 152,
    ? ? ? ? 144,
    ? ? ? ? 39,
    ? ? ? ? 171,
    ? ? ? ? 45,
    ? ? ? ? 91,
    ? ? ? ? 78,
    ? ? ? ? 45,
    ? ? ? ? 158,
    ? ? ? ? 8
    ]
    tmp = []
    for i in range(len(check)):
    ? ? for j in range(1, 1000):
    ? ? ? ? if ord(check[i]) * 33 % j == c[i]:
    ? ? ? ? ? ? tmp.append(j)
    b = max(tmp, key=tmp.count)
    List = "0123456789abcdeflg{}-"
    for i in range(42):
    ? ? for tmp in List:
    ? ? ? ? if ord(tmp) * 33 % b == c[i]:
    ? ? ? ? ? ? print(tmp, end="")

  • 运行得到flag:flag{2889e7a3-0d6b-4cbb-b6e9-04c0f26c9dca}

  • 代码分析:

    首先,这段代码的主要目的是通过对给定的数字数组c进行某种转换,以找到与check字符串相匹配的字符,并最终输出一个结果字符串。

    让我们逐步分析这段代码:

  • 一个名为check的字符串被初始化为"flag{"
  • 一个名为c的数组被初始化,包含了一系列的数字。
  • 首先,我们可以注意到,在第一个嵌套循环中,对于每个字符,我们都重复进行了相同的计算多次,这是不必要的。我们可以直接计算每个字符的结果,并存储在一个字典中,以便后面直接使用。
  • 其次,由于最终要输出的字符是来自于List中的,我们可以在初始化时就将这个列表转换为一个字典,以ord(char)为键,char为值,这样就可以直接通过计算结果来查找对应的字符
    • 一个名为tmp的空列表被初始化,用于存储临时结果。
    • 第一个嵌套的for循环遍历check字符串的每个字符,并在内部循环中与数字1到1000进行某种运算。这里的主要目的是找到一个数字j,使得ord(check[i]) * 33 % j的结果等于c[i]。如果找到这样的数字,就将其添加到tmp列表中。
    • 找到tmp列表中出现次数最多的数字b
    • 在最后的循环中,通过遍历一个名为List的字符串列表,并再次使用相同的运算逻辑,找到一个字符,其ord(tmp) * 33 % b的结果等于c[i]。如果找到这样的字符,就打印出来。

文章来源:https://blog.csdn.net/Zhiyilang/article/details/134968208
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。