Big thanks to RĂ©mi Mevaere for the userful tutorial for protecting of Python files! Original tutorial link is there: https://www.sciences-physiques.net/555d13e4a9b34836bb4753192f14225c

Introduction

As you know, it’s a bit hard to protect python code. Cause it’s interpreted ; with a small modification of python source code you could extract all the code (even if it’s obfuscated).

The method I will explain here is really solid and use a third-party tool named “The Enigma Protector”.

Prerequisites

  • Using windows
  • Cython
  • MSVC build-tools installed
  • The enigma protector software (199$), the trial version will obviously raise some alerts from antivirus cause some crackers & hackers use it to protect their apps.

Installing and using Cython

Cython is a programming language that aims to be a superset of the Python programming language, designed to give C-like performance with code that is written mostly in Python with optional additional C-inspired syntax. Cython is a compiled language that is typically used to generate CPython extension modules.”

In one word : Cython could generate with your python code a c++ file compiled in a dynamic link library (DLL) which could be directly called by your python code.

Setup

pip install cython

Example

The first file is the python file script you want to run at the speed of C. Please note the extension .pyx

File : fib.pyx

#cython: language_level=3

def fib(n):
    """Print the Fibonacci series up to n."""
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a + b

    print()

Now we need to create the setup.py, which is like a python Makefile (for more information see Source Files and Compilation)

File : setup.py

from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("fib.pyx"),
)

Now convert and compile all this with

python setup.py build_ext --inplace

Calling the dll created (fib.cp38-win_amd64.pyd) is really easy from python. It’s just treating it like a module.

image
import fib
fib.fib(50000000) # will give the expected result

Protecting your app

The goal is to protect your python app. In order to do this, you will need :

  • some of your most important code in a .pyx file which will be converted in c++, only this code will be protected
  • call the API of enigma protector to introduce the protection (RISC virtual machine etc.)
  • packed the dll produced by cython with enigma protector
  • Use it !

BONUS :As you know, C compilation is largely faster than python cause it doesn’t deal with python object structure. C is also faster when it deals with loops. And cause it doesn’t deal with the GIL, cython gives you the opportunity to use all your CPU cores.

Prepare the compilation

File : setup.py

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

setup(
  name = 'Test app',
  ext_modules=[
    Extension('test',
              sources=['script_test.pyx'],
              extra_link_args=['/MAP'],
			  libraries = ["enigma_ide64"],
			  language="c++")
    ],
  cmdclass = {'build_ext': build_ext}
)

Watch the API

A Marker is a set of bytes placed into the source code and helping Enigma Protector find the code inside markers for processing. A marker consists of two parts: begin marker and end marker.”

// Markers API
void __declspec(dllimport) __stdcall EP_Marker(char* Name);

EP_RegHardware function serves for retrieving unique user PC information. The function does not have parameters. If the function succeeds, the return value is a pointer to the null terminated ANSI string. If the function fails, the return value is 0.”

// Registration API
LPCWSTR __declspec(dllimport) __stdcall EP_RegHardwareIDW();

Some files to add in the directory (sdk path of enigma protector)

image

And you must add to the top of enigma_ide.h

#include <windows.h>

The script test

# distutils: language = c++
# cython: language_level=3

# Declare EP_Marker function in enigma_ide64.lib
cdef extern from "enigma_ide.h":
    void EP_Marker(char* Name)

# Declare EP_RegHardwareID function in enigma_ide64.lib
cdef extern from "enigma_ide.h":
    char* EP_RegHardwareID()

# Declare a trivial function
def sum_it(number1,number2):
    return number1 + number2

# Call and print the EP_RegHardwareID
print(EP_RegHardwareID())

# Crypt this stuff which will only be decrypt with registration
EP_Marker("reg_crypt_begin1")
print("here it's crypted")
EP_Marker("reg_crypt_end1")

# Protect this with virtualization
EP_Marker("vm_risc_begin")
a = 4
b = 7
c = a + b
print('Virtualized :', c)
EP_Marker("vm_risc_end")

# Classic python code
print("Give me the sum :", sum_it(1,2))
input("End, press key")

Build-it

python setup.py build_ext --inplace

Protect-it with enigma protector

image

Call-it

image

BONUS : using widestring character wchar_t*

from cpython.ref cimport PyObject 
from libc.stddef cimport wchar_t

cdef extern from "Python.h":
    PyObject* PyUnicode_FromWideChar(wchar_t *w, Py_ssize_t size)
	
cdef extern from "enigma_ide.h":
    wchar_t* EP_RegHardwareIDW()

cdef PyObject * pystr = PyUnicode_FromWideChar(EP_RegHardwareIDW(), -1)
print(<object>pystr)