Skip to Content
Nuova versione 12 disponibile 🎉

Algoritmi standard

Babel Obfuscator include tre algoritmi integrati di cifratura delle stringhe: XOR, HASH e STREAM. Tutti e tre sono autonomi (decifrano senza alcun input esterno), quindi la scelta è un compromesso tra tempo di caricamento, dimensione dell’output e difficoltà di recupero delle stringhe. Questa pagina descrive ogni algoritmo e come abilitarlo; la tabella di confronto alla fine ne riassume le differenze.

Algoritmo XOR

L’algoritmo XOR usato da Babel Obfuscator per la cifratura delle stringhe è semplice: applica lo XOR tra i caratteri delle stringhe inline e una chiave intera casuale. Questo metodo ha il vantaggio di una decifratura più veloce e di un tempo di caricamento inferiore rispetto ad altri algoritmi di cifratura.

Riga di comando

Per configurare l’algoritmo di cifratura XOR per la tua applicazione con Babel Obfuscator dalla riga di comando, usa questo semplice comando:

babel myapp.exe --string xor

Questo comando indica a Babel di applicare la cifratura XOR alle stringhe inline dell’eseguibile della tua applicazione, sfruttando la semplicità e la velocità dell’algoritmo XOR per offuscare le stringhe in modo sicuro.

Task Babel di MSBuild

Configurare l’algoritmo XOR per la cifratura delle stringhe nel tuo progetto con il task Babel di MSBuild è semplice. Aggiungi al tuo file di progetto MSBuild un gruppo di proprietà e un task Babel con la configurazione specifica per il metodo di cifratura XOR:

<PropertyGroup> <StringEncryption>xor</StringEncryption> </PropertyGroup> <Babel StringEncryption="$(StringEncryption)" />

Con questa configurazione Babel Obfuscator applica l’algoritmo di cifratura XOR alle stringhe del tuo progetto, rendendole più sicure e più resistenti ai tentativi di manomissione e di reverse engineering.

Algoritmo HASH

L’algoritmo HASH usato da Babel Obfuscator si basa su tabelle hash indirizzate da chiavi intere. Questo algoritmo comprime e cifra i dati delle stringhe, il che può contribuire a ridurne la dimensione complessiva su file. Tuttavia, poiché i dati compressi delle stringhe devono essere decifrati in fase di esecuzione, può incidere sul tempo di caricamento dell’applicazione. Rispetto all’algoritmo XOR, che applica lo XOR tra i caratteri delle stringhe inline e una chiave intera casuale, l’algoritmo HASH offre una protezione migliore contro gli attacchi di decifratura delle stringhe, ma al costo di un tempo di caricamento più lungo.

Riga di comando

Per impostare la cifratura HASH con Babel Obfuscator dalla CLI, usa questo comando:

babel myapp.exe --string hash

Task Babel di MSBuild

Per abilitare la cifratura HASH con il task MSBuild di Babel, imposta la proprietà StringEncryption come segue:

<PropertyGroup> <StringEncryption>hash</StringEncryption> </PropertyGroup> <Babel StringEncryption="$(StringEncryption)" />

Con l’algoritmo HASH integrato in questo modo, le stringhe del tuo progetto vengono adeguatamente protette durante il processo di build.

Algoritmo STREAM Ultimate

