Humanos en problemas (II)

En la primera parte vimos algunas cosas básicas del debugger de DOSBox, y llegamos hasta el comienzo del programa principal ya desencriptado. Para retomar nuestro análisis, no hace falta volver sobre los mismos pasos. (Excepto abrir el DOSBox con debugger y activarlo escribiendo debug humans, claro). Habíamos hablado de que la sección Code Overview funciona como una ventana que podemos desplazar con las flechas del cursor. Pero también podemos apuntarla a una dirección determinada escribiendo C [segmento]:[desplazamiento]. Escribamos C CS:0132, que es donde sabemos termina la rutina de arranque, y coloquemos un freno con F9.

Aquí hace falta hacer un par de aclaraciones. En primer lugar, que CS no es otra cosa que un registro que indica el segmento de código actual. (Momento para mirar en Register Overview y ver que no miento, al menos sobre esta cuestión.) Venimos usándolo en lugar de un valor fijo porque éste puede cambiar, no sólo durante la ejecución, sino también de una máquina a otra, dependiendo de la configuración, controladores y programas residentes. En segundo lugar, sepamos que sólo apuntamos la ventana, pero el registro IP (instruction pointer) sigue indicando la dirección original (0000) y es desde ahí que el procesador va a continuar. Pulsemos F5 para ejecutar toda la rutina de arranque (desde CS:0000 hasta llegar a CS:0132), demos un paso más con F10, y estaremos nuevamente ante el comienzo del programa principal:

0000: call 0000816E
0003: call 000056BC
0006: call 00000677
0009: call 0000B43C         ;esta es la que nos interesa
000C: mov  [00EF4],0000

A diferencia de la vez anterior, ahora vamos a ejecutar sólo las tres primeras subrutinas (¿hace falta que aclare que tenemos que pulsar F10 tres veces? – bárbaro, gracias). Sabemos que en la cuarta está el código a desmenuzar así que, en vez de F10, pulsemos F11 (step into). ¿Cuál es la diferencia? Como su nombre lo sugiere, en vez de ejecutar la llamada call 0000B43C completa, dimos un paso “hacia adentro” de la subrutina. Una vez dentro, seguiremos con el habitual F10 (step over). Tomemos nota de que estamos en CS:B43C, que es nuestro nuevo punto de partida.

Empecemos a caminar esta nueva etapa y veremos aparecer la pantalla de carga (esa que dice «The Humans – Spanish Version»). Luego, en CS:B4C2 y CS:B4D1, aparecen dos saltos condicionales hacia atrás. Ya sabemos cómo lidiar con ellos (sí, se pueden saltear los dos juntos). Al llegar a CS:B505 pongamos un freno, pero antes borremos todos los anteriores (BPDEL *) para poder retomar directamente desde aquí cuantas veces haga falta:

B505: mov di,4894
B508: mov bp,0028       ;mostrar pantalla
B50B: mov bx,0000       ;solicitando la
B50E: mov cx,0100       ;página correspondiente
B511: call 00008C08     ;al dibujo
B514: call 00008BE7
B517: jmps 0000B574     ;ingreso y verificación (¿no vuelve?)
B519: mov di,4594
B51C: mov bp,0023       ;borrar pantalla
B51F: mov bx,0000       ;tras el ingreso correcto
B522: mov cx,0100       ;(nótese la simetría
B525: call 00008C08     ;con el bloque anterior)
B528: call 00008BE7

Acá está la papa. En estas líneas se muestra la pantalla con la señorita, se piden las claves y se vuelve a borrar la pantalla. Sin embargo, hay algo que no cierra. El salto en CS:B517 debería, en principio, ser una llamada, ya que cuando damos una respuesta correcta el flujo del programa continúa en CS:B519 (para comprobarlo sólo hace falta poner un freno ahí). Si nuestra respuesta no es satisfactoria, en cambio, iremos a parar a la consola del DOS con el mensaje “Program protection failure, Please re-run”, sin haber pasado por CS:B519.

Lo que esto significa es que a partir de CS:B574 está la lectura del teclado y el control de autenticidad. Si algo falla, el programa no vuelve a su curso normal. Esto nos lleva a realizar un experimento casi cantado. Si el fragmento citado contiene todo el proceso, por qué no saltearlo y ver qué pasa. Salgamos al DOS pulsando Alt-X, volvamos a activar el debugger escribiendo debug humans, y pulsemos F5 para dejar correr el programa. Cuando la ejecución se detenga en CS:B505 escribamos SR IP B52B. Esta orden cambia el valor del registro IP que mencionábamos antes. Es decir que cuando dejemos correr nuevamente el programa, en vez de continuar en CS:B505 lo hará desde CS:B52B, justo después del bloque.

Pulsemos F5. Suena la melodía y vemos el logo de Mirage. ¡¿Lo logramos?! No cantemos victoria: ni bien tratemos de continuar nos encontraremos de regreso en la consola del DOS. ¡A no desalentarnos! No habremos logrado quebrar la protección, pero es evidente que estamos bien encaminados. Y además descubrimos que más adelante existen otros puntos en los que se controla el dato ingresado. En lugar de buscarlos uno por uno (sin saber cuántos son en total), nos conviene identificar y reproducir el efecto de ingresar el número de página correcto. Vamos a tener que zambullirnos en el código localizado en CS:B574, que es ahora nuestro nuevo punto de referencia.

Un maestro, una causa, un efecto

Volvamos a empezar, y al llegar a CS:B505 pongamos un freno en CS:B574, para ver qué pasa al ingresar el número de página. Las rutinas de lectura del teclado suelen consistir en un gran bucle que compara los códigos suministrados por una función del DOS o de la BIOS para determinar qué tecla ha sido pulsada, llevar la cuenta de los caracteres ingresados, y salir cuando el ingreso esté completo. Recorriendo el código veremos que en CS:B5AB hay un salto al comienzo, que cierra el bucle del que hablábamos. Pero también vemos la salida:

B5A2: mov  al,[457B]
B5A5: or   al,al
B5A7: jne  0000B5AD
B5A9: loop 0000B59F
B5AB: jmps 0000B574
B5AD: mov  b,[457B],00
B5B2: cmp  al,0D
B5B4: je   0000B5F2
B5B6: cmp  al,0E
B5B8: je   0000B5DC
B5BA: cmp  al,30
B5BC: jb   0000B574
B5BE: cmp  al,39
B5C0: ja   0000B574

A partir de CS:B5B2 comienza una serie de comparaciones sobre el caracter ingresado, que entre otras cosas descarta cualquier letra o símbolo que no sea un número del 0 al 9 (ver CS:B5BA y CS:B5BE, valores en ASCII). Pero la primera comparación de todas es contra el número hexadecimal 0Dh (¡bendito número 13 de la suerte!) que corresponde a la tecla Intro. Podemos estar seguros que esa es la salida que buscamos, así que pongamos un freno en CS:B5F2, dejemos correr el programa e ingresemos el número de página correcto.

Tal como lo previmos, el debugger retomó el control en CS:B5F2. Avancemos por el código para ver adónde nos lleva, y tras unos pocos pasos nos encontraremos saltando entre CS:B63A y CS:B637. Hemos venido a parar a un callejón sin salida, cuyo propósito es precisamente que le perdamos el rastro a la ejecución. La buena noticia es que no hace falta seguirlo.

B633: mov    [3ECD],bp
B637: mov    bx,B637
B63A: jmp    bx

