Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Re: (SPOILER) in reverse

by meta4 (Monk)
on May 29, 2002 at 16:59 UTC ( [id://170153]=note: print w/replies, xml ) Need Help??

in reply to in reverse

The cornerstone of this obfuscation is the use of Perl's quote like operators. Where you see

qw; your text here ;;

Perl sees

qw( your text here );


("your", "text", "here")

This is Perl saving you from typing a bunch of double quotes, and commas. The flexible argument delimiters allow you do something like this:

qw# this list contains brackets ( { [ ] } ) #;

which is the same as

qw( this list contains brackets ( { [ ] } \) );.

(Note the escaped left parenthesis at the end of the list.) (There are probably better reasons why you would want to do this, but I can't think of any right now.) or,

( "this","list","contains","brackets","(","{","[","]","}",")" );.

This obfuscation uses the fact that you can use any non-alphabetic character (there might be more restrictions) as the argument delimiters. Specificaly, iamcal uses the semi-colon which, because it normaly acts as the Perl statement separator, obfuscates his intentions quite well.

Once you have chosen your argument delimiters,s you can only use them as arguments if you escape them with a back-slash ('\'). (More delightful obfuscatiory misdirection.) Every semi-colon which follows the qw which has a back-slash right before it (there are two) is part of the argument to qw. So the entire qw function call (qw is realy an operator, but I think you can think of it as a function with out engaging in to much heresy) is

qw; //\; }split \;$_ tr/[a-mn-z]/[n-za-m]/ map{ reverse for print;

Which, with proper delimiters, looks like:

qw( //; }split ;$_ tr/[a-mn-z]/[n-za-m]/ map{ reverse for print)

which may not be to illuminating untill you work out what qw does. It builds an list which could also be built like this:

("//;", "}split", ";$_", "tr/[a-mn-z]/[n-za-m]/", "map{", "reverse", "for", "print")

ie. it's just an array of code fragments.

Now, examine the code to the right of the qw. The reverse reverses the above array. and the join concatinates it into one big string. which looks like

print for reverse map{ tr/[a-mn-z]/[n-za-m]/ ;$_ }split //;

this string is passed to eval where it is executed as a one line perl program.

This one liner streams a couple of tasks in to one statement. It's easiest to read it from right to left. First split // splits $_ into an list of characters. Then the map statement opperates on each character, and transliterates characters in the first half of the alphabet into characters in the second half of the alphabet and vis-versa. The transliteration ignores all non-lowercase alphabetic characters; in this case the spaces are ignored. The list of characters is then streamed to the reverse statement which reverses the list of characters. The for statement doesn't really do anything, and could be omited with out changing the functioning of the script. Finaly the print prints out the array of characters.

So all that's left is the last line line.

if (($_ = 'erxpnu yerc erugban gfhw') ne '');

The pupose of this line is simply to set $_ to 'erxpnu yerc erugban gfhw'. This is the transliterated, reversed string which the eval turns into "just another perl hacker". iamcal performs the assignement in an if statement that will always evaluate to true; the result of the assignment is the string, which is not equal to the empty string. The perl idiom in use here is:

<i>statement</i> if ( true )

which will always execute the statement. In this case the statment is the eval of the reversed code fragments discussed above.

In closing, I have two more comments, one compliment and one critique. First, above I said the for could be removed without changing the functioning of the script. This is true, but iamcal is using for here in an interesting/obfuscating method. He (She?) is using for as an end line flow control structure, similar to the way if was used above.

print "$i\n" for $i ( 1..3 )

is the same as

for $i ( 1..3 ) { print "$i"; }

If the $i is omited $_ is used which is the default argument to print. so the this code fragment could be rewritten:

print for( 1..3 );

In the obfuscation the array passed to the for is the reversed transliterated list of characters. So they are printed out one at a time. My critique is a minor technicality. A true JAPH prints the string "Just another Perl hacker,". Note the capital J, and P, and the comma at the end. I believe the intent is to create a salutation at the close of a letter simmilar to the common "Yours truly," So

Yours truly,



Just another Perl hacker,


The only reason I mention this is because it can be easily fixed by changing the string in the if statement to ',erxpnu yerP erugban gfhJ', then you would have a very fine JAPH.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://170153]
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (3)
As of 2024-07-13 19:27 GMT
Find Nodes?
    Voting Booth?

    No recent polls found

    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.