52 Commits

Author SHA1 Message Date
Mason Shi
c984fd06c0 Merge pull request #48 from Mas0nShi/auto-analysis-dev
Auto analysis dev
2022-04-04 13:53:29 +08:00
MasOnShi
f340eef89c dev commits. 2022-04-04 13:51:22 +08:00
Mason Shi
b8f870e55f Update manual.yml 2022-04-04 13:45:57 +08:00
Mason Shi
4d635ec11f Update manual.yml 2022-04-04 13:43:20 +08:00
Mason Shi
2d106c7e48 Update manual.yml 2022-04-04 13:42:01 +08:00
Mason Shi
8f070902e0 Update manual.yml 2022-04-04 13:39:22 +08:00
Mason Shi
f9bb673a51 Update manual.yml 2022-04-04 13:35:45 +08:00
MasOnShi
c3fa3cb361 dev commits. 2022-04-04 13:34:38 +08:00
Mason Shi
8ee3a91b18 Update manual.yml 2022-04-04 13:30:16 +08:00
Mason Shi
c262491d67 Merge pull request #47 from Mas0nShi/auto-analysis-dev
dev commits.
2022-04-04 13:27:21 +08:00
Mason Shi
2ff0a25c1f Update manual.yml 2022-04-04 13:26:57 +08:00
MasOnShi
7176bb2f82 dev commits. 2022-04-04 13:26:25 +08:00
Mason Shi
785b3bcd49 Update manual.yml 2022-04-04 13:17:29 +08:00
Mason Shi
40a95424b0 Update manual.yml 2022-04-04 13:03:21 +08:00
Mason Shi
cd28ff03d9 Update manual.yml 2022-04-04 12:53:23 +08:00
Mason Shi
7aaba1739a Update manual.yml 2022-04-04 12:43:04 +08:00
Mason Shi
7d73c08257 Update manual.yml 2022-04-04 12:42:14 +08:00
Mason Shi
d2fd9f12bf Update manual.yml 2022-04-04 12:22:50 +08:00
Mason Shi
142367e963 Update manual.yml 2022-04-03 22:53:08 +08:00
Mason Shi
7e1916e8e9 Update manual.yml 2022-04-03 22:45:50 +08:00
Mason Shi
fe77785833 Update manual.yml 2022-04-03 22:37:38 +08:00
Mason Shi
53a9057a2c Update manual.yml 2022-04-03 22:34:17 +08:00
Mason Shi
1df0199b91 Update manual.yml 2022-04-03 22:08:37 +08:00
Mason Shi
9b298bc024 Update manual.yml 2022-04-03 22:03:10 +08:00
Mason Shi
029a4a344f Update manual.yml 2022-04-03 22:00:37 +08:00
Mason Shi
3e690f077d Update manual.yml 2022-04-03 21:59:50 +08:00
Mason Shi
5c518c7ab8 Update manual.yml 2022-04-03 21:58:21 +08:00
Mason Shi
fc578afd40 Update manual.yml 2022-04-03 21:56:07 +08:00
Mason Shi
4b3cfee43f Create manual.yml 2022-04-03 21:50:50 +08:00
Mason Shi
9369288989 Merge pull request #46 from Mas0nShi/auto-analysis-dev
Auto analysis dev
2022-04-03 21:50:34 +08:00
MasOnShi
8f467159c7 dev commits. 2022-04-03 21:50:23 +08:00
MasOnShi
5489c3b664 dev commits. 2022-04-03 21:47:37 +08:00
MasOnShi
1710c83f99 dev commits. 2022-04-03 20:51:11 +08:00
MasOnShi
219e0316f5 dev commits. 2022-04-03 20:47:47 +08:00
MasOnShi
60a7f39b16 dev commits. 2022-04-03 20:47:37 +08:00
MasOnShi
3c9d47b625 dev commits. 2022-04-03 20:41:22 +08:00
MasOnShi
16c225bb1f dev commits. 2022-04-03 20:38:48 +08:00
MasOnShi
643c0f3e3c dev commits. 2022-04-03 20:33:02 +08:00
MasOnShi
d84a6b9041 dev commits. 2022-04-03 20:26:41 +08:00
MasOnShi
e21922b1f1 dev commits. 2022-04-03 20:22:25 +08:00
MasOnShi
18e2562146 dev commits. 2022-04-03 20:16:16 +08:00
MasOnShi
d870e234c3 dev commits. 2022-04-03 20:14:34 +08:00
MasOnShi
76c9a5b398 dev commits. 2022-04-03 20:12:42 +08:00
MasOnShi
25cb04e00f dev commits. 2022-04-03 20:09:41 +08:00
MasOnShi
1c879400fd dev commits. 2022-04-03 20:05:35 +08:00
MasOnShi
d5f9fa4026 dev commits. 2022-04-03 19:58:21 +08:00
MasOnShi
11589aff49 dev commits. 2022-04-03 19:57:35 +08:00
MasOnShi
2a7fd5622a dev commits. 2022-04-03 19:50:15 +08:00
MasOnShi
df83d9cae4 updaate aesIv for 1.2.2-dev 2022-04-03 19:28:39 +08:00
Mason Shi
d90f36ba80 Update README_CN.md 2022-03-30 21:17:28 +08:00
Mason Shi
6c5039c632 Update README.md 2022-03-30 21:16:50 +08:00
Mason Shi
f44e9355fb Update issue templates 2022-03-24 19:36:36 +08:00
17 changed files with 328 additions and 363 deletions

