如何有效地混淆 Python 代码?

我正在寻找如何隐藏我的 Python 源代码。

print "Hello World!"

我如何编码这个例子,使它不是人类可读的?有人告诉我用 Base64但我不知道怎么用。

234395 次浏览

您可以使用 base64模块对字符串进行编码来停止 肩上冲浪,但是如果有人能够访问您的文件,那么这并不能阻止他们找到您的代码。

然后,一旦解码了代码,就可以使用 compile()功能eval() function来执行代码。

>>> import base64
>>> mycode = "print 'Hello World!'"
>>> secret = base64.b64encode(mycode)
>>> secret
'cHJpbnQgJ2hlbGxvIFdvcmxkICEn'
>>> mydecode = base64.b64decode(secret)
>>> eval(compile(mydecode,'<string>','exec'))
Hello World!

因此,如果你有30行代码,你可能会想要加密它,这样做:

>>> f = open('myscript.py')
>>> encoded = base64.b64encode(f.read())

然后,您需要编写第二个脚本来执行 compile()eval(),这个脚本可能将编码后的脚本作为字符串包含在 三重引号中。它看起来像这样:

import base64
myscript = """IyBUaGlzIGlzIGEgc2FtcGxlIFB5d
GhvbiBzY3JpcHQKcHJpbnQgIkhlbG
xvIiwKcHJpbnQgIldvcmxkISIK"""
eval(compile(base64.b64decode(myscript),'<string>','exec'))

这样就不能被人类读懂了?

i mean all the file is encoded !! when you open it you can't understand anything .. ! that what i want

最大限度地,您可以将源编译成字节码,然后只分发字节码。但这也是可逆的。字节码可以被反编译成半可读的源代码。

Base64对于任何人来说都是微不足道的解码,所以它不能作为实际的保护,只能对完全不懂电脑的人“隐藏”源代码。此外,如果你计划通过任何方式实际运行这些代码,你必须在脚本中包含解码器(或者在你的发行版中包含另一个脚本,它需要由合法用户运行) ,这将立即暴露你的编码/加密。

模糊处理技术通常涉及注释/文档剥离、名称混淆、垃圾代码插入等等,因此即使反编译字节码,也得不到非常可读的源代码。尽管如此,它们仍将是 Python 源代码,而且 Python 不善于变成无法阅读的混乱。

如果您确实需要保护某些功能,我建议使用编译语言,如 C 或 C + + ,编译和发布。所以/。然后使用 Python 绑定来保护代码。

As other answers have stated, there really just isn't a way that's any good. Base64 can be decoded. Bytecode can be decompiled. Python was initially just interpreted, and most interpreted languages try to speed up machine interpretation more than make it difficult for human interpretation.

Python 是可读和可分享的,而不是模糊的。关于必须如何格式化代码的语言决策是为了提高不同作者之间的可读性。

混淆 Python 代码实际上并不能与语言相融合。

也许您应该考虑使用一些简单的东西,比如 真密码体积来存储源代码,因为这似乎是您关心的问题。您可以在 usb 密钥上创建一个加密的文件,或者只是对整个卷进行加密(如果代码适合的话) ,这样您就可以在一天结束时随身携带这个密钥。

为了编译,您可以使用类似 PyInstallerPy2exe的程序来创建独立的可执行文件。如果你真的想走更多的英里,看看 封隔器或压缩设备为了增加更多的混淆。如果没有这些选项,您至少可以将脚本编译成字节码,这样它就不会立即可读。请记住,这些方法只会减慢试图调试或反编译程序的人的速度。

我会用说教的方式写出我的答案。

首先在 Python 解释器中输入:

import this

然后,查看 Python 发行版中 Lib 目录中的 this.py文件,并尝试了解它的作用。

然后,看一下文档中的 eval函数:

help(eval)

现在您应该已经找到了一种有趣的方法来保护您的代码。但是要小心,因为那只适用于那些不如你聪明的人!(我不是想冒犯你,任何一个聪明到能够理解你所做的事情的人都可以扭转这种局面)。

