When doing contract development, I think there are situations where you have to deliver the deliverables to the client. In such a case, for example, if the function can be provided as SaaS, the client side will hit the API and use the function, so of course it will not be possible to grasp the source code on this side. However, I think there are many situations where that is not realistic. There is a library called PyArmor that is useful in such cases. PyArmor is a library that encrypts the source code, and in theory the source code cannot be restored. It is a very convenient library when you want to deliver the source code quickly. In addition to just encrypting, it is also possible to encrypt after granting various licenses (usability period, execution device, etc.). Regarding PyArmor, other articles will come out if you google, but since the specifications have changed slightly, I tried to summarize it as an article this time including the meaning of the memo. For how to use PyArmor, refer to the official document below.
The source code of this blog is posted on [GitHub]. Please refer to the local environment of Mac for the environment and Pipfile for the required libraries.
Here is the code before encryption. [Single_Module] After reading the image file, the trained VGG16 model is used for image classification and image display.
Single_Module/main.py
import os
import sys
import cv2
import numpy as np
import matplotlib.pyplot as plt
import torch
from torchvision import models, transforms
sys.path.append(os.path.abspath('..'))
import imagenet_class
img = cv2.imread('../baseball.png')
img = cv2.resize(img, (256, 256))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_tensor = transforms.ToTensor()(img)
img_tensor = img_tensor.unsqueeze_(0)
model = models.vgg16(pretrained=True)
output = model(img_tensor)
output = np.argmax(output.detach().numpy())
print(imagenet_class.target[str(output)][1]) # ballplayer
plt.imshow(img)
plt.show()
You can see that the result is also correctly classified as ball player
.
Encrypt with the following command.
$ pyarmor obfuscate main.py
After execution, the dist
directory will be created directly underneath. The contents file is as follows.
As you can see in the Generating License For Obfuscated Scripts
section of the official documentation, first run the following command: (Freely name such as r001)
$ pyarmor licenses --expired 2022-01-01 r001
Then, the licenses
directory will be created directly under it, and various files will be created under it. Next, as before, encrypt the main file with the pyarmor obfuscate
command, but execute it with the following options.
$ pyarmor obfuscate --with-license licenses/r001/license.lic main.py
Then, it is possible to encrypt in the form of a license with an expiration date. [Single_Module_Obfuscating_pre]
--In addition, if you try --expired 2019-01-01
with the previous command, you will not be able to execute the encrypted main file because it has already expired.
――It is also possible to issue a license that specifies the device in the same way as above. See the official documentation for details.
The encrypted main file can be executed like a normal python script. The main file encrypted so far exists directly under the dist
directory, so the relative path is slightly different from the original. Therefore, in order to align the paths, I made a configuration like [Single_Module_Obfuscating]. I haven't messed with anything other than changing the location of the files. Try executing the following command.
$ python main.py
【result】
[out] ballplayer
You can see that the result is exactly the same as before encryption. Also, let's try and see what the main file looks like.
$ cat main.py
from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52 ...(Omitted below)
You can see that it is normally encrypted in this way. As I wrote in a bulleted list earlier, a dynamic library that is generated at the same time is always required for this main file to be executed.
By default, all the .py
files directly underneath are encrypted. If you want to recursively encrypt all .py
files including subdirectories, execute with the following options.
$ pyarmor obfuscate --recursive main.py
I will try it.
[Whole_Module] is the source code before encryption, but it is the same as before. (Just separate the files)
The direct main.py
is reading the model / model.py
under the subdirect.
Whole_Module/model/model.py
from torch import nn
from torchvision import models
class SampleModel(nn.Module):
def __init__(self):
super(SampleModel, self).__init__()
self.backborn = models.vgg16(pretrained=True)
def forward(self, x):
x = self.backborn(x)
return x
Whole_Module/main.py
import os
import sys
import cv2
import numpy as np
import matplotlib.pyplot as plt
import torch
from torchvision import transforms
from model.model import SampleModel
sys.path.append(os.path.abspath('..'))
import imagenet_class
img = cv2.imread('../baseball.png')
img = cv2.resize(img, (256, 256))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_tensor = transforms.ToTensor()(img)
img_tensor = img_tensor.unsqueeze_(0)
model = SampleModel()
output = model(img_tensor)
output = np.argmax(output.detach().numpy())
print(imagenet_class.target[str(output)][1])
plt.imshow(img)
plt.show()
Here, when you execute the above command, a new dist
directory will be created, and encrypted main.py
, model.py
, and dynamic library will be created. [Whole_Module_Obfuscating_pre]. However, since it cannot be executed for the same reason as before (the relative path of the image file is different), the generated file has been moved. This can also be executed without any problems as before. [Whole_Module_Obfuscating]
Recommended Posts