Come hide una stringa nel codice binario?

A volte, è utile hide una stringa da un file binario (eseguibile). Ad esempio, ha senso hide le chiavi di crittografia dai binari.

Quando dico “nascondi”, intendo rendere le stringhe più difficili da trovare nel binario compilato.

Ad esempio, questo codice:

const char* encryptionKey = "My strong encryption key"; // Using the key 

dopo la compilazione produce un file eseguibile con il seguente nella sua sezione dati:

 4D 79 20 73 74 72 6F 6E-67 20 65 6E 63 72 79 70 |My strong encryp| 74 69 6F 6E 20 6B 65 79 |tion key | 

Puoi vedere che la nostra stringa segreta può essere facilmente trovata e / o modificata.

Potrei hide la stringa …

 char encryptionKey[30]; int n = 0; encryptionKey[n++] = 'M'; encryptionKey[n++] = 'y'; encryptionKey[n++] = ' '; encryptionKey[n++] = 's'; encryptionKey[n++] = 't'; encryptionKey[n++] = 'r'; encryptionKey[n++] = 'o'; encryptionKey[n++] = 'n'; encryptionKey[n++] = 'g'; encryptionKey[n++] = ' '; encryptionKey[n++] = 'e'; encryptionKey[n++] = 'n'; encryptionKey[n++] = 'c'; encryptionKey[n++] = 'r'; encryptionKey[n++] = 'y'; encryptionKey[n++] = 'p'; encryptionKey[n++] = 't'; encryptionKey[n++] = 'i'; encryptionKey[n++] = 'o'; encryptionKey[n++] = 'n'; encryptionKey[n++] = ' '; encryptionKey[n++] = 'k'; encryptionKey[n++] = 'e'; encryptionKey[n++] = 'y'; 

… ma non è un buon metodo Qualche idea migliore?

PS: So che hide solo i segreti non funziona contro un determinato aggressore, ma è molto meglio di niente …

Inoltre, conosco la crittografia asimmetrica, ma in questo caso non è accettabile. Sto effettuando il refactoring di un’applicazione esistente che utilizza la crittografia Blowfish e trasmette i dati crittografati al server (il server decrittografa i dati con la stessa chiave).

Non riesco a modificare l’algoritmo di crittografia perché è necessario fornire la compatibilità con le versioni precedenti. Non riesco nemmeno a cambiare la chiave di crittografia.

Come notato nel commento alla risposta di Pavium, hai due scelte:

  • Proteggi la chiave
  • Proteggi l’algoritmo di decrittografia

Sfortunatamente, se si deve ricorrere a incorporare sia la chiave che l’algoritmo all’interno del codice, nessuno dei due è veramente segreto, quindi si rimane con l’alternativa (molto più debole) della sicurezza attraverso l’oscurità . In altre parole, come hai detto, hai bisogno di un modo intelligente per hide uno o entrambi all’interno del tuo eseguibile.

Ecco alcune opzioni, anche se è necessario ricordare che nessuno di questi è veramente sicuro secondo le migliori pratiche crittografiche, e ognuna ha i suoi svantaggi:

  1. Travestire la chiave come una stringa che normalmente apparirebbe all’interno del codice. Un esempio potrebbe essere la stringa di formato di un’istruzione printf() , che tende ad avere numeri, lettere e segni di punteggiatura.
  2. Inserisci alcuni o tutti i segmenti di codice o di dati all’avvio e utilizzalo come chiave. (Dovrai essere un po ‘furbo su questo per assicurarti che la chiave non cambi inaspettatamente!) Questo ha un effetto collaterale potenzialmente desiderabile di verificare la porzione di hash del tuo codice ogni volta che viene eseguito.
  3. Generare la chiave in fase di esecuzione da qualcosa che è unico (e costante all’interno) del sistema, ad esempio, eseguendo l’hashing dell’indirizzo MAC di una scheda di rete.
  4. Crea la chiave scegliendo i byte da altri dati. Se hai dati statici o globali, indipendentemente dal tipo ( int , char , ecc. ), Prendi un byte da qualche parte all’interno di ogni variabile dopo che è stato inizializzato (a un valore diverso da zero, ovviamente) e prima che cambi.

Per favore fateci sapere come risolvete il problema!

Modifica: hai commentato che stai rifattando il codice esistente, quindi presumo che tu non possa necessariamente scegliere la chiave tu stesso. In tal caso, seguire una procedura in due passaggi: utilizzare uno dei metodi sopra riportati per crittografare la chiave stessa, quindi utilizzare tale chiave per decodificare i dati degli utenti.

