Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

japhy's Obfuscation Review

by japhy (Canon)
on Sep 23, 2000 at 21:20 UTC ( [id://33780]=obfuscated: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
Re: japhy's Obfuscation Review
by Rydor (Scribe) on Sep 24, 2000 at 18:34 UTC
    I like looking at obfuscated code, but most of the time, i can't figure out what they mean. that was a great little tutorial on how yours work, and the pointers will definatly help me when i try to mak a good JAPH

    @:::::::((==========Rydor====>

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: obfuscated [id://33780]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (6)
As of 2024-03-19 10:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found