L’algoritmo STREAM è l’opzione moderna di Babel Obfuscator per la cifratura autenticata delle stringhe. Cifra ogni stringa singolarmente con un cifrario robusto, standard di settore, e verifica un tag di autenticazione prima di restituirla. Questo dà a STREAM quattro proprietà che gli algoritmi precedenti non hanno:

  • Decifratura differita (lazy), stringa per stringa. Una stringa viene decifrata solo la prima volta che viene effettivamente usata, e il risultato viene messo in cache. A differenza di HASH, STREAM non decifra mai l’intero insieme in anticipo, quindi un dump della memoria espone solo le poche stringhe che l’applicazione ha già toccato: non esiste un momento in cui tutte le stringhe si trovano in memoria in chiaro.
  • Integrità e randomizzazione per stringa. Il tag di autenticazione rileva qualsiasi manomissione dei dati cifrati, e due stringhe identiche vengono cifrate in byte diversi, il che vanifica l’analisi di frequenza e di uguaglianza a cui un semplice schema XOR si presta.
  • Una chiave che non è mai memorizzata inline. La chiave non è conservata come valore letterale accanto alla chiamata di decifratura, quindi un deoffuscatore automatico deve eseguire il codice reale per recuperare una stringa, invece di limitarsi a leggere una chiave accanto alla chiamata.
  • Decifratura legata al contesto di chiamata del tuo codice. Eseguire il decifratore non basta: restituisce una stringa solo a un chiamante in esecuzione all’interno dello stesso assembly protetto (o all’interno del runtime .NET, così i percorsi LINQ/PLINQ e asincroni continuano a funzionare). Il metodo abituale con cui un deoffuscatore automatico estrae con un solo comando tutte le stringhe cifrate in una volta, cioè associare un delegate direttamente al decifratore e invocarlo in blocco dal proprio assembly di supporto, viene respinto. Questo non rende impossibile recuperare le stringhe con un’analisi dinamica completa, ma elimina la via economica dell’estrazione in blocco e costringe un attaccante a far eseguire il tuo codice reale.

Il decifratore è scritto interamente in codice gestito, senza alcuna dipendenza dal provider crittografico della piattaforma. Di conseguenza STREAM funziona senza modifiche da .NET Framework 4.x (e 2.0) fino a .NET 10, su Mono/Xamarin e, poiché non usa Reflection.Emit, nelle applicazioni NativeAOT e sottoposte a trimming. Inoltre non risente del problema di avvio legato a FIPS che può interessare gli algoritmi basati sulla piattaforma.

STREAM è autonomo: come XOR e HASH decifra senza alcun input esterno. Essendo uno schema solo locale, non può rendere impossibile il recupero delle stringhe con un’analisi dinamica completa (nessuno schema autonomo può farlo), ma porta il costo sia dell’estrazione automatica sia del reverse engineering manuale ben al di sopra di XOR e HASH.

Piattaforme e framework supportati

Poiché il decifratore è interamente gestito e non usa Reflection.Emit, codice dinamico, analisi dello stack o il provider crittografico della piattaforma, STREAM è stato verificato su tutta la gamma .NET e sui dispositivi mobili:

  • Da .NET Framework 2.0 / 4.x fino a .NET 10, .NET Core e Mono / Xamarin.
  • Applicazioni sottoposte a trimming (ILLink) e NativeAOT: il decifratore iniettato e la sua tabella di stringhe incorporata sopravvivono al linking, e il codice viene compilato ahead-of-time senza JIT.
  • Android e iOS: verificato da un capo all’altro con vere app offuscate sull’emulatore Android e sul simulatore iOS, comprese le build Release sottoposte a trimming completo. Le stringhe vengono decifrate in fase di esecuzione e nell’assembly del pacchetto non resta testo in chiaro. .NET MAUI si basa su questi stessi runtime (net*-android / net*-ios), quindi vale lo stesso supporto. La compilazione full-AOT sui dispositivi iOS è coperta dalla compatibilità con NativeAOT descritta sopra.

Sui dispositivi mobili, applica STREAM come passaggio di sola cifratura delle stringhe, così i nomi dei tipi e dei membri restano intatti per lo strato di interoperabilità Java / Objective-C:

babel MyApp.dll --stringencryption stream --notypes --nomethods --nofields --noproperties --noevents --noparameters --novirtualmembers

Quando Babel viene eseguito come passo di MSBuild all’interno di una build .NET Android / iOS / MAUI, aggancialo dopo la compilazione dell’assembly dell’app e prima della creazione del pacchetto, così l’app nel pacchetto contiene l’assembly offuscato.

Riga di comando

Per applicare la cifratura STREAM dalla riga di comando:

babel myapp.exe --string stream

Task Babel di MSBuild

Per abilitare la cifratura STREAM con il task MSBuild di Babel, imposta la proprietà StringEncryption come segue:

<PropertyGroup> <StringEncryption>stream</StringEncryption> </PropertyGroup> <Babel StringEncryption="$(StringEncryption)" />