Mi dispiace per la lunga risposta.

Le tue risposte sono assolutamente corrette, ma la domanda era come hide la stringa e farlo bene.

L’ho fatto in questo modo:

 #include "HideString.h" DEFINE_HIDDEN_STRING(EncryptionKey, 0x7f, ('M')('y')(' ')('s')('t')('r')('o')('n')('g')(' ')('e')('n')('c')('r')('y')('p')('t')('i')('o')('n')(' ')('k')('e')('y')) DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t')) int main() { std::cout << GetEncryptionKey() << std::endl; std::cout << GetEncryptionKey2() << std::endl; return 0; } 

HideString.h:

 #include  #include  #include  #define CRYPT_MACRO(r, d, i, elem) ( elem ^ ( d - i ) ) #define DEFINE_HIDDEN_STRING(NAME, SEED, SEQ)\ static const char* BOOST_PP_CAT(Get, NAME)()\ {\ static char data[] = {\ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)),\ '\0'\ };\ \ static bool isEncrypted = true;\ if ( isEncrypted )\ {\ for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)\ {\ data[i] = CRYPT_MACRO(_, SEED, i, data[i]);\ }\ \ isEncrypted = false;\ }\ \ return data;\ } 

La maggior parte delle linee complicate in HideString.h è:

 BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)) 

Fammi esplorare la linea. Per il codice:

 DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t')) 

  BOOST_PP_SEQ_FOR_EACH_I (CRYPT_MACRO, SEED, SEQ) 

generare sequenza:

 ( 'T' ^ ( 0x27 - 0 ) ) ( 'e' ^ ( 0x27 - 1 ) ) ( 's' ^ ( 0x27 - 2 ) ) ( 't' ^ ( 0x27 - 3 ) ) 

  BOOST_PP_SEQ_ENUM (BOOST_PP_SEQ_FOR_EACH_I (CRYPT_MACRO, SEED, SEQ)) 

creare:

 'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 ) 

e infine,

 DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t')) 

creare:

 static const char* GetEncryptionKey2() { static char data[] = { 'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 ), '\0' }; static bool isEncrypted = true; if ( isEncrypted ) { for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i) { data[i] = ( data[i] ^ ( 0x27 - i ) ); } isEncrypted = false; } return data; } 

i dati per "La mia chiave di crittografia forte" è simile a:

 0x00B0200C 32 07 5d 0f 0f 08 16 16 10 56 10 1a 10 00 08 2.]......V..... 0x00B0201B 00 1b 07 02 02 4b 01 0c 11 00 00 00 00 00 00 .....K......... 

Grazie mille per le tue risposte!

  1. Pubblicalo come un problema di golf in codice
  2. Aspetta una soluzione scritta in J
  3. Incorpora un interprete J nella tua app

Nascondere le password nel tuo codice è sicurezza per oscurità. Questo è dannoso perché ti fa pensare di avere un certo livello di protezione, quando in effetti hai pochissimo. Se vale la pena assicurarsi qualcosa, vale la pena assicurarsi in modo appropriato.

PS: So che non funziona contro i veri hacker, ma è molto meglio di niente …

In realtà, in molte situazioni niente è meglio della debole sicurezza. Almeno sai esattamente dove ti trovi. Non è necessario essere un “vero hacker” per aggirare una password incorporata …

EDIT: rispondendo a questo commento:

So di coppie di chiavi, ma non è accettabile in questo caso. Riattivo l’appicazione esistente che utilizza la crittografia Blowfish. Dati crittografati passati a server e server decrittografano i dati. Non riesco a cambiare l’algoritmo di ecryption perché dovrei fornire la compatibilità con le versioni precedenti.

Se ti interessa la sicurezza, mantenere la compatibilità con le versioni precedenti è un motivo VERAMENTE CATTIVO per lasciarti vulnerabile con le password incorporate. È una BUONA COSA rompere la compatibilità con uno schema di sicurezza insicuro.

È come quando i ragazzi di strada scoprono di lasciare la chiave della porta d’ingresso sotto il tappetino, ma continui a farlo perché il nonno si aspetta di trovarlo lì.

Il tuo esempio non nasconde affatto la stringa; la stringa viene ancora presentata come una serie di caratteri nell’output.

Ci sono una varietà di modi in cui puoi confondere le stringhe. C’è la semplice sostituzione di cifratura , o potresti eseguire un’operazione matematica su ciascun personaggio (un XOR, ad esempio) dove il risultato si nutre dell’operazione del personaggio successivo, ecc. Ecc.

L’objective sarebbe finire con i dati che non assomigliano a una stringa, quindi ad esempio se lavori nella maggior parte delle lingue occidentali, la maggior parte dei valori dei tuoi personaggi sarà compresa nell’intervallo 32-127, quindi il tuo objective sarebbe perché l’operazione li metta per lo più fuori da quel raggio, quindi non attirano l’attenzione.

Questo è sicuro come lasciare la bicicletta sbloccata ad Amsterdam, nei Paesi Bassi, vicino alla stazione centrale. (Blink, ed è andato!)

Se si sta tentando di aggiungere sicurezza alla propria applicazione, si è destinati a fallire dall’inizio, poiché qualsiasi schema di protezione avrà esito negativo. Tutto quello che puoi fare è rendere più complesso per un hacker trovare le informazioni di cui ha bisogno. Ancora alcuni trucchi:

*) Assicurati che la stringa sia memorizzata come UTF-16 nel tuo binario.

