I'm going to dissect those recent JAPHs of mine, so that you
understand what's going on, and get some ideas as to how to
better obfuscate your programs.
Mis-use of globs for JAPHing purpose
This was a fun JAPH to write, when I realized a possible bug
in file-globbing. I had to exploit it (obviously).
Code Dissection
$_=q;
$_ *= $_ ++;
;s;\S+;<rekcaH lreP rehtonA tsuJ>;eg;
print q ... reverse;
The section in blue is a single-quoted string, using
;
as the delimiter. Let's un-obfuscate that (adding some whitespace,
and moving the semicolon from the next line up). Notice that
the first character in the string is a newline.
$_ = '
$_ *= $_ ++';
s;\S+;<rekcaH lreP rehtonA tsuJ>;eg;
print q ... reverse;
This blue text is a substitution (again with ; as the
delimiter). We also notice "Just Another Perl Hacker"
backwards, and between less-than and greater-than signs, and
that the regex has the
e modifier. Hmm...
$_ = '
$_ *= $_ ++';
s/\S+/<rekcaH lreP rehtonA tsuJ>/eg;
print q ... reverse;
The print line is meant to make you think I'm using the
... operator (which is like the
..
flip-flop operator). But it's actually using the
q//
operator, using
. as the delimiter. So we're doing
print '' . reverse;, which is concatenating an empty
string and the return value of
reverse($_), which
calls
reverse in scalar context. We have to call
it in scalar context, since
print is a list operator,
and won't impose the scalar context on any of its arguments.
The intermediate code is now:
$_ = "\n" . '$_ *= $_ ++';
s/\S+/<rekcaH lreP rehtonA tsuJ>/eg;
print scalar reverse($_);
The only mystery left is how this works. The left-hand side
of the
s/// matches non-whitespace. So on the
first run-through, it matches the string '
$_'. Then
it substitutes in
<rekcaH lreP rehtonA tsuJ> for
it.
What the hell does that mean? Well, that madness
is actually a file-glob, just like
<*.txt>.
This is where the title of the JAPH comes into play. When you
call
glob() with an argument that does not have any
glob-like characteristics (that is, there is no wildcard),
Perl just returns the argument. And here, I'm calling the
same glob operator four times, in scalar context.
glob() returns on element at a time in scalar context.
So the first time, it replaces '
$_' with '
rekcaH'.
The next time, it replaces '
*=' with '
lreP',
and so on. Then we reverse the string in scalar context, and
get '
Just Another Perl Hacker', with a newline, of
course.
Lessons to be Learned
- Find a "feature", and exploit it
- Using semicolons, commas, and periods as quoting
delimiters can be a good obfuscator
- Design strings to look like code, and structure your
program to make them appear to be used as code
- Do sneaky, simplistic things to get a desired effect
(like concatenating a function with an empty string to force
scalar context)
Do you dare run this JAPH?
The opening line of this one scares most people, so they
never run it. The "hint" I gave ("I'm not an evil person")
was meant to dispel any beliefs that this JAPH is malevolent
in nature. I'm about to show you how kind it really is.
Code Dissection
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;
Because they're operators, you can't use 'm', 's', 'tr', or
'y' as the name of a function, or as a filehandle, as I
"tried" to do here. The blue code is actually a long
m//,
with a comma as the delimiter. This obviously returns an
empty string, since the match fails, and then the code in
red adds 0 to it.
open 0;
seek$|=>($/=\24,$\="\012",$=--,$=--,$=)[$++4]=>$[;
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
close m;
exit;
As you may or may not know, one-argument
open() uses
a package variable of the same name as the constant used, so,
in package
main, saying
open FOO; is like
open FOO, $main::FOO;. Here, we use 0 as the
filehandle, so Perl opens
$0.
This is why the JAPH must be in a file when it is run
-- the Perl program must open itself. If I'd used the <DATA>
filehandle, it wouldn't need to be in its own file, but the
mechanism would've been far more obvious.
open 0, $0;
seek$|=>($/=\24,$\="\012",$=--,$=--,$=)[$++4]=>$[;
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
close m;
exit;
Lots of obfuscation. Using Perl's special variables, like
$|,
$/,
$\,
$=,
$+,
and
$[. Let me substitute in the default values
for a few of them:
open 0, $0;
seek 0, ($/ = \24, $\ = "\012", $=--, $=--, $=)[0 + 4], 0;
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
close m;
exit;
It's still a bit of a mess inside those parentheses.
- $/ is the input record separator. Here, I set it equal to a
reference to an integer, which means that <FH> behaves like
read(FH,$_,$$/) -- that is, it reads the specified number of bytes.
24 happens to be the length of "Just Another Perl Hacker".
- $\ is the output record separator. It is tacked onto the end of
each print() statement. I set it equal to newline here, by using an
octal character escape.
- $= is the "format lines per page" variable, which defaults to
60. I just use it here to decrement it twice to get it to 58. I used
post-decrement, instead of pre-decrement, which means I had to get $=
three times, as you see in the code: (..., $=--, $=--, $=).
The subscript,
0 + 4, is just 4. So the element with index four is
$=, which by then had the value of 58. 58 happens to be the number
of bytes past the beginning of the file where "Just Another Perl Hacker"
resides.
open 0, $0;
seek 0, ($/ = \24, $\ = "\012", 60, 59, 58)[4], 0;
print <4>.<3>.<2>.<1>.<0>.<blastoff>;
close m;
exit;
So we've seeked to the location of the JAPH string, and now we get to print
the 24-byte record. The other filehandles used here are just for show, and to
hide the important one,
<0>. But the important thing to realize
is that I couldn't have just said
print <0>;, since that would be
list context, and return more than I want. So I concatenate again, with the
empty string, to enforce scalar context.
open 0, $0;
$/ = \24;
$\ = "\n";
seek 0, 58, 0;
print scalar <0>;
close m;
exit;
That's just another
m//, closing a non-existent filehandle. Remember,
m// returns the empty string in scalar context on failure, not 0.
open 0, $0;
$/ = \24;
$\ = "\n";
seek 0, 58, 0;
print scalar <0>;
You'll notice this program won't work, because the length is now different,
and I've removed the string that it was going to print. But this is WHAT the
program does, in plainer english.
Lessons to be Learned
- Using <DATA> or opening the file itself is good and fun
- Perl's punctuation variables offer a lot of line noise -- use variables
that coincide with punctuation you're already using, like $---
- Regular expressions can make code look like text, and vice-versa
- Make your program appear to do something else entirely
A coolwTransfer Interrupted h3>Just Another Python Hacker
The comment I made at the top is just in fun. The program more or less turns
^X escapes from their visible form to their actual form. If you did
not already know,
^H is backspace, and is written in Perl as
\cH.
That's what the use of
overload and
overload::constant are
for. Nothing more, nothing less.
use overload;BEGIN{overload::constant
"q",sub{$_[1]=~s/\^(.)/qq!"\\c$1"!/eeg,return$_[1]}}
print "Just Another Python^H^H^H^H^Herl Hacker\n";
The blue code uses the
overload module, and then (at compile-time)
calls
overload::constant() to intercept quoted strings. It's not so
much obfuscation, as a fun feature of overloading. The regex changes the
two-character representation of the escape sequences into the actual sequence.
use overload;
BEGIN {
overload::constant "q", sub {
$_[1] =~ s/\^(.)/qq!"\\c$1"!/eeg;
return $_[1];
}
}
print "Just Another Python^H^H^H^H^Herl Hacker\n";
And that's really all there is to it.
Lessons to be Learned
- It's obfuscation to a person who doesn't know what you're doing
- Clutter can help obfuscate
$_="goto+F.print+chop;\n=yhpaj";F1:eval