Cómo crear un Compact Framework (I)

Imaginemos los siguientes escenarios:

  • tenemos un gran framework super completo del cual queremos obtener una versión más pequeña para correr en dispositivos móviles, y tenemos que quitar ciertas características que el dispositivo no soporta. Además la idea del concepto de “Compact” Framework también hace referencia al tamaño, por lo que queremos eliminar todo el código del las características no soportadas.
  • queremos lanzar varias versiones de nuestra aplicación como Free Edition, Profesional Edition, Enterprise Edition, y no deseamos que en cada una de las ediciones solo vaya el código necesario para esa edición (y no todo).

¿Cómo lo hacemos?

La idea que propongo, si bien no es original ya que tengo entendido que .Net Compact Framework se ha realizado con una técnica similar, es simplemente identificar en los assemblies todos aquellos fragmentos de código (IL) que se utilizan para implementar las características deseadas y una vez conocido esto, eliminar el resto. Es decir, eliminar todos los métodos, propiedades, campos, eventos, tipos y demás elementos que no tomen parte en la realización de las características que queremos.

Por ejemplo:

En un procesador de texto tenemos 2 features: Save y Print. Y la interacción como se ve abajo.

image 

Del diagrama queda claro que no todas las funcionalidades hacen uso de todos los tipos ni de todos los métodos ni de todas las propiedades, etc. Entonces, si solo quisiésemos permitir guardar un documento, sería seguro eliminar los tipos DocumentReader y Printer, verdad?

Esto es posible hacerlo reescribiendo los ensamblados con Mono.Cecil, solo hay que conocer algo de IL para hacer cosas sencillas aunque para hacerlo realmente bien hay que conocer IL a la perfección.

Veamos el código del ejemplo del cual queremos eliminar la feature que permite imprimir es documento.

image

Ahora bien, antes de continuar quiero mostrar el resultado de como queda el assembly antes y después de ser procesado:

image

Lo que se eliminó es la posibilidad de imprimir y por eso, en el assembly procesado no existe el método TargetClass.Print(), ni los field _reader y printer ya que estos solo eran usados por el método TargetClass.Print(). Los tipos Printer y DocumentReader también han sido eliminados ya que no eran usados por otro método más que el TargetClass.Print().

Por otra parte, todos los tipos, campos, propiedades y eventos que son utilizados por otras características, aún cuando sean utilizados además por el TargetClass.Print(), permanecen intactos. Para esto solo hace falta comprobar que la clase Document aún sigue allí (aunque modificada ya que la propiedad Title se ha quitado porque solo era relevante al imprimir).

Bien, la idea entonces es que una vez que conocemos cuales son los métodos que implementan las features que deseamos mantener, la técnica consiste en seguir todas y cada una de las llamadas que ese método realiza. Luego, hacer lo mismo con cada uno de estos de manera recursiva hasta tener la lista de todos los métodos que participan en la realización de la características deseada. Abajo puede verse el mecanismo:

image

Y con esto ya tenemos el listado de todos los métodos usados, ahora lo que hacemos es eliminar todos aquellos métodos que no se encuentren en la lista:

image

Esto elimina todos los métodos no usados incluyendo getters, setters, los add y remove de los eventos. Por esta razón es que de seguro nos quedan propiedades sin gettes ni setters y que por lo tanto tenemos que eliminar para que el ensamblado nos quede funcional.

image

Por último, si existen clases sin métodos, ni campos, ni propiedades ni eventos… la eliminamos también.

image

Como esto es solo una prueba de concepto, quizás cambie muchas cosas en el código, voy a continuar con la explicación en la siguiente entrada.

Patrón repetido en MVC. Mi propuesta

Cuando usamos ASP.MVC uno de los patrones que debemos respetar es el de “Un modelo entra, un modelo sale” y otro muy común es el de validar el modelo y si este no es válido devolverle la misma vista al usuario para que corrija los datos de entrada. Por esto es común ver el siguiente patrón:

image

¿Qué está mal aquí? ¿Lo ves?

Lo que está mal es que este patrón se repite en cada una de las acciones de cada uno de los controladores de cada una de las aplicaciones que hacemos con MVC Framework y eso huele muy pero muy mal (DRY).

Este es el tipo de casos en los que puede ayudarnos AOP, y MVC framework hace un trabajo espectacular al posibilitarnos implementar AOP mediante filtros. Entonces mi idea es que esto debería hacerse así:

image 

Ahora sí el método hace lo que tiene que hacer, ni más ni menos. Para esto (y solo para demostrar mi punto) he creado el atributo ModelValidationAttribute (necesita un mejor nombre!) como sigue:

image

Y esto funciona a la perfección! Ojo, no recomiendo usarlo sin antes hacer una buena implementación ya que le faltan cosas al ViewResult como TempData entre otras (y el OnActionExecuted vacio tiene un olorcito medio feo – pero no es culpa mia! ¿por qué el creador de la interface IActionFilter me obliga a incumplir el contrato? ¿No serán 2 interfaces distintas [LSP]?)

Bueno, esto como les dije funciona a la perfección. La prueba:

image

Saludos.

Viendo la web como en 2006

Muchas veces uno tuvo un sitio que dejó en el olvido y quisiera verlo nuevamente o quizás recordar cómo se veía Geeks.ms en el 2006. Bueno, para los segundos les muestro cómo se veía esto en 206. Que lindos tiempos! las fechas de los artículos en inglés, nada de publicidad, un artículo de Carlos Fouz sobre la batalla que Microsoft la daría a Google y las 18 visitas que tuvo, algo que hoy tenemos en 15 minutos. 

image

 

Bueno, la verdad es que esta entrada es solo para mostrar algo que probablemente ya muchos conocen pero que para aquellos que no, les resultará interesante. Se trata de http://web.archive.org donde podemos ver el contenido de la web en las fechas que queramos.

10 consejos para que no te crackeen tan fácil

Crackear una aplicación tiene algo de arte y de ingenio pero crackear una aplicación .Net es demasiado sencillo para mi gusto, y representa un problema para la empresa o desarrollador que la comercializa y vive de mantener y mejorar el producto. Ya no vale eso de “total quien se va a dar cuenta”, eso no es válido para ninguna aplicación pero esto es aún peor cuando se trata de herramientas de desarrollo de software las cuales serán utilizadas por profesionales que conocen como pueden saltearse los sistemas de licencias.

Hoy, con todas las herramientas de que disponemos en .Net (ILSpy y sus addins de debugging, Cecil, RegSpy, entre otros) saltearse estas validaciones es demasiado sencillo. Por eso quiero hacer algunas pocas recomendaciones que si bien no pueden por si solas detener a quien se lo proponga, por lo menos le dificultará un poco la tarea. Esto es importante desde mi punto de vista ya que si debo invertir una semana para crackear una aplicación quizás me convenga comprarla.

Aquí van:

1. Firma los ensamblados. Esto hará que no puedan reemplazarlo por otro o reescribirlo; caso contrario lo único que debe hacer alguien para saltearse las validaciones es reescribir algún método y listo. Para ilustrar esto veamos un ejemplo, imaginemos que tenemos una clase encargada de verificar la licencia del usuario llamada LicenseChecker la cual tiene un método CheckLicense que retorna TRUE si la licencia es válida y FALSE en caso contrario. En este caso, solo bastaría con reescribir el método insertándole un return true. (ldc.i4.1 ret) Abajo puede verse lo ridículamente sencillo que es lograr esto con Cecil. 

image

Del mismo modo, no guardes información valiosa en constantes publicas ya que de la misma manera, se puede modificar sus valores. Por ejemplo, imagina que en una clase LicenseConstants tenemos un field llamado TrialDays con un valor de 30 que se asigna en el constructor estático en la instrucción nro. 10. en este caso, lo más sencillo será sobrescribir el assembly para asignarle un valor mayor y así extender el período de prueba tanto como queramos. El código sería así:

image

 

2. Nombres de identificadores. Nada va a salvarte de ti mismo, si la clase que verifica la licencia se llama LicenseChecker, LicenseValidator o de alguna otra forma altamente sugestiva, cualquiera con medio dedo de frente comenzará por ahí. Simplemente buscará con Reflector o ILSpy la pablabra “License” y listo!
Recuerda que los ofuscadores no pueden cambiar los nombres de los identificadores que forman parte de la superficie pública porque de lo contrario el assembly quedaría inutilizable. Sabiendo esto, te darás cuenta que todo lo que suene altamente sugestivo no puede formar parte de la superficie pública.

3. Ofusca los ensamblados como parte del proceso de Build. Si bien esto tiene pros y contras, con esto no solo dificultas un poco la tarea de examinar el código mediante Reflector, ILSpy u otros sino que además proteges un poco el código de copias ya que la mayoría de estas herramientas permiten extraer el código fuente en proyectos compilables.

4. Strings. Los strings con mensajes que se muestran en la UI referidos a temas de licencias deben estar encriptados de alguna manera. Por ejemplo, si alguien quisiera romper con el componente de validación de licencias y ve que en la UI dice “Enter License Number:”, lo primero que hará será buscar ese string en los assemblies y una vez que lo encuentre se fijará en donde se usa, y desde allí llegará a donde quiera.

5. Encriptación. Encriptar datos no detendrá a nadie aunque sí hará más lento el proceso de cracking y quizás llegue a frustrar a la persona que intenta saltearse la licencia. Lo que hay que tener en cuenta es que no debe ser la clave pública fácil de encontrar. Otro punto aún más importante es que los métodos que encriptan y desencriptan no deben nunca estar en assemblies separados de donde se usan porque de lo contrario estos tendrán que ser públicos y con esto ya arruinamos todo. Es decir, nunca poner estos métodos en un assembly “Utils” con nombres tales como Encript y Decrypt y hacerlos públicos, de lo contrario, cualquiera creará un proyecto de consola, referenciará a ese assembly y buscará las en el registro aquellas claves creadas por la aplicación que se ven claramente como encriptadas. Es decir, aquellas que se ven como sigue “9L3a1IQLzZFOmlU/3GkYu5Dm1ijRA+”, todo el mundo sabe que ésta es una cadena que ha sido encriptada.

6. Registro de windows. Ya sea que el código esté o no ofuscado, los strings con las claves del registro no deben reconocerse fácilmente. Si en alguna parte tenemos un string que comienza con “HKCUSoftware”, ya está! Si además ponemos públicos los algoritmos de desencriptación ni siquiera será necesario buscar la clave de desencriptación, solo será necesario hacer algo así:

image

Luego modificamos el valor que obtuvimos por consola y lo encriptamos nuevamente (gracias a los métodos que nos brinda el mismo producto) y lo escribimos en el registro.

7. Períodos de prueba. La mayoría de los productos permiten ser usados por un período de prueba, este es uno de los puntos más débiles de casi todos los productos que conozco ya que para lograrlo, por lo general deben guardar la información de activación, días de prueba o fecha de finalización en alguna parte de nuestros equipos (por lo general, en el registro de windows). Suele ser muy sencillo manipular las claves del registro para que ese período de evaluación se extienda ad infinitum. Recuerda que quien intenta saltearse la licencia, tiene el código a su disposición por lo que encontrará la manera de burlar el mecanismo.

Ten en cuenta que puede espiarse la actividad que el sistema realiza en el registro al momento de aceptar la licencia de evaluación con lo que encontrar qué es lo que hay que modificar no es el problema sino el cómo hay que modificarla. Ahí es donde debe ponerse toda la inteligencia. 

8. Datetime.Now. Restringe al máximo el uso de Datetime.Now para checkear las diferencias de días que le quedan antes de que expire el período de evaluación, ya que este devuelve la fecha del sistema y esta es modificable por el usuario. Es decir, le damos al usuario una variable que él puede modificar a su antojo.  Por lo tanto verifica todos los casos posibles, piensa que el usuario puede cambiar la fecha del sistema al momento de la instalación, piensa en lo valores límites, piensa si la diferencia (DateTime.Now – InstallationDate) es positiva y si es negativa, piensa que lo más fácil para el que menos sabe será jugar con la fecha del sistema.

9. Prueba. Muchas empresas desarrollan sus sistemas de licencias y realizan pruebas funcionales sobre estas para comprobar su correcto funcionamiento. Esto es necesario pero es solo una prueba de caja negra; sobre estos sistemas deben realizarse pruebas de caja blanca y dedicar al menos un día de, al menos, uno de los mejores desarrolladores a probar si puede vulnerar el sistema.

10. Dedicarle el esfuerzo y la inteligencia necesaria. Si no querés que las instrucciones de cómo vulnerar tu sistema se encuentren al otro día de realizado el release  accesibles en toda la internet, si no querés que los seriales estén por todos lados, si no querés perder ventas, entonces dedicale el tiempo, esfuerzo, inteligencia y recursos necesarios. Quizás te convenga contratar a un experto en el tema.