How to Protect Python Code from Being Read by Users

How do I protect Python code from being read by users?

Python, being a byte-code-compiled interpreted language, is very difficult to lock down. Even if you use a exe-packager like py2exe, the layout of the executable is well-known, and the Python byte-codes are well understood.

Usually in cases like this, you have to make a tradeoff. How important is it really to protect the code? Are there real secrets in there (such as a key for symmetric encryption of bank transfers), or are you just being paranoid? Choose the language that lets you develop the best product quickest, and be realistic about how valuable your novel ideas are.

If you decide you really need to enforce the license check securely, write it as a small C extension so that the license check code can be extra-hard (but not impossible!) to reverse engineer, and leave the bulk of your code in Python.

Hiding python source code to prevent people from seeing it.. The whole and proper way

So we will be using a few modules to -->

--> Convert our Python code to C source code and PYD files (PYD is the Python equivalent of DLL files)

--> Pack them into an exe


The modules we will be needing are -->

--> Cython

--> Pyinstaller


We will be using my project files (as an example), to demonstrate how to convert all the files to C source code and PYD files

The files in my project are -->

--> chat_screen.py

--> constants.py

--> main_app.py

--> rooms_list_screen.py



  1. We will make another folder, and name it Distributable executable files (You can name
    it whatever u want)

  2. We will be adding all the Python files in the folder but change the extension of
    all the files from py to pyx

  3. Then make another file, known as setup.py and add the following code to it (Note the extension of that file should be py and not pyx)


The setup.py file:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

ext_modules = [
Extension("chat_screen", ["chat_screen.pyx"]),
Extension("constants", ["constants.pyx"]),
Extension("rooms_list_screen", ["rooms_list_screen.pyx"]),
Extension("main_app", ["main_app.pyx"])
]
setup(name='My Cython App',
cmdclass={'build_ext': build_ext},
ext_modules=cythonize(ext_modules),
compiler_directives={'language_level': 3},
zip_safe=False
)

So what we are doing here is, using the Extension class to make all the files, an extension of the app. So the format for the same would be Extension("The name by which u r importing the file (mostly the file name)", ["The full file name with its pyx extension"]

Then we are making a setup function and specifying the list of the extensions we made earlier in the ext_modules kwarg.. But, we have to wrap the list inside of the cythonize function to compile all the files into C source code and pyd files.




  1. Now, open a command prompt window in the same folder and run this command python setup.py build_ext --inplace and wait. This will output some C and PYD files.. These are your Python files, now compiled.

  2. Now, make another file named main.py (Note the extension of that file should be py and not pyx) and add this code to it __import__("main_app") (Replace the file name with your main file), and then run it simply. If your script works without any errors, that means you are ready to compile your app into an exe!!

  3. Now, install Pyinstaller with pip install pyinstaller and in the same folder, open a command prompt and run pyinstaller main.py and wait.

  4. You will see a spec file in the same directory and a build and dist folder. We can ignore both of those folders for now.

  5. Then, open the spec file and you will see something like this in it



block_cipher = None


a = Analysis(['main.py'],
pathex=['Omitted due to privacy concerns'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main')

In the datas kwarg of the Analysis function, add the following code

[
("chat_screen.c", "."),
("chat_screen.cp39-win_amd64.pyd", "."),
("constants.c", "."),
("constants.cp39-win_amd64.pyd", "."),
("main_app.c", "."),
("main_app.cp39-win_amd64.pyd", "."),
("rooms_list_screen.c", "."),
("rooms_list_screen.cp39-win_amd64.pyd", ".")
]

So what we are doing here is, adding every C and pyd file that was generated in the spec file, to make sure they make into the compiled exe. The . represents that the files should be kept in the root of the exe directory.

Also, add the list of imports you are doing in your script in the hiddenimports kwarg of the Analysis function, which in my case is ['requests', 'kivy'].. So my spec file looks something like this

# -*- mode: python ; coding: utf-8 -*-
block_cipher = None


a = Analysis(['main.py'],
pathex=['Omitted due to privacy concerns'],
binaries=[],
datas=[
("chat_screen.c", "."),
("chat_screen.cp39-win_amd64.pyd", "."),
("constants.c", "."),
("constants.cp39-win_amd64.pyd", "."),
("main_app.c", "."),
("main_app.cp39-win_amd64.pyd", "."),
("rooms_list_screen.c", "."),
("rooms_list_screen.cp39-win_amd64.pyd", ".")
],
hiddenimports=['requests', 'kivy'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main')




  1. Now, in a command prompt window in the same folder, type in pyinstaller main.spec and press Enter.. Now your files are being converted to an exe!!! Just wait for some time now, and when the process is finished, just go to the following directory in the same folder dist\<name>\ and you will find all the files of your app here. If you want to launch the app, look for a main.exe file and run it.. There, your app is now ready!!!

If you want to make it into a single file, then instead of just doing pyinstaller main.spec, do pyinstaller main.spec --onefile and it would make a single executable file in the same directory!!!

Don't use UPX with Pyinstaller in this case, as it resulted in some random DLL errors in my case, which got fixed after I repacked without using UPX

How can you lock python code for users without license?

I don't know what sort of license you are using, but if it's in something saved in a text file, you can add an if statement to you code that checks where or not the file exists. Additionally you could add hashing to compare a digest of the official license to the file save on the user's computer.

Python does not have the facilities built-in to enforce copy protection.

How to protect Python source code, while making the file available for running?

The criteria you've posted are inconsistent.
Python is an interpreted language. The entity running the language (i.e. Python interpreter) is reading your code and executing it, line by line. If you wrap it up to send to someone, their Python interpreter must have read permissions on the file, whether it's source code or "compiled" Python (which is easily decompiled into equivalent source code).

If we take a wider interpretation of "send to someone", there may be a business solution that serves your needs. You would provide your functionality, rather than the code: deploy it as a service from some available server: your own, or rented space. To do this, you instead provide an interface to your functionality.

If this fulfills your needs, you now have your next research topic.



Related Topics



Leave a reply



Submit