├── code_obfuscate ├── utils.py ├── __init__.py └── obfuscate.py ├── pyc_example.py ├── py_example.py ├── code_example.py ├── code_replace.py ├── .gitignore └── README.md /code_obfuscate/utils.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import struct 3 | import random 4 | 5 | 6 | def sum_jump(index): 7 | ''' 8 | 计算长度,返回的内容可以写在pyc关于长度的位置 9 | :param length: 计算的长度 10 | :return: 返回长度字节 11 | ''' 12 | return struct.pack("90 即为有参数 19 | :return: 20 | ''' 21 | return chr(random.randint(90, 255)) + chr(random.randint(0, 255)) + chr(random.randint(0, 255)) 22 | -------------------------------------------------------------------------------- /pyc_example.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 简单的读取pyc文件,并且进行混淆 4 | ''' 5 | from code_obfuscate import PycFileObfuscate, advanced, insert_Identification 6 | from functools import partial 7 | 8 | pyf_obfu = PycFileObfuscate("/Users/minisys/PycharmProjects/scan_core/lscore/obfus/tmp.pyc") 9 | for line in range(200): 10 | pyf_obfu.obfuscate(advanced) 11 | for line in range(100): 12 | pyf_obfu.obfuscate(partial(insert_Identification, 13 | "\r\n\r\n=======================================\r\n\r\n这里不知道是什么东西\r\n\r\n=======================================\r\n\r\n")) 14 | for x in range(100): 15 | pyf_obfu.obfuscate(partial(insert_Identification, "看个鸡儿")) 16 | 17 | pyf_obfu.change_all_filename("望咩啊,死仔") 18 | pyf_obfu.write("/Users/minisys/tmp.pyc") 19 | -------------------------------------------------------------------------------- /py_example.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 简单的对py文件混淆的输出,py文件实际上的过程是会被编译成pyc文件,然后读字节码 4 | ''' 5 | from code_obfuscate import PyFileObfuscate, advanced, insert_Identification 6 | from functools import partial 7 | 8 | pyf_obfu = PyFileObfuscate("/Users/minisys/PycharmProjects/scan_core/lscore/obfus/tmp.py") 9 | for line in range(200): 10 | pyf_obfu.obfuscate(advanced) 11 | for line in range(100): 12 | pyf_obfu.obfuscate(partial(insert_Identification, 13 | "\r\n\r\n=======================================\r\n\r\n测试的字符串\r\n\r\n=======================================\r\n\r\n")) 14 | for x in range(100): 15 | pyf_obfu.obfuscate(partial(insert_Identification, "望咩啊,死仔")) 16 | 17 | pyf_obfu.change_all_filename("望咩啊,死仔") # 这里会暴露编译的位置,该方法会同时递归处理常量中的文件名信息 18 | pyf_obfu.write("/Users/minisys/tmp.pyc") 19 | -------------------------------------------------------------------------------- /code_example.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 将函数的code对象插入混淆指令,然后执行,其实可以再塞回到函数内的 4 | ''' 5 | from code_obfuscate import CodeObfuscate, advanced, insert_Identification 6 | import time 7 | import dis 8 | import sys 9 | 10 | 11 | def hello(): 12 | for x in range(10): 13 | for y in range(10): 14 | for z in range(10): 15 | print(x + y + z) 16 | if x + y + z == 10: 17 | continue 18 | 19 | 20 | dis.dis(hello.func_code) 21 | import time 22 | 23 | time.sleep(0.2) 24 | 25 | pycode_obfu = CodeObfuscate(hello.func_code) # 取函数的code部分,进行混淆 26 | for line in range(200): 27 | pycode_obfu.obfuscate(advanced) 28 | code = pycode_obfu.get_code() 29 | print(code) 30 | try: 31 | dis.dis(code) 32 | except: 33 | time.sleep(0.2) 34 | sys.stderr.write("\r\n解析字节码出现问题\r\n") 35 | time.sleep(0.2) 36 | exec (code) -------------------------------------------------------------------------------- /code_replace.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 替换函数体,将函数体内原本字节码的位置存储数据并且解压缩执行的例子 4 | ''' 5 | from code_obfuscate import CodeObfuscate, advanced 6 | import marshal 7 | import types 8 | import zlib 9 | 10 | 11 | def add(): 12 | print("你好,沙雕") 13 | 14 | 15 | t_code = ''' 16 | import zlib 17 | import marshal 18 | def add(a,b): 19 | return a+b 20 | print "Run Func 'add' func_code" 21 | exec (marshal.loads(zlib.decompress(add.func_code.co_code[::-1]))) 22 | ''' 23 | 24 | t_insert_code = ''' 25 | print("xdcfgvbhjnkjbhvgcf") 26 | print("这里是测试要加密的代码") 27 | ''' 28 | c = compile(t_code, "", "exec") 29 | c1 = compile(t_insert_code, "", "exec") # 被插入的对象,用于替换t_code中的add函数 30 | c = CodeObfuscate(c) 31 | for x in range(100): 32 | c.obfuscate(advanced) 33 | 34 | def joinFunc(func, code): 35 | ''' 36 | 拼接到函数的code对象中,这里是为了字节码对象的最后一步包装 37 | 这个函数并不能返回回去继续调用,只是为了包装code对象成为func对象的字节码,也就是存储在代码段的数据 38 | 39 | :param func: 传入一个函数(该函数无各种需要,code对象只是为了放在这个func对象中,func对外还是有显示的,所以开放给用户) 40 | :param code: 传入一个code对象,这个code对象是需要放在func的body中的code 41 | :return: 42 | ''' 43 | # dump code对象,进行压缩,就真的可以放到代码段的位置了 44 | code = marshal.dumps(code) 45 | code = zlib.compress(code, 9) 46 | f = CodeObfuscate(func.func_code) 47 | f.co_code = code[::-1] 48 | code = f.get_code() 49 | f = types.FunctionType(code, func.func_globals, func.func_name, 50 | func.func_defaults, func.func_closure) 51 | return f 52 | 53 | # 拼接code对象到函数内,函数就算是可以获取到内容也无所谓,毕竟无法进行编译 54 | f = joinFunc(add, c1) 55 | 56 | c.co_consts[2] = f.func_code 57 | 58 | # 看似内置有一个对象,实则是一个隐藏了内容的对象,这段内容并不放在字节码中,有可能被误解成垃圾数据丢掉 59 | print(f.func_code) 60 | code = c.get_code() 61 | print("=" * 100) 62 | exec (code) 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # code_obfuscate 2 | 3 | python字节码简单混淆类 4 | 5 | ## 工作原理 6 | > 对python字节码`co_code`属性内容进行垃圾数据的插入 7 | 8 | ## 使用方式 9 | 10 | from code_obfuscate import PycFileObfuscate, advanced, insert_Identification 11 | 12 | c = PycFileObfuscate("xxx.pyc") # 打开一个pyc文件 13 | for line in range(200): # 使用 advanced 混淆器,插入垃圾指令200次 14 | pyf_obfu.obfuscate(advanced) 15 | 16 | # 在字节码中贴上自己的版权信息,通过cat xxx.pyc可以看到 17 | pyf_obfu.obfuscate(partial(insert_Identification, "\r\n\r\n=======================================\r\n\r\n这里不知道是什么东西\r\n\r\n=======================================\r\n\r\n")) 18 | pyf_obfu.change_all_filename("望咩啊,死仔") # 更改所有code对象的文件名,方式信息的泄漏 19 | pyf_obfu.write("aaa.pyc") # 输出到其他文件 20 | 21 | 上面的例子导入的是 `PycFileObfuscate` 是对pyc文件混淆的类,同时还有 `PyFileObfuscate` 是对py文件编译后混淆的类(如果没有编译,会自动调用编译),以及 `CodeObfuscate` ,就是对字节码混淆的类 22 | 23 | ## 混淆函数 24 | 25 | 这个东西我习惯叫混淆器,感觉上逼格高一点 26 | 27 | 就是在 `code_obfuscate/__init__.py` 中的几个负责产生垃圾指令的函数 28 | 29 | 分别是 30 | 31 | overlapping_Instruction 32 | ordinary 33 | insert_Identification 34 | advanced 35 | 36 | 这四个函数除了 `insert_Identification`的参数是两个,其余的参数都是1个,`pyf_obfu.obfuscate`在调用的时候,传入一个生成垃圾代码的混淆器 37 | 38 | `pyf_obfu.obfuscate`会调用这个传入的函数,然后分割指令集,找到合适的位置,把这个位置给传入的函数,就是上面这四个函数,当然也可以自己写,然后返回一段字节码,返回的字节码会进行计算,并且修正调用的位置 39 | 40 | 因为`insert_Identification` 函数比较特殊,需要传入你想要插进去的信息,所以可以使用如下的操作方式 41 | 42 | from functools import partial 43 | insert_Identification = partial(insert_Identification,"你要插入的信息") 44 | 45 | `partial` 可以给函数添加默认参数,上面这个例子就是会给`insert_Identification` 的第一个参数设置参数为`"你要插入的信息"` 如果对上面包装后的 `insert_Identification` 进行调用,只能传入一个参数,就是第二个参数的index 46 | 47 | 下面详细介绍一下这四个函数 48 | 49 | ### overlapping_Instruction 50 | 51 | 重叠字节码混淆,这个混淆只可以用一次,就是在最后一次混淆,因为字节码重叠了 52 | 53 | 程序计算的时候会出现问题,最后就会导致文件失效 54 | 55 | 每使用一次该混淆器,字节码会增加9或12字节码 56 | 57 | 参数:`index` 整数 上层通知混淆的位置,就是当前要放置的位置,返回的payload会自动计算 58 | 59 | 返回:产生的字节码,上层会自动计算 60 | 61 | ### ordinary 62 | 一个可以多次使用并且工作状态良好的混淆器,这个混淆器一般没什么问题,只是有一条绝对跳转加上一条混淆指令 63 | 64 | 每使用一次该混淆器,字节码会增大6字节的长度,也就是两条指令 65 | 66 | 参数:`index` 整数 上层通知混淆的位置,就是当前要放置的位置,返回的payload会自动计算 67 | 68 | 返回:产生的字节码,上层会自动计算 69 | 70 | ### insert_Identification 71 | 72 | 插入标识,该标识主要是有一个长的字符串构成(该函数主要是为了什么版权信息加进去的,就是对字节码进行`cat`的时候看得到的一段信息) 73 | 74 | 75 | 警告: 这个混淆部分推荐是只插入一次,插入多了怕插进去的信息出问题 76 | 77 | 每使用一次该混淆器,字节码会增加`信息长度`加3字节码 78 | 79 | 该函数目前测试如果`汉字`加`数字` `等于号`,`换行`,多次混淆不会出问题,但是加入字母推荐只使用一次,加入字母容易计算错误 80 | 81 | 参数:`msg` 文本 需要插入到字节码中的信息 82 | 83 | 参数:`index` 整数 上层程序传入给这个函数的参数,其值为将要将这个数据插入的位置,计算好自己要插入的数据跳转的位置 84 | 85 | 返回:产生的字节码,上层会自动计算 86 | 87 | ### advanced 88 | 这个混淆器会先获取`1-20`的随机数,根据随机数获取行的数量,然后放到一起 89 | 90 | 每使用一次该混淆器,字节码会增加信息长度加随机数三倍加3字节码 91 | 92 | 参数:`index` 整数 上层程序传入给这个函数的参数,其值为将要将这个数据插入的位置,计算好自己要插入的数据跳转的位置 93 | 94 | 返回:产生的字节码,上层会自动计算 95 | -------------------------------------------------------------------------------- /code_obfuscate/__init__.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 这里面的函数都是混稀器 4 | ''' 5 | from opcode import HAVE_ARGUMENT 6 | from .obfuscate import PycFileObfuscate, PyFileObfuscate, CodeObfuscate 7 | from .utils import sum_jump, generate_line 8 | import random 9 | 10 | 11 | def overlapping_Instruction(index): 12 | ''' 13 | 重叠字节码混淆,⚠️,这个混淆只可以用一次,就是在最后一次混淆,因为字节码重叠了 14 | 程序计算的时候会出现问题,最后就会导致文件失效 15 | 16 | 请勿觉得自己很屌或者随便运行一下觉得没问题就行了,这个是真的会乱,跳转算不出来 17 | 18 | 每使用一次该混淆器,字节码会增加9或12字节码 19 | :param index: 上层通知混淆的位置,就是当前要放置的位置,返回的payload会自动计算 20 | :return: 21 | ''' 22 | 23 | # 混淆内容的关键位置 24 | change = ( 25 | "\x71\x04\x00" + # jump 04 26 | "\x64\x71\x07" + # LOAD_CONST 1905 27 | "\x00", # -- -- stop code 28 | "\x64", "??,??", # code 29 | ) 30 | ret = "\x71%s" % sum_jump(index + 4) # 一次跳转 31 | two_jump = sum_jump(index + 9) # 二次跳转 32 | if ord(two_jump[-1]) >= HAVE_ARGUMENT: # 如果跳的有点远,需要重新计算 33 | two_jump = sum_jump(index + 12) 34 | two_jump += "\xff\xff\x00" 35 | ret += "\x64\x71%s\x64\xff" % two_jump 36 | return ret 37 | 38 | 39 | def ordinary(index): 40 | ''' 41 | 一个可以多次使用并且工作状态良好的混淆器,这个混淆器一般没什么问题,只是有一条绝对跳转加上一条混淆指令 42 | 43 | 每使用一次该混淆器,字节码会增大6字节的长度,也就是两条指令 44 | 45 | :param index: 上层通知混淆的位置,就是当前要放置的位置,返回的payload会自动计算 46 | :return: 47 | ''' 48 | 49 | def g_line(): 50 | return chr(random.randint(0, 255)) + chr(random.randint(0, 255)) 51 | 52 | obfs_codes = [ 53 | "\x71%s\x64%s" % (sum_jump(index + 6), g_line()), # JUMP_ABSOLUTE 6,LOAD_CONST rand 54 | "\x71%s\x62%s" % (sum_jump(index + 6), g_line()), # JUMP_ABSOLUTE 6,DELETE_GLOBAL rand 55 | "\x71%s\x74%s" % (sum_jump(index + 6), g_line()), # JUMP_ABSOLUTE 6,LOAD_GLOBAL rand 56 | "\x71%s\x5b%s" % (sum_jump(index + 6), g_line()), # JUMP_ABSOLUTE 6,DELETE_NAME rand 57 | ] 58 | return random.choice(obfs_codes) 59 | 60 | 61 | def insert_Identification(msg, index): 62 | ''' 63 | 插入标识,该标识主要是有一个长的字符串构成 64 | 字符串本身有一部分可以解释成opcode 65 | 有一部分不能解释成opcode的会对解析造成困难 66 | 67 | 警告⚠️: 这个混淆部分推荐是只插入一次,插入多了怕插进去的信息出问题 68 | 每使用一次该混淆器,字节码会增加信息长度加3字节码 69 | 该函数目前测试如果纯汉字加数字等于号,换行,多次混淆不会出问题,但是加入字母就经常出问题了 70 | 71 | :param index: 上层程序传入给这个函数的参数,其值为将要将这个数据插入的位置,计算好自己要插入的数据跳转的位置 72 | :return: 73 | ''' 74 | 75 | r = "\x71%s%s" % (sum_jump(index + len(msg) + 3), msg) 76 | return r 77 | 78 | 79 | def advanced(index): 80 | ''' 81 | 这个混淆器会先获取随机数,根据随机数获取行的数量,然后放到一起 82 | 83 | 每使用一次该混淆器,字节码会增加信息长度加随机数三倍加3字节码 84 | 85 | :param index: 86 | :return: 87 | ''' 88 | 89 | count = random.randint(1, 20) 90 | codes = "" 91 | for _ in range(count): 92 | codes += generate_line() 93 | return "\x71%s%s" % (sum_jump(index + 3 + len(codes)), codes) 94 | -------------------------------------------------------------------------------- /code_obfuscate/obfuscate.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | ''' 3 | 该文件主要用于处理代码的混淆和替换问题,以及一些字节码操作问题 4 | ''' 5 | 6 | from opcode import hasjrel, hasjabs, HAVE_ARGUMENT 7 | from inspect import iscode 8 | import py_compile 9 | import marshal 10 | import struct 11 | import random 12 | import types 13 | import time 14 | import imp 15 | import dis 16 | import os 17 | 18 | 19 | class CodeObfuscate(): 20 | ''' 21 | 代码混淆类 22 | ''' 23 | 24 | def __init__(self, code): 25 | assert iscode(code) 26 | self.code = code 27 | self.co_argcount = self.code.co_argcount 28 | self.co_code = self.code.co_code 29 | self.co_consts = list(self.code.co_consts) 30 | self.co_filename = self.code.co_filename 31 | self.co_firstlineno = self.code.co_firstlineno 32 | self.co_flags = self.code.co_flags 33 | self.co_lnotab = self.code.co_lnotab 34 | self.co_name = self.code.co_name 35 | self.co_names = list(self.code.co_names) 36 | self.co_nlocals = self.code.co_nlocals 37 | self.co_stacksize = self.code.co_stacksize 38 | self.co_varnames = self.code.co_varnames 39 | 40 | def change_file(self, name): 41 | ''' 42 | 更改文件名 43 | :param name: 需要修改的文件名 44 | :return: 45 | ''' 46 | self.co_filename = name 47 | 48 | def change_all_file(self, name): 49 | ''' 50 | 更改所有code对象的文件名 51 | :param name: 文件名 52 | :return: 53 | ''' 54 | self.change_file(name) 55 | for index, codes in enumerate(self.co_consts): 56 | if iscode(codes): 57 | # 如果是code对象,则调用目标的更改所有文件名方法,处理后取回对象 58 | c = self.__class__(codes) 59 | c.change_all_file(name) 60 | self.co_consts[index] = c.get_code() 61 | 62 | def __parse_line(self, code): 63 | ''' 64 | 解析行数据并且填写到self.__lines 65 | :return: 66 | ''' 67 | # 标记入库 68 | self.__lines = [] 69 | tmp_line = "" 70 | n = len(code) 71 | i = 0 72 | while i < n: 73 | c = code[i] 74 | op = ord(c) 75 | if c in ["\x71", # 跳转绝对位置 76 | "\x64", # 加载常量 77 | "\x6c", # 导入支持库 78 | "\x65", # 通过变量名加载内容 79 | "\x84", # 创建函数 80 | "\x72", # 如果上方表达式不成立跳转到 81 | "\x5b", # DELETE_NAME 82 | "\x48", # 换行 83 | "\x6e", # 跳出循环 84 | "\x09", # 空指令 85 | ]: 86 | if tmp_line: 87 | self.__lines.append(tmp_line) 88 | tmp_line = "" 89 | i = i + 1 90 | if op >= HAVE_ARGUMENT: 91 | tmp_line += c 92 | tmp_line += code[i:i + 2] 93 | i = i + 2 94 | else: 95 | # not have argument 96 | tmp_line += c 97 | if tmp_line: 98 | self.__lines.append(tmp_line) 99 | 100 | def __sum_length(self, length): 101 | ''' 102 | 计算长度,返回的内容可以写在pyc关于长度的位置 103 | :param length: 计算的长度 104 | :return: 返回长度字节 105 | ''' 106 | return struct.pack("= HAVE_ARGUMENT: # 有参数指令 140 | oparg = self.__parse_length(code_line[i] + code_line[i + 1]) # 计算跳转位置 141 | if op in hasjrel: # 判断是否是相对跳转 142 | jump = count + oparg - 1 143 | # 如果当前位置小于跳转位置 并且 跳转目标大于目标位置 144 | if count < obfu_index and jump >= obfu_index: 145 | r = self.__sum_length(oparg + payload_len) 146 | code_line[i], code_line[i + 1] = r[0], r[1] 147 | elif count > jump: 148 | r = self.__sum_length(oparg - payload_len) 149 | code_line[i], code_line[i + 1] = r[0], r[1] 150 | if op in hasjabs: # 判断是否是绝对跳转 151 | # 如果跳转的位置大于当前位置 152 | if oparg > obfu_index: 153 | r = self.__sum_length(oparg + payload_len) 154 | code_line[i], code_line[i + 1] = r[0], r[1] 155 | i += 2 156 | count += 2 157 | pass 158 | else: # 无参数指令 159 | pass 160 | # code_line 经过修改,需要重新存储了 161 | self.__lines[index] = "".join(code_line) 162 | self.__lines[obfu_line_num] = payload + self.__lines[obfu_line_num] 163 | # 拼接payload成code对象,计算code长度 164 | code = "".join(self.__lines) 165 | 166 | # 重新生成code对象 167 | # 将code对象重新放到原本的位置 168 | self.co_code = code 169 | 170 | def dis(self): 171 | ''' 172 | 输出字节码助记符 173 | :return: 无输出 174 | ''' 175 | dis.dis(self.co_code) 176 | 177 | def obfuscate(self, o_func): 178 | ''' 179 | 混淆函数 180 | :param o_func: 传入混淆器 181 | :return: 182 | ''' 183 | # 算出来有多少个code对象在const中,如果没得就只能对自身code对象进行混淆 184 | have_const_code = [iscode(x) for x in self.co_consts].count(True) 185 | if have_const_code > 0: # 常量表中包含code对象,可以选择对自身或者是对常量表中的内容进行混淆 186 | if random.choice([1, 0]): # 随机选择是对自身混淆还是对自身常量中的code对象进行混淆 187 | # 对自身进行混淆 188 | return self.__obfuscate(o_func) 189 | else: 190 | # 选一个对象进行混淆 191 | const_index = random.randint(0, have_const_code - 1) 192 | count_index = 0 # 计算遇到过多少个code对象,这个值在跳转之前会被转换成code对象的下标 193 | for index, code_obj in enumerate(self.co_consts): 194 | if iscode(code_obj): 195 | if const_index == count_index: # 写在前面是因为有时候会出现下标为0的情况 196 | count_index = index 197 | break 198 | count_index += 1 199 | o = self.__class__(self.co_consts[count_index]) 200 | o.obfuscate(o_func) 201 | self.co_consts[count_index] = o.get_code() 202 | return 203 | else: 204 | self.__obfuscate(o_func) 205 | 206 | def get_code(self): 207 | ''' 208 | 拼接成code对象 209 | :return: 返回被拼接的code对象 210 | ''' 211 | return types.CodeType(self.co_argcount, 212 | self.co_nlocals, 213 | self.co_stacksize, 214 | self.co_flags, 215 | self.co_code, 216 | tuple(self.co_consts), 217 | tuple(self.co_names), 218 | self.co_varnames, 219 | self.co_filename, 220 | self.co_name, 221 | self.co_firstlineno, 222 | self.co_lnotab) 223 | 224 | def run(self): 225 | ''' 226 | 字节码运行,请注意,因为环境复杂,该方法运行可靠性不好 227 | :return: 228 | ''' 229 | try: 230 | exec (self.get_code()) 231 | return 0 232 | except Exception as e: 233 | print("Runtime Error") 234 | raise e 235 | 236 | def __str__(self): 237 | return str(self.get_code()) 238 | 239 | def write(self, path, magic, time=0): 240 | ''' 241 | 写出到pyc文件 242 | :param path: 文件路径 243 | :param magic: 魔术字 244 | :param time: 时间(要求整数) 245 | :return: 246 | ''' 247 | with open(path, 'wb') as fc: 248 | fc.write(magic) 249 | fc.write(struct.pack("