Mailing List

Flat
Fwd: Mark's brain teasing JAPH [LONG]
User: afbach
Date: 10/26/2011 4:33 pm
Views: 266
Rating: 0
Just ran across this and it's such a nice example of obfu and Perl ...


> That's one convoluted JAPH!

Thanks.

> I like to pick through admirable JAPH's like this in an
> attempt to learn more Perl idiosyncrasies [...]
> Anyone care to explain what's going on?

How about I tell you how I created it, which I think makes it easier to see
what's going on.

Every JAPH starts out with a small idea on some transormation of the phrase
"Just Another Perl Hacker". I noticed there are repeated letters in that
phrase, so I decided to collapse the phrase down to just the unique letters
and then rebuild the phrase by indexing them.

 # Reduce to unique characters
 $japh = 'Just another Perl hacker';
 $letter{$_}++ foreach split//, $japh;
 print join "", sort keys %letter;

So now I'm working with the string " JPacehklnorstu". But that string
doesn't have to be in any particular order, so it's off to the Internet
Anagram Server (
http://www.wordsmith.org/anagram/) to make things
interesting. There are lots of funny results, and I ended up making a few
different versions of the JAPH with them. The one you saw was "steal Porch
Junk".

So now, I just create a list of indices to extract the proper phrase back
out. I could simply use the following:

 @indices = (12,13,0,1,5,3,14,7,1,10,2,8,5,6,2,8,4,5,10,3,9,15,2,8);

But that would be boring. And take up quite a bit of space. I noticed that
the range is 0..15 or one nybble (half a byte). I could pack all 24 indices
in just 12 bytes! I decided to represent the bytes as hex characters:

 # pack 'em into bytes.
 while (@indices) {
                  $byte = (shift(@indices)<<4)|shift(@indices);
                  printf('%x',$byte);
 }

So now I'm working with the string "cd0153e71a28562845a39f28". To extract
the indices, I just have to use hex() on each character in that string.

So now, let's put together a first cut at the JAPH, using the techniques
above:

 $_="steal Porch Junk"; @_=split //;  # put the letters in @_
 $_="cd0153e71a28562845a39f28";       # put the indices in $_
 s/(.)/$_[hex($1)]/ge;                # replace each index with its letter
 print;                               # print it out

Now the fun part... let's obfuscate it a bit. Adding another "e" to the
regex is low-hanging fruit. We just assemble the '$_[hex($1)]' part via
concatenated strings:

 s/(.)/'$_[' . 'hex' . '($1)]'/gee;

I don't like the quotes. Too obvious. Lets change them to q() but use '.'
instead of the parens (anyone who gets confused by this needs to read
perldoc perlop):

 s/(.)/q.$_[. . q.hex. . q.($1)]./gee;

Looking better, but that 'hex' stands out like a sore thumb. Noticing that
the "h" and "e" are in our letter list (@_), I decide to assemble the 'hex',
which is just a string at this point, from those letters ("h" = $_[10], "e"
= $_[2]), leaving the 'x':

 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee

Better. Now let's get rid of the quotes from the rest of the JAPH too, using
q.. and q==

 $_=q;steal Porch Junk;;split //;
 $_=q=cd0153e71a28562845a39f28=;
 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
 print;

Now let's break up that hex string in the second line. I see it has three
"28"'s, so we can do a substitution. What should we replace it with? The
string "s/" is about as diabolical as any. ";q" would have been a good one
too. I chose the former. In our y// (or y;; to be tricky) lets also replace
the "a"'s with ";". We'll also insert a newline, which we take back out with
a s/\n//;

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;
 y;\;s/;a28;;
 s;\n;;;
 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
 print;

Now let's smash it up a bit:

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28;;s;\n;;;
 s/(.)/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;
 
Looking kinda JAPHy now, isn't it? There's a few more things we can do. The
y operator can take extra chars at the end without consequence. We'll add
another decoy 's/'. In the first substitution, let's replace '\n' with '$/'.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;
 s((.))/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;

Now, let's work on the substitution again. We can split it between lines,
and change s/// to s()//. Also we can change the ';' before print to a '/',
which is interpreted as a divide, but will still evaluate the 'print' with a
warning under -w which we don't care about.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;s((.))/
 q.$_[..$_[10].$_[2].q.x..q.($1)]./gee/print;

More stuff: Let's insert some ';' where they won't make a difference, and
remove one from the end. Then I want to pad the last line by 2 chars because
I like to make the lines equal length. We can add $. (which contains nothing
outside a loop) to the substitution string.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
 q.$_[.$..$_[10].$_[2].q.x..q.($1)]./gee/print;

More can always be done, but at this point it looks good enough for me.

Hope you found this interesting.


--
Mark Thomas                 Thomas.Mark@...s.gov
Internet Systems Architect     DigitalNet, Inc.

$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[..$..$_[10].$_[2].q.x..q.($1)]./gee/print




--

a

Andy Bach,
afbach@gmail.com
608 658-1890 cell
608 261-5738 wk
Re: Fwd: Mark's brain teasing JAPH [LONG]
User: david-delikat
Date: 10/26/2011 7:20 pm
Views: 0
Rating: 0

thats pretty cool...  I had to take the challenge to understand it before reading
the explanation, I was fine up until the s()// bit which my version of perl didn't
recognize anyway.  after I changed it to s()() and fixed some stray unicode in the
last line it worked...  in case you have an older version of perl like me this should
work:

$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))(;
q.$_[..$_[10].$_[2].q.x..q.($1)].)gee/print;

-dav

On Oct 26, 2011, at 4:33 PM, <afbach@gmail.com> <afbach@gmail.com> wrote:

afbach wrote:

Just ran across this and it's such a nice example of obfu and Perl ...


> That's one convoluted JAPH!

Thanks.

> I like to pick through admirable JAPH's like this in an
> attempt to learn more Perl idiosyncrasies [...]
> Anyone care to explain what's going on?

How about I tell you how I created it, which I think makes it easier to see
what's going on.

Every JAPH starts out with a small idea on some transormation of the phrase
"Just Another Perl Hacker". I noticed there are repeated letters in that
phrase, so I decided to collapse the phrase down to just the unique letters
and then rebuild the phrase by indexing them.

 # Reduce to unique characters
 $japh = 'Just another Perl hacker';
 $letter{$_}++ foreach split//, $japh;
 print join "", sort keys %letter;

So now I'm working with the string " JPacehklnorstu". But that string
doesn't have to be in any particular order, so it's off to the Internet
Anagram Server (
http://www.wordsmith.org/anagram/) to make things
interesting. There are lots of funny results, and I ended up making a few
different versions of the JAPH with them. The one you saw was "steal Porch
Junk".

So now, I just create a list of indices to extract the proper phrase back
out. I could simply use the following:

 @indices = (12,13,0,1,5,3,14,7,1,10,2,8,5,6,2,8,4,5,10,3,9,15,2,8);

But that would be boring. And take up quite a bit of space. I noticed that
the range is 0..15 or one nybble (half a byte). I could pack all 24 indices
in just 12 bytes! I decided to represent the bytes as hex characters:

 # pack 'em into bytes.
 while (@indices) {
                  $byte = (shift(@indices)<<4)|shift(@indices);
                  printf('%x',$byte);
 }

So now I'm working with the string "cd0153e71a28562845a39f28". To extract
the indices, I just have to use hex() on each character in that string.

So now, let's put together a first cut at the JAPH, using the techniques
above:

 $_="steal Porch Junk"; @_=split //;  # put the letters in @_
 $_="cd0153e71a28562845a39f28";       # put the indices in $_
 s/(.)/$_[hex($1)]/ge;                # replace each index with its letter
 print;                               # print it out

Now the fun part... let's obfuscate it a bit. Adding another "e" to the
regex is low-hanging fruit. We just assemble the '$_[hex($1)]' part via
concatenated strings:

 s/(.)/'$_[' . 'hex' . '($1)]'/gee;

I don't like the quotes. Too obvious. Lets change them to q() but use '.'
instead of the parens (anyone who gets confused by this needs to read
perldoc perlop):

 s/(.)/q.$_[. . q.hex. . q.($1)]./gee;

Looking better, but that 'hex' stands out like a sore thumb. Noticing that
the "h" and "e" are in our letter list (@_), I decide to assemble the 'hex',
which is just a string at this point, from those letters ("h" = $_[10], "e"
= $_[2]), leaving the 'x':

 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee

Better. Now let's get rid of the quotes from the rest of the JAPH too, using
q.. and q==

 $_=q;steal Porch Junk;;split //;
 $_=q=cd0153e71a28562845a39f28=;
 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
 print;

Now let's break up that hex string in the second line. I see it has three
"28"'s, so we can do a substitution. What should we replace it with? The
string "s/" is about as diabolical as any. ";q" would have been a good one
too. I chose the former. In our y// (or y;; to be tricky) lets also replace
the "a"'s with ";". We'll also insert a newline, which we take back out with
a s/\n//;

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;
 y;\;s/;a28;;
 s;\n;;;
 s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
 print;

Now let's smash it up a bit:

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28;;s;\n;;;
 s/(.)/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;
 
Looking kinda JAPHy now, isn't it? There's a few more things we can do. The
y operator can take extra chars at the end without consequence. We'll add
another decoy 's/'. In the first substitution, let's replace '\n' with '$/'.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;
 s((.))/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;

Now, let's work on the substitution again. We can split it between lines,
and change s/// to s()//. Also we can change the ';' before print to a '/',
which is interpreted as a divide, but will still evaluate the 'print' with a
warning under -w which we don't care about.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;s((.))/
 q.$_[..$_[10].$_[2].q.x..q.($1)]./gee/print;

More stuff: Let's insert some ';' where they won't make a difference, and
remove one from the end. Then I want to pad the last line by 2 chars because
I like to make the lines equal length. We can add $. (which contains nothing
outside a loop) to the substitution string.

 $_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
 s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
 q.$_[.$..$_[10].$_[2].q.x..q.($1)]./gee/print;

More can always be done, but at this point it looks good enough for me.

Hope you found this interesting.


--
Mark Thomas                 Thomas.Mark@...s.gov
Internet Systems Architect     DigitalNet, Inc.

$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[..$..$_[10].$_[2].q.x..q.($1)]./gee/print




--

a

Andy Bach,
afbach@gmail.com
608 658-1890 cell
608 261-5738 wk

View Online



Madison Area Perl Mongers - MadMongers
http://www.madmongers.org

Re: Fwd: Mark's brain teasing JAPH [LONG]
User: afbach
Date: 10/27/2011 11:08 am
Views: 1
Rating: 0
On Wed, Oct 26, 2011 at 7:20 PM,   wrote:
> In case you have an older version of perl like me this should
> work:

Worked here on 5.005, 5.8, 5.8.8 and 5.10 - though all on
linux/Solaris/Leopard.  Interesting the 5.005 and 5.8 produced a new
line (on Sol.) and the others didn't.

Hmm, on Win7 5.12, the original version returns nothing and your fixed
version returns "steal Porch junk" ;->

What OS are you using?


--

a

Andy Bach,
afbach@gmail.com
608 658-1890 cell
608 261-5738 wk
PreviousNext
Madison Area Perl Mongers