Justo antes de estancarse, el código almacena el contenido del registro BP, resultado de unos cálculos hechos líneas antes, en la dirección DS:3ECD. Apuntemos la ventana Data Overview unas posiciones antes, para tener un contexto, escribiendo D DS:3EC0 y ver si podemos sacar alguna conclusión. El valor de 16 bits (word) almacenado en DS:3ECD (señalado con B en la captura) es idéntico al de la posición DS:3ECB (señalado con A). Y si repetimos el experimento, respondiendo correctamente a un dibujo diferente, veremos que el valor A cambia, y el correspondiente valor calculado (B) coincide con él. Es hora de repetir el mismo experimento de antes, pero con una variante: una vez detenidos en CS:B505 copiaremos el contenido de la memoria de A a B, antes de modificar el IP para saltear toda la rutina, siguiendo estos pasos:

  1. D DS:3ECB (apuntar la ventana de datos)
  2. SM DS:3ECD <byte> <byte> (copiar los dos primeros bytes de Data Overview)
  3. SR IP B52B (modificar el IP para saltear la rutina de claves)

Dejemos correr el programa y esta vez pasamos sin problemas a la segunda presentación, al menú y podemos disfrutar del juego, nivel tras nivel. Esta vez sí hemos quebrado la protección.

¿Y ahora qué pasa, eh?

El método funciona pero está claro que todavía tenemos pendiente elaborar un parche que lo implemente. Dicho parche tendrá que hacer su trabajo una vez que el juego esté desencriptado en memoria. Y algún lector sagaz se habrá percatado de una complicación adicional: el valor correspondiente al dibujo (aquel que señalamos con A) se calcula aleatoriamente cada vez que el juego se pone en funcionamiento. Una posible solución es localizar la rutina que calcula un nuevo valor y anularla.

Dado que esta segunda parte también se ha extendido bastante, prefiero dejar la implementación del parche para la próxima (y última) entrega. Mientras tanto, recomiendo a quien haya leído esta guía practicar localizando la rutina que hay que anular para que el valor original no cambie. También puede experimentar con la edición de GameTek de este juego, donde la protección fue modificada levemente, agregándole una complicación adicional.

PS: Existe también una versión en CD-ROM que contiene el juego original combinado con los niveles del disco de expansión. Si bien en este caso la protección es distinta, al tratarse de un cd-check, la rutina de arranque es idéntica y podemos llegar al inicio del programa con los metodos aquí explicados.

Humanos en problemas (I)

Días atrás, en pleno encierro forzoso “aislamiento social preventivo y obligatorio”, colaboré en abandonsocios con un crack para la versión en castellano de The Humans. De ahí surgió la idea de hacer una guía paso a paso explicando el procedimiento, cosa que resultó bastante más difícil que el propio crack. Como la protección no es demasiado compleja, decidí orientar la guía a quienes ya tengan algún conocimiento de lenguaje ensamblador y quieran incursionar en el apasionante mundo de la desprotección de juegos antiguos. En esta primera entrega vamos a familiarizarnos con las funciones principales de un debugger, a medida que avancemos sobre el código de arranque del juego. ¿Qué es un debugger? Una herramienta interactiva que nos permite analizar un programa mientras éste se ejecuta.

Los pasos de la guía están descriptos para el debugger de DOSBox, porque fue la herramienta que utilicé al desproteger el juego y porque cualquier aficionado actual a los juegos de DOS seguramente está familiarizado con dicho emulador, ya sea que utilice Windows o Linux. Cabe aclarar que la instalación estándar no incluye el debugger; es necesario descargar un ejecutable alternativo y agregárselo. Si utilizamos Windows, debemos descargar el ejecutable correspondiente y moverlo a la carpeta donde tengamos instalado DOSBox. En caso de usar Linux, en esta página están disponibles los paquetes correspondientes, e instrucciones de instalación desde consola. Ambos enlaces corresponden a la versión vigente a la fecha (0.74-3).

Un pantallazo al debugger

Si iniciamos DOSBox con el nuevo ejecutable (dosbox-debug), en lugar de la habitual ventana de estado se abrirá otra llamada “DOSBox Debugger”. Pulsemos alt-pausa para activarla y observemos que está compuesta por cinco secciones, cada una encabezada por una barra color celeste con su título:

  • Register Overview: contenido de los registros del procesador
  • Data Overview: contenido de la memoria, en hexadecimal y ASCII
  • Code Overview: contenido de la memoria interpretado como código ejecutable
  • Variable Overview: contenido de variables definidas por el usuario
  • OutPut/Input: mensajes informativos

Las secciones Data, Code e Input/Output funcionan como ventanas que pueden desplazarse pulsando las teclas indicadas en el título («Scroll»).

Mientras tanto, el programa en ejecución ha quedado en suspenso, cosa que podemos comprobar viendo que el apuntador del DOS ya no parpadea. Para retomar la actividad y devolver el foco a la ventana principal, debemos indicarle al debugger, pulsando la tecla F5 (run), que deje correr libremente el código. Otra forma, quizás más útil, de tomar el control desde el debugger es hacerlo desde el comienzo del programa a analizar. Escribamos debug humans desde el directorio donde tenemos instalado el juego. Esta orden le dice a DOSBox que cargue el juego en memoria y que en vez de ejecutarlo le transfiera el control al debugger.

Quiero ver, quiero entrar

The Humans está protegido con un doc-check, es decir, la verificación de que el usuario tiene en su poder algún elemento adicional difícil de duplicar (instrucciones, rueda de códigos, etc.). Ni bien cargamos el juego, una señorita nos pregunta en qué página del manual aparece un dibujo determinado. Como esto ocurre incluso antes de la presentación, vamos a intentar seguir el flujo del programa hasta llegar a ese punto. Prestemos atención a la sección Code Overview. Estamos en la dirección cero del segmento de código actual (CS:0000) ante la instrucción jmps 0176. Cada vez que pulsamos F10 (step over), el debugger deja que se ejecute la instrucción actual (señalada en color verde) y avanza a la siguiente. Esto se refleja también en el apuntador de instrucción (registro EIP en la sección Code Overview).

Debugger screenshotVayamos avanzando paso a paso, sin profundizar demasiado en los detalles pero intentando seguir el hilo del programa. Nuestro objetivo es llegar rápidamente hasta el fragmento de código en que se encuentra la protección. Al llegar a CS:01CA encontramos un salto condicional hacia atrás. Si continuáramos como hasta ahora, pasaríamos cientos de veces por el mismo fragmento de código. ¡Podemos emplear mejor nuestro tiempo, incluso en cuarentena! Bajemos a la siguiente instrucción utilizando el cursor (CS:01CC, ver captura) y pulsemos F9 (breakpoint) para poner un freno, que el debugger señala en color rojo. Si ahora dejamos correr el programa (F5), como hicimos al principio, DOSBox ejecutará reiteradamente las instrucciones precedentes hasta que la condición deje de cumplirse. Recién ahí pasará a CS:01CC y al toparse con nuestro freno volveremos a tomar el control desde el debugger.

Continuemos paso a paso hasta CS:011D, donde encontraremos otro salto hacia atrás. La manera de saltearlo es la misma, y es una excelente oportunidad para poner en práctica lo que acabamos de aprender. Un poco más adelante, más precisamente en CS:0132, encontramos la última instrucción de este fragmento: jmp far word cs:[0138]. Pulsemos F10 una vez más y preparémonos para la siguiente etapa.

Del corazón pa’ dentro

Observemos que estamos otra vez en la dirección CS:0000, pero de un segmento de código distinto. Aunque no lo parezca, hemos avanzado bastante. Sin demasiado esfuerzo atravesamos la rutina de arranque y tenemos ante nuestros ojos el comienzo del programa principal ya desencriptado:

0000: call 0000816E
0003: call 000056BC
0006: call 00000677
0009: call 0000B43C         ;¿lobo, estás?
000C: mov  [00EF4],0000

De entrada nomás llama a cuatro subrutinas. Vayamos ejecutándolas una por una, pulsando F10. Las tres primeras terminan sin novedades, pero al pasar a la cuarta vemos la pantalla de carga y llega la señorita a pedirnos el número de página. Contestemos el dato correcto y veremos el logo de Mirage y el comienzo de la presentación antes de que el debugger recupere el control en CS:000C.

