Anyway, my train of thinking was that YUI, Minifier and Packer are all so well-known that they almost all have dedicated un-minifiers written for them I believe. So I decided to look into proprietary obfuscators to see what was available. I found Javascript Obfuscator, a commercial application that had some impressive looking stats as far as minification, and some very unreadable end-product. As an example, here is one of my tests, which is the "Lightbox" jQuery addon:
(for space reasons, the entire script is not included)
Code: Select all
// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
(function($) {
/**
* $ is an alias to jQuery object
*
*/
$.fn.lightBox = function(settings) {
// Settings to configure the jQuery lightBox plugin how you like
settings = jQuery.extend({
// Configuration related to overlay
overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
// Configuration related to navigation
fixedNavigation: false, // (boolean) Boolean that informs if the navigation (next and prev button) will be fixed or not in the interface.
// Configuration related to images
imageLoading: 'images/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon
imageBtnPrev: 'images/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image
imageBtnNext: 'images/lightbox-btn-next.gif', // (string) Path and the name of the next button image
imageBtnClose: 'images/lightbox-btn-close.gif', // (string) Path and the name of the close btn
imageBlank: 'images/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel)
// Configuration related to container image box
containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
txtImage: 'Image', // (string) Specify text "Image"
txtOf: 'of', // (string) Specify text "of"
// Configuration related to keyboard navigation
keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image
keyToNext: 'n', // (string) (n = next) Letter to show the next image.
// Don´t alter these variables in any way
imageArray: [],
activeImage: 0
},settings);
// Caching the jQuery object with all elements matched
var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
/**
* Initializing the plugin calling the start function
*
* @return boolean false
*/
function _initialize() {
_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
return false; // Avoid the browser following the link
}
/**
* Start the jQuery lightBox plugin
*
* @param object objClicked The object (link) whick the user have clicked
* @param object jQueryMatchedObj The jQuery object with all elements matched
*/
function _start(objClicked,jQueryMatchedObj) {
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
$('embed, object, select').css({ 'visibility' : 'hidden' });
// Call the function to create the markup structure; style some elements; assign events in some elements.
_set_interface();
// Unset total images in imageArray
settings.imageArray.length = 0;
// Unset image active information
settings.activeImage = 0;
// We have an image set? Or just an image? Let´s see it.
if ( jQueryMatchedObj.length == 1 ) {
settings.imageArray.push(new Array(objClicked.getAttribute('href'),objClicked.getAttribute('title')));
} else {
// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
for ( var i = 0; i < jQueryMatchedObj.length; i++ ) {
settings.imageArray.push(new Array(jQueryMatchedObj[i].getAttribute('href'),jQueryMatchedObj[i].getAttribute('title')));
}
}
while ( settings.imageArray[settings.activeImage][0] != objClicked.getAttribute('href') ) {
settings.activeImage++;
}
// Call the function that prepares image exibition
_set_image_to_view();
}
/**
* Create the jQuery lightBox plugin interface
*
* The HTML markup will be like that:
<div id="jquery-overlay"></div>
<div id="jquery-lightbox">
<div id="lightbox-container-image-box">
<div id="lightbox-container-image">
<img src="../fotos/XX.jpg" id="lightbox-image">
<div id="lightbox-nav">
<a href="#" id="lightbox-nav-btnPrev"></a>
<a href="#" id="lightbox-nav-btnNext"></a>
</div>
<div id="lightbox-loading">
<a href="#" id="lightbox-loading-link">
<img src="../images/lightbox-ico-loading.gif">
</a>
</div>
</div>
</div>
<div id="lightbox-container-image-data-box">
<div id="lightbox-container-image-data">
<div id="lightbox-image-details">
<span id="lightbox-image-details-caption"></span>
<span id="lightbox-image-details-currentNumber"></span>
</div>
<div id="lightbox-secNav">
<a href="#" id="lightbox-secNav-btnClose">
<img src="../images/lightbox-btn-close.gif">
</a>
</div>
</div>
</div>
</div>
*
*/
function _set_interface() {
// Apply the HTML markup into body tag
$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + settings.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><img src="' + settings.imageBtnClose + '"></a></div></div></div></div>');
// Get page sizes
var arrPageSizes = ___getPageSize();
// Style overlay and show it
$('#jquery-overlay').css({
backgroundColor: settings.overlayBgColor,
opacity: settings.overlayOpacity,
width: arrPageSizes[0],
height: arrPageSizes[1]
}).fadeIn();
// Get page scroll
var arrPageScroll = ___getPageScroll();
// Calculate top and left offset for the jquery-lightbox div object and show it
$('#jquery-lightbox').css({
top: arrPageScroll[1] + (arrPageSizes[3] / 10),
left: arrPageScroll[0]
}).show();
// Assigning click events in elements to close overlay
$('#jquery-overlay,#jquery-lightbox').click(function() {
_finish();
});
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
_finish();
return false;
});
// If window was resized, calculate the new overlay dimensions
$(window).resize(function() {
// Get page sizes
var arrPageSizes = ___getPageSize();
// Style overlay and show it
$('#jquery-overlay').css({
width: arrPageSizes[0],
height: arrPageSizes[1]
});
// Get page scroll
var arrPageScroll = ___getPageScroll();
// Calculate top and left offset for the jquery-lightbox div object and show it
$('#jquery-lightbox').css({
top: arrPageScroll[1] + (arrPageSizes[3] / 10),
left: arrPageScroll[0]
});
});
}
Code: Select all
eval((function(x){var d="";var p=0;while(p<x.length){if(x.charAt(p)!="`")d+=x.charAt(p++);else{var l=x.charCodeAt(p+3)-28;if(l>4)d+=d.substr(d.length-x.charCodeAt(p+1)*96-x.charCodeAt(p+2)+3104-l,l);else d+="`";p+=4}}return d})("(function ($) {$.fn.lightBox = ` 4&settings) {` #$ = jQuery.extend({overlayBgColor:\"#000\", ` 0#Opacity:0.8, fixedNavigation:false, imageLoading:\"` )!s/`!L!box-ico-l` 7\".gif\"` I#BtnPrev` >.btn-prev` >+Next` 72next` >+Close` 82close` B)lank` 9/lank` A#containerBorderSize:10` -'ResizeSpeed:400, txtImage:\"` \"!\"` -!Of:\"of\", keyTo`!]#c` ($`\"h\"p` '$`\"J\"n`!^$Array:[], active` x\"0}, `$|%;var`$v#MatchedObj = this;`%L%_initialize() {_start(this,` L-);return `%$!;}` [&` P\"objClicked` I/ {$(\"embed, object, select\").css({visibility:\"hidden\"});_set_interface();`\"9$.`\"_&.length = 0` 5&`\"n'` 4!if (`!F,` P%= 1`(+'` o(push(new ` *!`\"9'.getAttribute(\"href\")`\"7!` +2title\")));} else {for (var i`!\\! i <`#'-`![#; i++`!<B` R,[i]`!T3` (>`!i'}while`+;&`!7'[`#`0][0] !=`\"V6`!@\"`\"((` S'++;}`%4\"mage_to_view()`&H)`%K*`&<!body\").append(\"<div id=\\\"jquery-`,`#\\\"></div>` 0-`*I$\\\"` 1'`*]%`*9%-`!S!-b` \"C\\\"><img` 6+` 2%div style=\\\"\\\"` 7+nav\\\"><a href=\\\"#` .0-b`-z\"`\"4!a` *@Next` F$`\"\\+` B%`/!#`! ;` ?#-link`\"A$src=\\\"\" +`,'%`%j\"`00# + \"`!/*`!=\"`!14`#h,data`#TE` I!` 42` 9#etails\\\"><span` (8-caption`!x!span` /@urrentNumber` N'`\"54secN`$s=` ?\"-b`26#`#X<`2]$`#[:` '!\"`0T\"arrPageSizes = ___get` *$();$(\"#`(g*`/@$backgroundColor:`!A%` A#Bg` 3!, opacity` 0-O` 2\", width:`!>([0], height` **1]}).fadeIn(`!s*croll`!t*` .!`!v*`#Q$`!}$top` x%` J![1] +`\"i)[3] / 10, lef`!E&` H\"0]}).show`\"v1,`!#0lick(`,v%`4)!finish();}` `\"`(1,` -&`%G+` V<`4a*);$(window).resize` J*`$xZ`$=K`#a~`$>A;}`1!.`1D*`12!`$(-\"`%4%if`3)'fixedNavigation` M,` v!`$d'`,4` 8'`+P7\").hide();} else` n;nav` m'`0i'` $.Next` spvar objImagePreloader = new ` 0!;` --.onload = `&)`\"8.\").attr(\"src\",`-P+Array[` +%active`!$!][0]);_`(*\"_`\"A%`%?#box(`!A.`'f!,`!x..`'g\")`!]E};}` 9/src =`!TI`'7(`!o7int` N!W`!z\"` '$H`!p\"`*[\"intC`$X\"` A! = `#^)`%;,`)I\"`\"l!`.p#` W&` r\"` <B`#4\"` Y&`!5$`!f)`2J(` ^%BorderSize * 2` X$`!3%`\"7*` 7GDiffW` [\"`\"d)-`!V%` >(H` =)`!&#` F!` &\"`/P*`\"N2animate`.#$` $`-{%` d%}`'T'` b%R`%3!Speed,`&8*_show`%A\"`1$\"if (`\"8&= 0 &&`!~'= 0) {if ($.browser.msie) {___pause(250`+m&` -%100);}}`\"/9`+5$`0J*`${)`2],`,+=` d$`\"&`$fJ`/v+`#$'`/e5`,l#`+s1fadeIn`4&+` l&_data();`1)!n`0D%`$6\"_p`*b\"_neighbo`)y#s(`!L3` f#`!Y+`#Z7slideDown(\"fast\"`!p0`/?&aption`\"F&`2A)`+y=1]`.v1` k0tml` KJ`3x$}`!<3.length > 1`!&;`1],`!>)tx`&&\" + \" \" +`!!'`!G' + 1)` :%` Q(Of` %.`!T-`\"#&`$x(`%O+`!v+nav\"` O%`'yRbackground:\"transparent url(`!_.Blank + \") no-repeat\"`+G#`\"_1!`+?&` 7%fixedN`\"-%`\"\"/`!s$`!Q0`!B3` K\"`!U\"left 15%`!W).unbind().` \"!\"click\"`-X+`!o1`3}'` +(- 1`)y\"`)>\"to_view();return false;}`-Y&`\"+7`!S%hover`+(*$(this`!g;}`\"G+` i5`$wV}`&o$`\"l~`#^,`*_*`&^+`*j7-`+ !`&WO`(W4`$'5Next`#.\"r`1X!`&*}+`&G``\"@#`&=j`\"K<`&*~`&9o`#EF}_enable_keyboard`/a);`/'` ,8 {$(document).keydown`$/'objEvent) {` W&a` 8!` 2&`!K!`!&&dis` iD`%J$` S(` n5`(|\"` '$ == null) {keycode = event.keyCode;escapeKey = 27`&y%` E&` d$` =1` 4%DOM_VK_ESCAPE;}k` 9!String.fromCharCode(` r#).toLowerCase(`2<\"` P!`%)'keyToClose || ` 7#\"x\"` '#`!Z\"= `!G%) {_finish();`,B!` ]1`/:!` U*37`,+,`3R/`-Ja`${:`.7#`!b1`*&!`!h+9`!X;`.N=`(Ra`!s=`'.'preload_neighbor` g\"s(`!y,`!\\1 >`!E1) {obj`\"{!= new ` 1!;` -#.src`\"'(` p&[`\"/4][0]`%{\"` -1>`%J!obj`&%!`!$+Prev` cL-`!+$`\"~'`'A$`0G\"jquery-`0L$\").remove();` 4'overlay\").fadeOut`0O,` 7/` _%}` f!embed, object, selec`3u%visibility:\"` '!le`/\\!`!l&__getPageSize() {var xScroll, y` #\"`*H!window.innerHe`1X!&& ` /#s` C!MaxY) {` X# =` 6$` O!Width +` A-X;` #` A+` }#` ?.Y`-,$if `.M%.body` =#` S#> ` /*offset` 7\"`!Y)` P0`!i!`!O'` n6`.Y%` Y4`!2\"` S:`!P(;}var`\"O#` Q!,` &#` 8#`'a\"lf`\"{(`)G#` l%` v$Ele` +!clien`!>\") {` q'`!>(` :7`\"<%` M*`!=&` ?#`!](` 3)`\"~*`!P8 &&`!3<`\"K%`!VO`!S+` eA`&85`!)6`$q!` t@` >'`!%$if (`%a$<`%/)) {page` ]%`%K)`$;$` 5)`)z$` p!`&z$` m$`%H$page`!z$` =#` a)` 4$` K';}array`+2$`..#`-n!(` J%, `!B&`'=$`'?/);return ` f)`,02`!X!`,+9`&O!pageYO`(p!) {`),&` -,;`)k&` 2%X` 4#`&L\\`*\"Top`!5)` ->`!I'` .;Lef`&_9`,A:` t3` 7'` z\"`$,&` D$`%!&`#r,`$X/`&(#`$a(pause(ms`$`#dat`%y$Date;curD` ,#ull;do` A\"` ,'` B$} while (` 4$-` g\"< ms);}`!E#this.unbind(\"click\").` #!(_initialize);};})(jQuery);"))
But something was bugging me, and it was that eval() at the start of the script. I changed it to an alert() and boom, there was the entire, unobfuscated script. The whole thing is nothing but smoke and mirrors that anyone could undo. They could make the code look as crazy as they want to, because they are sending the key to unobfuscate along with the code! This is not the way YUI, Minifier and Packer work as far as I have seen. Very sad to say the least.