*) Aggiungi numeri e caratteri speciali alla stringa.

*) Utilizza una matrice di numeri interi a 32 bit anziché una stringa! Converti ciascuno in una stringa e concatenali tutti.

*) Usa un GUID, memorizzalo come binario e convertilo in una stringa da usare.

E se hai davvero bisogno di un testo predefinito, criptalo e archivia il valore crittografato nel tuo binario. Decifralo in runtime dove la chiave per decrittografare è una delle opzioni che ho menzionato prima.

Ti rendi conto che gli hacker tendono a violare la tua applicazione in modi diversi da questo. Anche un esperto di crittografia non sarà in grado di mantenere qualcosa di sicuro. In generale, l’unica cosa che ti protegge è il profitto che un hacker può trarre dall’hacking del tuo codice, rispetto al costo di hackerarlo. (Questi costi sarebbero spesso solo un sacco di tempo, ma se ci vuole una settimana per hackerare la tua applicazione e solo 2 giorni per hackerare qualcos’altro, qualcos’altro è più probabile che venga attaccato.)


Rispondi al commento: UTF-16 sarebbe due byte per carattere, quindi più difficile da riconoscere per gli utenti che guardano un dump del file binario, semplicemente perché c’è un byte aggiuntivo tra ogni lettera. Puoi comunque vedere le parole, comunque. UTF-32 sarebbe anche meglio perché aggiunge più spazio tra le lettere. Inoltre, potresti anche comprimere un po ‘il testo passando a uno schema a 6 bit per carattere. Ogni 4 caratteri sarebbero quindi compatti a tre numeri. Ma questo potrebbe limitare a 2×26 lettere, 10 cifre e forse lo spazio e il punto per ottenere 64 caratteri.

L’uso di un GUID è pratico se si memorizza il GUID nel suo formato binario, non nel formato testuale. Un GUID è lungo 16 byte e può essere generato casualmente. Pertanto è difficile indovinare il GUID utilizzato come password. Ma se hai ancora bisogno di mandare un testo normale, un GUID può essere convertito in una rappresentazione di stringa per essere qualcosa come “3F2504E0-4F89-11D3-9A0C-0305E82C3301”. (O Base64-encoded come “7QDBkvCA1 + B9K / U0vrQx1A ==”.) Ma gli utenti non vedranno alcun testo in chiaro nel codice, solo alcuni dati apparentemente casuali. Tuttavia, non tutti i byte in un GUID sono casuali. C’è un numero di versione nascosto nei GUID. Tuttavia, l’utilizzo di un GUID non è l’opzione migliore per scopi crittografici. È calcolato in base al tuo indirizzo MAC o in base a un numero pseudo-casuale, rendendolo ragionevolmente prevedibile. Tuttavia, è facile da creare e facile da memorizzare, convertire e utilizzare. Creare qualcosa di più non aggiunge più valore dal momento che un hacker vorrebbe solo cercare altri trucchi per violare la sicurezza. È solo una domanda su quanto sono disposti a investire più tempo nell’analisi dei binari.

In generale, la cosa più importante che tiene al sicuro le tue applicazioni è il numero di persone interessate. Se a nessuno importa della tua applicazione, nessuno si prenderà la briga di hackerarlo. Quando sei il prodotto principale con 500 milioni di utenti, la tua applicazione si rompe entro un’ora.

