实战 Python3.7+64位 Exe 反编译

记得有年在上海弘连培训,其中一个逆向题就是关于python的Exe,当时就想着写个文档,后来由于忙就拖延了下来;这里补上,并且是大补上:奉献一个干货,网上没有(我没发现)Python3.7的反编译教程,有的都是python2.7的,二者有一个关键的地方不一样(一层窗户纸),花费了一些时间才明白,无私地分享给你,这里是否是应该有掌声。html

 

    1、生成python3.7+64位Exe程序python

    在反以前要先编一个。用文本工具写个几行的python代码,如图:算法

    安装Pyinstaller是一个坑,我忙乎了半天,费了几回劲才成功,真是要看运气。安装成功后,用pyinstaller -F filename打包编译成exe。微信


    拖进exeinfo查看,果真是64位的;cookie


    运行下,能够运行,说明编译成功。python2.7

    在开工前,先准备好相关的知识,为后面的顺利进行打下基础。(后面文字有点长,耐心点)编辑器

 

2、Pyc、Pyd、Pyo、Pyz介绍函数

    (一)在实际开发中,Python做为解释型语言,在实际的代码分发过程当中,有比较多的格式定义:.pyc\.pyd\.pyo\.pyz工具


    ①.pyc文件是什么? python编译后的二进制文件优化

     

    Python源码编译的结果就是PyCodeObject(简称“代码对象”),每一个做用域会编译出一个对应的代码对象,其中名为co_codePyStringObject保存着代码对象的字节码。     


    一个Python源文件就是一个模块。每一个模块顶层的代码对象经过marshal序列化以后就获得了.pyc文件。marshallittle-endian字节序来序列化数据。     


    那嵌套于顶层做用域里面的那些做用域,例如函数、类的定义,它们对应的代码对象在哪里?它们每个都乖乖的躺在上一层做用域的代码对象的co_const(常量池)域里,因此其实顶层代码对象已经嵌套包含了底下其它做用域的代码对象。  

   

    PyCodeObject的结构和marshal的序列化逻辑和咱们反编译这块没有太大的关系,不介绍了,不然又是洋洋洒洒一大篇。 


    当导入一个模块时,类型为.pyc的文件将由解释器自动生成,这将加速该模块将来的导入。所以,这些文件仅在由另外一个.py文件或模块导入时从.py文件建立。


    注意,使用.pyc文件只会加快程序的加载速度,而不会加快程序的实际执行速度。这意味着您能够经过在一个模块中编写主程序来提升启动时间,这个模块由另外一个更小的模块导入。

 

    pyc主要写入三个内容:

    1).Magic num

    2).Pyc建立时间

    3).PyCodeObject.(python/marshal.c)


    因而pyc magic num的做用有三: 

    一是拒绝彻底不多是正常的.pyc的文件,例如普通文本,图片、音乐,或者别的二进制格式。检查文件的头4个字节已经能有效的筛掉许多无效文件;

    二是拒毫不慎被文本编辑器编辑而破损的文件;     

    三是拒毫不对应的Python解释器生成的.pyc文件。


    因为不一样Python版本的marshal算法可能不一样,虚拟机采用的字节码指令集也可能不一样,因此保守起见不一样版本的Python解释器生成的.pyc文件被认为是不兼容的。 


    Python在不一样的版本,pyc的头部长度和内容是不一样的:

     PEP 3147中指出:.pyc文件包含两个2字节Header(表示一个Magic Num和Timestamp),后面跟序列化的PyCodeObject每当Python改变字节码格式时,Magic Num会改变。Timestamp用于确保pyc文件与用于建立它的py文件匹配。Magic NumTimestamp不匹配时,将从新编译py文件并写入新的pyc文件。


    PEP 552中指出:.pyc头文件目前由4个字节组成。第一个字节还是magic number,对字节码和pyc格式进行版本控制。第二个字节为新增长的字段,将是一个位字段(bit field),对报头其他部分的解释和pyc的失效行为取决于位字段的内容。若是位字段(bit field)为0,则pyc是传统的基于时间戳的pyc;第三个和第四个字节分别是时间戳和文件大小,经过比较源文件的元数据和头文件中的元数据来进行无效判断。


    若是位字段的最低位被设置,则pyc是基于哈希的pyc。咱们将第二个最低位称为check_source标志,位字段以后是源文件的64位散列,咱们将使用带有源文件内容硬编码密钥;另外一个相似MD5或BLAKE2的快速散列也能够,咱们选择SipHash是由于Python已经从PEP 456中得到了它的内置实现,尽管容许选择SipHash键的接口必须公开给Python。


    如下是一些常见的Magic num:

     ②.pyo文件:文件类型也是由解释器在导入模块时建立的。可是,.pyo文件是在启用优化设置时运行解释器的结果。


    当咱们调用Python解释器时,经过添加“-O”标志来启用优化器。


    ③.pyd文件:文件类型是特定于Windows操做系统类平台的。所以,在我的版和企业版的Windows 十、Windows 7和其余版本中可能常常遇到这种状况。


    在Windows生态系统中,.pyd文件是一个包含Python代码的库文件,能够被其余Python应用程序调用和使用。为了使这个库对其余Python程序可用,它被打包为一个动态连接库。


    .pyd文件是一个动态连接库,它包含一个Python模块,或一组模块,由其余Python代码调用。要建立.pyd文件,须要建立一个名为example.pyd的模块。在这个模块中,须要建立一个名为PyInit_example()的函数。当程序调用这个库时,它们须要调用import foo, PyInit_example()函数将运行。


    ④.pyz文件: executable python zip archives具体内容参见下面的ZlibArchive


    (二)Python打包文件

    打包文件是包含其余文件的文件,例如.tar文件、.jar文件或.zip文件。PyInstaller中使用了两种存档。一个是ZlibArchive,它容许高效地存储Python模块,并经过一些导入钩子直接导入。另外一个是CArchive,相似于.zip文件,这是一种打包(或压缩)任意数据块的通用方法。它的名字来源于这样一个事实,即它能够很容易地从C和Python中操做。这两个类都来自一个公共基类,这使得建立新类型的归档变得至关容易。


    ①ZlibArchive:包含压缩的.pyc或.pyo文件。spec文件中的PYZ类调用建立了一个ZlibArchiveZlibArchive中的目录是一个Python字典,它的Key(import语句中给定的成员名)与ZlibArchive中的查找位置和长度相关联。ZlibArchive的全部部分都以编组格式存储,所以与平台无关。


    ZlibArchive在运行时用于导入绑定的python模块。即便使用最大压缩,这也比正常导入快。而不是搜索系统。路径,在字典里有一个查找。没有目录操做,也没有要打开的文件(该文件已经打开)。只有一次搜索,一次读取和一次解压。


    Python错误跟踪将指向建立归档条目的源文件(.pyc编译、捕获并保存到归档时的_file__属性)。这不会告诉您的用户任何有用的东西,可是若是他们向您发送Python错误跟踪,您能够理解它。


    ②CArchive:能够包含任何类型的文件。它很像一个.zip文件。它们很容易用Python建立,也很容易从C代码中解包。CArchive能够附加到另外一个文件,好比ELF和COFF可执行文件。为了实现这一点,存档是在文件的末尾用它的目录建立的,后面只跟一个cookie,它告诉目录从哪里开始以及存档自己从哪里开始。


    CArchive能够嵌入到另外一个CArchive中。内部存档能够在适当的地方打开和使用,而没必要提取它。


    每一个目录条目都有可变的长度。条目中的第一个字段给出了条目的长度。最后一个字段是相应打包文件的名称。名称以空结尾。压缩对于每一个成员都是可选的。


    还有一个与每一个成员相关联的类型代码。类型代码由自提取的可执行程序使用。若是使用CArchive做为.zip文件,则没必要担忧代码。


    ELF可执行格式容许将任意数据链接到可执行文件的末尾,而不影响其功能。所以,CArchive的目录在归档的最后。可执行文件能够以二进制文件的形式打开本身,查找到最后并“打开”CArchive


    3、反编译Exe过程

    由于是64位程序,用x64dbg载入查看,如图:

    发现PyInstaller等关键信息,能够确认是利用PyInstaller打包的python文件,因此咱们要想办法把python文件dump出来。


    从网上搜索下,发现有工具能够直接将pyinstaller打包的Exe直接反编译出来,拿来主义,直接用......,为了你们不走弯路,我直接给出正确途径,若是按照网上的教程,你要摸索半天。


    我没用网上介绍的Pyinstxtractor.py,够麻烦;我用的是用来提取的py脚本叫archive_viewer.py,将这个脚本文件和Exe放置在同一个目录下,

    python archive_viewer.py wei.exe

    出现以下图:

    在这图里,最重要的就是上面用红线标上的两个部分,如今咱们将它们dump出来,以下图:

    用x 命令将两个结构体导出,

