Comment cacher une chaîne en code binary?

Parfois, il est utile de masquer une chaîne dans un fichier binary (exécutable). Par exemple, il est judicieux de masquer les clés de chiffrement à partir de fichiers binarys.

Quand je dis «cacher», je veux dire rendre les chaînes plus difficiles à trouver dans le binary compilé.

Par exemple, ce code:

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

après la compilation produit un fichier exécutable avec les éléments suivants dans sa section de données:

 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 | 

Vous pouvez voir que notre chaîne secrète peut être facilement trouvée et / ou modifiée.

Je pourrais cacher la ficelle…

 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'; 

… Mais ce n’est pas une bonne méthode. De meilleures idées?

PS: Je sais que le simple fait de cacher des secrets ne fonctionne pas contre un attaquant déterminé, mais c’est bien mieux que rien…

De plus, je connais le cryptage asymésortingque, mais ce n’est pas acceptable dans ce cas. Je suis en train de refactoriser une application existante qui utilise le cryptage Blowfish et transmet les données cryptées au serveur (le serveur décrypte les données avec la même clé).

Je ne peux pas modifier l’algorithme de chiffrement car je dois fournir une compatibilité descendante. Je ne peux même pas changer la clé de cryptage.

Comme indiqué dans le commentaire de la réponse de Pavium, vous avez deux choix:

  • Sécuriser la clé
  • Sécuriser l’algorithme de décryptage

Malheureusement, si vous devez recourir à l’incorporation de la clé et de l’algorithme dans le code, aucun de ces deux éléments n’est vraiment secret, vous avez donc l’option (beaucoup plus faible) de la sécurité par l’obscurité . En d’autres termes, comme vous l’avez mentionné, vous avez besoin d’un moyen astucieux pour les masquer ou les deux dans votre exécutable.

Voici quelques options, mais vous devez vous rappeler qu’aucune d’entre elles n’est vraiment sécurisée selon les meilleures pratiques cryptographiques et que chacune d’entre elles présente les inconvénients suivants:

  1. Déguisez votre clé en une chaîne qui apparaît normalement dans le code. Un exemple serait la chaîne de format d’une instruction printf() , qui a tendance à comporter des nombres, des lettres et des signes de ponctuation.
  2. Hacher certains ou tous les segments de code ou de données au démarrage et les utiliser comme clé. (Vous devez être un peu intelligent pour vous assurer que la clé ne change pas de manière inattendue!) Cela a un effet secondaire potentiellement souhaitable de vérifier la partie hachée de votre code à chaque exécution.
  3. Générez la clé à l’exécution à partir de quelque chose qui est unique (et constant dans le système), par exemple, en hachant l’adresse MAC d’une carte réseau.
  4. Créez la clé en choisissant des octets à partir d’autres données. Si vous avez des données statiques ou globales, quel que soit le type ( int , char , etc. ), prenez un octet quelque part dans chaque variable après son initialisation (à une valeur non nulle, bien sûr) et avant qu’elle ne change.

S’il vous plaît laissez-nous savoir comment vous résolvez le problème!

Edit: Vous avez dit que vous refactoriez le code existant, donc je suppose que vous ne pouvez pas nécessairement choisir la clé vous-même. Dans ce cas, suivez un processus en deux étapes: Utilisez l’une des méthodes ci-dessus pour chiffrer la clé elle-même, puis utilisez cette clé pour déchiffrer les données des utilisateurs.

Je suis désolé pour une longue réponse.

Vos réponses sont absolument correctes, mais la question était de savoir comment masquer la ficelle et le faire bien.

Je l’ai fait de telle manière:

 #include "HideSsortingng.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; } 

HideSsortingng.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 ligne la plus délicate de HideSsortingng.h est:

 BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)) 

Laisse moi exploser la ligne. Pour le code:

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

  BOOST_PP_SEQ_FOR_EACH_I (CRYPT_MACRO, SEED, SEQ) 

générer la séquence:

 ( '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)) 

produire:

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

et enfin,

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

produire:

 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; } 

les données pour "Ma clé de cryptage forte" ressemblent à:

 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......... 

Merci beaucoup pour vos réponses!

  1. Affichez-le comme un problème de code golf
  2. Attendez une solution écrite en J
  3. Intégrer un interpréteur J dans votre application

Cacher les mots de passe dans votre code est la sécurité par l’obscurité. Ceci est préjudiciable parce que vous pensez avoir un certain niveau de protection, alors qu’en réalité vous en avez très peu. Si quelque chose mérite d’être sécurisé, cela vaut la peine de le sécuriser correctement.

