Step 1 Programming
Take the Queen N problem on github as an example
"""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()
Test the run
>python3 sample.py Q . . . . . . . . . . . Q . . . . . . . . . . Q . . . . . Q . . . . Q . . . . . . . . . . . Q . . Q . . . . . . . . . Q . . . . Q . . . . . . . . . . . . Q . . . . . . . . . Q . . Q . . . . . . . . . . . Q . . . . Q . . . . . Q . . . . . . . . . . Q . . . ...slightly... . . . . . . . Q . . . Q . . . . Q . . . . . . . . . Q . . . . . . . . . . Q . . . Q . . . . . . . . . . . . Q . . . . . Q . . . Found 92 solutions.
Second step confusion
open Oxyry The website copies the code into the Source box on the left and clicks the Obfuscate button. The code in the Estimate box on the right is the confused code, similar to the following:
""#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
Copy and save the file as sample_ob.python
Test the results:
>python3 sample_ob.py Q . . . . . . . . . . . Q . . . . . . . . . . Q . . . . . Q . . . . Q . . . . . . . . . . . Q . . Q . . . . . . . . . Q . . . . Q . . . . . . . . . . . . Q . . . . . . . . . Q . . Q . . . . . . . . . . . Q . . . . Q . . . . . Q . . . . . . . . . . Q . . . ...slightly... . . . . . . . Q . . Q . . . . . Q . . . . . . . . . . . . Q . . . Q . . . . . . . . . . Q . . . . . . . . . Q . . . . Q . . . . . . . . . . . Q . . . Q . . . . Q . . . . . . . . . Q . . . . . . . . . . Q . . . Q . . . . . . . . . . . . Q . . . . . Q . . . Found 92 solutions.
Step 3 Compilation
Compile using the py_compile module that comes with it:
> python3 -m py_compile sample_ob.py > ls __pycache__ sample_ob.cpython-37.pyc > cp __pycache__/sample_ob.cpython-37.pyc sample_ob.pyc
Test run:
>python3 sample_ob.pyc Q . . . . . . . . . . . Q . . . . . . . . . . Q . . . . . Q . . . . Q . . . . . . . . . . . Q . . Q . . . . . . . . . Q . . . . Q . . . . . . . . . . . . Q . . . . . . . . . Q . . Q . . . . . . . . . . . Q . . . . Q . . . . . Q . . . . . . . . . . Q . . . ...slightly... . . . . . . . Q . . Q . . . . . Q . . . . . . . . . . . . Q . . . Q . . . . . . . . . . Q . . . . . . . . . Q . . . . Q . . . . . . . . . . . Q . . . Q . . . . Q . . . . . . . . . Q . . . . . . . . . . Q . . . Q . . . . . . . . . . . . Q . . . . . Q . . . Found 92 solutions.
Step 4 Decompiler Verification
Install decompiler uncompyle 6
> pip3 install uncompyle6 > uncompyle6 No files given usage: uncompyle6 [--verify | --weak-verify ] [--asm] [--tree[+]] [--grammar] [-o <path>] FILE|DIR... uncompyle6 [--help | -h | --version | -V]
The installation was successful.
Confused Decomposition
Prepare a comparison program named sample.pyc
> python3 -m py_compile sample.py > ls __pycache__ sample.cpython-37.pyc > cp __pycache__/sample.cpython-37.pyc sample.pyc
Decompiled to re-sample.py
> uncompyle6 sample.pyc > re-sample.py
The result is:
# 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
Personally, I feel that the decompilation result can see almost all the core of the algorithm.
Confused Decompilation
> uncompyle6 sample_ob.pyc > re-sample_ob.py
Result:
# 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
Look at this again? Ha ha ha ha
Finally, quote a sentence Teacher Liao Xuefeng's remarks The vigorous open source movement is consistent with the spirit of Internet freedom and openness. There are countless excellent open source codes like Linux on the Internet. We must not overestimate the "commercial value" of our code.