Skip to content

Instantly share code, notes, and snippets.

@dlukt
Created August 20, 2026 14:44
Show Gist options
  • Select an option

  • Save dlukt/764f5568847c56a9f13047ec12de2c12 to your computer and use it in GitHub Desktop.

Select an option

Save dlukt/764f5568847c56a9f13047ec12de2c12 to your computer and use it in GitHub Desktop.
GRAV (Steam app 332500) expiry kill-switch patcher — fixes the 'App Expired' launch error by pinning the engine's calendar year to 2024. Pure Python 3, verifies the build, backs up, verifies result, supports --status/--undo.
#!/usr/bin/env python3
"""GRAV (Steam app 332500) — expiry kill-switch patcher.
GRAV's final build (buildid 1763174, Jan 2016) contains a hard-coded
expiry date check: after ~9 years it shows "App Expired - This app has
expired. Please get the latest update from steam." and quits, even for
valid owners. The check reads the calendar year through four engine
wrapper functions that all extract it with the same 6-byte instruction:
44 0f b7 5c 24 20 movzx r11d, WORD PTR [rsp+0x20] ; wYear
This script replaces each occurrence, same length, with:
41 bb e8 07 00 00 mov r11d, 2024
so the engine reports year 2024 forever and the kill-switch never
trips. All other timekeeping (durations, timers, file times) is real.
Usage:
patch_grav.py ensure patched (backs up first run)
patch_grav.py --status just report the current state
patch_grav.py --undo restore the original from the backup
patch_grav.py <path> operate on a different copy of the exe
"""
import os
import re
import shutil
import stat
import sys
from pathlib import Path
if sys.platform.startswith("win"):
DEFAULT_EXE = Path(os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")) / (
"Steam/steamapps/common/GRAV/Binaries/Win64/CAGGame-Win64-Shipping.exe"
)
else:
DEFAULT_EXE = Path.home() / (
".local/share/Steam/steamapps/common/GRAV/Binaries/Win64/"
"CAGGame-Win64-Shipping.exe"
)
PATCH_FROM = bytes.fromhex("440fb75c2420") # movzx r11d, WORD [rsp+0x20]
PATCH_TO = bytes.fromhex("41bbe8070000") # mov r11d, 2024
EXPECTED_SITES = 4
BACKUP_SUFFIX = ".orig.bak"
def find(data: bytes, pat: bytes):
return [m.start() for m in re.finditer(re.escape(pat), data)]
def main() -> int:
args = [a for a in sys.argv[1:]]
undo = "--undo" in args
status_only = "--status" in args
paths = [Path(a) for a in args if not a.startswith("--")]
exe = paths[0] if paths else DEFAULT_EXE
backup = exe.with_name(exe.name + BACKUP_SUFFIX)
if not exe.is_file():
print(f"ERROR: game executable not found: {exe}")
print("Pass the path explicitly if GRAV is installed elsewhere.")
return 1
data = exe.read_bytes()
unpatched = find(data, PATCH_FROM)
patched = find(data, PATCH_TO)
already = len(patched) >= EXPECTED_SITES and not unpatched
print(f"Executable : {exe}")
print(f"Size : {len(data):,} bytes")
print(f"Unpatched sites: {len(unpatched)} patched sites: {len(patched)}")
if undo:
if not backup.is_file():
print("ERROR : no backup found, cannot undo.")
return 1
shutil.copy2(backup, exe)
print("STATUS : original executable restored from backup.")
return 0
if already:
print("STATUS : already patched (kill-switch disabled). Nothing to do.")
return 0
if status_only:
print("STATUS : not patched.")
return 0
if len(unpatched) != EXPECTED_SITES:
print(
f"ERROR : expected exactly {EXPECTED_SITES} patch sites, "
f"found {len(unpatched)}. This is probably a different build — "
"aborting without changes."
)
return 1
if not backup.is_file():
shutil.copy2(exe, backup)
print(f"Backup : saved {backup.name}")
else:
print(f"Backup : {backup.name} already exists, left untouched.")
buf = bytearray(data)
for off in unpatched:
buf[off : off + len(PATCH_TO)] = PATCH_TO
tmp = exe.with_name(exe.name + ".patching.tmp")
tmp.write_bytes(buf)
os.chmod(tmp, stat.S_IMODE(exe.stat().st_mode))
tmp.replace(exe)
verify = exe.read_bytes()
if len(find(verify, PATCH_TO)) != len(unpatched) or find(verify, PATCH_FROM):
print("ERROR : verification failed! Restore from the backup.")
return 1
print(f"STATUS : patched {len(unpatched)} sites. Kill-switch disabled.")
print("Remember : Steam 'Verify integrity of game files' will undo this;")
print(" re-run this script afterwards to re-apply.")
return 0
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment