CODIGO FUENTE: CLIPPER

PROTECCION DE NUESTRAS APLICACIONES

•Pablo Martínez Morillas•

El esfuerzo del desarrollo de una aplicación, exige una mínima recompensa. Las empresas productoras de software y los programadores más modestos, ven como el esfuerzo de meses (a veces años) para conseguir una aplicación más o menos decente no tiene ni siquiera la recompensa de que éstos puedan decidir cuándo su producto puede ser libremente copiado (freeware) y cuando no, debido a la piratería informática. Así, se van aplicando métodos de protección cada vez más sofisticados para evitar la copia de las aplicaciones de los discos de instalación o de los programas una vez instalados. Protecciones que a veces pueden ser múltiples, y que van desde las más sofisticadas a las más simples.

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)

Pablo Martinez Morillas