索引
I – python 如何深入工作
– Ia – OpCodes
– Ib – PYC 文件
– Ic – Python 对象
II – 逆向和解压 PyInstaller
III – 逆向和解压 CxFreeze
IV – Python
V代码注入- 逆向和解压 PyArmor
VI – 如何保护
IIX – 一些额外的文档

逆向分析之破解和解压Python打包工具插图

Python 是如何深入工作的

在本章中,我将重点关注 Python 的低级方面,即。IE。解释器、python 堆栈和 python 线程。
我们将专注于每个人都拥有的解释器 CPython。
解释器从调用函数开始对比:

Py_Initialize();

这将初始化解释器和线程。
如果解释器初始化成功,那么我们就可以访问 Python 对象,并且解释器已准备好而且被解释为你好

OpCodes

OpCodes 或Operation Code 是python 解释器的汇编语言。
通常,当用本地语言编译程序时,代码由编译器直接翻译成汇编语言,再翻译成二进制文件供计算机执行。

逆向分析之破解和解压Python打包工具插图1

(在 c ++ 中打印 hello world 代码并使用 Visual Studio 2019 编译的程序)

但是对于 python,它是不同的,这里是相同的程序(大致)在 python 中编码并使用dis反汇编

逆向分析之破解和解压Python打包工具插图2

如果有人想要将这些字节码转换为十六进制并在 asm 中编码,甚至修改文件,应用程序就会出错并崩溃。因为计算机无法识别它必须询问的指令。

python 中的每个应用程序/脚本后面都有操作码,因为您还可以看到操作码相当精确,即使不一定知道它,您也可以轻松猜测应用程序的作用,甚至重新编码应用程序。

PYC 文件 pyc

文件只是 python 编译器文件,所以我们在上面讨论了操作码。

逆向分析之破解和解压Python打包工具插图3
Pyc的头文件HEx

这就是我们在十六进制编辑器中打开 pyc 文件时的样子

最重要的部分是模块的标题和结尾(实际上它们都是,但有时由于某些部分有缺陷,您可能会遇到问题) .
ps:头文件取决于生成脚本的python版本所以想一想

有人说这会提高执行速度,但我们今天知道这是错误的,它只是提高了初始化速度解释器。

Python 对象

早些时候我谈到了 Python 对象,但没有解释它是什么,现在我将解释它,所以请睁大你的耳朵。
为了避免过多的细节,你可以在 python 中做所有的 Python 对象,即。IE。变量、字符串、模块、查询,但在 c 中重新定义。
例如,python 中的字符串,将是 c 中的 struct PythonObject。

Reverse & Unpack PyInstaller


现在我们将进入问题的核心。
我将向您介绍两种技术,一种是手动的,另一种是自动化的,还有另一种方法,但我稍后会讨论。

手动方法

对于手动方法,如果软件是 64 位,您将需要 x32dbg 或 x64dbg,并且知道 CPython 是如何工作的。
启动您的 x32dbg 并使用 PyInstaller 运行脚本“compile”,然后转到 Breakpoints 窗口,右键单击 -> Add DLL BreakPoints,然后添加脚本为我使用的 python dll 将是python38.dll.

不仅仅是启动软件并等待它加载。

逆向分析之破解和解压Python打包工具插图4
OD

这是 dll 的入口点,现在我们将使用命令断点PyEval_EvalCode函数bp PyEval_EvalCode

该函数用于执行python操作码。

但在继续之前,我有需要解释一下 pyinstaller 是如何工作的

当您输入命令时pyinstaller script.py,pyinstaller 将编译您导入脚本的所有模块,复制您的脚本并在 python 操作码中编译它,并复制您的 dll python 版本,例如 python38.dll
之后它会“压缩”用 zlib 编译的脚本,这就是为什么当你在 Detect It Easy 中使用 pyinstaller 传递打包脚本时,它会输出为 zlib 存档。

所以,一旦断点设置,让我们回到我们的小脚本,您将启动它,直到您在堆栈中看到脚本的名称,如下所示

逆向分析之破解和解压Python打包工具插图5

如果你看到了,你是在右边的人看到它缺少更多的情况检查 ebp 寄存器,看看它是否有你的软件的字符串如果是,复制十六进制代码并修复模块的头部和结尾,如果不重申手术。
要复制脚本,您只需右键单击 -> 二进制 -> 复制并将其放入十六进制编辑器。
要修复标头,只需从另一个 base_library.zip 文件复制标头,如果模块的末尾损坏,则相同。

现在要转录操作码,建议使用uncompyle6,其他的工具也可以,不过个人认为没有这个好。
要安装它,没有什么比这更简单的了,您只需要输入pip install uncompyle6,然后转录您只需输入的操作码uncompyle6 script-unpacked.pyc

如果一切正常,我们将会看到以下结果:

逆向分析之破解和解压Python打包工具插图6

自动方法