这只是一个有限的第一级混淆解决方案,但它是内置的: Python 有一个字节码编译器:

python -OO -m py_compile <your program.py>

产生一个 .pyo文件,其中包含字节码,其中文档字符串被删除等。可以使用 .py扩展名重命名 .pyo文件,并且 python <your program.py>运行方式与程序相似,但不包含源代码。

PS : 您得到的“有限”混淆级别是这样的: 可以恢复代码(使用一些变量名,但没有注释和 docstring)。请参阅第一条注释,了解如何做到这一点。然而,在某些情况下,这种程度的混淆可能被认为是足够的。

PPS : 如果您的程序导入的模块像这样被混淆,那么您需要改为使用 .pyc后缀来重命名它们(我不确定这一点是否会在某一天中断) ,或者您可以使用 .pyo并使用 python -O ….pyo运行它们(导入应该可以工作)。这将允许 Python 查找您的模块(否则,Python 将查找 .py模块)。

我最近偶然发现了这篇 blog 文章: 使用 AST 模糊 Python 源代码,作者在其中谈到了使用内置 AST 模块进行 python 源文件模糊处理。所编译的二进制文件将用于 HitB CTF,因此有严格的混淆要求。

由于可以访问单个 AST 节点,因此使用这种方法可以对源文件执行任意修改。根据所执行的转换,生成的二进制文件可能/可能不完全符合未混淆的源代码的行为。

如果你想做一个半混淆的代码,你可以这样做:

import base64
import zlib
def run(code): exec(zlib.decompress(base64.b16decode(code)))
def enc(code): return base64.b16encode(zlib.compress(code))

并创建如下文件(使用上面的代码) :

f = open('something.py','w')
f.write("code=" + enc("""
print("test program")
print(raw_input("> "))"""))
f.close()

文件“ something. py”:

code = '789CE352008282A2CCBC120DA592D4E212203B3FBD28315749930B215394581E9F9957500A5463A7A0A4A90900ADFB0FF9'

just import "something.py" and run run(something.code) to run the code in the file.

其中一个技巧是让代码难以设计读取: 如果必须记录任何内容,就只给出函数的输出,而不是函数的工作方式。使变量名称非常广泛,电影引用,或相反的例子: btmnsfavclr = 16777215,其中“ btmnsfavclr”意味着“蝙蝠侠最喜欢的颜色”,其值是 16777215或小数形式的“ ffffff”或白色。记住要混合使用不同的命名风格,以保持代码中那些讨厌的人。此外,在这个网站使用技巧: 开发不可维护代码的11大技巧

我会像这样掩盖代码:

def MakeSC():
c = raw_input(" Encode: ")
sc = "\\x" + "\\x".join("{0:x}".format(ord(c)) for c in c)
print "\n shellcode =('" + sc + "'); exec(shellcode)"; MakeSC();

明文:

import os; os.system("whoami")

编码:

Payload = ('\x69\x6d\x70\x6f\x72\x74\x20\x6f\x73\x3b\x20\x6f\x73\x2e\x73\x79\x73\x74\x65\x6d\x28\x22\x77\x68\x6f\x61\x6d\x69\x22\x29'); exec(Payload);

您可以将代码嵌入到 C/C + + 中并进行编译 在另一个应用程序中嵌入 Python

嵌入式

#include <Python.h>


int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]);  /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("print('Hello world !')");
Py_Finalize();
return 0;
}

在 Ubuntu/Debian 中

$ sudo apt-get install python-dev

在 Centos/红帽/软呢帽

$ sudo yum install python-devel

compile with

$ gcc -o embedded -fPIC -I/usr/include/python2.7 -lpython2.7 embedded.c

run with

$ chmod u+x ./embedded
$ time ./embedded
Hello world !


real  0m0.014s
user  0m0.008s
sys 0m0.004s

初始脚本: hello_world.py:

