Para los programadores que se mueven en el entorno de los lenguajes dBase, y concretamente los que se han introducido en el mundo del lenguaje Clipper, a continuación se muestra un trozo de código con el que se consigue una protección simple pero efectiva. Esta protección puede evitar la copia de los programas de nuestras aplicaciones una vez instaladas en disco fijo, y de los discos de nuestras aplicaciones (si éstos son copiados simplemente con utilidades del sistema) incluyéndola en nuestro programa de instalación. Se basa en el reconocimiento del número de serie de volumen que el sistema da a nuestros discos fijos y disquetes al ser formateados. También incluye una protección para delimitar el número de instalaciones. Esto se consigue utilizando, entre otras, algunas funciones Clipper de bajo nivel.
CODIGO FUENTE:
*-------------------------------------------------------------- * INSTALAR * Programa para instalación de aplicativos con protección *-------------------------------------------------------------- CLS PRIVATE lVer:=.F. // Conmutador para decidir si la función SustiExe() actúa para la protección por nº de serie o por nº de instalaciones. PRIVATE cStrGrab:="" // Cadena a grabar en rutina sustitución contenido variable para protección. * -------------------------------------------------------------- * PROTECCIÓN DEL PROGRAMA INSTALAR ... * -------------------------------------------------------------- * === VARIABLES RUTINA PROTECCIÓN POR NUMERO DE SERIE VOLUMEN === PRIVATE cp1ml:="ÉÅ..û..mé" // variable cuyo contenido inicial será sustituido por el nº de serie de volumen del disquete para protección de INSTALAR.EXE. PRIVATE c1ptk:="ÉÅ..û..mé" // variable cuyo contenido inicial será sustituido por el número de serie del volumen del disco fijo una vez instalado el aplicativo a proteger. * > cp1ml y c1ptk tienen que tener el mismo número de caracteres que la cadena que mandemos al fichero .EXE, en este caso serán 9 caracteres = caracteres del nº de serie del disco a substituir en el fichero .EXE al valor inicial de cp1ml y c1ptk. PRIVATE cNumSeDisk:= SPACE(9) // Nº de Serie Volumen recogido del eco de VOL. * ======= VARIABLES PARA PROTECCION POR NUMERO DE INSTALACIONES ====== * Contador de nº de instalaciones: PRIVATE nInstalacion:=0 * Cadena que contiene 3 caracteres codificados que representan el nº de la última instalación efectuada. Estos tres caracteres (los 3 últimos de cCic ÏÏÏ) son '000' codificados: PRIVATE cInstalacion:=" " * Número máximo de instalaciones: PRIVATE nMaxInst:=3 * Valor de inicio de la variable que contiene en sus 3 últimos caracteres el contador del nº de instalaciones: PRIVATE cCic:="%_. /_È_—ÏÏÏ" * Valor para condición de seguir o no ejecución devuelto por VERNINST(): PRIVATE lSeguir:=.T. * ========= OBTENCION Nº SERIE VOLUMEN ========== * Se envia al fichero m00zxw el mensaje del comando VOL del MS-DOS. RUN VOL > m00zxw // El fichero (m00zxw) contendrá el mensaje del eco de VOL. * cNumSeDisk toma los 9 caracteres correspondientes al num. de serie del disco. cNumSeDisk:=SUBST(MEMOREAD("m00zxw"),-11,9) cNumSeDisk=CODIF(cNumSeDisk) * Se borra el fichero Volume.TXT una vez obtenido el num.serie del disco. ERASE m00zxw * * ======= PROTECCION DE INSTALAR.EXE POR Nº SERIE VOLUMEN ========= * IF cp1ml="ÉÅ..û..mé" * Se envian a INIT los par metros NOMBRE_FICHERO_EXE_A_PROTEGER y la variablea cPAMM que contendr inicialmente un valor cualquiera de 9 caracteres ("ÉÅ..û..mé" en este caso). lVer=.F. SustiExe('INSTALAR.EXE',cp1ml) // Solo se realizar una vez la función INIT pues cp1ml solo tomar una vez su valor inicial "ÉÅ..û..mé" .. las veces siguientes que se ejecute el programa la variable cp1ml contendrá los caracteres del número de serie del disco (cNumSeDisk) ya que el contenido de cNumSeDisk es grabado con la función INIT en el lugar que ocupaba el valor de cp1ml. ELSE // Si cp1ml no contiene el numero de serie del disco termina el programa. IF cp1ml#cNumSeDisk @ 10,10 SAY "COPIA NO AUTORIZADA. LA APLICACION ESTA BLOQUEADA" @ 12,10 SAY "PULSE UNA TECLA PARA TERMINAR... "; INKEY(0) RETURN ENDIF ENDIF * * ====== PROTECCION DE INSTALAR.EXE POR Nº DE INSTALACIONES ====== * lVer=.T. * A la función que controla en nº de instalaciones se le pasan los parámetros Nombre_del_fichero y los 9 primeros caracteres de la variable cCic para ser buscados en INSTALAR.EXE. SustiExe('INSTALAR.EXE',LEFT(cCic,9)) IF lSeguir=.F. @ 4,10 SAY "HA SUPERADO EL NUMERO DE INSTALACIONES AUTORIZADAS ..." @ 5,10 SAY "PULSE UNA TECLA PARA TERMINAR "; INKEY(0) RETURN ENDIF * ------------------------------------------------------------------ * INSTALACION ... *------------------------------------------------------------------- CLS PRIVATE cDirInst:="C:\TEMP" RUN COPY APLICATI.EXE c:\TEMP * Esta rutina de instalación solo es un ejemplo. Hay programas de instalación freeware para hacer esta tarea mucho más elaborada. *------------------------------------------------------------------- * PROTECCION DEL APLICATIVO A SER INSTALADO ... *------------------------------------------------------------------- RUN VOL C: > m00zxw CLS cNumSeDisk:=SUBST(MEMOREAD("m00zxw"),-11,9) cNumSeDisk=CODIF(cNumSeDisk) ERASE m00zxw lVer=.F. SustiExe("C:\TEMP\APLICATI.EXE",c1ptk) RETURN *------------------------------------------------------------------- * Aquí acaba el programa de instalación de aplicativos. *------------------------------------------------------------------- **** FUNCIONES UTILIZADAS EN EL PROGRAMA DE INSTALACION ************ * SUSTIEXE = Función para cambiar el contenido de una variable en un fichero ejecutable a proteger. * ------------------------------------------------------------------- FUNCTION SustiExe(cNomExe,cStr) * Apertura del fichero .EXE en modo 2 (lectura-escritura): LOCAL cFicExe:=FOPEN(cNomExe,2) * nLeoFic = nº de caracteres leidos por Fread(): LOCAL nLeoFic:=0 * nPosStr = Posición de la cadena buscada en la cadena de la variable cBuffer que recibe los caracteres en Fread(): LOCAL nPosStr:=0 * lLoop = Variable de control del ciclo Do While: LOCAL lLoop:=.T. * cBuffer = Variable que recibe cadenas de 4096 caracteres leidos con Fread() * en el fichero ejecutable: LOCAL cBuffer:=SPACE(4096) // Inicializaci¢n de cBuffer. * Posición de comienzo de lectura en el fichero .EXE: LOCAL nStart:=FSEEK(cFicExe,0,0) * Nº de caracteres de la cadena a buscar y reemplazar: LOCAL nLenStr:=LEN(cStr)+1 DO WHILE lLoop lLoop = .F. // lLoop ser .F. al comienzo de cada ciclo. * Lectura del fichero abierto cada 4096 bytes sobre la variable c Buffer, y asignación sobre nLeoFic del nº de bytes leidos (serán 4096 menos la última lectura que se haga en caso de que se fin de fichero): nLeoFic=FREAD(cFicExe,@cBuffer,4096) * Posición de la cadena buscada en cBuffer: nPosStr=AT(cStr,cBuffer) DO CASE CASE nPosStr>0 // Si se cumple es que hemos encontrado la cadena buscada. * --------- Rutina control nº instalaciones -------------------- IF lVer // Si lVer=.T. la función verá el nº de instalaciones efectuadas. lSeguir=.T. nInstalacion=VAL(CODIF(SUBSTR(cBuffer,nPosStr+9,3))) IF nInstalacion=nMaxInst+1 // Si se han superado el nº instalaciones. lSeguir=.F. // No se permitir n m s instalaciones ... RETURN NIL // No sigue el proceso y regresa ... ELSE ++nInstalacion cInstalacion=CODIF(STRZERO(nInstalacion,3)) cCic=cStr+cInstalacion cStrGrab=cCic ENDIF ELSE // Si lVer=.F. la función hará protección por nº serie cStrGrab=cNumSeDisk // La cadena a grabar ser el nº de serie ENDIF * ---------- Fin rutina control nº instalaciones ----------------- * Posicionamiento en el byte anterior a la cadena encontrada: FSEEK(cFicExe,(nStart+nPosStr)-1,0) * Grabación de la nueva cadena en el lugar de la encontrada, con lo que cambiará el contenido de la variable cuyo valor es el de la cadena a sustituir: FWRITE(cFicExe,cStrGrab) FCLOSE(cFicExe) // Se cierra el fichero. * Si se ha llegado a fin de fichero es que no se ha encontrado la cadena buscada y se cierra el fichero: CASE nLeoFic < 4096 FCLOSE(cFicExe) * Si no se ha llegado a fin de fichero ni se ha encotrado la cadena buscada se actualiza el valor del puntero de lectura nStart, se inicializa el contenido de cBuffer y se continua la búsqueda (lLoop=.T.): OTHERWISE nStart=FSEEK(cFicExe,-nLenStr,1) cBuffer=SPACE(4096) lLoop= .T. ENDCASE ENDDO RETURN NIL *-------------------------------------------------------------------- * CODIF = Función de codificación de literales ... *-------------------------------------------------------------------- FUNC CODIF(cTexto) PRIVATE nI:=0, nLenTexto:=LEN(cTexto), cCarTexto:=" " FOR nI=1 TO nLenTexto cCarTexto=SUBSTR(cTexto,nI,1) cTexto=STUFF(cTexto,nI,1,CHR(255-ASC(cCarTexto))) NEXT RETURN (cTexto) *-------------------------------------------------------------------- * Fin de programa ........... *--------------------------------------------------------------------
COMENTARIOS A LA PROTECCION DE INSTALAR.EXE
La protección del programa INSTALAR.EXE se hace en dos fases:
1.- La primera se hace mediante la creación de una variable (cp1ml) que toma un valor inicial cualquiera ("ÉÅ..û..mé" en este caso concreto) y mediante funciones de bajo nivel de Clipper se abre en forma binaria (ASCII) en fichero INSTALAR.EXE y se cambia el contenido de esta variable por los 9 caracteres correspondientes al número de serie del disquete donde se encuentra el programa INSTALAR.EXE y el aplicativo que ha de ser instalado al ser ejecutado INSTALAR una primera vez. El número de serie del volumen (disquete) se obtiene enviando un eco del comando VOL a un fichero (m00zxw) que recoge el literal que contiene entre otros datos dicho nº de serie de volumen. Este nº de serie será comparado con la variable en cuestión, preguntando previmente si el contenido de la variable es el que le asignamos de entrada ("ÉÅ..û..mé") en cuyo caso seguir la ejecución del programa por ser la primera vez que lo intentamos, si no es as¡ comparar con la variable (cp1ml) con el nº de serie de volumen y si no coincide se abortar la ejecución.
La función que realiza el cambio del contenido de la variable es la función INIT que se ejecuta una sola vez para proteger el programa INSTALAR.EXE, mediante las referidas funciones de bajo nivel.
Notas:
A) Hay que tener en cuenta que el literal asociado por primera vez a la variable ("ÉÅ..û..mé") no puede aparecer en el programa anteriormente a su declaración como privada (exceptuando en los comentarios como este) ya que si fuera as¡ la función INIT cambiaría la primera ocurrencia de éste y no funcionaría la comparación de la variable (cp1ml) ya que no habría sido cambiado su contenido.
B) También habrá que tener en cuenta que este literal asociado a dicha variable deberá aparecer en el programa considerado como fichero ASCII antes del byte o carácter nº 65.535, por limitaciones de la búsqueda de las funciones de bajo nivel en la función INIT.
C) También hay que tener en cuenta que el disquete que contiene INSTALAR. EXE y el aplicativo motivo de la instalación, ha de tener número de serie de volumen, pues de no ser así podrá ser copiado en otro disquete sin nº de volumen y la protección no servirá de nada ya que coincidirán las cadenas obtenidas del mensaje producido por el eco de VOL.
D) El hecho de que el disquete donde se intentara copiar el disco de instalación no tenga nº de serie no importa, ya que tampoco coincidirán en este caso el nº de volumen del disco de instalación con el literal obtenido con VOL del disco copia.
2.- La segunda fase de protección consiste en controlar el número de instalaciones que se hacen. Se utiliza para ello un método parecido al anterior en cuanto que se va cambiando el contenido inicial de una variable en el propio programa INSTALAR.EXE mediante la función VERNINST. Es el contenido de la variable (cCic) en este caso, que contiene un literal de 12 caracteres: los 9 primeros sin significado alguno y los 3 últimos son ("000") codificados con la función CODIF(). El hecho de contener 9 caracteres anteriores a los tres que harán de contador de instalaciones es debido a que existe una mínima posibilidad de que existieran esos tres caracteres anteriormente en el programa INSTALAR.EXE, y al tener 12 caracteres a comparar disminuye la posibilidad de coincidencia. Al contrario de lo que sucede en la 1ª fase (INIT solo se ejecuta una vez) la función VERNINST() se ejecutará tantas veces como instalaciones hagamos hasta el nº de instalciones permitido que está declarado en la variable nMaxInst. Se diferencia esta función de INIT básicamente en que controla los 3 últimos caracteres del contenido de la variable elegida (cCic) y cambia su valor cada vez, por lo demás, su funcionamiento es el mismo. También habrá que tener aquí en cuenta las notas expresadas en la primera fase de protección.
Aclaratoria: El hecho de tomar como literales de entrada en las variables cp1ml y cCic un grupo de caracteres tan poco significativos como el nombre de las propias variables, es para evitar su fácil identificación en caso editar el fichero INSTALAR.EXE antes de ejecutarlo por primera vez (cosa poco probable ya que es condición indispensable el ejecutar una 1ª vez el programa INSTALAR.EXE antes de su posible "pirateo".), ya que son literales cuyo contenido es distinto antes y después de ejecutarse el programa. También por este motivo se codifican mediante la función CODIF() el contenido de las variables (el nº de volumen del disco) que sería visible al editarse el programa .EXE y ser revisado con cierta dosis de paciencia.
Notas Finales: Como se ha dicho, es indispensable ejecutar una primera vez el programa de instalación INSTALAR.EXE para protegerlo copiando el nº del disquete en el propio INSTALAR.EXE, ya que sino lo hacemos se podrian sacar tantas copias como quisieramos con un DISKCOPY "vulgaris" antes efectuar la instalación, esta primera instalación sería la nº cero, y no contaría como instalación de usuario ya que se ha tenido en cuenta a la hora de inicia
lizar el contador...
El aplicativo a ser instalado tendrá que contener las siguientes lineas de programa o parecidas...:
* RUN VOL > m00zxw * PRIVATE cNumSeDisk:=SUBST(MEMOREAD("m00zxw"),-11,9) * cNumSeDisk=CODIF(cNumSeDisk) * ERASE m00zxw * PRIVATE cp1ml:="ÉÅ..û..mé" * IF cp1ml#cNumSeDisk * @ 5,5 say "Copia no autorizada... * @ 6,5 say "Pulsa una tecla y... adios!"; inkey(0) * RETURN * ENDIF * y la función CODIF, ya que es utilizada...No es necesaria aquí la función INIT() ya que el contenido de la variable (cp1ml) es de principio el que corresponde a los 9 caracteres elegidos del eco del comando VOL, pues esta tarea ya se hace en el programa de instalaci¢n INSTALAR.EXE ...
En cuanto a las posible formas de "piratear" el aplicativo una vez instalado las desconozco, ya que no sé de ningún "copión" que copie el nº de serie del disco duro y la aplicación pues tendría que ser de propósito muy específico para esta protección... y en cuanto a la posibilidad de encontrar y manipular el contenido de la variable es mínima ya que está codificada y además hay que saber que la protección es ésta.
Con un buen copión podemos copiar el disco de instalación íntegro, con nº de serie de volumen incluido, con lo que ya no es efectiva la protección. Claro que esto requiere ya la disposición de unos medios y una intención "decidida" de copiar lo que sea. Por otra parte, una vez ejecutada la instalción la 1ª vez para activar la protección, no podemos ejecutar el aplicativo en el propio disquete (además no tiene sentido y probablemente ni espacio, ya que todo el aplicativo con sus ficheros de datos posiblemente irán comprimidos), y si copiamos el aplicativo a otro disquete o disco fijo tampoco funcionará ya que no coincidirá el valor inicial de la variable con el nº de serie codificado del disco en el que hayamos copiado (tenga o no dicho nº) salvo coincidencia, casi imposible... por el mismo motivo tampoco funcionará una copia del aplicativo ya instalado del disco fijo a otro disco.
COMENTARIOS A LA PROTECCION EN EL APLICATIVO
El sistema de protección del aplicativo que se instala en disco fijo es el mismo que el utilizado para proteger el programa INSTALAR.EXE en su primera fase, con la diferencia de que el cambio del contenido de la variable elegida a tal efecto (c1pkt) se hace sobre el aplicativo ya instalado en el disco fijo, quedando el contenido del aplicativo en el disquete sin modificar en ninguna de las instalaciones efectuadas. No habrá que olvidar tampoco en este caso las notas expuestas en la 1ª fase de protección del programa de instalación INSTALAR.EXE.
*-------------------------------------------------------------------- * APLICATI = APLICATIVO A INSTALAR CLS * Se envia al fichero m00zxw el mensaje del comando VOL del MS-DOS RUN VOL > m00zxw // EL FICHERO m00zxw CONTENDRA EL MENSAJE CLEAR ALL * cNumSeDisk toma los 9 caracteres correspondientes al num.serie del disco PRIVATE cNumSeDisk:=SUBST(MEMOREAD("m00zxw"),-11,9) cNumSeDisk=CODIF(cNumSeDisk) * Se borra el fichero Volume.TXT una vez obtenido el num.serie del disco. ERASE m00zxw * PRIVATE cp1ml:="ÉÅ..û..mé" // cp1ml tiene que tener el mismo número de caracteres que la cadena que mandemos al fichero .EXE, en este caso serán 9 caracteres = caracteres del nº de serie del disco que sustituirá en el fichero .EXE al valor inicial de cp1ml. La palabra "ÉÅ..û..mé" debe aparacer por primera vez (o sólo una vez) en el programa asociada a su variable cp1ml, pues de lo contrario al ser buscada y encontrada en otro lugar anterior sería ahí cambiada por el num. de serie del disco por lo que ya la variable seguiria teniendo el contenido "ÉÅ..û..mé". IF cp1ml#cNumSeDisk @ 5,5 say "Copia no autorizada... !" @ 6,5 say "Pulsa una tecla y... adios!"; inkey(0) RETURN ENDIF @ 5,5 SAY "ESTO ESTA FUNCIONANDO....." @ 6,5 SAY "PULSA UNA TECLA PARA FIN..";INKEY(0) RETURN **** FUNC CODIF(cTexto) PRIVATE nI:=0, nLenTexto:=LEN(cTexto), cCarTexto:=" " FOR nI=1 TO nLenTexto cCarTexto=SUBSTR(cTexto,nI,1) cTexto=STUFF(cTexto,nI,1,CHR(255-ASC(cCarTexto))) NEXT RETURN (cTexto)