自动化技术更为人所知,您只需要安装pyinstxtractor脚本即可从 exe 中提取 pyc 文件,然后将操作码转换为我上面做的。
这种方法有几个缺点,使用这种方法的人已经大多数对python和reverse一无所知,所以他们是skid,那么很容易欺骗脚本,我将向您展示。
Python:

MAGIC = b'MEI\014\013\012\013\016'  # Magic number which identifies pyinstaller
# in hex is 4D 45 49 0C 0B 0A 0B 0E
    def checkFile(self):
        print(self.MAGIC)
        print('[+] Processing {0}'.format(self.filePath))
        # Check if it is a 2.0 archive
        self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
        magicFromFile = self.fPtr.read(len(self.MAGIC))

        if magicFromFile == self.MAGIC:
            self.pyinstVer = 20     # pyinstaller 2.0
            print('[+] Pyinstaller version: 2.0')
            return True

        # Check for pyinstaller 2.1+ before bailing out
        self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
        magicFromFile = self.fPtr.read(len(self.MAGIC))

        if magicFromFile == self.MAGIC:
            print('[+] Pyinstaller version: 2.1+')
            self.pyinstVer = 21     # pyinstaller 2.1+
            return True

        print('[!] Error : Unsupported pyinstaller version or not a pyinstaller archive')
        return False

这是 pyinsxtractor 脚本的功能,它检查脚本是否确实是 pyinstaller 存档,为此它检查是否存在幻数,聪明的人已经理解了。您只需要更改幻数,以便脚本无法提取存档。绕过这个小技巧很容易,但看到大多数使用这个脚本的人是skid。

Reverse & Unpack CxFreeze

类似于 PyInstaller一样将向您展示两种技术,一个是手动的,另一个是自动化的,还有另一种方法,但我稍后会讨论。

手动方法

手动解压缩 cx_Freeze 下的脚本,只需转到 lib 文件,然后转到 library.zip。
打开 library.zip 后,查找名为“lenomduscript__main __.Pyc”的文件

逆向分析之破解和解压Python打包工具插图7

一旦你发现提取文件就足够了,主要是反编译字节码,这 要归功于 uncompyle6

自动方法

自动解压 cx_Freeze 下的脚本,只需安装我的 python 脚本,您可以在这里找到:cxFreezextractor。下载完毕后,将它放在与解压脚本相同的文件中想要并将 exe 拖到我很棒的 h4x0r 脚本上,然后让它发挥作用。接着你就可以使用 uncompyle6 反编译所有东西

Python 代码注入

现在对我来说最有趣的部分
是 CPython API,我们可以与 python 交互,甚至可以使用 c++ 与 python 中的代码交互,
这是一个例子:https://docs.python.org/3/extending/embedding.html
所以只需制作一个 dll 并使用 APIPyRun_SimpleString就可以调用函数、重新定义它们、注入 webhook 等……因此非常好使用。

PS:它也适用于不是用python制作的本机应用程序,您可以轻松地使窃取者几乎无法检测到

但是您需要为大多数应用程序提供一个特殊的注入器,以便在python的dll加载或pyarmor的dll时注入。

DWORD WINAPI MainThread(HMODULE hModule)
{
    Py_SetProgramName(L"pyinject");
    bool init = Py_IsInitialized();
    if (init)
    {
        PyEval_InitThreads();

        PyGILState_STATE s = PyGILState_Ensure();

        PyRun_SimpleString("def auth():\n   return True");

        PyGILState_Release(s);
    }
    CloseHandle(hModule);
    FreeLibraryAndExitThread(hModule, 0);
    return 0;
}

因此,用这个“解压” pyarmor也是一个不错的选择 

逆向与解压PyArmor

此外,我正在谈论解包,但我们将比其他任何事情都更要反汇编字节码函数。
因此,对于“解包”pyarmor,我们将采用顶级 dll 并使用允许您反汇编 Python 函数的 dis 模块。
由于这行代码,您只需要找到函数
import dis,os,inspect,sys\nscriptMem = inspect.getmembers(sys.modules[__name__])\nfor name in scriptMem:\n print(name)
或进行猜解,然后您只需使用此代码注入 dll
import dis\nprint(dis.dis(auth))
ps:您要反汇编的函数必须正在运行,因为 pyarmor 在运行时加密和解密字节码,因此如果函数未加载,因此未使用字节码,它将不会解密它们。
ps:在某些版本的pyarmor
ps2上使用此方法:您也可以将此方法用于pyinstaller或cx_Freeze

如何保护自己的python程序

要保护它没有3000个解决方案,您只需要使用像pyarmor这样强大的obfu或从阻止 LoadLibrary API,编写好您的身份验证系统,然后祈祷不会有人破解您的软件,不过攻防是相对的,没有绝对的安全。对于开发人员,您应该查阅有关文档的章节,特别是 defcon 的视频,该视频解释了除此线程之外的许多内容。
有了这个,大多数人 (skids) 将无法破解您的软件。