PS: Je sais que ça ne marche pas contre de vrais hackers, mais c’est bien mieux que rien …

En fait, dans beaucoup de situations, rien ne vaut une sécurité faible. Au moins, vous savez exactement où vous en êtes. Vous n’avez pas besoin d’être un “véritable pirate” pour contourner un mot de passe intégré …

EDIT: Répondre à ce commentaire:

Je connais des paires de clés, mais ce n’est pas acceptable dans ce cas. Je refactore les applications existantes qui utilisent le cryptage Blowfish. Les données chiffrées transmises au serveur et au serveur décryptent les données. Je ne peux pas modifier l’algorithme de cryptage car je devrais fournir une compatibilité ascendante.

Si vous vous souciez de la sécurité, le maintien de la compatibilité ascendante est une mauvaise raison de vous laisser vulnérable avec des mots de passe intégrés. C’est une bonne chose à briser la compatibilité avec un système de sécurité non sécurisé.

C’est comme lorsque les enfants de la rue découvrent que vous laissez la clé de la porte d’entrée sous le tapis, mais vous continuez à le faire parce que papy s’attend à le trouver là.

Votre exemple ne cache pas la chaîne du tout; la chaîne est toujours présentée comme une série de caractères dans la sortie.

Il existe une variété de façons de brouiller les chaînes. Il y a le cryptage de substitution simple, ou vous pouvez effectuer une opération mathématique sur chaque caractère (un XOR, par exemple) où le résultat alimente l’opération du prochain caractère, etc., etc.

Le but serait de se retrouver avec des données qui ne ressemblent pas à une chaîne de caractères, par exemple si vous travaillez dans la plupart des langues occidentales, la plupart de vos valeurs de caractères seront comsockets entre 32 et 127 – votre objective serait donc pour que l’opération les place principalement hors de cet intervalle, ils n’attirent donc pas l’attention.

C’est aussi sûr que de laisser votre vélo débloqué à Amsterdam, aux Pays-Bas, près de la gare centrale. (Clignote, et c’est parti!)

Si vous essayez d’append de la sécurité à votre application, vous êtes condamné à échouer dès le début, car tout système de protection échouera. Tout ce que vous pouvez faire est de rendre plus complexe pour un pirate de trouver les informations dont il a besoin. Encore quelques astuces:

*) Assurez-vous que la chaîne est stockée en UTF-16 dans votre fichier binary.

*) Ajouter des nombres et des caractères spéciaux à la chaîne.

*) Utilisez un tableau d’entiers de 32 bits au lieu d’une chaîne! Convertissez chacun en une chaîne et concaténez-les tous.

*) Utilisez un GUID, stockez-le en tant que binary et convertissez-le en chaîne à utiliser.

Et si vous avez vraiment besoin d’un texte prédéfini, chiffrez-le et stockez la valeur chiffrée dans votre fichier binary. Déchiffrez-le lors de l’exécution où la clé à déchiffrer est l’une des options que j’ai mentionnées précédemment.

Réalisez que les pirates auront tendance à craquer votre application autrement. Même un expert en cryptographie ne pourra pas garder quelque chose en sécurité. En général, la seule chose qui vous protège est le profit qu’un pirate peut tirer du piratage de votre code, comparé au coût du piratage. (Ces coûts seraient souvent très longs, mais si cela prend une semaine pour pirater votre application et seulement 2 jours pour pirater autre chose, il y a plus de chances que quelque chose d’autre soit attaqué.)


Répondre au commentaire: UTF-16 serait deux octets par caractère, donc plus difficile à reconnaître pour les utilisateurs qui regardent un vidage du binary, simplement parce qu’il y a un octet supplémentaire entre chaque lettre. Vous pouvez toujours voir les mots, cependant. UTF-32 serait même meilleur car il ajoute plus d’espace entre les lettres. Ensuite, vous pouvez également compresser un peu le texte en adoptant un schéma de 6 bits par caractère. Tous les 4 caractères seraient alors compacts à trois chiffres. Mais cela vous limiterait à 2×26 lettres, 10 chiffres et peut-être à l’espace et au point pour obtenir 64 caractères.

