├── .gitignore ├── modules ├── __init__.py ├── logging.py ├── banners.py └── patcher.py ├── requirements.txt ├── main.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | license/ -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama 2 | -------------------------------------------------------------------------------- /modules/logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | from colorama import Fore, Style, init 4 | from datetime import datetime 5 | 6 | # Initialize colorama 7 | init(autoreset=True) 8 | 9 | class ColorFormatter(logging.Formatter): 10 | def format(self, record): 11 | time_color = Fore.WHITE 12 | level_color = Fore.CYAN 13 | name_color = Fore.MAGENTA 14 | message_color = Fore.GREEN 15 | 16 | time_str = datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S") 17 | return ( 18 | f"{time_color}[{time_str}] " 19 | f"{level_color}[{record.levelname:<8}] " 20 | f"{name_color}{record.name}: " 21 | f"{message_color}{record.getMessage()}{Style.RESET_ALL}" 22 | ) 23 | 24 | # Configure logger 25 | logger = logging.getLogger("IDA-Patcher") 26 | logger.setLevel(logging.DEBUG) 27 | 28 | stream_handler = logging.StreamHandler(sys.stdout) 29 | stream_handler.setFormatter(ColorFormatter()) 30 | 31 | logger.handlers.clear() 32 | logger.addHandler(stream_handler) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import os 4 | from datetime import datetime 5 | from modules.patcher import IDA 6 | from modules.banners import banners 7 | from modules.logging import logger 8 | 9 | def main(): 10 | parser = argparse.ArgumentParser(description="IDA Pro License Generator and Patcher") 11 | 12 | parser.add_argument("-p", "--path", required=True, help="Full path to the IDA Pro installation directory") 13 | parser.add_argument("-n", "--name", default="HexRays User", help="Name for the license") 14 | parser.add_argument("-e", "--email", default="user@hexrays.com", help="Email for the license") 15 | parser.add_argument("-ed", "--end-date", type=int, help="End year of the license (e.g. 2035)") 16 | 17 | args = parser.parse_args() 18 | 19 | ida = IDA() 20 | ida.name = args.name 21 | ida.email = args.email 22 | ida.path = args.path.strip('"').strip() 23 | if not os.path.isdir(ida.path): 24 | logger.error(f"Invalid IDA path provided: {ida.path}") 25 | return 26 | 27 | now = datetime.now() 28 | end_year = args.end_date or (now + 10) 29 | ida.end_date = datetime(end_year, now.month, now.day, now.hour, now.minute, now.second).strftime("%Y-%m-%d %H:%M:%S") 30 | 31 | ida.generate_license_file() 32 | ida.patch_platform_binaries(ida.path) 33 | ida.move_license_file(ida.path) 34 | 35 | if __name__ == "__main__": 36 | banners() 37 | main() 38 | -------------------------------------------------------------------------------- /modules/banners.py: -------------------------------------------------------------------------------- 1 | import os, time 2 | from sys import stdout 3 | from colorama import Fore, Style 4 | 5 | def clear_screen(): 6 | os.system('cls' if os.name == 'nt' else 'clear') 7 | 8 | def banners(): 9 | clear_screen() 10 | stdout.write(" \n") 11 | stdout.write(""+Fore.LIGHTRED_EX +"██╗██████╗ █████╗ ██████╗ █████╗ ████████╗ ██████╗██╗ ██╗███████╗██████╗ \n") 12 | stdout.write(""+Fore.LIGHTRED_EX +"██║██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔════╝██╔══██╗\n") 13 | stdout.write(""+Fore.LIGHTRED_EX +"██║██║ ██║███████║█████╗██████╔╝███████║ ██║ ██║ ███████║█████╗ ██████╔╝\n") 14 | stdout.write(""+Fore.LIGHTRED_EX +"██║██║ ██║██╔══██║╚════╝██╔═══╝ ██╔══██║ ██║ ██║ ██╔══██║██╔══╝ ██╔══██╗\n") 15 | stdout.write(""+Fore.LIGHTRED_EX +"██║██████╔╝██║ ██║ ██║ ██║ ██║ ██║ ╚██████╗██║ ██║███████╗██║ ██║\n") 16 | stdout.write(""+Fore.LIGHTRED_EX +"╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝\n") 17 | stdout.write(""+Fore.YELLOW +"═════════════╦═════════════════════════════════╦══════════════════════════════\n") 18 | stdout.write(""+Fore.YELLOW +"╔════════════╩═════════════════════════════════╩═════════════════════════════╗\n") 19 | stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"AUTHOR "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" PARI MALAM "+Fore.YELLOW+"║\n") 20 | stdout.write(""+Fore.YELLOW +"╔════════════════════════════════════════════════════════════════════════════╝\n") 21 | stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"GITHUB "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" GITHUB.COM/THATNOTEASY "+Fore.YELLOW+"║\n") 22 | stdout.write(""+Fore.YELLOW +"╚════════════════════════════════════════════════════════════════════════════╝\n") 23 | print(f"{Fore.YELLOW}[IDA-PATCHER] - {Fore.GREEN}IDA Patcher - {Fore.RED}[V0.2] \n{Fore.RESET}") 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IDA Pro Patcher & License Generator 2 | 3 | ![Python](https://img.shields.io/badge/python-3.7+-blue.svg) 4 | ![Platform](https://img.shields.io/badge/platform-windows%20%7C%20linux%20%7C%20macos-lightgrey.svg) 5 | 6 | A Python utility that automates patching IDA Pro binaries and generating valid license files. This tool supports the IDA Pro 9.1 version and can be run on Windows, Linux, and macOS. 7 | 8 | ## Features 9 | 10 | - 🛠️ **Binary Patching**: Automatically patches IDA's core binaries to bypass cryptographic checks. 11 | - 🔑 **License Generation**: Creates a valid 10-year license file (`idapro.hexlic`) for IDA Pro. 12 | - 📦 **All Add-ons Included**: Includes support for multiple architectures like x86, ARM, MIPS, PPC, RISC-V, and ARC. 13 | - ⚡ **Cross-platform**: Fully compatible with Windows, Linux, and macOS. 14 | - ✅ **Verification**: Ensures that binaries are patched correctly and checks if they are already patched. 15 | 16 | ## Supported Versions 17 | 18 | - [IDA Pro 9.1 ](https://t.me/drmechan1sm/392) 19 | 20 | ## Installation 21 | 22 | ### Requirements 23 | 24 | Before running the tool, ensure the following dependencies are installed: 25 | 26 | - Python 3.7+ (We recommend using Python 3.8 or above) 27 | - An existing IDA Pro installation 28 | 29 | ### Clone the repository 30 | 31 | ```bash 32 | git clone https://github.com/ThatNotEasy/IDA-Patcher 33 | cd IDA-Patcher 34 | ``` 35 | 36 | ### Usage 37 | 38 | Run the tool with the following command: 39 | 40 | ```bash 41 | python main.py 42 | ``` 43 | 44 | Follow the on-screen prompts: 45 | 46 | 1. The tool will **generate a license file** (`idapro.hexlic`). 47 | 2. You'll be asked to **provide the path to your IDA Pro installation**. 48 | 3. It will automatically **patch the necessary binaries** and move the generated license file to the correct IDA directory. 49 | 50 | ## Technical Details 51 | 52 | - **Patch Pattern**: The patch modifies the cryptographic verification in the IDA Pro binaries. 53 | 54 | **Original Pattern**: `EDFD425CF978` 55 | **Patched Pattern**: `EDFD42CBF978` 56 | 57 | - **License Features**: 58 | - 10-year validity 59 | - All processor modules included 60 | - RSA-signed for authenticity 61 | 62 | - ![{AB95A9B8-E0F7-45BE-A254-38EEAE780CF3}](https://github.com/user-attachments/assets/b9860e13-16ab-4aef-a062-9880a72289c5) 63 | 64 | ## Disclaimer 65 | 66 | ⚠️ **Important**: This tool is provided for **educational purposes only**. Use it only on software you legally own or have permission to modify. The maintainers are **not responsible** for any misuse of this tool or any resulting consequences. Always respect software licenses and intellectual property rights. 67 | -------------------------------------------------------------------------------- /modules/patcher.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hashlib 3 | import os 4 | import platform 5 | import shutil 6 | from datetime import datetime, timedelta 7 | from modules.logging import logger 8 | 9 | class IDA: 10 | def __init__(self): 11 | self.path = None 12 | self.email = None 13 | self.name = None 14 | self.log = logger 15 | self.addons = ["HEXX86", "HEXX64", "HEXARM", "HEXARM64", "HEXMIPS", "HEXMIPS64", "HEXPPC", "HEXPPC64", "HEXRV64", "HEXARC", "HEXARC64"] # "HEXCX86", "HEXCX64", "HEXCARM", "HEXCARM64", "HEXCMIPS", "HEXCMIPS64", "HEXCPPC", "HEXCPPC64", "HEXCRV", "HEXCRV64", "HEXCARC", "HEXCARC64", 16 | self.patch_origin = bytes.fromhex("EDFD425CF978") 17 | self.patched = bytes.fromhex("EDFD42CBF978") 18 | self.license_filename = "idapro.hexlic" 19 | self.start_date = (datetime.now() - timedelta(days=3)).strftime("%Y-%m-%d %H:%M:%S") 20 | self.end_date = None 21 | 22 | def public_modules_patched(self): 23 | return int.from_bytes( 24 | bytes.fromhex( 25 | "EDFD42CBF978546E8911225884436C57140525650BCF6EBFE80EDBC5FB1DE68F4C66C29CB22EB668788AFCB0ABBB718044584B810F8970CDD" 26 | "F227385F75D5DDDD91D4F18937A08AA83B28C49D12DC92E7505BB38809E91BD0FBD2F2E6AB1D2E33C0C55D5BDDD478EE8BF845FCEF3C82B9D" 27 | "2929ECB71F4D1B3DB96E3A8E7AAF93" 28 | ), 29 | "little" 30 | ) 31 | 32 | def private_keys(self): 33 | return int.from_bytes( 34 | bytes.fromhex( 35 | "77C86ABBB7F3BB134436797B68FF47BEB1A5457816608DBFB72641814DD464DD640D711D5732D3017A1C4E63D835822F00A4EAB619A2C4791" 36 | "CF33F9F57F9C2AE4D9EED9981E79AC9B8F8A411F68F25B9F0C05D04D11E22A3A0D8D4672B56A61F1532282FF4E4E74759E832B70E98B9D102" 37 | "D07E9FB9BA8D15810B144970029874" 38 | ), 39 | "little" 40 | ) 41 | 42 | def generate_license(self): 43 | self.log.info(f"Generating license") 44 | return { 45 | "header": {"version": 1}, 46 | "payload": { 47 | "name": self.name, 48 | "email": self.email, 49 | "licenses": [ 50 | { 51 | "id": "48-2137-ACAB-99", # do not generate random license id 52 | "edition_id": "ida-pro", 53 | "description": "Patched By Pari Malam", 54 | "license_type": "named", 55 | "product": "IDA", 56 | "product_id": "IDAPRO", 57 | "product_version": "9.1", 58 | "seats": 1, 59 | "start_date": self.start_date, 60 | "end_date": self.end_date, # # This can't be more than 10 years! 61 | "issued_on": self.start_date, 62 | "owner": "HexRays", 63 | "add_ons": [ 64 | # { 65 | # "id": "48-1337-DEAD-01", 66 | # "code": "HEXX86L", 67 | # "owner": "48-0000-0000-00", 68 | # "start_date": "2025-06-13 00:00:00", 69 | # "end_date": "2035-12-31 23:59:59", 70 | # }, 71 | # { 72 | # "id": "48-1337-DEAD-02", 73 | # "code": "HEXX64L", 74 | # "owner": "48-0000-0000-00", 75 | # "start_date": "2025-06-13 00:00:00", 76 | # "end_date": "2035-12-31 23:59:59", 77 | # }, 78 | ], 79 | "features": [], 80 | } 81 | ], 82 | }, 83 | } 84 | 85 | def add_all_addons(self, license_dict): 86 | self.log.info(f"Adding {len(self.addons)} addons") 87 | base = license_dict["payload"]["licenses"][0] 88 | for idx, code in enumerate(self.addons, start=1): 89 | base["add_ons"].append({ 90 | "id": f"48-1337-0000-{idx:02}", 91 | "code": code, 92 | "owner": base["id"], 93 | "start_date": self.start_date, 94 | "end_date": self.end_date, 95 | }) 96 | 97 | def json_stringify_sorted(self, obj): 98 | return json.dumps(obj, sort_keys=True, separators=(",", ":")) 99 | 100 | def int_to_le_bytes(self, i: int) -> bytes: 101 | return i.to_bytes((i.bit_length() + 7) // 8, "little") 102 | 103 | def le_bytes_to_int(self, b: bytes) -> int: 104 | return int.from_bytes(b, "little") 105 | 106 | def encrypt(self, data: bytes) -> bytes: 107 | encrypted = pow(self.le_bytes_to_int(data[::-1]), self.private_keys(), self.public_modules_patched()) 108 | return self.int_to_le_bytes(encrypted) 109 | 110 | def sign_license(self, payload: dict) -> str: 111 | self.log.info("Signing license") 112 | data_str = self.json_stringify_sorted({"payload": payload}) 113 | buffer = bytearray(128) 114 | buffer[:33] = b"\x42" * 33 115 | digest = hashlib.sha256(data_str.encode()).digest() 116 | buffer[33:65] = digest 117 | encrypted = self.encrypt(buffer) 118 | return encrypted.hex().upper() 119 | 120 | def patch_binary(self, file_path: str): 121 | self.log.info(f"Patching {file_path}") 122 | if not os.path.exists(file_path): 123 | self.log.warning(f"Skip: {file_path} - file does not exist") 124 | return 125 | 126 | with open(file_path, "rb") as f: 127 | data = f.read() 128 | 129 | if self.patched in data: 130 | self.log.warning(f"{file_path} is already patched") 131 | return 132 | if self.patch_origin not in data: 133 | self.log.warning(f"{file_path} - original patch pattern not found") 134 | return 135 | 136 | data = data.replace(self.patch_origin, self.patched) 137 | with open(file_path, "wb") as f: 138 | f.write(data) 139 | 140 | self.log.info(f"{file_path} has been successfully patched") 141 | 142 | def generate_license_file(self): 143 | license_data = self.generate_license() 144 | self.add_all_addons(license_data) 145 | license_data["signature"] = self.sign_license(license_data["payload"]) 146 | license_dir = os.path.join(os.getcwd(), "license") 147 | os.makedirs(license_dir, exist_ok=True) 148 | license_path = os.path.join(license_dir, self.license_filename) 149 | with open(license_path, "w") as f: 150 | f.write(self.json_stringify_sorted(license_data)) 151 | self.log.info(f"New license generated and saved to {license_path}") 152 | 153 | def patch_platform_binaries(self, ida_path: str): 154 | self.log.info("Patching platform binaries") 155 | os_name = platform.system().lower() 156 | binaries = { 157 | "windows": ["ida.dll", "ida32.dll"], 158 | "linux": ["libida.so", "libida32.so"], 159 | "darwin": ["libida.dylib", "libida32.dylib"], 160 | }.get(os_name, []) 161 | 162 | if not binaries: 163 | self.log.error(f"Unsupported operating system: {os_name}") 164 | return 165 | 166 | for binary in binaries: 167 | binary_path = os.path.join(ida_path, binary) 168 | self.patch_binary(binary_path) 169 | 170 | def move_license_file(self, ida_path: str): 171 | self.log.info("Copying license file") 172 | try: 173 | source_path = os.path.join(os.getcwd(), "license", self.license_filename) 174 | dest = os.path.join(ida_path, self.license_filename) 175 | shutil.copy2(source_path, dest) 176 | self.log.info(f"License file copied to: {dest}") 177 | except Exception as e: 178 | self.log.error(f"Failed to copy license file: {e}") 179 | --------------------------------------------------------------------------------