javascriptobfuscator.com support

...and wrote a tiny testing harness for easier testing.
This commit is contained in:
Einar Lielmanis 2009-10-22 09:18:14 +03:00
parent 796d7647b0
commit 7883b4d823
4 changed files with 273 additions and 50 deletions

View File

@ -1,7 +1,4 @@
/*global js_beautify */
var tests_passed = 0;
var tests_failed = 0;
var test_result = '';
var indent_size = 4;
@ -9,40 +6,26 @@ var indent_char = ' ';
var preserve_newlines = true;
var space_after_anon_function = true;
function lazy_escape(str)
function test_beautifier(input)
{
return str.replace(/</g, '&lt;').replace(/\>/g, '&gt;').replace(/\n/g, '<br />');
}
function test_beautifier(input, expected)
{
expected = expected || input;
var result = js_beautify(input, {
return js_beautify(input, {
indent_size: indent_size,
indent_char: indent_char,
preserve_newlines: preserve_newlines,
space_after_anon_function: space_after_anon_function
});
if (result !== expected) {
test_result +=
'\n---- input --------\n' + lazy_escape(input) +
'\n---- expected -----\n' + lazy_escape(expected) +
'\n---- received -----\n' + lazy_escape(result) +
'\n-------------------';
tests_failed += 1;
} else {
tests_passed += 1;
}
}
var sanitytest;
function bt(input, expected)
{
var wrapped_input, wrapped_expectation;
expected = expected || input;
test_beautifier(input, expected);
sanitytest.expect(input, expected);
// test also the returned indentation
// e.g if input = "asdf();"
@ -56,23 +39,17 @@ function bt(input, expected)
wrapped_input = '{\n' + input + '\nindent;}';
//wrapped_expectation = '{\n ' + expected.replace(/^\s{4}/gm, 'g ') + '\n indent;\n}';
wrapped_expectation = '{\n' + expected.replace(/^(.+)$/mg, ' $1') + '\n indent;\n}';
test_beautifier(wrapped_input, wrapped_expectation);
sanitytest.expect(wrapped_input, wrapped_expectation);
}
}
function results()
{
if (tests_failed === 0) {
test_result += 'All ' + tests_passed + ' tests passed.';
} else {
test_result += '\n' + tests_failed + ' tests failed.';
}
return test_result;
}
function test_js_beautify()
function run_beautifier_tests(test_obj)
{
sanitytest = test_obj || new SanityTest();
sanitytest.test_function(test_beautifier, 'js_beautify');
indent_size = 4;
tests_passed = 0;
tests_failed = 0;
@ -276,5 +253,5 @@ function test_js_beautify()
preserve_newlines = true;
bt('var\na=do_preserve_newlines', 'var\na = do_preserve_newlines');
return results();
return sanitytest;
}

View File

@ -46,16 +46,23 @@ function unpacker_filter(source)
if (document.getElementById('detect-packers').checked) {
stripped_source = trim_leading_comments(source);
var stripped_source = trim_leading_comments(source);
if (starts_with(stripped_source.toLowerCase().replace(/ +/g, ''), 'eval(function(p,a,c,k')) {
try {
eval('var unpacked_source = ' + stripped_source.substring(4) + ';')
return unpacker_filter(unpacked_source);
} catch (error) {
source = '// jsbeautifier: unpacking failed\n' + source;
//try {
if (starts_with(stripped_source.toLowerCase().replace(/ +/g, ''), 'eval(function(p,a,c,k')) {
eval('var unpacked_source = ' + stripped_source.substring(4) + ';')
return unpacker_filter(unpacked_source);
} else if (JavascriptObfuscator.detect(stripped_source)) {
var unpacked = JavascriptObfuscator.unpack(stripped_source);
if (unpacked !== stripped_source) { // avoid infinite loop if nothing done
return unpacker_filter(unpacked);
}
}
}
//} catch (error) {
//source = '// jsbeautifier: unpacking failed\n'
//+ '// You may wish to send me a report to einar@jsbeautifier.org\n'
//+ source;
//}
}
return source;
@ -93,9 +100,19 @@ function get_var( name )
return res ? res[1] : "";
}
function run_tests()
{
var st = new SanityTest();
run_beautifier_tests(st);
JavascriptObfuscator.run_tests(st);
return st.results();
}
</script>
<script type="text/javascript" src="sanitytest.js" ></script>
<script type="text/javascript" src="beautify.js" ></script>
<script type="text/javascript" src="beautify-tests.js" ></script>
<script type="text/javascript" src="javascriptobfuscator_unpacker.js" ></script>
<script type="text/javascript" src="HTML-Beautify.js" ></script>
<style type="text/css">
@ -105,7 +122,7 @@ form {
}
textarea {
width: 100%;
height: 320px;
height: 420px;
border: 1px solid #ccc; padding: 3px;
}
h1 {
@ -192,8 +209,8 @@ if('this_is'==/an_example/){do_something();}else{var a=b?(c%d):e[f];}
<p>You can always see the latest version of the code in <a
href="http://github.com/einars/js-beautify">github</a>, and you can download the beautifier for
local use (<a href="http://github.com/einars/js-beautify/zipball/master">zip</a>, <a href="http://github.com/einars/js-beautify/tarball/master">tar.gz</a>) as well.</p>
<h2>Unpacking javascript</h2>
<p>The beautifier has a simple unpacker for <a href="http://dean.edwards.name/packer/">Dean Edward's packer</a>. To unpack javascript, paste (p,a,c,k,e,d) scripts and they'll hopefully get decoded. Your feedback is welcome.</p>
<h2>Packers and obfuscators</h2>
<p>The beautifier contains automatic unpacker for <a href="http://dean.edwards.name/packer/">Dean Edward's p.a.c.k.e.r</a> and scripts obfuscated by javascriptobfuscator.com.</p>
<h2>Formatting from command-line</h2>
<p>To beautify from the command-line you can use provided beautify-cl.js script, using <a href="http://www.mozilla.org/rhino/">Rhino javascript engine</a>. See the file contents for the details.</p>
<h2><a href="http://fiddler2.com/">Fiddler</a></h2>
@ -203,11 +220,11 @@ if('this_is'==/an_example/){do_something();}else{var a=b?(c%d):e[f];}
<h2>Other nice things</h2>
<p>If you're writing javascript code, <a href="http://jslint.com/">JSLint</a> is a really fine piece of software, too. You don't have to follow its recommendations blindly, but understanding what it says about your code can greatly improve your skills.</p>
<h2>Contacts</h2>
<p>If you find some problems with the generated javascript, adapt the script for your favorite editor, or something, my email is einars@gmail.com.</p>
<p>If you find some problems with the generated javascript, adapt the script for your favorite editor, or something, my email is einar@jsbeautifier.org.</p>
<div id="footer">
<pre id="testresults"></pre>
Written by <a href="mailto:einars@gmail.com">Einar Lielmanis</a>, with the help of <a href="http://jason.diamond.name/weblog/">Jason Diamond</a>, Patrick Hof, Nochum, Andreas Schneider, Dave Vasilevsky, <a href="http://my.opera.com/Vital/blog/">Vital,</a> Ron Baldwin and others.
<a href="#" onclick="document.getElementById('testresults').style.display='block'; document.getElementById('testresults').innerHTML=test_js_beautify(); return false;">Run tests?</a>
Written by <a href="mailto:einar@jsbeautifier.org">Einar Lielmanis</a>, with the help of <a href="http://jason.diamond.name/weblog/">Jason Diamond</a>, Patrick Hof, Nochum, Andreas Schneider, Dave Vasilevsky, <a href="http://my.opera.com/Vital/blog/">Vital,</a> Ron Baldwin and others.
<a href="#" onclick="document.getElementById('testresults').style.display='block'; document.getElementById('testresults').innerHTML=run_tests(); return false;">Run tests?</a>
</div>
</div>

View File

@ -0,0 +1,101 @@
//
// simple unpacker/deobfuscator for scripts messed up with javascriptobfuscator.com
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// usage:
//
// if (JavascriptObfuscator.detect(some_string)) {
// var unpacked = JavascriptObfuscator.unpack(some_string);
// }
//
//
var JavascriptObfuscator = {
detect: function (str) {
return /^var _0x[a-f0-9]+ ?\= ?\[/.test(str);
},
unpack: function (str) {
if (JavascriptObfuscator.detect(str)) {
var matches = /var (_0x[a-f\d]+) ?\= ?\[(.*?)\];/.exec(str);
if (matches) {
var var_name = matches[1];
var strings = JavascriptObfuscator._smart_split(JavascriptObfuscator._unescape(matches[2]));
var str = str.substring(matches[0].length);
for (var k in strings) {
str = str.replace(var_name + '[' + k + ']', strings[k]);
}
}
}
return str;
},
_smart_split: function(str) {
var strings = [];
var pos = 0;
while (pos < str.length) {
switch(str[pos]) {
case ' ':
case ',':
break;
case '"':
// new word
var word = '';
pos += 1;
while (pos < str.length) {
if (str[pos] == '"') {
break;
}
if (str[pos] == '\\') {
word += '\\';
pos++;
}
word += str[pos];
pos++;
}
strings.push('"' + word + '"');
break;
case ',':
// string separator
break;
case ']':
// la finita
}
pos += 1;
}
return strings;
},
_unescape: function (str) {
for (var i = 32; i < 128; i++) {
str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
}
return str;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
t.test_function(JavascriptObfuscator._smart_split, "JavascriptObfuscator._smart_split");
t.expect('', []);
t.expect('"a", "b"', ['"a"', '"b"']);
t.expect('"aaa","bbbb"', ['"aaa"', '"bbbb"']);
t.expect('"a", "b\\\""', ['"a"', '"b\\\""']);
t.test_function(JavascriptObfuscator._unescape, 'JavascriptObfuscator._unescape');
t.expect('\\x40', '@');
t.expect('\\x10', '\\x10');
t.expect('\\x1', '\\x1');
t.test_function(JavascriptObfuscator.detect, 'JavascriptObfuscator.detect');
t.expect('', false);
t.expect('abcd', false);
t.expect('var _0xaaaa', false);
t.expect('var _0xaaaa = ["a", "b"]', true);
t.expect('var _0xaaaa=["a", "b"]', true);
t.expect('var _0x1234=["a","b"]', true);
return t;
}
}

128
sanitytest.js Normal file
View File

@ -0,0 +1,128 @@
//
// simple testing interface
// written by Einar Lielmanis, einar@jsbeautifier.org
//
// usage:
//
// var t = new SanityTest(function (x) { return x; }, 'my function');
// t.expect('input', 'output');
// t.expect('a', 'a');
// output_somewhere(t.results()); // good for <pre>, html safe-ish
// alert(t.results_raw()); // html unescaped
function SanityTest (func, test_name) {
var test_func = func || function (x) {
return x;
}
var test_name = test_name || '';
var n_failed = 0;
var n_succeeded = 0;
var failures = [];
this.test_function = function(func, name) {
test_func = func;
test_name = name || '';
}
this.expect = function(parameters, expected_value) {
// multi-parameter calls not supported (I don't need them now).
var result = test_func(parameters);
// proper array checking is a pain. i'll do it later, compare as strings now :P (fails under IE, I guess)
if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') == expected_value.join(', '))) {
n_succeeded += 1;
} else {
n_failed += 1;
failures.push([test_name, parameters, expected_value, result]);
}
}
this.results_raw = function() {
var results = '';
if (n_failed === 0) {
if (n_succeeded === 0) {
results = 'No tests run.';
} else {
results = 'All ' + n_succeeded + ' tests passed.';
}
} else {
for (var i = 0 ; i < failures.length; i++) {
var f = failures[i];
if (f[0]) {
f[0] = f[0] + ' ';
}
results += '---- ' + f[0] + 'input -------\n' + this.prettyprint(f[1]) + '\n';
results += '---- ' + f[0] + 'expected ----\n' + this.prettyprint(f[2]) + '\n';
results += '---- ' + f[0] + 'output ------\n' + this.prettyprint(f[3]) + '\n\n';
}
results += n_failed + ' tests failed.\n';
}
return results;
}
this.results = function() {
return this.lazy_escape(this.results_raw());
}
this.prettyprint = function(something, quote_strings) {
var type = typeof something;
switch(type.toLowerCase()) {
case 'string':
if (quote_strings) {
return "'" + something.replace("'", "\\'") + "'";
} else {
return something;
}
case 'number':
return '' + something;
case 'boolean':
return something ? 'true' : 'false';
case 'undefined':
return 'undefined';
case 'object':
if (something instanceof Array) {
var x = [];
var expected_index = 0;
for (k in something) {
if (k == expected_index) {
x.push(this.prettyprint(something[k], true));
expected_index += 1;
} else {
x.push('\n' + k + ': ' + this.prettyprint(something[k], true));
}
}
return '[' + x.join(', ') + ']';
} else {
return 'object: ' + something;
}
default:
return type + ': ' + something;
}
}
this.lazy_escape = function (str) {
return str.replace(/</g, '&lt;').replace(/\>/g, '&gt;').replace(/\n/g, '<br />');
}
this.log = function () {
if (window.console) {
if (console.firebug) {
console.log.apply(console, Array.prototype.slice.call(arguments));
} else {
console.log.call(console, Array.prototype.slice.call(arguments));
}
}
};
}