Una de mis entradas, hace tiempo, se refería a una de las tantas posibles formas de proteger nuestras macros. Con ello intentaba dar solución a que nuestros archivos no puedan ser ejecutados en cualquier computadora, y así salvaguardar nuestros proyectos y esfuerzos de copias no autorizadas. Para muchos el desarrollo de estos trabajos implica su medio de vida o bien una fuente extra de ingresos, deseando proteger de esta forma trabajos que han llevado mucho esfuerzo realizar.
Primero aclaremos un poco los tantos: nada es absolutamente infranqueable. Noten que hasta los "invulnerables" Linux o Mac OS han caído con los virus, o bien la facilidad total con la que hoy encontramos el mas variado tipo de software, crackeados para todos sin ningún problema: desde Photoshop a Windows. Sería al utópico pedir que un simple archivo de la suite de Office (Excel, Access, Word, etc) se convierta en un muro impenetrable. Nada mas alejado de la realidad, aunque podemos realizar ciertas tareas a los fines de complicarle la vida un poco y así salvaguardar nuestros proyectos.
La entrada que les mencionaba al comienzo del post funciona bastante bien, pero mi amigo AnyMa (quien posee un excelente blog para hacer funcionar las neuronas en base a la lógica y el pensamiento lateral, se los recomiento) tiene otra cuestión: el le manda a su cliente el archivo dentro de un CD, y me plantea: quiere que dicho libro de Excel solo se ejecute en la PC del cliente, evitando que pueda copiar y pegar el libro en otra PC distinta, y así bloquear la posibilidad de que se lo distribuya a otras personas. Ja, me la complicó bastante, pero se puede hacer algo al respecto.
Desde ya que esta labor solo es factible de ser realizada mediante Macros, y con el ejemplo que elaboré, haciendo uso del objeto FileSystemObject. Según la página oficial de Microsoft, este objeto:
"proporciona una estructura no jerárquica para manipular, leer y crear archivos de texto ASCII y Unicode. Esta estructura es muy diferente de la estructura jerárquica de la implementación original de E/S de archivo de Visual Basic. FileSystemObject no admite el acceso a archivos binarios, de modo que aún debe usar el modelo original E/S de archivos de Visual Basic para ello"
¿¿¿¿???? Si, tan útil como siempre. Agregaré a esta descripción que también podemos manipular directorios y unidades de disco.... y hacia estas últimas nos dirigimos.
Entonces, empecemos. Si queremos entregar un proyecto que no pueda ser utilizado por cualquiera, debemos:
1) ocultar todas las hojas del libro, excepto la de "bienvenida"
2) solicitar una clave de desbloqueo: si es correcta, mediante macros habilitamos las hojas y funciones; caso contrario solo queda la hoja de "bienvenida".
3) Excel siempre alerta sobre la presencia de macros: si las deshabilitan para abrir el libro... solo verán la hoja de "bienvenida", ya que también protegeremos mediante contraseña el proyecto en VBA.
seleccionamos una hoja y en su respectiva ventana de propiedades asignamos "xlSheetVeryHidden", para así ocultarla completamente. Cabe aclarar que si escondemos una hoja de esta forma, solo se podrá hacerla visible nuevamente mediante código
Cualquiera de los pasos arriba mencionados es objetable y (fundamentalmente) mejorable, pero nos brinda al menos un panorama general, un buen punto de partida.
Repasemos: ocultamos todas las hojas como indiqué en la imagen de arriba, salvo una, que será la visible al momento de abrir el libro.
Ahora se me ocurre: si voy a enviarle este archivo dentro de un CD, si o si (y como generalmente hacen la mayoría de los programas comerciales pagos) necesitaré enviarle posteriormente una clave de activación. Pero claro, se preguntarán, si mando una clave ¿como evitaré que sea utilizada para abrir el archivo en otra PC? A simple vista resulta algo ilógico, pero recurriré al numero de serie del disco rígido en donde se ejecuta el archivo. Ese número es único y lo aprovecharé para generar una clave: si el libro se intenta abrir en otra máquina, compararé los seriales de los discos rígidos... y si no coinciden, a cantarle a Gardel.
Los pasos a seguir son:
1) envío el archivo a mi cliente.
2) este abre el mismo, habilitando las macros (si no lo hace, está todo oculto y no funcional)
3) aprovecho la apertura del libro para ver desde que disco se ejecuta: si es removible (cd, pendrive, disquette, etc), el archivo se cierra. Con esto evito que mi cliente abra el archivo en cualquier parte: yo necesito que esté copiado a un disco rígido y ejecutándose allí.
4) si es un disco duro, tomo su número de serie y lo copio a una celda, dentro de alguna hoja protegida y oculta, creada a esos efectos.
5) corro algún algoritmo de encriptación (aunque sea uno muy básico y casero) para generar un serial en base al número del disco, y lo pongo en la misma hoja, en alguna celda contigua.
6) le muestro a mi cliente en algún form o celda el número serial del disco, solicitándole que me pase por teléfono, mail, sms, etc, etc el mismo. Aquí no importa el medio de comunicación: solo necesito tener a mi alcance el número de serie de su disco duro.
7) con dicho número en mi poder, le corro el mismo procedimiento del punto 5. De esta forma tengo ahora el serial que mi cliente posee, sin saberlo, en la celda de la hoja oculta.
8) le envío el serial para que lo ingrese y comparo: si los códigos son idénticos, abro el proyecto, caso contrario... se queda con la hoja de "bienvenida".
Declararemos un par de variables públicas, en un módulo, para facilitar su acceso desde cualquier parte del sistema. Dijimos que lo primero era verificar en que disco se está corriendo el archivo:
El código anterior lo pondremos dentro del Private Sub Workbook_Open(), de ThisWorkbook, de esta forma:Public Function DiscoApto(ByVal Ruta As String) As Boolean 'recibo con Ruta el path al disco desde el cual se está ejecutando 'el archivo 'creo el objeto FileSystemObject Set oFso = CreateObject("Scripting.FileSystemObject") 'seteo a oDrive, convirtiéndolo en un "disco" de oFso Set oDrive = oFso.getdrive(oFso.getdrivename(oFso.getabsolutepathname(Ruta))) 'y ahora analizo el disco: si es desconocido, separable, cd o ram, 'retorno False. Solo la función devuelve verdadero si el disco es fijo: Select Case oDrive.drivetype Case 0 'tipo desconocido DiscoApto = False Case 1 'separable DiscoApto = False Case 4 'cd-rom DiscoApto = False Case 5 'disco ram DiscoApto = False Case 2 'disco fijo DiscoApto = True Case Else DiscoApto = False End Select 'otros tipos, se los dejo de letra '2=fijo '3=red End Function
Nos queda ahora tomar el número de serie del disco rígido y encriptarlo, para así generar la clave que luego nuestro cliente nos pasará. Creo la hoja "xxx" y en la propiedad Visible le asigno: xlVeryHidden, como describí anteriormente. Allí guardaremos el número de serie del disco y el serial, junto a cualquier otro posible dato de interés; la siguiente función se encargará de obtener ese bendito número:
Public Function TomarNroDeSerie(Ruta As String) As Variant 'utilizo el mismo código para crear los objetos: Set oFso = CreateObject("Scripting.FileSystemObject") Set oDrive = oFso.getdrive(oFso.getdrivename(oFso.getabsolutepathname(Ruta))) 'algunos números son negativos, por lo tanto tomo su valor absoluto, 'con la función ABS de VBA: TomarNroDeSerie = Abs(oDrive.serialnumber) Set oFso = Nothing Set oDrive = Nothing End Function
Ahora es oportuno agregar el código al que ya tenemos:
Private Sub Workbook_Open() If Sheets("xxx").Range("a3").Value = 1 Then Exit Sub 'tomo los 3 primeros caracteres del path en donde se ejecuta el libro, 'y así obtengo, por ejemplo, C:\ ó cualquier otra letra Ruta = Left(ActiveWorkbook.Path, 3) If DiscoApto(Ruta) = False Then 'si la funcion es False, aviso y cierro el libro: MsgBox "Copie el archivo al disco duro de su máquina.", vbCritical, "Error" ActiveWorkbook.Close xlDoNotSaveChanges Else With Sheets("xxx") 'si se ejecuta en el disco correcto, guardo su nro de serie .Range("a1").Value = TomarNroDeSerie(Ruta) End With End If End Sub
Corresponde seguir con alguna encriptación de ese número de serie. Voy a utilizar la forma mas sencilla de todas: ir tomando el código Ascii de cada número y sumarlo al código Ascii de una clave predefinida. Cada cual podrá luego ir haciendo mas complejo este procedimiento, o bien consultar bibliografia específica sobre criptografía. En la siguiente imagen les muestro la lógica que se aplica:
El código VBA para generar la clave a partir del número de serie del disco y nuestra palabra secretra ("blog" en este caso) es el siguiente:
Corriendo lo anterior se observa que obtenemos la misma clave de la imagen anterior:
Esa clave que vemos en el MsgBox será la que guardaremos junto al nro de serie del disco. Cuando nuestro cliente nos haga llegar dicho número, correremos la misma función y tendremos a mano la misma clave que el, sin saberlo, posee almacenada en su libro. Le mando esa serie de 9 números para que la ingrese, si es correcta, se muestran otras hojas, si no, nada. ¿Se va comprendiendo la "estrategia" antipiratería? Como cada disco tiene su propio nro de serie resultará practicamente imposible que dos claves se repitan.
En A2 colocamos la clave:
Sheets("xxx").Range("a2"),value = EncriptarNroDeSerie(Numero)
La hoja de inicio (llamada "bienvenida") deberá verse, mas o menos, así:
Con el botón llamaremos a un formulario que solicitará la clave que nosotros le pasamos por mail o teléfono (o lo que sea), y que el usuario tiene en Sheets("xxx").Range("A2"), sin saberlo:
La hoja "xxx" quedaría así:
Un par de consideraciones: la macro principal se ejecuta al abrir el libro. Antes de llevar a cabo cualquier tipo de acción (tomar serie del disco, encriptar, etc) nos fijaremos que valor hay en la celda A3 de la Hoja "xxx". Si es 1, todo el procedimiento de desbloqueo no se ejecutará, ya que ese 1 nos avisa que la clave fué ingresada correctamente con anterioridad. Habría que agregar una pequeña línea de código:
If Sheets("xxx").Range("a3").Value = 1 Then Exit Sub
Necesitaremos complicarle la vida a quien rompa el bloqueo de las macros, ya que cualquiera mete en Google la pregunta "del millón" y aparecen los resultados. Entonces, y viendo que está en poder de hasta el mas ignoto "manejar" ciertos aspectos, deberemos realizar un código de difícil lectura. Noten que todas las funciones que programé tienen nombres muy (por demás) de obvios: EncriptarNroDeSerie, DiscoApto, etc, como así también los nombres de las variables utilizadas. No hagan eso en un trabajo real, dado que lo llevé a cabo para que sea "entendible". Usen, por ejemplo: DefineWin32AXPT_01(Rest) o CheckTAGhja, a la vez que ocultan las celdas de la hoja "xxx" y luego la protegen.
Y les doy un ejemplo secillo: en lugar de hacer referencia directa a Sheets("xxx"), que es la hoja donde guardamos la info, podemos ir seteando objetos (worksheets) en cadena, hasta hacer una mezcla de hojas y celdas que solo nosotros podremos interpretar facilmente.
Set FPE = Sheets("xxx")
'luego, en otra función:
Set GBCmm_NN = FPE
'y mas adelante, como para complicar bien las cosas, creo otra hoja que (en realidad) siempre hace 'referencia a Sheets("xxx")
Set RR_GAW_JKL = GBCmm_NN
De igual forma que lo descripto anteriormente, nos manejamos con los nombres de rangos... nada de A1, ni A3... todos nombres bien complicados y que iremos seteando, tal cual lo hicimos con las hojas.
Ojo: se dejan todo bien documentado, por que después de un par de meses ni el que lo hizo sabrá para donde "salir corriendo", como sucede siempre en estos casos. Y quiten cualquier comentario que hayan efectuado en el código. Les aseguro que cualquier "no programador" que rompa el módulo de VBA y acceda a su código... no podrá hacer mucho, mas que mirar y entender realmente poco.
Les dejo el link al archivo, con las hojas y los módulos desprotegidos. Ejecuten un "paso a paso" con algunos puntos de interrupción y F8, para ver "en vivo" como trabaja todo esto.
Gracias AnyMa por tus mensajes y palabras, espero que te sea de utilidad (no se olviden de visitar su blog).
la última línea es nuestra "clave". la contraseña que genera la clave es "blog", aunque puede ser cualquier palabra o seguidilla de caracteres que se nos ocurra. Recalco que esto es muy básico, se puede elevar mucho mas la complejidad, pero no es el caso.
El código VBA para generar la clave a partir del número de serie del disco y nuestra palabra secretra ("blog" en este caso) es el siguiente:
Public Function EncriptarNroDeSerie(Numero) As String Dim X, J, N, N2, R As Integer Dim Cadena, Pass As String Pass = "blog" 'recorro todos los caracteres del nro de serie: For X = 1 To Len(Numero) 'caracter x caracter tomo su numero ascii N = Asc(Mid(Numero, X, 1)) 'tambien el ascii de la contraseña J = J + 1 N2 = Asc(Mid(Pass, J, 1)) 'y sumo ambos: R = N + N2 'concateno, separando cada número por un 'guión: Cadena = Cadena & R & "-" 'si J alcanza la longitud de la contraseña, 'lo vuelvo a 1 If J = Len(Pass) Then J = 0 End If 'quito el último guión: EncriptarNroDeSerie = Left(Cadena, Len(Cadena) - 1) Next X End Function
Corriendo lo anterior se observa que obtenemos la misma clave de la imagen anterior:
Esa clave que vemos en el MsgBox será la que guardaremos junto al nro de serie del disco. Cuando nuestro cliente nos haga llegar dicho número, correremos la misma función y tendremos a mano la misma clave que el, sin saberlo, posee almacenada en su libro. Le mando esa serie de 9 números para que la ingrese, si es correcta, se muestran otras hojas, si no, nada. ¿Se va comprendiendo la "estrategia" antipiratería? Como cada disco tiene su propio nro de serie resultará practicamente imposible que dos claves se repitan.
En A2 colocamos la clave:
Sheets("xxx").Range("a2"),value = EncriptarNroDeSerie(Numero)
La hoja de inicio (llamada "bienvenida") deberá verse, mas o menos, así:
ops. si la clave que le paso no es igual a la que generé previamente en su computadora, no se desbloquea
La hoja "xxx" quedaría así:
Un par de consideraciones: la macro principal se ejecuta al abrir el libro. Antes de llevar a cabo cualquier tipo de acción (tomar serie del disco, encriptar, etc) nos fijaremos que valor hay en la celda A3 de la Hoja "xxx". Si es 1, todo el procedimiento de desbloqueo no se ejecutará, ya que ese 1 nos avisa que la clave fué ingresada correctamente con anterioridad. Habría que agregar una pequeña línea de código:
If Sheets("xxx").Range("a3").Value = 1 Then Exit Sub
Necesitaremos complicarle la vida a quien rompa el bloqueo de las macros, ya que cualquiera mete en Google la pregunta "del millón" y aparecen los resultados. Entonces, y viendo que está en poder de hasta el mas ignoto "manejar" ciertos aspectos, deberemos realizar un código de difícil lectura. Noten que todas las funciones que programé tienen nombres muy (por demás) de obvios: EncriptarNroDeSerie, DiscoApto, etc, como así también los nombres de las variables utilizadas. No hagan eso en un trabajo real, dado que lo llevé a cabo para que sea "entendible". Usen, por ejemplo: DefineWin32AXPT_01(Rest) o CheckTAGhja, a la vez que ocultan las celdas de la hoja "xxx" y luego la protegen.
Y les doy un ejemplo secillo: en lugar de hacer referencia directa a Sheets("xxx"), que es la hoja donde guardamos la info, podemos ir seteando objetos (worksheets) en cadena, hasta hacer una mezcla de hojas y celdas que solo nosotros podremos interpretar facilmente.
Set FPE = Sheets("xxx")
'luego, en otra función:
Set GBCmm_NN = FPE
'y mas adelante, como para complicar bien las cosas, creo otra hoja que (en realidad) siempre hace 'referencia a Sheets("xxx")
Set RR_GAW_JKL = GBCmm_NN
De igual forma que lo descripto anteriormente, nos manejamos con los nombres de rangos... nada de A1, ni A3... todos nombres bien complicados y que iremos seteando, tal cual lo hicimos con las hojas.
Ojo: se dejan todo bien documentado, por que después de un par de meses ni el que lo hizo sabrá para donde "salir corriendo", como sucede siempre en estos casos. Y quiten cualquier comentario que hayan efectuado en el código. Les aseguro que cualquier "no programador" que rompa el módulo de VBA y acceda a su código... no podrá hacer mucho, mas que mirar y entender realmente poco.
Les dejo el link al archivo, con las hojas y los módulos desprotegidos. Ejecuten un "paso a paso" con algunos puntos de interrupción y F8, para ver "en vivo" como trabaja todo esto.
Gracias AnyMa por tus mensajes y palabras, espero que te sea de utilidad (no se olviden de visitar su blog).
- Obtener enlace
- X
- Correo electrónico
- Otras aplicaciones
Etiquetas
Macros
Etiquetas:
Macros
- Obtener enlace
- X
- Correo electrónico
- Otras aplicaciones
Gracias a vos por todo, Damián !!! Esto de trabajar DESINTERESADAMENTE, sólo por el hecho de ayudar, realmente pocas veces se ve. Has ingresado en mi TOP TEN de DIOSES. Ahora sos "Damián, el Dios del Excel"
ResponderEliminarTe mando un abrazo grande y finalmente voy a poner en práctica esta técnica, me parece de lo más acertado.
jajaja, gracias capo, en verdad agradezco tus palabras. agregué tu blog a mi sección de "blogs de interés", por que ese realmente te hace pensar. no pude descifrar el de los 11 kms... pero no me rindo, voy a seguir intentando.
ResponderEliminarun abrazo y suerte con el proyecto, cualquier cosa me avisas.
Gracias por hacerme "publicidad" jeje. Yo también te agregué. Cualquier cosa que necesite te molestaré nuevamente. Abrazo!
ResponderEliminarexcelente aporte...
ResponderEliminar