Protecciones anticopia en el Spectrum 48K

Avatar de Usuario
Colossus
Miembro de honor
Miembro de honor
Mensajes: 4891
Registrado: 05 May 2004, 17:49
Gracias recibidas: 1 vez

Protecciones anticopia en el Spectrum 48K

Mensajepor Colossus » 15 Oct 2012, 18:42

(por Colossus)

El software actual viene normalmente protegido con distintos métodos para impedir su copia. Números de serie, disquetes llave, anillos grabados en la superficie del CD-ROM... Pero esta lucha contra la piratería no es nueva. Con el auge, en los años 80, de los microordenadores domésticos proliferaron también los sistemas de protección.



En este artículo pretendo enumerar los que recuerdo por haberlos llegado a conocer en su día. Me centraré exclusivamente en el ordenador ZX Spectrum de 48K. Fue el único que tuve antes de mi primer PC y, por tanto, el que mejor conozco. Espero que la exposición os resulte interesante, y que os anime a contarnos los mecanismos de protección que se usaban en vuestros ordenadores favoritos.



Comenzaremos por lo más sofisticado y ajeno al ordenador en sí. E iremos avanzando hasta llegar a lo que, por aprovechar las características más elementales de la máquina, requiere de menos capacidad de desarrollo. A lo largo del artículo, y especialmente en el apartado 4, supondré que el lector tiene unos mínimos conocimientos sobre el funcionamiento del Spectrum.




1. Cuando ya se ha logrado la copia.



Supongamos que, por el método que sea, hemos conseguido copiar un juego (sí, se trataba principalmente de juegos) en una cinta que el Spectrum es capaz de leer. ¿Qué es lo que nos impediría usarla?



Lo más sencillo, y utilizado, eran las "claves". Una vez cargado, el programa solicita una información que el usuario debe consultar en la documentación que se incluye con el juego original. Esta información solía venir en una hoja impresa con muy bajo contraste, o contener diferentes colores. De esta manera se hacía más difícil realizar un duplicado mediante las fotocopiadoras en blanco y negro de la época.



Más elaborado era el sistema Lenslock, de Firebird. En la pantalla aparecen unos caracteres que debemos teclear, pero tan deformados que se necesita una lente especial para poder leerlos. No tuvo mucho éxito porque era engorroso de utilizar y en más de una ocasión se pusieron a la venta partidas que incluían lentes defectuosas o correspondientes a otro programa, produciéndose las consiguientes quejas de los compradores. Como curiosidad podéis encontrar un emulador de Lenslock en www.simonowen.com/spectrum/lenskey. Intentad leer con él los caracteres de la imagen que sigue:





Protección Lenslock del juego Elite, el primero que la utilizó.




Todavía más sofisticados eran los dispositivos hardware necesarios para el funcionamiento del juego. Por ejemplo la primera edición del "Camelot Warriors", de la española Dinamic, venía con un pequeño artilugio (denominado SD1) que había que conectar al slot de expansión del Spectrum. En su interior había una simple resistencia, pero el juego no funcionaba sin ella.



Por supuesto todas estas protecciones (y las que veremos más adelante) se podían eliminar modificando el programa. Bastaba con acceder al código y evitar las rutinas que pedían las claves, que comprobaban si había algo en el slot, etc. Desproteger de esta forma los juegos no estaba al alcance de cualquiera, pero siempre había alguien que lograba hacerlo.

Me atrevería a afirmar que un juego siempre podía ser desprotegido con los conocimientos adecuados. Salvo que dicho juego incluyera algo que resultase demasiado caro de duplicar y fuera realmente imprescindible, no un simple estorbo como el aparatito del Camelot. Este es el caso del Mikro-plus, de Mikro-gen. Consistía en un periférico que, enchufado al slot trasero, sustituía los 16Ks de ROM del Spectrum por una nueva ROM repleta de rutinas diseñadas para juegos. La compañía promocionaba el invento no como una protección anticopia, sino como una manera de conseguir juegos mejores al haber más memoria "útil" disponible. En realidad el programa no funcionaba sin el dispositivo adicional, y este era sencillamente imposible de copiar para la mayoría de los usuarios. Que yo sepa sólo se sacó un juego con este sistema, el "Shadow of the Unicorn". Por desgracia para Mikrogen el programa no cumplió las espectativas, el Mikro-plus lo encarecía demasiado, y fue un fracaso.