Con esto ya podemos dirigir nuestro análisis a la subrutina que comienza en CS:B43C. Y así debemos seguir como si se tratase de una muñequita rusa, pelando capa tras capa hasta llegar al meollo de la cuestión. Pero como esto se ha hecho ya bastante largo, vamos a dejarlo aquí hasta la próxima entrega.

Basic 8 ROM para Commodore 128

Pensando en estrenar una MiniPro TL866 me propuse grabar una function ROM para la Commodore 128 de mi viejo. Alguna vez habíamos charlado sobre la posibilidad de poner el Basic 8 en el zócalo interno, así que probé la versión disponible en internet con el emulador VICE antes de grabar nada. El resultado fue decepcionante.

La versión en cuestión se activa manteniendo pulsada la tecla Control al encender o reiniciar. A partir de ahí muestra el copyright habitual de la C128, va en busca de un sector de arranque, luego muestra un cartelón de Basic 8 y, finalmente, muestra un copyright con el agregado de la referencia a WalruSoft. Todo el proceso lleva casi 9 segundos.

Para peor, se cuelga si es activada en modo de 40 columnas, y no funciona como ROM externa, es decir que no puede colocarse en un cartucho ni en el zócalo disponible en las expansiones de memoria de Commodore. Seguro de que existe una manera de hacerlo mejor, me puse a refrescar mis conocimientos sobre el tema con la invaluable ayuda del 128 Internals (Abacus), Mapping the Commodore 128 (COMPUTE! Publications) y especialmente EPROM’s Programmer Handbook (CMS).

CONSIDERACIONS GENERALES

Lo primero es comprender que una function ROM en la C128 puede activarse en dos momentos diferentes durante el ciclo de arranque: al comienzo de la rutina RESET o más tarde, durante el inicio del BASIC, por la rutina PHOENIX. Para ello existen dos vectores al comienzo de la ROM —denominados Coldstart y Warmstart— a los que se accede mediante un JSR. Esto permite a una ROM tomar el control de la máquina o realizar alguna tarea preparatoria y continuar el arranque devolviendo el control mediante un RTS. Que una ROM se active dependerá de su ID, que es el siguiente byte presente a continuación de los vectores. Si es cero, el Kernal no la activará nunca, aunque es posible acceder a ella manualmente luego de terminado el arranque.

La cosa se pone interesante cuando el ID es distinto de cero. En ese caso, la rutina PHOENIX cederá el control a la ROM mediante un JSR al vector Coldstart. Si ésta le devuelve la gentileza, intentará como último paso cargar un sector de arranque desde la unidad 8 y, en caso de no encontrarlo, dará por finalizada su tarea con un RTS, lo que nos deja en manos del editor de BASIC. Sobre este punto volveremos para evitar que se intente cargar el sector de arranque cuando optemos por activar Basic 8.

Dijimos que una function ROM podía activarse también tempranamente durante la rutina RESET. Esto sólo ocurrirá si dicha ROM lleva un ID igual a 1. De aquí surgen dos cuestiones a tener en cuenta. La primera y principal es que si la ROM no toma el control al ser activada durante RESET, volverá a ser activada por PHOENIX. Y la segunda es que en ambos casos se utilizará el vector Coldstart, es decir que Warmstart no es utilizado en ningún caso. Quizás la intención original haya sido que RESET llamase a Coldstart y PHOENIX a Warmstart, pero es sólo una conjetura.

VOLVER A LA FUENTE

Existieron dos ediciones de Basic 8. Primero fue publicado por Patech Software en 1987 (aunque los copyrights de WalruSoft dicen 1986) y luego en 1989 por Free Spirit Software, sin diferencias a la vista (sólo el mensaje de copyright de la aplicación WIOS dice ahora 1986,89). El código en sí fue levemente alterado con un parche que se carga en $B00 ("p.basic8.patch") y dos saltos desde el código principal $B00 y $B10. Increíblemente, ¡la versión para ROM disponible en internet tiene el código actualizado pero no el parche! Un cuelgue garantizado al momento en que se intente acceder a alguno de esos cambios.

El cargador original ("BOOT ED") está escrito en BASIC y carga tres bloques de código: el bloque principal (a partir de $1300), el parche (a partir de $0B00) y las imágenes de los punteros del ratón (a partir de $0E00). En el camino, cambia la definición de algunas teclas de función, invoca el cartelón de Basic 8 mediante la orden @WALRUS,0 e imprime un mensaje de copyright donde agrega la mención a WalruSoft. Un detalle antipático es que, además de poner el editor en modo mayúsculas/minúsculas, intenta desactivar la posibilidad de revertirlo manualmente mediante la impresión de CHR$(8). Dicho código produce el efecto deseado, ¡en la Commodore 64! Para la Commodore 128, el correcto habría sido CHR$(11).

Con toda esta información, podemos pensar en qué queremos lograr con esta ROM nueva. A mi modo de ver, optar por utilizar Basic 8 implica que no vamos a cargar otra aplicación, por lo que el acceso al sector de arranque debe ser anulado. Tampoco es necesario el cartelón de WalruSoft cada vez que usemos el editor, y a éste es mejor dejarlo en mayúsculas, como estamos acostumbrados a usarlo. El hecho de que Basic 8 cuelgue en modo de 40 columnas es propio de su código, así que para evitarlo vamos a hacer que la ROM sólo pueda activarse estando en modo de 80 columnas, y pulsando la tecla Alt al encender o reiniciar.

Dado que Basic 8 es un agregado al BASIC 7, vamos a dejar que nuestra ROM arranque durante PHOENIX, cargue el código como si lo hubiésemos cargado desde diskette y devuelva el control. Para eso necesitamos escribir una rutina que copie los tres bloques de código máquina, más el cargador en BASIC, modificado a nuestro gusto, y luego coloque la secuencia "rU"+CHR$(13) para que al volver al editor de BASIC lo active por nosotros. El último paso restante es el de anular el arranque desde diskette. Resulta que no está previsto en la rutina PHOENIX pasar por alto esa secuencia, pero si observamos el comportamiento, surge una solución elegante. Dijimos que PHOENIX cede el control a nuestra ROM mediante con JSR; nosotros lo devolveremos a su vez con RTS, PHOENIX buscará un sector de arranque y —si no lo encuentra— cederá el control al editor de BASIC con otro RTS. La respuesta está, entonces, en quitar de la pila la dirección de retorno a PHOENIX, para que nuestro RTS vaya directo al editor. A fin de evitar confusiones, actualizé también el mensaje «copr. 1986» en el cartel que genera la instrucción @WALRUS por «(c)1986,89». Los demás detalles pueden leerse en el código que incluyo a continuación.

* = $8000
            nop                 ;vector coldstart
            nop
            nop
            jmp     start       ;vector warmstart
            !byte   2           ;ROM ID (activar durante PHOENIX)
            !text   "CBM"
start       bit     $d7         ;estado de la tecla 40/80 columnas
            bpl     return
            lda     $d3         ;estado de las teclas de control
            and     #$08        ;Alt pulsada?
            beq     return
            sei
            lda     $ff00       ;quitar I/O del medio
            ora     #$01
            sta     $ff00
            lda     #$00        ;mover bloque principal a $1300
            sta     $fb
            sta     $fd
            lda     #>code
            sta     $fc
            lda     #$13
            sta     $fe
            ldx     #$5e        ;5d páginas de código máquina +1 de BASIC
            ldy     #$00
loop_pge    lda     ( $fb ), y
            sta     ( $fd ), y
            iny
            bne     loop_pge
            inc     $fc
            inc     $fe
            dex
            bne     loop_pge
            ldy     #$00        ;mover punteros del ratón a $E00
