"Javascript Obfuscator" is a no-go

All development related reviews. Engines, Tutorials, books, ect.
Post Reply
User avatar
Jackolantern
Posts: 10891
Joined: Wed Jul 01, 2009 11:00 pm

"Javascript Obfuscator" is a no-go

Post by Jackolantern »

Ok, first let me say I am not looking at obfuscators to keep people from learning from my scripts. I would only use one in the case of making an HTML5 MMORPG a tiny bit more secure to keep the common user from poking around in the code too much to find potential weaknesses; the exact same reason why Blizzard would never release the source to the WoW client.

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]
				});
			});
		}
		
Here is what Javascript Obfuscator produced with it:

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);"))
As you can see, it is as close to garble as you could ever imagine. No one could ever make sense of that. It was also a 75% decrease in file size, which is nearly 2x more minification than YUI Compressor. Impressive by any stretch of the imagination. I loaded it up and it worked like a charm, just like the full, uncompressed script.

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.
The indelible lord of tl;dr
User avatar
hallsofvallhalla
Site Admin
Posts: 12023
Joined: Wed Apr 22, 2009 11:29 pm

Re: "Javascript Obfuscator" is a no-go

Post by hallsofvallhalla »

Whoa thats crazy! I would email the makers and say "Hey whats up!"
User avatar
Jackolantern
Posts: 10891
Joined: Wed Jul 01, 2009 11:00 pm

Re: "Javascript Obfuscator" is a no-go

Post by Jackolantern »

I did about 2 hours ago ;)
The indelible lord of tl;dr
User avatar
hallsofvallhalla
Site Admin
Posts: 12023
Joined: Wed Apr 22, 2009 11:29 pm

Re: "Javascript Obfuscator" is a no-go

Post by hallsofvallhalla »

I am worried about obfuscating and keeping cheaters from using javascript through the URL. Kind of a mix, 20% Obfuscator/80% URL tampering.

I am interested in what they reply
Post Reply

Return to “Development Related”