1+ import logging
2+ import os
3+ import re
4+ import shutil
5+ import subprocess
6+ import sys
7+ import zipfile
8+
9+ # Define color codes for logging
10+ class LogColors :
11+ HEADER = '\033 [95m'
12+ OKBLUE = '\033 [94m'
13+ OKCYAN = '\033 [96m'
14+ OKGREEN = '\033 [92m'
15+ WARNING = '\033 [93m'
16+ FAIL = '\033 [91m'
17+ ENDC = '\033 [0m'
18+ BOLD = '\033 [1m'
19+ UNDERLINE = '\033 [4m'
20+
21+ # Custom logging formatter to include colors
22+ class ColoredFormatter (logging .Formatter ):
23+ def format (self , record ):
24+ log_colors = {
25+ 'DEBUG' : LogColors .OKCYAN ,
26+ 'INFO' : LogColors .OKGREEN ,
27+ 'WARNING' : LogColors .WARNING ,
28+ 'ERROR' : LogColors .FAIL ,
29+ 'CRITICAL' : LogColors .FAIL + LogColors .BOLD
30+ }
31+ log_color = log_colors .get (record .levelname , LogColors .ENDC )
32+ record .msg = f"{ log_color } { record .msg } { LogColors .ENDC } "
33+ return super ().format (record )
34+
35+ def run_command (command ):
36+ """
37+ Runs a shell command and logs the output.
38+
39+ Args:
40+ command (str): The command to run.
41+
42+ Returns:
43+ str: The standard output from the command.
44+
45+ Raises:
46+ SystemExit: If the command fails.
47+ """
48+ logging .info (f"Running command: { command } " )
49+ result = subprocess .run (command , shell = True , capture_output = True , text = True )
50+ if result .returncode != 0 and "Cannot determine entrypoint" not in result .stderr :
51+ logging .error (f"Command failed: { command } \n { result .stderr } " )
52+ sys .exit (1 )
53+ logging .info (f"Command output: { result .stdout } " )
54+ return result .stdout
55+
56+ def get_symbol_address (file_path , symbol_name ):
57+ """
58+ Gets the address of a symbol in a binary file using radare2.
59+
60+ Args:
61+ file_path (str): The path to the binary file.
62+ symbol_name (str): The name of the symbol to find.
63+
64+ Returns:
65+ str: The address of the symbol.
66+
67+ Raises:
68+ SystemExit: If the symbol is not found.
69+ """
70+ logging .info (f"Getting address for symbol: { symbol_name } " )
71+ output = run_command (f"radare2 -q -e bin.cache=true -c 'is~{ symbol_name } ' -z { file_path } " )
72+ match = re .search (r'0x[0-9a-fA-F]+' , output )
73+ if match :
74+ address = match .group (0 )
75+ logging .info (f"Found address for { symbol_name } : { address } " )
76+ return address
77+ else :
78+ logging .error (f"Symbol { symbol_name } not found in { file_path } " )
79+ sys .exit (1 )
80+
81+ def patch_address (file_path , address , patch_bytes ):
82+ """
83+ Patches a specific address in a binary file with given bytes using radare2.
84+
85+ Args:
86+ file_path (str): The path to the binary file.
87+ address (str): The address to patch.
88+ patch_bytes (str): The bytes to write at the address.
89+
90+ Raises:
91+ SystemExit: If the patching command fails.
92+ """
93+ logging .info (f"Patching address { address } with bytes: { patch_bytes } " )
94+ run_command (f"radare2 -q -e bin.cache=true -w -c 's { address } ; wx { patch_bytes } ; wci' { file_path } " )
95+ logging .info (f"Successfully patched address { address } " )
96+
97+ def copy_file_to_src (file_path ):
98+ """
99+ Copies a file to the 'src/' directory.
100+
101+ Args:
102+ file_path (str): The path to the file to copy.
103+ """
104+ src_dir = 'src/'
105+ if not os .path .exists (src_dir ):
106+ os .makedirs (src_dir )
107+ shutil .copy (file_path , src_dir )
108+ logging .info (f"Copied { file_path } to { src_dir } " )
109+
110+ def zip_src_files ():
111+ """
112+ Zips all files in the 'src/' directory into 'btl2capfix.zip', preserving symlinks.
113+ """
114+ with zipfile .ZipFile ('btl2capfix.zip' , 'w' , zipfile .ZIP_DEFLATED , allowZip64 = True ) as zipf :
115+ for root , dirs , files in os .walk ('src/' ):
116+ for file in files :
117+ file_path = os .path .join (root , file )
118+ if file_path == os .path .join ('src' , os .path .basename (file_path )):
119+ continue # Skip the original uploaded file
120+ if os .path .islink (file_path ):
121+ link_target = os .readlink (file_path )
122+ zip_info = zipfile .ZipInfo (os .path .relpath (file_path , 'src/' ))
123+ zip_info .create_system = 3 # Unix
124+ zip_info .external_attr = 0o777 << 16
125+ zip_info .external_attr |= 0xA000
126+ zipf .writestr (zip_info , link_target )
127+ else :
128+ zipf .write (file_path , os .path .relpath (file_path , 'src/' ))
129+ logging .info ("Zipped files under src/ into btl2capfix.zip" )
130+
131+ def main ():
132+ """
133+ Main function to execute the script. It performs the following steps:
134+ 1. Copies the input file to the 'src/' directory.
135+ 2. Patches specific addresses in the binary file.
136+ 3. Zips the files in the 'src/' directory into 'btl2capfix.zip'.
137+ """
138+ logging .basicConfig (level = logging .INFO , format = '%(asctime)s - %(levelname)s - %(message)s' )
139+ logger = logging .getLogger ()
140+ handler = logger .handlers [0 ]
141+ handler .setFormatter (ColoredFormatter ('%(asctime)s - %(levelname)s - %(message)s' ))
142+
143+ if len (sys .argv ) != 2 :
144+ logging .error ("Usage: python main.py <file_path>" )
145+ sys .exit (1 )
146+
147+ file_path = sys .argv [1 ]
148+
149+ # Patch l2c_fcr_chk_chan_modes
150+ l2c_fcr_chk_chan_modes_address = get_symbol_address (file_path , "l2c_fcr_chk_chan_modes" )
151+ patch_address (file_path , l2c_fcr_chk_chan_modes_address , "20008052c0035fd6" )
152+
153+ # Patch l2cu_send_peer_info_req
154+ l2cu_send_peer_info_req_address = get_symbol_address (file_path , "l2cu_send_peer_info_req" )
155+ patch_address (file_path , l2cu_send_peer_info_req_address , "c0035fd6" )
156+
157+ # Copy file to src/
158+ copy_file_to_src (file_path )
159+
160+ # Zip files under src/
161+ zip_src_files ()
162+
163+ if __name__ == "__main__" :
164+ main ()
0 commit comments