print('Hello World !')

运行脚本

$ time python hello_world.py
Hello World !


real  0m0.014s
user  0m0.008s
sys 0m0.004s

然而,一些字符串的 Python 代码可以在编译的文件中找到

$ grep "Hello" ./embedded
Binary file ./embedded matches


$ grep "Hello World" ./embedded
$

如果需要额外的模糊处理,可以使用 base64

...
PyRun_SimpleString("import base64\n"
"base64_code = 'your python code in base64'\n"
"code = base64.b64decode(base64_code)\n"
"exec(code)");
...

例如:

create the base 64 string of your code

$ base64 hello_world.py
cHJpbnQoJ0hlbGxvIFdvcmxkICEnKQoK

嵌入式 _ base64. c

#include <Python.h>


int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]);  /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("import base64\n"
"base64_code = 'cHJpbnQoJ0hlbGxvIFdvcmxkICEnKQoK'\n"
"code = base64.b64decode(base64_code)\n"
"exec(code)\n");
Py_Finalize();
return 0;
}

all commands

$ gcc -o embedded_base64 -fPIC -I/usr/include/python2.7 -lpython2.7 ./embedded_base64.c
$ chmod u+x ./embedded_base64


$ time ./embedded_base64
Hello World !


real  0m0.014s
user  0m0.008s
sys 0m0.004s


$ grep "Hello" ./embedded_base64
$

更新:

这个项目(pyarmor)也可能有所帮助:

Https://pypi.org/project/pyarmor/

也许你可以试试 混凝土,这是我的开源项目

.pyc加密到 .pye并在导入时解密

通过库 OpenAES 加密和解密

用法