loop_ptr    lda     pointers, y
            sta     $e00, y
            iny
            bne     loop_ptr
            ldy     #$1f        ;mover el parche a $B00
loop_ptc    lda     patch, y
            sta     $b00, y
            dey
            bpl     loop_ptc
            lda     #$70        ;cambiar el comienzo del BASIC ($7001)
            sta     $2e
            lda     #$52        ;poner la secuencia "rU" + RETURN en el buffer del teclado
            sta     $34a
            lda     #$d5
            sta     $34b
            lda     #$d
            sta     $34c
            lda     #$3
            sta     $d0
            pla                 ;quitar la dirección de retorno de PHOENIX de la pila
            tax                 ;y así pasar directo al editor
            pla
            tay
            pla
            pla
            tya
            pha
            txa
            pha
return      rts

* = $8100
code        !binary "p.basic8.prg",$5d00,2 ;código principal seguido del cargador BASIC
            !byte $00,$27,$70,$0A,$00,$8F,$20,$42
            !byte $41,$53,$49,$43,$20,$38,$20,$46
            !byte $55,$4E,$43,$54,$49,$4F,$4E,$20
            !byte $52,$4F,$4D,$20,$42,$59,$20,$44
            !byte $49,$45,$53,$54,$52,$4F,$00,$35
            !byte $70,$14,$00,$FE,$25,$3A,$9E,$20
            !byte $34,$38,$36,$34,$00,$5E,$70,$1E
            !byte $00,$F9,$20,$31,$2C,$22,$40,$54
            !byte $45,$58,$54,$3A,$46,$41,$53,$54
            !byte $22,$AA,$C7,$28,$31,$33,$29,$3A
            !byte $F9,$20,$34,$2C,$22,$22,$3A,$F9
            !byte $20,$38,$2C,$22,$22,$00,$97,$70
            !byte $28,$00,$99,$20,$22,$93,$11,$22
            !byte $3B,$A6,$32,$33,$29,$3B,$22,$9F
            !byte $57,$41,$4C,$52,$55,$53,$4F,$46
            !byte $54,$20,$9A,$42,$41,$53,$49,$43
            !byte $20,$38,$9F,$20,$31,$30,$30,$38
            !byte $36,$31,$20,$42,$59,$54,$45,$53
            !byte $20,$46,$52,$45,$45,$22,$00,$BE
            !byte $70,$32,$00,$99,$20,$A6,$32,$38
            !byte $29,$3B,$22,$28,$43,$29,$31,$39
            !byte $38,$36,$2C,$38,$39,$20,$57,$41
            !byte $4C,$52,$55,$53,$4F,$46,$54,$20
            !byte $49,$4E,$43,$2E,$22,$00,$C4,$70
            !byte $3C,$00,$A2,$00,$00,$00
            !align $ff,0,$ff
pointers    !binary "p.ptrdefs.prg",$100,$302 ;punteros para el ratón
patch       !binary "p.basic8.patch.prg",$20,2 ;parche edición 1989
            !align $ffff,$ffff,$ff

* = $ffff   !byte $ff

* = code + $474d 
            !pet "(c)1986,89" ;actualizar el mensaje del comando @WALRUS
            !to "Basic 8 [DST].bin", plain

Descargar / Download

Construyendo un adaptador para filtro de agua

Jamás imaginé, mientras compraba un purificador de agua, que me vería obligado a incursionar en el Diseño Asistido por Computadora para poder instalarlo. Después de probar sin éxito con los adaptadores provistos por el fabricante, comencé a recorrer ferreterías y casas de sanitarios en busca de una reducción apropiada. Pero los días pasaban entre promesas difusas y excusas repetidas y la bendita pieza no aparecía.

Llave de paso del filtro de agua, instalada con el adaptador impreso.
Llave de paso del filtro de agua, instalada con el adaptador impreso.

Entonces se me ocurrió preguntarle a mi amigo taos78, que desde hace varios meses está metido en el asunto de las impresoras 3D, si sería factible imprimir la pieza. Confieso que dudaba de la precisión de estos aparatos para producir una rosca funcional, pero quedamos en que yo prepararía un modelo usando FreeCAD y él se encargaría de la impresión.

AdaptadorMkI
Primer intento. Nótese que se trata de dos piezas encimadas.

Después de instalar el software e investigar en los foros para hacerme una idea de por dónde empezar, encontré un hilo donde el usuario ulrich1a ofrecía un perfil de la rosca ISO, junto con unas instrucciones poco intuitivas para construir una superficie roscada. Con un poco de paciencia y tomando las medidas de un modo bastante precario, armé el primer prototipo del adaptador.

A pesar de estar siempre muy ocupado (y eso que ya no se dedica a catar flanes mixtos), al día siguiente taos78 me trajo la pieza, de una calidad sorprendentemente buena. También me advirtió sobre errores en el modelo y el hecho de que la pieza, tal como fue creada, era hueca por dentro. Así y todo la rosca superior calzó perfectamente en la canilla, pero la parte inferior no tenía las dimensiones correctas (mala mía).

Para el segundo prototipo tomé las medidas con más cuidado, y me propuse encontrar un método más intuitivo para crear las roscas. Partiendo del perfil ISO, igual que antes, esta vez creé un helicoidal del mismo díametro que la rosca buscada y se lo resté a un cilindro macizo. Corregí el paso de la rosca superior, de 1mm a 1,5mm, y agregué un suplemento en el centro, para que la rosca inferior quedara a nivel.

Aquí volví a equivocarme, ya que la idea original era que el suplemento central fuera angosto y sirviera para calzar un anillo de goma que -de paso- asegurara la hermeticidad. Pero el angostamiento iba a requerir material de soporte para su correcta impresión, entonces lo remplacé a último momento por un sólido que me quedó demasiado grande. Una pena porque, como puede verse en la foto, el resultado impreso (en negro) quedó muy bien.

Obligado a corregir el modelo de la pieza, decidí rehacerlo desde el comienzo, incorporando detalles que de entrada me parecían «pedir demasiado». Finalmente, el adaptador quedó compuesto por una base roscada de 22mm de diámetro, con un paso de 1mm, y 4mm de altura. Antes de roscar el cilindro, le apliqué un bisel que sirve en su base para centrar la rosca, y en su parte superior para coincidir con el suplemento central, de 20mm de diámetro.

Barolo sostiene pieza final.
Barolo quizo participar del proceso constructivo.

Éste tiene una curva en su parte superior que facilita su asentamiento en el interior de la canilla (también de 1mm, para coincidir con la parte superior del adaptador). La parte superior consiste en otro cilindro roscado, de 18mm de diámetro una rosca de 1,5mm de paso, con bisel en la parte superior. El conjunto se completa con un calado hexagonal que, además del paso del agua, permite ajustarlo a la canilla mediante una llave Allen.

Delirios de pinball

Durante muchos años, probablemente debido a la complejidad técnica, las máquinas de pinball estuvieron a salvo de sus pares electrónicos. Muchas fueron las adaptaciones que intentaron capturar, con escaso éxito, la esencia del juego. Algunos, aceptando la imposición del formato horizontal de los televisores, ensayaron con mesas más anchas que largas; otros respetaron la proporción natural, pagando el costo con una pantalla dividida o reduciendo considerablemente el tamaño de la mesa.

Night Mission
La versión 3.0 de 1988

Incluso hubo ideas ingeniosas como el Pinball Construction Set (una especie de Meccano de los pinball), pero los resultados no llegaban muy lejos: salvo honrosas excepciones como el Night Mission Pinball de SubLOGIC, no recuerdo haberme entusiasmado con ninguno.

