from flask import Flask, request, jsonify, send_file, make_response import os import json import uuid import time import threading import logging from main import get_symbol_address, patch_address, copy_file_to_src, zip_src_files app = Flask(__name__) PATCHED_LIBRARIES = {} PERMALINK_EXPIRY = 600 # 10 minutes PATCHES_JSON = 'patches.json' # Configure logging class LogColors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKCYAN = '\033[96m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' class ColoredFormatter(logging.Formatter): def format(self, record): log_colors = { 'DEBUG': LogColors.OKCYAN, 'INFO': LogColors.OKGREEN, 'WARNING': LogColors.WARNING, 'ERROR': LogColors.FAIL, 'CRITICAL': LogColors.FAIL + LogColors.BOLD } log_color = log_colors.get(record.levelname, LogColors.ENDC) record.msg = f"{log_color}{record.msg}{LogColors.ENDC}" return super().format(record) logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger() handler = logger.handlers[0] handler.setFormatter(ColoredFormatter('%(asctime)s - %(levelname)s - %(message)s')) def save_patch_info(permalink_id, file_path): # Dummy function to maintain logs logger.info(f"Patch info saved for permalink_id: {permalink_id}, file_path: {file_path}") @app.route('/') def index(): return ''' Library Patcher

Upload a library to patch

Click to upload a file
''' @app.route('/patch', methods=['POST']) def patch(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 if not file.filename.endswith('.so'): return jsonify({"error": "Invalid file type. Only .so files are allowed."}), 400 # Generate a unique file path file_uuid = str(uuid.uuid4()) file_path = os.path.join('uploads', f"{file_uuid}_{file.filename}") file.save(file_path) # Determine the library name based on the checkbox library_name = "libbluetooth_qti.so" if 'qti' in request.form else "libbluetooth_jni.so" # Patch the file try: l2c_fcr_chk_chan_modes_address = get_symbol_address(file_path, "l2c_fcr_chk_chan_modes") patch_address(file_path, l2c_fcr_chk_chan_modes_address, "20008052c0035fd6") l2cu_send_peer_info_req_address = get_symbol_address(file_path, "l2cu_send_peer_info_req") patch_address(file_path, l2cu_send_peer_info_req_address, "c0035fd6") except Exception as e: logger.error(f"Error patching file: {str(e)}") return jsonify({"error": f"Error patching file: {str(e)}"}), 500 # Create permalink permalink_id = str(uuid.uuid4()) PATCHED_LIBRARIES[permalink_id] = { 'file_path': file_path, 'library_name': library_name, 'timestamp': time.time() } # Save patch info save_patch_info(permalink_id, file_path) # Schedule deletion threading.Timer(PERMALINK_EXPIRY, delete_expired_permalink, args=[permalink_id]).start() logger.info(f"Permalink {permalink_id} created, will expire in {PERMALINK_EXPIRY} seconds") return jsonify({'permalink': f'/download/{permalink_id}'}) @app.route('/api', methods=['POST']) def api(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 if not file.filename.endswith('.so'): return jsonify({"error": "Invalid file type. Only .so files are allowed."}), 400 # Generate a unique file path file_uuid = str(uuid.uuid4()) file_path = os.path.join('uploads', f"{file_uuid}_{file.filename}") file.save(file_path) # Determine the library name based on the request data data = request.form library_name = data.get('library_name', 'libbluetooth_jni.so') # Patch the file try: l2c_fcr_chk_chan_modes_address = get_symbol_address(file_path, "l2c_fcr_chk_chan_modes") patch_address(file_path, l2c_fcr_chk_chan_modes_address, "20008052c0035fd6") l2cu_send_peer_info_req_address = get_symbol_address(file_path, "l2cu_send_peer_info_req") patch_address(file_path, l2cu_send_peer_info_req_address, "c0035fd6") except Exception as e: logger.error(f"Error patching file: {str(e)}") return jsonify({"error": f"Error patching file: {str(e)}"}), 500 # Log patch info permalink_id = str(uuid.uuid4()) save_patch_info(permalink_id, file_path) # Return the patched file directly try: return send_file(file_path, as_attachment=True, download_name=library_name) except Exception as e: logger.error(f"Error sending file: {str(e)}") return jsonify({"error": f"Error sending file: {str(e)}"}), 500 @app.route('/download/', methods=['GET']) def download(permalink_id): if permalink_id not in PATCHED_LIBRARIES: return "Permalink expired or invalid", 404 file_path = PATCHED_LIBRARIES[permalink_id]['file_path'] library_name = PATCHED_LIBRARIES[permalink_id]['library_name'] if not os.path.exists(file_path): return "File not found", 404 try: copy_file_to_src(file_path, library_name) zip_src_files() except Exception as e: logger.error(f"Error preparing download: {str(e)}") return f"Error preparing download: {str(e)}", 500 resp = make_response(send_file('btl2capfix.zip', as_attachment=True)) resp.headers['Content-Disposition'] = f'attachment; filename=btl2capfix.zip' return resp def delete_expired_permalink(permalink_id): if permalink_id in PATCHED_LIBRARIES: file_path = PATCHED_LIBRARIES[permalink_id]['file_path'] if os.path.exists(file_path): os.remove(file_path) logger.info(f"Deleted file: {file_path}") del PATCHED_LIBRARIES[permalink_id] logger.info(f"Permalink {permalink_id} expired and removed from PATCHED_LIBRARIES") if not os.path.exists('uploads'): os.makedirs('uploads') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8080)