脚本代码混淆-Python篇-pyminifier(1)
前言
最近研究了一下脚本语言的混淆方法,比如 python,javascript等。脚本语言属于动态语言,代码大多无法直接编译成二进制机器码,发行脚本基本上相当于暴露源码,这对于一些商业应用是无法接受的。因此对脚本代码进行加固,成为很多应用的首选。代码加固的一项措施是代码混淆,增加逆向人员阅读代码逻辑的难度,拖延被破解的时间。
今天讲解一下Python代码的混淆方法,Python代码一般用作web,提供服务接口,但也有一些桌面的应用,这一部分就需要对代码进行混淆保护。以一个开源项目pyminifier (https://github.com/qiyeboy/pyminifier)来说明混淆的技巧方法,这个项目已经有4年没更新,有一些bug,但是依然值得我们学习和入门。
项目结构

框架详情:
analyze.py - 用于分析Python代码
compression.py - 使用压缩算法压缩代码
minification.py - 用于简化Python代码
obfuscate.py - 用于混淆Python 代码
token_utils.py - 用于收集Python Token
从项目代码中,可以看到pyminifier的混淆方法是基于Token的,即基于词法分析,假如大家之前做过混淆的话,这应该属于混淆的初级方案,因为这样的混淆并不会修改代码原有的逻辑结构。
提取Token
如何提取Python语言的Token呢?Python中提供了专门的包进行词法分析: tokenize。使用起来很简单,在token_utils.py中代码如下:
def listified_tokenizer(source): """Tokenizes *source* and returns the tokens as a list of lists."""
io_obj = io.StringIO(source)
return [list(a) for a in tokenize.generate_tokens(io_obj.readline)]
首先读取源文件,然后通过tokenize.generate_tokens生成token列表。咱们就将这个提取token的函数保存起来,然后让他自己提取自己,看一下token列表的结构。
[[1, 'def', (1, 0), (1, 3), 'def listified_tokenizer(source):\n'],
[1, 'listified_tokenizer', (1, 4), (1, 23), 'def listified_tokenizer(source):\n'],
[53, '(', (1, 23), (1, 24), 'def listified_tokenizer(source):\n'],
[1, 'source', (1, 24), (1, 30), 'def listified_tokenizer(source):\n'],
[53, ')', (1, 30), (1, 31), 'def listified_tokenizer(source):\n'],
[53, ':', (1, 31), (1, 32), 'def listified_tokenizer(source):\n'],
[4, '\n', (1, 32), (1, 33), 'def listified_tokenizer(source):\n'],
......
每一个Token对应一个list,以第一行 [1,'def',(1,0),(1,3),'def listified_tokenizer(source):\n']为例子进行解释:
1代表的是token的类型
def是提取的token字符串
(1, 0)代表的是token字符串的起始行与列
(1, 3)代表的是token字符串的结束行与列
'def listified_tokenizer(source):\n' 代表所在的行
Token还原代码
能从源文件中提取token 列表,如何从token列表还原为源代码呢?其实很简单,因为提取token 列表里面有位置信息和字符串信息,所以进行字符串拼接即可。
def untokenize(tokens):
"""
Converts the output of tokenize.generate_tokens back into a human-readable
string (that doesn't contain oddly-placed whitespace everywhere).
.. note:: Unlike :meth:`tokenize.untokenize`, this function requires the 3rd and
4th items in each token tuple (though we can use lists *or* tuples).
"""
out = ""
last_lineno = -1
last_col = 0
for tok in tokens:
token_string = tok[1]
start_line, start_col = tok[2]
end_line, end_col = tok[3]
# The following two conditionals preserve indentation:
if start_line > last_lineno:
last_col = 0
if start_col > last_col and token_string != '\n':
out += (" " * (start_col - last_col))
out += token_string
last_col = end_col
last_lineno = end_line
return out
精简与压缩代码
在pyminifier中,有两个缩小Python代码的方法:一个是精简方式,另一个是使用压缩算法的方式。
精简
在minification.py中使用的是精简方式,具体代码如下:
def minify(tokens, options):
"""
Performs minification on *tokens* according to the values in *options*
"""
# Remove comments
remove_comments(tokens)
# Remove docstrings
remove_docstrings(tokens)
result = token_utils.untokenize(tokens)
# Minify our input script
result = multiline_indicator.sub('', result)
result = fix_empty_methods(result)
result = join_multiline_pairs(result)
result = join_multiline_pairs(result, '[]')
result = join_multiline_pairs(result, '{}')
result = remove_blank_lines(result)
result = reduce_operators(result)
result = dedent(result, use_tabs=options.tabs)
return result
上面的代码总共使用了9种方法来缩小脚本的体积:
remove_comments
去掉代码中的注释,但是有两类要保留:1.脚本解释器路径 2. 脚本编码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
remove_docstrings
去掉doc所指定的内容,example:
__doc__ = """\
Module for minification functions. """
fix_empty_methods
修改空函数变成pass
def myfunc():
'''This is just a placeholder function.'''
转化为:
def myfunc():pass
join_multiline_pairs
(1) 第一种情况:
test = (
"This is inside a multi-line pair of parentheses"
)
转化为:
test = ( "This is inside a multi-line pair of parentheses")
(2)第二种情况:
test = [
"This is inside a multi-line pair of parentheses"
]
转化为:
test = [ "This is inside a multi-line pair of parentheses"]
(3)第三种情况:
test = {
"parentheses":"This is inside a multi-line pair of parentheses"
}
转化为:
test = { "parentheses":"This is inside a multi-line pair of parentheses"}
remove_blank_lines
移除空白行。
test = "foo" test2 = "bar"
转化为:
test = "foo"
test2 = "bar"
reduce_operators
移除操作符之间的空格。
def foo(foo, bar, blah):
test = "This is a %s" % foo
修改为:
def foo(foo,bar,blah):
test="This is a %s"%foo
dedent
替换代码间的缩进,比如替换成单个空格
def foo(bar):
test = "This is a test"
修改为:
def foo(bar): test = "This is a test"
压缩
在这个项目中的compression.py,提供了4种代码压缩的方法,其中3个原理是一样,只不过使用的压缩算法不一样。
bz2,gz,lzma 压缩执行原理
假如新建一个1.py,并保存如下内容:
if __name__=="__main__":
print(__name__)
以bz2为例子,首先使用bz2算法压缩代码,然后转化成base64编码。
code='''
if __name__=="__main__":
print(__name__)
'''
import bz2,base64
compressed_source = bz2.compress(code.encode("utf-8"))
print(base64.b64encode(compressed_source).decode('utf-8'))
输出:
QlpoOTFBWSZTWdfQmoEAAAHbgEAQUGAAEgAAoyNUACAAIam1NNGgaaFNMjExMQ2Za0TTvJepAjgXb2pDBBGoliFIT04+LuSKcKEhr6E1Ag==
代码压缩完成后,如何执行呢?其实就用到了exec这个函数/关键字。将编码好的内容,先base64解码,再使用bz2算法解压缩,最后获得真实的代码,并使用exec执行
import bz2, base64
exec(bz2.decompress(base64.b64decode("QlpoOTFBWSZTWdfQmoEAAAHbgEAQUGAAEgAAoyNUACAAIam1NNGgaaFNMjExMQ2Za0TTvJepAjgXb2pDBBGoliFIT04+LuSKcKEhr6E1Ag==")))
这段代码就代表了最原始的代码,而使用gz,lzma压缩方式,将bz2包换成zlib 或者lzma即可。
zip执行原理
可能很多朋友不知道,Python是可以直接运行zip文件的(特别的),主要是为了方便开发者管理和发布项目。Python能直接执行一个包含 __main__.py的目录或者zip文件。
举个例子:
|—— ABC/ |—— A.py |—— __main__.py
示例代码:
# A.py
def echo(): print('ABC!') # __main__.py
if __name == '__main__': import A
A.echo()
可以直接将多个文件压缩成一个zip文件,直接运行zip文件就可以。目录结构:
|—— ABC.zip/ |—— A.py |—— __main__.py
运行情况:
$ python ABC.zip ABC!
未完待续。。。
最后
关注公众号:七夜安全博客

- 回复【1】:领取 Python数据分析 教程大礼包
- 回复【2】:领取 Python Flask 全套教程
- 回复【3】:领取 某学院 机器学习 教程
- 回复【4】:领取 爬虫 教程
- 回复【5】:领取 编译原理 教程
- 回复【6】:领取 渗透测试 教程
- 回复【7】:领取 人工智能数学基础 教程
脚本代码混淆-Python篇-pyminifier(1)的更多相关文章
- Python代码混淆和加密技术
Python进行商业开发时, 需要有一定的安全意识, 为了不被轻易的逆向. 混淆和加密就有所必要了. 为了增加代码阅读的难度, 源代码的混淆非常必要, 一个在线的Python代码混淆网站. http: ...
- Python处理NetCDF格式数据为TIFF数据(附脚本代码)
博客小序:NetCDF格式数据广泛应用于科学数据的存储,最近几日自己利用python处理了一些NetCDF数据,特撰此博文以记之. 参考博客: https://www.cnblogs.com/shou ...
- Python 代码混淆和加密技术
动机 Python进行商业开发时, 需要有一定的安全意识, 为了不被轻易的逆向. 混淆和加密就有所必要了. 混淆 为了增加代码阅读的难度, 源代码的混淆非常必要, 一个在线的Python代码混淆网站. ...
- .net代码混淆 .NET Reactor 研究 脚本一键混淆一键发布
.net代码混淆 .NET Reactor 研究 为什么要混淆? .net比较适合快速开发桌面型应用,但缺点是发布出来的文件是可以反编译的,有时候为了客户的安全,我们的代码或者我们的逻辑不想让别人知道 ...
- Python 控制流代码混淆简介,加大别人分析你代码逻辑和流程难度
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 王平 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- C++混合编程之idlcpp教程Python篇(9)
上一篇在这 C++混合编程之idlcpp教程Python篇(8) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相比,工程PythonTutorial7中除了四个文件PythonTu ...
- C++混合编程之idlcpp教程Python篇(8)
上一篇在这 C++混合编程之idlcpp教程Python篇(7) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相似,工程PythonTutorial6中,同样加入了四个文件:Pyt ...
- C++混合编程之idlcpp教程Python篇(7)
上一篇在这 C++混合编程之idlcpp教程Python篇(6) 第一篇在这 C++混合编程之idlcpp教程(一) 与PythonTutorial4工程相似,工程PythonTutorial5中,同 ...
- C++混合编程之idlcpp教程Python篇(6)
上一篇在这 C++混合编程之idlcpp教程Python篇(5) 第一篇在这 C++混合编程之idlcpp教程(一) 工程PythonTutorial4中加入了四个文件:PythonTutorial4 ...
随机推荐
- Zookeeper开源客户端Curator的使用
开源zk客户端-Curator 创建会话: RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3); CuratorFramewor ...
- java并发编程(一)----线程基础知识
在任何的生产环境中我们都不可逃避并发这个问题,多线程作为并发问题的技术支持让我们不得不去了解.这一块知识就像一个大蛋糕一样等着我们去分享,抱着学习的心态,记录下自己对并发的认识. 1.线程的状态: 线 ...
- Maven 项目使用mybatis的环境搭建-基于xml形式实现查询所有的功能
首先了解一下什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. ...
- 原生js实现分页功能
原生就是实现分页功能 代码如下: var pagination = function(option,fun){ this.parentId = option.id; //容器 this.pageSiz ...
- MySQL里的COUNT
count(*).count(1).count(主键).count(字段)的执行效率 在没有where条件的情况下 MyISAM引擎返回结果会比InnoDB快上很多,主要是因为MyISAM会单独记录了 ...
- 优化Recorder H5录音:可边录边转码上传服务器,支持微信提供Android IOS Hybrid App源码
Recorder H5 GitHub开源库随着支持功能的增多,音频转码处理效率渐渐的跟不上需求了,近期抽时间对音频转码部分进行了升级优化,以支持更多实用的功能. 另外IOS的Hybrid App也完成 ...
- DBUtils框架的使用(下)
刚才讲了使用QueryRunner插入.修改.更新数据,现在来学习一下使用QueryRunner进行数据库表查询. 通过QueryRunner类的query()方法即可完成数据库表的查询操作,但是在查 ...
- 分布式配置中心Apollo——QuickStart
分布式配置中心 剥离配置文件,实现动态修改,自动更新. [假设没有分布式配置中心,修改配置文件后都需要重启服务,对于数量庞多的微服务开发来说,就会非常繁琐] 分布式配置中心有哪些 disconf(依赖 ...
- IO核心子系统
IO核心子系统 一.IO层次结构 IO实现普遍采用了层次式的结构.其基本思想与计算机网络中的层次结构相同:将系统IO的功能组织成一系列的层次,每一层完成整个系统功能的一个子集,其实现依赖于下层完成更原 ...
- Spring Cloud Alibaba | Nacos动态网关路由
Spring Cloud Alibaba | Gateway基于Nacos动态网关路由 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud ...