From 579b96f326d0d524dc2492ad57b3465f70c8b53d Mon Sep 17 00:00:00 2001 From: Felix Seiboldt <52862870+devilAPI@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:39:50 +0300 Subject: [PATCH] Merge Scripts into one (AIO Script), add DSA Parameters to Config, update Readme --- README.md | 32 ++++---- activate_ableton.py | 176 ++++++++++++++++++++++++++++++++++++++++++++ config.json | 9 ++- keygen.py | 104 -------------------------- patcher.py | 71 ------------------ 5 files changed, 198 insertions(+), 194 deletions(-) create mode 100644 activate_ableton.py delete mode 100644 keygen.py delete mode 100644 patcher.py diff --git a/README.md b/README.md index dbde9fd..bf0f305 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,27 @@ # What is this? -This is an open-source implementation of the R2R Modification of Ableton Live. +This is an open-source implementation of the R2R Patch and `R2RLIVE.dll` of Ableton Live, written in Python 3. + +Like `R2RLIVE.dll`, this script uses Team R2R's signing key only. + +# Disclaimer +This script is not the result of reverse engineering Ableton Live, and the output of this script **will not** circumvent the protection on an **unmodified** copy of Ableton Live. # Download -Download the src from Releases Tab. Otherwise features could be broken. +Download the source code zip file from Releases tab. Otherwise features could be broken. https://github.com/devilAPI/abletonCracker/releases/latest # Compatibility - Works on Windows and Linux (with wine) -- This should work for all Ableton Live Versions above Live 9 (9,10,11,12) +- Should work for all Ableton Live Versions above Live 9 (9,10,11,12) - Every Edition works too (Lite, Intro, Standard, Suite) # How to use -1. Open `hex.json` -2. Change the `file_path` variable to your Ableton executable path. -3. Save the file, dont touch the other variables. -4. Run `patcher.py`, your Ableton should be patched. -5. Run the `keygen.py` -# KeyGen -## Usage -` usage: keygen.py [-h] -i HWID [-o OUTPUT] [-v {9,10,11,12}] [-e {Lite,Intro,Standard,Suite}]` - -The KeyGen is an open source reimplementation of `R2RLIVE.dll` by Team R2R. +1. Open `config.json` +2. Change the variables to fit your Ableton Live installation. +3. Save the file. +4. Run `activate_ableton.py`, your Ableton should be patched and the `Authorize.auz` file should generate. +5. Run Ableton, drag the `Authorize.auz` file into the activation Window +6. You're done. Ableton Live is now activated. -Like `R2RLIVE.dll`, this script uses Team R2R's signing key only. - -This script is not the result of reverse engineering Ableton Live, and the output of this script **will not** circumvent the protection on an unmodified copy of Ableton Live. # Credits The Implementation of the KeyGen (keygen.py) was made by rufoa. Go leave a star on his Git page! # Coming Soon - Ableton Downloader -- AIO Script - diff --git a/activate_ableton.py b/activate_ableton.py new file mode 100644 index 0000000..694d3ac --- /dev/null +++ b/activate_ableton.py @@ -0,0 +1,176 @@ +import json +import re +from random import randint +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature +from cryptography.hazmat.primitives.hashes import SHA1 + +def load_config(filename: str): + try: + with open(filename, 'r') as f: + data = json.load(f) + + # Extract values from JSON + file_path = data.get("file_path") + old_signkey = data.get("old_signkey") + new_signkey = data.get("new_signkey") + hwid = data.get('hwid', '').upper() + edition = data.get('edition', 'Suite') + version = data.get('version', 12) + authorize_file_output = data.get('authorize_file_output', 'Authorize.auz') + dsa_params = data.get('dsa_parameters') + + # Validate essential keys + if not file_path or not old_signkey or not new_signkey: + raise ValueError("JSON file must contain 'file_path', 'old_signkey', and 'new_signkey'.") + if len(hwid) == 24: + hwid = "-".join(hwid[i:i+4] for i in range(0, 24, 4)) + assert re.fullmatch(r"([0-9A-F]{4}-){5}[0-9A-F]{4}", hwid), f"Expected hardware ID like 1111-1111-1111-1111-1111-1111, not {hwid}" + + if not dsa_params: + raise ValueError("DSA parameters are missing in the config file.") + + return file_path, old_signkey, new_signkey, hwid, edition, version, authorize_file_output, dsa_params + + except FileNotFoundError: + print(f"The JSON file {filename} was not found.") + raise + except json.JSONDecodeError: + print(f"Error parsing the JSON file {filename}.") + raise + +def construct_key(dsa_params) -> dsa.DSAPrivateKey: + p = int(dsa_params['p'], 16) + q = int(dsa_params['q'], 16) + g = int(dsa_params['g'], 16) + y = int(dsa_params['y'], 16) + x = int(dsa_params['x'], 16) + + params = dsa.DSAParameterNumbers(p, q, g) + pub = dsa.DSAPublicNumbers(y, params) + priv = dsa.DSAPrivateNumbers(x, pub) + return priv.private_key(backend=default_backend()) + +def replace_signkey_in_file(file_path, old_signkey, new_signkey): + if len(old_signkey) != len(new_signkey): + raise ValueError("The new signkey must be the same length as the old signkey.") + + if old_signkey.startswith("0x"): + old_signkey = old_signkey[2:] + if new_signkey.startswith("0x"): + new_signkey = new_signkey[2:] + + if not re.fullmatch(r'[0-9a-fA-F]+', old_signkey): + raise ValueError("The old signkey is not valid.") + if not re.fullmatch(r'[0-9a-fA-F]+', new_signkey): + raise ValueError("The new signkey is not valid.") + + try: + with open(file_path, 'rb') as file: + content = file.read() + + old_signkey_bytes = bytes.fromhex(old_signkey) + new_signkey_bytes = bytes.fromhex(new_signkey) + + if old_signkey_bytes not in content: + print(f"The old signkey '{old_signkey}' was not found in the file.") + else: + print(f"The old signkey '{old_signkey}' was found. Replacing...") + + content = content.replace(old_signkey_bytes, new_signkey_bytes) + + with open(file_path, 'wb') as file: + file.write(content) + + if old_signkey_bytes in content: + print("Error: The old signkey is still present in the file.") + else: + print("Signkey successfully replaced.") + + except FileNotFoundError: + print(f"The file '{file_path}' was not found.") + except Exception as e: + print(f"An error occurred: {e}") + +def sign(k: dsa.DSAPrivateKey, m: str) -> str: + """P1363 format sig over m as a string of hex digits""" + assert k.key_size == 1024 + sig = k.sign(m.encode(), SHA1()) + r, s = decode_dss_signature(sig) + return "{:040X}{:040X}".format(r, s) + +def fix_group_checksum(group_number: int, n: int) -> int: + checksum = n >> 4 & 0xf ^ \ + n >> 5 & 0x8 ^ \ + n >> 9 & 0x7 ^ \ + n >> 11 & 0xe ^ \ + n >> 15 & 0x1 ^ \ + group_number + return n & 0xfff0 | checksum + +def overall_checksum(groups: list[int]) -> int: + r = 0 + for i in range(20): + g, digit = divmod(i, 4) + v = groups[g] >> (digit * 8) & 0xff + r ^= v << 8 + for _ in range(8): + r <<= 1 + if r & 0x10000: + r ^= 0x8005 + return r & 0xffff + +def random_serial(): + """ + 3xxc-xxxc-xxxc-xxxc-xxxc-dddd + x is random + c is a checksum over each group + d is a checksum over all groups + """ + groups = [randint(0x3000, 0x3fff), + randint(0x0000, 0xffff), + randint(0x0000, 0xffff), + randint(0x0000, 0xffff), + randint(0x0000, 0xffff)] + for i in range(5): + groups[i] = fix_group_checksum(i, groups[i]) + d = overall_checksum(groups) + return "{:04X}-{:04X}-{:04X}-{:04X}-{:04X}-{:04X}".format(*groups, d) + +def generate_single(k: dsa.DSAPrivateKey, id1: int, id2: int, hwid: str) -> str: + f = "{},{:02X},{:02X},Standard,{}" + serial = random_serial() + msg = f.format(serial, id1, id2, hwid) + sig = sign(k, msg) + return f.format(serial, id1, id2, sig) + +def generate_all(k: dsa.DSAPrivateKey, edition: str, version: int, hwid: str) -> str: + yield generate_single(k, EDITIONS[edition], version << 4, hwid) + for i in range(0x40, 0xff + 1): + yield generate_single(k, i, 0x10, hwid) + for i in range(0x8000, 0x80ff + 1): + yield generate_single(k, i, 0x10, hwid) + +# Mapping for the editions +EDITIONS = { + "Lite": 4, + "Intro": 3, + "Standard": 0, + "Suite": 2, +} + +# Load configuration +config_file = 'config.json' +file_path, old_signkey, new_signkey, hwid, edition, version, authorize_file_output, dsa_params = load_config(config_file) + +# Construct the key from the loaded parameters +team_r2r_key = construct_key(dsa_params) + +# Generate keys and save the authorize file +lines = generate_all(team_r2r_key, edition, version, hwid) +with open(authorize_file_output, mode="w", newline="\n") as f: + f.write("\n".join(lines)) + +# Replace the signkey in the binary file +replace_signkey_in_file(file_path, old_signkey, new_signkey) diff --git a/config.json b/config.json index a96f236..6a6a89b 100644 --- a/config.json +++ b/config.json @@ -8,5 +8,12 @@ "authorize_file_output": "Authorize.auz", "old_signkey":"33303832303142373330383230313242303630373241383634384345333830343031333038323031314530323831383130304343454441393938434243363636463044323836313133333230454130344342374431424236364345384243323230364331303933354534353134314534373846303639363543313141464241303944343043463943323145364345354231324439414433384532433842424430453444464641413642373239314432464230333230334639343543354145463231423745433238383134373132353144324231363344364444374539414245313344413938363933314646423436384537353243444641423038303845364142363437304442373438443746444638433038434533414346423833463646323338373543353232373945433938353230304430323135303038304437423338413141324438413843324141363843434136444341313738433637433630363335303238313830303532394431443946373637443735423745364435393838444438373445323942314531314635413638413831443233453934353130354346464635363637463235453445324335323736313341303230303833443746334533363430384139463442383444384338454242364134443041383241393236313238353833313443424339464338463235364142353131353142344246394333364637353242424241353534394642333234344233343539424346453243324132464342313032373534333542414136323531463643423539393536424638453946363033343443444532423333413743334333313842393345424541454246333542463642423033383138353030303238313831303039313836443932453734433530373034334339374438454136394638314436313132454643343439313638374635333431463932443641424134464337363445304346424143443943364436443533353738423146413838383231374646364442433743433538303934413832434434433141384238364245353734353739414639364637343035364433343741344131353145323339443443443238394146354238394144433741413338373246444541444538363533453945374144393736373844354330343239314438424134423137373534393036353644363243374433354232413642363044463144373131393142313442453537433238454436", - "new_signkey":"33303832303142373330383230313243303630373241383634384345333830343031333038323031314630323831383130304241423541313039373046303833453236364131323532383937444141433144363733373437313245373944334446314243384330384133343933433641413941324646333342453435313344384236373637414236414145324146364343393130373937364641373546454531333445384237424530334437384343363445303839433834353230374433303641363033354631373243354237353032373546303042443343413233333142384135394435344645373933393338353444443838344238443333344435353342333842433545383836433041324444304534454333324637443838444531413743394446354334323445453742314345364430323135303043333742453930453346384536344530334134324341384436384144354338334542343744334139303238313831303041333343383733374634324532353136413135323535343445363131443731323935383035434544393444323630443537373744423937364636373231463532343739313538453234373745464230454136464633304433344431354232333636394630393637443239413243373436323838454534324338443931464534444245373941373345453838333132353141333536363836343835384535383941444344343143333836334541313138464242434446333442443634454630453741453230423030313932373039413833343643383136423534413531443830344136453036464345314441344230343343324235323730443445343431363232303338313834303030323831383033334644313246443435394645364335433142433039393145393135463842463439393937373136424445354333424446394130393642444342463741343235454636413439353638334343383446334441464142374131443543463946333737464441383443303432453437453743363038323938433639313741334341414234304233433632363235353946453639393039314335424236414338444530314630413946383837433733394646413341314138353830303046383541313831314543333341323139303036333334314538433230414241303638423930333833463843413237443330414138394144463430444539434537333544454442" + "new_signkey":"33303832303142373330383230313243303630373241383634384345333830343031333038323031314630323831383130304241423541313039373046303833453236364131323532383937444141433144363733373437313245373944334446314243384330384133343933433641413941324646333342453435313344384236373637414236414145324146364343393130373937364641373546454531333445384237424530334437384343363445303839433834353230374433303641363033354631373243354237353032373546303042443343413233333142384135394435344645373933393338353444443838344238443333344435353342333842433545383836433041324444304534454333324637443838444531413743394446354334323445453742314345364430323135303043333742453930453346384536344530334134324341384436384144354338334542343744334139303238313831303041333343383733374634324532353136413135323535343445363131443731323935383035434544393444323630443537373744423937364636373231463532343739313538453234373745464230454136464633304433344431354232333636394630393637443239413243373436323838454534324338443931464534444245373941373345453838333132353141333536363836343835384535383941444344343143333836334541313138464242434446333442443634454630453741453230423030313932373039413833343643383136423534413531443830344136453036464345314441344230343343324235323730443445343431363232303338313834303030323831383033334644313246443435394645364335433142433039393145393135463842463439393937373136424445354333424446394130393642444342463741343235454636413439353638334343383446334441464142374131443543463946333737464441383443303432453437453743363038323938433639313741334341414234304233433632363235353946453639393039314335424236414338444530314630413946383837433733394646413341314138353830303046383541313831314543333341323139303036333334314538433230414241303638423930333833463843413237443330414138394144463430444539434537333544454442", + "dsa_parameters": { + "p": "0xbab5a10970f083e266a1252897daac1d67374712e79d3df1bc8c08a3493c6aa9a2ff33be4513d8b6767ab6aae2af6cc9107976fa75fee134e8b7be03d78cc64e089c845207d306a6035f172c5b750275f00bd3ca2331b8a59d54fe79393854dd884b8d334d553b38bc5e886c0a2dd0e4ec32f7d88de1a7c9df5c424ee7b1ce6d", + "q": "0xc37be90e3f8e64e03a42ca8d68ad5c83eb47d3a9", + "g": "0xa33c8737f42e2516a1525544e611d71295805ced94d260d5777db976f6721f52479158e2477efb0ea6ff30d34d15b23669f0967d29a2c746288ee42c8d91fe4dbe79a73ee8831251a3566864858e589adcd41c3863ea118fbbcdf34bd64ef0e7ae20b00192709a8346c816b54a51d804a6e06fce1da4b043c2b5270d4e441622", + "y": "0x33fd12fd459fe6c5c1bc0991e915f8bf49997716bde5c3bdf9a096bdcbf7a425ef6a495683cc84f3dafab7a1d5cf9f377fda84c042e47e7c608298c6917a3caab40b3c6262559fe699091c5bb6ac8de01f0a9f887c739ffa3a1a858000f85a1811ec33a2190063341e8c20aba068b90383f8ca27d30aa89adf40de9ce735dedb", + "x": "0xc369ea757b46484d1df3819cc4183f6f9a9bcf3c" + } } diff --git a/keygen.py b/keygen.py deleted file mode 100644 index 8bbeb02..0000000 --- a/keygen.py +++ /dev/null @@ -1,104 +0,0 @@ -import json -import re -from random import randint - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import dsa -from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature -from cryptography.hazmat.primitives.hashes import SHA1 - -EDITIONS = { - "Lite": 4, - "Intro": 3, - "Standard": 0, - "Suite": 2, -} - -def load_config(filename: str): - with open(filename, 'r') as f: - return json.load(f) - -def construct_key(*, p, q, g, y, x) -> dsa.DSAPrivateKey: - params = dsa.DSAParameterNumbers(p, q, g) - pub = dsa.DSAPublicNumbers(y, params) - priv = dsa.DSAPrivateNumbers(x, pub) - return priv.private_key(backend=default_backend()) - -def sign(k: dsa.DSAPrivateKey, m: str) -> str: - """P1363 format sig over m as a string of hex digits""" - assert k.key_size == 1024 - sig = k.sign(m.encode(), SHA1()) - r, s = decode_dss_signature(sig) - return "{:040X}{:040X}".format(r, s) - -def fix_group_checksum(group_number: int, n: int) -> int: - checksum = n >> 4 & 0xf ^ \ - n >> 5 & 0x8 ^ \ - n >> 9 & 0x7 ^ \ - n >> 11 & 0xe ^ \ - n >> 15 & 0x1 ^ \ - group_number - return n & 0xfff0 | checksum - -def overall_checksum(groups: list[int]) -> int: - r = 0 - for i in range(20): - g, digit = divmod(i, 4) - v = groups[g] >> (digit * 8) & 0xff - r ^= v << 8 - for _ in range(8): - r <<= 1 - if r & 0x10000: - r ^= 0x8005 - return r & 0xffff - -def random_serial(): - """ - 3xxc-xxxc-xxxc-xxxc-xxxc-dddd - x is random - c is a checksum over each group - d is a checksum over all groups - """ - groups = [randint(0x3000, 0x3fff), - randint(0x0000, 0xffff), - randint(0x0000, 0xffff), - randint(0x0000, 0xffff), - randint(0x0000, 0xffff)] - for i in range(5): - groups[i] = fix_group_checksum(i, groups[i]) - d = overall_checksum(groups) - return "{:04X}-{:04X}-{:04X}-{:04X}-{:04X}-{:04X}".format(*groups, d) - -def generate_single(k: dsa.DSAPrivateKey, id1: int, id2: int, hwid: str) -> str: - f = "{},{:02X},{:02X},Standard,{}" - serial = random_serial() - msg = f.format(serial, id1, id2, hwid) - sig = sign(k, msg) - return f.format(serial, id1, id2, sig) - -def generate_all(k: dsa.DSAPrivateKey, edition: str, version: int, hwid: str) -> str: - yield generate_single(k, EDITIONS[edition], version << 4, hwid) - for i in range(0x40, 0xff + 1): - yield generate_single(k, i, 0x10, hwid) - for i in range(0x8000, 0x80ff + 1): - yield generate_single(k, i, 0x10, hwid) - -team_r2r_key = construct_key( - p=0xbab5a10970f083e266a1252897daac1d67374712e79d3df1bc8c08a3493c6aa9a2ff33be4513d8b6767ab6aae2af6cc9107976fa75fee134e8b7be03d78cc64e089c845207d306a6035f172c5b750275f00bd3ca2331b8a59d54fe79393854dd884b8d334d553b38bc5e886c0a2dd0e4ec32f7d88de1a7c9df5c424ee7b1ce6d, - q=0xc37be90e3f8e64e03a42ca8d68ad5c83eb47d3a9, - g=0xa33c8737f42e2516a1525544e611d71295805ced94d260d5777db976f6721f52479158e2477efb0ea6ff30d34d15b23669f0967d29a2c746288ee42c8d91fe4dbe79a73ee8831251a3566864858e589adcd41c3863ea118fbbcdf34bd64ef0e7ae20b00192709a8346c816b54a51d804a6e06fce1da4b043c2b5270d4e441622, - y=0x33fd12fd459fe6c5c1bc0991e915f8bf49997716bde5c3bdf9a096bdcbf7a425ef6a495683cc84f3dafab7a1d5cf9f377fda84c042e47e7c608298c6917a3caab40b3c6262559fe699091c5bb6ac8de01f0a9f887c739ffa3a1a858000f85a1811ec33a2190063341e8c20aba068b90383f8ca27d30aa89adf40de9ce735dedb, - x=0xc369ea757b46484d1df3819cc4183f6f9a9bcf3c -) - -# Load configuration -config = load_config('config.json') - -hwid = config.get('hwid', '').upper() -if len(hwid) == 24: - hwid = "-".join(hwid[i:i+4] for i in range(0, 24, 4)) -assert re.fullmatch(r"([0-9A-F]{4}-){5}[0-9A-F]{4}", hwid), f"Expected hardware ID like 1111-1111-1111-1111-1111-1111, not {hwid}" - -lines = generate_all(team_r2r_key, config.get('edition', 'Suite'), config.get('version', 12), hwid) -with open(config.get('authorize_file_output', 'Authorize.auz'), mode="w", newline="\n") as f: - f.write("\n".join(lines)) diff --git a/patcher.py b/patcher.py deleted file mode 100644 index f0b88c6..0000000 --- a/patcher.py +++ /dev/null @@ -1,71 +0,0 @@ -import json -import re - -def read_config_from_json(json_file_path): - try: - with open(json_file_path, 'r') as json_file: - data = json.load(json_file) - file_path = data.get("file_path") - old_signkey = data.get("old_signkey") - new_signkey = data.get("new_signkey") - - if not file_path or not old_signkey or not new_signkey: - raise ValueError("JSON file must contain 'file_path', 'old_signkey', and 'new_signkey'.") - - return file_path, old_signkey, new_signkey - - except FileNotFoundError: - print(f"The JSON file {json_file_path} was not found.") - raise - except json.JSONDecodeError: - print(f"Error parsing the JSON file {json_file_path}.") - raise - -def replace_signkey_in_file(file_path, old_signkey, new_signkey): - if len(old_signkey) != len(new_signkey): - raise ValueError("The new signkey must be the same length as the old signkey.") - - if old_signkey.startswith("0x"): - old_signkey = old_signkey[2:] - if new_signkey.startswith("0x"): - new_signkey = new_signkey[2:] - - if not re.fullmatch(r'[0-9a-fA-F]+', old_signkey): - raise ValueError("The old signkey is not valid.") - if not re.fullmatch(r'[0-9a-fA-F]+', new_signkey): - raise ValueError("The new signkey is not valid.") - - try: - with open(file_path, 'rb') as file: - content = file.read() - - old_signkey_bytes = bytes.fromhex(old_signkey) - new_signkey_bytes = bytes.fromhex(new_signkey) - - if old_signkey_bytes not in content: - print(f"The old signkey '{old_signkey}' was not found in the file.") - else: - print(f"The old signkey '{old_signkey}' was found. Replacing...") - - content = content.replace(old_signkey_bytes, new_signkey_bytes) - - with open(file_path, 'wb') as file: - file.write(content) - - if old_signkey_bytes in content: - print("Error: The old signkey is still present in the file.") - else: - print("Signkey successfully replaced.") - - except FileNotFoundError: - print(f"The file '{file_path}' was not found.") - except Exception as e: - print(f"An error occurred: {e}") - -json_file_path = 'config.json' - -try: - file_path, old_signkey, new_signkey = read_config_from_json(json_file_path) - replace_signkey_in_file(file_path, old_signkey, new_signkey) -except Exception as e: - print(f"Error: {e}")