Una volta ero in una posizione altrettanto imbarazzante. Avevo dati che dovevano essere nel binario ma non nel testo normale. La mia soluzione è stata quella di crittografare i dati utilizzando uno schema molto semplice che ha fatto sembrare il resto del programma. L’ho crittografato scrivendo un programma che ha preso una stringa, convertito tutti i caratteri nel codice ASCII (riempito di zeri secondo le necessità per ottenere un numero di tre cifre) e poi aggiunto una cifra casuale all’inizio e alla fine del codice a 3 cifre . Quindi ogni carattere della stringa era rappresentato da 5 caratteri (tutti i numeri) nella stringa crittografata. Ho incollato quella stringa nell’applicazione come costante e poi quando ho avuto bisogno di usare la stringa, ho decifrato e memorizzato il risultato in una variabile il tempo sufficiente per fare ciò che dovevo.

Quindi, per utilizzare il tuo esempio, “La mia chiave di crittografia forte” diventa “207719121310329211541116181111111111111116161212111116161056811109110470321510787101511213”. Quindi, quando hai bisogno della tua chiave di crittografia, decodificarla ma annullare la procedura.

Non è certamente a prova di proiettile, ma non miravo a quello.

La tecnologia di crittografia è abbastanza forte per proteggere i dati importanti senza nasconderlo in un file binario.

Oppure la tua idea di usare un file binario per hide il fatto che qualcosa è nascosto?

Quello si chiamerebbe steganografia .

È un’applicazione client-server! Non salvarlo nel client stesso, questo è il posto in cui gli hacker appariranno ovviamente. Invece, aggiungi (solo per il tuo nuovo client) una funzione server extra (su HTTPS) per recuperare questa password. Quindi questa password non dovrebbe mai colpire il disco del client.

Come bonus, diventa molto più semplice riparare il server in un secondo momento. Basta inviare ogni volta una password diversa, limitata al cliente. Non dimenticare di consentire password più lunghe nel tuo nuovo client.

È ansible codificare la stringa utilizzando alcune codifiche banali, ad esempio xor con binario 01010101. Naturalmente non esiste una protezione reale, ma blocca l’uso di strumenti come la string .

Se si memorizza la chiave di crittografia al contrario (“yek noitpyrcne gnorts yM”) e quindi si inverte il codice (String.Reverse), ciò impedirebbe una semplice ricerca attraverso il binario per il testo della chiave di crittografia.

Per ribadire il punto sollevato da ogni altro poster qui, tuttavia, ciò non comporterà praticamente nulla per te in termini di sicurezza.

Ecco un esempio di ciò che hanno spiegato, ma sappiate che questo sarà semplicemente rotto da chiunque sia un “hacker”, ma fermerà i bambini con un editor esadecimale. L’esempio che ho fornito aggiunge semplicemente il valore 80 e sottotraccia l’indice da esso e quindi crea nuovamente una stringa. Se si progetta di archiviarlo in un file binario, ci sono molti modi per convertire una stringa in una matrice di byte [].

Quando hai questo lavoro nella tua app, vorrei rendere la “matematica” che ho usato un po ‘più complesso

Per chiarire, per coloro che non capiscono …. Si cripta la stringa prima di salvarla in modo che NON venga salvata in chiaro. Se il testo crittografato non cambierà mai, non includerai nemmeno la funzione di crittografia nella tua versione, hai solo la decrittografia. Quindi, quando si desidera decodificare la stringa, si legge il file e quindi si decodifica il contenuto. Significa che la tua stringa non verrà mai memorizzata su file in formato testo normale.

Ovviamente puoi anche avere la stringa crittografata memorizzata come stringa di costanti nell’applicazione e decrittografarla quando ti serve, scegliere ciò che è giusto per te, a seconda delle dimensioni della stringa e della frequenza con cui cambia.

 string Encrypted = EncryptMystring("AAbbBb"); string Decrypted = DecryptMystring(Encrypted); string DecryptMystring(string RawStr) { string DecryptedStr = ""; for (int i = 0; i < RawStr.Length; i++) { DecryptedStr += (char)((int)RawStr[i] - 80 + i); } return DecryptedStr; } string EncryptMystring(string RawStr) { string EncryptedStr = ""; for (int i = 0; i < RawStr.Length; i++) { EncryptedStr += (char)((int)RawStr[i] + 80 - i); } return EncryptedStr; } 

Per C controlla questo: https://github.com/mafonya/c_hide_strings

Per C ++ questo:

 class Alpha : public std::string { public: Alpha(string str) { std::string phrase(str.c_str(), str.length()); this->assign(phrase); } Alpha c(char c) { std::string phrase(this->c_str(), this->length()); phrase += c; this->assign(phrase); return *this; } }; 