22
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
- OS: [e.g. Ubuntu]
- Version [e.g. 1.1.2]
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

135
.github/workflows/manual.yml vendored Normal file
View File

@@ -0,0 +1,135 @@
# This is a basic workflow that is manually triggered
name: Manual workflow
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
workflow_dispatch:
schedule:
- cron: '23 21 * * 1'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "greet"
check_version:
name: check the latest version
# The type of runner that the job will run on
runs-on: ubuntu-20.04
outputs:
RELEASE_VERSION: ${{ steps.getLatestRelease.outputs.RELEASE_VERSION }}
LATEST_VERSION: ${{ steps.checkVersion.outputs.LATEST_VERSION }}
steps:
- name: Get Latest Release
id: getLatestRelease
uses: actions/github-script@v3.1.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const releaseResponse = await github.repos.getLatestRelease({
owner: 'Mas0nShi',
repo: 'typoraCracker',
})
const {
data: { tag_name: ver }
} = releaseResponse;
core.setOutput('RELEASE_VERSION', ver);
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: '3.8' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- name: install dependencies
run: |
sudo apt-get update && DEBIAN_FRONTEND=noninteractive sudo apt-get install innoextract -y
python3 -m pip install loguru
- name: Check Latest Version
id: checkVersion
run: |
python3 auto-analysis/check_version.py
output="$(cat auto-analysis/win/x64/LATEST_VERSION)"
echo "$output"
echo "::set-output name=LATEST_VERSION::$output"
create_release:
needs: check_version
runs-on: ubuntu-20.04
if: needs.check_version.outputs.RELEASE_VERSION != needs.check_version.outputs.LATEST_VERSION
steps:
- run: echo '${{ needs.check_version.outputs.LATEST_VERSION }}'
- name: Create Runner Release
uses: actions/create-release@v1
id: createRelease
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: '${{ needs.check_version.outputs.LATEST_VERSION }}'
release_name: '${{ needs.check_version.outputs.LATEST_VERSION }}'
prerelease: false
patch_file:
needs: [check_version, create_release]
runs-on: ubuntu-20.04
if: needs.check_version.outputs.RELEASE_VERSION != needs.check_version.outputs.LATEST_VERSION
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: '3.8' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- name: install dependencies
run: |
sudo apt-get update && DEBIAN_FRONTEND=noninteractive sudo apt-get install innoextract cmake -y
python3 -m pip install r2pipe loguru
- name: build radare2
shell: bash
run: |
git clone https://github.com/radareorg/radare2
radare2/sys/install.sh
- name: patch version
run: |
python3 auto-analysis/patch.py
tar -zcvf auto-analysis/win/x64/build/typoraCracker.tar.gz auto-analysis/win/x64/build/*
- name: Check release version
id: checkReleaseVersion
uses: actions/github-script@v3.1.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
try {
const releaseVersion = '${{ needs.check_version.outputs.LATEST_VERSION }}'
const releaseResponse = await github.repos.getReleaseByTag({
owner: 'Mas0nShi',
repo: 'typoraCracker',
tag: releaseVersion
})
const {
data: { id: releaseId, html_url: htmlUrl, upload_url: uploadUrl }
} = releaseResponse;
core.setOutput('id', releaseId);
core.setOutput('html_url', htmlUrl);
core.setOutput('upload_url', uploadUrl);
core.setOutput('version', releaseVersion);
} catch (e) {
core.setFailed(e.message);
}
- name: Upload win x64 typora.py for typoraCracker
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: '${{ steps.checkReleaseVersion.outputs.upload_url }}'
asset_path: '${{ github.workspace }}/auto-analysis/win/x64/build/typoraCracker.tar.gz'
asset_name: 'typoraCracker-${{ needs.check_version.outputs.LATEST_VERSION }}-win-x64.tar.gz'
asset_content_type: application/x-tgz

View File

@@ -1,3 +1,6 @@
**typoraCracker STOPS MAINTENANCE NOW. [why](https://github.com/Mas0nShi/typoraCracker/issues/39#issuecomment-1083117056)?**
# typora Cracker
![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMas0nShi%2FtyporaCracker.svg?type=shield)

View File

@@ -1,3 +1,6 @@
**typoraCracker 停止维护. [为什么](https://github.com/Mas0nShi/typoraCracker/issues/39#issuecomment-1083117056)**
# typora Cracker
一个typora的解包&解密,打包&加密工具

View File

@@ -0,0 +1,22 @@
from utils import get_version, download_file, extract_file, log, DOWNLOAD_LINK
import os
BASE_DIR = os.path.dirname(__file__)
def win_x64_version():
url = DOWNLOAD_LINK["win"]["x64"]
dir = os.path.join(BASE_DIR, "win/x64")
download_path = os.path.join(dir, os.path.basename(url))
download_file(url, download_path)
extract_file(download_path, dir)
version = get_version(dir)
open(os.path.join(dir, "LATEST_VERSION"), "w").write(version)
log.success(version)
if __name__ == '__main__':
win_x64_version()

View File

View File

12
auto-analysis/patch.py Normal file
View File

@@ -0,0 +1,12 @@
# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: patch.py
@Time: 2022/4/3 18:36
@Desc: It's all about getting better.
"""
import utils
if __name__ == '__main__':
utils.win_x64_run()

94
auto-analysis/utils.py Normal file
View File

@@ -0,0 +1,94 @@
# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: utils.py
@Time: 2022/4/3 18:36
@Desc: It's all about getting better.
"""
from loguru import logger as log
import subprocess
import json
import os
# Usage:
# innoextract
#
BASE_DIR = os.path.dirname(__file__)
DOWNLOAD_LINK = {
"win": {
"x86": "https://typora.io/windows/typora-setup-ia32.exe",
"x64": "https://typora.io/windows/typora-setup-x64.exe",
"arm": "https://typora.io/windows/typora-setup-arm64.exe",
},
"linux": {
"x64": "https://download.typora.io/linux/Typora-linux-x64.tar.gz",
"arm": "https://download.typora.io/linux/Typora-linux-arm64.tar.gz",
},
}
def get_version(to_path):
package_file_path = os.path.join(to_path, "app/resources/package.json")
package_info = open(package_file_path, "r").read()
package_obj = json.loads(package_info)
return package_obj["version"]
def download_file(from_link, to_path):
subprocess.check_call(["wget", from_link, "-O", to_path])
def extract_file(from_path, to_path):
subprocess.check_call(["innoextract", from_path, "-d", to_path])
def patch_file(_key, _iv, to_dir):
exports_file_path = os.path.join(BASE_DIR, "../exports.tar.gz")
save_dir = os.path.join(to_dir, "build")
if not os.path.exists(save_dir):
os.makedirs(save_dir)
subprocess.check_call(["tar", "-zxvf", exports_file_path, "-C", save_dir])
patch_file_path = os.path.join(save_dir, "typora.py")
content = open(patch_file_path, "r").read()
content = content.replace("{AES_KEY}", f"b''.fromhex('{_key}')")
content = content.replace("{AES_IV}", f"b''.fromhex('{_iv}')")
open(patch_file_path, "w").write(content)
def win_x64_run():
from win.x64 import analysis
basedir = os.path.join(BASE_DIR, "win/x64")
link = DOWNLOAD_LINK["win"]["x64"]
download_path = os.path.join(basedir, os.path.basename(link))
log.info(f"downloading from {link}")
download_file(link, download_path)
log.info("ready extract package")
extract_file(download_path, basedir)
log.info("preparation stage completed")
main_node_path = os.path.join(basedir, "app/resources/app.asar.unpacked/main.node")
log.info("auto analysis start")
key, iv = analysis.get_aes_key_and_iv(main_node_path)
log.success("analysis done")
patch_file(key.hex(), iv.hex(), basedir)
log.success("patch done")
if __name__ == '__main__':
win_x64_run()
# hashString = open("LATEST_VERSION", "r").read()
# if hashString == "":
# log.info("not history for typora version")
# exit()
# basedir = os.path.dirname(__file__)
# for h1 in DOWNLOAD_LINK.keys():
# h1dir = os.path.join(basedir, h1)
# for h2 in DOWNLOAD_LINK.get(h1).keys():
# h2dir = os.path.join(h1dir, h2)
# print(h2dir)

View File

View File

View File

@@ -0,0 +1,37 @@
# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@Name: typora_win_x64_analysis
@Time: 2022/4/3 18:26
@Desc: It's all about getting better.
"""
import struct
import r2pipe
def get_aes_key_and_iv(file_path):
r = r2pipe.open(file_path)
# auto analysis
r.cmd("aaa")
# string "base64" x-cross reference
regex = r.cmdj("axtj @@ str.base64")
assert len(regex) == 1
func = regex[0]["fcn_name"]
# disasm func
r.cmd(f"s {func}")
asm = r.cmdj("pdfj")['ops']
assert len(asm) != 0
asm_regex = []
for body in asm:
if "=[4]" in body["esil"] and body['type'] == 'mov':
opcode, value = body["disasm"].split(", ")
asm_regex.append({"opcode": opcode, "value": value})
assert len(asm_regex) == 12
iv = struct.pack("<4L", *[int(asm_regex[i]['value'], 16) for i in range(4)])
key = struct.pack("<8L", *[int(asm_regex[i]['value'], 16) for i in range(4, 12)])
# print(key, iv)
return key, iv

View File

BIN
exports.tar.gz Normal file

Binary file not shown.

176
masar.py
View File

@@ -1,176 +0,0 @@
# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: masar.py
@Time: 2021-11-29 22:34
@Desc: It's all about getting better.
"""
import os
import errno
import io
import struct
import shutil
import fileinput
import json
def round_up(i, m):
return (i + m - 1) & ~(m - 1)
class Asar:
def __init__(self, path, fp, header, base_offset):
self.path = path
self.fp = fp
self.header = header
self.base_offset = base_offset
@classmethod
def open(cls, path):
fp = open(path, 'rb')
data_size, header_size, header_object_size, header_string_size = struct.unpack('<4I', fp.read(16))
header_json = fp.read(header_string_size).decode('utf-8')
return cls(
path=path,
fp=fp,
header=json.loads(header_json),
base_offset=round_up(16 + header_string_size, 4)
)
@classmethod
def compress(cls, path):
offset = 0
paths = []
def _path_to_dict(path):
nonlocal offset, paths
result = {'files': {}}
for f in os.scandir(path):
if os.path.isdir(f.path):
result['files'][f.name] = _path_to_dict(f.path)
elif f.is_symlink():
result['files'][f.name] = {
'link': os.path.realpath(f.name)
}
# modify
elif f.name == "main.node":
size = f.stat().st_size
result['files'][f.name] = {
'size': size,
"unpacked": True
}
else:
paths.append(f.path)
size = f.stat().st_size
result['files'][f.name] = {
'size': size,
'offset': str(offset)
}
offset += size
return result
def _paths_to_bytes(paths):
_bytes = io.BytesIO()
with fileinput.FileInput(files=paths, mode="rb") as f:
for i in f:
_bytes.write(i)
return _bytes.getvalue()
header = _path_to_dict(path)
header_json = json.dumps(header, sort_keys=True, separators=(',', ':')).encode('utf-8')
header_string_size = len(header_json)
data_size = 4
aligned_size = round_up(header_string_size, data_size)
header_size = aligned_size + 8
header_object_size = aligned_size + data_size
diff = aligned_size - header_string_size
header_json = header_json + b'\0' * diff if diff else header_json
fp = io.BytesIO()
fp.write(struct.pack('<4I', data_size, header_size, header_object_size, header_string_size))
fp.write(header_json)
fp.write(_paths_to_bytes(paths))
return cls(
path=path,
fp=fp,
header=header,
base_offset=round_up(16 + header_string_size, 4))
def _copy_unpacked_file(self, source, destination):
unpacked_dir = self.path + '.unpacked'
if not os.path.isdir(unpacked_dir):
print("Couldn't copy file {}, no extracted directory".format(source))
return
src = os.path.join(unpacked_dir, source)
if not os.path.exists(src):
print("Couldn't copy file {}, doesn't exist".format(src))
return
dest = os.path.join(destination, source)
shutil.copyfile(src, dest)
def _extract_file(self, source, info, destination):
if 'offset' not in info:
self._copy_unpacked_file(source, destination)
return
self.fp.seek(self.base_offset + int(info['offset']))
r = self.fp.read(int(info['size']))
dest = os.path.join(destination, source)
with open(dest, 'wb') as f:
f.write(r)
def _extract_link(self, source, link, destination):
dest_filename = os.path.normpath(os.path.join(destination, source))
link_src_path = os.path.dirname(os.path.join(destination, link))
link_to = os.path.join(link_src_path, os.path.basename(link))
try:
os.symlink(link_to, dest_filename)
except OSError as e:
if e.errno == errno.EXIST:
os.unlink(dest_filename)
os.symlink(link_to, dest_filename)
else:
raise e
def _extract_directory(self, source, files, destination):
dest = os.path.normpath(os.path.join(destination, source))
if not os.path.exists(dest):
os.makedirs(dest)
for name, info in files.items():
item_path = os.path.join(source, name)
if 'files' in info:
self._extract_directory(item_path, info['files'], destination)
elif 'link' in info:
self._extract_link(item_path, info['link'], destination)
else:
self._extract_file(item_path, info, destination)
def extract(self, path):
if not os.path.isdir(path):
raise NotADirectoryError()
self._extract_directory('.', self.header['files'], path)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.fp.close()
def pack_asar(source, dest):
with Asar.compress(source) as a:
with open(dest, 'wb') as fp:
a.fp.seek(0)
fp.write(a.fp.read())
def extract_asar(source, dest):
with Asar.open(source) as a:
a.extract(dest)

View File

@@ -1,4 +0,0 @@
jsbeautifier==1.14.0
jsmin==3.0.0
loguru==0.5.3
pycryptodome==3.11.0

183
typora.py
View File

@@ -1,183 +0,0 @@
# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: typora.py
@Time: 2021-11-29 21:24
@Desc: It's all about getting better.
"""
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from base64 import b64decode, b64encode
from jsbeautifier import beautify
from jsmin import jsmin
from os import listdir, urandom, makedirs
from os.path import isfile, isdir, join as pjoin, split as psplit, exists, abspath
from loguru import logger as log
from masar import extract_asar, pack_asar
from shutil import rmtree
from argparse import ArgumentParser
import struct
import sys
# DEBUG
DEBUG = False
log.remove()
if DEBUG:
log.add(sys.stderr, level="DEBUG")
else:
log.add(sys.stderr, level="INFO")
AES_KEY = struct.pack("<4Q", *[0x252A4C7BD0B85281, 0xA31BD92CE099F719, 0x13E283392646D82D, 0x118BDE501CF74120])
AES_IV = struct.pack("<4L", *[0x33706964, 0x5387CDD2, 0xD05F336D, 0x53F82468])
def _mkDir(_path):
if not exists(_path):
makedirs(_path)
else:
if _path == psplit(__file__)[0]:
log.warning("plz try not to use the root dir.")
else:
log.warning(f"May FolderExists: {_path}")
def decScript(b64: bytes, prettify: bool):
lCode = b64decode(b64)
# iv
aesIv = AES_IV
# cipher text
cipherText = lCode[:]
# AES 256 CBC
ins = AES.new(key=AES_KEY, iv=aesIv, mode=AES.MODE_CBC)
code = unpad(ins.decrypt(cipherText), 16, 'pkcs7')
if prettify:
code = beautify(code.decode()).encode()
return code
def extractWdec(asarPath, path, prettify):
"""
:param prettify: bool
:param asarPath: asar out dir
:param path: out dir
:return: None
"""
# try to create empty dir to save extract files
path = pjoin(path, "typoraCrackerTemp")
if exists(path):
rmtree(path)
_mkDir(path)
log.info(f"extract asar file: {asarPath}")
# extract app.asar to {path}/*
extract_asar(asarPath, path)
log.success(f"extract ended.")
log.info(f"read Directory: {path}")
# construct the save directory {pathRoot}/dec_app
outPath = pjoin(psplit(path)[0], "dec_app")
# try to create empty dir to save decryption files
if exists(outPath):
rmtree(outPath)
_mkDir(outPath)
log.info(f"set Directory: {outPath}")
# enumerate extract files
fileArr = listdir(path)
for name in fileArr:
# read files content
fpath = pjoin(path, name)
scode = open(fpath, "rb").read()
log.info(f"open file: {name}")
# if file suffix is *.js then decryption file
if isfile(fpath) and name.endswith(".js"):
scode = decScript(scode, prettify)
else:
log.debug(f"skip file: {name}")
# save content {outPath}/{name}
open(pjoin(outPath, name), "wb").write(scode)
log.success(f"decrypt and save file: {name}")
rmtree(path)
log.debug("remove temp dir")
def encScript(_code: bytes, compress):
if compress:
_code = jsmin(_code.decode(), quote_chars="'\"`").encode()
aesIv = AES_IV
cipherText = _code
ins = AES.new(key=AES_KEY, iv=aesIv, mode=AES.MODE_CBC)
enc = ins.encrypt(pad(cipherText, 16, 'pkcs7'))
lCode = b64encode(enc)
return lCode
def packWenc(path, outPath, compress):
"""
:param path: out dir
:param outPath: pack path app.asar
:param compress: Bool
:return: None
"""
# check out path
if isfile(outPath):
log.error("plz input Directory for app.asar")
raise NotADirectoryError
_mkDir(outPath)
encFilePath = pjoin(psplit(outPath)[0], "typoraCrackerTemp")
if exists(encFilePath):
rmtree(encFilePath)
_mkDir(encFilePath)
outFilePath = pjoin(outPath, "app.asar")
log.info(f"set outFilePath: {outFilePath}")
fileArr = listdir(path)
for name in fileArr:
fpath = pjoin(path, name)
if isdir(fpath):
log.error("TODO: found folder")
raise IsADirectoryError
scode = open(fpath, "rb").read()
log.info(f"open file: {name}")
if isfile(fpath) and name.endswith(".js"):
scode = encScript(scode, compress)
open(pjoin(encFilePath, name), "wb").write(scode)
log.success(f"encrypt and save file: {name}")
log.info("ready to pack")
pack_asar(encFilePath, outFilePath)
log.success("pack done")
rmtree(encFilePath)
log.debug("remove temp dir")
def main():
argParser = ArgumentParser(
description="[extract and decryption / pack and encryption] app.asar file from [Typora].",
epilog="If you have any questions, please contact [ MasonShi@88.com ]")
argParser.add_argument("asarPath", type=str, help="app.asar file path/dir [input/ouput]")
argParser.add_argument("dirPath", type=str, help="as tmp and out directory.")
argParser.add_argument('-u', dest='mode', action='store_const',
const=packWenc, default=extractWdec,
help='pack & encryption (default: extract & decryption)')
argParser.add_argument('-f', dest='format', action='store_const',
const=True, default=False,
help='enabled prettify/compress (default: disabled)')
args = argParser.parse_args()
args.mode(args.asarPath, args.dirPath, args.format)
log.success("Done!")
if __name__ == '__main__':
main()