L’utilisation d’un GUID est pratique si vous stockez le GUID dans son format binary, pas son format textuel. Un GUID a une longueur de 16 octets et peut être généré de manière aléatoire. Il est donc difficile de deviner le GUID utilisé comme mot de passe. Mais si vous avez encore besoin d’envoyer du texte brut, un GUID pourrait être converti en une représentation de type chaîne de caractères comme “3F2504E0-4F89-11D3-9A0C-0305E82C3301”. (Ou encodé en Base64 en tant que “7QDBkvCA1 + B9K / U0vrQx1A ==”.) Mais les utilisateurs ne verront aucun texte en clair, juste des données apparemment aléatoires. Tous les octets d’un GUID ne sont pas aléatoires. Il y a un numéro de version caché dans les GUID. L’utilisation d’un GUID n’est toutefois pas la meilleure option à des fins de cryptographie. Il est calculé en fonction de votre adresse MAC ou d’un nombre pseudo-aléatoire, ce qui le rend raisonnablement prévisible. Cependant, il est facile à créer et à stocker, convertir et utiliser. Créer quelque chose de plus long n’ajoute pas plus de valeur car un pirate essaierait simplement de trouver d’autres astuces pour casser la sécurité. C’est juste une question sur leur volonté d’investir plus de temps dans l’parsing des binarys.

En général, la chose la plus importante qui protège vos applications est le nombre de personnes qui s’y intéressent. Si personne ne se soucie de votre candidature, personne ne prendra la peine de la pirater. Lorsque vous êtes le premier produit avec 500 millions d’utilisateurs, votre application est craquée en une heure.

J’étais dans une situation similaire. J’avais des données qui devaient être dans le binary mais pas en texte brut. Ma solution consistait à crypter les données en utilisant un schéma très simple qui le faisait ressembler au rest du programme. Je l’ai crypté en écrivant un programme qui a pris une chaîne, converti tous les caractères en code ASCII (rempli avec des zéros si nécessaire pour obtenir un numéro à trois chiffres), puis ajouté un chiffre aléatoire au début et à la fin du code à 3 chiffres . Ainsi, chaque caractère de la chaîne était représenté par 5 caractères (tous des nombres) dans la chaîne cryptée. J’ai collé cette chaîne dans l’application en tant que constante et puis, lorsque j’ai eu besoin d’utiliser la chaîne, j’ai décrypté et stocké le résultat dans une variable juste assez longue pour faire ce dont j’avais besoin.

Donc, pour utiliser votre exemple, “Ma clé de cryptage forte” devient “207719121310329211541116181145111157110071030703283101101109309926114151216611289116161056811109110470321510787101511213”. Ensuite, lorsque vous avez besoin de votre clé de cryptage, décodez-la mais annulez le processus.

Ce n’est certainement pas à l’épreuve des balles mais je ne visais pas cela.

La technologie de cryptage est suffisamment puissante pour sécuriser les données importantes sans les cacher dans un fichier binary.

Ou est-ce que votre idée d’utiliser un fichier binary pour masquer le fait que quelque chose est caché?

Cela s’appellerait la stéganographie .

C’est une application client-serveur! Ne le stockez pas dans le client lui-même, c’est à cet endroit que les pirates informatiques vont évidemment chercher. Ajoutez plutôt (pour votre nouveau client uniquement) une fonction de serveur supplémentaire (via HTTPS) pour récupérer ce mot de passe. Ainsi, ce mot de passe ne doit jamais bash le disque client.

En prime, il devient beaucoup plus facile de réparer le serveur plus tard. Envoyez simplement un mot de passe à durée limitée différent par client à chaque fois. N’oubliez pas d’autoriser des mots de passe plus longs dans votre nouveau client.

Vous pouvez encoder la chaîne en utilisant un encodage sortingvial, par exemple xor avec le binary 01010101. Aucune protection réelle bien sûr, mais déjoue l’utilisation d’outils comme ssortingng .

Si vous stockez la clé de cryptage à l’envers (“yek noitpyrcne gnorts yM”), puis l’inversez dans votre code (Ssortingng.Reverse), cela empêcherait une recherche simple dans le binary du texte de votre clé de cryptage.

Pour réitérer le point soulevé par toutes les autres affiches, cependant, cela ne vous apportera pratiquement rien en termes de sécurité.

Voici un exemple de ce qu’ils ont expliqué, mais sachez que cela sera tout simplement cassé par quiconque est un “hacker” mais arrêtera les kiddies avec un éditeur hexadécimal. L’exemple que j’ai fourni ajoute simplement la valeur 80 et soustrait l’index de celui-ci, puis crée à nouveau une chaîne. Si vous envisagez de stocker cela dans un fichier binary, il existe de nombreuses façons de convertir une chaîne en tableau d’octets.

Lorsque vous avez ce travail dans votre application, je ferais le “maths” un peu plus complexe