2. Copias "cassette a cassette".



Los juegos con Mikro-plus eran imposibles de piratear, y métodos como el Lenslock había que desprotegerlos. Pero las claves se podían, con la suficiente paciencia, transcribir a mano. Bastaba entonces con copiar la cinta en sí. Y, como un juego no contenía más que ruiditos, si uno disponía de dos magnetófonos se podía volcar en una cinta virgen el contenido del cassette original. Este método se conocía como "copia de cassette a cassette" y permitía duplicar cualquier cosa. ¿No?



Pues no exactamente. Para empezar no era demasiado práctico. Había que disponer de la cinta original o de una copia muy buena. Porque los duplicados de tercera o cuarta generación daban demasiados errores de carga. Sobre todo si se trataba de programas "turbo" (ver apartado siguiente). Se hacían bastantes copias con este método, pero no podían propagarse demasiado. Una versión salvada directamente del Spectrum era siempre más fiable.

Se afirmaba además, yo nunca llegué a comprobarlo, que algunos juegos sólo se podían duplicar empleando magnetófonos buenos (platinas con control manual de nivel de grabación). Y es que la mayoría de las grabadoras baratas de la época tenían control automático de ganancia. Es decir, amplificaban automáticamente la señal a grabar si ésta era muy débil, o la atenuaban si era demasiado fuerte. Esto traía como consecuencia que en las partes silenciosas del original la grabadora amplificaba tanto su entrada que en la copia se podía escuchar un notable zumbido donde debería haber silencio. Este zumbido no era otra cosa que el ruido de fondo presente en todo aparato eléctrónico (sobre todo si es barato) excesivamente amplificado. Pues bien. Al parecer algunos juegos, como los de Ocean, detectaban este zumbido y abortaban la carga. En consecuencia incluso una copia de cassette a cassette realizada a partir de un original podía fallar con determinados programas. A pesar de que los magnetófonos utilizados funcionasen perfectamente.




3. Copiando lo desconocido.



La copia más fiel es, entonces, la realizada mediante el propio ordenador. Y los juegos constan de bloques de bytes. Se salvan con un SAVE "nombre" CODE y listo ¿no? Pues tampoco.



¿Cómo impedir que un usuario cargue en memoria el bloque del programa y salve luego una copia de primera generación, eliminando de paso las protecciones? Pues, por ejemplo, no dejando memoria suficiente para hacerlo. Algunos juegos constaban de un bloque de bytes tan grande que ocupaban toda la memoria disponible para el BASIC. Cualquier programa tecleado para salvarlos sería sobreescrito por el juego a copiar. A veces estos bloques de código incluían también la pantalla de presentación (un volcado de la memoria de vídeo). Ocupaban, pues, los 48Ks de la RAM. Y en algunos casos extremos se salvaba incluso la ROM. El sufrido Spectrum tenía entonces que leer 65536 bytes seguidos, a pesar de que los primeros 16Ks no servían más que para incordiar (intentar escribir sobre la ROM no provoca error, pero no tiene ningún efecto). Imposible, pues, copiar algo así desde el BASIC.



Otra técnica consistía en usar bloques sin cabecera. Todo lo que se salva desde el BASIC tiene un pequeño bloque de bytes (llamado cabecera) con información para el sistema, seguido de los datos propiamente dichos. Un pequeño programilla en código máquina que llame a la rutina de la ROM apropiada puede, sin embargo, leer desde la cinta sin necesidad de cabecera. Y el usuario inexperto será incapaz de cargar desde el BASIC un bloque como este.



Estos métodos son, no obstante, fáciles de superar. Incluso los programas copiadores más sencillos podían leer (y salvar posteriormente) un bloque de casi 48K, tuviese o no cabecera. Y joyas de la programación como el TC7 de LERM eran capaces de contar en una primera pasada la longitud del bloque, sacar sus cuentas, cargarlo en una segunda lectura y salvarlo después. ¡Aunque ocupase los 64K direccionables por el Spectrum!



