参考链接
https://apeng.fun/2019/08/16/2019zgc_quals/
PYC 文件的简单分析
PYC文件格式分析
Python字节码解混淆
Python字节码解混淆之反控制流扁平化
src_leak
题目给了源码如下:
#include<iostream>
using namespace std;
typedef unsigned int uint;
template <bool Flag, class MaybeA, class MaybeB> class IfElse;
template <class MaybeA, class MaybeB>
class IfElse<true, MaybeA, MaybeB> {
public:
using ResultType = MaybeA;
};
template <class MaybeA, class MaybeB>
class IfElse<false, MaybeA, MaybeB> {
public:
using ResultType = MaybeB;
};
template <uint N, uint L, uint R> struct func1 {
enum { mid = (L + R + 1) / 2 };
using ResultType = typename IfElse<(N < mid * mid),
func1<N, L, mid - 1>, func1<N, mid, R> >::ResultType;
enum { result = ResultType::result };
};
template <uint N, uint L> struct func1<N, L, L> { enum { result = L }; };
template <uint N> struct _func1 { enum { result = func1<N, 1, N>::result }; };
template<size_t Input>
constexpr size_t func2 = (Input % 2) + func2< (Input / 2) >;
template<>
constexpr size_t func2<0> = 0;
template<size_t num>
constexpr size_t func3 = num % 2;
template<uint n, uint m>struct NEXTN {
const static uint value = ((n % m != 0) * n);
};
template<uint n, uint m>struct NEXTM {
const static uint value = (m * m <= n ? (m + 1) : 0);
};
template<uint n, uint m>struct TEST {
const static uint value = TEST<NEXTN<n, m>::value, NEXTM<n, m>::value>::value;
};
template<uint m>struct TEST<0, m> {
const static uint value = 0;
};
template<uint n>struct TEST<n, 0> {
const static uint value = 1;
};
template<uint n>struct func4 {
const static uint value = TEST<n, 2>::value;
};
template<>struct func4<1> {
const static uint value = 0;
};
template<>struct func4<2> {
const static uint value = 1;
};
int main(int argc, char**argv) {
//input 5 uint numbers ,x1,x2,x3,x4,x5
//the sum of them should be MIN
cout << func3< func2<x1> > << endl;
cout << func3< func2<x2> > << endl;
cout << func3< func2<x3> > << endl;
cout << func3< func2<x4> > << endl;
cout << func3< func2<x5> > << endl;
// output: 1 1 1 1 1
cout << _func1<x1>::result << endl;
cout << _func1<x2>::result << endl;
cout << _func1<x3>::result << endl;
cout << _func1<x4>::result << endl;
cout << _func1<x5>::result << endl;
//output: 963 4396 6666 1999 3141
//how many "1" will func4<1>,func4<2>,fun4<3>......fun4<10000> ::value return?
x6 = count;
// your flag is flag{x1-x2-x3-x4-x5-x6}
// if x1=1,x2=2,x3=3,x4=4,x5=5,x6=6
// flag is flag{1-2-3-4-5-6}
return 0;
}
模板:
https://zh.cppreference.com/w/cpp/language/template_specialization
模板特化:
https://blog.csdn.net/gatieme/article/details/50953564
- _func1求参数的平方根,向下取整(二分法)
- func2求参数的二进制数中1的个数
- func3求参数的奇偶
- func4求是否位素数(sqrt(n)以下的数是否是n的参数)
所以函数要求: x1-x5都是奇数,而且平方根分别为963 4396 6666 1999 3141, x6是10000以内素数的个数。
pyc
使用010Editor的相应模板打开,并修改pyc.bt中的以下部分:
enum <uint16> MagicValue {
PY_24a0 = 62041,
PY_24a3 = 62051,
PY_24b1 = 62061,
PY_25a0_1 = 62071,
PY_25a0_2 = 62081,
PY_25a0_3 = 62091,
PY_25a0_4 = 62092,
PY_25b3_1 = 62101,
PY_25b3_2 = 62111,
PY_25c1 = 62121,
PY_25c2 = 62131,
PY_26a0 = 62151,
PY_26a1 = 62161,
PY_27a0_1 = 62171,
PY_27a0_2 = 62181,
PY_27a0_a = 62211,
};
-
Magic Number(四字节)
前两个字节是可变的,它和编译 python 文件的 python 版本有关,接下来两个字节是固定的 0D0A,转换成 ASC 码就是 \r\n
- mtime(四字节)
这个字段表示该 pyc 文件的编译日期,用 unix 时间戳来表示 - PyCodeObject
//Include/code.h
typedef struct {
PyObject_HEAD // 用它来表示一个 PyCodeObject的开始
int co_argcount; /* #arguments, except *args */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* instruction opcodes */
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash/cmp */
PyObject *co_filename; /* string (where it was loaded from) */
PyObject *co_name; /* string (name, for reference) */
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support weakrefs to code objects */
} PyCodeObject;
各个字段的含义如下:
argcount 参数个数
nlocals 局部变量个数
stacksize 栈空间大小
flags N/A
TYPE_STRING 表示字节码开始
r_long n 字节码的数量
struct Instruction inst[] 字节码序列
PyObject 的序列化在 python/marshal.c 内实现,一般是先写入一个 byte 来标识此 PyObject 的类型,每种 PyObject 对应的类型也在 Python/marshal.c 内定义:
#define TYPE_NULL '0'
#define TYPE_NONE 'N'
#define TYPE_FALSE 'F'
#define TYPE_TRUE 'T'
#define TYPE_STOPITER 'S'
#define TYPE_ELLIPSIS '.'
#define TYPE_INT 'i'
#define TYPE_INT64 'I'
#define TYPE_FLOAT 'f'
#define TYPE_BINARY_FLOAT 'g'
#define TYPE_COMPLEX 'x'
#define TYPE_BINARY_COMPLEX 'y'
#define TYPE_LONG 'l'
#define TYPE_STRING 's'
#define TYPE_INTERNED 't'
#define TYPE_STRINGREF 'R'
#define TYPE_TUPLE '('
#define TYPE_LIST '['
#define TYPE_DICT '{'
#define TYPE_CODE 'c'
#define TYPE_UNICODE 'u'
#define TYPE_UNKNOWN '?'
#define TYPE_SET '<'
#define TYPE_FROZENSET '>'
如图type等于99就是TYPE_CODE类型,PyCodeObject 的第一个部分肯定是 TYPE_CODE,表示字节码区块
使用dis模块转化为反编译字节码区块部分:
import dis
class read():
def __init__(self, filename, mode):
# print filename, mode
self.file = open(filename, mode)
def read(self, num):
return (self.file).read(num)
pyc = read("test.pyc", "rb")
pyc.read(30)
target = pyc.read(0x31)
# print bytes(target)
dis.dis(target)
结果:
0 LOAD_CONST 0 (0) # 读取常量列表中的 0 号常量
3 MAKE_FUNCTION 0 # 制作一个函数
6 STORE_NAME 0 (0) # 函数命名为字符串列表中的 0 号
9 LOAD_CONST 1 (1)
12 MAKE_FUNCTION 0
15 STORE_NAME 1 (1)
18 LOAD_NAME 0 (0) # 取出函数 1
21 CALL_FUNCTION 0 # 调用
24 STORE_NAME 2 (2) # 存储返回值
27 LOAD_NAME 1 (1)
30 CALL_FUNCTION 0
33 STORE_NAME 3 (3)
36 LOAD_NAME 2 (2) # 取出两个返回值
39 LOAD_NAME 3 (3)
42 BINARY_ADD # 相加
43 PRINT_ITEM # 打印结果
44 PRINT_NEWLINE
45 LOAD_CONST 2 (2)
48 RETURN_VALUE
混淆的一般思路:
uncompyle 的工作原理和一般的反编译器类似,它会尽力去匹配每一条指令,尝试将所有指令都覆盖到,但是在解析上面的代码时,碰到 load 不存在的常量时就会出错,无法继续反编译。
所有的指令可以分为两类,不需要参数和需要参数的,Python字节码在设计的时候故意把没有参数的指令分配在了对应编号的低位,高位都是有参数的,以Include/opcode.h中的HAVE_ARGUMENT分界。他们的在二进制级别上的组织是这样的:
[指令] 不需要参数的指令只占用一个字节
[指令] [参数低字节] [参数高字节] 需要参数的指令占用三个字节,一个字节指令,两个字节参数
本题中经过混淆的字节码:
>>> list(map(ord,code.co_code[:9]))
[113, 158, 2, 136, 104, 110, 126, 58, 140]
>>> dis.opname[113]
'JUMP_ABSOLUTE'
>>> 2*256+158
670
>>> dis.opname[136]
'LOAD_DEREF'
>>> 110*256+104
28264
从上面可以看到,第一条指令是JUMP_ABSOLTE 670,这个offset的指令是真实存在的,所以指令合法。但是第二条指令应该是LOAD_DEREF 28264,这个index的对象并不存在,在dis尝试解析的时候就会崩溃。
实际上因为之前的跳转指令所以第二条的非法指令并不会被真实执行到,所以pyc文件作者是故意加入不影响执行的非法指令触发分析软件崩溃,阻碍对该pyc文件的分析。
去混淆
用Apeng师傅的方法可以去掉混淆
# python2 disasm_anti.py py.pyc
import dis, marshal, struct, sys, time, types
from opcode import *
def ana_branch(code, i, hits):
if i > len(code):
return
if i in hits:
return
else:
hits.append(i)
c = code[i]
op = ord(c)
if op == 111 or op == 112 or op == 114 or op == 115 or op == 120 or op == 93:
oparg = ord(code[i+1]) + ord(code[i+2])*256
if op == 120 or op == 93:
oparg += i
oparg += 3
ana_branch(code, oparg, hits)
ana_branch(code, i+3, hits)
elif op == 110:
oparg = ord(code[i+1]) + ord(code[i+2])*256
ana_branch(code, i + oparg + 3, hits)
elif op == 113:
oparg = ord(code[i+1]) + ord(code[i+2])*256
ana_branch(code, oparg, hits)
else:
if op>=HAVE_ARGUMENT:
ana_branch(code, i+3, hits)
else:
ana_branch(code, i+1, hits)
def findlinestarts(code):
"""Find the offsets in a byte code which are start of lines in the source.
Generate pairs (offset, lineno) as described in Python/compile.c.
"""
byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
line_increments = [ord(c) for c in code.co_lnotab[1::2]]
lastlineno = None
lineno = code.co_firstlineno
addr = 0
for byte_incr, line_incr in zip(byte_increments, line_increments):
if byte_incr:
if lineno != lastlineno:
yield (addr, lineno)
lastlineno = lineno
addr += byte_incr
lineno += line_incr
if lineno != lastlineno:
yield (addr, lineno)
def findhits(code):
hits = []
n = len(code)
i = 0
ana_branch(code, i, hits)
hits.sort()
return hits
def anti_findlabels(code):
"""Detect all offsets in a byte code which are jump targets.
Return the list of offsets.
"""
hits = findhits(code)
labels = []
n = len(code)
i = 0
while i < n:
if i not in hits:
i+=1
continue
c = code[i]
op = ord(c)
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256
i = i+2
label = -1
if op in hasjrel:
label = i+oparg
elif op in hasjabs:
label = oparg
if label >= 0:
if label not in labels:
labels.append(label)
return labels
def dis_anti_obf(co, lasti = -1):
"""Disassemble a code object, anti obf"""
anti_code = ""
code = co.co_code
hits = findhits(code)
labels = anti_findlabels(code)
linestarts = dict(findlinestarts(co))
n = len(code)
i = 0
i = 0
extended_arg = 0
free = None
while i < n:
if i not in hits:
i+=1
anti_code+="\x09"
continue
c = code[i]
op = ord(c)
if i in linestarts:
if i > 0:
print
print "%3d" % linestarts[i],
else:
print ' ',
if i == lasti: print '-->',
else: print ' ',
if i in labels: print '>>',
else: print ' ',
print repr(i).rjust(4),
print opname[op].ljust(20),
anti_code += code[i]
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
extended_arg = 0
anti_code+=code[i]
anti_code+=code[i+1]
i = i+2
if op == EXTENDED_ARG:
extended_arg = oparg*65536L
print repr(oparg).rjust(5),
if op in hasconst:
print '(' + repr(co.co_consts[oparg]) + ')',
elif op in hasname:
print '(' + co.co_names[oparg] + ')',
elif op in hasjrel:
print '(to ' + repr(i + oparg) + ')',
elif op in haslocal:
print '(' + co.co_varnames[oparg] + ')',
elif op in hascompare:
print '(' + cmp_op[oparg] + ')',
elif op in hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
print '(' + free[oparg] + ')',
print
print "patch code:"
print(anti_code.encode("hex"))
def show_file(fname):
f = open(fname, "rb")
magic = f.read(4)
moddate = f.read(4)
modtime = time.asctime(time.localtime(struct.unpack('L', moddate)[0]))
print "magic %s" % (magic.encode('hex'))
print "moddate %s (%s)" % (moddate.encode('hex'), modtime)
code = marshal.load(f)
show_code(code)
def show_code(code, indent=''):
print "%scode" % indent
indent += ' '
print "%sargcount %d" % (indent, code.co_argcount)
print "%snlocals %d" % (indent, code.co_nlocals)
print "%sstacksize %d" % (indent, code.co_stacksize)
print "%sflags %04x" % (indent, code.co_flags)
show_hex("code", code.co_code, indent=indent)
dis_anti_obf(code)
print "%sconsts" % indent
for const in code.co_consts:
if type(const) == types.CodeType:
show_code(const, indent+' ')
else:
print " %s%r" % (indent, const)
print "%snames %r" % (indent, code.co_names)
print "%svarnames %r" % (indent, code.co_varnames)
print "%sfreevars %r" % (indent, code.co_freevars)
print "%scellvars %r" % (indent, code.co_cellvars)
print "%sfilename %r" % (indent, code.co_filename)
print "%sname %r" % (indent, code.co_name)
print "%sfirstlineno %d" % (indent, code.co_firstlineno)
show_hex("lnotab", code.co_lnotab, indent=indent)
def show_hex(label, h, indent):
h = h.encode('hex')
if len(h) < 60:
print "%s%s %s" % (indent, label, h)
else:
print "%s%s" % (indent, label)
for i in range(0, len(h), 60):
print "%s %s" % (indent, h[i:i+60])
show_file(sys.argv[1])
或者使用FXTi师傅的方法将不合法的指令转换为nop:
import marshal, sys, opcode, types, dis
NOP = 9
HAVE_ARGUMENT = 90
JUMP_FORWARD = 110
JUMP_IF_FALSE_OR_POP = 111
JUMP_IF_TRUE_OR_POP = 112
JUMP_ABSOLUTE = 113
POP_JUMP_IF_FALSE = 114
POP_JUMP_IF_TRUE = 115
CONTINUE_LOOP = 119
FOR_ITER = 93
RETURN_VALUE = 83
used_set = set()
def deconf_inner(code, now):
global used_set
while code[now] != RETURN_VALUE:
if now in used_set:
break
used_set.add(now)
if code[now] >= HAVE_ARGUMENT:
used_set.add(now+1)
used_set.add(now+2)
op = code[now]
#print(str(now) + " " + opcode.opname[op])
if op == JUMP_FORWARD:
arg = code[now+2] << 8 | code[now+1]
now += arg + 3
continue
elif op == JUMP_ABSOLUTE:
arg = code[now+2] << 8 | code[now+1]
now = arg
continue
elif op == JUMP_IF_TRUE_OR_POP:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, arg)
elif op == JUMP_IF_FALSE_OR_POP:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, arg)
elif op == POP_JUMP_IF_TRUE:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, arg)
elif op == POP_JUMP_IF_FALSE:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, arg)
elif op == CONTINUE_LOOP:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, arg)
elif op == FOR_ITER:
arg = code[now+2] << 8 | code[now+1]
deconf_inner(code, now + arg + 3)
if op < HAVE_ARGUMENT:
now += 1
else:
now += 3
used_set.add(now)
if code[now] >= HAVE_ARGUMENT:
used_set.add(now+1)
used_set.add(now+2)
def deconf(code):
global used_set
used_set = set() #Remember to clean up used_set for every target function
cod = list(map(ord, code))
deconf_inner(cod, 0)
for i in range(len(cod)):
if i not in used_set:
cod[i] = NOP
return "".join(list(map(chr, cod)))
with open(sys.argv[1], 'rb') as f:
header = f.read(8)
code = marshal.load(f)
'''
print(code.co_consts[3].co_name)
print(dis.dis(deconf(code.co_consts[3].co_code)))
'''
consts = list()
for i in range(len(code.co_consts)):
if hasattr(code.co_consts[i], 'co_code'):
consts.append(types.CodeType(code.co_consts[i].co_argcount,
# c.co_kwonlyargcount, Add this in Python3
code.co_consts[i].co_nlocals,
code.co_consts[i].co_stacksize,
code.co_consts[i].co_flags,
deconf(code.co_consts[i].co_code),
code.co_consts[i].co_consts,
code.co_consts[i].co_names,
code.co_consts[i].co_varnames,
code.co_consts[i].co_filename,
code.co_consts[i].co_name,
code.co_consts[i].co_firstlineno,
code.co_consts[i].co_lnotab, # In general, You should adjust this
code.co_consts[i].co_freevars,
code.co_consts[i].co_cellvars))
else:
consts.append(code.co_consts[i])
mode = types.CodeType(code.co_argcount,
# c.co_kwonlyargcount, Add this in Python3
code.co_nlocals,
code.co_stacksize,
code.co_flags,
deconf(code.co_code),
tuple(consts),
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab, # In general, You should adjust this
code.co_freevars,
code.co_cellvars)
f = open(sys.argv[1]+".mod", 'wb')
f.write(header)
marshal.dump(mode, f)
NOP指令去除与块的合并
fxti师傅给了一个工具:
https://github.com/extremecoders-re/bytecode_simplifier
利用这个工具可以去除上面去混淆以后生成的全是NOP指令的块,以及合并一些块
完成之后使用uncompyle6可以反汇编一下(反编译还是会失败)
L. 1 0 LOAD_CONST 2694209818L
3 STORE_FAST 0 'DIVIDER'
6 JUMP_FORWARD 0 'to 9'
9_0 COME_FROM 6 '6'
9 LOAD_CONST 4130330538L
L. 3 12 LOAD_FAST 0 'DIVIDER'
15 COMPARE_OP 2 ==
18 POP_JUMP_IF_TRUE 366 'to 366'
L. 9 21 LOAD_CONST 3168701571L
24 LOAD_FAST 0 'DIVIDER'
27 COMPARE_OP 2 ==
L. 14 30 POP_JUMP_IF_TRUE 345 'to 345'
33 LOAD_CONST 3715947653L
36 LOAD_FAST 0 'DIVIDER'
L. 17 39 COMPARE_OP 2 ==
42 POP_JUMP_IF_TRUE 324 'to 324'
45 LOAD_CONST 2694209818L
L. 20 48 LOAD_FAST 0 'DIVIDER'
51 COMPARE_OP 2 ==
54 POP_JUMP_IF_TRUE 306 'to 306'
L. 38 57 LOAD_CONST 651787064
60 LOAD_FAST 0 'DIVIDER'
63 COMPARE_OP 2 ==
66 POP_JUMP_IF_TRUE 285 'to 285'
69 LOAD_CONST 3521152606L
72 LOAD_FAST 0 'DIVIDER'
75 COMPARE_OP 2 ==
78 POP_JUMP_IF_TRUE 264 'to 264'
81 LOAD_CONST 2730391645L
84 LOAD_FAST 0 'DIVIDER'
87 COMPARE_OP 2 ==
90 POP_JUMP_IF_TRUE 246 'to 246'
93 LOAD_CONST 4084147187L
96 LOAD_FAST 0 'DIVIDER'
99 COMPARE_OP 2 ==
102 POP_JUMP_IF_TRUE 222 'to 222'
105 LOAD_CONST 1860581437
108 LOAD_FAST 0 'DIVIDER'
111 COMPARE_OP 2 ==
114 POP_JUMP_IF_TRUE 204 'to 204'
117 LOAD_CONST 3816944324L
120 LOAD_FAST 0 'DIVIDER'
123 COMPARE_OP 2 ==
126 POP_JUMP_IF_TRUE 203 'to 203'
129 LOAD_CONST 394367122
132 LOAD_FAST 0 'DIVIDER'
135 COMPARE_OP 2 ==
138 POP_JUMP_IF_TRUE 181 'to 181'
141 LOAD_CONST 1627830889
144 LOAD_FAST 0 'DIVIDER'
147 COMPARE_OP 2 ==
150 POP_JUMP_IF_TRUE 157 'to 157'
153 LOAD_CONST None
156 RETURN_END_IF
157_0 COME_FROM 150 '150'
157 STORE_NAME 0 'sys'
160 LOAD_CODE <code_object str2hex>
163 MAKE_FUNCTION_0 0 None
166 STORE_NAME 1 'str2hex'
169 LOAD_CODE <code_object hex2str>
172 LOAD_CONST 3715947653L
175 STORE_FAST 0 'DIVIDER'
178 JUMP_BACK 9 'to 9'
181 LOAD_NAME 10 'flag'
184 CALL_FUNCTION_1 1 None
187 CALL_FUNCTION_1 1 None
190 POP_TOP
191 LOAD_CONST None
194 LOAD_CONST 3816944324L
197 STORE_FAST 0 'DIVIDER'
200 JUMP_BACK 9 'to 9'
203 RETURN_END_IF
204_0 COME_FROM 114 '114'
204 LOAD_CONST 97
207 LOAD_CONST 103
210 LOAD_CONST 58
213 LOAD_CONST 2730391645L
216 STORE_FAST 0 'DIVIDER'
219 JUMP_BACK 9 'to 9'
222 LOAD_ATTR 6 'stdout'
225 LOAD_ATTR 7 'write'
228 LOAD_NAME 2 'hex2str'
231 LOAD_CONST 102
234 LOAD_CONST 108
237 LOAD_CONST 1860581437
240 STORE_FAST 0 'DIVIDER'
243 JUMP_BACK 9 'to 9'
246 BUILD_LIST_5 5
249 CALL_FUNCTION_1 1 None
252 CALL_FUNCTION_1 1 None
255 LOAD_CONST 4130330538L
258 STORE_FAST 0 'DIVIDER'
261 JUMP_BACK 9 'to 9'
264 CALL_FUNCTION_1 1 None
267 STORE_NAME 10 'flag'
270 LOAD_NAME 5 'count'
273 LOAD_NAME 1 'str2hex'
276 LOAD_CONST 394367122
279 STORE_FAST 0 'DIVIDER'
282 JUMP_BACK 9 'to 9'
285 STORE_NAME 3 'p_s'
288 LOAD_CODE <code_object p_f>
291 MAKE_FUNCTION_0 0 None
294 STORE_NAME 4 'p_f'
297 LOAD_CONST 3168701571L
300 STORE_FAST 0 'DIVIDER'
303 JUMP_BACK 9 'to 9'
306 LOAD_CONST -1
309 LOAD_CONST None
312 IMPORT_NAME 0 'sys'
315 LOAD_CONST 1627830889
318 STORE_FAST 0 'DIVIDER'
321 JUMP_BACK 9 'to 9'
324 MAKE_FUNCTION_0 0 None
327 STORE_NAME 2 'hex2str'
330 LOAD_CODE <code_object p_s>
333 MAKE_FUNCTION_0 0 None
336 LOAD_CONST 651787064
339 STORE_FAST 0 'DIVIDER'
342 JUMP_BACK 9 'to 9'
345 LOAD_CODE <code_object count>
348 MAKE_FUNCTION_0 0 None
351 STORE_NAME 5 'count'
354 LOAD_NAME 0 'sys'
357 LOAD_CONST 4084147187L
360 STORE_FAST 0 'DIVIDER'
363 JUMP_BACK 9 'to 9'
366 POP_TOP
367 LOAD_NAME 0 'sys'
370 LOAD_ATTR 8 'stdin'
373 LOAD_ATTR 9 'read'
376 LOAD_CONST 38
379 LOAD_CONST 3521152606L
382 STORE_FAST 0 'DIVIDER'
385 JUMP_BACK 9 'to 9'
然后我暂时只会看这个字节码,fxti师傅那个自己去混淆的方案没开源,我打算自己仔细研究一下这个发编译的代码,看能不能按照那个思路复现一下。
网友评论