Pour que ce soit clair, pour ceux qui ne comprennent pas … Vous cryptez la chaîne avant de l’enregistrer afin qu’elle ne soit PAS enregistrée en texte clair. Si le texte crypté ne change jamais, vous n’incluez même pas la fonction encrypt dans votre version, vous avez juste le décrypteur. Ainsi, lorsque vous souhaitez déchiffrer la chaîne, vous lisez le fichier, puis vous décryptez le contenu. Cela signifie que votre chaîne ne sera jamais stockée dans un fichier au format texte brut.

Bien sûr, vous pouvez également stocker la chaîne cryptée sous forme de chaîne de constantes dans votre application et décrypter lorsque vous en avez besoin, choisissez ce qui vous convient le mieux en fonction de la taille de la chaîne et de sa fréquence.

 ssortingng Encrypted = EncryptMyssortingng("AAbbBb"); ssortingng Decrypted = DecryptMyssortingng(Encrypted); ssortingng DecryptMyssortingng(ssortingng RawStr) { ssortingng 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; } 

Pour C vérifier ceci: https://github.com/mafonya/c_hide_ssortingngs

Pour C ++ ceci:

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

Pour l’utiliser, il suffit d’inclure Alpha et:

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

Donc, mystr est “Test” maintenant et la chaîne est cachée de la table de chaînes en binary.

Vous pouvez utiliser une bibliothèque c ++ que j’ai développée à cette fin. Un autre article, beaucoup plus simple à mettre en œuvre, a remporté le titre de meilleur article de c ++ de septembre 2017.

Je pense que vous voulez le faire ressembler à des instructions, votre exemple de

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

Cela suffirait, la longue séquence d’instructions répétées avec une petite variation peut ressortir et ce serait mauvais, l’octet en question peut être codé tel quel dans l’instruction, ce qui serait mauvais, alors peut-être que la méthode xor, et peut-être d’autres astuces pour faire en sorte que cette longue section de code ne se démarque pas, certains appels de fonctions factices peut-être. Dépend de votre processeur, ARM par exemple, il est très facile de regarder les données binarys et de choisir les instructions à partir des données et de là (si vous recherchez une clé par défaut) pour éventuellement choisir ce qui pourrait être la clé car ce sont des données mais ce n’est pas ascii et ça attaque. De même, un bloc d’instructions similaires avec le champ immédiat variant, même si vous avez le compilateur ou les données avec une constante.

Je me demande si, après l’avoir d’abord obscurci comme d’autres l’ont mentionné, vous pourriez incorporer votre chaîne dans un bloc d’assemblage pour essayer de lui donner l’apparence d’instructions. Vous pourriez alors avoir un “if 0” ou un “goto just_past_ssortingng_assembly” pour sauter le “code” qui cache réellement votre chaîne. Cela nécessiterait probablement un peu plus de travail pour récupérer la chaîne dans le code (un coût de codage unique), mais cela pourrait s’avérer un peu plus obscur.

Crypter la clé de cryptage avec un autre code. Afficher une image de l’autre code pour l’utilisateur. Maintenant, l’utilisateur doit entrer la clé qu’il voit (comme un captcha, mais toujours le même code). Cela rend également impossible pour d’autres programmes de prévoir le code. Vous pouvez éventuellement enregistrer un hachage (salé) du code pour vérifier l’entrée de l’utilisateur.

Je suggère m4 .

  1. Stockez votre chaîne avec des macros comme la const ssortingng sPassword = _ENCRYPT("real password");

  2. Avant la construction, développez les macros en chaîne cryptée avec m4 , afin que votre code ressemble à une const ssortingng sPassword = "encrypted ssortingng";

  3. Déchiffrez dans l’environnement d’exécution.

créer une fonction qui assigne votre mot de passe à un tableau de caractères statique et renvoie un pointeur sur cette fonction. Ensuite, exécutez cette fonction via un programme d’obscurcissement.

Si le programme fait du bon travail il devrait être impossible de lire votre mot de passe en texte brut en utilisant un éditeur hexadécimal pour examiner le programme binary. (au moins, pas sans ingénierie inverse du langage d’assemblage. Cela devrait arrêter tous les scripteurs armés de “chaînes” ou d’éditeurs hexadécimaux, à l’exception du pirate informatique qui n’a rien de mieux à perdre son temps.)

Voici un script perl permettant de générer du code-c cale pour masquer un mot de passe en texte brut du programme “ssortingngs”.

  obfuscate_password("myPassword123"); sub obfuscate_password($) { my $ssortingng = shift; my @c = split(//, $ssortingng); 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;"; } } }