Skip to content

Instantly share code, notes, and snippets.

@maboloshi
Last active April 30, 2024 17:41
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maboloshi/5baecbddacf43855f13240b63be5673d to your computer and use it in GitHub Desktop.
Save maboloshi/5baecbddacf43855f13240b63be5673d to your computer and use it in GitHub Desktop.
Sublime Text & Sublime Merge破解
#!/usr/bin/python3
# -*- coding: utf-8 -*
import re
import os
import shutil
import sys
import hashlib
import socks
import socket
from tqdm import tqdm
from urllib import request
from urllib.error import HTTPError
import tarfile
import zipfile
import ssl # Fix ssl error
ssl._create_default_https_context = ssl._create_unverified_context # Fix ssl error
def quit():
sys.exit()
def get_url_extract_file(app_type, version):
base_url = "https://download.sublimetext.com/"
url_win = f"{base_url}{app_type}_build_{version}_x64.zip"
url_linux = f"{base_url}{app_type}_build_{version}_x64.tar.xz"
url_osx = f"{base_url}{app_type}_build_{version}_mac.zip"
extract_file_win = f"{app_type}.exe"
extract_file_linux = f"{app_type}/{app_type}"
extract_file_osx = (
f"{'Sublime Text' if app_type == 'sublime_text' else 'Sublime Merge'}.app/Contents/MacOS/{app_type}"
)
return (url_win, extract_file_win), (url_linux, extract_file_linux), (url_osx, extract_file_osx)
def download_file(url, filename=None, progress_bar=True, proxy=False, force=False):
# 呈现下载进度条
def my_hook(t):
last_b = [0]
def inner(b=1, bsize=1, tsize=None):
if tsize is not None:
t.total = tsize
if b > 0:
t.update((b - last_b[0]) * bsize)
last_b[0] = b
return inner
# 如果proxy为True,设置SOCKS5代理
if proxy:
socks.set_default_proxy(socks.SOCKS5, addr="127.0.0.1", port=7890) # 设置socks代理
socket.socket = socks.socksocket # 把代理应用到socket
# 如果没有指定文件名,使用URL的最后一部分作为文件名
file_basename = url.split('/')[-1]
filename = filename if filename else file_basename
# force 表示是否强制下载
print(f"[*] Download: {file_basename}, from: {url} ...") if log else None
if os.path.exists(filename):
print(f"[*] The {filename} already exists.") if log else None
if force or not os.path.exists(filename):
try:
if progress_bar:
with tqdm(unit='B', unit_scale=True, miniters=1, desc=">>>") as t:
tmp_download_filename, _ = request.urlretrieve(url, reporthook=my_hook(t))
# pass
else:
tmp_download_filename, _ = request.urlretrieve(url)
except (KeyboardInterrupt, SystemExit):
quit()
except HTTPError:
print("[!] HTTP Error 404: Not Found.\n" +
" Please check if the application type and the version are correct.")
quit()
except PermissionError:
print(f"[!] Permission denied: {tmp_download_filename}.")
quit()
else:
# 下载完成后,将文件移动到指定的位置
try:
shutil.move(tmp_download_filename, filename)
except PermissionError:
print(f"[!] Permission denied: {filename}.")
quit()
def extract(filename, extract_file, target_dir="."):
def file_exists(*filepath):
full_path = os.path.join(*filepath)
return os.path.exists(full_path)
if file_exists(target_dir, extract_file):
print(f"[*] The {extract_file} already exists.\n")
else:
print(f"[*] Extract: {extract_file}, from: {filename} ...\n")
if not file_exists(target_dir):
os.makedirs(target_dir.upper())
try:
if filename.endswith("zip"):
with zipfile.ZipFile(filename) as zip:
zip.extract(extract_file, path=target_dir)
elif filename.endswith("xz"):
with tarfile.open(filename, 'r:xz') as tar:
tar.extract(extract_file, path=target_dir)
except Exception as e:
print(f"An error occurred while extracting the file: {e}")
class File():
def __init__(self, filename, app_type=None):
self.filename = filename
try:
with open(self.filename, 'rb') as file:
self.binary = file.read()
except (FileNotFoundError):
print(f"[!] {self.filename} file not found")
quit()
except (IsADirectoryError, PermissionError):
print(f"[!] {self.filename} is not a valid file")
quit()
self.patternes = self.check_pattern(app_type)
self.backup()
def md5sum(self, filename, blocksize=65536):
hash_md5 = hashlib.md5()
with open(filename, "rb") as f:
for block in iter(lambda: f.read(blocksize), b""):
hash_md5.update(block)
return hash_md5.hexdigest().upper()
def check_pattern(self, app_type=None):
def file_detection(filename=self.filename, binary=self.binary, app_type=None):
def detect_executable_file_format(filename=self.filename, binary=self.binary):
flag1 = binary[0:2]
offset = ord(binary[0x3c:0x3c+1])
flag2 = binary[offset:offset + 6]
if flag1 == b'MZ' and flag2 == b'PE\x00\x00\x64\x86': # 判断是否为PE文件的典型特征签名, \x64\x86 -> AMD64
return "Win64"
elif flag1 == b'MZ' and flag2 == b'PE\x00\x00\x4C\x01': # \x4C\x01 -> Intel x86
return "Win32"
elif binary[0:4] == b'\x7fELF' and binary[4] != 1:
return "Linux"
elif binary[0:4] == b'\xCA\xFE\xBA\xBE' and binary[8:12] == b'\x01\x00\x00\x07':
return "macOS"
else:
print(
"[!] Failed to recognize a binary file.\n " +
"Are you sure this is a 64-bit executable binary file for Sublime Text or Sublime Merge?'"
) if log else None
quit()
# return False
def detect_app_type(binary=self.binary, app_type=None):
# print("[>] Attempting to autodetect application type ...") if log else None
if app_type == "sublime_text":
return "Sublime Text"
elif app_type == "sublime_merge":
return "Sublime Merge"
sm_check_pattern = b'That appears to be a Sublime Text'
st_check_pattern = b'That appears to be a Sublime Merge'
match_st = re.search(st_check_pattern, binary, re.DOTALL)
match_sm = re.search(sm_check_pattern, binary, re.DOTALL)
# Only one of these patterns can exist in the application.
# If neither is found, it's not a known application.
if match_st == match_sm is None:
print(
"[!] Failed to identify binary. Are you sure this is a Sublime Text or Sublime Merge binary?"
) if log else None
quit()
elif match_st and match_sm:
print(
"[!] Both identity signatures detected in the binary! Cannot detect what application this is."
) if log else None
quit()
elif match_st is None and match_sm:
return "Sublime Merge"
else:
return "Sublime Text"
def get_update_channel_and_version(binary=self.binary):
# print("[>] Attempt to auto detect update channel type and release version ...") if log else None
stable_check_pattern = b'stable_update_check\\?version='
match_stable_channel = re.search(stable_check_pattern, binary, re.DOTALL)
dev_check_pattern = b'dev_update_check\\?version='
match_dev_channel = re.search(dev_check_pattern, binary, re.DOTALL)
def get_version(binary, offset):
return binary[offset:offset+4].decode('ascii')
if match_stable_channel:
return ("Stable Channel", get_version(binary, match_stable_channel.end()))
elif match_dev_channel:
return ("Dev Channel", get_version(binary, match_dev_channel.end()))
print("[>] Attempting to autodetect input file ...") if log else None
exe_file_format = detect_executable_file_format()
app_type = detect_app_type()
update_channel, version = get_update_channel_and_version()
print(
f"[*] Input file -> {app_type}, {exe_file_format}, {update_channel}, Build {version}\n"
f" MD5 Checksum -> {self.md5sum(self.filename)}\n"
) if log else None
return (exe_file_format, app_type)
exe_file_format, app_type = file_detection(app_type)
if app_type == "Sublime Text":
if exe_file_format == "Win64":
return st_win_patternes
# elif exe_file_format == "Win32":
# return st_win_32_patternes
elif exe_file_format == "Linux":
return st_linux_patternes
elif exe_file_format == "macOS":
return st_osx_patternes
else:
if exe_file_format == "Win64":
return sm_win_patternes
# elif exe_file_format == "Win32":
# return sm_win_32_patternes
elif exe_file_format == "Linux":
return sm_linux_patternes
elif exe_file_format == "macOS":
return sm_osx_patternes
def backup(self):
if PATCH:
original = self.filename
target = self.filename + ".bak"
if not os.path.exists(target):
# os.rename(os.path.realpath(filename), os.path.realpath(filename)+".bak")
shutil.copy2(original, target)
print(f"[>] Backup \"{os.path.basename(original)}\" successfully.\n") if log else None
def patched(self):
if PATCH:
print(f"[<] MD5 checksum of the patched file -> {self.md5sum(self.filename)}")
print("[<] Successfully patched the application!")
class Pattern(object):
def __init__(self, filename, binary, name, regex, displacement, patch_code, rva=False): # binary,
self.filename = filename # 输入文件路径
self.binary = binary
self.name = name # 模式的名称
self.regex = regex # 模式的正则表达式 如: br'\xE8....\x3D....\x75\x14'
self.displacement = displacement # 模式的偏量值
self.patch_code = patch_code # 模式的二进制补丁
self.rva = rva # RVA 标记符
if self.set_offset():
self.patch()
def set_offset(self):
# Converts 4 byte chunk of a bytearray() into a 32-bit Little-Endian encoded integer.
def LE32(value):
return value[3] << 24 | value[2] << 16 | value[1] << 8 | value[0]
# Calculates an absolute offset from a relative virtual address using a base offset and instruction length.
def RVA2ABS(base, rva, instruction_len):
return ((base + instruction_len) + rva) & 0xFFFFFFFF
match_object = re.search(self.regex, self.binary, re.DOTALL)
if match_object:
self.offset = match_object.start()
else:
print(f"[!] No pattern found for \"{self.name}\" ...\n") if log else None
return False
self.offset += self.displacement # 添加 模式所需位移
# Calculate the absolute offset from an RVA instruction if required.
if self.rva:
# The RVA value is stored after the first byte relative to the found offset as a 4-byte LE integer.
rva = LE32(self.binary[self.offset + 1: self.offset + 5]) # Reverse byte order due to CPU encoding
offset_abs = RVA2ABS(self.offset, rva, 5) # Get true offset from referenced pointer
print(
f"[>] Found RVA pattern for \"{self.name}\" at 0x{self.offset:X} -> 0x{rva:X} -> 0x{offset_abs:X} ..."
) if log else None
self.offset = offset_abs
else:
# Not an RVA so no additional calculations required
print(f"[>] Found pattern for \"{self.name}\" at 0x{self.offset:X} ...") if log else None
return True
def patch(self):
def B2A_HEX(data):
lin = ['%02X' % i for i in data]
return " ".join(lin)
original_code = self.binary[self.offset:self.offset + len(self.patch_code)]
print(f"[*] Rewrite data \'{B2A_HEX(original_code)}\' -> \'{B2A_HEX(self.patch_code)}\' ...") if log else None
if PATCH:
try:
with open(self.filename, 'r+b') as file:
file.seek(self.offset)
file.write(self.patch_code)
except IOError:
print(f"[!] Error writing to {self.filename}.") if log else None
else:
print(f"[=] Patched \"{self.name}\" successfully.") if log else None
print("\n")
st_osx_patternes = (
# ("isValidLicense" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\xC7\xC0\x00\x00\x00\x00\xC3' , 1), # noqa: E203,E501
# ("invalidationFunction", br'\xE8....\x48\x89\x9D....\x48\x8B\xB3' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("validationFunction" , br'\xE8....\x48\x8D\x3D....\xE8....\x83\x25' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("invalidationFunction for 4139 ", br'\xE8..(\x12|\x13)\x00\x48\x8B\xB3\x88\x02\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction for 4139" , br'\xE8..(\x12|\x13)\x00\x48\x89\xDF\xE8...\x00\xBF' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("invalidationFunction for 4150 ", br'\xE8..\x13\x00\x48\x8B\xB3\x88\x02\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("validationFunction for 4150" , br'\xE8..\x13\x00\x48\x89\xDF\xE8...\x00\xBF' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
#("serverThread" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x53\x50\x41\x89\xF6\x49\x89\xFF\x6A\x20' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 0), # noqa: E203,E501
("serverThread" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x53\x50\x41\x89\xF6\x49\x89\xFF\x6A\x20' , 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x55\x48\x89\xE5\x53\x48\x81\xEC....\x48\x89\xFB\x48\x8B\x05....\x48\x8B\x00\x48\x89\x45\xF0\x48\x8D\x3D....', 0, b'\xC3' , 0), # noqa: E203,E501
#("crashReporter" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xCE\x49\x89\xF7' , 0, b'\xC3' , 0), # noqa: E203,E501
("isValidLicense_arm64 for 4114", br'\x08\x00\x80\x52...\x14\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA' , 8, b'\xE0\x03\x1F\xAA\xC0\x03\x5F\xD6', 0), # noqa: E203,E501
# ("isValidLicense_arm64 for 4150", br'\x08\x00\x80\x52...\x14....\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA' , 8, b'\xE0\x03\x1F\xAA\xC0\x03\x5F\xD6', 0), # noqa: E203,E501
("isValidLicense_arm64 for 4150", br'\x08\x00\x80\x52...\x14....\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA' , 8, b'\x00\x00\x80\xD2\xC0\x03\x5F\xD6', 0), # noqa: E203,E501
("invalidationFunction_arm64" , br'...\x94..\x41\xF9..\x00\x10\x1F\x20\x03\xD5\x02\x53\x87\x52' , 0, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
# ("validationFunction_arm64" , br'\x02..\x52...\x94..\x00....\x91...\x91\xE0.\x00\xF9' , 4, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
# 等效 ("validationFunction_arm64" , br'...\x94..\x00.\xF7..\x91\xE0..\x91\xE0\x6F\x00\xF9' , 0, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
("validationFunction_arm64 for 4139", br'...\x94\xE0\x03.\xAA...\x94\x00\x45\x80\x52' , 0, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
("serverThread_arm64" , br'\xF6\x57\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91...\x94...\x94\xF3\x03\x00\xAA...\x94\x74\x1A\x00\xB9', 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
("licenseNotifyThread_arm64" , br'\xFC\x6F\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91\xFF\x43\x0C\xD1\xF3\x03\x00\xAA' , 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
#("crashReporte_arm64" , br'\xFC\x6F\xBC\xA9\xF6\x57\x01\xA9\xF4\x4F\x02\xA9\xFD\x7B\x03\xA9\xFD\xC3\x00\x91\xFF\x03\x0F\xD1' , 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
)
st_linux_patternes = (
#("isValidLicense" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\xC7\xC0\x00\x00\x00\x00\xC3' , 1), # noqa: E203,E501
# ("invalidationFunction", br'\xE8....\x48\x89\x5C\x24.\x48\x8B\xB3' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("invalidationFunction for 4121", br'\xBA....\xE8....\x49\x8B\xB7' , 5, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("invalidationFunction for 4139", br'\xBA....\xE8....\x48\x8B\xB5' , 5, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("invalidationFunction for 4139", br'\xE8..\x12\x00.\x8B.\xD8\x02\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("invalidationFunction for 4153", br'\xE8..\x14\x00.\x8B.\x88\x02\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("validationFunction" , br'\xE8....\xBF....\xE8....\x83\x25' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction for 4139" , br'\xE8..\x12\x00.\x89.\xE8..\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction for 4153" , br'\xE8..\x14\x00.\x89.\xE8..\x00\x00' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("serverThread" , br'\x55\x41\x56\x53\x41\x89\xF6\x48\x89\xFD\x6A\x28' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3', 0), # noqa: E203,E501
#("serverThread for 4153", br'\x55\x41\x56\x53\x41\x89\xF6\x48\x89\xFD\x6A\x20' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3', 0), # noqa: E203,E501
("serverThread for 4153", br'\x55\x41\x56\x53\x41\x89\xF6\x48\x89\xFD\x6A\x20' , 0, b'\xC3', 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x41\x56\x53\x48\x81\xEC....\x48\x89\xFB\xBF....\xE8....\x4C\x8D\xB4\x24....' , 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread for 4146" , br'\x41\x56\x53\x48\x81\xEC....\x48\x89\xFB\x48\x8D\x3D....\xE8....\x48\x8D\x15....' , 0, b'\xC3' , 0), # noqa: E203,E501
# ("crashReporter" , br'\x55\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xD4\x48\x89\xFD' , 0, b'\xC3' , 0), # noqa: E203,E501
)
st_win_patternes = (
#("isValidLicense for 4117", br'\x45\x31\xC9\xE8....\x85\xC0\x75\x15' , 3, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501 for 4117
("isValidLicense for 4117", br'\x45\x31\xC9\xE8....\x85\xC0\x75\x15' , 3, b'\x48\xC7\xC0\x00\x00\x00\x00\xC3' , 1), # noqa: E203,E501 for 4117
# ("isValidLicense" , br'\xE8....\x48\x8B\x8B....\x85\xC0' , 0, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501
("invalidationFunction" , br'\x41\xB8....\xE8....(\x49|\x48)\x8B\x96' , 6, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("validationFunction" , br'\xE8....\xE8....(\x4C|\x48)\x89\xF1\xE8' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction for 4139", br'\xE8.....\x89\xF1\xE8..\x00\x00\xB9' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
# ("serverThread" , br'\x55\x56\x57\x48\x83\xEC\x30\x48\x8D\x6C\x24.\x48\xC7\x45.....\x89\xD6\x48\x89\xCF\x6A\x28' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3', 0), # noqa: E203,E501
("serverThread" , br'\x55\x56\x57\x48\x83\xEC\x30\x48\x8D\x6C\x24.\x48\xC7\x45.....\x89\xD6\x48\x89\xCF\x6A\x28' , 0, b'\xC3', 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x55\x56\x57\x48\x81\xEC....\x48\x8D\xAC\x24....\x0F\x29\xB5....\x48\xC7\x85........\x48\x89\xCF' , 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread for 4146" , br'\x55\x56\x57\x48\x81\xEC....\x48\x8D\xAC\x24....\x48\xC7\x85........\x48\x89\xCF' , 0, b'\xC3' , 0), # noqa: E203,E501
# ("crashReporter" , br'\x41\x57\x41\x56\x41\x55\x41\x54\x56\x57\x55\x53\xB8....\xE8....\x48\x29\xC4\x8A\x84\x24....' , 0, b'\xC3' , 0), # noqa: E203,E501
# ("crashReporter for 4153 *guess*" , br'\x55\x48\x89\xE5\x9C\xC7\x41\x30\x0F\x00\x10\x00\x48\x89\x41\x78' , 0, b'\xC3' , 0), # noqa: E203,E501
)
sm_osx_patternes = (
("isValidLicense" , br'\xE8....\x3D....\x75\x14' , 0, b'\x48\xC7\xC0\x19\x01\x00\x00\xC3' , 1), # noqa: E203,E501
("isValidLicense" , br'\xE8....\x3D....\x75\x14' , 0, b'\x48\xC7\xC0\x19\x01\x00\x00\xC3' , 1), # noqa: E203,E501
("isValidLicense2" , br'\xE8....\x83\xF8\x01\x75\x14' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense_st" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501
("invalidationFunction", br'\xE8....\x48\x89\x9D....\x48\x8B\xB3' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction" , br'\xE8....\x48\x8D\x3D....\xE8....\x83\x25' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("serverThread" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x53\x50\x41\x89\xF6\x49\x89\xFF\x6A\x20' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x55\x48\x89\xE5\x53\x48\x81\xEC....\x48\x89\xFB\x48\x8B\x05....\x48\x8B\x00\x48\x89\x45\xF0\x48\x8D\x3D....', 0, b'\xC3' , 0), # noqa: E203,E501
# ("crashReporter" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xCE\x49\x89\xF7' , 0, b'\xC3' , 0), # noqa: E203,E501
# ("crashReporter for 4153 *guess*" , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xCE\x49\x89\xF7' , 0, b'\xC3' , 0), # noqa: E203,E501
("isValidLicense_arm64 for 2062", br'\x17\xf8\x5f\xbc\xa9\xf6\x57\x01\xa9\xf4\x4f\x02\xa9\xfd\x7b\x03\xa9\xFD\xC3\x00\x91\xFF\x03\x08\xD1', 1, b'\x20\x23\x80\xD2\xC0\x03\x5F\xD6', 0), # noqa: E203,E501
("isValidLicense_arm64 for 2075", br'\x00\x04\x40\xF9...\x17\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA' , 8, b'\xE0\x03\x1F\xAA\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
("isValidLicense_arm64 for 2083", br'\x00\x04\x40\xF9...\x17....\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA' , 8, b'\xE0\x03\x1F\xAA\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
("invalidationFunction_arm64 for 2065" , br'...\x94\x61.\x41\xF9..\x00\x10\x1F\x20\x03\xD5\x02\x53\x87\x52' , 0, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
# ("invalidationFunction_arm64 for 2065" , br'\x02..\x52...\x94\x61\x62\x41\xF9...\x10' , 4, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
("validationFunction_arm64 for 2065" , br'\x02..\x52...\x94..\x00....\x91...\x91\xE0.\x00\xF9' , 4, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
# ("validationFunction_arm64 for 2065" , br'\x02..\x52...\x94..\x00.\x5a..\x91\x40..\x91' , 4, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
("validationFunction_arm64 for 2078" , br'\x02..\x52...\x94..\x00....\x91\xE0.\x00\xF9' , 4, b'\x1F\x20\x03\xD5' , 0), # noqa: E203,E501
("serverThread_arm64" , br'\xF6\x57\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91...\x94...\x94\xF3\x03\x00\xAA...\x94\x74\x1A\x00\xB9', 0, b'\xC0\x03\x5F\xD6', 0), # noqa: E203,E501
# ("serverThread_arm64" , br'\xC0\x03\x5F\xD6\xF6\x57\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91...\x94...\x94\xF3\x03\x00\xAA', 4, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
("licenseNotifyThread_arm64" , br'\xFC\x6F\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91\xFF\x43\x0C\xD1' , 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
#("crashReporter_arm64" , br'\xFC\x6F\xBC\xA9\xF6\x57\x01\xA9\xF4\x4F\x02\xA9\xFD\x7B\x03\xA9\xFD\xC3\x00\x91\xFF\x03\x0F\xD1' , 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
## ("crashReporter_arm64 4153 *guess*" , br'\xFC\x6F\xBC\xA9\xF6\x57\x01\xA9\xF4\x4F\x02\xA9\xFD\x7B\x03\xA9\xFD\xC3\x00\x91\xFF\x03\x0F\xD1' , 0, b'\xC0\x03\x5F\xD6' , 0), # noqa: E203,E501
)
sm_linux_patternes = (
("isValidLicense" , br'\xE8....\x3D....\x75\x12' , 0, b'\x48\xC7\xC0\x19\x01\x00\x00\xC3' , 1), # noqa: E203,E501
("isValidLicense2" , br'\xE8....\x83\xF8\x01\x75\x12' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense_st" , br'\xE8....\x49\x8B\xBF....\x85\xC0' , 0, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense for 2082", br'\xE8....\x49\x8B\xBF....\x3D\x19' , 0, b'\x48\xC7\xC0\x19\x01\x00\x00\xC3' , 1), # noqa: E203,E501
("invalidationFunction", br'\xE8....\x48\x89\x5C\x24.\x48\x8B\xB3' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("invalidationFunction for 2078", br'\xBA...\x00\xE8....\x49\x8B\xB6\x10\x03\x00\x00\xBF...\x00' , 5, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("invalidationFunction for 2086", br'\xBA...\x00\xE8....\x48\x8B\xB3\xC0\x02\x00\x00\x48' , 5, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction" , br'\xE8....\xBF....\xE8....\x83\x25' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction for 2082" , br'\xE8....\x48......\xE8....\x83\x25' , 0, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("serverThread" , br'\x55\x41\x56\x53\x41\x89\xF6\x48\x89\xFD\x6A' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x41\x56\x53\x48\x81\xEC....\x48\x89\xFB\xBF....' , 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread for 2086" , br'\x41\x56\x53\x48\x81\xEC....\x48\x89\xFB\x48\x8D\x3D' , 0, b'\xC3' , 0), # noqa: E203,E501
#("crashReporter" , br'\x55\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xD4\x48\x89\xFD' , 0, b'\xC3' , 0), # noqa: E203,E501
)
sm_win_patternes = (
("isValidLicense" , br'\xE8....\x3D....\x75\x15' , 0, b'\x48\xC7\xC0\x19\x01\x00\x00\xC3' , 1), # noqa: E203,E501
("isValidLicense2" , br'\xE8....\x49\x8B\x8E....\x83\xF8\x01' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 1), # noqa: E203,E501
("isValidLicense_st" , br'\x45\x31\xC9\xE8....\x85\xC0\x75\x15' , 3, b'\x48\x31\xC0\xC3' , 1), # noqa: E203,E501 for 4117
("invalidationFunction", br'\x41\xB8....\xE8....\x48\x8B\x96....' , 6, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("validationFunction" , br'\x41\xB8....\xE8....\xE8....\xB9' , 6, b'\x90\x90\x90\x90\x90' , 0), # noqa: E203,E501
("serverThread" , br'\x55\x56\x57\x48\x83\xEC\x30\x48\x8D\x6C\x24.\x48\xC7\x45.....\x89\xD6\x48\x89\xCF\x6A\x28' , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread" , br'\x55\x56\x57\x48\x81\xEC....\x48\x8D\xAC\x24....\x0F\x29\xB5....' , 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread for 2082" , br'\x55\x56\x57\x48\x81\xEC....\x48\x8D\xAC\x24....\x48\xC7\x85\x48\x03\x00\x00\xFE\xFF\xFF\xFF\x48\x89\xCF', 0, b'\xC3' , 0), # noqa: E203,E501
("licenseNotifyThread for 2091" , br'\x55\x56\x57\x48\x81\xEC....\x48\x8D\xAC\x24....\x48\xC7\x85\x58\x03\x00\x00\xFE\xFF\xFF\xFF\x48\x89\xCF', 0, b'\xC3' , 0), # noqa: E203,E501
#("crashReporter" , br'\x41\x57\x41\x56\x41\x55\x41\x54\x56\x57\x55\x53\xB8....\xE8....\x48\x29\xC4\x8A\x84\x24....' , 0, b'\xC3' , 0), # noqa: E203,E501
)
global log, PATCH
log = True
PATCH = False
def crack(filename, app_type=None, target_dir="."):
filename = os.path.join(target_dir.upper(), filename)
sublime = File(filename, app_type)
for pat in sublime.patternes:
Pattern(filename, sublime.binary, pat[0], pat[1], pat[2], pat[3], pat[4])
sublime.patched()
def main():
if len(sys.argv) > 1:
if sys.argv[1][0:2] == "st" and int(sys.argv[1][2:6]) and len(sys.argv[1][2:6]) == 4:
app_type = "sublime_text"
elif sys.argv[1][0:2] == "sm" and int(sys.argv[1][2:6]) and len(sys.argv[1][2:6]) == 4:
app_type = "sublime_merge"
else:
crack(sys.argv[1])
quit()
version = sys.argv[1][2:6]
for url, extract_file in get_url_extract_file(app_type, version):
print("="*90) if log else None
filename = url.split('/')[-1]
download_file(url, proxy=True)
extract(filename, extract_file, sys.argv[1])
crack(extract_file, app_type, sys.argv[1])
if __name__ == "__main__":
main()
E8 ? ? ? ? 3D ? ? ? ? 75 14
E8 ? ? ? ? 3D ? ? ? ? 75 14
E8 CA D0 FF FF 3D 19 01 00 00 75 14
'\xE8....\x3D....\x75\x14'
0x290EC -->移动5个字节(+ 0x5)--> 0x290F1
->(+ 0xFFFFFFFFFFFFD0CA) -->0x261BB
0x290EC 读取 后面4个字节 CA D0 FF FF -->字节翻转 --> FF FF D0 CA -->补全offset --> 0xFFFFFFFFFFFFD0CA
0xFFFFFFFF
file=open('/Users/maboloshi/sublime_merge','rb').read()
# 读取二进制偏量值
s=file[0x290EC+1:0x290EC+5]
# 字节翻转
a=struct.unpack('<I', s)
s=struct.pack('>I',*a)
'0xFFFFFFFF' + ''.join([format(x, '02X') for x in s])
# 输出16进制变量值
''.join([format(x, '02X') for x in s])
---
0xFFFFFFFFFFFFD0CA
0xFFFFD0CA
hex(0x290EC + 0x5 + 0xFFFFD0CA - 0x100000000)
---
'0x261bb'
# 输出最终(initial_license_check函数)偏量值
"0x{:X}".format(0x290EC + 0x5 + 0xFFFFD0CA -0x100000000)
"0x{:X}".format((0x290EC + 0x5 + 0xFFFFD0CA) & 0xFFFFFFFF)
---
'0x261BB'
===
f = open('/Users/maboloshi/Downloads/sublime_merge', 'rb')
data = f.read()
f.close()
pattern = "\xE8....\x3D....\x75\x14"
regex = re.compile(pattern)
for match_obj in regex.finditer(data):
offset = match_obj.start()
print "decimal: {}".format(offset)
# print "hex(): " + hex(offset)
# print "hex(): " + hex(offset).upper()
# print 'formatted hex: {:02X} \n'.format(offset)
print 'formatted hex: 0x{:02X} \n'.format(offset)
======
name isValidLicense
binary open(, 'rb')
rva
class car()
def __init__(self, name, binary, rva):
self.name = model
self.binary = color
self.rva =
def locate(binary):
pattern = b"\xE8....\x3D....\x75\x14"
offset = re.search(pattern,binary).start()
def locate(pattern,binary):
offset = re.search(pattern,binary).start()
class xxxx():
def __init__(self, binary,):
self.binary= binary
def locate(self.binary):
return re.search(pattern,self.binary).start() # 十进制 offset
def displacement():
pattern = xxxxx()
offset = pattern.locate(self.binary)
offset += pattern.displacement()
@maboloshi
Copy link
Author

@maboloshi
Copy link
Author

SM linux?
41 5D 41 5E 41 5F 5D C3 55 48 89 E5 41 57 -> 41 5D 41 5E 41 5F 5D C3 B8 00 00 00 00 C3
1200之前 参考 https://gist.github.com/cipherknight/bd3c7c8786f056e83490482696921245#sublime-merge---build-11091111---linux-x64

Name Original Patched
Initial License Check 3F 00 0F [84] ED 00 00 3F 00 0F [85] ED 00 00
Persistent License Check C7 02 [75] 2E 41 C7 02 [74] 2E 41
Disable Nag 33 A1 D3 26 [48] 33 A1 D3 26 [C3]

@wuwwyy
Copy link

wuwwyy commented Aug 6, 2023

Better mac patterns for sublime text

    ("isValidLicense"      , br'\xE8....\x49\x8B\xBF....\x85\xC0'                                                                            , 0, b'\x48\x31\xC0\xC3'                , 1),  # noqa: E203,E501
    # ("invalidationFunction", br'\xE8....\x48\x89\x9D....\x48\x8B\xB3'                                                                      , 0, b'\x90\x90\x90\x90\x90'            , 0),  # noqa: E203,E501
    # ("validationFunction"  , br'\xE8....\x48\x8D\x3D....\xE8....\x83\x25'                                                                  , 0, b'\x90\x90\x90\x90\x90'            , 0),  # noqa: E203,E501
    #("invalidationFunction for 4139 ", br'\xE8..\x13\x00\x48\x8B\xB3\x88\x02\x00\x00'                                                       , 0, b'\x90\x90\x90\x90\x90'            , 0),  # noqa: E203,E501
    #("validationFunction for 4139"  , br'\xE8..\x13\x00\x48\x89\xDF\xE8...\x00\xBF'                                                         , 0, b'\x90\x90\x90\x90\x90'            , 0),  # noqa: E203,E501
    ("invalidationFunction for 4152"  , br'\x48\x8B\xB3\x88\x02\x00\x00\x48\x8D\x3D..\x00\x00\xBA\x88\x13\x00\x00\xE8...\x00\x48'            , 19, b'\x90\x90\x90\x90\x90'           , 0),  # noqa: E203,E501
    ("validationFunction for 4152"  , br'\xE8....\x48\x8D\x3D....\xE8....\x83\x25'                                                           , 0, b'\x90\x90\x90\x90\x90'            , 0),  # noqa: E203,E501
    
    ("serverThread"        , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x53\x50\x41\x89\xF6\x49\x89\xFF\x6A\x20'                                    , 0, b'\x48\x31\xC0\x48\xFF\xC0\xC3'    , 0),  # noqa: E203,E501
    ("licenseNotifyThread" , br'\x55\x48\x89\xE5\x53\x48\x81\xEC....\x48\x89\xFB\x48\x8B\x05....\x48\x8B\x00\x48\x89\x45\xF0\x48\x8D\x3D....', 0, b'\xC3'                            , 0),  # noqa: E203,E501
    ("crashReporter"       , br'\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC....\x41\x89\xCE\x49\x89\xF7'                , 0, b'\xC3'                            , 0),  # noqa: E203,E501

    #("isValidLicense_arm64 for 4114", br'\x08\x00\x80\x52...\x14\xE6\x03\x1E\xAA...\x94\xFE\x03\x06\xAA'                                    , 8, b'\xE0\x03\x1F\xAA\xC0\x03\x5F\xD6', 0),  # noqa: E203,E501
    ("isValidLicense_arm64 NOP for 4152", br'\xFC....\x03\x1E\xAA..\x0E\x94\xFE\x03.\xAA\xFD\x7B.\xA9\xFD.\x01\x91\xFF\xC3\x07\xD1'          , 0, b'\x1F\x20\x03\xD5'                , 0),
    ("isValidLicense_arm64 for 4152", br'.\x03\x1E\xAA..\x0E\x94\xFE\x03.\xAA\xFD\x7B.\xA9\xFD.\x01\x91\xFF\xC3\x07\xD1'                     , 0, b'\x00\x00\x80\xD2\xC0\x03\x5F\xD6', 0),

    #("invalidationFunction_arm64"   , br'...\x94\x61.\x41\xF9..\x00\x10\x1F\x20\x03\xD5\x02\x53\x87\x52'                                    , 0, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501
    #等效 ("invalidationFunction_arm64"   , br'...\x94\x61\x46\x41\xF9..\x00\x10\x1F\x20\x03\xD5\x02\x53\x87\x52'                             , 0, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501
    ("invalidationFunction_arm64 for 4152", br'\x1F\x20\x03\xD5\x02\x71\x82\x52..\x03\x94.\x46\x41\xf9'                                      , 8, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501

    #("validationFunction_arm64"     , br'\x02..\x52...\x94..\x00....\x91...\x91\xE0.\x00\xF9'                                               , 4, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501
    #等效 ("validationFunction_arm64"     , br'...\x94..\x00.\xF7..\x91\xE0..\x91\xE0\x6F\x00\xF9'                                            , 0, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501
    #("validationFunction_arm64 for 4139", br'...\x94\xE0\x03\x13\xAA...\x94\x00\x45\x80\x52'                                                , 0, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501
    ("validationFunction_arm64 for 4152", br'\x1F\x20\x03\xD5\x02\x53\x87\x52..\x03\x94'                                                     , 8, b'\x1F\x20\x03\xD5'                , 0),  # noqa: E203,E501

    ("serverThread_arm64"           , br'\xF6\x57\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91...\x94...\x94\xF3\x03\x00\xAA...\x94\x74\x1A\x00\xB9', 0, b'\xC0\x03\x5F\xD6' , 0),  # noqa: E203,E501
    ("licenseNotifyThread_arm64"    , br'\xFC\x6F\xBD\xA9\xF4\x4F\x01\xA9\xFD\x7B\x02\xA9\xFD\x83\x00\x91\xFF\x43\x0C\xD1\xF3\x03\x00\xAA'   , 0, b'\xC0\x03\x5F\xD6'                , 0),  # noqa: E203,E501
    ("crashReporte_arm64"           , br'\xFC\x6F\xBC\xA9\xF6\x57\x01\xA9\xF4\x4F\x02\xA9\xFD\x7B\x03\xA9\xFD\xC3\x00\x91\xFF\x03\x0F\xD1'   , 0, b'\xC0\x03\x5F\xD6'                , 0),  # noqa: E203,E501

Tested them and they work from build 4107 to 4152 so hopefully they will last longer.
"isValidLicense_arm64 NOP" is needed because since a few builds ago the compiler is changing the stack pointer at the start of that function which breaks the patch. So I just add an extra NOP if that instruction is found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment