Python3 中的默认编码是 UTF-8,这给大家写 Python 代码带来了很大的便利,不用再像 Python2.x 那样为数据编码操碎了心。但是,由于全面转向 UTF-8 编码,Python3 里面会有一些小细节,稍有不慎容易栽坑。本文就对二进制数据 XOR 编码这一种操作,浅析 Py2/Py3 中默认编码相关的一个细节小差异而引起的小 Bug。

XOR 编码是最简单有效的编码方法之一,虽然简单,但仍然应用广泛。在分析恶意样本时,经常会遇到样本内置的隐秘数据或者网络通信数据,用到了 XOR 编码。比如,一个典型就是 XOR.DDoS 家族,它样本内部关键字符串全用 XOR 编码过,而且其网络通信中 Bot 发给 C2 的上线数据包和 C2 给 Bot 下发的控制指令数据包中均涉及 XOR 编码/解码操作。

对于这类样本,分析的时候我们不免要写一些自动化的解析脚本,把其中的编码数据还原成名文以便分析。在其他开发场景中也偶尔会用 Python 写一些 XOR 编码/解码的程序。网上一搜 「Python XOR 编码 加密」或者「Python XOR encoding crypt」,都会搜出很多别人发出来的 Python XOR 编解码脚本,大多数情况下拿来直接用就行。比如我搜来的几个中文帖子中的相关脚本(本人不保证下面截图里代码的正确性):

这些脚本,在 Python2 环境下都没有问题,都可以正确进行 XOR 编解码,然而如果直接拿到 Python3 环境下去运行,却会发生一个不容易发现的小 Bug。来看一段在 ipthon3 里的操作记录:

In [1]: def xor_crypt(data, key): ...: cipher_data = [] ...: len_data = len(data) ...: len_key = len(key) ...: for idx in range(len_data): ...: bias = key[idx % len_key] ...: curr_byte = data[idx] ...: cipher_data.append(chr(bias ^ curr_byte)) ...: return bytearray("".join(cipher_data).encode()) ...:In [2]: xor_key = b'0123456789'In [3]: sam1 = b'abcdefgh'In [4]: sam2 = b'abcdefghijklmnopqrstuvwxyz'In [5]: print(xor_crypt(sam1,xor_key))bytearray(b'QSQWQSQ_')In [6]: print(xor_crypt(xor_crypt(sam1,xor_key), xor_key))bytearray(b'abcdefgh')In [7]: print(xor_crypt(sam2, xor_key))bytearray(b'QSQWQSQ_QS[]_][EGEKMEGEKMO')In [8]: print(xor_crypt(xor_crypt(sam2,xor_key), xor_key))bytearray(b'abcdefghijklmnopqrstuvwxyz')In [9]: sam3 = b'x7fx80x81x90x91xA0xA1xB0xB1xC0xC1xD0xD1xE0xE1xF0xF1xFA'In [10]: print(xor_crypt(sam3, xor_key))bytearray(b'Oxc2xb1xc2xb3xc2xa3xc2xa5xc2x95xc2x97xc2x87xc2x89xc3xb9xc3xb1xc3xa1xc3xa3xc3x93xc3x95xc3x85xc3x87xc3x8d')In [11]: print(xor_crypt(xor_crypt(sam3,xor_key), xor_key))bytearray(b'x7fxc3xb3xc2x83xc3xb1xc2x87xc3xb7xc2x95xc3xb5xc2x9dxc3xbbxc2xa5xc3xb3xc2xa5xc3xb1xc2xb3xc3xb7xc2xbfxc3xb4xc2x81xc3xbaxc2x81xc3xb2xc2x93xc3xb0xc2x97xc3xb6xc2xa5xc3xb4xc2xadxc3xbaxc2xb5xc3xb2xc2xb5xc3xb0xc2xb9')In [12]: print(len(sam3))18In [13]: print(len(xor_crypt(xor_crypt(sam3,xor_key), xor_key)))69

