dev commits.

This commit is contained in:
MasOnShi
2022-04-03 20:47:47 +08:00
parent 60a7f39b16
commit 219e0316f5
6 changed files with 0 additions and 518 deletions

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Mason Shi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,70 +0,0 @@
# typora Cracker
![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMas0nShi%2FtyporaCracker.svg?type=shield)
![GitHub](https://img.shields.io/github/license/Mas0nShi/typoraCracker)
A extract & decryption and pack & encryption tools for typora.
中文说明请戳[这里](README_CN.md)
## WARNING
**NOTE: typoraCracker doesn't provide support for crack.**
```
FOR STUDY AND DISCUSSION ONLY, PLEASE DO NOT ENGAGE IN ANY ILLEGAL ACTS.
ANY PROBLEMS ARISING FROM THIS WILL BE BORNE BY THE USER (YOU).
```
## Features
- Supports Version 1.0.0+ (At least for now.)
- tested fine in Windows, Ubuntu
## Usage
1. `pip install -r requirements.txt`
2. `python typora.py --help`
3. read and use.
4. do something.
5. pack and replace app.asar.
6. enjoy it.
## Example
```shell
> python typora.py --help
usage: typora.py [-h] [-u] [-f] asarPath dirPath
[extract and decryption / pack and encryption] app.asar file from [Typora].
positional arguments:
asarPath app.asar file path/dir [input/ouput]
dirPath as tmp and out directory.
optional arguments:
-h, --help show this help message and exit
-u pack & encryption (default: extract & decryption)
-f enabled prettify/compress (default: disabled)
If you have any questions, please contact [ MasonShi@88.com ]
> python typora.py {installRoot}/Typora/resources/app.asar workstation/outfile/
# (patch code by yourself in workstation/outfile/dec_app)
> python typora.py -u workstation/outfile/dec_app workstation/outappasar
> cp {installRoot}/Typora/resources/app.asar {installRoot}/Typora/resources/app.asar.bak
> mv workstation/outappasar/app.asar {installRoot}/Typora/resources/app.asar
> node example/keygen.js
XXXXXX-XXXXXX-XXXXXX-XXXXXX
> typora
# (input info)
email: crack@example.com
serial: XXXXXX-XXXXXX-XXXXXX-XXXXXX
```
## LICENSE
MIT LICENSE
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMas0nShi%2FtyporaCracker.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FMas0nShi%2FtyporaCracker?ref=badge_large)

View File

@@ -1,64 +0,0 @@
# typora Cracker
一个typora的解包&解密,打包&加密工具
## 敬告
**请注意:** typoraCracker不会提供破解相关支持包括但不限于思路、流程、成品。
```
仅供学习和讨论,请不要从事任何非法行为。
由此产生的任何问题都将由用户(您)承担。
```
## Features
- 支持版本1.0.0以上(至少现在是这样)
- 测试通过平台Win/Ubuntu
## 食用方式
1. `pip install -r requirements.txt`
2. `python typora.py --help`
3. 阅读帮助文档及使用。
4. 做你想做的事。
5. 打包并替换原目录下的 app.asar。
6. 享受成果。
## 示例
```shell
> python typora.py --help
usage: typora.py [-h] [-u] [-f] asarPath dirPath
[extract and decryption / pack and encryption] app.asar file from [Typora].
positional arguments:
asarPath app.asar file path/dir [input/ouput]
dirPath as tmp and out directory.
optional arguments:
-h, --help show this help message and exit
-u pack & encryption (default: extract & decryption)
-f enabled prettify/compress (default: disabled)
If you have any questions, please contact [ MasonShi@88.com ]
> python typora.py {installRoot}/Typora/resources/app.asar workstation/outfile/
# (patch code by yourself in workstation/outfile/dec_app)
> python typora.py -u workstation/outfile/dec_app workstation/outappasar
> cp {installRoot}/Typora/resources/app.asar {installRoot}/Typora/resources/app.asar.bak
> mv workstation/outappasar/app.asar {installRoot}/Typora/resources/app.asar
> node example/keygen.js
XXXXXX-XXXXXX-XXXXXX-XXXXXX
> typora
# (input info)
email: crack@example.com
serial: XXXXXX-XXXXXX-XXXXXX-XXXXXX
```
## LICENSE
MIT LICENSE

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

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 = {AES_KEY}
AES_IV = {AES_IV}
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()