前言

  最近总算是找到自己的平衡时间了,所以开坑把自己一些东西记录一下。
  这个工具可谓是新鲜出炉,今天才刚刚弄好。
  其实原理一点也不复杂,具体的原理都是基于PLB下载器加密制作的。
  那么今天为啥突然来开发这么一个工具呢?
  其实完全自己没事找事弄出来的。
  公司需要给外包公司提供一个Maya的检查工具,公司内部是有一套的,但是又不想要把源码交给别人。
  所以就委托我把这个事情干了。
  我自己干得还行吧,虽然中途磕磕碰碰出了些许差错,但是还是出色地将工作落地了。
  在这个过程中涉及到了json的读写操作,json是明文文件,会被别人修改,或者说是将代码的一些核心暴露。
  这样确实不太好,于是我自己就基于PLB下载器的基础上延伸了加密方法,写了这次的加密工具。

加密原理

  原理是相当简单, ASCII 和 unicode 都有一套编码系统来对应相关的字符。
  其实 ASCII 就属于 unicode 的子集,两者是完全不冲突的。
  那么感觉这个原理,如果将字符转换为对应的编号,然后对编号进行偏移,那么所有字符都自然成了乱码了。
  其实这个原理就和ABCD对应数字1234的加密是一样的。
  因此了解这个加密原理之后,就可以开始敲代码了。
  不过在此之前,如果获取到编号以及如何通过编号获取字符呢?
  这就需要介绍 Python 的 ord()chr() 两个函数

Python ord() 函数

   ord() 函数可以获取字符的编号

Python chr() 函数

   chr() 函数可以将相关编号对应到字符上

自己定义的加密规则

获取当前时间 - 将时间以数字的形式相加 (年+月+日+时+分+秒) 这个值定义为 时间权重
获取随机数 - 从 0 到 10000 范围的随机整数 这个值定义为 随机权重

加密信息存储如下:
文件内容 - + 字符序号 + 时间权重 + 随机权重 偏移加密
随机权重 - + 时间权重 偏移加密
文件后缀 - + 时间权重 偏移加密
时间权重 - + 1000 偏移加密
备注信息 (可随意修改)

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import uic
import sys
import time
import os
import traceback
import random

dirname = os.path.dirname(__file__)
UI_PATH = os.path.join(dirname,"ui",'CHR_Encrypter.ui')
form_class , base_class = uic.loadUiType(UI_PATH)

class CHR_Encrypter(base_class,form_class):

def __init__(self):
super(CHR_Encrypter,self).__init__()
self.setupUi(self)

self.Encrypt_Path_BTN.clicked.connect(self.Input_Browse)
self.Decrypt_Path_BTN.clicked.connect(self.Output_Browse)
self.Encrypt_BTN.clicked.connect(self.Encrypt_Fn)
self.Decrypt_BTN.clicked.connect(self.Decrypt_Fn)

def Input_Browse(self):
File_Path = QFileDialog.getOpenFileName(self, caption="获取文件", directory=".")
self.Encrypt_Path_LE.setText(QDir.toNativeSeparators(File_Path))

def Output_Browse(self):
File_Path = QFileDialog.getOpenFileName(self, caption="保存文件到", directory=".")
self.Decrypt_Path_LE.setText(QDir.toNativeSeparators(File_Path))

def Encrypt_Fn(self):

Encrypt_Path = self.Encrypt_Path_LE.text()

if Encrypt_Path == "":
QMessageBox.information(self, "Information", "请输入文件路径")
return

File_Encrypt = ""
RandEncrypt = ""
TimeStampEncrypt = ""
FileExtensionEncrypt = ""

with open(Encrypt_Path,'r', encoding='UTF-8') as f:
Data = f.read()

rand = random.randint(1,10000)

# 获取当前 年月日 时间戳
date = time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time())).split("-")
timeStamp = 0
for d in date:
timeStamp += int(d)
print (timeStamp)

count = 0
for letter in Data:
count += 1
File_Encrypt += chr(ord(letter)+rand+count+timeStamp)

for letter in str(rand):
RandEncrypt += chr(ord(letter)+timeStamp)

for letter in str(timeStamp):
TimeStampEncrypt += chr(ord(letter)+1000)