可以看到,仿照 Python2 环境下那些常用的 XOR 编码操作写的函数,在 Python3 环境下,偶尔会出现意料之外的结果:上面的操作记录中,对于 sam1sam2 两个全都是可打印字符的字节串进行 XOR 编解码是没有问题的;但是对于 sam3 ,一个内含大量 HEX 值大于 0x7F 的非可打印字符字节串,原本是 18 个字节,进行两次 XOR 操作之后竟然变成了 69 个字节。

这就十分蹊跷了。问题出在哪个环节?是函数内部的字节列表 cipher_data 的问题,还是最后 bytearray() 操作出了问题,还是进行 XOR 计算的时候, chr() 函数的问题?

经过一番排查,发现这是 chr() 函数的问题。先看这个函数在 Python2 和 Python3 中各有什么表现:

在 Python2 版本中,除了 chr() 还有一个 unichr() ,可以看到 Py2 中的 unichr() 与Py3 中的 chr() 行为是一致的:对于 HEX 值大于 0x7F 的字符,返回值占 2 Bytes;对于 HEX 值小于或等于 0x7F 的字符,返回值占 1 Byte。

为什么会出现这么个差异?刚开始一直以为 chr() 函数只会返回 1 Byte 的结果,对此感到很是不解。

查阅一下 Py2 中 chr()unichr() 的文档如下:

而 Py3 中 chr() 函数的文档说明如下:

从文档来看, Py3 中的 chr() 函数确实对应到了 Py2 中的 unichr() 函数,只返回 Unicode 编码的结果。在点破最后的一层窗户纸之前,我们再去 CPython 的源码里瞅一眼,以便把这个结论锤结实了。

Py3 中的 chr() 函数,源码中是这样实现的:

至于其中的 unicode_char() 函数如何实现,我们就不深究了,知道它就是返回一个 Unicode 编码的字符即可。再看 Py2 中 unichr() 函数:

如出一辙有木有。

最后一层窗户纸 到底是什么?就是 Py3 默认的 UTF-8 编码了。在 http://www.utf-8.com 网站上有这么一段话:

UTF-8 encodes each Unicode character as a variable number of 1 to 4 octets , where the number of octets depends on the integer value assigned to the Unicode character. It is an efficient encoding of Unicode documents that use mostly US-ASCII characters because it represents each character in the range U+0000 through U+007F as a single octet .

注意上面加粗部分的重点:

  1. UTF-8 编码的字符占 1~4 个字节;
  2. 字符 U+0000 到 U+007F 都用一个字节来表示,其它字符 1 个字节不够,就用 2~4 个字节来表示。

这样就明确上面问题的原因了:Py3 中的 chr() 函数,只有在参数的 HEX 值位于 [0x00, 0x7F] 区间内的时候才返回 1 Byte 的结果,这个结果同于 Py2 中的 chr() 函数;当 HEX 值大于 0x7F ,其返回值占 2 Bytes,行为同于 Py2 中的 unichr() 函数。

那么 Py3 中正确的 XOR 编解码姿势是什么?上面 ipython3 操作记录中的函数稍加改动即可:

def xor_crypt(data, key): cipher_data = [] len_data = len(data) len_key = len(key) for idx in range(len_data): bias = key[idx % len_key] curr_byte = data[idx] cipher_data.append(bias ^ curr_byte) return bytearray(cipher_data)

当然,还有更简洁的写法:

def XORCrypt(data, key): return bytearray(a^b for a, b in zip(*map(bytearray, [data, key])))

“我自己是一名从事了多年开发的Python老程序员,辞职目前在做自己的Python私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的Python学习干货,从最基础的到各种框架都有整理,送给每一位喜欢Python小伙伴,想要获取的可以转发文章并关注我的头条。在后台私信我:01,即可免费获取。"