No fue hasta 1992, y no es de sorprender que el milagro haya ocurrido en la Amiga, que la entonces desconocida Digital Illusions creó el genial Pinball Dreams. Por primera vez estaban todos los ingredientes necesarios para producir un juego adictivo, comenzando por una mecánica suficientemente realista, siguiendo con una mesa vertical que se desplaza suavemente por la pantalla, y terminando con un acompañamiento musical de primera. A partir de ahí fue «un partidito más» mañana, tarde y noche…

Party LandLa propia Digital Illusions demostró, sólo unos meses después, que no estaba dicha la última palabra destronando a su propio juego con el increíble Pinball Fantasies. Tras sólo unas horas me convertí en un cobayito cuya única satisfacción era efectuar sucesivos tiros a las rampas opuestas para escuchar el «Five million!» y así seguir aumentando la cuenta. Es el día de hoy que la mesa Party Land sigue siendo una de mis favoritas.

No hay dos sin tres

Entonces llegó 1995 y un día como cualquier otro mi entonces amigo y compañero de estudios Bosterix me pasó el dato de que el flamante Pinball Illusions estaba disponible en uno de los BBS que frecuentábamos. Conectado a las 5 de la mañana terminé de descargar el lanzamiento de Hybrid de la versión en CD: un enorme ejecutable de 50MB, una presentación cinematográfica donde Digital Illusions se jactaba con toda razón de su impecable trayectoria, cuatro mesas distintas y al elegir cualquiera de ellas… silencio absoluto.

Así fue la «era dorada» del CD-ROM: los cráneos de las editoriales vieron al nuevo soporte y sus inconmensurables 650MB como una nueva variante de llave anti-piratería, para lo cual a todo había que agregarle animaciones en 3D, voces (¡como en las odiosas versiones parlantes de las aventuras de Lucas Arts!), fragmentos fílmicos de dudosa calidad o simplemente, como en este caso, poner la música como pistas de audio.

Para colmo, 21st Century Entertainment sacó también una versión en sólo tres diskettes que traía toda su música sintetizada, pero no incluía la cuarta mesa (Viking Tales) que, lógicamente, se convirtió en mi favorita. Y no fue solamente por ser la «figurita difícil» sino también porque la canción de apertura, coreada por una manga de vikingos ebrios (bueno, cada uno…) a la manera en que hoy se corea el Himno Nacional Argentino en los estadios, era francamente irresistible.

Pero volviendo a la cuestión que nos ocupa, ocurrió que una vez, en medio de alguno de esos baches auditivos, tuve que poner pausa para atender un llamado y cuando retomé el partido aconteció algo inesperado: el silencio se volvió música. Era evidente que el ejecutable de Pinball Illusions CD incluía toda la música original, sólo que en algunas circunstancias ésta era enmudecida para ceder el micrófono (¡je!) a su versión alternativa en CD-Audio.

Pasó el tiempo, estudios, trabajo y familia, y hace algunos días caí en cuenta que se cumplen nada menos que 20 años de la aparición de este juego, ¿y qué mejor para conmemorarlo que hacer una edición que funcione al 100% sin necesidad del CD?

DPMI o no DPMI. ¿Es ésa la cuestión?

A diferencia de sus antecesores, Pinball Illusions viene empaquetado en un solo archivo de unos 4MB (trabajé sobre la versión incluída en el Pinball Power Pack, para no arrastrar los 40 y pico de megabytes de relleno de la versión original). El juego trabaja en modo protegido y se rehusa a funcionar bajo Windows, lo cual nos cierra la posibilidad de usar SoftICE para inspeccionarlo -o quizás no.

Después de una pequeña rutina en CS:0070 que desencripta el stub, podemos hacer un vuelco de 5210h bytes a partir CS:0100, y observar el código tranquilos. Lo primero que vemos, en texto plano, son algunos mensajes interesantes sobre DPMI y compatibilidad con Windows.

Efectivamente, en CS:16C9 está la cadena «DPMI detected» y es mostrada por una rutina huérfana que empieza en CS:1667. Veamos si podemos rehabilitarla. Las subrutinas de detección de distintas API de gestión de memoria colocan en CS:503F el tipo detectado, que luego es comparado en la rutina CS:01BA. Es ahí donde deberíamos interponer la llamada a esta subrutina olvidada. Pero hay algo más: si leemos la API de DPMI nos daremos cuenta de que la subrutina da por hecho que existe el DPMI y comienza por guardar los valores que devolvería la llamada para tantear la presencia del servicio, pero esta no aparece por ninguna parte. Una manera de rehabilitar esta opción, entonces, es desviar la llamada en CS:01EC hacia una rutina que haga:

mov ax, 1687h
int 2Fh
test ax, ax
je 1667 ;hay DPMI, llamo a la rutina
jmp 15B6 ;no pasó nada, sigo con lo que venía

Esto me permitió finalmente usar el debugger (bajo Windows 98), pero entre la lentitud con que funciona y una placa de video poco compatible, lo que parecía prometedor se redujo a pequeños avances: sólo adelanté en eliminar el cd-check, que no tenía nada de especial, excepto por la cantidad de checksums que lo protegen.

Entonces empecé a seguirle el rastro a las llamadas al MSCDEX, y encontré un patrón que me permitió ubicar la API de sonido en CS:98A0 y con ello los puntos en los que se silenciaba la música sintetizada: al volver al menú de selección de mesas, en el attract mode, y ante los eventos (mini juegos y fanfarrias al entrar en el marcador). Pero había algo en el modo de sortear este último caso que no me terminaba de convencer…

Las comparaciones no siempre son odiosas

Exultante por lo conseguido hasta acá, y con un panorama general de la situación en la cabeza, quería cerciorarme de que la solución fuera la correcta. Entonces recurrí al IDA (en su versión gratuita) para generar listados del código de las versiones en diskette y en CD, y poder así cotejar las diferencias en la rutina que me inquietaba. Después de algunas horas de concentración, apareció clarito ante mis ojos el punto ideal para evitar por completo toda la rutina adicional y disfrutar, ahora sí, del juego con toda su música.

Ya envalentonado le anulé también la rutina que grababa la configuración en un archivo externo y la necesidad de anteponer una barra (/) a las opciones (ver con ILLUSION ? la lista). Para terminar, restaba implementar el parche. En el stub, cuando estaba rehabilitando el DPMI, me crucé con el siguiente fragmento:

mov ah, 000h
int 094
call 1112 ;llamada inútil a un RET
call 1308 ;descompresión del juego en memoria

La primera llamada es la clave para intercalar las modificaciones al código. Invirtiendo el órden para que quede a continuación de la descompresión, nos queda un call libre para aplicar los cambios. Sólo debemos buscar un lugar no utilizado para escribir el parche y asunto terminado.

El resultado final de todo este trabajo puede aplicarse sobre cualquier versión original (i.e. no modificada) de Pinball Illusions CD, ya sea la de 1995 que ocupaba unos 50MB ó, mejor aún, la que se incluyó en las recopilaciones Pinball Power Pack y Pinball Gold Pack, que no están infladas y ocupan sólo 4MB, aproximadamente. Para eso, les dejo para descargar el parche completo. Un crack.

Pinball Illusions 20th Anniversary

Dedicado a Anita

Au revoir, Bonjour!

Después de desactualizar mi portátil nueva a Windows 7, llegó el momento de reinstalar las aplicaciones y otra vez la cuestión del insufrible iTunes: Instalarlo o no instalarlo, he ahí el dilema. Detesto el iTunes, el QuickTime y todas las porquerías innecesarias que instalan. Pero para leer, no he visto nada mejor que un iPad con pantalla Retina.

Lamentablemente, no hay hasta el momento alternativa al software oficial de Apple para gestionar el contenido de la tablita: los programas que se ofrecen como tales requieren invariablemente que se instale el iTunes para poder acceder al dispositivo. Así que descargué la versión 12 para Windows de 64 bits y me dispuse a tratar de instalarlo de la manera menos obstrusiva posible.