for letter in Encrypt_Path.split(".")[1]:
FileExtensionEncrypt += chr(ord(letter)+timeStamp)

try:
with open(Encrypt_Path+"_CHR",'w', encoding='UTF-8') as f:
f.write(File_Encrypt+"\n"+RandEncrypt+ "\n" + FileExtensionEncrypt + "\n" + TimeStampEncrypt + "\n文件已经加密,请勿擅自修改\n如需解密请联系 820472580@qq.com")
except:
QMessageBox.information(self, "Information", "写入失败\n检查当前路径是否正确")
return

QMessageBox.information(self, "Information", "数据写入完成")

def Decrypt_Fn(self):

Decrypt_Path = self.Decrypt_Path_LE.text()

if Decrypt_Path == "":
QMessageBox.information(self, "Information", "请输入文件路径")
return

rand = ""
Extension = ""
File_Decrypt = ""
timeStamp = ""
count = 0

# createTime = os.path.getctime(Decrypt_Path)
# timeStruct = time.localtime(createTime)
# timeFeedback = time.strftime('%Y-%m-%d-%H-%M-%S',timeStruct)
# for d in timeFeedback.split("-"):
# timeStamp += int(d)

# print (timeStamp)

with open(Decrypt_Path,'r',encoding='UTF-8') as f:
DataEncrypt = f.read()

try:
for letter in DataEncrypt.split('\n')[3]:
timeStamp += chr(ord(letter)-1000)
timeStamp = int(timeStamp)
print (timeStamp)

for letter in DataEncrypt.split('\n')[1]:
rand += chr(ord(letter)-timeStamp)

print (rand)

for letter in DataEncrypt.split('\n')[2]:
Extension += chr(ord(letter)-timeStamp)

print (Extension)

for letter in DataEncrypt.split('\n')[0]:
count += 1
File_Decrypt += chr(ord(letter)-int(rand)-count-timeStamp)

except Exception:
traceback.print_exc()
QMessageBox.warning(self, "Warning", "文件解密失败")
return

try:
with open(Decrypt_Path.split(".")[0]+"." + Extension,'w', encoding='UTF-8') as f:
f.write(File_Decrypt)
except:
QMessageBox.information(self, "Information", "写入失败\n检查当前路径是否正确")
return

QMessageBox.information(self, "Information", "数据写入完成")

app = QApplication(sys.argv)
dl = CHR_Encrypter()
dl.show()
app.exec_()

运行效果

运行截图

遇到的坑

  其实开发这个插件并没有坑
  最大的坑在于插件使用Python3开发的,而在maya运行的时候是在Python2
  没想到 chr() 函数居然还有版本差异
  Python3 支持 65536 位 而 Python2 只支持 256 位
  结果就是Maya没办法执行解密操作(:з」∠)
  想到是版本差异造成的,也是我就一直试图让Python3的加密配合Python2
  于是我的解决方案改为编号求256的模,从而确保所有的编号都在256的区间中。
  那么解密就很麻烦,需要进行求模的逆运算。
计算原理
  256应该是 ASCII 编号数,那应该求编号的时候也应该在 0 到 255 区间
  从而可以知道 x 的取值范围
  通过不等式并且y是整数的条件可以求出y的值
  回代方程可以求出x的值
  上述就是我想到的解决方案。
  然而我折腾了一个下午之后,发现解码是这样的
解码结果
  其实可以看到非中文字符都已经还原了,没错非 ASCII 全部是乱码
  因为我上面的方法统统将它们转成了以 ASCII 码的形式显示了
  其实我最初的假设是错误的,也就是 ord() 获取的编号是会超过 256 的取值范围
  所以这种方法没办法还原其他字符 (:з」∠)

  后面我思来想去,决定让Python2的代码去兼容Python3的效果
  但是 chr() 的函数完全不一样了,难道还有什么替代方案了
  于是上网找呀找
  后面才发现,为什么 Python3 的 chr() 取值范围大了那么多。
  因为 Python3 默认的编码格式 unicode 而 Python2 的默认编码格式 ASCII
  更好就有了对应关系。
  如此一来,只要 Python2 也有 unicode 的处理方案 应该就可以了。
  没想到这一找还真有,Python2 有 unichr() 函数,可以实现unicode的字符获取
  功夫不负有心人,得来全不费工夫。