DEV Community

Tib
Tib

Posted on • Edited on

Japhs autopsies (2)

Con de MIME

This is a french joke 😃

Barrez vous, cons de MIMES

This Japh is from Reynold Scem

use MIME::Base64;(eval decode_base64('am9pbignJywobWFwe2NocigpfShncmVwL1xTLyxzcGxpdCgvKC4uLikvLCcwNzQxMTcxMTUxMTYwMzIwOTcxMTAxMTExMTYxMDQxMDExMTQwMzIwODAxMDExMTQxMDgwMzIxMDQwOTcwOTkxMDcxMDExMTQwNDQnKSkpKQ=='))=~/.*/;print$&
Enter fullscreen mode Exit fullscreen mode

This one... We can't guess what will happen when we eval this encoded string...

It uses actually 2 steps to produce the Japh, first base64 encoding, that we can check with base64 -d :

echo "am9pbignJywobWFwe2NocigpfShncmVwL1xTLyxzcGxpdCgvKC4uLikvLCcwNzQxMTcxMTUxMTYwMzIwOTcxMTAxMTExMTYxMDQxMDExMTQwMzIwODAxMDExMTQxMDgwMzIxMDQwOTcwOTkxMDcxMDExMTQwNDQnKSkpKQ==" | base64 -d
join('',(map{chr()}(grep/\S/,split(/(...)/,'074117115116032097110111116104101114032080101114108032104097099107101114044'))))
Enter fullscreen mode Exit fullscreen mode

From decoding I got the actual Perl code to evaluate and I feel a bit better now 😁

The code Japh string creation code looks like this :

my $str = eval { join('', ( map {chr()} ( grep/\S/, split(/(...)/, '074117115116032097110111116104101114032080101114108032104097099107101114044')))) };
Enter fullscreen mode Exit fullscreen mode

Then in the perl code there is map, chr (convert value to char), grep \S (match all non whitespace), split(/(...)/, (split per 3 chars).

It's a nice showcase 😃 even if a lot of things are actually useless and only there to obfuscate the Perl code.

The Japh creation could be simply reduced to :

my $str = join('', map {chr()} ( split(/(...)/, '074117115116032097110111116104101114032080101114108032104097099107101114044')));
Enter fullscreen mode Exit fullscreen mode

The join seems useless but forces the string context.

So here is the complete Japh unfolded :

use MIME::Base64;

# Base 64 encoding
# am9pbignJywobWFwe2NocigpfShncmVwL1xTLyxzcGxpdCgvKC4uLikvLCcwNzQxMTcxMTUxMTYwMzIwOTcxMTAxMTExMTYxMDQxMDExMTQwMzIwODAxMDExMTQxMDgwMzIxMDQwOTcwOTkxMDcxMDExMTQwNDQnKSkpKQ==
# join('',(map{chr()}(grep/\S/,split(/(...)/,'074117115116032097110111116104101114032080101114108032104097099107101114044'))))

my $str = eval { join('', ( map {chr()} ( grep/\S/, split(/(...)/, '074117115116032097110111116104101114032080101114108032104097099107101114044')))) };
# Take 3 chars (...), do not keep space (\S), tranform to corresponding string representation, concat without space, eval (useless)

$str =~ /.*/; # Match everything
print $&; # The string matched by the last successful pattern match
Enter fullscreen mode Exit fullscreen mode

Maybe you noticed that there is also some fun with regex match and captured match print. It is just for fun ! 😃

Open Format Write

open(P,"|perl");print P"format=\nJust another Perl hacker,\n.\nwrite"
Enter fullscreen mode Exit fullscreen mode

After some ordering, it looks like this :

open(P,"|perl"); 
print P "
format=
Just another Perl hacker,
.
write"
Enter fullscreen mode Exit fullscreen mode

There are 2 tricks here :

  • The open filehandle to another perl process
  • The "format", an old Perl function that is less and less used nowadays

It is actually the same than :

format=
Just another Perl hacker,
.
write
Enter fullscreen mode Exit fullscreen mode

Kisses

Here is a simple but interesting japh with some kisses inside ! 😘 😘 😘 😘

@_=("Just another Perl hacker," =~ /(.*) (.*) (.*) (.*)/);print"@_";
Enter fullscreen mode Exit fullscreen mode

Kisses

This japh demonstrates default variable @_ and matching.

I think it can be rewritten like this :

my $str = "Just another Perl hacker,";
my @a = $str =~ /(.*) (.*) (.*) (.*)/;
print join(" ", @a) . "\n";
Enter fullscreen mode Exit fullscreen mode

Perl developers should be comfortable with this form 😄

It's probably time to address regex greediness.

This kind of greedy .* matches is "dangerous" (depends what you want), because it will try to match as much as possible.

For instance "Just another Perl 7 hacker," with the greedy version will produce a string split like the following :

Just another Perl 7 hacker, (because the .* happily eats also the space)

x25

Let's have a look at a simple japh using printf, x and ASCII conversion.

printf "%c"x 25,74,117,115,116,32,97,110,111,116,104,101,114,32,80,101,114,108,32,104,97,99,107,101,114,44;
Enter fullscreen mode Exit fullscreen mode

The operator x is magic :

printf "perl " x 10
Enter fullscreen mode Exit fullscreen mode

Will produce :

perl perl perl perl perl perl perl perl perl perl 
Enter fullscreen mode Exit fullscreen mode

In the japh, the x 25 is applied to "%c", therefore it can be translated like this :

printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",74,117,115,116,32,97,110,111,116,104,101,114,32,80,101,114,108,32,104,97,99,107,101,114,44);
Enter fullscreen mode Exit fullscreen mode

Arrow here

Again a printf "%c" but this time the input being in hexadecimal and the whole thing being stored in a string assignment :

$_ = <<'-- '; s/../printf "%c",hex($&)/ge;
4a75737420616e6f74686572205065726c206861636b65722c
-- 
Enter fullscreen mode Exit fullscreen mode

It starts with a "here-doc" delimited by -- (dash + dash + space not represented by DevTo).

What is weird, is that the whole thing looks only like a string assignation...

Actually, the printf inside the regex will print to stdout. At the end $_ does not even contains the string, but sprintf would have done the job.

And what is exactly s/../printf "%c",hex($&)/ge ?

  • s/// is for substitution.
  • $& is what have been matched.
  • The /e modifier allows the right part of the substitution to be actually executed as normal Perl code.
  • The /g modifier is the "looping modifier" or "global modifier" and just repeats the match along the string !

Finally, I propose this rewrite of the japh :

my $str = "4a75737420616e6f74686572205065726c206861636b65722c";
while($str =~ /../g) { # Or $str =~ s/..//
       printf("%c", hex($&));
}
Enter fullscreen mode Exit fullscreen mode

Cristal clear like this isn't it ?

Top comments (0)