Versiones crackeadas de software son creados con el uso de debuggers. Un debugger es un tipo especial de software que permite a los programadores depurar su software en sus partes constituyentes, con el fin de encontrar Error de software, y asi eliminar errores. Adicionalmente, debuggers pueden ser utilizados para Ingeniería inversa (del inglés, reverse-engineering), o para ver que hay dentro del software, para aprender su lógica. El último método es usado principalmente por investigadores de malware y estudiar lo que el malware (o virus de computador) hace dentro del software. Pero puede ser utilizado por un attacker para descifrar (o evitar) el registro legal de sofware, o algunas veces, para alterar el desempeño normal del software, por ejemplo instalando código malicioso en este.
En aras de este ejemplo, asumiré que el software que está siendo «descifrado» fue compilado en código nativo, y no en una aplicación .NET o JavaScript (de lo contrario será algo trivial ver el código fuente). El compilado de código nativo es un poco más «difícil» de estudiar (Nativo significa que el código se ejuta directamente por el CPU, GPU, u otro hardware).
Entonces, vamos asumir que el objetivo de un attacker es evitar la lógica de registro del sofware para que él o ella no tengan que pagarlo (más tarde para jactarse, él o ella podrian tambien publicar tal descifrado (crack) en algun foro oculto en línea o en un sitio de torrent para que otros puedan usarlo también y mostrarle su aprecio).
Por simplicidad, vamos asumir que la lógica original que estaba comprobando el registro de software fue escrito en C++ y fue algo similar al siguiente fragmento de código:
En este fragmento de código «RegistrationName» y «RegistrationCode» son caracteres (strings) especiales de texto que cualquier usuario legítimo del software recibirá despues de pagar por la licencia (El «RegistrationName» es el nombre real de la persona o su correo de email, y el «RegistrationCode» es un conjunto de caracteres especiales y únicos que está vinculado a la persona).
En la lógica arriba, la función denominada «isRegistrationCodeGood()» verificará si el «RegistrationName» y el «RegistrationCode» se aceptan utilizando un método patentado. Si son aceptados, retornará true,true, en otro caso falsefalse. Ese código de sálida determinará que rama (o sálida) seguirá la ejecución.
Asi que la lógica de arriba podria mostrar que el registro falló y se cerró:
O, si el código de registro y el nombre coinciden, guardará los detalles de registro en el almacenamiento permanennte (como el File system o System Registry) usando la función denominada «rememberRegistrationParameters()» y luego mostrará el mensaje de agradecimiento al usuario por el registro:
Un «cracker» obviamente quiere lograr el segundo resultado para cualquier código de registro que él o ella (usuario) ingrese, pero ellos tienen un problema. Ellos no tiene el código fuente C++, parte del cual mostré anteriormente.
Asi el único recurso que tiene el attacker es utilizar un Desensamblador de código binario (que siempre se suministra con el software en forma de archivos .exe y .dll en Windows, y sobre todo como ejecutables de Unix dentro de los paquetes .app en un Mac). Un attacker entonces utilizará un debugger para estudiar el código binario y tratar de localizar la lógica de registro que señalé arriba.
A continuación, pueden ver el diagrama de flujo de un fragmento del código que muestro en C++, presentado a través de un debugger de bajo nivel. O, como el código se leerá en forma binaria despues de utilizar el Compilador (Para legibilidad se añadió comentarios a la derecha con los nombres de funciones y variables. Estos no estarán presentes en el código que un attacker podria ver).
(Para entender lo que se mostro arriba un attacker tendrá que tener un buen conocimiento de las instrucciones del lenguaje ensamblador del código nativo).
Necesito señalar que tener un fragmento de desmontaje como el de arriba es el resultado final para un attacker. La principal dificultad para él o para ella es localizarlo entre millones y millones de otras líneas de código similares. Y que es su principal reto. No cualquier persona puede hacerlo y por esta razón «craqueo» de software es una habilidad especial.
Entonces, habiendo encontrado el fragmento de código anterior en el archivo binario del software un «cracker» tiene dos opciones:
- Modificar (o patch) el binario.
- Ingenieria inversa de la función «isRegistrationCodeGood()isRegistrationCodeGood()» y copiar su lógica para crear lo que es conocido como un «KeyGenKeyGen» o «KeyGeneratorKeyGenerator».
Revisemos ambos:
La primera elección es bastante simple. Cuando un attacker llega tan lejos, él o ella conoce el conjunto de instrucciones de Intel x64 bastante bien. Entonces simplemente cambian el salto condicional de «jnzshortloc7FF645671430jnzshortloc7FF645671430» en la dirección 00007FF64567141800007FF645671418 (encerrado en las capturas de pantalla) a un salto incondicional, o «jmpshortloc7FF645671430jmpshortloc7FF645671430». Esto eliminará efectivamente cualquier código de registro fallido y todo lo que el usuario escriba será aceptado como un registro válido.
También, tengase en cuenta que esta modificación puede ser alcanzada apenas por un byte en el código binario de 0x750x75 a 0xEB0xEB:
Pero este enfoque de modificar el archivo binario original viene con un «precio». Para hacerlo, un attacker necesita escribir su propio «patcher» (o un pequeño ejecutable que aplicará la modificación descrita anteriormente). La desventaja de este enfoque para el attacker es que «patch» (o modificar) el archivo original romperá su firma digital, que puede alertar al usuario final o al vendedor. Adicionalmente, el ejecutable «patcher» hecho por un attacker puede ser marcado y bloqueado fácilmente por el antivirus del usuario final, o puede llevar a investigadores criminales a la identidad del attacker.
La segunda elección es un poco más complicada. Un attacker tendrá que estudiar la función «isRegistrationCodeGood()» y copiarlo en su propio pequeño programa que efectivamente duplicará la lógica inplementada en el software original y le permitirá generar el código registro para cualquier nombre, dando así a cualquier usuario sin escrúpulos de ese software la posibilidad de registro sin realizar un pago.
Los proveedores de muchos de los principales productos de software comprenden el impacto del segundo método y tratan de prevenirlo al elegir lo que se conoce como «autenticación». Esto es básicamente un segundo paso despues del registro, donde el software envia el nombre del registro al servidor web de la compañia que devuelve una respuesta al software de si el código era legítimo o no. Esto es realizado por Microsoft cuando se compra Windows (ellos lo llaman «Activate Windows») y también por Adobe, y muchas otras compañias. Este segundo paso se puede realizar detrás de escena en segundo plano mientras el software se está ejecutando, y generalmente dará lugar a la cancelación del registro previo si se obtuvo de manera ilegal.
Entonces ahora conocen como el software es «craqueado».
Porque no es prevenible
Vamos a responder porque no es posible prevenirlo. Todo se reduce al hecho de que cualquier código de software debe ser leído por la CPU (en el caso de un código nativo binario) o por un Intérprete de JavaScript o por Compilación en tiempo de ejecución. (en el caso de JavaScript o código .NET). Esto significa que si hay una manera de leer o interpretar algo, no importa cuán complejo o complicado sea, un attacker con suficiente conocimiento y persistencia también podrá leerlo y quebrarlo.
Existe una discusión de que el software en la nube es más seguro, lo cual es verdad, ya que su código (binario) permanece en el servidor y el usuario final no tiene acceso directo. Y aunque el sofware en la nube es definitavamente el futuro, tiene algunos incovenientes que no permitiran reemplazar por completo el software convencional. Por nombrar unos pocos:
- No todos tienen conexion a internet o están de acuerdo a cargar sus datos a la red. Además la conexión a internet de alguien puede ser muy costosa o demasiado lenta de modo que el software tenga un desempeño muy lento.
- Luego está la cuestión de la informatica distribuida. Por ejemplo, Blizzard Entertainment nunca haria que «World of Warcraft» se ejecutará completamente en sus servidores debido a los inmensos recursos computacionales necesarios para representar cada escena para cada jugador que tengan.
Por lo tanto, les conviene dejar que la computadora de cada usuario final realice el renderizado.
Como un desarrollador, obviamente no me gusta cuando la gente roba las licencias de software. Pero tengo que aceptarlo y vivir con ello. La buena noticia es que no hay muchas personas que estén dispuestas a hacer un esfuerzo adicional y buscar una versión craqueada del software. El principal problema para aquellos que lo hacen es que al descargar el ejecutable «patch» o el «KeyGen» de un attacker, ellos efectivamente estan confiando en él o ella para que no ponga nada desagradable en su computadora que no se haya «anunciado en el paquete» (cosas como troyanos, malware o keyloggers). Entonces la pregunta para esas personas es: ¿vale la pena infectar potencialmente sus sistema con un virus desagradable por el costo de la licencia del software?
En el otro lado de la ecuación, algunos desarrolladores reaccionan muy negativamente a cualquier intento de robo de sus licencias de software. Ellos intentan implementar muchos tipos de contramedidas, desde engañar a los ingenieros inversos hasta agregar trampas explosivas en el código que pueden hacer algo desagradable si el código detecta que se está depurando, para ofuscar o codificar el código, para hacer cumplir todo tipo de esquemas de Gestión de derechos digitales, para bloquear usuarios de ciertos paises. Personalmente trato de alejarme de todas estas medidas. Y te digo porque:
- Cualquier tipo de táctica de ingenieria anti-inversa podria ser ignorada por un attacker con suficiente persistencia. Entonces, ¿por qué molestarme y perder mi tiempo cuando puedo invertirlo añadiendo algo útil a mi software que lo haga más productivo a los usuarios legítimos?
- Cualquier code packers podria crear falsos positivos con software malicioso, lo cual obviamente no es bueno para la comercialización del software. También crea una complejidad innecesaria para que el desarrollador debug el software.
- Agregar trampas explosivas en el código también puede «fallar» en sus usuarios legítimos, lo que realmente los enfurecerá e incluso puede llevar a demandas judiciales.
- Cualquier esquema de Gestión de derechos digitales probablemente atrapará a 100 usuarios legales e incomodorá a 10000 legítimos. Entonces, ¿por qué hacerlo a tus buenos clientes?
- Nuestra estadistica muestra que alrededor de 75% de las licencias ilegales vienen de China, Rusia, Brasil, para nombrar a los peores delincuentes (también entiendo que la razón puede ser debido a ingresos mucho más bajos que las personas tienen en estos países). El principal problema para nosotros fue el hecho de que si aplicamos nuestra Gestión de derechos digitales o agregamos una fuerte autenticación de registro, muchas personas que deseen omitir nuestro registro simplemente utilizaran un número de tarjeta de crédito robado. Y no tenemos control sobre eso. Nuestro sistema lo usará para enviarles una licencia legítima solo para que el pago se devuelva en semanas. Como resultado, perderiamos el dinero que se pagó por la licencia, además, la compañia de la tarjeta de crédito impondrá una tarifa de devolución de cargo adicional a nuestra cuenta, que puede variar de 0.25$ a 20$ por compra incorrecta además del costo de la licencia.
- Como se señaló en los coméntarios, algunas compañias pueden realmente beneficiarse al permitir copias piratas del software. Microsoft, por ejemplo, recibe mucha publicidad gratuita de las personas que usan su sistema operativo Windows, lo mismo ocurre con Adobe con su Photoshop. Ese es un punto bueno con el que estoy de acuerdo.