El primer paso fue extraer el contenido del instalador itunes6464.exe. De entrada vemos dos componentes que no queremos tener dando vueltas: AppleSoftwareUpdate.msi y Bonjour64.msi (sumémosle SetupAdmin.msi, ya que estamos). Probé hacer la instalación con las conocidas opciones DESKTOP_SHORTCUTS=0 MEDIA_DEFAULTS=0 y SCHEDULE_ASUW=0, pero sólo las dos primeras funcionaron. No es un gran problema, salvo para instalaciones desatendidas.

Buscando en el sitio de Apple llegué a la página donde explican cómo restringir ciertas opciones en Windows, entre ellas la búsqueda automática de actualizaciones y el acceso al iTunesStore. Con eso me di por satisfecho, así que instalé AppleApplicationSupport64.msi, AppleMobileDeviceSupport6464.msi e iTunes6464.msi, y creé la clave del registro para bloquear los componentes mencionados:

reg add "HKLM\SOFTWARE\Apple Computer, Inc.\iTunes\Parental Controls\Default" /v "AdminFlags" /t REG_DWORD /d "0x104" /f

Libre de otras criaturas despreciables, conecté mi iPad y ahí mismo se colgó el iTunes. Volví a abrirlo e intenté acceder a las preferencias, para ver si estaba seleccionado algún componente de los que quedaron afuera, pero eso también colgó al iTunes. Otras opciones, como reproducir música, funcionaban. Después de buscar sin éxito en la web, probé instalando el AppleApplicationSupport.msi y… ¡todo comenzó a andar! Ahora se entiende por qué la diferencia en los nombres de los componentes: los que tienen el sufijo «6464» parecen ser completamente de 64 bits, mientras que los que sólo dicen «64» dependen de los correspondientes componentes de 32 bits.

Para redondear, de los archivos extraídos del instalador, hay que conservar:

  • AppleApplicationSupport.msi
  • AppleApplicationSupport64.msi
  • AppleMobileDeviceSupport6464.msi
  • iTunes6464.msi

Y se puede agregar un archivo por lotes con lo siguiente:

msiexec /i "AppleApplicationSupport.msi" /passive /norestart
msiexec /i "AppleApplicationSupport64.msi" /passive /norestart
msiexec /i "AppleMobileDeviceSupport6464.msi" /passive /norestart
msiexec /i "iTunes6464.msi" /norestart
reg add "HKLM\SOFTWARE\Apple Computer, Inc.\iTunes\Parental Controls\Default" /v "AdminFlags" /t REG_DWORD /d "0x104" /f

Cambio de mandos

A raíz del clon del Atari sin soporte para paddles, me puse a leer un poco la Stella Developer’s Guide para ver qué tan complejo sería adaptar un juego como el Tac-Scan de SEGA, donde el control proporcional no parece escencial, para que funcione con joysticks. Resultó ser un desafío simple pero entretenido.

TacScan screenshot

Como en tantas otras cosas, el Atari es muy básico a la hora de lidiar con los potenciómetros de los paddles: en vez de devolver un valor concreto en un momento dado, requiere que el programador lleve la cuenta del tiempo que tarda en cargarse un capacitor y en base a eso determine la orientación del control.

Tras una pasada del DiStella, pude ver que el Tac-Scan utiliza las direcciones $BB y $BC para tal fin. En $F7C9 comienza la rutina que, a partir del valor almacenado en $BC, carga el acumulador (A) con un offset ($81, $9B ó $B5) a la imagen de la nave, y el registro X con el bit 3 encendido si hay que «espejarla». En el registro Y se carga la orientación de la nave, con valores entre $FE y $02 (-2, -1, 0, 1 y 2), donde 0 es el norte.

Para empezar, fue necesario apartar dos direcciones de RAM para almacenar la última posición registrada del joystick y un contador que nos permita controlar la velocidad de reacción de los mandos. ¿Qué mejor que las mencionadas $BB y $BC? Así que comencé por anular todas las instrucciones del estilo INC $BB, STA $BC y las llamadas a la rutina en $FFEB, que también las afectaba. A partir de ahí, mi objetivo fue escribir una rutina que se ocupara de cargar los registros con los valores correctos y que cupiera en el mismo espacio de la rutina original, para evitar reubicar código y lograr que el resultado difiriera en la menor cantidad de bytes posibles.

Luego de un par de intentos, pude escribir una que calzara justo entre $F7C9-$F80C (68 bytes) más una pequeña tabla de offsets colocada a partir de $FFEC. Algunas operaciones pueden parecer redundantes, pero gracias a ellas se resuelve también el valor inicial, cuando todavía no se han tocado los controles, para que la nave tenga la imagen correcta y esté apuntando hacia el norte. El código es el siguiente:

LF7C9: LDX #$08
       LDA SWCHA         ;lee estado de las palancas
       AND #$C0          ;descarto lo que no sea izq/der
       CMP $BC
       BEQ SAME          ;misma posición de la palanca
       STA $BC
       LDY #$0A          ;restablezco el contador
       STY $BB
 SAME: CMP #$C0
       BEQ DFLT          ;palanca al centro -sin cambios
       DEC $BB           ;decremento retardo
       BNE DFLT
       LDY $F0
       CMP #$80
       BEQ LEFT
       CPY #$02
       BEQ VOID
       INY
 VOID: JMP UPDT
 LEFT: CPY #$FE
       BEQ UPDT
       DEY
 UPDT: STY $F0           ;guardo la dirección calculada
       LDY #$0A          ;restablezco el contador
       STY $BB
 DFLT: LDY $F0
       BPL POS
       LDX #$00
       TYA
       EOR #$FF
       CLC
       ADC #$01
       TAY
  POS: LDA CABJ,Y        ;tabla de punteros
       LDY $F0
       ...
 CABJ: .byte $81,$9B,$B5,$0D,$10,$50

Para compilar el código fuente y generar un binario que podamos correr en un emulador necesitamos el DAsm, un maravilloso ensamblador para 6502, 6811, 68HC11 y 68705. La órden que necesitamos es la siguiente:

DASM tacscan.s -otacscan.bin -f3

Si lo que te interesa es la versión modificada, podés aplicarle mi parche completo a la ROM usando el Hack-O-Matic 3, utilidad que recomiendo, además, para hacer otro tipo de modificaciones a los juegos del Atari.

2600S: Un clon del Atari

Mudanzas. Ocasiones ideales para hacerse de algún que otro aparato destinado a la basura. Así fue como cayó en mis manos este clon de Atari Junior que yo mismo miré con desdén, hasta que vi, en una esquina de la chapa frontal, el texto “160 GAMES BUILT IN”. Ahí nomás, busqué una fuente apropiada y cable de antena para conectarlo a la tele y comprobar que funcionara.

Bobby Goes Home screenshot
Robertito en RF.

La primera impresión fue bastante pobre, por la mala calidad de la imagen. Claro que el modulador que traen estos aparatos debe ser infinitamente más berreta que el de un Atari original. Por lo demás, la consola funcionaba y pulsando el botón de reset (que ocupa el lugar del selector de canales) se podía ir pasando por cada uno de los «160» juegos incluidos.

Algo que me confundió en un comienzo fue que los juegos no parecían seguir una secuencia fija y que al mover el interruptor de dificultad izquierdo, la consola se colgaba. Entonces me di cuenta de que dicho interruptor estaba siendo utilizado para elegir entre dos bancos de juegos. En realidad, una posición permite acceder a la totalidad de los 128 juegos incorporados y la otra a los últimos 32 (128+32=160). Ya vemos cómo viene la mano…

