Python逆向总结

47 篇文章 10 订阅
40 篇文章 2 订阅

第一种:直接反编译型

除了直接获得题目内容的python文件外,出题人也可以稍微加工一点点,给出题目python文件所对应的pyc文件,即python的字节码

PYC 文件的定义

pyc 文件是 python 在编译过程中出现的主要中间过程文件。pyc 文件是二进制的,类似 java 的字节码,可以由 python 虚拟机直接执行的。

这个时候我们一般使用uncompyle6(适用于python3.8)或者Pycdc将pyc文件反编译成py文件

Uncompyle6下载以及使用:GitHub - rocky/python-uncompyle6: A cross-version Python bytecode decompiler

命令:

pip install uncompyle6

安装完成后可以使用

uncompyle6 --version

查看是否安装成功,若成功显示版本号,则安装成功

(注意:下载的uncompyle6的版本最好别高于所使用的python版本)

使用命令:

uncompyle6 -o output_file.py your_file.pyc

-o 目标生成的Python文件名 原pyc文件名

pycdc下载以及使用:

GitHub - extremecoders-re/decompyle-builds: Precompiled Decompyle++ (pycdc) binaries for Windows & Linux

pycdc -o output_file.py your_file.pyc

当然也可以使用一些在线网站将Pyc文件转换为python文件

第二种:打包成exe的py文件

一般来说py文件打包生成的exe的图标是

并且如果直接使用IDA打开会有很多包含python的字样

然后即可以判断是Py文件打包而成的exe

这个时候我们需要使用Pyinstxtractor工具将exe文件进行解包

pyinstxtractor.py 工具的下载地址:PyInstaller Extractor download | SourceForge.net

将上面下载好的pyinstxtractor文件复制到题目所在目录下

然后直接在打包的exe的路径下打开终端

使用命令:python pyinstxtractor.py 待解包的文件名.exe

然后获得生成的解包后的文件夹

打开extracted文件夹

一般来说我们会获得一个和我们解包的exe同名的pyc文件,这个时候就和第一种类型题目一样,将pyc文件还原成py文件进行逆向即可

但是有特殊情况,因为使用Pyinstxtractor进行解包以后源文件一般不会包含原始的魔术数字和时间戳,在反编译的时候就可能会出错,如下图查看login.pyc

而解包后的 struct.pyc 文件会保留其原始的 Python .pyc 文件的魔数和时间戳信息

所以我们通常使用strcut.pyc中的信息对原来的Pyc进行补全

 

将E3前的数字复制粘贴到test.pyc的前面即可

然后保存即可正常得到Py文件

 

第三种:  给pyc字节码(类汇编形式)

如果出题人给的题目形式如下,那么我们应该怎么操作呢

前述:

1.dis库:

用于反汇编 Python 字节码。它可以将 Python 函数或代码对象的字节码指令序列转换成我们的可读形式,显示每个字节码指令的操作码和操作数。

dis.dis 函数的作用:

  • 接受一个 Python 函数对象或者代码对象作为参数。
  • 将这个函数或代码对象的字节码指令序列反汇编成易于理解的格式。
  • 显示每个字节码指令的操作码(opcode)和操作数(operand),以及相应的行号和位置信息。

例如,对于一个简单的 Python 函数:

def add_numbers(a, b):
    return a + b

使用 dis.dis 来查看其字节码:

import dis

dis.dis(add_numbers)

输出结果:

0 LOAD_FAST                0 (a) 

2 LOAD_FAST                1 (b) 

4 BINARY_ADD 

6 RETURN_VALUE

2.marshal:

Python 标准库中的一个模块,提供了对 Python 对象进行序列化(转换为字节流)和反序列化(从字节流恢复为对象)功能。

不同python版本的pyc文件头

python2的pyc文件的前4个字节是一个固定的魔数(03 F3 0D 0A),而紧接着的后 4 个字节表示编译这个 .pyc 文件的 Python 解释器的版本号

python3的pyc文件前4个字节是固定的魔数 (33 0D 0D 0A),然后是两个字节的时间戳,标识了 .py 文件的最后修改时间, 接着是 4 个字节的源文件大小,最后是源文件名的字符串,以 null 字节结尾

注意:Python3的pyc文件头部并非固定的 16 个字节,而是一个不确定的长度,至少是 12 个字节,加上源文件名字符串的长度

怎么获取pyc字节码

在交互模式下:

1

2

3

4

5

6

7

8

9

10

11

import dis,marshal

#导入 Python 的两个标准库模块 dis 和 marshal。dis 用于反汇编字节码,marshal 用于序列化和反序列化对象

f=open("Pz.pyc","rb").read()

#以二进制只读模式打开文件 Pz.pyc

f

#将读取的字节流内容存储在变量 f 中

code=marshal.loads(f[8:])

#跳过文件头部分将字节码加载到code中

code

#查看

dis.dis(code)

#反汇编字节码

最后获得输出(可以理解为python的汇编)

最后不理解的可以对照官方文档搜索去还原字节码的含义

32.12. dis — Disassembler for Python bytecode — Python 2.7.18 documentation

这里贴几个常见的含义:

LOAD_CONST :加载const 变量,比如数值,字符串等等, 一般用于传递给函数作为参数

LOAD_FAST :一般用于加载局部变量的值,也就是读取值,用于计算或者函数调用传传等

STORE_FAST :一般用于保存值到局部变量

