这是我的《逆向分析学习小纪》第三篇,这个系列主要介绍我的逆向分析学习过程,本篇涉及python编译中的保护手段、利用IDA工具完成静态分析、python解释器的解密和应用程序反编译等内容。希望和大家多多交流,共同进步!
(ps:前几天休了年假所以更晚了orz,大家见谅~)
随着近些年大数据、AI等技术的兴起,Python作为低门槛但又功能强大的编程语言频繁出现在了我们计算机领域人的眼前,就比如作为获取数据的手段——爬虫,相关程序员中80%以上基本都在用Python语言。并且,Python的社区环境十分友好,网上相关技术教程十分丰富,很多第三方库如Numpy、TensorFlow、Pytorch都很好用,各种Web、爬虫、数据分析、机器学习、深度学习框架应有尽有,如果用Python编程,会省去很多的基础实现步骤。
然而,利用Python编程是存在一定的安全隐患的,就比如和c语言开发相比,由于Python开放程度高,对基于其开发的应用程序进行逆向和反编译获取源码相对容易,所以为了保护自己的知识产权,绝大多数开发者会采取一定的措施来阻止用户获取到源代码。
大多数保护Python源代码的手段是利用加密、混淆等方案实现的,大致有以下五种:
- 一是生成.pyc文件,即利用Python标准库中的compile库对.py源码进行编译,形成.pyc二进制文件,防止用户直接看出源码逻辑(ps:但现在有很多现成的反编译工具,破解成本极低,后面会讲到);
- 二是结合加密算法,即利用AES、DES、RC4等对称加密算法,在生成.pyc的时候进行加密,在导入.pyc的时候解密,在第一种方法的基础上进一步强化源代码的保护;
- 三是代码混淆,通过一系列的转换,让代码变得逻辑混乱,不让人那么容易明白,例如加入无效代码和改变字节码顺序;
- 四是利用py2exe,将 Python 脚本转换为 Windows 平台上的可执行文件的工具,将源码编译为.pyc文件,加之必要的依赖文件,一起打包成一个可执行文件,提高破解门槛;
- 五是使用Cython,将.py编译为.c文件,再利用其进一步生成.so(linux)/.pyd(Windows)文件,有效防止用户破解。
在最近学习中,我遇到的情况就是前两种保护手段(.pyc+加密)相结合而形成的,如下图所示,利用十六进制编辑器(我这里用的是010 Editor,其他的比如UE等等也可以)打开.pyc文件可以看出该文件进行了加密操作,前四个字节是指示Python版本的Magic Number,03 F3 0D 0A表示当前Python版本为2.7,五至八四个字节是时间戳,后面的字节为程序内容,其中根本没有可读性的字符串(若不加密.pyc中部分内容可读性还是很高的),因此我们直接利用反编译工具是无法获得源码的,必须知道其加密逻辑并解密。
目前,我们对其使用的加密算法和如何解密是一无所知的,如果没有其运行环境或其他样本,可以说是一个很难的破密过程。数数手中的资源,还好我们有其可以完整运行的Python环境,这为我们分析加密过程提供了基础环境,因为在Python编译和导入.pyc的过程中,必须利用其内置库来加载这些模块,也就意味着加解密过程都在Python解释器内部完成,因此我们的整体思路如下:
首先,利用IDA将运行环境中的Python解释器打开,再打开一个同版本的未加密原始的Python解释器进行对比,从下图我们可以直观看出,两个解释器中,IDA分析得出的函数数量是不同的,所以可以进一步印证Python解释器确实做了改动,导致编译的.pyc文件加密。
然后,由于Python在导入.pyc文件时,需要利用其内置的import相关函数将.pyc文件加载,所以我们初步的想法是,在import的过程中,具备加密功能的解释器必然要对.pyc文件进行解密,在内存中还原出原始的.pyc文件,才能被计算机所识别读取,因此我们基于IDA对内置import函数进行分析。
在import过程中,必不可少的一步就是获取MagicNumber,如下图所示,利用IDA可以看到该函数名称为PyImport_GetMagicNumber,我们右键可直观看出该函数在哪些环节中出现(xrefs):
进而我们对PyRun_SimpleFile进行分析,可以看出,这部分函数就涉及了部分.pyc文件的加载和读取,随之而来的就是Python中的Marshal模块(红色方框内)。
简单说说Marshal模块~(小插曲,可跳过)
Python 标准库中的marshal 模块的对象序列化特性与pickle 模块类似。 但是,此模块不用于通用数据。 另一方面,它被 Python 本身用于 Python 的内部对象序列化,以支持对已编译版本的 Python 模块(.pyc 文件)的读/写操作。
marshal 模块使用的数据格式与 Python 版本不兼容。 因此,一个版本的已编译 Python 脚本(.pyc 文件)很可能不会在另一个版本上执行。
此时我们已经很接近真相了,利用IDA打开PyMarshal_ReadLastObjectFromFile(Marshal模块读取.pyc文件的函数),通过和原版Python2.7环境中的同名函数对比,我们可以直观看到二者不同,很明显这部分函数进行了修改,比原版大很多,如下图所示:
通过进一步分析,可以看到在该模块中就涉及了.pyc文件的加/解密操作,涉及RC4加密算法,通过分析算法逻辑可获取加密的密文和密钥(在IDA中点击变量即可显示):
最后,我们通过密文和密钥,可以解出原文,是Linux系统的一行命令,拿到该命令后,就可以将加密的.pyc文件恢复成原始未加密形态啦,恢复结果如下(由于不可抗力未全部展示请理解~),我们马上就要大功告成啦,接下来将进行最后一个环节!将.pyc反编译为.py文件。
“.pyc”文件是Python将.py源代码编译后生成的字节码文件,不具备直接让人理解的可读性,所以无法直接用文本/code编辑器打开查看其具体内容,但是互联网上有一些专门适用于.pyc文件的反编译工具,利用这些工具可以较为便捷地将.py源码还原:
- Uncompyle6:该工具最为常见,是支持跨版本的Python反编译器,由曾经的decompyle、uncompyle和uncompyle2升级继承而来,可以将Python字节码转换为等效的源代码,目前支持Python1.0-3.8(本文后续的反编译过程也将基于该工具进行),网址如下:Uncompyle6网址;
- Easy-Python-Decompiler:显而易见,该工具的名称翻译过来就是“简易Python反编译器”,适用于Windows环境,有图形化操作窗口,可以反编译.pyc、.pyo 文件,下载解压后可以直接运行;
- Decompyle++:该工具由C++编写,包括pycdas(字节码反汇编程序)和pycdc(反编译器),同样可以恢复.pyc文件,从github上的资料可知,该工具支持多个Python版本,网址如下:Decompyle++网址;
- 其他在线网站/工具:网上还有很多在线的网站/工具,也可以上传.pyc进行反编译,这里就不一一列举啦~。
在已有Python环境中,依托pip操作,直接单行命令就解决啦,如果速度很慢的话,建议大家可以挂载国内的依赖源(比如清华源:清华镜像源)进行下载安装:
#直接安装
pip install uncompyle
#依托清华源安装
pip install uncompyle -i https://pypi.tuna.tsinghua.edu.cn/simple
首先cd到.pyc文件所在的文件夹,使用如下命令进行反编译:
uncompyle6 sample.pyc
这样就会在该目录下生成反编译好的sample.py啦!!
本篇我是从实际遇到的Python代码逆向问题出发,基于IDA工具对受保护的Python代码进行逆向分析,完成其解密和反编译,整体学习过程中遇到了很多很多问题,由于我遇到的样本的特殊性和代码保护的多样性,可能部分观点比较片面,称不上全面和完善,不当之处敬请批评指正!
另外由于某些因素和避免不必要的麻烦,将部分内容进行了隐藏处理,还请大家理解~
与诸君共勉~
参考:
逆向分析学习小纪——IDA Pro工具的安装与基本使用