完全加密

  • 将所有的 .py转换为 *.pye

      $ pyconcrete-admin.py compile --source={your py script}  --pye
    $ pyconcrete-admin.py compile --source={your py module dir} --pye
    
  • 删除 *.py *.pyc或将 *.pye复制到其他文件夹

  • Main . py 加密为 总台.pye,普通的 python无法执行。 You must use pyconcrete to process the main.pye script. pyconcrete(前任)将安装在您的系统路径(例如:/usr/local/bin)中

      pyconcrete main.pye
    src/*.pye  # your libs
    

部分加密(pytered 为 lib)

  • 下载 pymix 源代码并通过 setup.py 进行安装

      $ python setup.py install \
    --install-lib={your project path} \
    --install-scripts={where you want to execute pyconcrete-admin.py and pyconcrete(exe)}
    
  • 在您的主脚本中导入 pymix

  • 推荐工程设计图

      main.py       # import pyconcrete and your lib
    pyconcrete/*  # put pyconcrete lib in project root, keep it as original files
    src/*.pye     # your libs
    

尝试将 hello world Python 代码粘贴到以下网站:

Http://enscryption.com/encrypt-and-obfuscate-scripts.html

它将为您生成一个复杂的、经过加密和模糊处理的、但功能齐全的脚本。看看你能不能破解这个脚本并揭示真正的代码。或者看看它提供的复杂程度是否能满足你对内心平静的需求。

通过本站点为您生成的加密脚本应该可以在安装了 python 的任何 Unix 系统上运行。

如果您想用另一种方式加密,我强烈建议您编写自己的加密/模糊算法 (如果安全对你那么重要)。那样的话,除了你没人知道它是怎么运作的。但是,为了让它真正起作用,你必须花费大量的时间在它上面,以确保没有任何漏洞,让那些有大量时间的人可以利用。并确保您使用的工具已经是 Unix 系统的自然... 即 openssl 或 base64。这样,您的加密脚本更加便携。

有多种方法可以混淆代码,这里只有一个例子:

(lambda _, __, ___, ____, _____, ______, _______, ________:
getattr(
__import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
().__class__.__eq__.__class__.__name__[:__] +
().__iter__().__class__.__name__[_____:________]
)(
_, (lambda _, __, ___: _(_, __, ___))(
lambda _, __, ___:
chr(___ % __) + _(_, __, ___ // __) if ___ else
(lambda: _).func_code.co_lnotab,
_ << ________,
(((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
- _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
__) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
<< ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
__) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
<< (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
_) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
(((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
_))) + (_____ << ______) + (_ << ___)
)
)
)(
*(lambda _, __, ___: _(_, __, ___))(
(lambda _, __, ___:
[__(___[(lambda: _).func_code.co_nlocals])] +
_(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
),
lambda _: _.func_code.co_argcount,
(
lambda _: _,
lambda _, __: _,
lambda _, __, ___: _,
lambda _, __, ___, ____: _,
lambda _, __, ___, ____, _____: _,
lambda _, __, ___, ____, _____, ______: _,
lambda _, __, ___, ____, _____, ______, _______: _,
lambda _, __, ___, ____, _____, ______, _______, ________: _
)
)
)

有两种方法可以混淆 Python 脚本

  • 混淆每个代码对象的字节码
  • 模糊 Python 模块的整个代码对象

混淆 Python 脚本

  • 将 python 源文件编译为代码对象

    char * filename = "xxx.py";
    char * source = read_file( filename );
    PyObject *co = Py_CompileString( source, filename, Py_file_input );
    
  • Iterate code object, wrap bytecode of each code object as the following format

    0   JUMP_ABSOLUTE            n = 3 + len(bytecode)
    3
    ...
    ... Here it's obfuscated bytecode
    ...
    
    
    n   LOAD_GLOBAL              ? (__armor__)
    n+3 CALL_FUNCTION            0
    n+6 POP_TOP
    n+7 JUMP_ABSOLUTE            0
    
  • Serialize code object and obfuscate it

    char *original_code = marshal.dumps( co );
    char *obfuscated_code = obfuscate_algorithm( original_code  );
    
  • Create wrapper script "xxx.py", ${obfuscated_code} stands for string constant generated in previous step.

    __pyarmor__(__name__, b'${obfuscated_code}')
    

Run or Import Obfuscated Python Scripts

When import or run this wrapper script, the first statement is to call a CFunction:

int __pyarmor__(char *name, unsigned char *obfuscated_code)
{
char *original_code = resotre_obfuscated_code( obfuscated_code );
PyObject *co = marshal.loads( original_code );
PyObject *mod = PyImport_ExecCodeModule( name, co );
}

这个函数接受2个参数: 模块名和模糊代码,然后

  • 还原模糊代码
  • 通过原始代码创建代码对象
  • 导入原始模块 < strong > (这将导致 回溯)

运行或导入模糊字节码

导入模块后,当调用此模块中的任何代码对象时 第一次,从上一节描述的封装字节码中,我们 知道

  • 第一个行动 JUMP_ABSOLUTE跳跃到偏移 n

  • 在偏移量 n 处,指令是调用一个 PyCfunction 将恢复偏移量3和 n 之间的那些模糊字节码,并且 将原始字节码放置在偏移量0

  • 函数调用之后,最后一条指令跳回 偏移量0。现在执行真正的字节码

请参阅 火焰盔甲

试试这个巨蟒模糊处理器:

Pyob.oxyry.com Pyob.oxyy.c

__all__ = ['foo']


a = 'a'
_b = 'b'


def foo():
print(a)


def bar():
print(_b)


def _baz():
print(a + _b)


foo()
bar()
_baz()

将被翻译成

__all__ =['foo']#line:1
OO00OO0OO0O00O0OO ='a'#line:3
_O00OO0000OO0O0O0O ='b'#line:4
def foo ():#line:6
print (OO00OO0OO0O00O0OO )#line:7
def O0000000OOOO00OO0 ():#line:9
print (_O00OO0000OO0O0O0O )#line:10
def _OOO00000O000O0OOO ():#line:12
print (OO00OO0OO0O00O0OO +_O00OO0000OO0O0O0O )#line:13
foo ()#line:15
O0000000OOOO00OO0 ()#line:16
_OOO00000O000O0OOO ()#line:17

Cython

看来这个问题的答案是 Cython。我很惊讶居然没人提起过?这是主页: https://cython.org

简而言之,这将把 python 转换为 C 并编译它,从而使它像任何“普通”编译的可发布 C 程序一样受到很好的保护。

但也有局限性。我自己还没有深入地探索它们,因为当我开始阅读它们的时候,我为了自己的目的而放弃了这个想法。但对你来说可能还是有用的。从本质上讲,您不能充分使用 Python,因为它提供了动态的令人敬畏的特性。一个突出的主要问题是,关键字参数不可用: (必须仅使用位置参数编写函数调用。我没有确认这一点,但我怀疑您可以使用条件导入或评估。我不知道如何处理多态性..。

无论如何,如果您不想在事后混淆庞大的代码库,或者理想情况下,如果您首先想到的是使用 Cython,那么这是一个非常值得注意的选择。

奥佩

Https://github.com/qquick/opy

Opy 会混淆你的广泛,真实的世界,多模块 Python 源代码免费! 您选择每个项目什么混淆和 什么不是,通过编辑配置文件:

You can recursively exclude all identifiers of certain modules from obfuscation.
You can exclude human readable configuration files containing Python code.
You can use getattr, setattr, exec and eval by excluding the identifiers they use.
You can even obfuscate module file names and string literals.
You can run your obfuscated code from any platform.

不像其他一些选项张贴,这对 Python2和3都适用!它也是免费/开源的,而且它不像其他一些工具那样只是一个在线工具(除非你付费)。

我承认我自己仍然在评估这个问题,但是所有的初始测试都非常完美。看来这正是我要找的!

The official version runs as a standalone utility, with the original intended design being that you drop a script into the root of the directory you want to obfuscate, along with a config file to define the details/options you want to employ. I wasn't in love with that plan, so I added a fork from project, allowing you to import and utilize the tool from a library instead. That way, you can roll this directly into a more encompassing packaging script. (You could of course wrap multiple py scripts in bash/batch, but I think a pure python solution is ideal). I requested my fork be merged into the original work, but in case that never happens, here's the url to my revised version:

https://github.com/BuvinJT/Opy

The best way to do this is to first generate a .c file, and then compile it with tcc to a .pyd file
注意: 只适用于 Windows

规定

  1. TCC
  2. 混淆视听
  3. Cython

安装:

sudo pip install -U cython

混淆.py 文件:

pyobfuscate.py myfile.py >obfuscated.py

要生成.c 文件,

  1. 将一个 init<filename>()函数添加到. py 文件 可选

  2. cython --embed file.py

  3. cp Python.h tcc\include

  4. tcc file.c -o file.pyd -shared -I\path\to\Python\include -L\path\to\Python\lib

  5. import .pyd file into app.exe

Check out these tools for 混淆视听 and 缩小 of python code:

  • 使用十六进制编码的完全混淆; 显然不允许仅对变量/函数名进行部分混淆
  • python-minifier, https://pypi.org/project/python-minifier/ - minifies the code and obfuscates function/variable names (although not as intensely as pyminifier below)
  • Pyminifier,https://pypi.org/project/pyminifier/-在混淆函数、变量和文字的名称方面做得很好; 也可以执行类似于 pyarmare 的十六进制编码(压缩)。问题: 混淆后的代码可能包含语法错误,不运行。

Example .py output from pyminifier when run with --obfuscate and --gzip:

$pyminifier —— obfuscate —— gzip/tmp/tumult.py

#!/usr/bin/env python3
import zlib, base64
exec(zlib.decompress(base64.b64decode('eJx1kcFOwzAMhu95ClMO66apu0/KAQEbE5eJC+IUpa27haVJ5Ljb+vakLYJx4JAoiT/7/+3c3626SKvSuBW6M4Sej96Jq9y1wRM/E3kSexnIOBZObrSNKI7Sl59YsWDq1wLMiEKNrenoYCqB1woDwzXF9nn2rskZd1jDh+9mhOD8DVvAQ8WdtrZfwg74aNwp7ZpnMXHUaltk878ybR/ZNKbSjP8JPWk6wdn72ntodQ8lQucIrdGlxaHgq3QgKqtjhCY/zlN6jQ0oZZxhpfKItlkuNB3icrE4XYbDwEBICRP6NjG1rri3YyzK356CtsGwZuNd/o0kYitvrBd18qgmj3kcwoTckYPtJPAyCVzSKPCMNErs85+rMINdp1tUSspMqVYbp1Q2DWKTJpcGURRDr9DIJs8wJFlKq+qzZRaQ4lAnVRuJgjFynj36Ol7SX/iQXr8ANfezCw==')))
# Created by pyminifier.py (https://github.com/liftoff/pyminifier)

这个输出对应于一个40行的原始输入脚本,如 给你所示。

我知道这是个老问题了。只是想加上我搞笑的模糊的“你好,世界!”及一些提示;)

#//'written in c++'


#include <iostream.h>
#define true false
import os
n = int(input())
_STACK_CALS=  [ ];
_i_CountCals__= (0x00)
while os.urandom(0x00 >> 0x01) or (1 & True):
_i_CountCals__+= 0o0;break;# call shell command echo "hello world" > text.txt
""#print'hello'
__cal__= getattr( __builtins__  ,'c_DATATYPE_hFILE_radnom'[ 0x00 ]+'.h'[-1]+'getRndint'[3].lower() )
_o0wiXSysRdrct   =eval (  __cal__(0x63) + __cal__(104) + 'r_RUN_CALLER'[0] );
_i1CLS_NATIVE=  getattr (__builtins__ ,__cal__(101)+__cal__(118  )+_o0wiXSysRdrct ( 0b1100001 )+'LINE 2'[0].lower( ))#line 2 kernel call
__executeMAIN_0x07453320abef  =_i1CLS_NATIVE ( 'map');
def _Main():
raise 0x06;return 0 # exit program with exit code 0
def _0o7af():_i1CLS_NATIVE('_int'.replace('_', 'programMain'[:2]))(''.join(  __executeMAIN_0x07453320abef( _o0wiXSysRdrct ,_STACK_CALS)));return;_Main()
for _INCREAMENT in [0]*1024:
_STACK_CALS= [0x000 >> 0x001 ,True&False&True&False ,'c++', 'h', 'e', 'l', 'o',' ', 'w', 'o', 'r', 'l', 'd']
   

#if
for _INCREAMENT in [0]*1024:
_STACK_CALS= [40, 111, 41, 46, 46] * n
    

""""""#print'word'
while True:
break;
_0o7af();
while os.urandom(0x00 >> 0xfa) or (1 & True): # print "Hello, world!"
_i_CountCals__-= 0o0;break;
while os.urandom(0x00 >> 0x01) or (1 & True):
_i_CountCals__ += 0o0;
break;

手动操作是可行的,我的建议是:

  • 对加密字符串使用 eval和/或 exec

  • 使用 [ord(i) for i in s]/''.join(map(chr, [list of chars goes here]))作为简单的加密/解密

  • 使用模糊的变量名

  • make it unreadable

  • 不要只写1或 True,写 1&True&0x00000001;)

  • 使用不同的数字系统

  • 添加令人困惑的注释,如第10行的“第2行”或 while 循环的“它返回0”。

  • use __builtins__

  • 使用 getattrsetattr

Here's my very noob approach for something I'm doing in CircuitPython. It's currently partially tested. I've posted in this state because I thought it might be useful.

有两种观点:

  • 以逗号分隔的输入文件列表
  • 以逗号分隔的输出文件列表

它是这样的:

  1. Find all the variable names by looking at method signatures and the left-hand-sides of assignments, and all the import aliases.
  2. 隔离一些我不想修改的东西。
  3. 用无意义的标记替换其余的名称。
  4. 解除我隔离的东西的检疫。

它把代码变成这样

degreeIncrement = 90
durationIncrement = 0.25
def GetEditGlyphParams(self, waveform, editIndex):
segments = waveform.leftSegments
waveformFunctionCount =  len(self.waveformFunctions)
totalParameterCount = 0
segmentIndex = 0
while segmentIndex < len(segments):
segment = segments[segmentIndex]
segmentParameterCount = len(self.sineFunctions)
if segment.type == "line":
segmentParameterCount = len(self.lineFunctions)

变成这样的代码:

a6 = 90 # degreeIncrement = 90
a7 = 0.25 # durationIncrement = 0.25
def a8(a9, a10, a11): # def GetEditGlyphParams(self, waveform, editIndex):
a12 = a10.leftSegments # segments = waveform.leftSegments
a13 =  len(a9.a5) # waveformFunctionCount =  len(self.waveformFunctions)
a14 = 0 # totalParameterCount = 0
a15 = 0 # segmentIndex = 0
while a15 < len(a12): # while segmentIndex < len(segments):
a16 = a12[a15] # segment = segments[segmentIndex]
a17 = len(a9.a3) # segmentParameterCount = len(self.sineFunctions)
if a16.a332 == "line": # if segment.type == "line":
a17 = len(a9.a4) # segmentParameterCount = len(self.lineFunctions)

如果需要,可以省略注释。

下面是这样做的代码:

import sys, re
sourceDirectory = sys.argv[1]
print("sourceDirectory", sourceDirectory)
sourceFiles = sys.argv[2].split(",")
targetFiles = sys.argv[3].split(",")


if len(sourceFiles) != len(targetFiles):
raise Exception("Source file count must match target file count. Use comma to separate.")


print("uglify", sys.argv[1])


names = []
translations = []


class Analyser:
def AnalyseLines(self, lines):
for line in lines:
self._AnalyseLine(line)
def _AnalyseLine(self, line):
parts = self._GetParts(line)
if len(parts) > 1 and parts[0] == "import":
self._AnalyseImport(parts)
if len(parts) > 1 and parts[0] == "class":
self._AnalyseClass(parts)
if len(parts) > 1 and parts[1] == "=":
self._AnalyseAssignment(parts)
if len(parts) > 1 and parts[0] == "def":
self._AnalyseDef(parts)
def _GetParts(self, line):
minusTabs = line.strip().replace("\t", " ")
minusOpenSquare = minusTabs.replace("[", " ")
minusCloseSquare = minusOpenSquare.replace("]", " ")
minusDoubleSpace = minusCloseSquare.replace("  ", " ")
parts = minusDoubleSpace.split(" ")
while "#" in parts:
del parts[-1]
while len(parts) > 0 and parts[0] == "":
del parts[0]
nonEmptyParts = []
for part in parts:
if len(part) > 0:
nonEmptyParts.append(part)
return nonEmptyParts
def _AddName(self, name, elementType):
nameToAppend = name # + " " + elementType
if nameToAppend in names:
return
if nameToAppend == "sin" or nameToAppend == "value":
print("--> adding", nameToAppend, "as", elementType)
names.append(nameToAppend)
translation = "a" + str(len(names))
translations.append((name, translation))
def _AnalyseImport(self, parts):
if len(parts) == 4 and parts[0] == "import" and parts[2] == "as":
self._AddName(parts[3], "import")
def _AnalyseClass(self, parts):
p1 = parts[1].split(":")
p2 = p1[0].split("(")
self._AddName(p2[0], "class")
def _AnalyseAssignment(self, parts):
mutableName = parts[0].split(".")[0]
self._AddName(mutableName, "assignment")
def _AnalyseDef(self, parts):
methodNameParts = parts[1].split("(")
if methodNameParts[0] == "__init__":
return
self._AddName(methodNameParts[0], "method")
if len(methodNameParts) > 1:
self._AddName(methodNameParts[1].replace(",", "").replace("):", ""), "param1")
for part in parts[2:]:
params = part.split(",")
for param in params:
if param != "":
if param.replace(":", "").replace(")", "") == "value":
print("found value amongst", parts)
self._AddName(param.replace(":", "").replace(")", ""), "paramN")


class Translator:
def TranslateLines(self, content):
oldLines = content.split("\n")
content = content.replace('"', "_QUOTE_").replace("\\", "_BACKSLASH_")
for (oldWord, newWord) in translations:
content = re.sub(r"\b%s\b" % oldWord, newWord, content)
content = content.replace("_QUOTE_", '"').replace("_BACKSLASH_", "\\")
newLines = content.split("\n")
for i in range(len(newLines) - 1):
if newLines[i] != "":
newLines[i] += " # " + oldLines[i].strip()
return "\n".join(newLines)
def TranslateLines2(self, content):
oldLines = content.split("\n")
newLines = []
for lineNumber, oldLine in enumerate(oldLines):
# print("translating line of length", len(oldLine), ":", oldLine)
content = oldLine.split(" # ")[0]
if len(content.strip(" \t")) > 0:
content = content.replace('"', "_QUOTE_").replace("\\", "_BACKSLASH_")
for (oldWord, newWord) in translations:
try:
content = re.sub(r"\b%s\b" % oldWord, newWord, content)
except:
print("problem translating", oldWord, "into", newWord)
raise Exception("error in translation")
content = content.replace("_QUOTE_", '"').replace("_BACKSLASH_", "\\")
newLines.append(content + " # " + oldLine.strip())
return "\n".join(newLines)


lines = []


for i, sourceFileName in enumerate(sourceFiles):
names.append(sourceFileName)
targetFileName = targetFiles[i]
translations.append((sourceFileName, targetFileName))


for sourceFileName in sourceFiles:
fullFileName = sourceDirectory + sourceFileName + ".py"
sourceFile = open(fullFileName, 'r')
content = sourceFile.read()
fileLines = content.split("\n")
lines.extend(fileLines)
print("found", len(fileLines), "lines in", sourceFileName)


print("----------------")
print("found a total of", len(lines), "lines")
print("----------------")
analyser = Analyser()
analyser.AnalyseLines(lines)


for i, name in enumerate(names):
if len(name) < 1:
print("deleting name", i, "because it is zero length")
names.remove(name)
translation = translations[i]
translations.remove(translation)


# print(names)


# raise Exception("Not implemented beyond here.")
translator = Translator()


for i, sourceFileName in enumerate(sourceFiles):
print("translating", sourceFileName, "into", targetFiles[i])
fullFileName = sourceDirectory + sourceFileName + ".py"
targetFileName = sourceDirectory + targetFiles[i] + ".py"
sourceFile = open(fullFileName, 'r')
content = sourceFile.read()
targetFile = open(targetFileName, 'w')
fileLines = content.split("\n")
newContent = translator.TranslateLines2(content)
targetFile.write(newContent)
sourceFile.close()
targetFile.close()


# print(len(lines), "lines, starting with", lines[0])
# print(names)
# print(translations)

努伊特卡

I would really recommend 努伊特卡 over Cython. Nuitka also compiles Python to native platform code providing a similar level of obfuscation like compiled C code.

python -m pip install nuitka
python -m nuitka --follow-imports --include-package urllib3.util.ssl_ myprogram.py
./myprogram.bin
  • --follow-imports在包含所有导入模块方面做得很好。
  • 如果在启动编译程序时某些导入被隐藏并且丢失了,那么传递额外的包会很有帮助。

添加标志 --onefile-standalone,如果这样可以获得用于分发的包。

我在这里也引用了 pyarmor,但是 pytransform.sopytransform.dll共享对象是 pyarmor的核心,它是封闭源代码,在我的项目中是一个阻塞器。