CALL_FUNCTION:CALL_FUNCTION n,其中 n 表示函数调用时传递的参数数量

表示在此处调用了一个函数,并且传递了n个参数。

第四种:加花的pyc

这里需要了解一下pyc的文件结构

pyc文件分为pyc文件头部分PyCodeObject部分

文件头部分即为上文中谈到的魔数时间戳部分,而PyCodeObject是在CPython(Python 的官方解释器实现)中用来表示编译后的代码对象的结构体

实际上,pyc 文件就是 PyCodeObject 对象在硬盘上的保存形式。

不同版本的python的魔数头

PyObject_HEAD

不同的 Python 版本会有不同的 PyObject_HEAD:

Python 版本十六进制文件头
Python 2.703f30d0a00000000
Python 3.03b0c0d0a00000000
Python 3.14f0c0d0a00000000
Python 3.26c0c0d0a00000000
Python 3.39e0c0d0a0000000000000000
Python 3.4ee0c0d0a0000000000000000
Python 3.5170d0d0a0000000000000000
Python 3.6330d0d0a0000000000000000
Python 3.7420d0d0a000000000000000000000000
Python 3.855 0d 0d 0a 00 00 00 00 00 00 00 00 00 00 00 00
Python 3.9610d0d0a000000000000000000000000
Python 3.106f0d0d0a000000000000000000000000
Python 3.11a70d0d0a000000000000000000000000

PyCodeObject 的结构如下:

typedef struct {

PyObject_HEAD

int co_argcount;        /* 位置参数个数 */

int co_nlocals;         /* 局部变量个数 */

int co_stacksize;       /* 栈大小 */

int co_flags;

PyObject co_code;      / 字节码指令序列 */

PyObject co_consts;    / 所有常量集合 */

PyObject co_names;     / 所有符号名称集合 */

PyObject co_varnames;  / 局部变量名称集合 */

PyObject co_freevars;  / 闭包用的的变量名集合 */

PyObject co_cellvars;  / 内部嵌套函数引用的变量名集合 */

/* The rest doesn’t count for hash/cmp */

PyObject co_filename;  / 代码所在文件名 */

PyObject co_name;      / 模块名|函数名|类名 */

int co_firstlineno;     /* 代码块在文件中的起始行号 */

PyObject co_lnotab;    / 字节码指令和行号的对应关系 */

void co_zombieframe;   / for optimization only (see frameobject.c) */

} PyCodeObject;

而在PyCodeObject中有一个部分我们在做逆向题目时需要尤其注意,那就是

PyObject co_code;      / 字节码指令序列 */

数值代表了Pyc字节码的指令的字节数

如果我们对Pyc里面的指令进行了删减,那么在删减过后我们需要对PyObject *co_code数值也进行修改,将数值修改为删减后的字节数

查看指令数的参考命令:

len(code.co_code)其中第一个code为你装载字节码所命名的变量

例如下图我们可以看到删改前指令长度是27

 

在读取的字节码中,看到偏移量是12和15的地方

在12处我们可以看到指令会让我们跳转到偏移为18的位置

而在15处LOAD_CONST 255表示从常量表中加载索引为 255 的常量,并将其推入栈顶

但此处很显然我们没有那么多的常量

所以可以判断这个地方是一个花指令

和c汇编花指令的思路差不多,我们需要将12和15处给直接去掉(类比c去花的nop)

所以我们选择将该pyc文件使用010Editor打开后搜索对应的十六进制数据来定位指令所在

例如搜索官方文档发现JUMP_ABSOLUTE的操作码对应的十六级进制是0x71,所以从0x71往下的三个字节即为偏移量12处的指令

而LOAD_CONST的操作码对应的十六进制是0x64

所以我们只需要将从71开始的往后6个字节全部删除然后再修改PyObject *co_code的值即可

通过最初查看我们得知PyObject *co_code的值是27,十六进制为1B

 

然后我们减去了六个字节,所以我们需要将27改为21(0x15)

修改完成后

  • 36
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python逆向爬虫技术是指使用Python编程语言来进行逆向研究和实战的爬虫技术。逆向爬虫主要应用于对动态加载的页面进行数据抓取,而不仅仅是静态页面。Python的requests库虽然可以爬取静态页面,但无法爬取动态加载的页面。因此,逆向爬虫方法可以提高爬虫的门槛,使其能够获取到动态加载的数据。 在逆向爬虫中,有一种陷阱叫做蜘蛛陷阱,它可以导致网络爬虫进入无限循环,浪费资源并降低生产力。对于编写不好的爬虫程序,可能会导致程序崩溃。为了避免这种情况,礼貌蜘蛛会在不同主机之间交替请求,并且不会频繁请求同一服务器的文档,这样可以减小对服务器的负担,也减少了对网站的影响。因此,“礼貌”网络爬虫的影响程度要比“不礼貌”爬虫小得多。 总结来说,Python逆向爬虫技术是利用Python编程语言对动态加载的页面进行数据抓取的方法。通过逆向研究和实战,可以实现对动态页面的数据需求。然而,在进行逆向爬虫时,需要注意避免蜘蛛陷阱,保持爬虫的礼貌行为,以免对服务器造成过大的负担。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [某二手车爬虫逆向完整项目+python+爬虫+逆向研究+爬虫实战](https://download.csdn.net/download/qq_44000141/87835435)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [反爬方案总结](https://blog.csdn.net/qq_35809147/article/details/113447609)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值