几年前,当我准备发布我的第一个python应用程序时,我不得不面对一个问题:如何混淆python脚本,所以没有向我的客户显示明确的代码?
答案 0 :(得分:1)
现在我有一个解决方案
首先将Python脚本编译为代码对象
char *filename = "foo.py";
char *source = read_file( filename );
PyCodeObject *co = Py_CompileString( source, "<frozen foo>", Py_file_input );
接下来,按以下方式更改此代码对象
在co_code
块
try...finally
换行标题:
LOAD_GLOBALS N (__armor_enter__) N = length of co_consts
CALL_FUNCTION 0
POP_TOP
SETUP_FINALLY X (jump to wrap footer) X = size of original byte code
更改了原始字节代码:
Increase oparg of each absolute jump instruction by the size of wrap header
Obfuscate original byte code
...
包装页脚:
LOAD_GLOBALS N + 1 (__armor_exit__)
CALL_FUNCTION 0
POP_TOP
END_FINALLY
将函数名称__armor_enter
,__armor_exit__
附加到co_consts
将co_stacksize
增加2
在co_flags
以递归方式更改co_consts
然后序列化这个重组的代码对象,对其进行模糊处理以保护常量和文字字符串
char *string_code = marshal.dumps( co );
char *obfuscated_code = obfuscate_algorithm( string_code );
最后生成混淆的脚本
sprintf( buf, "__pyarmor__(__name__, __file__, b'%s')", obfuscated_code );
save_file( "dist/foo.py", buf );
混淆的脚本是一个普通的Python脚本,看起来像这样
__pyarmor__(__name__, __file__, b'\x01\x0a...')
为了通过常见的Python Interpreter运行obfuscted脚本dist/foo.py
,
需要将3个函数添加到模块builtins
:__pyarmor__
,__armor_enter__
,__armor_exit__
__pyarmor__
,它会从模糊代码中导入原始模块
static PyObject *
__pyarmor__(char *name, char *pathname, unsigned char *obfuscated_code)
{
char *string_code = restore_obfuscated_code( obfuscated_code );
PyCodeObject *co = marshal.loads( string_code );
return PyImport_ExecCodeModuleEx( name, co, pathname );
}
__armor_enter__
static PyObject *
__armor_enter__(PyObject *self, PyObject *args)
{
// Got code object
PyFrameObject *frame = PyEval_GetFrame();
PyCodeObject *f_code = frame->f_code;
// Increase refcalls of this code object
// Borrow co_names->ob_refcnt as call counter
// Generally it will not increased by Python Interpreter
PyObject *refcalls = f_code->co_names;
refcalls->ob_refcnt ++;
// Restore byte code if it's obfuscated
if (IS_OBFUSCATED(f_code->co_flags)) {
restore_byte_code(f_code->co_code);
clear_obfuscated_flag(f_code);
}
Py_RETURN_NONE;
}
__armor_exit__
static PyObject *
__armor_exit__(PyObject *self, PyObject *args)
{
// Got code object
PyFrameObject *frame = PyEval_GetFrame();
PyCodeObject *f_code = frame->f_code;
// Decrease refcalls of this code object
PyObject *refcalls = f_code->co_names;
refcalls->ob_refcnt --;
// Obfuscate byte code only if this code object isn't used by any function
// In multi-threads or recursive call, one code object may be referened
// by many functions at the same time
if (refcalls->ob_refcnt == 1) {
obfuscate_byte_code(f_code->co_code);
set_obfuscated_flag(f_code);
}
// Clear f_locals in this frame
clear_frame_locals(frame);
Py_RETURN_NONE;
}