Capitulo 1

Para empezar se necesitarían unas nociones básicas sobre ASM (Ensamblador) cuando digo nociones básicas me refiero a 4 instrucciones que tendremos que aprender, ya que este articulo no trata sobre ASM.

- Saltos condicionales

Todo aquel que sepa mínimamente sobre programación sabrá lo que son los saltos condicionales, ya que en todo los lenguajes de programación son necesarios.
En todo programa siempre hay un momento en el que tiene que comparar una cosa, en nuestro casi seria por ejemplo un numero de serie. El pseudocódigo de un salto condicional seria el siguiente :

SI (Numero_Serie==123456) ENTONCES

{
Resitra_Programa();
}
SI NO

{
Registro_Erroneo();
}


Como podemos comprobar lo que hace este pequeño programa, es comprobar si el numero de serie es igual a 123456, si es así entonces se ejecutara el código de Registra_Programa(); si no es así ejecutara el Registro_Erroneo();

A simple vista la manera mas fácil de registrar el programa seria adivinar cual es el numero de serie. Entonces se tendría que recurrir al método "Harcoded".Los "harcoded" son algo excepcional. Ya que hoy en día no se usan o lo usan los programadores que no quieren programar un gran sistema de protección a Software.

Para encontrar el código, tan solo haría falta abrir el programa con el Desensamblador y mirar de encontrar el Numero de serie. Para explicar esto haremos una practica, para ello miraremos el programa CrackMe1.exe.

Para empezar deberemos destripar el programa usando un desensamblador como por el ejemplo el Win32Dasm que es el que utilizaremos.

Una ves desensamblado el programa, veremos en el Menú Refs la función String Data Reference. Esto nos mostrara todos los datos de Cadenas (a partir de ahora Strings) que hay en el programa. Es decir nos mostrara las cadenas de texto que salen en pantalla. Como podemos ver entre otras, sale una cadenas de texto que nos interesa :

- "ABCD1234567"

 

Si nos fijamos bien tiene 12 caracteres, y el el programa nos pedía un numero de serie que tuviera 12 caracteres. Pues bien como no hay otra cadena de texto que tenga 12 caracteres, lo que haremos sera comprobar si este es el código valido.

Como veis si que era este el código correcto. Hay que matizar que hoy en día los programadores no suelen utilizar esta técnica tan primitiva a la hora de registrar programas, ya que como es visto, es muy fácil. Aún así en el mercado existen varios programas que siguen usando esta técnica.

Capitulo 2

Proseguiremos explicando una técnica que se usa todavía hoy en día en muchos programas llamada la técnica del 74/75. Para explicar está técnica nos hará falta un poco mas de material técnico y aprender un poco sobre Ensamblador y tener unas nociones básicas.

Como todos sabéis el Ensamblador (En adelante ASM) es un lenguaje de bajo nivel. Que quiere decir un lenguaje de bajo nivel? Que cuanto mas largo es el programa, mas pierde el control de el.
El ASM usa varios registros. Los registros de datos, contienen información en ellas para hacer cálculos u operaciones. Digamos que guardan datos temporales de la 'memoria'.
Serian vulgarmente equivalentes a unas VARIABLES
Los principales registros son AX,BX,CX,DX
Estos son registros de 16 bits, que serían los que usarían programas de DOS o WIN 3.11
Existen otros registros de 32 bits que son Eax,Ebx,Ecx,Edx
Los registros de pueden dividir en altos y bajos. Un registro (p.ej AX) se puede dividir en dos , en AH (High) y en AL (Low).
Tenemos varias instrucciones para operar con estos Registros. Tan solo se explicaran instrucciones básicas que nos ayuden a la hora de usar la ingeniería inversa.

Instrucción : MOV
La instrucción MOV, en inglés move, que significa mover, es decir que mueve
un valor a un registro
Pongamos un ejemplo :

MOV AX,10h


En este caso el valor 10h ( La 'h' indica que es un valor Hexadecimal, si en vez de la 'h' fuera un 'b' seria un valor en binario) se movería a AX, es decir que AX=10h
También es posible mover valores entre registros, es decir el Valor del registro AX en el registro BX
en este caso seria :


MOV BX,AX

Movería el valor de AX a BX

Ahora pongamos un ejemplo fácil de comprender.

: MOV AX,2
: MOV BX,4
: MOV AX, BX

En la primera instrucción vemos como AX coje el valor de 2 (Si no lleva ninguna letra es que es en base decimal)
En la segunda instrucción vemos como BX, coje el valor de 4 y en la tercera AX, coje el valor de BX, entonces AX valdría 4 :)

Instrucción CALL
Se trata de una orden que se utiliza para llamar a subrutinas.
que después se retorna con la instrucción RET

