'''TESTED for the following builds:Windows: 2024-2030Linux: 2024-2030Can potentially work for builds in between or newer!Original script: DE YI <https://github.com/deyixtan>'''import binasciiimport reimport argparseimport osclass Patcher:'''INITIAL_LICENSE_CHECK_AOB_ONEBuild: 2024, 2025, 2028IDA Sig: 80 78 ?? 00 74 3A <- windows80 78 ?? 00 74 1B <- linux^^ ^^48 01Build 2024: @0x2a80f <- windows@0x39bf29 <- linuxBuild 2025: @0x2a803 <- windows@0x39bf43 <- linuxBuild 2028: @0x2a8ff <- windows@0x397fe9 <- linux'''INITIAL_LICENSE_CHECK_AOB_ONE = {"windows": b"8078..00743a","linux": b"8078..00741b","patch_one": b"48","patch_one_offset": 2,"patch_two": b"01","patch_two_offset": 6}'''INITIAL_LICENSE_CHECK_AOB_TWOBuild: 2027, 2030IDA Sig: 80 38 00 74 3A <- windows80 38 00 74 1B <- linux^^ ^^08 01Build 2027: @0x2a815 <- windows@0x39bfe5 <- linuxBuild 2030: @0x2a8e9 <- windows@0x39b005 <- linux'''INITIAL_LICENSE_CHECK_AOB_TWO = {"windows": b"803800743a","linux": b"803800741b","patch": b"0801","patch_offset": 2}'''PERSISTENT_LICENSE_CHECK_AOB_ONEBuild: 2024, 2025, 2028 (windows)IDA Sig: C6 ?? ?? 00 C3 48 83 <- windowsC6 ?? ?? 00 C3 ?? 48 89 ?? E8 <- linux^^01Build 2024: @0x2f800 <- windows@0x39d13b <- linuxBuild 2025: @0x2f7f3 <- windows@0x39d151 <- linuxBuild 2028: @0x2f92f <- windows@NaN <- linux'''PERSISTENT_LICENSE_CHECK_AOB_ONE = {"windows": b"c6....00c34883","linux": b"c6....00c3..4889..e8","patch": b"01","patch_offset": 6}'''PERSISTENT_LICENSE_CHECK_AOB_TWOBuild: 2027, 2030IDA Sig: C6 ?? 00 C3 CC 48 <- windowsC6 ?? 00 C3 CC ?? 48 <- linux^^01Build 2027: @0x2f7fd <- windows@0x39d1ef <- linuxBuild 2030: @0x2edef <- windows@0x39bc91 <- linux'''PERSISTENT_LICENSE_CHECK_AOB_TWO = {"windows": b"c6..00c3cc48","linux": b"c6..00c3cc..48","patch": b"01","patch_offset": 4}'''PERSISTENT_LICENSE_CHECK_AOB_THREEBuild: 2028 (linux)IDA Sig: C6 ?? ?? 00 C3 CC 48 83 <- windowsC6 ?? ?? 00 C3 CC ?? 48 89 ?? E8 <- linux^^01Build 2028: @NaN <- windows@0x3991fc <- linux'''PERSISTENT_LICENSE_CHECK_AOB_THREE = {"windows": b"XXX","linux": b"c6....00c3cc..4889..e8","patch": b"01","patch_offset": 6}'''THEME_CHECK_AOB_ONEBuild: 2024, 2025, 2028IDA Sig: 80 78 ?? 00 74 08 0F 57 C0 0F 11 06 EB 0F 48 8D 15 ? ? ? ? 48 89 F1 E8 ? ? ? ? 48 89 F0 48 83 C4 20 5E C3 CC 56 57 <- windows80 78 ?? 00 BA ? ? ? ? 48 0F 45 D1 B8 ? ? ? ? 48 0F 45 C1 C3 41 57 <- linux^^ ^^48 01Build 2024: @0x2d959 <- windows@0x39c661 <- linuxBuild 2025: @0x2d949 <- windows@0x39c67b <- linuxBuild 2028: @0x2da85 <- windows@0x398721 <- linux'''THEME_CHECK_AOB_ONE = {"windows": b"8078..0074080f57c00f1106eb0f488d15........4889f1e8........4889f04883c4205ec3cc5657","linux": b"8078..00ba........480f45d1b8........480f45c1c34157","patch_one": b"48","patch_one_offset": 2,"patch_two": b"01","patch_two_offset": 6}'''THEME_CHECK_AOB_TWOBuild: 2027, 2030 (windows)IDA Sig: 80 38 00 74 08 0F 57 C0 0F 11 06 EB 0F 48 8D 15 ? ? ? ? 48 89 F1 E8 ? ? ? ? 48 89 F0 48 83 C4 20 5E C3 56 57 <- windows80 38 00 BA ? ? ? ? 48 0F 45 D1 B8 ? ? ? ? 48 0F 45 C1 C3 CC 41 57 <- linux^^ ^^08 01Build 2027: @0x2d955 <- windows@0x39c71d <- linuxBuild 2030: @0x2cf47 <- windows@NaN <- linux'''THEME_CHECK_AOB_TWO = {"windows": b"80380074080f57c00f1106eb0f488d15........4889f1e8........4889f04883c4205ec35657","linux": b"803800ba........480f45d1b8........480f45c1c3cc4157","patch": b"0801","patch_offset": 2}'''THEME_CHECK_AOB_THREEBuild: 2030 (linux)IDA Sig: 80 38 00 74 08 0F 57 C0 0F 11 06 EB 0F 48 8D 15 ? ? ? ? 48 89 F1 E8 ? ? ? ? 48 89 F0 48 83 C4 20 5E C3 56 57 <- windows80 38 00 BA ? ? ? ? 48 0F 45 D1 B8 ? ? ? ? 48 0F 45 C1 C3 CC 53 <- linux^^ ^^08 01Build 2030: @NaN <- windows@0x39b381 <- linux'''THEME_CHECK_AOB_THREE = {"windows": b"XXX","linux": b"803800ba........480f45d1b8........480f45c1c3cc53","patch": b"0801","patch_offset": 2}def __init__(self, file_path):self.file_path = file_path# get hex dump of input filewith open(file_path, "rb") as file:self.hex_dump = bytearray(binascii.hexlify(file.read()))def patch_file(self):os_target = self.__get_file_os_target()if os_target != None and self.__is_initial_license_check_index_valid(os_target) and self.__is_persistent_license_check_index_valid(os_target) and self.__is_theme_check_index_valid(os_target):# bypass & patch file with changesself.__patch_initial_license_check(os_target)self.__patch_persistent_license_check(os_target)self.__patch_theme_check(os_target)try:with open(self.file_path, "wb") as file:file.write(binascii.unhexlify(self.hex_dump))except PermissionError:return Falsereturn Truereturn Falsedef __get_file_os_target(self):if self.hex_dump.startswith(b"4d5a"):print(" >> Windows version")return "windows"elif self.hex_dump.startswith(b"7f454c4602"):print(" >> Linux version")return "linux"else:print(" >> Unknown version")return Nonedef __index_is_valid(self, check_aob):patterns = re.findall(check_aob, self.hex_dump)pat_len = len(patterns)print(" >> found " + str(pat_len) + " occurences (1 expected)")for p in patterns:check_index = self.hex_dump.index(p)print(" >> found @" + hex(int(check_index/2)))return pat_len == 1def __is_initial_license_check_index_valid(self, os):print(" >> looking for INITIAL_LICENSE_CHECK_AOB")# yes, we are checking for the newer AOBs first, because# the old one can be found in the newer builds but at the wrong# locationif self.__index_is_valid(Patcher.INITIAL_LICENSE_CHECK_AOB_ONE[os]):return Trueelif self.__index_is_valid(Patcher.INITIAL_LICENSE_CHECK_AOB_TWO[os]):return Trueelse:return Falsedef __is_persistent_license_check_index_valid(self, os):print(" >> looking for PERSISTENT_LICENSE_CHECK_AOB")if self.__index_is_valid(Patcher.PERSISTENT_LICENSE_CHECK_AOB_ONE[os]):return Trueelif self.__index_is_valid(Patcher.PERSISTENT_LICENSE_CHECK_AOB_TWO[os]):return Trueelif self.__index_is_valid(Patcher.PERSISTENT_LICENSE_CHECK_AOB_THREE[os]):return Trueelse:return Falsedef __is_theme_check_index_valid(self, os):print(" >> looking for THEME_CHECK_AOB")if self.__index_is_valid(Patcher.THEME_CHECK_AOB_ONE[os]):return Trueelif self.__index_is_valid(Patcher.THEME_CHECK_AOB_TWO[os]):return Trueelif self.__index_is_valid(Patcher.THEME_CHECK_AOB_THREE[os]):return Trueelse:return Falsedef __patch_check(self, check_aob, patch_aob, patch_offset = 0):# bypass checkscheck_index = self.hex_dump.index(re.findall(check_aob, self.hex_dump)[0])self.hex_dump[check_index+patch_offset:check_index+patch_offset+len(patch_aob)] = patch_aobdef __patch_check_by_index(self, check_index, patch_aob, patch_offset = 0):# bypass checksself.hex_dump[check_index+patch_offset:check_index+patch_offset+len(patch_aob)] = patch_aobdef __patch_initial_license_check(self, os):# bypass licensepatterns = re.findall(Patcher.INITIAL_LICENSE_CHECK_AOB_ONE[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.INITIAL_LICENSE_CHECK_AOB_ONE["patch_one"], Patcher.INITIAL_LICENSE_CHECK_AOB_ONE["patch_one_offset"])self.__patch_check_by_index(index, Patcher.INITIAL_LICENSE_CHECK_AOB_ONE["patch_two"], Patcher.INITIAL_LICENSE_CHECK_AOB_ONE["patch_two_offset"])else:patterns = re.findall(Patcher.INITIAL_LICENSE_CHECK_AOB_TWO[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.INITIAL_LICENSE_CHECK_AOB_TWO["patch"], Patcher.INITIAL_LICENSE_CHECK_AOB_TWO["patch_offset"])else:print(" >> error: expected 1 result, but got: " + str(len(patterns)))def __patch_persistent_license_check(self, os):# bypass licensepatterns = re.findall(Patcher.PERSISTENT_LICENSE_CHECK_AOB_ONE[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.PERSISTENT_LICENSE_CHECK_AOB_ONE["patch"], Patcher.PERSISTENT_LICENSE_CHECK_AOB_ONE["patch_offset"])else:patterns = re.findall(Patcher.PERSISTENT_LICENSE_CHECK_AOB_TWO[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.PERSISTENT_LICENSE_CHECK_AOB_TWO["patch"], Patcher.PERSISTENT_LICENSE_CHECK_AOB_TWO["patch_offset"])else:patterns = re.findall(Patcher.PERSISTENT_LICENSE_CHECK_AOB_THREE[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.PERSISTENT_LICENSE_CHECK_AOB_THREE["patch"], Patcher.PERSISTENT_LICENSE_CHECK_AOB_THREE["patch_offset"])else:print(" >> error: expected 1 result, but got: " + str(len(patterns)))def __patch_theme_check(self, os):# bypass themepatterns = re.findall(Patcher.THEME_CHECK_AOB_ONE[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.THEME_CHECK_AOB_ONE["patch_one"], Patcher.THEME_CHECK_AOB_ONE["patch_one_offset"])self.__patch_check_by_index(index, Patcher.THEME_CHECK_AOB_ONE["patch_two"], Patcher.THEME_CHECK_AOB_ONE["patch_two_offset"])else:patterns = re.findall(Patcher.THEME_CHECK_AOB_TWO[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.THEME_CHECK_AOB_TWO["patch"], Patcher.THEME_CHECK_AOB_TWO["patch_offset"])else:patterns = re.findall(Patcher.THEME_CHECK_AOB_THREE[os], self.hex_dump)if len(patterns) == 1:index = self.hex_dump.index(patterns[0])self.__patch_check_by_index(index, Patcher.THEME_CHECK_AOB_THREE["patch"], Patcher.THEME_CHECK_AOB_THREE["patch_offset"])else:print(" >> error: expected 1 result, but got: " + str(len(patterns)))# script logicdef main():parser = argparse.ArgumentParser(description="Patch Sublime Merge")parser.add_argument("file_path", help="file path to sublime merge executable.")args = parser.parse_args()print("Patcher >> Starting job...")# check for valid file pathif (os.path.isfile(args.file_path)):# performs patchpatcher = Patcher(args.file_path)result = patcher.patch_file()if result:print("Patcher >> Successfully patched '" + args.file_path + "'.")else:print("Patcher >> Could not work on input file.")else:print("Patcher >> Please input a valid file path.")# direct execution of the scriptif __name__ == "__main__":main()
from https://www.board4all.biz/threads/sublime-merge-dev-build-2030-windows-linux.823464/
