python混淆编译

第一步 编写程序

以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一样的开源代码,我们千万不要高估自己写的代码真的有非常大的“商业价值”。

image

参考文献

  1. 浅谈Python的编译与反编译
  2. Python Obfuscator
  3. 通过字节码混淆保护Python代码
  4. 知乎 使用Python语言如何保护源代码以防止逆向工程?

你可能感兴趣的:(python混淆编译)