El problema de estos sistemas de protección es que se basan en las rutinas de cassette estándar, contenidas en la ROM. Dichas rutinas son sobradamente conocidas: los datos (sean o no cabeceras) se graban en bloques que comienzan por un tono guia de unos 800 Hz, seguidos de una ristra de pulsos de 1000Hz (para representar un 1) o 2000Hz (para representar un 0). Esto da una velocidad de transmisión media de 1500 baudios (o bits por segundo).






El Spectrum leyendo un tono guia estándar.





Carga estándar de datos, a 1500 baudios.




Todas estas especificaciones no son más que una convención, un protocolo de comunicaciones. Pero nada impide a una compañía desarrollar sus propias rutinas de carga, creando así un protocolo nuevo. Y las rutinas de la ROM, o los copiadores basados en ellas, serán incapaces de interpretar los datos grabados.

Aparecío así la carga turbo, que en lugar de a 1500 baudios leía los datos a otras velocidades (hasta 3600, aunque sobre 3000 era lo más común). Esto, además de acelerar el proceso de carga y dificultar la copia de cassette a cassette, impedía la lectura de los datos si no se conocían las especificaciones del sistema.









Dos espectaculares ejemplos de carga turbo.




Además de las características de los datos se podía modificar también el tono guía. Surgieron así los tonos pulsantes (en ocasiones mal llamados cabeceras pulsantes), que consistían en tonos guía intermitentes. También los tonos ultra-cortos (que las rutinas de la ROM no lograban detectar), o tonos de distintas frecuencias.





Tono guía de Dinamic, más grave que el estándar.




Llegado un momento se emplearon en un mismo juego combinaciones de todo lo expuesto tan complejas que convertían el familiar sonido de la carga convencional en una cacofonía de chirridos, difíciles de leer y difíciles de reproducir. Aparecieron elaborados sistemas anticopia con nombre propio, como el Alkatraz o el Speedlock. La mayoría de estos sistemas incluían espectaculares efectos visuales mientras se realizaba la carga.





Speedlock: aunque parezca un bloque de datos convencional esto
es una carga turbo que incluye tonos pulsantes.





Alkatraz: en este sistema turbo los bordes permancen en negro,

un contador muestra cuánto queda de carga y la pantalla se dibuja

de manera distinta a lo habitual (en este caso de forma secuencial).





Bleepload: este sistema no es turbo. Pero consta de cientos de

pequeños bloques de bytes separados por tonos tan breves que he

logrado capturar el tono guía (arriba) y los datos (abajo) en una

sola instantánea.




La mayoría de los programas copiadores no consiguieron mantenerse actualizados frente a la avalancha de métodos de protección. Las posibilidades eran infinitas, por lo que había que desproteger a mano cada nuevo sistema de carga a medida. Aunque las rutinas estaban muchas veces encriptadas era posible en teoría analizarlas para intentar la desprotección. Bastaba con acceder al código, lo que nos conduce al último apartado del artículo.




4. Protegiendo el BASIC.



Casi todas las instrucciones de los juegos de Spectrum comienzan igual: Para jugar teclee LOAD "" y pulse PLAY en el cassette.

En efecto, por muy sofisticada que fuera la protección, lo primero que se leía era siempre un programa en BASIC. Dicho programa, denominado cargador, se ocupaba de ejecutar las rutinas de carga a medida si éstas estaban presentes. Si se quería evitar el análisis de dichas rutinas había que empezar por proteger el BASIC. Pero ¿puede un programa en BASIC ser protegido? Terminaremos este artículo contestando a esta pregunta mediante un ejemplo práctico.



Dado que el BASIC es un tema técnicamente más asequible que el código máquina intentaremos analizar el cargador del juego "Doomdark s Revenge", de Beyond Software. Este programa consta de un cargador BASIC, un bloque sin cabecera de 400 bytes (seguramente la rutina de carga a medida) y un extenso bloque turbo.



Escribimos el famoso LOAD "" y, una vez leído el cargador, pulsamos la tecla BREAK. Vaya, el Spectrum se reinicia. Lo primero que suelen hacer los cargadores es realizar un POKE 23659,0 , POKE 23613,0 o POKE 23613,82 . Cualquiera de estas sentencias modifica el contenido de una de las varibles del sistema de manera que el ordenador respectivamente se cuelga, se reinicia o símplemente nos ignora cuando pulsamos BREAK. Debemos evitar que esta instrucción se ejecute si queremos acceder al listado. El problema es que todos los cargadores están grabados con "autorun", que ejecuta el programa BASIC tan pronto se completa la carga. ¿Cómo evitar esto?