Per poterlo utilizzare, includi solo Alpha e:

 Alpha str(""); string myStr = str.c('T').c('e').c('s').c('t'); 

Quindi mystr è “Test” ora e la stringa è nascosta dalla tabella delle stringhe in binario.

È ansible utilizzare una libreria c ++ che ho sviluppato per questo scopo. Un altro articolo che è molto più semplice da implementare, ha vinto come miglior articolo c ++ di settembre 2017.

Penso che tu voglia far sembrare istruzioni, il tuo esempio di

x [y ++] = ‘M’; x [y ++] = ‘y’; …

Farebbe proprio questo, la lunga sequenza di istruzioni ripetute con una piccola variazione potrebbe risaltare e che sarebbe male, il byte in questione potrebbe essere codificato nell’istruzione così com’è e sarebbe male, quindi forse il metodo xor, e forse alcuni altri trucchi per rendere non visibile la lunga sezione di codice, forse alcune chiamate fittizie. Dipende anche dal tuo processore, ARM per esempio è davvero facile guardare i dati binari e scegliere le istruzioni dai dati e da lì (se stai cercando una chiave predefinita) per scegliere quale potrebbe essere la chiave perché è dati ma non è ascii e attacca quello. Allo stesso modo un blocco di istruzioni simili con il campo immediato varia, anche se il compilatore ha i dati con una costante.

Mi chiedo se dopo averlo prima oscurato come altri hanno già detto, potresti incorporare la tua stringa in un blocco di assiemi per provare a farlo sembrare istruzioni. Potresti quindi avere un “se 0” o “goto just_past_string_assembly” per saltare sopra il “codice” che in realtà nasconde la tua stringa. Ciò richiederebbe probabilmente un po ‘più di lavoro per recuperare la stringa nel codice (un costo di codifica una tantum), ma potrebbe rivelarsi un po’ più oscuro.

Cripta la chiave di crittografia con un altro codice. Mostra un’immagine dell’altro codice all’utente. Ora l’utente deve inserire la chiave che vede (come un captcha, ma sempre lo stesso codice). Ciò rende imansible anche per altri programmi prevedere il codice. Opzionalmente è ansible salvare un hash (salato) del codice per verificare l’input dell’utente.

Suggerisco m4 .

  1. Memorizza la stringa con macro come const string sPassword = _ENCRYPT("real password");

  2. Prima della compilazione, espandi le macro alla stringa crittografata con m4 , in modo che il tuo codice assomigli a const string sPassword = "encrypted string";

  3. Decifrare in ambiente runtime.

crea una funzione che assegna la tua password a un array di caratteri statici e restituisce un puntatore a questa funzione. Quindi eseguire questa funzione attraverso un programma di offuscamento.

Se il programma fa un buon lavoro. dovrebbe essere imansible leggere la password del testo in chiaro usando un editor esadecimale per esaminare il programma binario. (almeno, non senza il reverse engineering del linguaggio assembly.Questo dovrebbe fermare tutti gli script kiddies armati con “stringhe” o editor esadecimali, tranne che per l’hacker criminalmente pazzo che non ha niente di meglio da perdere tempo.)

Ecco uno script perl per generare un c-code offuscato per hide una password in chiaro dal programma “stringhe”.

  obfuscate_password("myPassword123"); sub obfuscate_password($) { my $string = shift; my @c = split(//, $string); push(@c, "skip"); # Skip Null Terminator # using memset to clear this byte # Add Decoy Characters for($i=0; $i < 100; $i++) { $ch = rand(255); next if ($ch == 0); push(@c, chr($ch)); } my $count1 = @c; print " int x1, x2, x3, x4;\n"; print " char password[$count1];\n"; print " memset(password, 0, $count1);\n"; my $count2 = 0; my %dict = (); while(1) { my $x = int(rand($count1)); $y = obfuscate_expr($count1, $x); next if (defined($dict{$x})); $dict{$x} = 1; last if ($count2+1 == $count1); if ($c[$x] ne "skip") { #print " $y\n"; print " $y password[x4] = (char)" . ord($c[$x]) . ";\n"; } $count2++; } } sub obfuscate_expr($$) { my $count = shift; my $target = shift; #return $target; while(1) { my $a = int(rand($count*2)); my $b = int(rand($count*2)); my $c = int(rand($count*2)); next if (($a == 0) || ($b == 0) || ($c == 0)); my $y = $a - $b; #print "$target: $y : $a - $b\n"; if ($y == $target) { #return "$a - $b + $c"; return "x1=$a; x2=$b; x3=$c; x4=x1-x2+x3; x5= +=x4;"; } } }