python obfuscation compilation

Posted by vishi83 on Fri, 10 May 2019 01:02:03 +0200

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.

image

Reference

  1. Compiling and Decompilating of Python
  2. Python Obfuscator
  3. Protecting Python code by bytecode obfuscation
  4. Know how to protect source code with Python language to prevent reverse engineering?

Topics: Python github Programming Linux