Para cargar un programa en BASIC se puede emplear, a parte de LOAD, la instrucción MERGE. Esta sentencia se utiliza para añadir a un programa que se encuentre en memoria un conjunto de nuevas líneas que se cargan de la cinta. Si en la memoria no hay ningún programa un MERGE equivale a un LOAD, con la diferencia de que MERGE ¡ignora el autorun!



Perfecto. Leemos de nuevo el cargador, pero esta vez con MERGE "" . Finalizada la carga obtenemos un error "4 Out of memory, 0:1" ¿Qué pasa ahora? Si con LOAD funciona ¿por qué falla el MERGE? El problema es que un MERGE, debido a que intenta fundir el programa en memoria con el cargado del cassette, realiza un análisis del listado más profundo que el LOAD. Si modificamos un programa de manera que contenga líneas con numeración o tamaño ilegal aún se podrá cargar con LOAD, pero todo intento de usar MERGE será desastroso. El intérprete BASIC no logrará cargar el programa, como en este caso, o sencillamente se colgará. Este tipo de protección se denomina "ANTIMERGE", por motivos obvios.



Afortunadamente todavía no se nos han agotado los recursos. Comenzamos nuevamente la carga con LOAD. Pero esta vez pulsamos BREAK inmediatamente después de la cabecera, antes del tono guía del cuerpo del programa. Aunque un intento de examinar el listado nos muestra una pantalla en blanco el sistema se ha preparado ya, al leer la cabecera, para recibir el programa que intentabamos cargar. Dicho de otra forma: no hemos cargado el programa en si (pulsamos BREAK antes de hacerlo) pero sí toda la información relativa a su tamaño. Si ahora hacemos un SAVE "programa" podremos grabar en el cassette una cabecera idéntica a la del cargador protegido ¡pero sin autorun! Hacemos pues un nuevo LOAD y reproducimos nuestra cabecera falsa recien creada, seguida del bloque de datos del cargador original. Listo. Ya tenemos el programa en memoria. Veamos, con la orden LIST, qué aspecto tiene.





Listado del cargador del Doomdark s Revenge, una vez
evitado el ANTIMERGE.




¿Qué es esto? ¿Dónde están los números de línea y las sentencias? Vamos a necesitar un análisis más profundo.



Todo programa en el BASIC del Spectrum consiste en una sucesión de una o varias líneas. Cada una de estas líneas comienza con dos parejas de bytes que indican el número de la línea y su longitud, por este orden. A estos cuatro bytes les sigue el contenido de la línea propiamente dicha, en formato ASCII. Y para finalizar, toda línea acaba con un byte de "fin de línea", de valor 13. Además, en un Spectrum que no tenga nada extraño conectado al slot de expansión, el primer byte del programa (es decir, el byte de mayor peso del número de la primera línea) se encuentra en la posición de memoria 23765. Con todos estos datos, y una tabla del código ASCII del Spectrum, se puede examinar a mano el contenido de cualquier listado. Un paciente análisis del cargador que nos ocupa, a base de leer con PEEK las posiciones de memoria pertinentes, nos desvela la siguiente información:




  1. El programa consta de 3 líneas de números 0, 10 y 100.



  2. El valor del campo longitud de la segunda línea es demasiado grande, y no coincide con la longitud real delimitada por el byte de valor 13 al final de la misma.



  3. El listado está plagado de códigos de desplazamiento del cursor a la izquierda, y de códigos de control de color.





La existencia de una línea 0, imposible en el BASIC del Spectrum, no supone mayor problema. Se usa frecuentemente para impedir que dicha línea sea borrada o editada por los métodos normales. Pero basta un simple POKE 23756,1 para convertirla en una línea 1. El valor erróneo de la longitud de de la línea 10 ya es más grave, y es el motivo por el que fallaba el MERGE. En cuanto a los códigos de control diseminados entre las sentencias, son los que nos impiden ver el listado. Un "cursor a la izquierda" estratégicamente colocado sobreescribe el número de línea. Y usar códigos para poner el fondo y los caracteres del mismo color (en este caso el blanco) permite ocultar el resto del listado. Por eso sólo podemos ver el irónico mensaje de advertencia.