Entre los juegos en sí, hay una mezcla de versiones truchas (con los mensajes de copyright borrados), modificados (como la imperdonable inclusión del «Tomboy» en lugar del clásico Pitfall!), y hasta algunos para el sistema PAL (la consola es NTSC). No son exactamente los juegos que yo habría elegido, pero hay bastantes como para encontrar unos cuantos entretenidos.

Mirando bajo la carcasa

La consola está construida sobre una plaqueta monocapa cuyas únicas inscripciones son: 2600S, LMT y AV-1, denominaciones que no arrojaron ningún resultado en la web. Toda la funcionalidad del Atari está implementada en un solo integrado. Completan la circuitería un contador de 7 bits, un integrado con 4 compuertas NAND y otro con 6 inversores; más una ROM de 512Kb (32 pines) que contiene los 128 juegos.

2600S_anverso
Anverso

El integrado principal es de 48 pines, aunque no es el mismo que el utilizado en los Atari “Unicorn”, ya que el pinout difiere. Su principal falencia es que carece de las líneas de entrada para paddles (y teclados), lo cual lo hace incompatible con clásicos como Breakout o Indy 500. Tampoco está presente la línea correspondiente al selector Color/BW (de hecho, el interruptor de la carcasa no está conectado a ninguna parte). Esto deja afuera a un puñado de juegos más, como el Star Raiders o Space Shuttle, que lo utilizan como un control adicional.

2600S_reverso
Reverso

Como punto a favor, la salida de video es provista mediante dos señales -Chroma y Luma- sin necesidad de armar una escalera de resistencias y combinarla con la señal de sincronismo como en los Atari auténticos. La salida de audio es monoaural.

Además de la ROM de 32 pines, la plaqueta prevé la posibilidad de instalar dos ROM de 28 pines, ya sea en lugar de la de 32 pines, o conjuntamente con ésta, en cuyo caso se podría aumentar la cantidad de juegos incluídos.

Circuitos adicionales

El contador, que se incrementa con cada pulsación del reset, produce los desplazamientos por la ROM en bloques de 4K y, junto con las compuertas NAND y los inversores aportan la lógica para alternar entre las dos ROM de 28 pines (en caso de estar presentes), y deshabilitar la ROM interna en caso de que haya un cartucho conectado en la ranura.

Siguiendo el trazado del selector de dificultad izquierdo, se puede ver que en la plaqueta está prevista su conexión para que funcione como tal, o como selector entre una ROM de 32 pines y dos ROM de 28 pines, actuando sobre sus patillas de OE (output enable). Esa configuración permite acceder a un total de 256 juegos de hasta 4K. En cuanto a la línea de dificultad izquierda en sí, puede quedar sin conectar (dificultad B) o con el puente J8 a tierra (dificultad A).

En el caso de este aparato, no se respetó ninguna de las configuraciones previstas: el selector está unido mediante un cable (azul) al uno de los inversores (cuya pista de entrada está cortada),  y lo único que hace es saltar a los últimos 32 bloques de 4K para simular la existencia de 32 juegos adicionales. Las patillas de entrada de ambos selectores de dificultad están unidas, en el IC principal, mediante exceso de estaño, de modo que ambos jugadores comparten la el estado del interruptor derecho. En mi opinión, es una verdadera lástima, ya que la posibilidad de equilibrar un partido entre jugadores con diferentes habilidades siempre me pareció un detalle bien pensado. Además, algunos juegos (me viene a la mente el Venture) usan combinaciones de ambos selectores para fijar el nivel de dificultad.

Correcciones y mejoras

El puente de cable para simular 32 juegos adicionales no justifica la pérdida del control de dificultad izquierdo. Llegado el caso, se podría usar el interruptor Color/BW para seleccionar entre distintos bancos de juegos. Así que lo primero que hice fue desoldar el puente de estaño entre las patillas del integrado principal, remover el cable azul y conectar el puente J59. De este modo, ámbos controles de dificultad funcionan tal como lo harían en un Atari auténtico. Basta probarlo con el «Rabbits» (versión trucha del Freeway): al atropellarnos en dificultad «B» (experto) volvemos a la vereda, mientras que si sólo nos hace retroceder unos pasos, está en dificultad «A» (principiante).

Circuito amplificador.
Circuito amplificador.

Hecha esta corrección simple, el siguiente paso fue dotar a la consola de una salida de video como la gente. Lo ideal habría sido sacar S-Video, aprovechando que el integrado nos da las señales necesarias, pero como el televisor que tenía a mano sólo aceptaba video compuesto, opté por esa variante.

Después de una prueba rápida, con un par telefónico y una ficha RCA, decidí desmontar toda la caja del modulador y la etapa anterior, donde se combinan el audio y el video. Usando los mismos componentes que había desoldado (1 x 2N3904, 1 x R2K2 y 1 x R3K3) monté un circuito de amplificación de video como el que ofrecen en Vintage Gaming and More, aprovechando las pistas que quedaron libres sobre la placa principal. El resultado habla por sí mismo:

Frogger screenshot
Frogger por salida compuesta.

La última modificación que se me ocurre, para convertir este aparato en una consola digna de admiración es reemplazar la ROM que trae de fábrica por una EPROM que contenga una mejor selección de juegos. Pero eso tendrá que esperar a que consiga una grabadora…

Hablar bien no cuesta un carajo

Pocas cosas hay más útiles que un diccionario en la PC, siempre listo y a mano para sacarnos las dudas sobre el uso de tal o cual palabra. Por eso, saber que existe una versión del fabuloso Diccionario de uso del español de María Moliner para Windows me llenó de alegría.

Sin embargo, su interfaz no está libre de problemas. Una falla en particular hace que cada cierto tiempo se encapriche y nos pida que insertemos el CD de instalación, sin otra razón aparente que hacernos levantar para buscar el bendito disco y echar por la borda la comodidad de tenerlo instalado.

Montando una simple imagen del disco se resuelve el problema, pero molesto por lo chapucero de esa solución me decidí a ir al grano y darle masa al problema.

«Para nosotros, para nuestra posteridad…»

El diccionario (me refiero a la versión 2.14.1, que abunda en las redes P2P) fue escrito en Visual Basic 6.0, y aparentemente registra información sobre los accesos en archivos cifrados (00.SIG, 01.SIG y 10.SIG) que conservan su fecha del año 2001, para que no nos percatemos a simple vista cuando son actualizados. Todo muy lindo, pero es bastante fácil de pasar la protección por alto, como veremos a continuación. A pesar de todo, al día de hoy no hay un solo crack disponible; se ve que los diccionarios no son tan populares como el Call of Duty

Vamos a aprovechar esta ocasión para probar el VB Decompiler en su versión «Lite», que si bien carece de algunas características útiles de la versión «Pro» (como la posibilidad de aplicar parches), es gratuita y cubre ampliamente nuestras necesidades. Ni bien abrimos la herramienta, vamos a ‘Tools/Options’ y ahí tildamos la opción ‘Add HEX dump’ para que incluya las secuencias de bytes correspondientes a las instrucciones desensambladas, cosa que nos permitirá compensar la mencionada falencia de la versión «Lite». Vayamos a ‘File/Open program’ y seleccionemos DUE.EXE (el ejecutable principal del diccionario). Nos preguntará si queremos descompilar el programa ahora; cliqueamos en ‘No’ y nos vamos a tomar unos mates.

Un par de cebadas después, repetimos los pasos anteriores pero esta vez contestamos que sí a la pregunta del millón. Después de trabajar un ratito (el VB Decompiler; nosotros seguimos con el mate) nos avisará con el mensaje «Decompiled OK» que ha terminado. Tenemos a la vista un panorama general de la estructura del programa. Aquí nos conviene echar un vistazo para ver si algo nos resulta llamativo. Entre los módulos aparece uno llamado DUEProtection que parecía prometedor, pero sólo me hizo perder tiempo. Hagamos doble-clic sobre Sub_Main. Unas pocas líneas después del comienzo aparece la primera cosa realmente interesante: una llamada (call), una comprobación (test) y un salto condicional (jnz) que en caso de no producirse termina en una llamada a la rutina «End».