会造成这两个文件,struct这个位置在0,因此是头部;


    咱们如今是将struct的头部嫁接到wei.pyc的头部,这里涉及到了pyc的头部格式问题,我花了很多时间,由于我是实战嫁接成功后才去找的缘由(理论做支撑);对一个没接触过的东西摸索入门确实要花费不少时间,并且过程很是难以忍受,难怪路遥在写完《平凡的世界》后第一件就是推开窗将手中的笔狠狠地扔了出去,我也有同感。

 

    咱们来看下导出的struct和pyc文件,当我打开pyc文件时,010editor提示要安装pyc.bt这个识别脚本,如图:

    确定是选择安装,我信任它;可就是这个脚本害苦了我,按这个脚本的格式头我怎么理解都相矛盾,且怎么嫁接都不成功,后来才发现这个脚本只能支持到python2.7,对后续的版本不支持,更别提3.7了,这也形成了我困惑好久。


    对比两个文件头部,咱们只要将struct的格式头插入到wei.pyc的头部,从上面的pyc的格式头咱们得知要插入16个字节的,当初没找到文件头的文档,致使走了很多弯路;插入完成后,如图:

    

    如今成为了一个完整的pyc格式的文件了,下面咱们要作的就是将pyc转换成py格式的,网上有不少的说明,这里我强调一下,不要用那个EasyPythonDecopiler,这个工具的效果并很差,其实有个网页提供了pyc在线反编译转换功能,挺好,

    到这里,反编译过程结束了,有机会我来说解下用IDA逆向python的exe文件,届时奉献给你们。


    这段时间连轴转,也蛮辛苦的;想一想疫情前线的医护人员,每时每刻都在同生死做搏斗,我就以为我要努力抓紧时间多作些力所能及的事情,才够资格向她们看齐。码字虽累,但边码字边陪着孩子,倒也其乐融融;若是您以为做者辛苦了,请看后点个赞,鼓励下!


    另,有些人给我留言,但愿能用上我写的那些个工具软件,我说能够,但有两个条件:一是我要认识你嘛,好歹你要找个熟悉人介绍吧;二是你必须是网安的,在(一)的基础上找我吧。



本文分享自微信公众号 - MicroPest(gh_696c36c5382b)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索