Podríamos editar las líneas y borrar todos estos códigos molestos, pero más adelante veremos que eso no es una buena idea. En lugar de ello sustituiremos dichos códigos, a base de POKEs, por espacios en blanco (32 en ASCII). Después de esta laboriosa sesión de PEEKs y POKEs podemos, por fin, contemplar el listado.





Listado del cargador del Doomdark s Revenge, una vez eliminados
los caracteres que impiden su visualización.





Las líneas que empiezan por REM son comentarios. La primera es un simple aviso, pero la número 100 resulta muy sospechosa. Tiene todo el aspecto de ser una pequeña rutina en código máquina que mostrada en formato ASCII resulta ininteligible. Seguramente se ocupe de leer el bloque sin cabecera que sigue al cargador en la cinta del juego.



Nos falta sólo, para completar nuestro análisis del BASIC, examinar la línea 10. El bucle con un BEEP se encarga de realizar unos sonidos de aviso, los POKEs modifican los colores de la pantalla e impiden el BREAK, y el RANDOMIZE USR 0 del final (que provocaría un reinicio) nunca llega a alcanzarse si el juego se carga completamente. Nos interesan el CLEAR 57999 y el RANDOMIZE USR 24061 . En realidad es lo que uno busca en todo cargador. El primero le dice al BASIC que reserve memoria para un bloque de datos, y el segundo lanza la ejecución de una rutina en código máquina. El valor 24061 cae en la línea 100, tal y como esperábamos. ¿Tenemos ya, entonces, lo que queríamos? Mejor no fiarse todavía.



Para asegurarnos sustituimos (como no, a base de nuevos POKEs) los POKE, CLEAR y RANDOMIZE USR del listado por simples PRINT. El Spectrum nos presenta los siguientes valores: 23693,56,23624,63,48599,23659,0,24129,0. Los POKEs y el último RANDOMIZE están bien. ¡Pero el CLEAR y el RANDOMIZE que nos interesan no producen el resultado esperado! ¿Qué está pasando? La respuesta es que el Spectrum almacena los números que aparecen en un listado de dos maneras diferentes: como caracteres ASCII y como un 14 seguido de 5 bytes en el formato de punto flotante propio del ordenador. Al ver el listado se nos muestra la representación ASCII, pero al ejecutar el programa se opera sobre el valor determinado por los 5 bytes. Normalmente estas dos representaciones coinciden, pero si modificamos una de ellas el valor del número para el Spectrum será diferente del que vemos nosotros. Y, lo que es peor, si editamos una línea así protegida el número en coma flotante se actualizará automáticamente al expresado en ASCII, por lo que perderemos el valor original. Por eso no es buena idea editar una línea de un programa que intentamos desproteger.



Podemos comprobar todas estas afirmaciones ejecutando la sentencia CLEAR 48599:RANDOMIZE USR 24129 . Si pulsamos PLAY en el cassette (o emulador) la carga continua normalmente. Siempre y cuando no hayamos eliminado ninguna línea, o modificado su longitud, pues esto cambiaría la posición en la que se encuentra la rutina de la línea 100.



Conseguido entonces. Hemos "destripado" completamente el cargador BASIC. Ahora sabemos que nuestro siguiente paso es analizar la rutina en código máquina que se encuentra a partir de la posición 24129, aunque esa es ya otra historia...

Como véis sí es posible proteger un programa en BASIC. Y eliminar esa protección puede llegar a resultar muy laborioso.




Las capturas de pantalla fueron realizadas con el emulador Spectaculator 5.2 (www.spectaculator.com/)

Los juegos en formato TZX (volcado digital perfecto de la cinta) fueron bajados del archivo de W.O.S. (www.worldofspectrum.org/)



Postdata: A raíz de una conversación surgida tras la publicación de este artículo, NotFound desarrolló un conjunto de utilidades que facilitan muchísimo la desprotección de un programa en BASIC del Spectrum. Se llaman Z80util, y podéis encontrarlas en la zona de descargas.



Autor:
Texto e imágenes: Colossus




Consultar artículo original en los antiguos foros

Volver a “Articulos”

¿Quién está conectado?

Usuarios navegando por este Foro: Bing [Bot] y 2 invitados