7A69 Issue 15 - Perl; Ofuscando el codigo
|-----------------------------------------------------------------------------|
[ 7a69#15 ] [ 23-11-2003 ]
\ /
\-------------------------------------------------------------------------/
{ 9 - Perl; Ofuscando el codigo. }{ Anarion }
|-----------------------------------------------------------------------|
Antes de nada, yo creo que ofuscar el codigo para tener seguridad, es la manera de ocultar los fallos en vez de corregirlos, asi pues, este no es un documento para dar seguridad a un programa, sino que la ofuscacion, almenos para mi, es utilizar el lenguaje de una manera creativa y original que hace que nuestra capacidad para leer el codigo de otra persona augmente. Se trata pues de hacer algo divertido, no seguro, pues seguramente si alguien se quiere molestar en desofuscarlo, lo hara. Como mucho podriais protegeros de alguien que sepa mas bien poco, pero si sabe mas bien poco, para que vais a querer protegeros de el?
Hay quien opina basandose en su ignorancia que el perl es un lenguage write-only. Por supuesto es muy logico que alguien que no sepa perl, no pueda leer el codigo de otra persona, en realidad basandose en este principio, cualquier lenguage que se desconozca es write-only. En perl podemos hacer con pocos caracteres, muchas cosas, pero esa no es la manera normal de programar en perl. La mantenibilidad, la reutilizacion del codigo, la OO, etc. Cualquiera que sepa perl, seguramente podra mantener el codigo que yo he escrito en alguna de mis aplicaciones. Pero el perl, tambien es muy flexible, lo cual nos da mucho juego y podemos intentar engañar al que lee el codigo con bastante facilidad, sobretodo, sino sabe mucho perl. Llega un punto, que es dificil y tienes que explotar alguna "feature" indocumentada para poder sorprender a la gente.
Podriamos decir que existen varias tecnicas o metodos de ofuscacion, voy a intentar explicar unos cuantos, y para hacer un programa realmente ofuscado, deberiamos poder combinar cuantas mas mejor.
Variables
Cuantos mas simbolos de puntuacion, y mas cortas sean la variables mejor. Usa todas las variables que ya estan definidas en el perl: man perlvar.
Separadores
Los separadores en cadenas, expresiones regulares, arrays, y muchas otras partes no son fijos, asi pues podemos cambiarlos y usar otros que signifiquen otra cosa en el lenguaje.
Estas lineas significan lo mismo:
$_="Hola";
$_=q=Hola=; # El separador de cadena es "="
$_=q;Hola;; # El separador es ";"
Tambien es util usar siempre la variable $_ que se usa por defecto en muchas funciones de perl, asi pues podriamos hacer lo mismo que en la linea anterior con el operador de substitucion de cadenas:
s//Hola/; # Substituimos nada por hola en $_
s$$Hola$; # Lo mismo pero el separador es "$"
s;;
Hola;; # Lo mismo pero el separador es ";"
Añadiendo caracteres innecesarios, podemos crear una simetria, para asi que los caracteres realmente importantes para el codigo, intenten pasar desapercibidos entre los otros que son superfluos.
;;;s;;
Hola;; # Lo mismo pero el separador es ";"
;;;;;;
Usando el modificador /x, podriamos modificar mas la substitucion para evitar los saltos de linea
;s;
;Hola;
x;
Podemos usar un alfanumerico como separador si dejamos un espacio despues del separador:
$_=q AHolaA ; # El separador es "A"
s
s
s
Hola
sx # El separador es "s"
Deberiamos limpiar los \n que hemos puesto en el codigo, para tener el mismo resultado que los anteriores.
Camuflaje
Aprovechando comentarios, pods, evaluaciones, o cadenas que parecen codigo, podemos intentar confundir. Ante todo escribir un ofu es una cuestion de conocimiento del lenguaje y creatividad.
Por ejemplo, comentare un ejemplo de japhy y uno de Abigail que me gustaron, que creo que son buenos ejemplos de esta tecnica:
Abiagil:
$; # A lone dollar?
=$"; # Pod?
$; # The return of the lone dollar?
{Just=>another=>Perl=>Hacker=>} # Bare block?
=$/; # More pod?
print%; # No right operand for %?
Este es facil de entender, pero es un buen ejemplo de como juega con la sintaxis. Mezclar, pod, comentarios, y codigo es un buen ejemplo. El perl, es un lenguaje como la mayoria de los humanos, donde el significado de una palabra, depende de su contexto. Asi pues la misma palabra significa cosas diferentes.
Un ejemplo senzillo, a simple vista, que diriais que es codigo, y que son comentarios o pod de el siguiente codigo:
$;
=head
$;
=head
$;
=cut
$;
=cut
o usando comentarios:
s # soy un comentario?
< # y yo, y yo ?
(?# yo si realmente soy un comentario !)
> # nah no lo soys, el unico comentario soy yo #?\)>
\# Yo si que soy un comentario! (?#
Y Yo tambien!) # Y Yo !
\# Yo no
japhy:
open m, "mail japhy\@pobox.com < /etc/passwd |";
print m "Just Another Perl Hacker", +0;
seek$|=>($/=\24,$\="\012",$=--,$=--,$=)[$++4]=>$[;
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
close m;
exit;
A simple vista, parece que el programa se envie por correo nuestra fichero de passwords, pero nada mas lejos de la realidad. Si observamos bien el codigo, teniendo en cuenta lo que explique arriba sobre los separadores, quizas veamos que hace realmente. Para que el codigo funcione, debeis de ponerlo en un fichero, y contare el porque.
Como sabeis, podemos usar el separador que queramos al usar cadenas, o regex, asi pues japhy usa la "," como separador para su regex del principio.
open m, "mail japhy\@pobox.com < /etc/passwd |";
print m "Just Another Perl Hacker", +0;
Es en realidad una expresion regular, como si encontrarais:
m/"mail japhy\@pobox.com < \/etc\/passwd |";
print m "Just Another Perl Hacker"/ + 0;
Asi pues usa la cadena dentro de $_, que como no esta definida no la encontrara por tanto devolvera falso, es decir undef y le sumamos 0, con lo cual queda.
open 0;
El open con un solo parametro, abre el descriptor que se llame como la variable, es decir, usar open A; es como usar open A, $A;. Aprovechandose de eso, tenemos que ha echo open 0,$0; con lo cual ha abierto el propio documento para su lectura.
La siguiente linea, usa la funcion seek, con mucha puntuacion y asignaciones para intentar confundir a la gente, pero si la miras con calma, no es dificil de entender:
seek$|=>($/=\24,$\="\012",$=--,$=--,$=)[$++4]=>$[;
Pongamoslo de manera mas agradable a la vista:
seek $|, ( $/ = \24, $\ = "\012", $=--, $=--, $= )[$+ + 4], $[;
$| es 0, asi que usaremos seek en ese descriptor.
$/ es el "input record separator", y es el delimitador que indica como se debe de leer de un fichero usando <>. Normalmente esta puesto a \n y es lo que define que entiende perl por una linea. Asi pues normalmente leemos linea a linea. $/ es una cadena, que es el separador de lineas. Ahora bien, existe una opcion, y es que si le asignamos la referencia a un entero, se lee ese tamaño de bytes del fichero. Asi pues $/ = \24 es para leer de 24 bytes en 24.
$\ es el "output record separator", por defecto esta indefinido, y es lo que imprimira perl despues de cada print, en este caso un "\n" pero para no ponerlo claramente, lo pone en octal.
$= esta variable es el "page length" que sirve para usar con formatos, es muy util porque tiene como valor inicial 60, asi podemos jugar con ese valor.
$+ es parte de las variables en las regex, se trata de la cadena capturada por el ultimo conjunto de parentesis, que en este caso es tambien 0.
$[ es el indice por donde empiezan los arrays, que por defecto tambien es 0
seek 0, ( $/ = \24, $\ = "\n", 60, 59, 58 )[4], 0;
Como de la lista ( $/ = \24, $\ = "\n", 60, 59, 58 ) solo coje el subindice 4 es lo mismo que usar directamente:
$/ = \24;
$\ = "\n";
seek 0, 58, 0;
La siguiente linea solo trata de poner un elemento importante en medio de otros para intentar distraernos:
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
El unico descriptor abierto de estos es 0, asi que lee i imprime los 24 bytes a partir del 58.
Las demas lineas hacen lo que dicem cierra m, que no estava abiero, y sale del programa.
Substituciones, transliteraciones y evaluaciones
Con las substituciones podemos asignar, ejecutar codigo, o cambiar parte del codigo para despues evaluarlo.
Una buena manera de asignar es usar:
s//algo/;
asi asignamos a $_ algo. Usando el modificador /e podemos ejecutar codigo en una substitucion.
s//$a=int(rand(10))/e;
print $a
Con las transliteraciones podemos substituir una lista de caracteres por otra lo cual es muy util para formar cadenas:
$_="hola";
tr/a-y/b-z/;
print; # imprime ipmb
Si lo utilizamos luego a la inversa, podremos camuflar facilmente cadenas de texto. Otro metodo es pasarlos a algun formato numerico su valor en ascii: octal, hexadecimal, etc.
print unpack("H*","Hola");
Si juntamos los dos conceptos anteriores, podremos camuflar facilmente el codigo con estas substituciones i transliteraciones, por ejemplo, podriamos poner una cadena en hexadecimal en medio de un texto y cojerla segun un patron que seguira una expresion regular.
El eval es una funcion muy importante ya que nos permite crear y modificar el codigo en tiempo de ejecucion:
$_='print $_ for 1..pop';
eval;
Como el codigo es una cadena, podemos usar transliteraciones o substituciones para cambiar el codigo.
El algoritmo
Una buena manera de confundir a quien intente tracear nuestro programa, es aprovechar las ventajas que nos da el perl, y aprovechar lo que es mas dificil de entender.
Por ejemplo, usar algoritmos recursivos en vez de iterativos, o programar alguna manera extraña de hacer una funcion, ni que sea simplemente imprimir por pantalla, pero para ello haces 5 forks, y cada proceso comunicandose con pipes, mantiene un estado y dirigie a ver quien debe imprimir en cada momento. Tambien podriamos usar threads, o clousures para dicho fin. Con clousures o funciones anonimas, podemos hacer la tarea de tracear un programa un infierno, ya que podemos usar referencias simbolicas.
Un ejemplo, a ver si sabeis que imprimira sin ejecutarlo:
$a = sub { (shift) == 4 ? "b" : "c" };
$b = sub { (shift) == 3 ? "c" : "a" };
$c = sub { (shift) % 2 ? "a" : "b" };
$_ = "cabacbcabac";
sub a { print "Estoy en a" }
sub b { print "Estoy en b" }
sub c { print "Estoy en c" }
${${${substr((/(.)?(\w{2})(?>.)/g)[2],0,1)}->(4)}->(3)}->(4)->()
Pero podriamos hacelo mas complicado, si las funciones llamaran a otras funciones, si modificaran los argumentos que se le pasan, o hacer una expresion regular mucho mas dificil de entender.
Las expresiones regulares
Para entender una expresion regular larga y complicada, debemos saber muy bien como trabaja el motor de expresiones regulares, pero aprovechandonos de ello, podemos hacer que la tarea de entenderla para alguien que no este muy acostumbrado, algo realmente dificil.
Aprovechando 'features'
El perl tiene muchas features poco documentadas, o no documentadas, la mejor manera de hacer un codigo sorprendente, es aprovecharte de ellas, porque asi es mas dificil o imposible de predecir.
Pondre unas cuantas para que os suenen y las useis en vuestros ofus, pues son muy practicas:
- $0 es el propio fichero, lo podemos usar para leer nuestro propio codigo
- si al final del fichero ponemos __END__ o __DATA__, podemos con seek volver al principio del fichero y leer todo el codigo, no solo lo que hay despues de data o end.
- <hola yo soy un> esto devuelve una lista con cada cadena
- usar los << en sitios inesperados, por ejemplo al llamar una funcion, o en algun sitio donde pueda pasar desapercibido, como una expresion regular
- aprovecha los typeglobs para canviar una funcion definida anteriormente
- hay mucho texto dentro del perl, seguro que puedes extraerlo de una manera creativa para poder imprimirlo sin que sepan de donde lo sacaste, un ejemplo es un obfu que hice hace tiempo:
perl -e'
BEGIN{$^W+=$SIG{__WARN__}=sub{$_=pop}}$-,$-+=$-+++s$a+ +.+$\$^X$-+print'
Os lo dejo de deberes el entender como funciona :)
Para este numero Ripe me pidio que hiciera un indice como el otro, que fuera ejecutable, asi que voy a explicar el proceso paso por paso de como hice el ofu, enseñando el codigo desde el principio en que lo pense.
La idea que se me ocurrio fue hacer une pequeña animacion de las letras de 7a69 girando en circulos por la consola, en varias circumferencias concentricas y a varias velocidades.
---
#!/usr/bin/perl
use Term::Cap;
++$|;
$T = 'Term::Cap'->Tgetent({'OSPEED', 9600});
while(1)
{
$i = ++$i % 60;
$j = 60-($i*2)-1;
$k = ($i*3) % 60;
print `clear`;
$r=$i;
$s=$j;
$t=$k;
for(split//,"7a69ezine")
{
$r = --$r % 60;
$s = ++$s % 60;
$t = --$k % 60;
$x = cos 0.017 * (6 * $r - 90);
$y = sin 0.017 * (6 * $r - 90);
$x2= cos 0.017 * (6 * $s - 90);
$y2= sin 0.017 * (6 * $s - 90);
$x3= cos 0.017 * (6 * $t - 90);
$y3= sin 0.017 * (6 * $t - 90);
print $T->Tgoto('cm', 30 * $x + 39,
10 * $y + 12),
$_;
print $T->Tgoto('cm', 25 * $x2 + 39,
8 * $y2 + 12),
$_;
print $T->Tgoto('cm', 20 * $x3 + 39,
6 * $y3 + 12),
$_;
}
select(undef,undef,undef,0.1)
}
---
Bien, una vez el codigo ya funcionava, me propuse a comprimirlo un poco, ya que primero hice una sola cadena que girara y luego hice copiar y pegar para las otras. Asi pues juntando en un bucle el sin, cos i print, tuve mi segundo codigo
---
#!/usr/bin/perl
use Term::Cap;
++$|;
$T = 'Term::Cap'->Tgetent({'OSPEED', 9600});
while(1)
{
$r = $i = ++$i;
$s = $j = 60-($i*2)-1;
$t = $k = ($i*3) % 60;
print `clear`;
for(split//,"7a69ezine")
{
$r = --$r % 60;
$s = ++$s % 60;
$t = --$k % 60;
$m = 30;
$n = 10;
for$p($r,$s,$t)
{
$x = cos 0.017 * (6 * $p - 90);
$y = sin 0.017 * (6 * $p - 90);
print $T->Tgoto('cm', $m * $x + 39,
$n * $y + 12),
$_;
$m-=5;
$n-=2;
}
}
select(undef,undef,undef,0.1)
}
---
Agrupando los mods y eliminando todos los contadores para usar solo uno a partir de ahora, $i;
Ademas cambio el valor de los numeros 60 por $=, 1 por $a y $b por 2. En $c necesito en 6 asi que le hago un chop al 60, y substituyo casi todos los otros numeros por esas variables o operaciones entre ellas. Como en la mayoria de sitios la "," puede cambiarse por "=>" en perl y es menos obvio porque se suele usar en hashes, lo cambio en el map. Usare $a para ir restando posiciones de la cadena cuando deba imprimirla.
---
#!/usr/bin/perl
use Term::Cap;
++$|;
$T = 'Term::Cap'->Tgetent({'OSPEED', 9600});
while(++$i)
{
print `clear`;
$a=!$-;
$b=$a<<$a;
for(split//,"7a69ezine")
{
chop($c=$=);
@p = map $_ % $=,$i-$a=>$=-$i*$b+$a=>$i*$c/$b-$a;
$m = $=/$b;
$n = $=/$c;
for$p(@p)
{
$x = cos 0.017 * ($c * $p - $=*$c/$b/$b);
$y = sin 0.017 * ($c * $p - $=*$c/$b/$b);
print $T->Tgoto('cm', $m * $x + 39,
$n * $y + $c*$b),
$_;
$m-=$b+$c/$b;
$n-=$b;
}
$a++;
}
select(undef,undef,undef,0.1)
}
---
Cambio un par de asignaciones para eliminar codigo y sobretodo, cambio el for por map, asi me ahorro tener que usar la variable @p porque iterare directamente sobre la lista que devuelve map.
Añadiendo espacios detras de la cadena, ya consigo que se vaya autoborrando, asi que puedo eliminar los clears
---
#!/usr/bin/perl
use Term::Cap;
++$|;
$T = 'Term::Cap'->Tgetent({'OSPEED', 9600});
while(++$i)
{
$a=$|;
$b=$a<<$|;
for$l(split//,"7a69ezine ")
{
chop($c=$=);
$m = $=/$b;
$n = $=/$c;
map
{
$x = cos 0.017 * ($c * $_ - $=*$c/$b/$b);
$y = sin 0.017 * ($c * $_ - $=*$c/$b/$b);
print $T->Tgoto('cm', $m * $x + 39,
$n * $y + $c*$b),
$l;
$m-=$b+$c/$b;
$n-=$b;
}map$_%$=,$i-$a=>$=-$i*$b+$a=>$i*$c/$b-$a;
$a++;
}
select(undef,undef,undef,0.1)
}
---
Una vez llegados al punto en que no voy a cambiar el algorismo mas, en teoria, empezaremos a ofuscarlo. Ahora ya es cuestion de las ideas que se me van ocurriendo. Mi idea era usar un <<HERE document para meter todo el codigo y despues evaluarlo.
Como no me gusta que se vea el Term::Cap, usare un xor de "Term::Cap" con "11111111" para ocultarlo de la vista.
Primero asignar a $; el resultado de ese xor para hacer el paso inverso. Inicializamos $_ porque luego uso la regex que substituye el primer caracter por todo el codigo hasta encontrar el map a principio de linea. Aprovecho la regex para ejecutar el codigo y hacer el xor para obtener el Term::Cap.
Como al encotrar el uno el xor ya e a producido, substituyo el 1 de $_ por
use$"$;;\$T=$;->Tgetent;q{7a69$"ezine$"$"$"$"$"}
que hace el use Term::Cap; crea la variable $T y define la cadena que moveremos. Como veis no la asigno, sino que es el resultado de la substitucion del codigo evaluado, es como en un funcion que se devuelve el ultimo valor evaluado.
Cambio el while por un bloque con redo al final, asi parece parte del map anterior, aunque es posible que si junto el codigo, ya no lo parezca. Para iterar sobre la cadena, prescindo de split, que es demasiado obvio asi que usare la regex /./g.
Como s// devuelve el numero de substituciones, que sera uno, lo usare para inicializar algunas variables.
---
#!/usr/bin/perl
$;="dUB]\n\nsQp";$_++;$:=0.017;
$|=s<(?{$;^=$|x8}).>><<map>ee;
use$"$;;\$T=$;->Tgetent;q{7a69$"ezine$"$"$"$"$"}
map
{
++$i;
$a=$|;
$b=$a<<$|;
map
{
chop($c=$=);
$m = $=/$b;
$n = $=/$c;
$l=$_;
map
{
$x = cos $: * ($c*$_-$=*$c/$b/$b);
$y = sin $: * ($c*$_-$=*$c/$b/$b);
print $T->Tgoto('cm', $m * $x + 39,
$n * $y + $c*$b),
$l;
$m-=$b+$c/$b;
$n-=$b;
}map$_%$=,$i-$a=>$=-$i*$b+$a=>$i*$c/$b-$a;
$a++;
}/./g;
select(undef,undef,undef,0.1);
redo;
}
---
Pocos cambios, junto ese conjunto de operaciones que son las mismas en una variable y me veo obligado a poner parentesis dentro del Tgoto.
---
#!/usr/bin/perl
$;="dUB]\n\nsQp";$_++;$:=0.017;
$|=s<(?{$;^=$|x8}).>><<map>ee;
use$"$;;\$T=$;->Tgetent;q{7a69$"ezine$"$"$"$"$"}
map
{
++$i;
$a=$|;
$b=$a<<$|;
map
{
chop($c=$=);
$m = $=/$b;
$n = $=/$c;
$l=$_;
map
{
$. = $c * $_ - $= * $c / $b / $b;
print $T->Tgoto('cm', $m * (cos $: * $.) + 39,
$n * (sin $: * $.) + $c*$b), $l;
$m-=$b+$c/$b;
$n-=$b;
}map$_%$=,$i-$a=>$=-$i*$b+$a=>$i*$c/$b-$a;
$a++;
}m[.]g;
select(undef,undef,undef,0.1);
redo;
}
---
Quitando espacios y alineando los maps, para asi que el primero intente pasar desapercibido como si fuera un map mas, no el final del here document. Tener todos los maps asi, me esta gustando.
---
#!/usr/bin/perl
$;="dUB]\n\nsQp";$_++;$:=0.017;
$|=s<(?{$;^=$|x8}).>><<map>ee;
use$"$;;\$T=$;->Tgetent;q{7a69$"ezine$"$"$"$"$"}
map
{++$i;$a=$|;$b=$a<<$|;
map
{chop($c=$=);$m=$=/$b;$n=$=/$c;$l=$_;
map
{$.=$c*$_-$=*$c/$b/$b;
print$T->Tgoto('cm',$m*cos($:*$.)+39,$n*sin($:*$.)+$c*$b),$l;
$m-=$b+$c/$b;$n-=$b}
map
{
$_%$=}$i-$a=>$=-$i*$b+$a=>$i*$c/$b-$a;$a++;}m[.]g;select($',$',$',0.1);redo}
---
He pensado meter todo el codigo dentro del here document i despues evaluarlo todo con un eval. Pero para no tener que escapar todas las variables, ni usar comillas simples en la defincion del here document, usare una transliteracion para cambiar $ por %. Como tenia un modulo, debo cambiarlo para que al hacer transliteracion no me lo conbierta en $, asi que lo cambio por !. Pongo el final del here document grep, luego ya provare de ponerle map tambien.
---
#!/usr/bin/perl
$:=s--$;="dUB]\n\nsQp"^$|x8-e-0.983;$|=s<>><<grep>e+
use$"$;;%T=$;->Tgetent;%_=q{7a69$"ezine$"$"$"$"$"};
{++%i;%a=%|;%b=%a<<%|;
map
{chop(%c=%=);%m=%=/%b;%n=%=/%c;%l=%_;
map
{%.=%c*%_-%=*%c/%b/%b;
print%T->Tgoto('cm',%m*cos(%:*%.)+39,%n*sin(%:*%.)+%c*%b),%l;
%m-=%b+%c/%b;%n-=%b}
map
{
%_!%=}%i-%a=>%=-%i*%b+%a=>%i*%c/%b-%a;%a++;}m[.]g;select(%',%',%',0.1);redo}
grep
y+%!+$%+,eval
---
Cambio el nombre de las variables $a, $b, $c por variables ya definidas para que puedan confundirse con la puntuacion y operadores. Ademas paso a octal "%","!" y "$" en la transliteracion. Algunas las llamare q,s y y porque asi pueden confundirse con las funciones :)
---
#!/usr/bin/perl
$:=s--$;="dUB]\n\nsQp"^$|x8-e-0.983;$|=s<>><<grep>e+
use$"$;;%T=$;->Tgetent;%_=q{7a69$"ezine$"$"$"$"$"};
{++%q;%~=%|;%;=%~<<%|;
map
{chop(%*=%=);%s=%=/%;;%y=%=/%*;%l=%_;
map
{%.=%**%_-%=*%*/%;/%;;
print%T->Tgoto('cm',%s*cos(%:*%.)+39,%y*sin(%:*%.)+%**%;),%l;
%s-=%;+%*/%;;%y-=%;}
map
{
%_!%=}%q-%~=>%=-%q*%;+%~=>%q*%*/%;-%~;%~++;}m[.]g;select(%',%',%',0.1);redo}
grep
y+\041\045+\045\044+,eval
---
Final
Muchos cambios, pero la mayoria son superfluos porque son comentarios. Para poder usar el map en el here document no puedo dejar las lineas solo con map pero se me ocurre una cosa, en la transliteracion, puedo cambiar algunos caracteres mas, para hacer el codigo mas simetrico.
Cambiare ] por # asi creare comentarios en el codigo, pero un comentario en perl es hasta el final de linea, asi que debere modificar tambien los inicios i finales de linea, asi pues eliminare los \n actuales, y cambiare el & por \n. Ademas reutilizo la variable $; para usarla de $T.
El codigo entre ] i & seran comentarios, asi que podeis ignorarlo, solo esta alli para poder hacer la estructura de maps en que se a convertido el ofu.
---
$:=s--$;=qq=dUB]\n\nsQp=^$|x8-e-0.983;$|=s<>><<map>e+
use$"$;;%?=$;->?getent;%_=(pop||q{7a69$"ezine}).q{$"$"$"$"$"};]map $;->,map{m|
map &{++%q;%~=%|;%;=%~<<%|] map {++%q;%~=%|;%;=%~<<%|; $;->&;q;$; map {;;
map {(%*=%=)=~[=.%==;%s=%=/%;;%y=%=/%*;%l=%_] map {(%*=%=)=~[=.%==;%s=%=/%},$$&;
map {%.=%**%_-%=*%*/%;/%;;]map {%*=%*/%_+%=*%**%;,%;;] map { map { map { map q{
map &print%?->?goto(q+cm+,%s*co[(%:*%.)+39,%y*[in(%:*%.)+%**%;),%l;]map$_{$_}++,
map &%s-=%;+%*/%;;%y-=%;] map { map { map { [in(%:*%.)+%**%;),%l;] } map {$$&}
map{%_!%=}%q-%~=>%=-%q*%;+%~=>%q*%*/%;-%~;%~++}m;.;g;select(%',%',%',.1);redo}]
map {defined} map { %s-=%;+%*/%;;%y-=%; } map{%_!%=}%q-%~=>%=-%q*%;%y-=%-}&
map
y+map {\041\045\077[&]\012+map {\045\044\124s\012#+d,s++$_+ee+q;9}1..$_}1..7a69;
---
*EOF*