VB Decompiler screenshot
La llamada.

¿A dónde se dirige la llamada? La rutina 601DD0h se encuentra en el módulo WindowsModule, que ¡oh sorpresa! hace referencia a la cadena «Inserte el CD-ROM del DUE en el lector». Mi primer impulso fue cambiar el jnz (saltar si no es cero) por un jmp (saltar «sin peros»), remplazando el 75h por EBh en la instrucción 75C0 (ver pantalla). Para ubicar este segmento de código en el archivo DUE.EXE se necesita -además de un editor como WinHex o similar- encontrar una secuencia de bytes característica (que no se repita). Probando con la mismísima llamada a la rutina donde suponemos reside la protección (E8D9F40300) lo encontramos al toque. Es fundamental verificar que sólo aparezca una vez, para confirmar que estamos en el lugar indicado. Hacemos el cambio y probamos. La misma ventana de siempre: «Inserte el CD-ROM del DUE en el lector». Pero al darle ‘Cancelar’ en vez de cerrarse… ¡abre el diccionario!

Toque, gol y fiesta

Evidentemente estamos en el área chica. Sólo hay que afinar un poco la puntería y clavarla en el ángulo. ¿Qué es lo que está pasando? El salto que encontramos es el que lleva a la protección, y cuando vuelve le dice al programa principal si pusimos el bendito CD en la unidad o no. Entonces, hagamos al revés y eliminemos la llamada por completo. Volvemos a buscar la misma secuencia de antes y esta vez la «tachamos» con cinco 90h (que en ensamblador de intel significa ‘nop’, i.e. NoOPeration). Probamos nuevamente y… ¡entró como piña!

Antes de cantar la victoria total y definitiva, tenemos que ocuparnos de dos pequeños ayudantes: Conjugador.exe y Definidor.exe, que son invocados desde el acceso rápido del DUE. Si tratamos de utilizarlos, se repite la misma historia que con el diccionario. Si los desensamblamos, comprobaremos con disgusto que ambos tienen una serie de saltos que buscan claves en el registro y mil cosas más. Además, los módulos parecen tener los nombres desordenados; WindowsModule no es lo que esperábamos y el mensaje «Inserte el CD-ROM del DUE en el lector» aparece ahora en uno llamado DueServerPathModule. Pensemos un poco y apliquemos la Ley del menor esfuerzo.

La protección del programa principal hacía un salto a una rutina propia (call xxxxxxxxh), una comprobación (test) y un salto condicional (jnz). Busquemos «test» en el módulo Sub_Main de los ayudantes. Encontraremos tres o cuatro bloques con la siguiente estructura:

call MSVBVM60.DLL.__vbaFreeObj  ;llamada rutina VB
movsx ecx, word ptr var_C4
test ecx, ecx
jz 4077DBh

Pero uno solo de la forma:

call 0040F180h  ;llamada rutina propia
movsx ecx, ax
test ecx, ecx
jnz 407BD7h

De más está decir que este último es el salto a la rutina maldita, que dejaremos sin efecto tachándolo en ambos ayudantes con cinco 90h, como en el programa principal. ¡Ahora sí! Nuestro diccionario ha quedado completamente corregido y no volverá a obligarnos a levantar de la silla.

Destapate un PopCap

¿Quién no se ha pasado un par de horitas con algún juego de PopCap? Si nunca oiste hablar de Bejeweled, Hammer Heads o Zuma, date una vuelta por www.popcapgames.com y echá un vistazo a la sección «Juegos para descargar», de donde vas a poder bajarte una versión limitada de cualquiera de sus 49 juegos para PC.

Una hora más tarde, cuando haya expirado el tiempo de prueba, en vez de arrancarte los pelos, usá lo que tenés en la cabeza y seguí leyendo, que te explico cómo «sacarles la chapita» a estos juegos, usando como ejemplo al lanzamiento de hoy: Zuma’s Revenge.

Lo primero que tenés que hacer es descargar la versión de prueba (ZumasRevengeSetup-es.exe) y desempaquetarla con algún compresor de archivos. Yo uso WinRAR, pero cualquier otro puede servir. Vas a ver dos carpetas: cfg y files, y dentro de files está la única que nos interesa: Zuma’s Revenge.

Hacé doble-clic sobre la ranita para ejecutar el programa. Vas a ver el cartel que nos advierte que es una versión de prueba y que nos quedan 60 minutos de juego. Pulsá Jugar demo y una vez que haya terminado de cargar, pulsá Alt-Tab para volver al Explorador de Windows. Si no tenés activada la opción para poder ver los archivos ocultos y de sistema, activala desde Herramientas/Opciones de carpeta… En la solapa Ver, marcá «Mostrar todos los archivos y carpetas ocultos».

Carpeta de instalación
El ejecutable principal aparece…

Prestá atención al contenido de la carpeta del juego. ¿Notás algo? Claro, ¡hay un nuevo archivo ejecutable! Está ahí, medio lavadito (por ser un archivo oculto) con el nombre de popcapgame1.exe. A esta altura te imaginarás que éste es el verdadero ejecutable del juego, libre de toda limitación. Sólo tendríamos que copiarlo y listo. Pero hay un problema. El hecho de que no tenga un ícono puede significar dos cosas: o que no lo tiene (nunca hay que pasar por alto lo obvio), o que el sistema no puede acceder a él. Tratemos de copiar este archivo fuera de la carpeta; no podemos.

Si volvemos al juego y salimos a Windows, nuestro archivo habrá desaparecido. Entonces, ¿qué hacemos? El ejecutable de la ranita es en realidad el que nos impone las limitaciones de tiempo y nos impide acceder al verdadero ejecutable. Tenemos que deshacernos de él. Sin cerrar el juego pasemos al Administrador de tareas (Ctrl-Alt-Supr ó botón derecho sobre la barra de tareas).

Aplicaciones activas
Matar la aplicación no servirá.

Ahí veremos dos aplicaciones: el juego Zuma’s Revenge! y el Explorador de Windows (que como está apuntando a la carpeta del mismo nombre se llama igual, aunque su ícono nos permite distinguirlo). Si ordenamos finalizar la tarea, el juego se cerrará del mismo modo que si lo hubiéramos cerrado normalmente, y el ejecutable habrá desaparecido también. Esto se debe a que el sistema operativo le pide que se cierre («cerrate o te reviento»), y como el juego sabe lo que le conviene elimina el ejecutable principal y volvemos al mismo punto de antes.

La solución está en terminar el proceso, para que la protección no tenga tiempo de borrar nada. Pasemos a la solapa Procesos (obvio, ¿no?) y ahí veremos más claramente lo que está pasando realmente.

Procesos activos
En los procesos está la solución.

El proceso de nombre ZumasRevenge.exe es la protección (la «chapita»), mientras que el juego que está andando en memoria se llama -como ya vimos antes- popcapgame1.exe. Nótese la diferencia de recursos que utiliza cada uno. Marcá entonces ZumasRevenge.exe y pulsá Terminar. Ahora que volvemos al Explorador, nuestro éxito debería ser evidente: ¡la ranita ha recuperado su aspecto! (aunque sigue estando un poco pálida).

Para terminar, borremos el archivo ZumasRevenge.exe y «des-ocultemos» al popcapgame1.exe, al que -dicho sea de paso- deberíamos ponerle el nombre ZumasRevenge.exe, nomás para ser prolijos. Ahora podemos hacer doble-clic sobre la única ranita que nos queda y disfrutar del Zuma’s Revenge (o cualquier otro juego del catálogo PopCap) durante todo el tiempo que se nos dé la gana.