Hola buenas.
Estoy elaborando como reto personal, un programa que averigue una determinada cadena comprobando caracteres uno a uno.
Este es el algoritmo que uso actualmente:
// Declaro la variable string de pruebas, le pongo todos los campos que voy a usar string Password = "bba345678911131517192123252729313335373941434547495153555759"; for (int i = 0; i < Password.size(); i ++) Password[i] = ' '; ConseguirContrasena(Password,Correcta);
bool ConseguirContrasena(string &Password,string Correcta) { int longitud = 0; int i,j,k; while (1) { if (Comprobar(Password,Correcta)) return true; // Comprobamos cada caracter uno a uno for (i = 0; i < 255; i++) { Password[longitud] = i; // Si coincidimos con la contraseña salimos del bucle if (Comprobar(Password,Correcta)) return true; } cout<<longitud<<" Probada"; /* Sino, habra que aumentar la longitud (si la cadena probada era de un caracter */ if (longitud == 0) longitud++; /* Si no es de un caracter, habra que modificar los caracteres anteriores */ else { // Vamos retrocediendo hasta que lleguemos al elemento 1... for (i = longitud; i > 0;i--) for (j = 0; j < 255; j++) { // Cambiamos un elemento de la cadena Password[i] = j; // Y volvemos a reintentar con la ultima cifra for (k = 0; k < 255; k++) { Password[longitud] = k; if (Comprobar(Password,Correcta)) return true; } } /* Despues de esas comprobaciones, aumentamos la longitud y volvemos al principio */ longitud++; } } }
Descarga: http://dl.dropbox.com/u/69551225/SacarContrase%C3%B1a.rar
Funciona bien con cadenas de 1 o 2 caracteres. Pero cuando son de 3 o más se empieza a rallar... Y acaba quedandose pillado (ten abierto el administrador de tareas antes de ejecutarlo).
Se me ha colado un error.
El código anterior, una vez que encontraba la cadena, no paraba. Para arreglarlo simplemente se le añade un if y un return:
Pruebalo. Debería ir mucho más rápido.
Saludos
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
Longitud
En la ultima parte, donde tienes los 3 for anidados, si quieres acceder al elemento "longitud" ss Password [longitud-1]. Además, el for debería ir desde i=longitud-1 y mietras i>=0, no i>0.
Aparte de eso, lo estás haciendo mal en concepto. Fíjate que no estás explorando todas las combinaciones, sino que cambias los valores de uno en uno: primero recorres todos los valores de la primera posición, después de la segunda, etc. Sin embargo lo que tendrías que hacer es cambiar todos los valores de la última, para todos los valores de la penúltima, etc. No se si me explico. En definitiva, que para explorar todas las posibilidades lo mejor es usar una función recursiva. Sin usar recursividad seguramente también se podría hacer, pero costaría mucho más.
Imagina la función
Que le das el rango donde tiene que hacer la combinatoria y te dice si combinando esos valores obtenemos el password. Con esa función, tan solo quedaría hacer un while de la longitud desde 1 hasta... infinito por ejemplo.
Saludos!
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
Hey! Muchas gracias. Si te
Hey! Muchas gracias.
Si te he entendido, solo cambio las cifras de uno en uno, pero tambien hay otras combinaciones posibles cambiando cifras dos a dos...
He probado tu función y funciona perfectamente (muy sotisficada, como se te ha ocurrido???). El unico problema es que es muyyy lenta. Para una secuencia de 3 caracteres se tira la de dios, sin embargo si uso una función con fors:
Lo saca en unos 10-15 segundos, mucho más rapido que la otra. Pero claro, no encuentro la forma de hacerla "general". Para cualquier numero de caracteres...
Mi creacciónes particulares:
http://www.scenebeta.com/noticia/la-serpiente
http://www.scenebeta.com/node/22535
No es lo mismo.
Ten en cuenta que el código que has puesto solo vale para 3 elementos. Si no sabes el número de elementos a priori tienes que explorar todas las combinaciones de 1 elemento, de 2, etc. Osea que el número de comprobaciones serían 256+256*256+256*256*256. Tu código solo hace 256*256*256 comprobaciones. Además, mucho cuidado a la hora de comprobar los tiempos. Encontrar la cadena "aaa" es mucho más rápido que encontra la "zzz"!!
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
Puedes acelerar el proceso
Puedes acelerar el proceso muchísimo usando máscaras; en vez de hacer un promedio de 128 comprobaciones, sólo harías 8, es decir:
Supongamos que tenemos que encontrar el carácter 'A', que el valor ASCII asociado es el 65. Con tu algoritmo harías 65 comprobaciones erróneas (de 0 a 64), y 1 correcta. Sin embargo si representamos el valor 65 en binario con un byte, quedaría así: 0100 0001. Por tanto sólo deberás comprobar bit a bit para construir el byte (el carácter) que buscas. ¿Cómo comprobamos bit a bit? Muy sencillo, usando máscaras. Una máscara no es más que una secuencia de bits que se usa para operar con otra secuencia de bits y así extraer los bits que nos insteresan. Por ejemplo:
Máscara 1: 1000 0000 (en decimal es el valor 128)
Vamos a "multiplicarla" por nuestra aún desconocida 'A'. Para ello usaremos el operador a nivel de bits &. Éste operador devuelve el producto lógico (AND), entre cada uno de los bits de cada secuencia:
0100 0001 & 1000 0000 = 0000 0000
Por tanto, acabamos de descubrir que el primer bit de nuestro caracter desconocido es un 0 (recordemos que el operador lógico AND sólo da 1 cuando 1 AND 1, en culquier otro caso dará cero). La segunda máscara seria 0100 0000, la tercera 0010 0000 y así hasta 0000 0001. Con esto ya tendríamos el primer carácter. Por tanto como puedes ver, con tan sólo 8 comprobaciones tendrías un carácter.
Un saludo y espero haberte ayudado!
Interesante... Lo he
Interesante...
Lo he intentado poner en practica:
Sin embargo no acaba de funcionar:
Mi creacciónes particulares:
http://www.scenebeta.com/noticia/la-serpiente
http://www.scenebeta.com/node/22535
Cuidado con los tipos
Ten cuidado con los bits de cada tipo de dato. Los int se codifican con 32 bits, es decir, para codificar el 1, tendrías 00000000 00000000 00000000 00000001, cuando la tabla ASCII va desde el 0 al 255, es decir 8 bits (2^8), por lo que el tipo char viene de perlas. Además has declarado 9 máscaras, cuando solo son usables 8 de ellas, ya que el valor 256 no se puede codificar con 8 bits: 1 0000 0000.
Ten en cuenta también, que el operador &, no devuelve true o false (técnicamente sí, ya que se considera false 8 ceros, y true cualquier otro valor distinto de cero), devuelve el resultado de la "multiplicacion" bit a bit, es decir, una secuencia de bits que constituyen otro valor en binario.
Concretando, declarando 8 máscaras como char, y el array (por supuesto de 8 posiciones, del 0 al 7) bool como char tambien, y sumando los valores del array deberías obtener el valor buscado.
Un saludo!
Es que si lo declaro char si
Es que si lo declaro char si que no funciona:
Mi creacciónes particulares:
http://www.scenebeta.com/noticia/la-serpiente
http://www.scenebeta.com/node/22535
Un par de cosas
Prueba con unsigned char en lugar de char. Y en la penúltima línea, de la función Explora_optimizado, contador++ debe ir despues del cout, ya que sino estás accediendo al Password [] siguiente, que al principio es basura.
Sigo diciendo que eso es como hacer Password [contador] = Correcta [contador]; de una forma más compleja y eso no es "adivinar una contraseña" puesto que ya la tienes. Es más, si esto se pudiese hacer, sería absurdo ponerle contraseña a las cosas, puesto que se podrían calcular en cuestión de segundos :P. No obstante la idea de Loopin está bien sobre todo para que aprendas a usar máscaras, que es muy util para otras cosas (ensamblador, programación de microcontroladores, sistemas operativos, etc).
PD: Arriba del todo te puse ayer el código de fuerza bruta arreglado. Ahora no debería de tardar tanto, a no ser que el password sea el 255-255-255, ya que para en cuanto que encuentra el pass xD.
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
unsigned char
Nanay, sigue sin funcionar.
PD: Da igual que le ponga una "salida" a la funcion recursiva, sigue siendo increiblemente lento que si lo hago a mano con unos cuantos fors (55.4 segundos vs 26.3 segundos (secuencia de 4 caracteres))
Mi creacciónes particulares:
http://www.scenebeta.com/noticia/la-serpiente
http://www.scenebeta.com/node/22535
No sé en que consiste la
No sé en que consiste la funcion ConvertirBinario, cuando lo tenias bien asignando el valor al char. Otra cosa es que estás operando con los valores de bytes que has obtenido de aplicar las máscaras. Es decir, la funcion evaluar potencia no debes usarla, puesto que cada bit del resultado de la máscara está en su sitio. Por tanto solo tienes que "fusionarlos" con una simple operació OR (o suma lógica, sólo da 0 cuando 0 OR 0, en caso contrario da 1): caracter = Byte[0]|Byte[1]|Byte[2]...
He escrito un miniejemplo en C:
Evidentemente, como bien dice joserc87, carece de sentido este tipo de algoritmos en este contexto, ya que si tuvieramos acceso a la clave buscada, simplemente hacemos clave = password, que es lo que estamos haciendo con las máscaras realmente, pero bueno aprovecharemos el ejemplo por el interés que tienes!
Un saludo!
Bueno la funcion Convertir
Bueno la funcion Convertir Binario transforma un valor decimal en un valor binario... Como hablastes de las mascaras en binario creía que había que declararlas en binario... Sí, soy estúpido xD
Tras eliminar la función binario y las potencias ya funciona:
Bastante rápido si señor, que lástima que no sea realista :(... Acabo de probar secuencias de 25 caracteres en menos de una décima de segundo xD. Pero bueno ya voy dando cosas nuevas, gracias : P
Mi creacciónes particulares:
http://www.scenebeta.com/noticia/la-serpiente
http://www.scenebeta.com/node/22535
Ten en cuenta que cuando
Ten en cuenta que cuando haces:
unsigned char cadena = 128;
lo que hace el compilador es convertir el número decimal "128" en un byte (por ser un char) en una secuencia de 8 bits y además el modificador unsigned sirve para indicar que todos los valores que se van a representar en memoria no tienen signo, es decir el valor decimal minimo es el 0 (0000 0000 una vez en binario), y el máximo es el 255 (1111 1111 en binario también). Cada uno de estos números se corresponde con un carácter en el estandar ASCII, que es lo que utiliza el sistema para imprimir estos numeritos por pantalla cuando se le dicen que son caracteres.
Salu2!
Eso es trampa!
Eso no es fuerza bruta. Eso es como encontrar el primer caracter, luego el segundo, etc. En un caso real en el que quieras "adivinar" una contraseña no vas a poder hacer eso, ya que no vas a tener el string "correcta", solo vas a tener una función que evalúe si una contraseña es válida o no.
Saludos!
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
Es cierto
Jeje, tienes toda la razon, pero bueno, hay técnicas como Blind SQL Injection que permiten aplicar este tipo de algoritmos, que de alguna manera u otra agilizan la tarea. Por otra parte, explorar todas las combinaciones secuencialmente para romper una clave de este tamaño prácticamente no tiene sentido, puesto que las claves que se generan son así de grandes por el gran coste computacional que hoy día conlleva romperla. Sería más lógico hacer uso de algoritmos probabilísticos que bueno, al fin y al cabo con "sólo" un promedio de la mitad de intentos se resolvería, (eso conociendo el tamaño de la clave...).
Me he entretenido en hacer un cálculo aproximado de cuánto se tardaría en explorar todas las combinaciones (He supuesto que el procesador, del futuro por supuesto XD, es de 10GHz, que una comprobación de la clave se realiza en una instrucción, y que cada instrucción se ejecuta en un solo ciclo de reloj):
Si no me he equivocado con las prisas, éste sería el tiempo en años. En fin, que un par de añitos. Según wikipedia el universo tiene aproximadamente 1.37*10^10 años, comparado con los 5.59*10^54 que tardaría nuestro procesador del futuro en romper la clave (aunque bueno, teniendo en cuenta el tiempo promedio, solo tendríamos que dividir entre 2, lo cual reduce un pelín el tiempo XD). Y si ya encima desconocemos el tamaño de la clave, suponiendo que fuera de 60 caracteres, habría que hacer 2^(60!*8^60), que ya el pobre Mathematica no tiene ni memoria suficiente para hacer este cálculo (8Gb de RAM tiene mi portatil!).
Después de este curioso paréntes, y teniendo en cuenta de que las claves para encriptar suelen ser números primos muy grandes, descartamos la opción de comenzar desde el principio a buscar la clave.
Un saludo! Y espero no haber errado mucho en la explicación :P
Procesador cuántico
Esto ya sí que no viene al caso, pero cuando llegue la era de la computación no determinística, se podrán resolver problemas NP como este en tiempo polinomial! :). Y no es tán fantasioso puesto que ya se han creado algunos procesadores cuánticos (de pocos qbits, pero bueno). Aunque para entonces las contraseñas serán inseguras y tendremos otros sistemas de seguridad.
PD: Lo del algoritmo de fuerza bruta nunca dije que tuviese futuro, solo que es de las pocas formas de resolverlo, a no ser que uses diccionarios o cosas así. Lo único que digo es que usar la contraseña para averiguar la contraseña... es un poco ilógico no? xD
PD2: Lo que has puesto me ha recordado a un profesor que tuve. Nos preguntó una vez que orden era menor: O(n), O(n²), O(n*log(n)), O(2^100) y caimos como totos diciendo que O(2^100) era constante. "Ni de coña" dijo él xD. En fin, cosas mías.
Dennis Ritchie. Padre de C y cocreador de UNIX.
R.I.P.
Ya bueno, pero ya que
Ya bueno, pero ya que estamos. Tengo curiosidad por este metodo ^^