STREAM funziona insieme alle altre protezioni. Puoi combinarlo con la cifratura del codice (MSIL) e con il rilevamento delle manomissioni sullo stesso assembly.

Rilevamento delle manomissioni e riscrittura dopo la build. Il rilevamento delle manomissioni imprime un hash di integrità dell’immagine offuscata. Qualsiasi passo che riscrive l’assembly dopo Babel, in particolare una pubblicazione con trimming o NativeAOT, che emette di nuovo l’assembly gestito, modifica quei byte e fa fallire subito il controllo all’avvio. È una caratteristica intrinseca del rilevamento delle manomissioni e non dipende dall’algoritmo delle stringhe (riguarda allo stesso modo XOR, HASH e STREAM): abilita il rilevamento delle manomissioni sulle build che non vengono poi riscritte dal trimming o dall’AOT, e affidati alla cifratura delle stringhe (più la cifratura del codice) per gli output con trimming o AOT.

Confronto tra gli algoritmi

Tutti e tre gli algoritmi integrati sono autonomi (decifrano senza alcun input esterno), quindi la scelta è un compromesso tra tempo di caricamento, dimensione dell’output e difficoltà di recupero delle stringhe.

XORHASHSTREAM
CifrarioXOR inline con una chiave intera per stringaTabella compressa cifrata con il cifrario della piattaformaCifrario autenticato per stringa, standard di settore
Stringhe mantenute come valori letteraliSì (rimescolate sul posto)NoNo
Modello di decifraturaPer stringa, a ogni accessoAnticipato: l’intera tabella viene decifrata al primo utilizzoDifferito: ogni stringa al primo utilizzo, poi in cache
Testo in chiaro tenuto in memoriaUna stringa alla voltaL’intera tabella, per tutta l’esecuzioneSolo le stringhe effettivamente usate
Tag di integrità per stringa——Sì
Stringhe identiche cifrate in modo diversoNoNoSì
Rifiuta la decifratura fuori contesto (invocazione tramite delegate)——Sì
Chiave di decifraturaInline, accanto alla chiamataMemorizzata nell’intestazione del blobMai inline: ricostruita in fase di esecuzione e intrecciata nel flusso di controllo
Decifratore interamente gestito (nessun provider crittografico della piattaforma; compatibile con FIPS)NoNoSì
Dimensione relativa dell’outputLa maggiore (i valori letterali restano nell’assembly)La minore (un unico blob compresso)Tra XOR e HASH
Costo relativo in fase di esecuzioneIl più bassoBasso, ma pagato in anticipo per ogni stringaPiù alto per stringa, pagato solo per le stringhe usate

Quale scegliere.

  • XOR: il più veloce e leggero, ma anche il più debole. Le stringhe cifrate restano come valori letterali nell’assembly e accanto a ogni chiamata si trova una chiave per stringa, quindi uno strumento automatico può recuperarle in un solo passaggio. Sceglilo quando il tempo di caricamento conta più della protezione.
  • HASH: elimina i valori letterali e raccoglie l’intero insieme in un unico blob compresso e cifrato (l’output più piccolo), con protezione dalle manomissioni sulla tabella. Il costo è che l’intera tabella viene decifrata in anticipo al primo utilizzo, quindi tutte le stringhe restano in memoria in chiaro per il resto dell’esecuzione, e che il suo decifratore dipende dal provider crittografico della piattaforma (vedi la nota su FIPS).
  • STREAM: il più robusto dei tre contro l’analisi sia statica sia dinamica. Offre cifratura autenticata per stringa, nessuna decifratura globale anticipata, nessuna chiave inline e un unico decifratore interamente gestito, che viene pubblicato senza modifiche con NativeAOT e trimming e non risente di FIPS. Paghi un costo di decifratura più alto per stringa, ma solo per le stringhe che l’applicazione tocca davvero, e in nessun momento l’insieme completo delle stringhe si trova in chiaro. Preferisci STREAM per .NET moderno, per le distribuzioni AOT, con trimming o FIPS, e ovunque conti soprattutto la resistenza all’estrazione in blocco delle stringhe.
Last updated on