Vamos a poner un ejemplo para que se vea claramente lo que es la instrucción CALL

: MOV AX,2h
: MOV BX, 4h
: CALL SUMA
: MOV CX,DX
: --- Supongamos que aquí estaría el END del programa
SUMA:
: Supongamos que aquí hay un codigo que sumaria AX y BX y guardaría el valor en DX
: RET


Explicaremos este ejemplo paso a paso.
Para empezar tenemos nuestra vieja instrucción MOV, daría el valor 2h a AX
Seguiríamos otra vez con la instrucción MOV, que daría el valor 4h al registro BX
y ahora tendríamos la instrucción CALL , esta lo que haría ir al CODIGO donde tenemos la etiqueta SUMA: donde aquí seguiría el código , en este caso por ejemplo aria una suma entre AX y BX y lo guardaría en DX
al ejecutar la instrucción RET el programa volvería a su ejecución normal, es decir volvería a la instrucción de después de CALL, en este caso MOV CX,DX
Que movería el valor de DX a CX.
Conclusión de este programa? Nos daría la suma de AX y BX y la guardaría en DX.


Saltos condicionales
Los saltos condicionales es todo aquel salto que ejecuta un programa siguiendo una condición si es cierta o es falsa va a una parte del código u otra.
Cuando nos encontramos con una instrucción CMP, significa que estamos
comparando dos valores
Esto comprueba el segundo valor con el primero. Para saber que es lo que comprueba, hay que mirar una instrucción mas abajo,la cual nos indica que es lo que comprueba, si es menor, mayor, igual, etc.
Estas instrucciones son llamadas Jumps (saltos), y todas empiezan por J.

Y son las siguientes :

CODIGO_HEX
INSTRUCCION
DESCRIPCIÓN
75 o 0F85
jne
Salta si no es equivalente
74 o 0F94
je
Salta si es equivalente
EB
jmp
Salta directamente a . . .
77 o 0F87
ja
Salta si esta sobre
OF86
jna
Salta si no esta sobre
0F83
jae
Salta si esta sobre o igual
0F82
jnae
Salta si no esta sobre o igual
0F82
jb
Salta si es inferior
0F83
jnb
Salta si no es inferior
0F86
jbe
Salta si esta debajo o igual
0F87
jnbe
Salta si no esta debajo o igual
0F8F
jg
Salta si es mayor
0F8E
jng
Salta si no es mayor
0F8D
jge
Salta si es mayor o igual
0F8C
jnge
Salta si no es mayor o igual
0F8C
jl
Salta si es menor
0F8D
jnl
Salta si no es menor
0F8E
jle
Salta si es menor o igual
0F8F
jnle
Salta si no es menor o igual

Aquí tenemos una tabla, donde nos muestra el nombre del salto en ensamblador,lo que significa y su CODIGO EN HEXADECIMAL, que mas adelante nos ayudara.
Estos saltos, se pueden explotar de tal manera que gracias a ellos
podemos invertir cosas. Me explicare con un ejemplo.

014F:00401DB 3BC7 Cmp eax,edi
014F:00401DC 0F85061DC1FF Jne 00551DE
014F:00401DD - REGISTRO -
.
.
.
.
014F:00551DE NO_REGISTRO

NOTA : el primer valor que hay pj 014F:00401DB es la dirección de memoria, el segundo es el código en HEXADECIMAL y el tercero en ASM

En este ejemplo , vemos que compara eax con edi, y la instrucción Jne nos indica que saltaría a la dirección 00401DF de memoria si eax y edi no son iguales entonces ejecutaría la rutina NO_REGISTRO. Si son iguales entonces lo que haría seria seguir su rumbo y registrase.

Como vemos en la tabla Jne significa que si eax y edi no son equivalentes saltaría a la
dirección de memoria 00551DE que es donde el programa daría un error y no se registraría.
Para que se registrara eax y edi tendrías que ser equivalentes.

Entonces... que pasaría si cambiaremos ese Jne por un Je? Pues que seria todo
al revés, si eax y edi son diferentes seguiría su rumbo y se completaría
el registro, en cambio si fueran iguales , no se completaría.

Supongamos que tenemos un programa que haya que poner un Serial, entonces el programa compararía el Serial introducido con el serial que tendría que ponerse. Si es correcto el serial, pues el programa estaría registrado.
Entonces lo que tendríamos que hacer es buscar donde esta la comparación entre el código, y invertir su Jump, para después al introducir un código falso, hacerle creer que es el bueno. En cambio si por mala pata introdujeras el bueno, sería como si hubieras introducido el código falso.

Para ello haremos un caso practico con el crackme2.

Este simple programa lo que hace es pedir un código. Si acertamos nos dirá que hemos acertado , y si no pues no dira que no... mm lógico no? xDDD
Entonces vamos a usar la teoría de antes en un ejemplo practico.

