added crack
This commit is contained in:
@@ -0,0 +1,229 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
This is a reupload of a script from Nico-Posada. The script was not made by me.
|
||||||
|
|
||||||
|
-- Mega Hack v9 Crack Script --
|
||||||
|
|
||||||
|
Prerequisites: Have Geode installed. If you don't, figure out how to install it.
|
||||||
|
|
||||||
|
1. Copy this file and place it somewhere on your desktop, doesn't matter where.
|
||||||
|
2. Go to your Microsoft Store app and install "Python" (Many versions may appear, just pick the first one e.g. Python 3.13).
|
||||||
|
3. Press Win + R, then type in "cmd" into the pop-up, a terminal should appear.
|
||||||
|
4. In the terminal, type "python " (with the space after it), and then drag the file containing this script into the terminal.
|
||||||
|
5. You should now see something *like* "python C:\path\to\crack.py" in your terminal, press Enter.
|
||||||
|
6. Let the script run, next steps will be in the output depending on what happens.
|
||||||
|
|
||||||
|
Tested for the following MH versions: v9.0.3, v9.0.7, v9.0.9
|
||||||
|
"""
|
||||||
|
|
||||||
|
import platform
|
||||||
|
|
||||||
|
err = lambda msg: print(f"[ERROR] {msg}") or exit(1)
|
||||||
|
warn = lambda msg: print(f"[WARNING] {msg}")
|
||||||
|
|
||||||
|
if platform.system().lower() != 'windows':
|
||||||
|
err(f"This crack is meant for windows versions of Mega Hack. {platform.system()} is not supported.")
|
||||||
|
|
||||||
|
# MegaHack uses SHGetKnownFolderPath to find the local appdata directory. If it fails we can fall back to using the environment variable.
|
||||||
|
import ctypes
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
|
||||||
|
FOLDERID_LocalAppData = uuid.UUID("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}").bytes_le
|
||||||
|
appdata_dir_buf = ctypes.c_wchar_p()
|
||||||
|
|
||||||
|
if ctypes.windll.shell32.SHGetKnownFolderPath(
|
||||||
|
ctypes.byref(ctypes.create_string_buffer(FOLDERID_LocalAppData, 16)),
|
||||||
|
0, 0,
|
||||||
|
ctypes.byref(appdata_dir_buf)
|
||||||
|
):
|
||||||
|
warn("Failed to find the local appdata directory using SHGetKnownFolderPath. Trying %LOCALAPPDATA%.")
|
||||||
|
LOCALAPPDATA = os.getenv("LOCALAPPDATA", None)
|
||||||
|
if not LOCALAPPDATA:
|
||||||
|
err("Unable to find the local AppData directory with SHGetKnownFolderPath or %LOCALAPPDATA%. Aborting.")
|
||||||
|
else:
|
||||||
|
LOCALAPPDATA = appdata_dir_buf.value
|
||||||
|
|
||||||
|
print(f"Found the local appdata directory at '{LOCALAPPDATA!s}'")
|
||||||
|
|
||||||
|
# the rest of the shit we need
|
||||||
|
from urllib.request import urlopen
|
||||||
|
from urllib.error import HTTPError, URLError
|
||||||
|
import zipfile
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
import functools
|
||||||
|
import shutil
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
# convenience
|
||||||
|
CWD = os.path.dirname(__file__)
|
||||||
|
os.chdir(CWD)
|
||||||
|
print = functools.partial(print, flush=True)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def progress_log(msg: str):
|
||||||
|
print(msg, end="... ")
|
||||||
|
success = False
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
success = True
|
||||||
|
finally:
|
||||||
|
print("Done!" if success else "Failed.")
|
||||||
|
|
||||||
|
# Part 1: Downloading current version
|
||||||
|
|
||||||
|
# json containing all megahack version and information
|
||||||
|
INSTALL_JSON_URL = "https://absolllute.com/api/mega_hack/v9/install.json"
|
||||||
|
|
||||||
|
r = urlopen(INSTALL_JSON_URL)
|
||||||
|
if r.status != 200:
|
||||||
|
err(f"Unable to get installation json. Status Code: {r.status}")
|
||||||
|
|
||||||
|
cur_package = json.load(r)["packages"][0]
|
||||||
|
if cur_package["name"] != "Mega Hack v9":
|
||||||
|
print(f"[WARNING] This was tested for Mega Hack v9, most recent version seems to now be {cur_package['name']}")
|
||||||
|
|
||||||
|
cur_bundle = cur_package["bundles"][0]
|
||||||
|
|
||||||
|
group = cur_bundle["group"]
|
||||||
|
filename = cur_bundle["file"]
|
||||||
|
|
||||||
|
MEGAHACK_URL = "https://absolllute.com/api/mega_hack/v9/files/{}/{}".format(group, filename)
|
||||||
|
|
||||||
|
with progress_log(f"Downloading {cur_bundle['name']}"):
|
||||||
|
try:
|
||||||
|
with urlopen(MEGAHACK_URL) as r:
|
||||||
|
megahack_zip = r.read()
|
||||||
|
except HTTPError as e:
|
||||||
|
err(f"HTTP error: {e.code}")
|
||||||
|
except URLError as e:
|
||||||
|
err(f"URL error: {e.reason}")
|
||||||
|
|
||||||
|
# Part 2: Extracting the geode
|
||||||
|
|
||||||
|
with progress_log("Extracting geode file and patching"):
|
||||||
|
OUT_FILENAME = "absolllute.megahack.cracked.geode"
|
||||||
|
with zipfile.ZipFile(io.BytesIO(megahack_zip), 'r') as zip_in, \
|
||||||
|
zipfile.ZipFile(OUT_FILENAME, 'w') as zip_out:
|
||||||
|
|
||||||
|
# Part 3: Setting up patching shit
|
||||||
|
# Patterns tested on v9.0.3, v9.0.7, and v9.0.9
|
||||||
|
|
||||||
|
# 56 57 48 83 EC ? 48 83 79 10 40
|
||||||
|
# patch id verification
|
||||||
|
ID_CHECK_PAT = re.compile(rb'\x56\x57\x48\x83\xEC.\x48\x83\x79\x10\x40', re.DOTALL | re.MULTILINE)
|
||||||
|
# 55 41 56 56 57 53 48 83 EC ? 48 8D 6C 24 ? 48 C7 45 ? ? ? ? ? ? ? ? ? 0F 84 ? ? ? ? 4C 89 C7
|
||||||
|
# patch json signature verification
|
||||||
|
JSON_SIGNATURE_CHECK_PAT = re.compile(rb'\x55\x41\x56\x56\x57\x53\x48\x83\xEC.\x48\x8D\x6C\x24.\x48\xC7\x45.........\x0F\x84....\x4C\x89\xC7', re.DOTALL | re.MULTILINE)
|
||||||
|
# 31 C9 41 B8 ? ? ? ? E8 ? ? ? ? 48 83 7F
|
||||||
|
# patch out checksum result of hardware manufacturer to make the key 0x00000000 no matter what
|
||||||
|
KEY_BYBASS_PAT = re.compile(rb'(?<=\x31\xC9\x41\xB8....)\xE8....(?=\x48\x83\x7F)', re.DOTALL | re.MULTILINE)
|
||||||
|
|
||||||
|
# the first two functions we need to patch involve forcing it to return 1 lol
|
||||||
|
PATCH_DATA1 = b"".join([
|
||||||
|
b"\xb8\x01\x00\x00\x00", # mov eax, 1
|
||||||
|
b"\xc3", # ret
|
||||||
|
])
|
||||||
|
|
||||||
|
# overwriting the function call to instead just set rax to 0
|
||||||
|
PATCH_DATA2 = b"".join([
|
||||||
|
b"\xb8\x00\x00\x00\x00", # mov eax, 0
|
||||||
|
])
|
||||||
|
|
||||||
|
# Part 4: Patching shit
|
||||||
|
for item in zip_in.infolist():
|
||||||
|
filename = item.filename
|
||||||
|
data = zip_in.read(filename)
|
||||||
|
|
||||||
|
match filename:
|
||||||
|
case "absolllute.megahack.dll":
|
||||||
|
if data == (data := ID_CHECK_PAT.sub(lambda m: PATCH_DATA1 + m.group(0)[len(PATCH_DATA1):], data, 1)):
|
||||||
|
err("Failed to find pattern for the id check!")
|
||||||
|
if data == (data := JSON_SIGNATURE_CHECK_PAT.sub(lambda m: PATCH_DATA1 + m.group(0)[len(PATCH_DATA1):], data, 1)):
|
||||||
|
err("Failed to find pattern for the json signature check!")
|
||||||
|
if data == (data := KEY_BYBASS_PAT.sub(PATCH_DATA2, data, 1)):
|
||||||
|
err("Failed to find pattern for the key bypass!")
|
||||||
|
|
||||||
|
# need to update the filename too
|
||||||
|
item.filename = "absolllute.megahack.cracked.dll"
|
||||||
|
case "mod.json":
|
||||||
|
# we need to modify the id to match the output filename, all the other changes are cosmetic
|
||||||
|
mod = json.loads(data)
|
||||||
|
mod["id"] = "absolllute.megahack.cracked"
|
||||||
|
mod["name"] = "Mega Hack Cracked"
|
||||||
|
mod["description"] = "ts pmo"
|
||||||
|
data = json.dumps(mod, indent="\t").encode()
|
||||||
|
|
||||||
|
zip_out.writestr(item, data)
|
||||||
|
|
||||||
|
# Part 5: Writing a now valid license to the expected directory
|
||||||
|
with progress_log("Creating fake license file"):
|
||||||
|
mh_local_dir = os.path.join(LOCALAPPDATA, "absolllute.megahack")
|
||||||
|
os.makedirs(mh_local_dir, exist_ok=True)
|
||||||
|
mh_license_path = os.path.join(mh_local_dir, "license")
|
||||||
|
mh_license_fallback_path = os.path.join(CWD, "license")
|
||||||
|
|
||||||
|
EXPECTED_CHACHA_KEY = bytes.fromhex("0E 84 1F A5 BF E5 CE 8F C9 1E B1 1A DD 1D CE F6 94 04 5B EE AF CF 52 1B F4 34 1D 39 97 C1 C2 19")
|
||||||
|
|
||||||
|
def random_hex(length):
|
||||||
|
assert length % 2 == 0
|
||||||
|
return os.urandom(length // 2).hex().upper()
|
||||||
|
|
||||||
|
signature = os.urandom(256) # we bypass this check, so just set it to whatever
|
||||||
|
identifier = random_hex(64) # this is possible to generate legitamately, but it really bloats the code bc it involves a lot of winapi stuff
|
||||||
|
token = random_hex(32) # honestly, I have no idea what this is for, it's not used anywhere from what I can tell
|
||||||
|
secret = random_hex(32) # same with this, couldn't find anywhere that reads this
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"id": identifier,
|
||||||
|
"token": token,
|
||||||
|
"secret": secret,
|
||||||
|
"timestamp": str(int(time.time())),
|
||||||
|
# this is used to decrypt some resources or something else important (didn't really dig into it) but it's requried to be this
|
||||||
|
"guid2": EXPECTED_CHACHA_KEY.hex().upper()
|
||||||
|
}
|
||||||
|
|
||||||
|
data_dump = json.dumps(data, separators=(",", ":"))
|
||||||
|
|
||||||
|
license = {
|
||||||
|
"data": base64.b64encode(data_dump.encode()).decode('utf-8'),
|
||||||
|
"sig": base64.b64encode(signature).decode('utf-8'),
|
||||||
|
"token": token
|
||||||
|
}
|
||||||
|
|
||||||
|
license_str = json.dumps(license, separators=(",", ":"))
|
||||||
|
with open(mh_license_path, "w") as f:
|
||||||
|
f.write(license_str)
|
||||||
|
with open(mh_license_fallback_path, "w") as f:
|
||||||
|
f.write(license_str)
|
||||||
|
|
||||||
|
assert os.path.exists(mh_license_path) or os.path.exists(mh_license_fallback_path)
|
||||||
|
|
||||||
|
# Yay all done, past this point is just printing stuff to make it nice and pretty
|
||||||
|
|
||||||
|
def get_terminal_width():
|
||||||
|
try:
|
||||||
|
size = shutil.get_terminal_size()
|
||||||
|
return size.columns
|
||||||
|
except OSError:
|
||||||
|
# Fallback if the terminal size cannot be determined
|
||||||
|
return 80
|
||||||
|
|
||||||
|
output_file = os.path.join(CWD, OUT_FILENAME)
|
||||||
|
if not os.path.exists(output_file):
|
||||||
|
err("The patching process went well, but the patched geode was never created.")
|
||||||
|
|
||||||
|
BORDER = '#' * get_terminal_width()
|
||||||
|
print(f"""
|
||||||
|
{BORDER}
|
||||||
|
Cracking process finished!
|
||||||
|
* The license file was created in {mh_local_dir} and {CWD}.
|
||||||
|
* If you don't see the license file in {mh_local_dir}, copy the one in {CWD} to there.
|
||||||
|
* The cracked geode can be found at {output_file}
|
||||||
|
* After installing Geode, you can manually import this cracked geode to use Mega Hack
|
||||||
|
{BORDER}\
|
||||||
|
""")
|
||||||
Reference in New Issue
Block a user