Perl-Sensitive Sunglasses | |
PerlMonks |
Structured obfuscationby gmax (Abbot) |
on Jan 22, 2002 at 13:38 UTC ( [id://140626]=obfuscated: print w/replies, xml ) | Need Help?? |
Dear fellow Monks, I would like to tell the inverse story of a recent obfuscation (Perl Monk's dream), starting from the idea of what to print and following the step-by-step process of garbling a honest (well, almost) piece of code. I have always been fascinated by obfuscation, even though I know that I don't have a natural knack for it. When writing code, I almost automatically think in terms of either structured or OO programming. Therefore, thinking in obfuscated manner is a sort of act against nature for me. However, I wanted to produce an obfuscation, and I decided to do it in a scientific way, following a logic path to disrupt those rules that I try to observe on a daily basis. This process is not something that I did in one day, but an idle pastime that I was enjoying from time to time between sessions of more demanding work. There are two ways of disliking art. One is to dislike it. The other is to like it rationally. -- Oscar Wilde Warning! This is a (self-)SPOILER. SPOILER SPOILER SPOILER SPOILER SPOILER SPOILER SPOILER If you'd rather work out the details on your own, read no more. THE FIRST LEVEL We start with the shape we want to print out. Not difficult to transform it into a pattern. Each space counts for 1, and so will each group of "{}". Always starting with a space, the pattern looks like this one: How to print it? Here is the most obvious, boring solution: a nested foreach, which does the job honestly and cleanly. Not much of obfuscation, here. To decide what to print, we need a variable indicating the current status (mark or space) It is a lot of wasted space, uninteresting code, too much predictable. We need some shrinking mechanism. How about making two maps out of the two for loops? Hmm, interesting, but we need to do something about this $mark=0 / $mark=1 business. Here we come up with the first trick. We store in a hash the values we want to print: The idea is that the key of one pair is the value of the other one. Then, having we can make a loop without internal "if" Looks promising: Let's try a map: And now let's transform it into a nested map: It works, but we still have the problem that the pattern is too big and obvious. We should store it into a thinner structure. We can pack each anonymous array into a string, and represent it this way: It is still somehow predictable, but we can easily change numbers to letters, and use a tr/// within the map to restore the form. Also, making the variable names less telling, the candidate for the obfuscation is shaping up. Not bad. Thin, efficient, darkish, not really obscure yet, but very much promising. THE SECOND LEVEL Could be more sophisticated, but it is effective. Some search and replace will make a string of gibberish out of our clean candidate. We rely on a tr/// operation to reinstate legitimate perl code, and a final eval will do the trick. But, is it obfuscated? Well, difficult to read, yes, but the key is quite obvious. The transliteration will recreate the code and eval is going to execute it. We need a further level of concealing. THE THIRD LEVEL We want something that is truly deceptive, and that looks like something else. After some fruitless attempts, here comes the idea of making a string of hex codes out of the gibberish from the second level. So we need an intermediate script to perform this further change. with the following output. The uc operator ensures that all the hex code is uppercase. We'll base the fourth level upon this assertion. Is it obfuscated now? Definitely. There is no way of telling what this program is going to do, without transforming back the hex codes into characters. But it is not interesting. Something more is needed. Yet the time for trasformation is over. Let's switch to deception (and fun). THE FOURTH LEVEL The idea is to insert some wild text within the hex code, so that it looks like something else. What exactly depends on the skills and the mood of the viewer, but at first sight it may look like Perl code embedded inside a string, or words that can be magically turned into Perl instructions. Thus the "use strict;", "require 7.0.6;", "open GUTS" and "no CGI;" are just a smoke screen. Actually, only "require" and "no GI" are fake, since the numbers and the "C" belong to the hex string. The same we can say of all the capital letters A-F scattered through the test, making impressive statements ("Dozens", "Another", "Cool!", "Finally" and so on). To execute this code, we need to filter off the non-Hex characters, including newlines, and eval the result. So the script could have been terminated this way. However, an experienced Perl user could tell on the spot what was happening. One last effort to conceal these two simple statement and we are done. THE FIFTH LEVEL To achieve the final goal, we replace the last two telling statements with the more committing line 'Now for something completely different:' introducing more darkness. The last four lines are busy creating a string containing 'ABCDEF0123456789' without giving it away. This latest trick, although confused looking, is the easiest part. Let's find the intervals between the ordinal values of '0', '9', 'A' and 'F' (3,5,8,9). Get them from the length of four words in an array (qw(the dream becomes: nightmare)), and use pack "c" to avoid the ord keyword. The base value is the result of summing up the array values and some ad hoc arithmetic operation. Then the letters are created from the base value from which we progressively subtract the array values. We'll skip the steps of this transformation that you can easily follow with a debugger. These details involve some acrobatic repeating uses of pack "c" to recreate the tr/// statement, further concealed as to almost look like a range (..) operator, by changing the slashes into dots. As a final touch, the "H*" becomes "$c*", thus getting visually camouflaged among the repeating pack "c*" and unpack "c*" scattered in the preceding code. And finally we achieve the obfuscated result. Now your average Joseph P. Hacker needs more than a casual glance to understand what is going on. The comment "this id <not> poetry" is a further attempt to defeat the judgement of the casual reader, because what looks like Perl code is actually garbage and what looks like garbage is Perl code. Hope you enjoyed it. update Want more of this same stuff? Try Yet another structured obfuscation _ _ _ _ (_|| | |(_|>< _|
Back to
Obfuscated Code
|
|