Para empezar ejecutamos el programa para ver lo que nos dice :



Al primer vistazo parece que es el mismo programa antes. Pero si hacemos un desensamblador veremos con no encontramos el código como en el otro programa. Habrá que recurrir a lo que os he explicado antes. Hacerle creer al programa que el código incorrecto es el correcto.

Para hacerlo, primero de todo hay que localizar la parte del código donde comprueba si el numero es correcto o no lo es. Para ello nos vamos al String Data reference, y veremos entre otras, estas dos cadenas de texto.

Pulsaremos encima de la de "Código correcto" donde nos llevará a la parte del código donde se muestra esta cadena de texto. Esto nos llevara a la dirección 00401037. Si nos fijamos en la dirección 00401035 veremos que hay un salto condicional el JNE, este nos dice que salte a la dirección 00401055 si no es equivalente. Es decir si el código no es el correcto salta a esa dirección. Si miramos en esa dirección lo que hay, es la parte donde nos dice que el código es incorrecto.

Entonces cual sería la solución? La solución estará en cambiar ese salto condicional por otro. En este caso tendríamos que decir si el código es incorrecto no saltes y sigue con tu camino. Es decir habría que cambiar el JNE por JE. Con esto conseguiríamos que al colocar el código correcto el programa saltaría a la dirección donde el programa no se registra,y viceversa, colocando el código incorrecto, nos registraría el programa.

Ahora bien, como cambiamos ese JNE por el JE? Para ello tendremos que usar un Editor Hexadecimal.

Una vez abierto el fichero con el editor hexadecimal, tendremos que ir a la parte del código que hay que cambiar. Para ello en el W32Dasm colocaremos el cursor justo donde esta el salto en este caso el JNE 00401055, y en la barra de estado (abajo) veremos que nos pone el OFFSET de donde esta esa parte del código.

Con el editor hexadecimal, buscamos en el menú "Ir a la dirección" o "ir al offset" y colocamos el offset, en este caso 000001035h. Donde nos mandará directamente a la parte del código a modificar.

Solo veremos muchos números y caracteres ya que esto es todo código maquina. Pero si nos fijamos en el W32Dasm entre la dirección de memoria (00401035) y el código en ASM (jne 00401055) tenemos unos números en hexadecimal (751E) que coinciden justo donde estamos en el editor hexadecimal. Si miramos la tabla de Saltos condicionales veréis que la instrucción JNE su código en hexadecimal es el 75, y la instrucción JN su código enhexadecimal es el 74. Tan solo tendríamos que cambiar con el editor este 75 por un 74 y grabar el programa.

Ahora lo ejecutaremos para ver si funciona correctamente.


Como se puede ver hemos introducido el mismo código de antes, pero esta vez a funcionado ya que hemos hecho creer al programa que un código incorrecto es el que debe de aceptar y un código correcto no lo debe de aceptar.

Si por casualidad pusiéramos el código correcto, nos daría el error de que el código es incorrecto. Si se quiere probar el código que en principio era el correcto es : 1631985.

Ahora ya sabemos dos maneras diferentes de des proteger un programa. Así sabremos a la hora de programar nuestro Soft que no hay que hacer, para que nos lo des protejan.

Aun qué parezca mentira mucho software que corre por el mercado, tienen muy poca seguridad y es muy fácil saltarse las medidas de seguridad del programa. No son tan fáciles, pero si del mismo estilo.

Capitulo 3
Como es obvio existen muchos mas métodos para proteger un programa, uno de ellos también muy usado es jugar con el registro del sistema y añadiéndole además la comprobación del código por todas partes. Me explicaré lo que quiero decir en esto ultimo poniendo un ejemplo. Supongamos que tenemos un software que nos pide un numero de serie para estar registrado. Bien, pues en este momento usaremos la ultima técnica aprendida 75/74. Hemos engañado al programa de que el numero de serie que hemos introducido es correcto. Bien, entonces sin que nosotros nos demos cuenta, el programa nos crea una clave en el registro con el user y el serial que anteriormente habías introducido. La sorpresa te la das cuando inicias el programa de nuevo y te dice que el serial introducido es incorrecto! Que es lo que ha pasado? Pues que el programa tenia una comprobación de serial en el arranque del programa. No tan solo hacia falta saltarse el sistema a la hora de introducir el numero de serie, si no también se tendría que saltar el sistema que comprueba el numero de serie al inicio.
Incluso hay programas que comprueban el numero un determinado de veces o incluyen código basura que pueden despistar a la hora de saltarse su seguridad.

Espero haber podido transmitir lo que he querido transmitir y intentéis hacer un software que sea lo mas seguro posible.

Escrito por |RooT|

#include <Programer_at_work.h>