第一步 编写程序
以github上的N皇后问题为例
"""The n queens puzzle.
https://github.com/sol-prog/N-Queens-Puzzle/blob/master/nqueens.py
"""
__all__ = []
class NQueens:
"""Generate all valid solutions for the n queens puzzle"""
def __init__(self, size):
# Store the puzzle (problem) size and the number of valid solutions
self.__size = size
self.__solutions = 0
self.__solve()
def __solve(self):
"""Solve the n queens puzzle and print the number of solutions"""
positions = [-1] * self.__size
self.__put_queen(positions, 0)
print("Found", self.__solutions, "solutions.")
def __put_queen(self, positions, target_row):
"""
Try to place a queen on target_row by checking all N possible cases.
If a valid place is found the function calls itself trying to place a queen
on the next row until all N queens are placed on the NxN board.
"""
# Base (stop) case - all N rows are occupied
if target_row == self.__size:
self.__show_full_board(positions)
self.__solutions += 1
else:
# For all N columns positions try to place a queen
for column in range(self.__size):
# Reject all invalid positions
if self.__check_place(positions, target_row, column):
positions[target_row] = column
self.__put_queen(positions, target_row + 1)
def __check_place(self, positions, ocuppied_rows, column):
"""
Check if a given position is under attack from any of
the previously placed queens (check column and diagonal positions)
"""
for i in range(ocuppied_rows):
if positions[i] == column or \
positions[i] - i == column - ocuppied_rows or \
positions[i] + i == column + ocuppied_rows:
return False
return True
def __show_full_board(self, positions):
"""Show the full NxN board"""
for row in range(self.__size):
line = ""
for column in range(self.__size):
if positions[row] == column:
line += "Q "
else:
line += ". "
print(line)
print("\n")
def __show_short_board(self, positions):
"""
Show the queens positions on the board in compressed form,
each number represent the occupied column position in the corresponding row.
"""
line = ""
for i in range(self.__size):
line += str(positions[i]) + " "
print(line)
def main():
"""Initialize and solve the n queens puzzle"""
NQueens(8)
if __name__ == "__main__":
# execute only if run as a script
main()
测试一下运行
>python3 sample.py
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .
...略...
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
Found 92 solutions.
第二步 混淆
打开Oxyry网站,将代码复制到左侧的Source
框里面,并点击Obfuscate
按钮,右侧的Destination
框中的代码就是混淆后的代码,类似如下:
""#line:4
__all__ =[]#line:6
class O000000OO00O00000 :#line:8
""#line:9
def __init__ (OO000000OOOO000OO ,O0000OOOO0O00OOO0 ):#line:11
OO000000OOOO000OO .__O000O0000OO00O000 =O0000OOOO0O00OOO0 #line:13
OO000000OOOO000OO .__OOO00OOOOO00000O0 =0 #line:14
OO000000OOOO000OO .__OOOO000OOO00O00OO ()#line:15
def __OOOO000OOO00O00OO (OO0OOO0OO0O0OOOOO ):#line:17
""#line:18
O0O00000O00000000 =[-1 ]*OO0OOO0OO0O0OOOOO .__O000O0000OO00O000 #line:19
OO0OOO0OO0O0OOOOO .__OO0O0O0000OO00OOO (O0O00000O00000000 ,0 )#line:20
print ("Found",OO0OOO0OO0O0OOOOO .__OOO00OOOOO00000O0 ,"solutions.")#line:21
def __OO0O0O0000OO00OOO (OO00OOOO0OOOO0O0O ,O000000O00O0000O0 ,OOO0OO0OOOO0OO0O0 ):#line:23
""#line:28
if OOO0OO0OOOO0OO0O0 ==OO00OOOO0OOOO0O0O .__O000O0000OO00O000 :#line:30
OO00OOOO0OOOO0O0O .__O00O0O0O00O00O0OO (O000000O00O0000O0 )#line:31
OO00OOOO0OOOO0O0O .__OOO00OOOOO00000O0 +=1 #line:32
else :#line:33
for OOOOO0OOOO000O000 in range (OO00OOOO0OOOO0O0O .__O000O0000OO00O000 ):#line:35
if OO00OOOO0OOOO0O0O .__O0O0O0000000O0O00 (O000000O00O0000O0 ,OOO0OO0OOOO0OO0O0 ,OOOOO0OOOO000O000 ):#line:37
O000000O00O0000O0 [OOO0OO0OOOO0OO0O0 ]=OOOOO0OOOO000O000 #line:38
OO00OOOO0OOOO0O0O .__OO0O0O0000OO00OOO (O000000O00O0000O0 ,OOO0OO0OOOO0OO0O0 +1 )#line:39
def __O0O0O0000000O0O00 (O0O0OO0OO0OO00OO0 ,OOO0OO0O0O00OOO00 ,OO0OO000OO0000000 ,OOO0OO0OOOOOOOOO0 ):#line:42
""#line:46
for OO000O0O0O00OO00O in range (OO0OO000OO0000000 ):#line:47
if OOO0OO0O0O00OOO00 [OO000O0O0O00OO00O ]==OOO0OO0OOOOOOOOO0 or OOO0OO0O0O00OOO00 [OO000O0O0O00OO00O ]-OO000O0O0O00OO00O ==OOO0OO0OOOOOOOOO0 -OO0OO000OO0000000 or OOO0OO0O0O00OOO00 [OO000O0O0O00OO00O ]+OO000O0O0O00OO00O ==OOO0OO0OOOOOOOOO0 +OO0OO000OO0000000 :#line:50
return False #line:52
return True #line:53
def __O00O0O0O00O00O0OO (OO00OO0OO0O0OO00O ,O00000OOOOO00OO0O ):#line:55
""#line:56
for O000000OO00OO000O in range (OO00OO0OO0O0OO00O .__O000O0000OO00O000 ):#line:57
O0000O00O0000O00O =""#line:58
for OOO000O0OO0O00O0O in range (OO00OO0OO0O0OO00O .__O000O0000OO00O000 ):#line:59
if O00000OOOOO00OO0O [O000000OO00OO000O ]==OOO000O0OO0O00O0O :#line:60
O0000O00O0000O00O +="Q "#line:61
else :#line:62
O0000O00O0000O00O +=". "#line:63
print (O0000O00O0000O00O )#line:64
print ("\n")#line:65
def __O000OOOOO0OO0O0OO (O00000000OOO00000 ,O0O00O00OO0O0O000 ):#line:67
""#line:71
OO0OOOOOO0OO0OO0O =""#line:72
for OO0OOOOOO00OO00O0 in range (O00000000OOO00000 .__O000O0000OO00O000 ):#line:73
OO0OOOOOO0OO0OO0O +=str (O0O00O00OO0O0O000 [OO0OOOOOO00OO00O0 ])+" "#line:74
print (OO0OOOOOO0OO0OO0O )#line:75
def OOOOOOO00O0000OO0 ():#line:77
""#line:78
O000000OO00O00000 (8 )#line:79
if __name__ =="__main__":#line:81
OOOOOOO00O0000OO0 ()#line:83
将该文件复制并保存为:sample_ob.python
测试一下运行结果:
>python3 sample_ob.py
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .
...略...
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . Q . . .
. . . . . . Q .
. . . Q . . . .
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
Found 92 solutions.
第三步 编译
利用自带的py_compile模块进行编译:
> python3 -m py_compile sample_ob.py
> ls __pycache__
sample_ob.cpython-37.pyc
> cp __pycache__/sample_ob.cpython-37.pyc sample_ob.pyc
测试运行:
>python3 sample_ob.pyc
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .
...略...
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . Q . . .
. . . . . . Q .
. . . Q . . . .
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
Found 92 solutions.
第四步 反编译验证
安装反编译器uncompyle6
> pip3 install uncompyle6
> uncompyle6
No files given
usage:
uncompyle6 [--verify | --weak-verify ] [--asm] [--tree[+]] [--grammar] [-o ] FILE|DIR...
uncompyle6 [--help | -h | --version | -V]
安装成功。
无混淆的反编译
准备对比程序,命名为sample.pyc
> python3 -m py_compile sample.py
> ls __pycache__
sample.cpython-37.pyc
> cp __pycache__/sample.cpython-37.pyc sample.pyc
反编译为re-sample.py
> uncompyle6 sample.pyc > re-sample.py
结果为:
# uncompyle6 version 3.2.5
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.2 (default, Feb 12 2019, 08:15:36)
# [Clang 10.0.0 (clang-1000.11.45.5)]
# Embedded file name: sample.py
# Size of source mod 2**32: 2790 bytes
"""The n queens puzzle.
https://github.com/sol-prog/N-Queens-Puzzle/blob/master/nqueens.py
"""
__all__ = []
class NQueens:
"""Generate all valid solutions for the n queens puzzle"""
def __init__(self, size):
self._NQueens__size = size
self._NQueens__solutions = 0
self._NQueens__solve()
def __solve(self):
"""Solve the n queens puzzle and print the number of solutions"""
positions = [
-1] * self._NQueens__size
self._NQueens__put_queen(positions, 0)
print('Found', self._NQueens__solutions, 'solutions.')
def __put_queen(self, positions, target_row):
"""
Try to place a queen on target_row by checking all N possible cases.
If a valid place is found the function calls itself trying to place a queen
on the next row until all N queens are placed on the NxN board.
"""
if target_row == self._NQueens__size:
self._NQueens__show_full_board(positions)
self._NQueens__solutions += 1
else:
for column in range(self._NQueens__size):
if self._NQueens__check_place(positions, target_row, column):
positions[target_row] = column
self._NQueens__put_queen(positions, target_row + 1)
def __check_place(self, positions, ocuppied_rows, column):
"""
Check if a given position is under attack from any of
the previously placed queens (check column and diagonal positions)
"""
for i in range(ocuppied_rows):
if positions[i] == column or positions[i] - i == column - ocuppied_rows or positions[i] + i == column + ocuppied_rows:
return False
return True
def __show_full_board(self, positions):
"""Show the full NxN board"""
for row in range(self._NQueens__size):
line = ''
for column in range(self._NQueens__size):
if positions[row] == column:
line += 'Q '
else:
line += '. '
print(line)
print('\n')
def __show_short_board(self, positions):
"""
Show the queens positions on the board in compressed form,
each number represent the occupied column position in the corresponding row.
"""
line = ''
for i in range(self._NQueens__size):
line += str(positions[i]) + ' '
print(line)
def main():
"""Initialize and solve the n queens puzzle"""
NQueens(8)
if __name__ == '__main__':
main()
# okay decompiling sample.pyc
个人感觉这个反编译结果几乎可以看到所有的算法核心。
有混淆的反编译
> uncompyle6 sample_ob.pyc > re-sample_ob.py
结果:
# uncompyle6 version 3.2.5
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.2 (default, Feb 12 2019, 08:15:36)
# [Clang 10.0.0 (clang-1000.11.45.5)]
# Embedded file name: sample_ob.py
# Size of source mod 2**32: 2616 bytes
""""""
__all__ = []
class O00O0O00000O000OO:
""""""
def __init__(self, size):
self._O00O0O00000O000OO__O00OO0OO00OOO000O = size
self._O00O0O00000O000OO__O0OOOO0000OO0O000 = 0
self._O00O0O00000O000OO__OO00OOO00000OOOOO()
def __OO00OOO00000OOOOO(self):
""""""
OOO0000OOO0OO0OOO = [
-1] * self._O00O0O00000O000OO__O00OO0OO00OOO000O
self._O00O0O00000O000OO__OOOO0OOOO00000OO0(OOO0000OOO0OO0OOO, 0)
print('Found', self._O00O0O00000O000OO__O0OOOO0000OO0O000, 'solutions.')
def __OOOO0OOOO00000OO0(self, positions, target_row):
""""""
if target_row == self._O00O0O00000O000OO__O00OO0OO00OOO000O:
self._O00O0O00000O000OO__O0OO0O00O0O000O00(positions)
self._O00O0O00000O000OO__O0OOOO0000OO0O000 += 1
else:
for OO00O0O00O00OOO0O in range(self._O00O0O00000O000OO__O00OO0OO00OOO000O):
if self._O00O0O00000O000OO__OO000000OOO0O0000(positions, target_row, OO00O0O00O00OOO0O):
positions[target_row] = OO00O0O00O00OOO0O
self._O00O0O00000O000OO__OOOO0OOOO00000OO0(positions, target_row + 1)
def __OO000000OOO0O0000(self, positions, ocuppied_rows, column):
""""""
for O0OO0O0O00O0O0000 in range(ocuppied_rows):
if positions[O0OO0O0O00O0O0000] == column or positions[O0OO0O0O00O0O0000] - O0OO0O0O00O0O0000 == column - ocuppied_rows or positions[O0OO0O0O00O0O0000] + O0OO0O0O00O0O0000 == column + ocuppied_rows:
return False
return True
def __O0OO0O00O0O000O00(self, positions):
""""""
for OOO0OO0OO0OO00OOO in range(self._O00O0O00000O000OO__O00OO0OO00OOO000O):
O0OOOO0O00OOO0OOO = ''
for O0OO0O00OOOO000O0 in range(self._O00O0O00000O000OO__O00OO0OO00OOO000O):
if positions[OOO0OO0OO0OO00OOO] == O0OO0O00OOOO000O0:
O0OOOO0O00OOO0OOO += 'Q '
else:
O0OOOO0O00OOO0OOO += '. '
print(O0OOOO0O00OOO0OOO)
print('\n')
def __OOO000OO0O0OO00OO(self, positions):
""""""
O00OOO000O00O0O00 = ''
for OOOOO0OOOO0000O0O in range(self._O00O0O00000O000OO__O00OO0OO00OOO000O):
O00OOO000O00O0O00 += str(positions[OOOOO0OOOO0000O0O]) + ' '
print(O00OOO000O00O0O00)
def O0O000OOO0O00OOOO():
""""""
O00O0O00000O000OO(8)
if __name__ == '__main__':
O0O000OOO0O00OOOO()
# okay decompiling sample_ob.pyc
再看看这个?哈哈哈哈
最后,引用一句廖雪峰老师的话:如火如荼的开源运动和互联网自由开放的精神是一致的,互联网上有无数非常优秀的像Linux一样的开源代码,我们千万不要高估自己写的代码真的有非常大的“商业价值”。
参考文献
- 浅谈Python的编译与反编译
- Python Obfuscator
- 通过字节码混淆保护Python代码
- 知乎 使用Python语言如何保护源代码以防止逆向工程?