用 Cython 生成的可执行文件真的没有源代码吗?

Are executables produced with Cython really free of the source code?

我已阅读Making an executable in Cython and BuvinJ's answer to How to obfuscate Python code effectively? and would like to test if the source code compiled with Cython is really "no-more-there" after the compilation. It is indeed a popular opinion that using Cython is a way to protect a Python source code, see for example the article Protecting Python Sources With Cython

让我们举个简单的例子test.pyx:

import json, time  # this will allow to see what happens when we import a library
print(json.dumps({'key': 'hello world'}))
time.sleep(3)
print(1/0)  # division error!

那我们就用Cython吧:

cython test.pyx --embed

这会产生 test.c。让我们编译它:

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
cl test.c /I C:\Python37\include /link C:\Python37\libs\python37.lib

有效!它生成一个 140KB test.exe 的可执行文件,不错!

但是在这个回答How to obfuscate Python code effectively?中隐含地说这个“编译”会隐藏源代码。 好像不对,如果你运行test.exe,你会看到:

Traceback (most recent call last):
  File "test.pyx", line 4, in init test
    print(1/0)  # division error!         <-- the source code and even the comments are still there!
ZeroDivisionError: integer division or modulo by zero

这表明 人类可读形式的源代码仍然存在

问题:有没有办法用Cython编译代码,使得“不再公开源代码”的说法是真的?

注意:我正在寻找一种既不存在源代码也不存在字节码 (.pyc) 的解决方案(如果嵌入了字节码/.pyc,使用 [=20= 恢复源代码是微不足道的])


PS:我记得几年前我也做过同样的观察,但我找不到了,经过深入研究,这里是:

代码可以在您的 exe 旁边的原始 pyx 文件中找到。 Delete/don不要将此 pyx 文件与您的 exe 一起分发。


当您查看生成的 C 代码时,您会明白为什么您的可执行文件会显示错误消息:

对于引发的错误,Cython 将发出类似于以下的代码:

__PYX_ERR(0, 11, __pyx_L3_error) 

其中 __PYX_ERR 是定义为的宏:

#define __PYX_ERR(f_index, lineno, Ln_error) \
{ \
  __pyx_filename = __pyx_f[f_index]; __pyx_lineno = lineno; __pyx_clineno = __LINE__; goto Ln_error; \
}

变量 __pyx_f 定义为

static const char *__pyx_f[] = {
  "test.pyx",
  "stringsource",
};

基本上__pyx_f[0] 告诉了在哪里可以找到原始代码。现在,当引发异常时,(嵌入式)Python 解释器会查找您的原始 pyx 文件并找到相应的代码(这可以在引发错误时调用的 __Pyx_AddTraceback 中查找).

一旦此 pyx 文件不存在,原始源代码将不再为 Python interpreter/anybody 其他人所知。但是,错误跟踪仍将显示函数名称和行号,但不再显示任何代码片段。

生成的可执行文件(或扩展名,如果有人创建的话)不包含任何字节码(如在 pyc 文件中)并且不能使用 uncompyle 等工具反编译:字节码是在 py 文件是翻译成 Python-操作码,然后在 a huge loop in ceval.c. Yet for builtin/cython modules no bytecode is needed because the resulting code uses directly Python's C-API, cutting out the need to have/evaluate the opcodes - these modules skip interpretation, which a reason for them 中进行评估。因此可执行文件中不会有字节码。

不过有一个重要的注意事项:应该检查链接器是否不包含调试信息(因此可以在 C 代码中找到 pyx 文件内容作为注释)。 MSVC with /Z7 options 就是这样一个例子。


但是,可以将生成的可执行文件反汇编为汇编程序,然后可以对生成的 C 代码进行逆向工程 - 因此,虽然 cythonizing 可以使代码难以理解,但它不是隐藏密钥的正确工具或安全算法。