Created
June 13, 2023 07:20
-
-
Save jesux/d9b3c66e82a0086b6c16acede80faceb to your computer and use it in GitHub Desktop.
Hollow Knight PermaDeath changer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# Requires pycryptodome | |
import sys, os | |
if sys.version_info.major<3: | |
sys.stderr.write("Python3 required\n") | |
sys.exit(0) | |
import json, base64 | |
from Crypto.Cipher import AES | |
from datetime import datetime | |
permadeathModes = {0:'Normal', 1:'Steel Soul', 2:'Steel Soul Broken π'} | |
header = bytes.fromhex('0001000000ffffffff01000000000000000601000000') | |
header_end = bytes([11]) | |
key = b'UKu52ePUBwetZ9wNX88o54dnfKRu0T1l' | |
cipher = AES.new(key, AES.MODE_ECB) | |
modified = False | |
# https://msdn.microsoft.com/en-us/library/cc236844.aspx | |
def header_len(length): | |
length = min(0x7fffffff, length); # maximum value | |
x = [] | |
for i in range(4): | |
if length >> 7 == 0: | |
x.append(0x7f & length) | |
length = length >> 7 | |
break | |
else: | |
x.append(0x7f & length | 0x80) | |
length = length >> 7 | |
if length != 0: | |
values.x(length) | |
return bytes(x) | |
def decode(data): | |
# Remove header | |
data = data[len(header): len(data) - 1] | |
for i in range(5): | |
if ((data[i] & 0x80) == 0): | |
break | |
data = data[i+1:] | |
data = base64.b64decode(data) | |
data = cipher.decrypt(data) | |
data = data[:-data[-1]] # Remove AES padding | |
return data | |
def encode(data): | |
# Add AES padding | |
length = 16 - (len(data) % 16) | |
data += bytes([length]) * length | |
data = cipher.encrypt(data) | |
data = base64.b64encode(data) | |
# Add header | |
data = header + header_len(len(data)) + data + header_end | |
return data | |
# OPEN FILE | |
try: | |
infile = open(sys.argv[1], mode="rb") | |
data = infile.read() | |
infile.close() | |
except: | |
print("[-] Error opening file") | |
exit() | |
# DECODE | |
data_decoded = decode(data) | |
y = json.loads(data_decoded) | |
# PRINT DATA | |
print('[VERSION] {0}'.format(y['playerData']['version'])) | |
m,s = divmod(y['playerData']['playTime'], 60) | |
h,m = divmod(m, 60) | |
print('[playTime] {0:.0f} hours {1:.0f} minutes'.format(h,m)) | |
print('[permadeathMode] {0}'.format(permadeathModes[y['playerData']['permadeathMode']])) | |
print('[π°] {0}'.format(y['playerData']['geo'])) | |
print('[nailDamage] {0}'.format(y['playerData']['nailDamage'])) | |
print('[completionPercentage] {0}%'.format(y['playerData']['completionPercentage'])) | |
# Remove PermaDeath | |
if y['playerData']['permadeathMode'] == 2: | |
print("ππ Reparing Steel Soul ππ") | |
y['playerData']['permadeathMode'] = 1 | |
modified = True | |
if modified: | |
data_encoded = encode(bytes(json.dumps(y), 'utf-8')) | |
print('[+] Saving data backup') | |
bakfile = open(sys.argv[1] + '_' + datetime.now().strftime('%Y%m%d%H%M%S') + '.bak', mode="wb") | |
bakfile.write(data) | |
infile.close() | |
print('[+] Saving modified data') | |
outfile = open(sys.argv[1], mode="wb") | |
#outfile = open('output.txt', mode="wb") | |
outfile.write(data_encoded) | |
outfile.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment