这是我们的实训项目,ASPACK和UPX的脱壳。
实训项目–ASPACK与UPX脱壳
前端——migrating to eel
避免使用PyQt等野蛮图形界面进行编程。因此尝试用electron和Python结合。
fyears/electron-python-example: Electron as GUI of Python Applications (github.com)
搭建流程就跟着教程走就好了。
尝试用webflow设计网页前端(失败)
完全不会用
后端(脱壳部分)
用的是这个项目。
大抵就是用unicorn模拟执行,然后运行到真正的起始点后把内存dump出来,再patch IAT表什么的。速度确实不如一般的实际执行的脱壳器要快,但是感觉挺安全的。
环境搭建
先git把项目源码拉下来
然后在项目文件夹下执行:
1 | pip3 install -e . |
这样之后,在项目文件夹下打开shell,导入unipacker
包,调用的就是项目文件夹内的了,而不是Python默认库里的。
版本1——基础脱壳功能
实现了基础脱壳功能。
patch1——解决utf8字符集问题
yara.compile
直接传入字符串,没法应对utf8的字符,比如我的中文路径。因此魔改了一下:
1 | try: |
python的原生open函数能解决utf8字符,所以直接传file handle就好了、
还有下面的rules.match
也是有这个问题。(整个yara库是不是都没考虑utf8字符路径情况)
1 | try: |
patch2——传入完整文件路径;指定脱壳后路径
写了两个简单的工具函数用于解析全局路径成文件夹路径,文件名,后缀
现在的unpack
函数只需要传入一个全局文件路径就可以了,当然也可以再指定额外的脱壳文件夹路径。
版本2——实现设计目标1,3
判断是否有压缩壳:本身就已经实现了,不过这里用logging模块单独拖出来利用。
1 | import logging |
disclaimer——关于目标2
我个人认为2是不可能成功的。
patch3——解决unpack后还被识别为加壳程序
ASPACK样本被unpack后,还保留了.aspack
段,导致yara和DIE的识别算法都还是会把它识别为加壳程序。
- 方案一:只要文件名有unpack标识符,便直接跳过
- 方案二:删掉
.aspack
段,用pefile模块或者直接在unipack里面看看
暂时用第一种方案吧,第二种有点麻烦。
patch4——解决解压UPX时的一个bug
core.py中有一行
1 | self.uc.mem_write(self.sample.BASE_ADDR, self.sample.loaded_image) |
这个第二个参数传入的应该就是bytes对象,而不能是bytearray。
因此改成:
1 | self.uc.mem_write(self.sample.BASE_ADDR, bytes(self.sample.loaded_image)) |
版本3——实现多线程
就用multiprocessing
来起多线程
1 | def unpack_dir_multiproc(dir_path:str, proc_num=1): |
版本4——前端整合(基于eel)
PS:前端好丑
patch5——chrome解决文件上传目录不全问题
默认情况下,eel基于的浏览器时chrome。而chrome有文件上传的安全设置,导致文件路径是不全的。
Edge放弃
换成edge的话,会直接弹到edge浏览器上,不知道打包后是不是也是这样,但是现在的话确实就变成web页面了。而用chrome是独立的一个APP样式的页面。
electron可行
electron似乎没这些限制,但是配置起来好麻烦,报错了。可能是没全局安装electron吧。在管理员shell里面执行下:
1 | npm install -g electron |
网速太慢了换个源:
解决Electron安装很慢的办法 - 掘金 (juejin.cn)
1 | npm config edit |
还是报错了
How to use eel with electron using mode=’electron’? · Issue #491 · python-eel/Eel (github.com)
[Eel/examples/09 - Eelectron-quick-start at main · python-eel/Eel · GitHub](https://github.com/python-eel/Eel/tree/main/examples/09 - Eelectron-quick-start)
如果本地的electron出错的话,那么这个example也不可能跑起来,但是这个确实成功了,说明是项目的组织有问题。
重新整理了一下结构:
- up.py(后端:Python脱壳核心脚本)
- main.js(启动electron的默认代码)
- web(dir)
- index.html(前端页面)
- renderer.js(前端页面处理逻辑)
当然,关于文件上传问题,参考了一下下面chrome的链接,可能起到了作用。(或者说electron由于目标是本地APP,根本没像chrome那样有限制)
chrome放弃
chrome 文件上传路径问题_上传成功怎么才能不返回文件路径_一日一夜呃呃的博客-CSDN博客
1 | Internet选项 -> 安全 -> 自定义级别 -> 找到“其他”中的“将本地文件上载至服务器时包含本地目录路径”,选中“启用”即可 |
还是有问题,不管了。
版本5——添加基于实际执行的脱壳器
添加原生UPX脱壳器的利用
模拟执行的脱壳速度真的是有点慢。
Release v4.0.2 · upx/upx · GitHub
这里添加一个mode选择,选择模拟执行+直接脱壳
ASPACK还没有直接脱壳的程序。
版本6——class化
将代码稍微进行了重构,变成了class的样子。
但是这样,就导致eel的跨进程调用变得更加的tricky了。
首先在py文件里面,需要进行一定的修改:首先方法中不能直接使用self
,必须用全局变量(这里是APP
)获得类A的一个引用,然后在方法里面用APP
来进行操作。
因为在这里APP
就跟self
的作用是一样的,都是对自身类的一个引用。
比如:
1 | APP = A() # 全局变量APP便是对类A的引用,约等于self |
然后,js那边也要稍微改动一下。
1 | eel.func(null); |
即,暴露出来的类方法的第一个参数self,
在Js接口这边,是不能匿名传递的,必须得传一个东西。一开始我是想设计A.get_self()
这种自己指向自己的玩意的,不过发现一开始函数定义的时候也得传入一个匿名self
,因此不可行。所以选择直接改成传null,然后在python中进行修改,用全局变量而不是self
来传递自身。
当然,我猜测,如果能用eel直接暴露类中的self,应该也可以做到类似的效果,但是暂且不去深究。
稍微参考了一下: Using Python OOP with Eel · Issue #141 · python-eel/Eel · GitHub
版本7
后端也是有一些bug要改进的。
- 安装包:安装,卸载
- 后端的ASPACK模拟执行脱壳太慢了
- 识别算法改进:用DIE
version1——DIE
添加文件夹logs
,tests
添加文件test_identifypacker.py
。
up.py改了点东西
1 |
|
version2——junpacker(未完成)
这玩意还是等等再搞吧,我发觉speakeasy居然还是用模拟执行实现的,说到底还是效率不行啊。
mandiant/speakeasy: Windows kernel and user mode emulation. (github.com)
1 | git clone https://github.com/mandiant/speakeasy.git |
然后里面一个unicorn版本与我现有的angr冲突了,逆天
1 | cd speakeasy |
执行
1 | python3 -m pip install -r requirements.txt |
然后在一个admin的Powershell里面执行
1 | python3 .\setup.py install |
mdrngrng/junpacker (github.com)
version3——clamav(未完成)
Release ClamAV 1.1.0 · Cisco-Talos/clamav (github.com)
这个工具里面好像有用C实现的Unpacker。
version4——RHUnpacker(成功)
从UnAspack里直接抠出来代码用。
实现方法是动态执行,断点中断dump。
1 | DEFAULT_ASUNPACKER_PATH = f"{os.path.dirname(upacker.__file__)}/Tools/RHUnpacker.exe" |
设计目标
- 自动判断给定大量的样本是否具有压缩壳,并对应脱壳√
- 支持对多次加壳的样本进行脱壳×
- 支持脱压缩壳upx3.91 aspack2.12√
- 有可视化界面操作,对指定目录文件判断脱壳
- 可批量脱壳,递归子目录√
- 多线程处理,快速脱壳√
- 可显示脱壳进度,消耗时间和剩余时间
- 支持操作系统windows XP sp3以上
Bug Fix
zeromq库(失败)
每次执行程序就弹出来控制台,看了一下里面有个报错,似乎就是这个zeromq
找不到的原因。然后再一查,发现与zerorpc
有关联,而这个是与Python交互的核心模块。因此必须得修。
先执行
1 | npm install zeromq |
解决方案:用eel库,不用魔改这个demo了,太野蛮了
问题总结
- 解决yara模块存在UTF8字符集问题
- 解决Unpack后程序还被识别为加壳程序的问题
- 解决UPX解压模块的一个Bug
- 解决文件路径上传不全的问题
- 解决了Python OOP与前端交互的问题
- 解决了Pyinstaller打包的问题
打包基于eel的Python Web GUI程序
Demo打包
demo项目结构如下:
- try_eel.py
- web
- index.html
在项目文件夹下执行
1 | python3 -m eel .\try_eel.py .\web\ |
生成了一大堆玩意,不过反正最后成功了。
项目打包
项目稍微比Demo项目要复杂亿点点。
模块重命名
首先我们一开始基于的项目是Unipacker。但是由于我的Python原生也下载了这个库,于是导致总是冲突。所以选择直接把项目里的模块名改成Upacker,便于区分。现在执行就不会有依赖冲突的问题了。
简化项目
复制一份项目作为备份,然后对项目里的文件进行简化(删掉一些无用的玩意)
现在的项目构成:
- up.py
- upacker
- 一堆代码文件
- Tools
- upx.exe
- web
试验一下:
1 | python3 -m eel .\up.py .\web\ |
我猜测会有问题,因为我觉得upx.exe应该不会被导入进来。但是先这样执行看看。
1 | .\dist\up\up.exe |
看上去没导入capstone。
下面用.spec
文件解决问题。
首先看一下自动生成的.spec
里面有啥:
1 | # -*- mode: python ; coding: utf-8 -*- |
本质上用python3 -m eel ....
底层调用的还是PyInstaller,只不过加了个:
1 | datas = [('D:\\Environment\\Python37\\lib\\site-packages\\eel\\eel.js', 'eel'), ('.\\web\\', '.\\web\\')] |
的玩意。
直接修改这个看看:
首先需要再把我们的upacker添加进来:
1 | datas += [ |
kivy - Pyinstaller adding data files - Stack Overflow
当然,也有可能一开始就已经放入up.exe里面了,但是还是这样加了再说。
1 | pyinstaller .\up.spec |
然后我觉得可能还是不够,capstone,unicorn这些模块都没被主动加入。
1 | python3 -m eel .\up.py .\web\ --collect-all capstone --collect-all unicorn --collect-all pefile --collect-all colorama --collect-all ctypes |
执行上面这个指令会生成spec文件,但是不完整。需要再把upacker那个单独填上去(主要是因为--add-data
选项不会填)
1 | # -*- mode: python ; coding: utf-8 -*- |
再加上:–noconfirm和-F:
1 | python3 -m eel .\up.py .\web\ --collect-all capstone --collect-all unicorn --collect-all pefile --collect-all colorama --collect-all ctypes --noconfirm -F |
1 | # -*- mode: python ; coding: utf-8 -*- |
确实能生成一个exe文件
但必须得在我的Windows PowerShell上面执行才能启动?我看了一下似乎是缺少electron了。(蚌埠住了)
没办法,本地下载一个electron,然后再collect。(大小应该会变大很多很多)
1 | npm install electron |
下载得到node_modules文件夹,里面一大堆文件。然后在spec里面添加:
1 | datas += [ |
1 | # -*- mode: python ; coding: utf-8 -*- |
在此之前我还要修改一下源码:
1 | def start_ui(self): |
要把electron设置成自己的local path,不能用默认全局的。
然后还要打包main.js
,package.json
,package-lock.json
1 | datas += [ |
即可。
多文件夹版本
1 | # -*- mode: python ; coding: utf-8 -*- |
单一文件版本(失败)
1 | # -*- mode: python ; coding: utf-8 -*- |
使用auto-py-to-exe GUI辅助打包
1 | pip3 install auto-py-to-exe |
现在需要改进的
- 关闭控制台输出
打包electron程序(outdated)
Demo打包
首先是对demo尝试打包:
参考了一下这个: Getting Started - Electron Forge
直接对着项目文件夹下执行:
Python部分
先用Pyinstaller打包:
1 | pyinstaller pycalc/api.py --distpath pycalcdist |
然后删除一些多余的玩意:(不删好像也没关系)
1 | rm -rf build/ |
这是Linux指令,Win上面手动删也行反正
Node部分
有点tricky。不过核心部分作者已经实现好了,不用我们操心
首先作者说执行:
1 | ./node_modules/.bin/electron-packager . --overwrite --ignore="pycalc$" --ignore="\.venv" --ignore="old-post-backup" |
但是这个本地的electron-packager
版本已经太老了,有版本识别错误bug
所以我直接
1 | npm install -g electron-packager |
全局下了个版本最新的
然后执行
1 | electron-packager . --overwrite --ignore="pycalc$" --ignore="\.venv" --ignore="old-post-backup" |
然后就生产了一个独立的文件夹,不出意外的话应该就是独立的exe了。
测试样例生成
AsPack2.12加壳器 +
Linux交叉编译32bit exe
1 | sudo apt install g++-mingw-w64-i686 |
参考
https://github.com/python-eel/Eel
Using PyInstaller — PyInstaller 5.12.0 documentation
Python-virtualenv创建虚拟环境 - 简书 (jianshu.com)
(160条消息) 新发的日常小实验——javascript自定义alert窗口样式_林新发的博客-CSDN博客
[原创]AsPack2.12 手工脱壳-加壳脱壳-看雪-安全社区|安全招聘|kanxue.com
脱壳——修复加密IAT - Sna1lGo - 博客园 (cnblogs.com)
PE格式:导入表与IAT内存修正 - lyshark - 博客园 (cnblogs.com)
IAT Patcher (hasherezade.github.io)
Rebuild IAT after manually unpacking DLL - Reverse Engineering Stack Exchange
- 本文作者: Taardis
- 本文链接: https://taardisaa.github.io/2023/05/31/实训项目-ASPACK与UPX脱壳/
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!