python3字节转化字符_浅谈 Python3 中对二进制数据 XOR 编码的正确姿势相关推荐

  1. python读取图像数据流_浅谈TensorFlow中读取图像数据的三种方式

    本文面对三种常常遇到的情况,总结三种读取数据的方式,分别用于处理单张图片.大量图片,和TFRecorder读取方式.并且还补充了功能相近的tf函数. 1.处理单张图片 我们训练完模型之后,常常要用图片 ...

  2. python3打印如何换行_浅谈Python3中print函数的换行

    Python3中print函数的换行 最近看了看Python的应用,从入门级的九九乘法表开始,结果发现Python3.x和Python2.x真的是有太大的不同之处,就比如这里的换行处理,怕忘记先记下来 ...

  3. python3字节转化字符_捋一捋字符串与字节序列的关系

    Python3的一个新特性算是把文本和二进制数据清晰地区分开来了.我们看到的文本其实就是一连串的Unicode字符组成的str(下文文无特殊说明均指Unicode字符),最后计算机以二进制数据的形式存 ...

  4. [转] 浅谈脱壳中的附加数据问题(overlay)

    浅谈脱壳中的附加数据问题(overlay) Author:Lenus From: www.popbase.net E-mail:Lenus_M@163.com -------------------- ...

  5. 浅谈脱壳中的附加数据问题(overlay)

    浅谈脱壳中的附加数据问题(overlay) Author:Lenus From: www.popbase.net E-mail:Lenus_M@163.com -------------------- ...

  6. python中rstrip用法_浅谈Python3中strip()、lstrip()、rstrip()用法详解

    简单来说,三种方法是为了删除字符串中不同位置的指定字符.其中,strip()用于去除字符串的首尾字符,同理,lstrip()用于去除左边的字符,rstrip()用于去除右边的字符 Python中有三个 ...

  7. 为什么python打包的应用那么大_浅谈python3打包与拆包在函数的应用详解

    1.序列(拆包) *用作序列拆包:*可对字符串.列表.集合.元组.字典.数字元素等序列进行拆包 print(*(1,2,3,4,5,6)) #1 2 3 4 5 6 print(*[1,2,3,4,5 ...

  8. python矩形语句_浅谈Python3实现两个矩形的交并比(IoU)

    一.前言 因为最近刚好被问到这个问题,但是自己当时特别懵逼,导致没有做出来.所以下来后自己Google了很多IoU的博客,但是很多博客要么过于简略,要么是互相转载的,有一些博客图和代码还有点问题,也导 ...

  9. python类构造方法缺省_浅谈python3 构造函数和析构函数

    要点: 1.魔法方法,被__双下划线所包围 在适当的时候自动被调用 2.在创建一个对象的时候,一定会调用构造函数 3. del析构函数,在del a对象的时候,并一定会调用该析构函数 只有当该对象的引 ...

最新文章

  1. 2017 湘潭邀请赛JSCPC GJ
  2. 亚马逊 AWS 免费云服务操作流程
  3. 菜鸟学Java(七)——Ajax+Servlet实现无刷新下拉联动
  4. Object类和常用的API
  5. discard python_Netty入门教程(一) 实现DISCARD服务
  6. 渗透测试入门14之渗透测试工具1
  7. 来自 119.*.*.*的回复: TTL 传输中过期
  8. Java 多线程 简单实例 (Thread)
  9. live2d手机制作软件_live2d制作器手机版
  10. OpenCV学习cvtColor函数
  11. java 里面 todo 作用
  12. android 多媒体播放器源代码,Android多媒体之VideoView视频播放器
  13. php手机网页下载文件,php 手机下载 POST 类
  14. shell脚本——注释(单行注释 多行注释)
  15. EasyExcel解析excel(合并单元格和未合并)
  16. 获取手机号码归属地工具类
  17. 幼儿园教师计算机技术培训计划,幼儿园教师基本功培训计划3篇
  18. 【web前端】JavaScript总结
  19. amp;#9733;平衡法则在生活中的应用
  20. mysql协议分析 row_MySQL协议分析(结合PyMySQL)

热门文章

  1. php 反射类 解析注释,php反射获取类和方法中的注释
  2. 线程之间的通信(thread signal)
  3. 华为平板matepad pro鸿蒙,华为MatePad Pro 2平板电脑入网:首款预装鸿蒙OS
  4. matlab基数排序,如何在MATLAB中编写基数排序的程序
  5. sql大小写 效率_【SQL】单行函数
  6. StringEscapeUtils的资料
  7. Docker启动失败提示【exec user process caused: exec format error】
  8. SpringMVC框架----SpringMVC入门程序中的组件介绍
  9. 滤镜应用——拼图效果
  10. vim内过长字符串导致的语法加亮错误