A friend of mine started telling me about a php script obfuscation / encoding application, I was none too convinced that it could have any viable use to thwart the theft of code, and my preconceptions were bang on the nail.
I shall from this point on be calling the application ‘obfuscator-x’(so not to jeopardise anyone’s codebase who actually attempt to protect their source with the product).
Obfuscation-x basically is a windows application which takes your source files then garbles them to the extent they are not easily readable, yet can be used as you normally would. Obfuscation-x does this through the means of shifting bytes backwards and forwards in a predetermined algorithm.
Let me show you the source of the original script which is being encoded / obfuscated:
Now we look at the obfuscated / encoded source:
if (!function_exists("a")) { function a($b) { $b = base64_decode($b); $a = 0; $c = 0; $d = 0; $e = (ord($b[1]) << 8) + ord($b[2]); $f = 3; $g = 0; $h = 16; $i = ""; $j = strlen($b); $k = __FILE__; $k = file_get_contents($k); $l = 0; preg_match(base64_decode("LyhwcmludHxzcHJpbnR8ZWNobykv"), $k, $l); for (;$f<$j;) { if (count($l)) exit; if ($h == 0) { $e = (ord($b[$f++]) << 8); $e += ord($b[$f++]); $h = 16; } if ($e & 0x8000) { $a = (ord($b[$f++]) << 4); $a += (ord($b[$f]) >> 4); if ($a) { $c = (ord($b[$f++]) & 0x0F) + 3; for ($d = 0; $d < $c; $d++) $i[$g+$d] = $i[$g-$a+$d]; $g += $c; } else { $c = (ord($b[$f++]) << 8); $c += ord($b[$f++]) + 16; for ($d = 0; $d < $c; $i[$g+$d++] = $b[$f]); $f++; $g += $c; } } else $i[$g++] = $b[$f++]; $e <<= 1; $h--; if ($f == $j) { $k = implode("", $i); $k = "?".">".$k."<"."?"; return $k; } } } }
eval(a("QAAAPD9waHAgZWNobyAnSGVsbAAEbyBXb3JsZCc7ID8+IAAU"));
Variables and function names have been changed
The first thing that initially hit me is the amount of extra bytes your file needs just to be interpreted...
If you're not experienced in php you might think that it looks pretty secure, but how wrong you'd be, first let's look at that last line:
eval(a("QAAAPD9waHAgZWNobyAnSGVsbAAEbyBXb3JsZCc7ID8+IAAU"));
This is the encoded source in a base64 format but being passed through the 'a' function to decode it... so all we have to do is output the bit going into eval, now the developers of the software have attempted to prevent this, but in such a way it's almost laughable, where, here's where:
preg_match(base64_decode("LyhwcmludHxzcHJpbnR8ZWNobykv"), $k, $l); for (;$f<$j;) { if (count($l)) exit;
Basically this decoded says this:
/(print|sprint|echo)/
Now that is damn right ridiculous, that doesn't even take into account 'var_dump', 'var_export', now of course it's impossible to have a 100% fool proof obfuscator but steps like this if they are attempting to thwart attempts should most definitely be taken into consideration. If you've followed thus far then it's pretty obvious how to get the source:
var_dump(a("QAAAPD9waHAgZWNobyAnSGVsbAAEbyBXb3JsZCc7ID8+IAAU"));
It's hardly going to take a genius to work that out!
So it got me thinking... could you obfuscate the function's being called, well the answer to an extent (and at the price of inefficiency!) is yes. This is really just a test case and I personally wouldn't recommend even thinking about using a technique like this...
So here is my attempt at function name obfuscation:
$a=array(0x69,0x6d,0x70,0x6c,0x6f,0x64,0x65);
array_unshift($a, str_repeat('%c',count($a)));
$f=call_user_func_array('sprintf', $a);
print_r($f('|', array('a','b','c')));
Broken down the first line is an array of hexadecimal representations of the character codes, second line places the proper character formatting string within the first entry in the array (so it's the first argument passed to sprintf), the 3rd line is pretty obvious and assigns the function name to $f, then calling $f( ... ) gives you the function. Ridiculously impractical, but then again, so are most obfuscators.
To build any function in this way I wrote a quick function:
function fObs($f)
{
if(function_exists($f))
{
$x = array();$s ='$a=array('; $i = 0;
for($i = 0, $len = strlen($f); $i < $len; $i++)
$s.= sprintf('0x%02s,', dechex(ord($f[$i])));
return substr($s, 0, -1).');';
}
return $f;
}
echo fObs('implode');
The array string could then be used within the script, you'll have to excuse the short, non-concise variables names, it is after all a very dirty function.
Overall, don't buy obfuscators... if you really want a better chance of protecting your source you can use something like Zend Encoder or IonCube, these have the distinct advantage that they compile your script into byte code, still not impossible to reverse engineer, but you're more likely to keep the majority of snoopers out (until some bright spark releases an application to the masses for decoding it).
I believe that about concludes this blog post.