**import os**
**import pathlib**
**import shutil**
**from base64 import b64decode, b64encode**
**from asarPy import extract\_asar, pack\_asar**
**from crypto\_plus import CryptoPlus**
**from crypto\_plus.**encrypt** import decrypt\_by\_key, encrypt\_by\_key**
**from abc import ABCMeta, abstractmethod**
**class** **KeyGen**(**metaclass=ABCMeta**)**:**
** @abstractmethod**
** **def** **generate**(**self**)**:
** pass**
** @abstractmethod**
** **def** **parse**(**self, licenses**)**:
** pass**
** @abstractmethod**
** **def** **patch**(**self**)**:
** **return** **""
** **def** **run**(**self, patch=**True**)**:**
** ciphertext\_licenses = self.**generate**()**
** **print**(**f**"ciphertext\_licenses: \\n{ciphertext\_licenses}"**)
** **if** patch:**
** patch\_info = self.**patch**()**
** **if** patch\_info:**
** **print**(**f**"patch: \\n{patch\_info}"**)
** plaintext\_licenses = self.**parse**(**ciphertext\_licenses**)**
** **print**(**f**'plaintext\_licenses: \\n{plaintext\_licenses}'**)
**class** **XmindKeyGen**(**KeyGen**)**:**
** **def** **\_\_init\_\_**(**self**)**:
** **# tmp\_path = os.environ['TMP']
** **# mac 软件路径不一样,可以自己看着修改
** asar\_path = pathlib.**Path**(**'/Applications/Xmind.app/Contents/Resources'**)**
** self.**asar\_file** = asar\_path.**joinpath**(**'app.asar'**)**
** self.**asar\_file\_bak** = asar\_path.**joinpath**(**'app.asar.bak'**)**
** self.**crack\_asar\_dir** = asar\_path.**joinpath**(**'ext'**)**
** self.**main\_dir** = self.**crack\_asar\_dir**.**joinpath**(**"main"**)**
** self.**renderer\_dir** = self.**crack\_asar\_dir**.**joinpath**(**"renderer"**)**
** self.**private\_key** = None**
** self.**public\_key** = None**
** self.**old\_public\_key** = **open**(**'old.pem'**)**.**read**()
** **def** **generate**(**self**)**:
** **if** os.**path**.**isfile**(**'key.pem'**)**:
** rsa = CryptoPlus.**load**(**'key.pem'**)**
** **else**:**
** rsa = CryptoPlus.**generate\_rsa**(**1024**)**
** rsa.**dump**(**"key.pem"**, **"new\_public\_key.pem"**)**
** license\_info = **'{"status": "sub", "expireTime": 4093057076000, "ss": "", "deviceId": "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"}'
** self.**public\_key** = rsa.**public\_key
** self.**private\_key** = rsa.**private\_key
** self.**license\_data** = **b64encode**(**encrypt\_by\_key**(**rsa.**private\_key**, license\_info.**encode**()))
** **return** self.**license\_data
** **def** **parse**(**self, licenses**)**:
** **return** **decrypt\_by\_key**(**self.**public\_key**, **b64decode**(**licenses**))
** **def** **patch**(**self**)**:
** **# 先删除接包的内容,防止已经存在报错
** shutil.**rmtree**(**self.**crack\_asar\_dir**, ignore\_errors=**True**)
** **# 解包
** **extract\_asar**(**str**(**self.**asar\_file**)**, **str**(**self.**crack\_asar\_dir**))
** shutil.**copytree**(**'crack'**, self.**main\_dir**, dirs\_exist\_ok=**True**)**
** **# 注入
** **# 看了一下mac最新版本的源代码只有一行,这里采用插入首行
** with **open**(**self.**main\_dir**.**joinpath**(**'main.js'**)**, **'rb'**)** as f:
** lines = f.**readlines**()**
** **# lines[5] = b'require("./hook")\\n'
** lines.**insert**(**0**, b**'require("./hook");\\n'**)**
** with **open**(**self.**main\_dir**.**joinpath**(**'main.js'**)**, **'wb'**)** as f:
** f.**writelines**(**lines**)**
** **# 替换密钥
** old\_key = f**"String.fromCharCode({','.join([str(i) for i in self.old\_public\_key.encode()])})"**.**encode**()**
** new\_key = f**"String.fromCharCode({','.join([str(i) for i in self.public\_key.export\_key()])})"**.**encode**()**
** **for** js\_file **in** self.**renderer\_dir**.**rglob**(**"\*.js"**)**:
** with **open**(**js\_file, **'rb'**)** as f:**
** byte\_str = f.**read**()**
** index = byte\_str.**find**(**old\_key**)**
** **if** index != **-1**:**
** byte\_str.**replace**(**old\_key, new\_key**)**
** with **open**(**js\_file, **'wb'**)** as \_f:**
** \_f.**write**(**byte\_str.**replace**(**old\_key, new\_key**))
** **print**(**js\_file**)**
** break**
** **# 手动搜索发现 main.js 里面也有公钥信息,一起替换掉
** **for** js\_file **in** self.**main\_dir**.**rglob**(**"\*.js"**)**:
** with **open**(**js\_file, **'rb'**)** as f:**
** byte\_str = f.**read**()**
** index = byte\_str.**find**(**old\_key**)**
** **if** index != **-1**:**
** byte\_str.**replace**(**old\_key, new\_key**)**
** with **open**(**js\_file, **'wb'**)** as \_f:**
** \_f.**write**(**byte\_str.**replace**(**old\_key, new\_key**))
** **print**(**js\_file**)**
** break**
** **# 占位符填充
** with **open**(**self.**main\_dir**.**joinpath**(**'hook.js'**)**, **'r'**, encoding=**'u8'**)** as f:
** content = f.**read**()**
** content = content.**replace**(**"{{license\_data}}"**, self.**license\_data**.**decode**())**
** with **open**(**self.**main\_dir**.**joinpath**(**'hook.js'**)**, **'w'**, encoding=**'u8'**)** as f:
** f.**write**(**content**)**
** with **open**(**self.**main\_dir**.**joinpath**(**'hook'**)**.**joinpath**(**'crypto.js'**)**, **'r'**, encoding=**'u8'**)** as f:**
** content = f.**read**()**
** content = content.**replace**(**"{{old\_public\_key}}"**, self.**old\_public\_key**.**replace**(**"\\n"**, **"\\\\n"**))**
** content = content.**replace**(**"{{new\_public\_key}}"**, self.**public\_key**.**export\_key**()**.**decode**()**.**replace**(**"\\n"**, **"\\\\n"**))**
** with **open**(**self.**main\_dir**.**joinpath**(**'hook'**)**.**joinpath**(**'crypto.js'**)**, **'w'**, encoding=**'u8'**)** as f:**
** f.**write**(**content**)**
** **# 封包
** os.**remove**(**self.**asar\_file**)
** **pack\_asar**(**self.**crack\_asar\_dir**, self.**asar\_file**)
** shutil.**rmtree**(**self.**crack\_asar\_dir**)
**if** \_\_name\_\_ == **'\_\_main\_\_'**:
** **XmindKeyGen**()**.**run**()
|