1010from cryptography .hazmat .primitives .asymmetric import dsa
1111from cryptography .hazmat .primitives .asymmetric .utils import decode_dss_signature
1212from cryptography .hazmat .primitives .hashes import SHA1
13- # Color support
13+
1414try :
1515 from colorama import init , Fore , Style
1616 init (autoreset = True )
@@ -19,6 +19,12 @@ class Dummy:
1919 RESET = RED = WHITE = GREEN = LIGHTBLACK_EX = BRIGHT = ''
2020 Fore = Style = Dummy ()
2121
22+ RED = Fore .RED + Style .BRIGHT
23+ WHITE = Fore .WHITE + Style .BRIGHT
24+ GREY = Fore .LIGHTBLACK_EX + Style .NORMAL
25+ GREEN = Fore .GREEN + Style .BRIGHT
26+ RESET = Style .RESET_ALL
27+
2228def is_admin ():
2329 try :
2430 return ctypes .windll .shell32 .IsUserAnAdmin () != 0
@@ -35,32 +41,27 @@ def load_config(filename: str):
3541 try :
3642 with open (filename , 'r' ) as f :
3743 data = json .load (f )
38-
39- file_path = data .get ("file_path" )
40- old_signkey = data .get ("old_signkey" )
41- new_signkey = data .get ("new_signkey" )
42- hwid = data .get ('hwid' , '' ).upper ()
43- edition = data .get ('edition' , 'Suite' )
44- version = data .get ('version' , 12 )
45- authorize_file_output = data .get ('authorize_file_output' , 'Authorize.auz' )
46- dsa_params = data .get ('dsa_parameters' )
47-
48- if not file_path or not old_signkey or not new_signkey :
49- raise ValueError ("JSON file must contain 'file_path', 'old_signkey', and 'new_signkey'." )
50- if len (hwid ) == 24 :
51- hwid = "-" .join (hwid [i :i + 4 ] for i in range (0 , 24 , 4 ))
52- 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 } "
53-
54- if not dsa_params :
55- raise ValueError ("DSA parameters are missing in the config file." )
56-
57- return file_path , old_signkey , new_signkey , hwid , edition , version , authorize_file_output , dsa_params
58-
44+ file_path = data .get ("file_path" )
45+ old_signkey = data .get ("old_signkey" )
46+ new_signkey = data .get ("new_signkey" )
47+ hwid = data .get ('hwid' , '' ).upper ()
48+ edition = data .get ('edition' , 'Suite' )
49+ version = data .get ('version' , 12 )
50+ authorize_file_output = data .get ('authorize_file_output' , 'Authorize.auz' )
51+ dsa_params = data .get ('dsa_parameters' )
52+ if not file_path or not old_signkey or not new_signkey :
53+ raise ValueError ("JSON file must contain 'file_path', 'old_signkey', and 'new_signkey'." )
54+ if len (hwid ) == 24 :
55+ hwid = "-" .join (hwid [i :i + 4 ] for i in range (0 , 24 , 4 ))
56+ 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 } "
57+ if not dsa_params :
58+ raise ValueError ("DSA parameters are missing in the config file." )
59+ return file_path , old_signkey , new_signkey , hwid , edition , version , authorize_file_output , dsa_params
5960 except FileNotFoundError :
60- print (f"The JSON file { filename } was not found." )
61+ print (RED + f"The JSON file { filename } was not found." + RESET )
6162 raise
6263 except json .JSONDecodeError :
63- print (f"Error parsing the JSON file { filename } ." )
64+ print (RED + f"Error parsing the JSON file { filename } ." + RESET )
6465 raise
6566
6667def construct_key (dsa_params ) -> dsa .DSAPrivateKey :
@@ -69,7 +70,6 @@ def construct_key(dsa_params) -> dsa.DSAPrivateKey:
6970 g = int (dsa_params ['g' ], 16 )
7071 y = int (dsa_params ['y' ], 16 )
7172 x = int (dsa_params ['x' ], 16 )
72-
7373 params = dsa .DSAParameterNumbers (p , q , g )
7474 pub = dsa .DSAPublicNumbers (y , params )
7575 priv = dsa .DSAPrivateNumbers (x , pub )
@@ -78,55 +78,46 @@ def construct_key(dsa_params) -> dsa.DSAPrivateKey:
7878def replace_signkey_in_file (file_path , old_signkey , new_signkey ):
7979 if len (old_signkey ) != len (new_signkey ):
8080 raise ValueError ("The new signkey must be the same length as the old signkey." )
81-
8281 if old_signkey .startswith ("0x" ):
8382 old_signkey = old_signkey [2 :]
8483 if new_signkey .startswith ("0x" ):
8584 new_signkey = new_signkey [2 :]
86-
8785 if not re .fullmatch (r'[0-9a-fA-F]+' , old_signkey ):
8886 raise ValueError ("The old signkey is not valid." )
8987 if not re .fullmatch (r'[0-9a-fA-F]+' , new_signkey ):
9088 raise ValueError ("The new signkey is not valid." )
91-
9289 try :
9390 with open (file_path , 'rb' ) as file :
9491 content = file .read ()
95-
9692 old_signkey_bytes = bytes .fromhex (old_signkey )
9793 new_signkey_bytes = bytes .fromhex (new_signkey )
98-
9994 if old_signkey_bytes not in content :
10095 if new_signkey_bytes in content :
101- print (f "The new signkey \n ' { new_signkey } ' \n is already present in the file. Ableton is already patched." )
96+ print (WHITE + "The new signkey is already present in the file. Ableton is already patched." + RESET )
10297 else :
103- print (f "Neither the old nor the new signkey was found in the file. You may be running an unsupported version or a different patch." )
98+ print (RED + "Neither the old nor the new signkey was found in the file. You may be running an unsupported version or a different patch." + RESET )
10499 else :
105- print (f"The old signkey '{ old_signkey } ' was found. Replacing..." )
106-
100+ print (WHITE + "The old signkey was found. Replacing..." + RESET )
107101 content = content .replace (old_signkey_bytes , new_signkey_bytes )
108-
109102 with open (file_path , 'wb' ) as file :
110103 file .write (content )
111-
112104 if old_signkey_bytes in content :
113- print ("Error: The old signkey is still present in the file." )
105+ print (RED + "Error: The old signkey is still present in the file." + RESET )
114106 else :
115- print ("Signkey successfully replaced." )
116-
107+ print (GREEN + "Signkey successfully replaced." + RESET )
117108 except PermissionError :
118- print ("\n Permission denied! Try running the script as Administrator." )
109+ print (RED + "\n Permission denied! Try running the script as Administrator." + RESET )
119110 if platform .system () == "Windows" :
120- print ("Relaunching with admin privileges..." )
111+ print (GREY + "Relaunching with admin privileges..." + RESET )
121112 run_as_admin ()
122113 else :
123- print ("On Linux/macOS, try running with sudo." )
124- raise
114+ print (GREY + "On Linux/macOS, try running with sudo." + RESET )
115+ raise
125116 except FileNotFoundError :
126- print (f"The file '{ file_path } ' was not found." )
117+ print (RED + f"The file '{ file_path } ' was not found." + RESET )
127118 raise
128119 except Exception as e :
129- print (f"An error occurred: { e } " )
120+ print (RED + f"An error occurred: { e } " + RESET )
130121 raise
131122
132123def sign (k : dsa .DSAPrivateKey , m : str ) -> str :
@@ -138,11 +129,11 @@ def sign(k: dsa.DSAPrivateKey, m: str) -> str:
138129
139130def fix_group_checksum (group_number : int , n : int ) -> int :
140131 checksum = n >> 4 & 0xf ^ \
141- n >> 5 & 0x8 ^ \
142- n >> 9 & 0x7 ^ \
143- n >> 11 & 0xe ^ \
144- n >> 15 & 0x1 ^ \
145- group_number
132+ n >> 5 & 0x8 ^ \
133+ n >> 9 & 0x7 ^ \
134+ n >> 11 & 0xe ^ \
135+ n >> 15 & 0x1 ^ \
136+ group_number
146137 return n & 0xfff0 | checksum
147138
148139def overall_checksum (groups : list [int ]) -> int :
@@ -158,17 +149,8 @@ def overall_checksum(groups: list[int]) -> int:
158149 return r & 0xffff
159150
160151def random_serial ():
161- """
162- 3xxc-xxxc-xxxc-xxxc-xxxc-dddd
163- x is random
164- c is a checksum over each group
165- d is a checksum over all groups
166- """
167- groups = [randint (0x3000 , 0x3fff ),
168- randint (0x0000 , 0xffff ),
169- randint (0x0000 , 0xffff ),
170- randint (0x0000 , 0xffff ),
171- randint (0x0000 , 0xffff )]
152+ """ 3xxc-xxxc-xxxc-xxxc-xxxc-dddd x is random c is a checksum over each group d is a checksum over all groups """
153+ groups = [randint (0x3000 , 0x3fff ), randint (0x0000 , 0xffff ), randint (0x0000 , 0xffff ), randint (0x0000 , 0xffff ), randint (0x0000 , 0xffff )]
172154 for i in range (5 ):
173155 groups [i ] = fix_group_checksum (i , groups [i ])
174156 d = overall_checksum (groups )
@@ -201,18 +183,16 @@ def get_user_config_dir():
201183 return os .getenv ('APPDATA' )
202184 elif system == "Darwin" :
203185 return os .path .join (os .path .expanduser ("~" ), "Library" , "Application Support" )
204- else :
186+ else :
205187 return os .getenv ('XDG_CONFIG_HOME' , os .path .join (os .path .expanduser ("~" ), ".config" ))
206188
207189def find_installations ():
208190 system = platform .system ()
209191 installations = []
210-
211192 if system == "Windows" :
212193 base_dir = "C:\\ ProgramData\\ Ableton"
213194 if not os .path .exists (base_dir ):
214195 return installations
215-
216196 for entry in os .listdir (base_dir ):
217197 if "Live" in entry :
218198 entry_path = os .path .join (base_dir , entry )
@@ -223,61 +203,47 @@ def find_installations():
223203 if file .endswith (".exe" ) and "Live" in file :
224204 exe_path = os .path .join (program_dir , file )
225205 installations .append ((exe_path , entry ))
226-
227206 elif system == "Darwin" :
228207 base_dir = "/Applications"
229208 if not os .path .exists (base_dir ):
230209 return installations
231-
232210 for entry in os .listdir (base_dir ):
233211 if entry .endswith (".app" ) and "Ableton Live" in entry :
234212 app_path = os .path .join (base_dir , entry )
235213 exe_path = os .path .join (app_path , "Contents" , "MacOS" , "Live" )
236214 if os .path .exists (exe_path ):
237215 name = entry .replace (".app" , "" )
238216 installations .append ((exe_path , name ))
239-
240217 return installations
241218
242219def find_installation_data ():
243220 config_dir = get_user_config_dir ()
244221 base_dir = os .path .join (config_dir , "Ableton" )
245222 data_dirs = []
246-
247223 if not os .path .exists (base_dir ):
248224 return data_dirs
249-
250225 for entry in os .listdir (base_dir ):
251226 entry_path = os .path .join (base_dir , entry )
252227 if os .path .isdir (entry_path ) and "Live" in entry :
253228 data_dirs .append ((entry_path , entry ))
254-
255229 return data_dirs
256230
257231def main ():
258- # Colors
259- RED = Fore .RED + Style .BRIGHT
260- WHITE = Fore .WHITE + Style .BRIGHT
261- GREY = Fore .LIGHTBLACK_EX + Style .NORMAL
262- LIME = Fore .GREEN + Style .BRIGHT
263- RESET = Style .RESET_ALL
264-
265232 if platform .system () == "Windows" and not is_admin ():
266233 print (RED + "\n This operation requires administrator privileges on Windows." + RESET )
267234 print (GREY + "Relaunching with admin rights..." + RESET )
268235 run_as_admin ()
269236 return
270- # ASCII art in bright red
271- print (RED + r"""
272- ___. .__ __ _________ __
237+
238+ print (RED + r""" ___. .__ __ _________ __
273239_____ \_ |__ | | _____/ |_ ____ ____ \_ ___ \____________ ____ | | __ ___________
274- \__ \ | __ \| | _/ __ \ __\/ _ \ / \/ \ \/_ __ \__ \ _/ ___\| |/ // __ \_ __ \
240+ \__ \ | __ \| | _/ __ \ __\/ _ \ / \/ \ \/\ _ __ \__ \ _/ ___\| |/ // __ \_ __ \
275241 / __ \| \_\ \ |_\ ___/| | ( <_> ) | \ \____| | \// __ \\ \___| <\ ___/| | \/
276242(____ /___ /____/\___ >__| \____/|___| /\______ /|__| (____ /\___ >__|_ \\___ >__|
277243 \/ \/ \/ \/ \/ \/ \/ \/ \/
278244 """ + RESET )
279- print (LIME + "Made by devilAPI" + RESET )
280- print (GREY + "GitHub: " + LIME + "https://github.com/devilAPI/abletonCracker/" + RESET + "\n " )
245+ print (WHITE + "Made by " + RED + " devilAPI" + RESET )
246+ print (WHITE + "GitHub: " + GREY + "https://github.com/devilAPI/abletonCracker/" + RESET + "\n " )
281247
282248 config_file = 'config.json'
283249 try :
@@ -293,18 +259,16 @@ def main():
293259 print (RED + "\n No Ableton Live installations found. Please specify the path manually." + RESET )
294260 input (GREY + "Press Enter to exit..." + RESET )
295261 return
296-
297- print (LIME + "\n Found Ableton installations:" + RESET )
262+ print (WHITE + "\n Found Ableton installations:" + RESET )
298263 for i , (path , name ) in enumerate (installations ):
299- print (WHITE + f"{ i + 1 } . " + LIME + f"{ name } " + GREY + f" at { path } " + RESET )
300-
264+ print (WHITE + f"{ i + 1 } . " + WHITE + f"{ name } " + GREY + f" at { path } " + RESET )
301265 try :
302- selection = int (input (LIME + "\n Select installation to patch: " + RESET )) - 1
266+ selection = int (input (WHITE + "\n Select installation to patch: " + RED )) - 1
303267 if selection < 0 or selection >= len (installations ):
304268 print (RED + "Invalid selection. Using first installation." + RESET )
305269 selection = 0
306270 file_path = installations [selection ][0 ]
307- print (LIME + f"Selected: { file_path } " + RESET )
271+ print (WHITE + f"Selected: { file_path } " + RESET )
308272 except ValueError :
309273 print (RED + "Invalid input. Using first installation found." + RESET )
310274 file_path = installations [0 ][0 ]
@@ -317,21 +281,20 @@ def main():
317281 unlock_dir = os .path .join (default_dir , "Unlock" )
318282 os .makedirs (unlock_dir , exist_ok = True )
319283 authorize_file_output = os .path .join (unlock_dir , "Authorize.auz" )
320- print (LIME + f"\n Using default authorization file location: " + WHITE + f"{ authorize_file_output } " + RESET )
284+ print (WHITE + f"\n Using default authorization file location: " + WHITE + f"{ authorize_file_output } " + RESET )
321285 else :
322- print (LIME + "\n Found Ableton data directories:" + RESET )
286+ print (WHITE + "\n Found Ableton data directories:" + RESET )
323287 for i , (path , name ) in enumerate (data_dirs ):
324- print (WHITE + f"{ i + 1 } . " + LIME + f"{ name } " + GREY + f" at { path } " + RESET )
325-
288+ print (WHITE + f"{ i + 1 } . " + WHITE + f"{ name } " + GREY + f" at { path } " + RESET )
326289 try :
327- selection = int (input (LIME + "\n Select data directory: " + RESET )) - 1
290+ selection = int (input (WHITE + "\n Select data directory: " + RESET )) - 1
328291 if selection < 0 or selection >= len (data_dirs ):
329292 print (RED + "Invalid selection. Using first directory." + RESET )
330293 selection = 0
331294 unlock_dir = os .path .join (data_dirs [selection ][0 ], "Unlock" )
332295 os .makedirs (unlock_dir , exist_ok = True )
333296 authorize_file_output = os .path .join (unlock_dir , "Authorize.auz" )
334- print (LIME + f"Selected: " + WHITE + f"{ authorize_file_output } " + RESET )
297+ print (WHITE + f"Selected: " + GREY + f"{ authorize_file_output } " + RESET )
335298 except ValueError :
336299 print (RED + "Invalid input. Using first data directory found." + RESET )
337300 unlock_dir = os .path .join (data_dirs [0 ][0 ], "Unlock" )
@@ -345,21 +308,21 @@ def main():
345308 input (GREY + "Press Enter to exit..." + RESET )
346309 return
347310
348- print (LIME + "\n Generating authorization keys..." + RESET )
311+ print (WHITE + "\n Generating authorization keys..." + RESET )
349312 try :
350313 lines = list (generate_all (team_r2r_key , edition , version , hwid ))
351314 with open (authorize_file_output , "w" , newline = "\n " ) as f :
352315 f .write ("\n " .join (lines ))
353- print (LIME + "Authorization file created: " + WHITE + f"{ authorize_file_output } " + RESET )
316+ print ("Authorization file created: " + WHITE + f"{ authorize_file_output } " + RESET )
354317 except Exception as e :
355318 print (RED + f"Error generating authorization keys: { e } " + RESET )
356319 input (GREY + "Press Enter to exit..." + RESET )
357320 return
358321
359- print (LIME + "\n Patching executable..." + RESET )
322+ print (WHITE + "\n Patching executable..." + RESET )
360323 try :
361324 replace_signkey_in_file (file_path , old_signkey , new_signkey )
362- print (LIME + "\n Patch completed successfully!" + RESET )
325+ print (WHITE + "\n Patch completed successfully!" + RESET )
363326 input (GREY + "Press Enter to exit..." + RESET )
364327 except Exception as e :
365328 print (RED + f"\n Patch failed: { e } " + RESET )
0 commit comments