28 exemples complets · AES-256-GCM · ECDSA P-384 · Argon2id · Anti-Debug · VM · GUI
IronLock v2.0 — Exemples
28 exemples prêts à l'emploi couvrant l'intégralité de l'API IronLock v2 : chiffrement AES-GCM et ChaCha20, licences ECDSA P-384, fingerprint 12 sources, anti-debug Ring0, VM detector, loader, packager, build Cython et migration v1→v2.
AES-256-GCMChaCha20ECDSA P-384
Argon2idFingerprint 12Anti-Debug 12
VM Detect 15Cython
01
Générer une paire de clés ECC P-384
tools/license_generator.py — generate_ecc_keypair()
Génère une paire ECC P-384. La clé privée ne doit jamais être committée dans Git ni envoyée par email. Stocker sur une clé USB chiffrée hors ligne.
from tools.license_generator import generate_ecc_keypair, save_keypair # Génère une paire ECC P-384 private_pem, public_pem = generate_ecc_keypair() # Sauvegarde sur disque (chmod 600 automatique sur Unix) save_keypair( private_pem, public_pem, private_path = "private_key.pem", public_path = "public_key.pem", ) # Afficher la clé publique à embarquer dans loader.py print(public_pem)
[+] Private key : private_key.pem ← KEEP SECRET — store offline!
[+] Public key : public_key.pem ← Embed in loader.py
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE...
-----END PUBLIC KEY-----
🔴
private_key.pem = clé de signature de toutes vos licences. Perte = impossible de regénérer les licences existantes. Stocker sur clé USB AES-256 ou gestionnaire de mots de passe.02
Générer des clés RSA-2048 (rétro-compat v1)
generate_rsa_keypair() — compatibilité v1
À utiliser uniquement si des clients ont encore des licences v1 signées RSA. Pour les nouveaux projets, toujours préférer ECDSA P-384 (exemple 01).
from tools.license_generator import generate_rsa_keypair, save_keypair # RSA-2048 — v1 uniquement private_pem, public_pem = generate_rsa_keypair(key_size=2048) save_keypair(private_pem, public_pem, "private_rsa_v1.pem", "public_rsa_v1.pem") # Dans core/loader.py — rétro-compat # IRONLOCK_PUBLIC_KEY_RSA_V1 = open("public_rsa_v1.pem").read()
03
Chiffrer un fichier — AES-256-GCM
CryptoEngine.encrypt_file() — AES-GCM
Chiffre un fichier en AES-256-GCM avec Argon2id. L'algorithme est sélectionné automatiquement (AES-GCM si AES-NI présent). Le fichier source n'est pas modifié.
import hashlib from core.crypto_engine import CryptoEngine, build_master_secret # Construction du secret maître (fingerprint + secret licence) fingerprint = "sha256-fingerprint-64-chars" licence_secret = "hex-secret-64-chars" master_secret = build_master_secret(fingerprint, licence_secret) engine = CryptoEngine(master_secret) # Chiffrement AES-256-GCM (auto-sélectionné si AES-NI présent) result = engine.encrypt_file("monapp.exe", "payload.ironenc") print(f"Algo : {result['algo']}") print(f"Original : {result['size_original']:,} bytes") print(f"Chiffré : {result['size_encrypted']:,} bytes") print(f"Chunks : {result['chunks_count']}") print(f"Hash orig : {result['hash_original']}")
Algo : AES-256-GCM
Original : 15,234,816 bytes
Chiffré : 15,308,240 bytes
Chunks : 233
Hash orig : 3a7f8b2c9d1e4f5a6b7c8d9e0f1a2b3c...
04
Chiffrer — ChaCha20-Poly1305 (forcé)
CryptoEngine(prefer_chacha=True)
Force l'utilisation de ChaCha20-Poly1305 — recommandé sur hardware sans AES-NI (ARM bas de gamme, vieilles VMs) car constant-time natif.
from core.crypto_engine import CryptoEngine, build_master_secret master_secret = build_master_secret("fingerprint", "secret") # Forcer ChaCha20-Poly1305 (timing-safe sans AES-NI) engine = CryptoEngine(master_secret, prefer_chacha=True) result = engine.encrypt_file("payload.py", "payload.ironenc") print(f"Algo : {result['algo']}") # ChaCha20-Poly1305 # Le déchiffrement est automatiquement dirigé vers le bon algo # via le champ ALGO (0x02) dans le header du .ironenc
Algo : ChaCha20-Poly1305
05
Déchiffrer en mémoire (RAM uniquement)
CryptoEngine.decrypt_to_memory()
Déchiffre et vérifie l'authenticité du payload directement en mémoire. Aucun fichier temporaire créé. Le hash SHA-256 final est vérifié contre les métadonnées chiffrées.
from core.crypto_engine import CryptoEngine, build_master_secret master_secret = build_master_secret("fingerprint", "licence_secret") engine = CryptoEngine(master_secret) try: payload_bytes = engine.decrypt_to_memory("payload.ironenc") print(f"[+] Déchiffré : {len(payload_bytes):,} bytes") print(f"[+] Type : {payload_bytes[:4].hex()}") # MZ, ELF, PK... # → Exécuter en mémoire except ValueError as e: print(f"[!] Échec : {e}") # Tampered / mauvaise clé finally: # Effacement mémoire best-effort payload_bytes = bytes(len(payload_bytes)) if 'payload_bytes' in dir() else None
[+] Déchiffré : 15,234,816 bytes
[+] Type : 4d5a0090 (MZ = Windows PE)
06
Déchiffrer un payload v1 (AES-CBC)
Rétro-compat v1 — auto-détection magic IRONLK1
Aucune configuration requise. Le moteur détecte automatiquement le magic
IRONLK1 et bascule sur le chemin de déchiffrement AES-256-GCM legacy.from core.crypto_engine import CryptoEngine, build_master_secret # IDENTIQUE à l'exemple 05 — le moteur détecte automatiquement master_secret = build_master_secret("old_fingerprint_v1", "old_v1_secret") engine = CryptoEngine(master_secret) # payload_v1.ironenc commence par "IRONLK1" → CBC legacy branch data = engine.decrypt_to_memory("payload_v1.ironenc") # Vérifier la version du package with open("payload.ironenc", "rb") as f: magic = f.read(7) print(f"Format : v1 CBC" if magic == b"IRONLK1" else "Format : v2 GCM")
[+] Déchiffré : 8,421,376 bytes (v1 AES-CBC legacy branch)
07
Argon2id — dérivation de clé maître
derive_master_key() — Argon2id m=64Mo
Utilisation directe de la fonction de dérivation Argon2id. 64 Mo de RAM requis par opération — rend le brute-force GPU ×62 plus difficile que PBKDF2.
import secrets from core.crypto_engine import derive_master_key, _ARGON2_AVAILABLE print(f"Argon2id disponible : {_ARGON2_AVAILABLE}") salt = secrets.token_bytes(32) passphrase = b"mon_secret_machine" # Argon2id (m=64Mo, t=3, p=4) master_key = derive_master_key(passphrase, salt) print(f"Clé dérivée : {master_key.hex()}") print(f"Longueur : {len(master_key)} bytes (256-bit)") # Si argon2-cffi absent → fallback PBKDF2-SHA256 300K auto
Argon2id disponible : True
Clé dérivée : 3a7f1b2c9d4e8f5a6b7c8d9e0f1a2b3c...
Longueur : 32 bytes (256-bit)
08
HKDF — dérivation de clés de session éphémères
derive_session_key() — HKDF-SHA256
Dérive des clés de session distinctes depuis la clé maître via HKDF-SHA256. La clé maître n'est jamais utilisée directement pour le chiffrement.
from core.crypto_engine import derive_master_key, derive_session_key import secrets salt = secrets.token_bytes(32) master_key = derive_master_key(b"master_secret", salt) # Deux clés distinctes depuis la même clé maître enc_key = derive_session_key(master_key, b"encryption") # 32B hmac_key = derive_session_key(master_key, b"hmac") # 32B auth_key = derive_session_key(master_key, b"server-auth", # 64B custom length=64) print(f"enc_key : {enc_key.hex()[:32]}...") print(f"hmac_key : {hmac_key.hex()[:32]}...") print(f"auth_key : {auth_key.hex()[:32]}...")
enc_key : 3a7f1b2c9d4e8f5a6b7c8d9e0f1a2b3c...
hmac_key : f8a2c4e6b0d3f5a7c9e1b3d5f7a9c1e3...
auth_key : 1a3b5c7d9e0f2a4b6c8d0e2f4a6b8c0d...
09
Collecter les 12 sources fingerprint
collect_all_sources() + generate_fingerprint()
Collecte les 12 sources hardware et génère le fingerprint SHA-256 64 chars. À exécuter sur la machine du client pour générer le fichier à envoyer au développeur.
from core.fingerprint import ( collect_all_sources, generate_fingerprint, generate_fingerprint_components, ) # Collecte des 12 sources (v2) sources = collect_all_sources() fingerprint = generate_fingerprint(sources) components = generate_fingerprint_components(sources) print(f"FINGERPRINT : {fingerprint}") print(f"Sources : {len(sources)}/12") # Afficher les 4 nouvelles sources v2 for k in ["chassis_serial", "volume_serial", "cpu_features", "screen_res"]: print(f" {k}: {sources[k]}")
FINGERPRINT : 3a7f1b2c9d4e8f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a
Sources : 12/12
chassis_serial : S1Y4NX0B
volume_serial : D8F3A21C
cpu_features : 7a3b9c2d
screen_res : 1920x1080
10
Matching partiel fingerprint
compute_match_score() — tolérance 8/12
Compare deux sets de composants. Retourne (score, total, clés concordantes). Score ≥ 8/12 = licence valide malgré des changements hardware mineurs.
from core.fingerprint import ( collect_all_sources, generate_fingerprint_components, compute_match_score, ) # Composants stockés dans la licence stored_components = { "cpu_id": "3a7f1b2c", "mac_address": "b2c9e8f4", "disk_serial": "c3d0f7a5", "motherboard_uuid": "d4e1a8b6", "bios_serial": "e5f2b9c7", "hostname": "f6a3c0d8", "platform_sig": "a7b4d1e9", "cpu_timing": "b8c5e2f0", "chassis_serial": "c9d6f3a1", "volume_serial": "d0e7a4b2", "cpu_features": "e1f8b5c3", "screen_res": "f2a9c6d4", } # Composants actuels (machine peut avoir changé) sources = collect_all_sources() current = generate_fingerprint_components(sources) score, total, matched = compute_match_score(stored_components, current) TOLERANCE = 8 print(f"Score : {score}/{total}") print(f"Valide : {score >= TOLERANCE}") print(f"Matchés : {matched}")
Score : 10/12
Valide : True (≥ 8 requis)
Matchés : ['cpu_id', 'disk_serial', 'motherboard_uuid', 'bios_serial',
'platform_sig', 'cpu_timing', 'chassis_serial', 'volume_serial',
'cpu_features', 'screen_res']
11
Sauvegarder le fingerprint (côté client)
machine_fingerprint.json — fichier à envoyer au développeur
Génère le fichier
machine_fingerprint.json que le client envoie au développeur pour générer sa licence. Inclut 12 composants hachés (pas les valeurs brutes).import json, platform from core.fingerprint import ( collect_all_sources, generate_fingerprint, generate_fingerprint_components, ) sources = collect_all_sources() fingerprint = generate_fingerprint(sources) components = generate_fingerprint_components(sources) output = { "version": "2.0", "fingerprint": fingerprint, "components": components, # Hashes 16B — pas les valeurs brutes "platform": platform.system(), "sources_count": len(components), } with open("machine_fingerprint.json", "w") as f: json.dump(output, f, indent=2) print(f"[+] machine_fingerprint.json créé ({len(components)} sources)") print(f"[+] Envoyer ce fichier au développeur pour obtenir votre licence.")
[+] machine_fingerprint.json créé (12 sources)
[+] Envoyer ce fichier au développeur pour obtenir votre licence.
12
Créer une licence ECDSA P-384 (1 an)
create_licence() + sign_licence() — ECDSA P-384
Création complète d'une licence 1 an pour un client. Signer avec la clé privée ECC P-384. Vérifier la signature avant envoi.
import json from tools.license_generator import ( load_private_key, load_public_key, create_licence, sign_licence, package_licence, save_licence, verify_licence_signature, ) # Clés ECC P-384 private_key = load_private_key("private_key.pem") public_key = load_public_key("public_key.pem") # Fingerprint client (12 sources) fp_data = json.loads(open("machine_fingerprint.json").read()) # Création de la licence licence_data = create_licence( customer_name = "Tristan Ruard", customer_email = "tristan@exemple.com", product = "Mon Application v2.0", fingerprint = fp_data["fingerprint"], components = fp_data["components"], duration_days = 365, max_activations = 1, perpetual = False, ) # Signature ECDSA P-384 signature = sign_licence(licence_data, private_key) package = package_licence(licence_data, signature, private_key) # Vérification avant envoi valid = verify_licence_signature(package, public_key) print(f"[+] Signature ECDSA valide : {valid}") print(f"[+] Format : {package['format']}") print(f"[+] ID : {licence_data['id']}") print(f"[+] Expire : {licence_data['expires_at']}") save_licence(package, "licence.lic")
[+] Signature ECDSA valide : True
[+] Format : IRONLOCK-ECDSA-P384-SHA256-v2
[+] ID : 4c8d7f23-a1b9-4e32-c7d6-8f3b2a1e9c04
[+] Expire : 2026-05-16
[+] Licence saved: licence.lic
13
Licence perpétuelle
create_licence(perpetual=True)
perpetual=True → expires_at = null dans la licence. Le loader v2 accepte null comme valeur perpétuelle. Pas de date, pas de vérification d'expiration.import json from tools.license_generator import * private_key = load_private_key("private_key.pem") fp = json.loads(open("machine_fingerprint.json").read()) licence_data = create_licence( customer_name = "Tristan Ruard", product = "Mon Application v2.0", fingerprint = fp["fingerprint"], components = fp["components"], perpetual = True, # expires_at = null max_activations = 1, ) print(f"expires_at : {licence_data['expires_at']}") # None sig = sign_licence(licence_data, private_key) pkg = package_licence(licence_data, sig, private_key) save_licence(pkg, "licence_perpetuelle.lic")
expires_at : None ← Perpétuelle — jamais de vérification expiration
14
Vérifier une signature de licence
verify_licence_signature() — ECDSA P-384
Vérification standalone d'une licence sans exécuter le loader. Utile pour auditer une .lic reçue ou intégrer dans un outil de gestion.
import json from tools.license_generator import load_public_key, verify_licence_signature from datetime import datetime public_key = load_public_key("public_key.pem") package = json.loads(open("licence.lic").read()) data = package["data"] # Vérification cryptographique valid = verify_licence_signature(package, public_key) # Vérification expiration exp = data.get("expires_at") expired = (not exp) or (datetime.utcnow() > datetime.strptime(exp, "%Y-%m-%d")) print(f"Client : {data['customer_name']}") print(f"Produit : {data['product']}") print(f"Signature : {'✅ VALIDE' if valid else '❌ INVALIDE'}") print(f"Expire : {exp or 'Perpétuelle'}") print(f"Statut : {'❌ Expirée' if expired else '✅ Active'}") print(f"Format : {package['format']}") print(f"Composants : {len(data['components'])}/12")
Client : Tristan Ruard
Produit : Mon Application v2.0
Signature : ✅ VALIDE
Expire : 2026-05-16
Statut : ✅ Active
Format : IRONLOCK-ECDSA-P384-SHA256-v2
Composants : 12/12
15
Rétro-compat : accepter licences RSA v1
IRONLOCK_PUBLIC_KEY_RSA_V1 dans loader.py
Configure le loader v2 pour accepter également les signatures RSA v1 existantes. La tentative ECDSA P-384 se fait d'abord — RSA uniquement si le format v1 est détecté.
# Dans core/loader.py — avant obfuscation et build # Clé ECC P-384 principale (nouvelle, v2) IRONLOCK_PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE... (votre clé ECC) -----END PUBLIC KEY-----""" # Clé RSA-2048 v1 — rétro-compat IRONLOCK_PUBLIC_KEY_RSA_V1 = """-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A... (votre ancienne clé RSA) -----END PUBLIC KEY-----""" # Le loader tente automatiquement : # 1. ECDSA P-384 si format = "IRONLOCK-ECDSA-P384-SHA256-v2" # 2. RSA-PSS si format = "IRONLOCK-RSA-PSS-SHA256-v1"
16
Licence avec features modules
create_licence(features=[...]) — feature flags
Les features permettent de distribuer différentes éditions du même programme depuis un seul payload. Le payload lit
licence_data["features"] pour activer les modules.import json from tools.license_generator import * private_key = load_private_key("private_key.pem") fp = json.loads(open("machine_fingerprint.json").read()) # Licence "Pro" avec modules avancés lic_pro = create_licence( customer_name = "Tristan Ruard", product = "MonApp v2.0 — Pro", fingerprint = fp["fingerprint"], components = fp["components"], duration_days = 365, features = ["standard", "pro", "analytics", "export_pdf"], ) # Dans le payload — lecture des features # features = licence_data.get("features", []) # if "pro" in features: enable_pro_module() # if "analytics" in features: enable_analytics() sig = sign_licence(lic_pro, private_key) pkg = package_licence(lic_pro, sig, private_key) save_licence(pkg, "licence_pro.lic") print(f"Features : {lic_pro['features']}")
Features : ['standard', 'pro', 'analytics', 'export_pdf']
17
Anti-debug — 12 checks en ordre aléatoire
run_security_checks(strict=False) — rapport complet
Exécute les 12 checks anti-debug dans un ordre aléatoire et retourne un rapport détaillé. Mode
strict=True (défaut production) → os._exit(1) si menace détectée.from core.anti_debug import run_security_checks # Mode rapport (dev/test) rapport = run_security_checks(strict=False) checks_display = [ ("IsDebuggerPresent", rapport["debugger_windows"]), ("CheckRemoteDebugger", rapport["debugger_remote"]), ("TracerPid (Linux)", rapport["debugger_linux"]), ("Timing anomaly", rapport["debugger_timing"]), ("NtQueryInformationProcess", rapport["nt_query_debug"]), ("PEB.NtGlobalFlag", rapport["peb_flags"]), ("RDTSC delta", rapport["rdtsc_timing"]), ("Exception handler", rapport["exception_handler"]), ("CloseHandle(NULL)", rapport["close_handle"]), ("Stack walk", rapport["stack_walk"]), ] for name, detected in checks_display: icon = "⚠️ DÉTECTÉ" if detected else "✅ Clean" print(f" {name:<30} : {icon}") print(f"\nProcessus debug : {rapport['debug_processes'] or 'Aucun'}") print(f"Env debug vars : {rapport['debug_env_vars'] or 'Aucun'}") print(f"MENACE DÉTECTÉE : {rapport['threat_detected']}")
IsDebuggerPresent : ✅ Clean
NtQueryInformationProcess : ✅ Clean
PEB.NtGlobalFlag : ✅ Clean
...
Processus debug : Aucun
MENACE DÉTECTÉE : False
18
VM Detector — 15 checks + rapport
detect_vm() — score pondéré 15 checks
Exécute les 15 checks VM et retourne score, confiance et liste des indicateurs détectés. Score ≥ 3 → VM. Confiance = min(1.0, score / 15.0).
from core.vm_detector import detect_vm result = detect_vm() print(f"VM détectée : {'⚠️ OUI' if result['is_vm'] else '✅ Non'}") print(f"Score : {result['score']}") print(f"Confiance : {result['confidence']*100:.0f}%") if result["detections"]: print(f"\nIndicateurs ({len(result['detections'])}) :") for d in result["detections"]: print(f" - {d}") else: print("\nAucun indicateur de virtualisation.") # Intégration dans un loader personnalisé THRESHOLD = 0.3 if result["is_vm"] and result["confidence"] > THRESHOLD: print("[!] Exécution en VM non autorisée.") import sys; sys.exit(1)
VM détectée : ✅ Non
Score : 0
Confiance : 0%
Aucun indicateur de virtualisation.
19
Watchdog avec handler personnalisé
WatchdogThread(on_threat=...) — surveillance continue
Démarre le watchdog avec un handler personnalisé pour alerter une API externe avant l'arrêt. Intervalle de 3 secondes (défaut 5s).
import os, sys, time, urllib.request, json from core.anti_debug import WatchdogThread def alert_and_exit(threat_type: str): # Envoyer une alerte avant arrêt try: payload = json.dumps({ "event": "security_threat", "threat": threat_type, "product": "MonApp v2.0", }).encode() urllib.request.urlopen( "https://api.votreapp.com/security/threat", data=payload, timeout=3 ) except Exception: pass # Arrêt immédiat (contourne atexit et finally) os._exit(1) # Démarrage du watchdog watchdog = WatchdogThread(check_interval=3.0, on_threat=alert_and_exit) watchdog.start() print("[+] Watchdog démarré (interval=3s)") time.sleep(30) # Simulation exécution programme watchdog.stop() print("[+] Watchdog arrêté proprement")
20
Loader minimal — workflow complet
run_loader() — séquence 12 étapes
Invocation standard du loader avec les paramètres de production. Tous les fichiers dans le même répertoire que le script.
from core.loader import run_loader import sys # Invocation standard — fichiers dans le même répertoire run_loader( licence_path = "licence.lic", payload_path = "payload.ironenc", extra_args = sys.argv[1:], # Arguments du programme protégé ) # Séquence complète automatique : # [1] Anti-debug 12 checks (ordre aléatoire) # [2] Anti-tamper pré-check # [3] VM detector 15 checks # [4] Chargement licence # [5] Vérif signature ECDSA P-384 # [6] Vérif expiration # [7] Serveur online (si configuré) # [8] Fingerprint 12 sources # [9] Matching 8/12 # [10] Déchiffrement AES-GCM mémoire # [11] Anti-tamper loader hash # [12] Exécution + watchdog
21
Loader cloud — VMs autorisées
run_loader(allow_vm=True) — hébergement cloud
Pour les applications déployées sur AWS, Azure, GCP, Docker. Désactive le VM Detector tout en conservant anti-debug, fingerprint et vérification de licence.
from core.loader import run_loader # Hébergement cloud — VMs explicitement autorisées run_loader( licence_path = "licence.lic", payload_path = "payload.ironenc", allow_vm = True, # Désactive VM Detector ) # Alternative : configurer dans loader.py avant build # ALLOW_VM = True
22
Exécuter un payload Python en mémoire
execute_payload_in_memory() — Python exec()
Exécute directement un payload Python en mémoire via
exec(). Aucun fichier temporaire. Le payload peut lire la licence via une variable injectée dans son namespace.from core.loader import execute_payload_in_memory # Payload Python à exécuter code = b""" import sys print(f"[Payload] Python {sys.version}") print("[Payload] Exécuté entièrement en mémoire — aucun fichier temporaire") print("[Payload] Arguments :", sys.argv) """ # Exécution en mémoire — pas de fichier, pas de trace disque execute_payload_in_memory( payload_bytes = code, payload_name = "app.py", # Indique le type → Python exec() args = ["--mode", "prod"], )
[Payload] Python 3.12.0
[Payload] Exécuté entièrement en mémoire — aucun fichier temporaire
[Payload] Arguments : ['<ironlock>', '--mode', 'prod']
23
Packager un fichier EXE
package_program() — fichier unique
Chiffre un fichier unique (EXE, ELF, script Python) en AES-256-GCM et génère le dossier client complet avec les fichiers core IronLock.
from tools.packager import package_program import secrets # Générer un master secret pour ce client (32 bytes aléatoires) master_secret = secrets.token_bytes(32) result = package_program( source = "monapp.exe", product_name = "Mon Application v2.0", output_dir = "dist_client/", version = "2.0.0", master_secret = master_secret, # Optionnel — auto-généré si None ) print(f"Output : {result['output_dir']}") print(f"Algo : {result['payload_result']['algo']}") print(f"Chunks : {result['payload_result']['chunks_count']}") print(f"Loader hash: {result['loader_hash'][:32]}...")
Output : dist_client/
Algo : AES-256-GCM
Chunks : 233
Loader hash: 3a7f1b2c9d4e8f5a6b7c8d9e0f1a2b3c...
24
Packager un répertoire complet
package_program() — ZIP répertoire → AES-GCM
Chiffre récursivement un répertoire complet : ZIP automatique de tous les fichiers puis chiffrement AES-256-GCM. Le loader dézippe et exécute
__main__.py en mémoire.from tools.packager import package_program result = package_program( source = "./mon_projet_python/", # Dossier complet product_name = "Suite Applicative v2.0", output_dir = "dist_suite/", ) # Le payload = ZIP contenant tout mon_projet_python/ # Le loader détecte PK magic → ZipFile → __main__.py → exec() manifest = result["manifest"] print(f"Original : {manifest['payload_name']}") print(f"Hash : {manifest['payload_hash'][:32]}...") print(f"Type : ZIP → __main__.py → exec() en mémoire")
Original : mon_projet_python.zip
Hash : 7b3a9c2d1e5f8a4b...
Type : ZIP → __main__.py → exec() en mémoire
25
Build PyArmor + PyInstaller (CLI)
obfuscate_build.py — pipeline 6 étapes
Pipeline complet en 6 étapes : vérification environnement, obfuscation PyArmor, hashes d'intégrité, compilation EXE PyInstaller, assemblage package client.
# Standard — PyArmor obf-code 2 + PyInstaller onefile python tools/obfuscate_build.py \ --source-dir core/ \ --exe-name MonApplication \ --icon resources/app.ico # Sans compilation EXE (obfuscation seule) python tools/obfuscate_build.py \ --source-dir core/ \ --skip-pyinstaller # Onedir (démarrage plus rapide, déploiement plus facile) python tools/obfuscate_build.py \ --source-dir core/ \ --exe-name MonApp \ --onedir ## Output : # dist_ironlock_v2_build/ # ├── MonApplication.exe (ou ELF/Mach-O) # └── client_package/ # ├── MonApplication.exe # └── README.txt
STEP 1/6 : Environment check → PyArmor ✅ PyInstaller ✅ ECC key ✅
STEP 2/6 : Preparing directories → build_ironlock_v2/
STEP 3/6 : PyArmor obfuscation → obfuscated/
STEP 4/6 : Integrity hashes → integrity_hashes.json
STEP 5/6 : PyInstaller → MonApplication.exe (42.1 MB)
STEP 6/6 : Client package → client_package/ (42.2 MB)
🎉 BUILD COMPLETE — IronLock v2.0
26
Build C Natif — Cython (v2 recommandé)
obfuscate_build.py --cython — .pyd/.so C natif
Compile les modules core en extensions C natives (.pyd Windows / .so Linux+macOS) via Cython. Plus de bytecode Python — code C compilé via LLVM, pratiquement impossible à décompiler.
# Prérequis : pip install cython # Cython C natif + PyInstaller python tools/obfuscate_build.py \ --source-dir core/ \ --exe-name MonApp \ --cython ## Ce que fait --cython : # 1. Copie les .py → .pyx # 2. Génère setup.py Cython # 3. Compile : python setup.py build_ext --inplace # 4. Produit : loader.pyd / fingerprint.pyd (Windows) # loader.so / fingerprint.so (Linux/macOS) # 5. PyInstaller emballe les extensions C natives # # Résultat : aucun bytecode Python dans le binaire final # IDA Pro voit du code C assemblé, pas du Python # Vérification du résultat python -c "import loader; print('Chargé depuis .pyd/.so C natif')"
STEP 3/6 : Cython compilation (C native)
→ loader.pyd (C native extension Windows)
→ fingerprint.pyd
→ vm_detector.pyd
→ anti_debug.pyd
→ crypto_engine.pyd
🎉 BUILD COMPLETE — IronLock v2.0 (C natif)
27
Self-test complet IronLock v2
Test end-to-end de tous les modules v2
Vérifie que tous les modules IronLock v2 fonctionnent correctement dans l'environnement courant : crypto, fingerprint, anti-debug, VM detector, génération licence.
import os, hashlib, secrets, tempfile print("═══ IronLock v2.0 — Self-Test Complet ═══\n") # 1. Crypto Engine from core.crypto_engine import CryptoEngine, build_master_secret, _ARGON2_AVAILABLE, _HAS_AES_NI print(f"[Crypto] Argon2id : {'✅' if _ARGON2_AVAILABLE else '⚠️ PBKDF2'}") print(f"[Crypto] AES-NI : {'✅' if _HAS_AES_NI else '⚠️ ChaCha20'}") secret = hashlib.sha256(b"test").digest() engine = CryptoEngine(secret) with tempfile.NamedTemporaryFile(delete=False, suffix=".bin") as f: f.write(os.urandom(100_000)); src = f.name r = engine.encrypt_file(src, src + ".enc") d = engine.decrypt_to_memory(src + ".enc") ok = d == open(src, "rb").read() print(f"[Crypto] AES-GCM : {'✅' if ok else '❌'} ({r['algo']})") os.unlink(src); os.unlink(src+".enc") # 2. Fingerprint from core.fingerprint import collect_all_sources, generate_fingerprint sources = collect_all_sources() fp = generate_fingerprint(sources) print(f"[Finger] Sources : {len(sources)}/12 ({'✅' if len(sources)==12 else '⚠️'})") # 3. Anti-debug from core.anti_debug import run_security_checks r = run_security_checks(strict=False) print(f"[Debug] Threat : {'⚠️' if r['threat_detected'] else '✅ None'}") # 4. VM Detector from core.vm_detector import detect_vm v = detect_vm() print(f"[VM] Score : {v['score']} ({'⚠️ VM' if v['is_vm'] else '✅ Physique'})") # 5. Licence (sans clé privée réelle) from tools.license_generator import generate_ecc_keypair priv, pub = generate_ecc_keypair() print("[Lic] ECC P-384 : ✅ Clé générée") print("\n═══ SELF-TEST COMPLET ═══")
═══ IronLock v2.0 — Self-Test Complet ═══
[Crypto] Argon2id : ✅
[Crypto] AES-NI : ✅
[Crypto] AES-GCM : ✅ (AES-256-GCM)
[Finger] Sources : 12/12 ✅
[Debug] Threat : ✅ None
[VM] Score : 0 ✅ Physique
[Lic] ECC P-384 : ✅ Clé générée
═══ SELF-TEST COMPLET ═══
28
Migration complète v1 → v2
Guide migration v1 → v2 — sans interruption de service
Migration complète en 5 étapes — maintien de la compatibilité avec les licences v1 existantes pendant la transition.
""" MIGRATION IronLock v1 → v2 — 5 étapes, zéro interruption ÉTAPE 1 : Générer les nouvelles clés ECC P-384 """ from tools.license_generator import generate_ecc_keypair, save_keypair priv, pub = generate_ecc_keypair() save_keypair(priv, pub, "private_ecc_v2.pem", "public_ecc_v2.pem") """ ÉTAPE 2 : Configurer loader.py v2 avec les DEUX clés (pendant la période de transition) IRONLOCK_PUBLIC_KEY_PEM = open("public_ecc_v2.pem").read() IRONLOCK_PUBLIC_KEY_RSA_V1 = open("public_rsa_v1.pem").read() ← Ancien FINGERPRINT_TOLERANCE = 8 ← Ajusté pour 12 sources ÉTAPE 3 : Rechiffrer les payloads existants """ from core.crypto_engine import CryptoEngine, build_master_secret import secrets old_secret = build_master_secret("old_fingerprint", "old_secret") new_secret = build_master_secret("old_fingerprint", "new_v2_secret") old_engine = CryptoEngine(old_secret) payload_bytes = old_engine.decrypt_to_memory("payload_v1.ironenc") new_engine = CryptoEngine(new_secret) import tempfile, os with tempfile.NamedTemporaryFile(delete=False) as f: f.write(payload_bytes); tmp = f.name result = new_engine.encrypt_file(tmp, "payload_v2.ironenc") os.unlink(tmp) print(f"[+] Re-chiffré : {result['algo']}") """ ÉTAPE 4 : Régénérer les licences v2 (ECDSA P-384) lors des renouvellements Les licences RSA v1 continuent de fonctionner via rétro-compat ÉTAPE 5 : Après expiration de TOUTES les licences v1 — Retirer IRONLOCK_PUBLIC_KEY_RSA_V1 du loader Rebuilder sans la clé RSA """
[+] Re-chiffré : AES-256-GCM
[+] Migration payload v1 → v2 terminée
[+] Les licences RSA v1 existantes continuent de fonctionner
[+] Générer les nouvelles licences ECDSA P-384 lors des renouvellements
✓
Migration sans interruption garantie. Les clients avec licences v1 RSA continuent d'utiliser leur logiciel pendant toute la période de transition. Le loader v2 gère les deux formats simultanément.