看 p牛fpm未授权访问到rce的poc 兼容python2和python3,看了一下代码,开头一部分是这样的:
...
PY2 = True if sys.version_info.major == 2 else False
def bchr(i):
if PY2:
return force_bytes(chr(i))
else:
return bytes([i])
def bord(c):
if isinstance(c, int):
return c
else:
return ord(c)
def force_bytes(s):
if isinstance(s, bytes):
return s
else:
return s.encode('utf-8', 'strict')
def force_text(s):
if issubclass(type(s), str):
return s
if isinstance(s, bytes):
s = str(s, 'utf-8', 'strict')
else:
s = str(s)
return s
...
在开头定义了一个变量 PY2 判断当前的环境是Python2还是Python3,在 bchr() 函数里,如果是python2的环境,强制转换为字节流然后返回,是python3的话使用 bytes() 转换为字节对象返回。下面三个函数都是使用 isinstance() 判断变量是否为对应的类型,为假则转换为对应的类型
这四个函数主要解决的是Python2和Python3中字符串类型str和bytes的问题,Python3中将字符串区分为文本(str)和字节流(bytes)类型,我们在代码中使用引号包裹起来的字符串都是文本(str)类型的:
而在网络上传输的数据,或是从磁盘中读写的数据都是字节流的:
而这时如果我要使用正则表达式来从响应报文中匹配数据,就不能直接re.findall(正则,content)了:
提示我们不能在bytes的字符串中使用str的字符串去匹配,Python3并不会隐式转换str和bytes。
但我们在Python2中使用同样的代码:
(PS 单独Python3可以这么写:
)
Python2和Python3的字符串好像有点乱?记个表格吧,之后也用得上:
版本
文本
字节
python2
Unicode
str
python3
str
bytes
所以开头的四个函数主要用于解决Python2和Python3字符串的问题,有了这四个函数就可以这么写来解决问题了:
Python3自带的 __future__
但是Python2和Python3的差别不止上面一个字符串,Python官方自带了一个 __future__ 模块用于兼容python2和python3。
__funture__ 的官方文档在这里: https://docs.python.org/zh-cn/3/library/__future__.html ,版本可在左上角切换,里面给出了 __funture__ 支持的一些特性:
division
/ 表示除法,在python2和python3中表现略微不一样,具体见下表:
版本
整除
除法
python2
/ (当除数与被除数均为整数)
/ (当除数与被除数中有一个为浮点数)
python3
//
/
当使用了 from __future__ import division 可以确保 / 在python2和python3中表现得一致:
absolute_import
绝对导入/相对导入是指,通过 import foo 导入一个模块时,在python自带的模块中存在一个foo.py,在本目录下也存在一个foo.py,优先导入系统/本目录的模块。python2.4之前默认是优先导入本目录下的模块,如果是需要导入python的自带模块,则需要使用 from __future__ import absolute_import 。
with_statement
with复合语句是从python2.5中引入的,低于2.5版本的python使用with语句需要另外引入。
不过现在常见的python2版本已经是2.7了,基本用不上这个了 :)
print_function
强制使用 print() 这种形式的输出。
举个栗子:
from __future__ import print_function
print('ok')
print 'ok'
正常情况下,python2有 print 'ok' 这种写法的,但当引入了 print_function ,则python2会对 print 'ok' 的写法报错:
unicode_literal
对字符串使用Unicode编码,在python2早期中不支持Unicode编码,所以中文等一些字符会出现乱码。
第三方模块six
six(6)的名字取自2*3=6
统一类型
补充一下我们的表格:
版本
文本
字节
python2
Unicode
str
python3
str
bytes
six
six.text_type
six.binary_type
six把python的文本统一为 six.text_type ,把字节统一为 six.binary_type
内部对象属性的重命名和模块的统一
Python3重新组织了标准库,并将一些函数移到了不同的模块中。six通过 six.moves 模块为它们提供了一致的接口。
—— six官方在线文档
例如python2的HTMLParser模块,在python3中变为了html.parser,python2的 import HTMLParser 和python3的 import html.parser 在引入six之后,都可以使用 from six.moves import html_parser 来引入了。
其他高级自定义的方法等
比如使用 from six.moves import input 来统一 input() 的返回值类型全为str:
转换Python2的input()类型为str:
Python3的input()即为str类型: