Nota del autor

Si la entrada que estás leyendo carece de imágenes, no se ve el vídeo que teóricamente lleva incrustado o el código fuente mostrado aparece sin formato, podéis conocer los motivos aquí. Poco a poco iré restableciendo la normalidad en el blog.
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.

sábado, 14 de junio de 2008

Creando una llave USB "casera" mediante código

Imaginemos que deseamos crear un sistema de seguridad para nuestro software basado en la comprobación de una llave hardware. Tradicionalmente, encontramos soluciones como el uso de disquetes con ciertas peculiaridades que impedían su grabación “normal”, por ejemplo mediante la inclusión en el mismo de sectores defectuosos o alterados que eran corregidos en la copia del disco, por lo que las copias eran inservibles para validar el software, evitando así (se supone) la duplicación no legal de estas aplicaciones. También se usaban llaves de encriptación, habitualmente conectadas al puerto serie, y últimamente es habitual el uso de llaves USB programables (del tipo de las llaves HASP) con este fin. Sin embargo, son dispositivos que tienen un precio si no elevado, sí al menos importante, y si lo que deseamos es proteger una aplicación cuyo coste no es demasiado elevado, puede costarnos, como suele decirse, más el collar que el perro. En la entrada de hoy quería usar un ejemplo, que se presenta únicamente (y el que avisa no es traidor) con fines didácticos y no como una solución real de seguridad, explicando cómo implantar la seguridad en el software basada en el uso de dispositivos USB de uso común, como un pendrive o memoria USB.


En la entrada de hoy crearemos un sistema de seguridad frente a la copia de nuestro software usando unidades de disco USB. Desarrollaremos una aplicación que sólo se ejecutará en presencia de una memoria USB que suministremos con el software. Podría servir incluso para distribuir la propia aplicación, y su capacidad no tiene por qué ser elevada.


Para conseguirlo, simplemente debemos obtener el número de serie del dispositivo (no el número de serie del volumen, que podemos obtener ejecutando el antiguo comando de MS-DOS vol sobre la unidad en cuestión, sino el número de serie físico), y con este número de serie convenientemente encriptado y almacenado en un archivo de configuración, llevar a cabo la comprobación en el inicio de la aplicación, o cuando deseemos comprobar si se trata de un software legalmente distribuido. El código para conseguirlo esto es sencillo. Imaginemos una aplicación de consola que verifica si tenemos conectado un determinado dispositivo USB. El código podría quedar como sigue:


[csharp]
class Program
{
static void Main(string[] args)
{
if (CheckUSB.CheckUSBKey())
Console.WriteLine("Llave USB detectada. Acceso permitido.");
else
Console.WriteLine("No se ha detectado la llave USB. Por favor, insértela y ejecute nuevamente la aplicación.");

Console.ReadLine();
}
}
[/csharp]

La comprobación viene dada por un método estático, CheckUSBKey, que devuelve un valor booleano tras comprobar el número de serie que hayamos especificado. Por simplicidad en el ejemplo, este valor es recuperado dentro del propio método.


[csharp]
static bool CheckUSBKey()
{
/* Este debería ser el valor adecuado del número de serie de nuestro dispositivo USB.
* Podríamos obtenerlo usando el método GetSerialNumber("letra_de_unidad") de la clase USBDrives,
* en el ensamblado USBSN.
*/
string _usbKeyAllowed = MiClaseConfiguracion.ObtenerNumeroSerieKEY() // un string con el número de serie que deseamos comprobar;
bool detected = false;

USBDrives usbDrive = new USBDrives();
string[] drives = Environment.GetLogicalDrives();

// Obtiene los nombres de los dispositivos, eliminando la \ de directorio
foreach (string strDrive in drives)
if (String.Equals(_usbKeyAllowed, usbDrive.GetSerialNumber(strDrive.Replace("\\", ""))))
detected = true;

return detected;
}
[/csharp]

Como vemos, simplemente recuperamos todas las unidades lógicas del equipo (entre las que se encuentran discos duros, unidades de disquete, CD o DVD, así como los discos USB que haya conectados en ese momento en el equipo. Para cada uno de ellos, comprueba si el número de serie devuelto es igual al del la llave USB que hemos utilizado, y que deberemos distribuir junto al programa. Hace uso de una clase, USBDrives, del espacio de nombres Lobosoft.Utilities.Disks, que viene incluido en el ensamblado adjunto, y que puede ser usado libremente. De momento incluye un único método, GetSerialNumber(string letra_unidad), que recibe una letra de unidad (vale tanto la letra en sí, por ejemplo "D", como el nombre de unidad, es decir, la letra seguida por dos puntos, "D:"), y devuelve una cadena de caracteres con el número de serie de esa unidad, si lo tiene, o la cadena vacía en caso contrario. En una próxima entrada veremos cómo funciona internamente esta clase.


La DLL de utilidad puede descargarse aquí:


Llave USB

36 comentarios:

  1. Hijole que buen articulo, es justo lo que buscaba gracias!!! vientos huracanados!!

    ResponderEliminar
  2. Pablo Enrique Reynoso20 de octubre de 2008, 20:58

    Quiero hacer una llave para proteger mi software, pero mis dos preguntas son: Primera, como obtener el número de serie físico del dispositivo USB, de modo tal que cuando opere mi software lea de manera codificada el dispositivo USB y entonces funcione; y segundo, saber si este dispositivo no puede ser clonado.
    Gracias.

    ResponderEliminar
  3. Si, me encanta la frase realista de que nada es seguro. Pero también ando buscando quitarme un par de rémoras de encima. Yo hice en Vfox un remedio casero con USB (mas bien copie una serie de instrucciones y la adapté a mi software) Pero como soy de naturaleza curiosa me puse a hacer pruebas y caí en la cuenta que las USB cambian de numero de serie despues de formateo. Entendí bien o estoy mirando "otro " numero de serie? Hay otro numero fijo?

    ResponderEliminar
  4. Muy buenas Teresa.

    Jejeje, me alegra de que te guste esa frase. Curiosamente, aunque en aquel entonces aún no lo conocía, me la encontré tiempo después en un blog que ahora leo asiduamente y que puede interesarte. Está entre los enlaces recomendados en seguridad que tengo más arriba.

    Respecto a tu pregunta, en efecto, lo que estamos mirando en este artículo es el número de serie físico del dispositivo, no el que se ha grabado tras un formateo de la unidad. Este número no cambia y es único (se supone). Es algo así como la MAC Address de una tarjeta de red: aunque la dirección IP puede cambiar, la MAC es única por tarjeta (aunque luego la realidad es otra, y en tarjetas de red baratas podemos encontrar varias con una misma MAC por motivos de licenciamiento). Pero resumiendo, con este código estamos viendo un número asociado al dispositivo físico, y no a la unidad lógica, por tanto, no cambiará tras un formateo :)

    Un saludo,

    Mith.

    ResponderEliminar
  5. jejejejej pues el Se supone a mi me sale falso. Cambia. Se supone que un cliente no va a ser tan tonto como para formatear un usb con licencia pero mi rencor va a quiener puedan "manipular" el serial en otras USB
    :( Toy confundidaa

    ResponderEliminar
  6. Hola que tal oye me gustaria saber si este codigo que publicas se puede utlizar tambien para un sistema hecho en java, para mi es importante saberlo por que necesito protejer muy bien este sistema para que no sea tan facil de que me lo roben, te agradesco mucho tu pronta respuesta.

    Quedo atenta a tus comentarios

    ResponderEliminar
  7. Muy buenas Maclovia.

    El código está en C#, sobre la plataforma .NET, y el acceso a los dispositivos está además basado en WMI; por esto, el uso no puede hacerse directamente, pero puedes basarte en la misma filosofía para crear una aplicación Java que acceda a los dispositivos de almacenamiento (en particular a las unidades USB) para ver si se encuentran disponibles. En Internet hay referencias interesantes a este respecto:

    http://j-integra.intrinsyc.com/support/com/doc/other_examples/WMI_Scripting_from_Java.htm

    Por otro lado, también es posible alguna integración entre Java y .NET utilizando desarrollos como IKVM.NET, sobre el que comenté alguna cosa en el blog hace un tiempo:

    http://www.lobosoft.es/2008/09/22/usando-java-desde-net/

    Suerte con tu adaptación a Java.

    Un saludo.

    ResponderEliminar
  8. Hola

    Me he topado con este post y me ha interesado muchisimo, soy medio nueva en C# pero igual soy curiosa y quisiera aplicar lo que aqui muestras, entonces me descargue la carpeta del link que ofreciste pero no me reconoce la version de la solucion y no me deja abrirlo, ¿podrias decirme cual usas para saber cual conseguir?
    Disculpa las molestias, y gracias de antemano.

    ResponderEliminar
  9. Muy buenas, Killerpriest.

    La versión de la solución es la 9, para Visual Studio 2008. Para poder editar el proyecto con versiones anteriores (2003 ó 2005) puedes hacer una cosa. Borra los archivos de la solución (DemoSecurity.sln y DemoSecurity.suo), y abre directamente el de proyecto (DemoSecurity.csproj). Lo abrirá tu Visual Studio, y cuando grabes o compiles, generará un fichero de solución adecuado para la versión que tengas de Visual Studio.

    Respecto al código fuente, tendrás que eliminar las líneas que hagan referencia al espacio de nombres de System.LINQ, ya que no está disponible en versiones anteriores del framework, así como las referencias que tenga el proyecto al mismo. Con esto debería bastar y podrías utilizarlo.

    En cualquier caso, si tienes algún problema con el proceso, házmelo saber.

    Un cordial saludo,

    Mith.

    ResponderEliminar
  10. Hola.

    estoy desarrollando una aplicacion con Flash y mdm Zinc, a la que gracias a este tema, le implemente el reconocimiento de una memoria usb como llave, lastimosamente Zinc solo me permite leer la serie de la memoria (La que cambia cuando es formateada, y me veo con la necesidad de que esta memoria o llave sea un poco mas segura, para lo cual me gustaria que el usuario no pueda ingresar ni almacenar datos, con el objetivo de que no usen la memoria como dispositivo de almacenamiento para que no se vean en la necesidad de querer formatear la memoria cuando este llena o en otro de los casos como virus.

    Muchas gracias.

    ResponderEliminar
  11. Gracias Mithdraug,

    Muy amable, en realidad se que no es una opcion muy optima, por que si en el momento en que conecte la llave hay otra usb que se esta utilizando como disp. de almacenamiento podria dar problemas que se intente bloquear la escritura en todos los puertos usb,

    Reitero, que lastima que no pueda reconocer la id. fisica de la usb, creo que para otros proyectos deberia orientarme a trabajar con lenguajes mas potentes, en realidad no conosco nada sobre c#, podrias decirme para que tipos de trabajos esta orientado este lenguaje, tiene algo que con c++, ¿Lamento si me estoy saliendo del tema?, de todas maneras, muchas gracias.

    ResponderEliminar
  12. Muy buenas Luis.

    En efecto, no es la mejor solución, pero puede resultar en caso de ser necesario. También existe ese software que, como te dije, protege contra escritura las unidades USB, pero generalmente es software comercial y no está disponible libremente.

    En cuanto a C#, es un lenguaje de programación muy avanzado y elegante. Nació tomando lo mejor de Java (en parte, porque Microsoft perdió el pleito contra Sun respecto a las modificaciones que introducía en el "Java estándar"), y lo cierto es que la plataforma .NET resulta muy ágil a la hora de desarrollar aplicaciones de todo tipo: desde utilidades de consola o Windows a aplicaciones y servicios Web, pasando por telecomunicaciones, acceso a datos... Es una plataforma de propósito general, por lo que te serviría para cualquier tipo de aplicación.

    Respecto a C++, lo único que tiene parecido es el nombre :) C# es un lenguaje más avanzado, mejor estructurado y más orientado a objetos, si cabe. Te lo recomiendo encarecidamente. En la red hay muchos recursos disponibles, pero para comenzar te dejo aquí un manual introductorio:

    http://www.clikear.com/manuales/csharp/index.aspx

    Un cordial saludo.

    ResponderEliminar
  13. Hola, muchas gracias por tu articulo, me parece estupendo. quisiera implementarlo en Visual Basic, si tienes idea te agradeceria mucho.

    ResponderEliminar
  14. buenas noches me interesaria hacer algo asi, soy programador, y tengo un programa que realice en visual basic 6.0 me gustaria crearle un key licence usb para asi distribuirlo de manera tal el programa que las personas no puedan copiarmelo y pasarselo de una manera a otra, tendra algo del codigo o de la manera para hacerlo en visual basic se lo agradeceria

    ResponderEliminar
  15. El texto tiene buena pinta, pero el proyecto que has colgado no hace nada de lo que dices, o no lo veo, no se xD

    Por lo que puedo ver es un proyecto en ASP net que tiene un form de login, pero del usb etc ni rastro :S

    ResponderEliminar
  16. Statement fragment: please enter a complete statement. al convertir a VB net con el script de developerfusion

    Si lo hago en C# también obtengo error... pero en el VS. A ver si puedes detallar mas todo o colgar el proyecto, porque sino no se puede hacer nada...

    Saludos y gracias igualmente

    ResponderEliminar
  17. Muy buenas.

    Tienes toda la razón. En una actualización del gestor de descargas creo que algo quedó mal y el enlace estaba apuntando a otro proyecto. Error subsanado y gracias por avisar del problema.

    Un cordial saludo.

    ResponderEliminar
  18. A ver si puedo preparar un proyecto de ejemplo con la DLL para que veáis cómo funciona.

    Saludos.

    ResponderEliminar
  19. Vaya! No esperaba respuesta tan rápido jeje

    Pues mira, como es algo que me urge he estado mirando y he escaneado la red intensivamente xD Al final he dado con esto:

    http://www.cfdan.com/posts/Retrieving_Non-Volatile_USB_Serial_Number_Using_C_Sharp.cfm

    Que es una clase que permite obtener el número de serie de una unidad usb a través de WMI y por ahora todo bien.

    El problema que tengo ahora es que tengo hardcodeada la unidad en mi PC, pero claro, la letra de unidad puede cambiar según donde este conectada la llave usb.

    Ahora lo que quisiera es escanear todos los dispositivos, localizar la llave usb y entonces comprobar si tiene el mismo serial.

    Y aquí he llegado otra vez xD

    Esto que me falta creo que viene a ser la segunda parte del código que hay aquí arriba, más o menos.

    Bueno, viendo que has colgado la dll bien, he probado y lo que no acabo de ver claro es esta parte:

    es la parte del foreach y el if del final:

    foreach string strDrive in drives.......
    if String.Equals _usbKeyAllowed

    Podrías ponerme el mismo código en VBnet o explicarme un poco que haces y como?

    Saludos y gracias!

    ResponderEliminar
  20. Nada! Ya está! Ya lo he conseguido hacer funcionar y va perfecto jeje

    Para devolverte el favor, luego cuelgo un proyecto sencillo en vb net por si puede servirle a alguien más. La verdad que combinándolo con algún método de encriptación, puede ser una alternativa a las "mochilas" convencionales bastante eficaz y sobretodo muy barato (que era lo que en este caso más me interesaba jeje)

    Me falta probarlo en otro equipo... luego a ver si me monto una máquina virtual y hago pruebas con varios SO

    Por cierto, al mandar el comentario de antes si pegaba tu código entero...

    foreach ...
    if (String.Equals...

    ...la web se petaba...

    acabo de comprobar que así es... ojo con eso!

    Saludos y gracias de nuevo!

    ResponderEliminar
  21. Jejeje, fui rápido la primera vez pero no me dejaron serlo una segunda. He visto por tu otro comentario que ya te ha funcionado el código, a ver qué nos cuentas al respecto.

    Un cordial saludo.

    ResponderEliminar
  22. ¡Buenas!

    Me alegra que finalmente te funcionase. Lo del proyecto en VB estaría genial, especialmente para el resto de lectores que puedan venir por aquí buscando algo similar, así que agradecido. Voy a revisar el tema de la web que me comentas.

    Un saludo.

    ResponderEliminar
  23. Hola de nuevo!

    Siento haber tardado tanto! el caso es que tenia (tengo) que centrarme en acabar el proyecto, pero no te preocupes que lo prometido es deuda y el proyecto lo subiré en cuanto tenga un poco de tiempo.

    Saludos

    ResponderEliminar
  24. Para nada, no te preocupes. Lo primero es lo primero y, como verás por la frecuencia de publicación yo también tengo el blog un poco olvidado en los últimos meses (aunque me gustaría recuperar la actividad de antaño, ciertamente).

    Cuando tengas algo listo, si deseas compartirlo con la comunidad, aquí estaremos para agradecértelo. :)

    Un saludo.

    ResponderEliminar
  25. Hola,
    Excelente articulo, se que es un blog que al parecer no ha tenido actividad reciente. Intente descargar la dll con la utilidad pero los enlaces estan rotos. Podrias volver a ponerlos? Gracias.

    ResponderEliminar
  26. Muy buenas. Gracias por tu comentario, Anónimo.

    Los enlaces no van bien, efectivamente, porque el blog estuvo sufriendo ataques y tuve que migrarlo de un servidor propio con Wordpress a Blogger. Tendría que buscar el código, pero si me dices cómo puedo hacértelo llegar te lo envío. Otra opción es montar una solución en Visual Studio e incorporar el código fuente que está en el propio artículo. Con eso no deberías tener problema para generar esa DLL.

    Saludos.

    ResponderEliminar
  27. Hola, muy buen post, hay alguna posibilidad de que me subieras de nuevo la DLL. Muchas Gracias

    ResponderEliminar
  28. Hola, me parece muy interesante tu aporte, al igual que davito pregunto hay manera de que pudieras activar tu enlace otra vez.

    ResponderEliminar
  29. hola, este post es fantastico. Se podría subir de nuevo la dll?

    ResponderEliminar
  30. Saludos soy el anonimo del 1 de marzo de 2012, Podrias hacerme llegar la DLL? Mi correo es jorge.enriquegallardo@gmail.com

    De antemano muchas gracias.

    ResponderEliminar
  31. Te pido lo mismo que los anonimo. El archivo DLL, desde ya muchas gracias. Mi correo es systime67@hotmail.com

    ResponderEliminar
  32. La idea es excelente. Yo he aplicado el código que se muestra en msdn para obtener el número de serie usando C# y excelente. Obtuve el número de serie de mis dos memorias una de la marca Cruzer y otra de la marca HP y que tal, el número de serie de cada una es distinto y no importa cuantas veces de la formatee, el número de serie sigue siendo el mismo.

    Para despejar dudas conseguí dos memorias usadas de la marca Cruzer y ambas tenían el número de serie distinto.

    Sin embargo debía asegurarme bien, pero no he conseguido memorias compradas al mismo tiempo. Así que tuve que comprar dos para probar.

    He comprado dos de la marca Silicón Power, dos fabricadas al mismo tiempo, con el mismo código de barra.

    Hice correr el programa de C# para ver el número de serie y que creen: la fábrica le puso a ambas el mismo número de serie. Digo que sentido tiene un número de serie que va a ser el mismo para todas las memorias.

    No quiero ya gastar en otras marcas, al parecer la marca Silicon-Power no le da importancia al número de serie.

    Así que me gustaría si algún usuario podría probar y ver si conoce alguna marca que le de un número de serie distinto a las memorias usb fabricadas en el mismo lote.

    ResponderEliminar
  33. Hola no se practicamente nada sobre llaves. Solo usaba una q venia con software de protocolos medicos que compre y me robaron con maletin. Tengo el C D pero no puedo trabajar sin la llave USB.es posible recuperar el codigo? gracias.mi correo es drhectorignacioluna@yahoo.com.ar

    ResponderEliminar
  34. Al Dueño/a de ésta página. Por favor podría resubir los dos archivos? Daleeee, se buenitoooo, mirá todos lo que te escribimos y nada e!
    Que tu corazón se apiade de estas pobres almas deambulantes que somos! :D Si sólo queremos los archivitos para seguir cultivando inteligencia y compartirla como hizo "Nestor Fabian Montoya", e? que decis...podés resubir los archivos? :)

    ResponderEliminar
  35. Muy buenas a todos.

    Para los que pedís las librerías asociadas a esta entrada, os he de comentar que ya no obran en mi poder. Las perdí hace mucho tras varios cambios en la web ya que sufrió diversos ataques (podéis encontrar entradas sobre ello), o en cualquier caso me resulta complicado saber en qué copia de seguridad pueden andar.

    De todas formas, con el código fuente que se incluye en la propia entrada es muy sencillo reproducir la DLL y mejorarla, de ahí que no le dé tanta importancia a los archivos compilados.

    Gracias a todos y un saludo,

    Lobosoft.

    ResponderEliminar