c++ - una - pasar de número entero a numero binario con recursividad
¿Cómo ocultar una cadena en código binario? (20)
- Publicarlo como un problema de golf de código
- Espere una solución escrita en J
- Incruste un intérprete J en su aplicación
A veces, es útil ocultar una cadena de un archivo binario (ejecutable). Por ejemplo, tiene sentido ocultar las claves de cifrado de los binarios.
Cuando digo "ocultar", me refiero a hacer las cadenas más difíciles de encontrar en el binario compilado.
Por ejemplo, este código:
const char* encryptionKey = "My strong encryption key";
// Using the key
después de la compilación produce un archivo ejecutable con lo siguiente en su sección de datos:
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 |
Puedes ver que nuestra cadena secreta se puede encontrar y / o modificar fácilmente.
Podría esconder la cuerda ...
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'';
... pero no es un buen método. Alguna mejor idea?
PD: Sé que simplemente ocultar secretos no funciona contra un atacante determinado, pero es mucho mejor que nada ...
Además, sé sobre encriptación asimétrica, pero no es aceptable en este caso. Estoy refacturando una aplicación existente que utiliza encriptación Blowfish y pasa datos encriptados al servidor (el servidor descifra los datos con la misma clave).
No puedo cambiar el algoritmo de cifrado porque necesito proporcionar compatibilidad con versiones anteriores. Ni siquiera puedo cambiar la clave de encriptación.
¡Es una aplicación cliente-servidor! No lo guarde en el cliente, ese es el lugar donde los hackers obviamente se verán. En su lugar, agregue (para su nuevo cliente solamente) una función de servidor adicional (a través de HTTPS) para recuperar esta contraseña. Por lo tanto, esta contraseña nunca debe golpear el disco del cliente.
Como beneficio adicional, se vuelve mucho más fácil arreglar el servidor más tarde. Simplemente envíe una contraseña diferente, por cada cliente, de tiempo limitado cada vez. No olvides permitir contraseñas más largas en tu nuevo cliente.
Aquí hay un ejemplo de lo que explicaron, pero ten en cuenta que cualquiera que sea un "hacker" simplemente lo romperá, pero detendrá a los niños con un editor hexadecimal. El ejemplo que proporcioné simplemente agrega el valor 80 y lo subraya desde el índice y luego vuelve a hacer una cadena. Si planea almacenar esto en un archivo binario, existen muchas formas de convertir una cadena en una matriz de bytes [].
Cuando tengas esto trabajando en tu aplicación, haré que las "matemáticas" las use un poco más complejas
Para que quede claro, para aquellos que no entienden ... Encriptas la cadena antes de guardarla para que NO se guarde en texto claro. Si el texto encriptado nunca va a cambiar, ni siquiera incluye la función de cifrado en su versión, solo tiene que descifrarla. Entonces, cuando desee descifrar la cadena, lea el archivo y luego descifre el contenido. Lo que significa que su cadena nunca se almacenará en el archivo en formato de texto plano.
También puede, por supuesto, tener la cadena encriptada almacenada como una cadena de constantes en su aplicación y descifrarla cuando la necesite, elija lo que es correcto para su problema dependiendo del tamaño de la cadena y la frecuencia con la que 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;
}
Aquí hay una secuencia de comandos de Perl para generar código c ofuscado para ocultar una contraseña de texto plano del programa "cadenas".
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;";
}
}
}
Como se señaló en el comentario a la answer de pavium, tiene dos opciones:
- Asegure la clave
- Asegure el algoritmo de descifrado
Desafortunadamente, si debe recurrir a la incrustación de la clave y el algoritmo dentro del código, ninguno de los dos es realmente secreto, por lo que se queda con la alternativa (mucho más débil) de seguridad a través de la oscuridad . En otras palabras, como mencionaste, necesitas una forma ingeniosa de ocultar una o ambas dentro de tu ejecutable.
Aquí hay algunas opciones, aunque debe recordar que ninguna de ellas es realmente segura según las mejores prácticas criptográficas, y cada una tiene sus inconvenientes:
- Disfraza tu llave como una cadena que normalmente aparecería dentro del código. Un ejemplo sería la cadena de formato de una instrucción
printf()
, que tiende a tener números, letras y signos de puntuación. - Hash una Hash algunos o todos los segmentos de código o datos al inicio y úselos como la clave. (¡Necesitará ser un poco inteligente sobre esto para asegurarse de que la clave no cambie inesperadamente!) Esto tiene un efecto secundario potencialmente deseable de verificar la parte hash de su código cada vez que se ejecuta.
- Genere la clave en tiempo de ejecución a partir de algo que sea único (y constante dentro) del sistema, por ejemplo, al calcular hash la dirección MAC de un adaptador de red.
- Cree la clave eligiendo bytes de otros datos. Si tiene datos estáticos o globales, independientemente del tipo (
int
,char
, etc. ), tome un byte de algún lugar dentro de cada variable después de que se haya inicializado (a un valor distinto de cero, por supuesto) y antes de que cambie.
Por favor, háganos saber cómo resolver el problema!
Editar: Comentó que está refaccionando el código existente, por lo que supongo que no necesariamente puede elegir la clave usted mismo. En ese caso, siga un proceso de 2 pasos: use uno de los métodos anteriores para encriptar la clave en sí, luego use esa clave para descifrar los datos de los usuarios.
Creo que quieres que parezca instrucciones, tu ejemplo de
x [y ++] = ''M''; x [y ++] = ''y''; ...
Haría exactamente eso, la larga secuencia de instrucciones repetidas con una pequeña variación podría sobresalir y eso sería malo, el byte en cuestión podría codificarse en la instrucción tal como está y eso sería malo, entonces tal vez el método xor, y tal vez algunos otros trucos para hacer que esa larga sección de código no se destaque, tal vez algunas llamadas ficticias. Depende también de tu procesador, ARM, por ejemplo, es muy fácil ver los datos binarios y seleccionar las instrucciones de los datos y desde allí (si estás buscando una clave predeterminada) para elegir cuál podría ser la clave, porque son datos pero no son ascii y atacan eso. Del mismo modo, un bloque de instrucciones similares con la variación de campo inmediata, incluso si tiene el compilador xor los datos con una constante.
Encripta la clave de cifrado con otro código. Muestra una imagen del otro código al usuario. Ahora el usuario tiene que ingresar la clave que ve (como un captcha, pero siempre el mismo código). Esto hace que sea también imposible para otros programas predecir el código. Opcionalmente puede guardar un hash (salado) del código para verificar la entrada del usuario.
Esto es tan seguro como dejar su bicicleta desbloqueada en Amsterdam, Países Bajos, cerca de la Estación Central. (Parpadea, y se ha ido!)
Si está intentando agregar seguridad a su aplicación, está condenado a fallar desde el principio, ya que cualquier esquema de protección fallará. Todo lo que puede hacer es hacer que sea más complejo para un hacker encontrar la información que necesita. Aún así, algunos trucos:
*) Asegúrese de que la cadena esté almacenada como UTF-16 en su binario.
*) Agregue números y caracteres especiales a la cadena.
*) ¡Usa una matriz de enteros de 32 bits en lugar de una cadena! Convierta cada una en una cadena y concatenelas todas.
*) Use un GUID, almacénelo como binario y conviértalo en una cadena para usar.
Y si realmente necesita un texto predefinido, encripte y almacene el valor encriptado en su binario. Descifrarlo en tiempo de ejecución donde la clave para descifrar es una de las opciones que he mencionado antes.
Tenga en cuenta que los piratas informáticos tenderán a descifrar su aplicación de otras formas que esto. Incluso un experto en criptografía no podrá mantener algo seguro. En general, lo único que lo protege es la ganancia que un pirata informático puede obtener pirateando su código, en comparación con el costo de piratearlo. (Estos costos a menudo son solo un montón de tiempo, pero si se tarda una semana en hackear su aplicación y solo 2 días en hackear otra cosa, es más probable que se ataque otra cosa).
Respuesta para comentar: UTF-16 sería dos bytes por carácter, por lo tanto, más difícil de reconocer para los usuarios que miran un volcado del binario, simplemente porque hay un byte adicional entre cada letra. Aún así puedes ver las palabras. UTF-32 sería incluso mejor porque agrega más espacio entre las letras. Por otra parte, también puede comprimir el texto un poco cambiando a un esquema de 6 bits por carácter. Cada 4 caracteres se compactarían a tres números. Pero esto le restringiría a 2x26 letras, 10 dígitos y quizás el espacio y el punto para obtener 64 caracteres.El uso de un GUID es práctico si almacena el GUID en su formato binario, no en su formato de texto. Un GUID tiene 16 bytes de largo y se puede generar aleatoriamente. Por lo tanto, es difícil adivinar el GUID que se usa como contraseña. Pero si aún necesita enviar texto sin formato, un GUID podría convertirse en una representación de cadena para que sea algo así como "3F2504E0-4F89-11D3-9A0C-0305E82C3301". (O codificado en Base64 como "7QDBkvCA1 + B9K / U0vrQx1A ==".) Pero los usuarios no verán ningún texto simple en el código, solo algunos datos aparentemente aleatorios. Sin embargo, no todos los bytes en un GUID son aleatorios. Hay un número de versión oculto en los GUID. Sin embargo, el uso de un GUID no es la mejor opción para fines criptográficos. Se calcula según su dirección MAC o por un número pseudoaleatorio, por lo que es razonablemente predecible. Aún así, es fácil de crear y fácil de almacenar, convertir y usar. Crear algo más tiempo no agrega más valor ya que un hacker simplemente intentaría encontrar otros trucos para descifrar la seguridad. Es solo una pregunta sobre qué tan dispuestos están a invertir más tiempo en analizar los binarios.
En general, lo más importante que mantiene seguras sus aplicaciones es la cantidad de personas que están interesadas en ello. Si a nadie le importa tu aplicación, entonces nadie se molestará en hackearla tampoco. Cuando eres el mejor producto con 500 millones de usuarios, tu aplicación se descifra en una hora.
La tecnología de cifrado es lo suficientemente fuerte como para proteger datos importantes sin ocultarlo en un archivo binario.
¿O es su idea utilizar un archivo binario para ocultar el hecho de que algo está oculto?
Eso se llamaría steganography .
Lo siento por la respuesta larga.
Sus respuestas son absolutamente correctas, pero la pregunta era cómo ocultar la cuerda y hacerlo bien.
Lo hice de esa manera:
#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 <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#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 línea más difícil en HideString.h es:
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ))
Me permite explicar la línea. Para el código:
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, (''T'')(''e'')(''s'')(''t''))
BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ) generar secuencia:
( ''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)) generar:
''T'' ^ ( 0x27 - 0 ), ''e'' ^ ( 0x27 - 1 ), ''s'' ^ ( 0x27 - 2 ), ''t'' ^ ( 0x27 - 3 )
y finalmente,
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, (''T'')(''e'')(''s'')(''t''))
generar:
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;
}
datos para "Mi clave de encriptación fuerte" se ve así:
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.........
¡Muchas gracias por sus respuestas!
Me pregunto si después de oscurecerlo por primera vez como otros lo han mencionado, podrías insertar tu cadena en un bloque de ensamblaje para intentar que parezca instrucciones. Podrías tener un "if 0" o "goto just_past_string_assembly" para saltar sobre el "código" que realmente está ocultando tu cadena. Esto probablemente requerirá un poco más de trabajo para recuperar la cadena en el código (un costo de codificación de una sola vez), pero podría ser un poco más oscuro.
Ocultar las contraseñas en su código es seguridad por oscuridad. Esto es dañino porque te hace pensar que tienes cierto nivel de protección, cuando en realidad tienes muy poco. Si algo vale la pena asegurar, vale la pena asegurarlo adecuadamente.
PD: Sé que no funciona contra el pirata informático real, pero es mucho mejor que nada ...
En realidad, en muchas situaciones, nada es mejor que una seguridad débil. Al menos sabes exactamente dónde estás parado. No necesita ser un "hacker real" para eludir una contraseña incrustada ...
EDITAR: Respondiendo a este comentario:
Sé sobre pares de llaves, pero no es aceptable en este caso. Refactorizando las aplicaciones existentes que usan el cifrado Blowfish. Datos encriptados pasados al servidor y datos de descifrado del servidor. No puedo cambiar el algoritmo de ecryption porque debería proporcionar compatibilidad con versiones anteriores.
Si te preocupas por la seguridad en absoluto, mantener la compatibilidad con versiones anteriores es una razón REALMENTE MAL para que te vuelvas vulnerable con las contraseñas incorporadas. Es BUENO que rompa la compatibilidad con un esquema de seguridad inseguro.
Es como cuando los niños de la calle descubren que dejas la llave de la puerta de entrada debajo de la alfombra, pero sigues haciéndolo porque el abuelo espera encontrarla allí.
Para C mira esto: https://github.com/mafonya/c_hide_strings
Para C ++ esto:
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;
}
};
Para usar esto, solo incluye Alpha y:
Alpha str("");
string myStr = str.c(''T'').c(''e'').c(''s'').c(''t'');
Entonces mystr es "Prueba" ahora y la cadena está oculta de la tabla de cadenas en binario.
Puede codificar la cadena utilizando alguna codificación trivial, por ejemplo, xor con el código binario 01010101. No hay protección real, por supuesto, pero frustra el uso de herramientas como la string
.
Puede utilizar una biblioteca de C ++ que he desarrollado para ese fin. Otro artículo que es mucho más simple de implementar, ganó como el mejor artículo de c ++ de septiembre de 2017.
Si almacena la clave de cifrado en reversa ("yek noitpyrcne gnorts yM") y luego la revierte en su código (String.Reverse), esto evitaría una simple búsqueda a través del binario del texto de su clave de cifrado.
Sin embargo, para reiterar el punto hecho por cada otro afiche aquí, esto no logrará virtualmente nada para usted en términos de seguridad.
Su ejemplo no oculta la cadena en absoluto; la cadena todavía se presenta como una serie de caracteres en la salida.
Hay una variedad de formas en que puede ofuscar cadenas. Está el cifrado de sustitución simple, o puede realizar una operación matemática en cada carácter (un XOR, por ejemplo) donde el resultado se alimenta en la operación del siguiente personaje, etc., etc.
El objetivo sería terminar con datos que no parezcan una cadena, por lo que, por ejemplo, si está trabajando en la mayoría de los idiomas occidentales, la mayoría de los valores de los caracteres estarán en el rango 32-127, por lo que su objetivo sería para la operación en su mayoría colocarlos fuera de ese rango, por lo que no llaman la atención.
Sugiero m4 .
Almacene su cadena con macros como
const string sPassword = _ENCRYPT("real password");
Antes de compilar, expanda macros a cadena cifrada con m4 , de modo que su código se vea como
const string sPassword = "encrypted string";
Descifrar en el entorno de tiempo de ejecución.
Una vez me encontré en una posición igualmente incómoda. Tenía datos que necesitaban estar en el binario pero no en texto plano. Mi solución fue encriptar los datos usando un esquema muy simple que lo hizo parecer el resto del programa. Lo cifré escribiendo un programa que tomó una cadena, convertí todos los caracteres al código ASCII (rellenado con ceros según sea necesario para obtener un número de tres dígitos) y luego agregué un dígito aleatorio al principio y al final del código de 3 dígitos . Por lo tanto, cada carácter de la cadena estaba representado por 5 caracteres (todos los números) en la cadena cifrada. Pegué esa cadena en la aplicación como una constante y luego, cuando necesité usar la cadena, descifré y almacené el resultado en una variable lo suficientemente larga como para hacer lo que necesitaba.
Entonces, para usar su ejemplo, "Mi clave de encriptación fuerte" se convierte en "207719121310329211541116181145111157110071030703283101101109309926114151216611289116161056811109110470321510787101511213". Luego, cuando necesite su clave de cifrado, decodifíquela pero deshaga el proceso.
Ciertamente no es a prueba de balas, pero no estaba apuntando a eso.
crea una función que asigna tu contraseña a una matriz char estática y devuelve un puntero a esta función. Luego ejecute esta función a través de un programa de ofuscación.
Si el programa hace un buen trabajo. debería ser imposible leer su contraseña de texto plano usando un editor hexadecimal para examinar el programa binario. (al menos, no sin ingeniería inversa del lenguaje ensamblador. Eso debería detener a todos los script kiddies armados con "cadenas" o editores hexadecimales, excepto el hacker criminalmente loco que no tiene nada mejor en lo que perder el tiempo).