About Me

My photo
Bay Area, CA, United States
I'm a computer security professional, most interested in cybercrime and computer forensics. I'm also on Twitter @bond_alexander All opinions are my own unless explicitly stated.

Friday, April 29, 2011

Firefox 4 Browser Forensics, Part 4

When I started this series, I had no idea it would go on this long. There are more forensic artifacts in FF4 than I thought. It seems like every time I turn around I find another database to mine for artifacts. For example, I was just about to start tearing apart cookies.sqlite when I saw there was a search.sqlite. It didn't turn out to have any significant artifacts, just the listing of search engines in the quick search box. Much more interesting are browser cookies.

Cookies
Browser cookies are a notable and well-known source of browsing history. In IE, each cookie is a separate text file, but in Firefox, they're stored in yet another sqlite database: cookies.sqlite. This database has just one table, moz_cookies, with several columns:
  • id: an index
  • name: the variable being stored
  • value: the value of "name"
  • host: the website the cookie is for
  • pathmain: the path the cookie is valid for.
  • expiry: when the cookie should be purged
  • lastAccessed: when the website last accessed the cookie in PRTime
  • isSecure: is HTTPS required to access the cookie?
  • isHttpOnly: Can the only be accessed by HTTP, or can other methods (Javascript) access it?
  • baseDomain: The site's base domain, without www or other subdomain
  • creationTime: when the cookie was created in PRTime

When you're drawing conclusions from cookies, remember to take into account the browser's cookie control settings as well as the cookie timestamps. Like browsing history, cookies can tell you when the user viewed a website and can be a source for usernames and passwords for insecure websites. However, if the user clears cookies regularly, the amount of data will be limited. It also doesn't mean the user visited the site directly, as many advertising cookies (such as doubleclick.net) will be downloaded for related sites.

Saved Sessions
One useful aspect of Firefox is it will automatically save the currently open browser tabs so you can reopen it the next session. The browser tabs are saved in sessionstore.js in Javascript object notation. These aren't just the raw URLs, possible fields include page title, referrer, formdata, and cookies. Any GET variables in the URL are preserved as well. As a result, if the user was logged into a website, they may still be be logged in when the saved session is restored. If sessionstore.js has been damaged or deleted, it may be recoverable in the backup, sessionstore.bak. A quick test shows that sessionstore.bak isn't always a duplicate of sessionstore.js, opening a new browser session overwrote sessionstore.js but not sessionstore.bak, so you may be able to recover two different browser sessions under some conditions.

Bookmark backups
In the profile folder there's a folder called bookmarkbackups, which contains a series of JSON files storing the last 10 backups of the user's bookmarks. The filenames are in the format of bookmarks-YYYY-MM-DD. These backup files include the bookmarks the user explicitly makes, but also Firefox's "Smart Bookmarks" which include items of forensic value like "Most Visited", "Recently Bookmarked", and "Recently Tagged". These backups also include timestamps in PRTime for when the bookmark was added and last modified. As always in forensics, backups like these can provide valuable insight into detecting antiforensics (such as deleting bookmarks) and in behavior over time.

Downloads
downloads.sqlite is where Firefox 3+ stores information relating to downloaded files. There's just one table, moz_downloads, but it has quite a few useful artifacts.
  • id: an index
  • name: the local filename of the download
  • source: the remote filename and path being downloaded
  • target: where it's being downloaded to
  • tempPath: if the file is complete, it will be blank. If not, it's where the incomplete file is being stored before moving to target
  • startTime: Time the download started, in PRTime
  • endTime: Time the download finished, also PRTime
  • state: state of download, encoded as an integer 
  • referrer: the page containing the link to the file
  • entityID: a value used for resuming downloads
  • currBytes: Number of bytes downloaded.
  • maxBytes: Total file size.
  • mimeType: MIME file type.
  • preferredApplication: From the download dialogue box, if the user clicks run, this stores the program that will open the downloaded file. If the user clicks save, this will be blank.
  • preferredAction: Action to take when download is complete. Default is 0, just save the file.
  • autoResume: Can the download be resumed if broken?
extensions.ini records what Firefox extensions are installed, which may be useful if, for example, hacker tools like Hackbar or anonymizers like FoxyProxy are installed.

Form history
formhistory.sqlite is another good source for artifacts, although unfortunately it doesn't track what website the form was used on.

  • id: Another numeric index
  • fieldname: The field that contained value. This may be an HTML field in a website's form, or it may be a Firefox field, like searchbar-history.
  • value: What was typed into fieldname. In addition to search history, this is a good source for email addresses and usernames connected to the user.
  • timesUsed: How many times the user has typed value into fieldname.
  • firstUsed: The first time the user typed value into fieldname, in PRTime as always.
  • lastUsed: The last time the user typed value into fieldname, in PRTime as always.
  • guid: A global id for the formhistory, in case syncing is enabled.


Cache
The cache exists in two folders, for Windows 7 they are Users/[user]/AppData/Local/Mozilla/Firefox/Profiles/[profile name]/Cache and Users/[user]/AppData/Local/Mozilla/Firefox/Profiles/[profile name]/OfflineCache. The offline cache is used when Firefox is in offline mode, but it's the standard cache which will be more likely to be used for forensics. The cache can be viewed through the browser by navigating to about:cache, or it can be viewed directly on the file system. On the filesystem, the actual cache files are stored in a set of directories and subdirectories with names in hex. The files can be located by using the _CACHE_MAP_ and _CACHE_00X_ files. A full writeup on how the cache works has been done by Symantec's response team here. As far as I can tell, the cache scheme hasn't changed since then. This layout is rather inconvenient to navigate by hand, so an automated tool like Firefox's cache browser or other tool is definitely the way to proceed here. Looking at about:cache, it appears Firefox stores useful information like the full URL cached (including GET parameters), cached file size, number of times the cached file was accessed, last time the file was modified and time the cached file expires, full hex dump of the file, and the full HTTP request issued to access it. Since these are individual files on the hard drive, the standard Modified-Accessed-Created timestamps can provide additional information.

Alright, we're finally through the forensic artifacts available in Firefox 4! There'll be just one entry left, anti-forensics in Firefox 4.

Friday, April 22, 2011

Firefox 4 Browser Forensics, Part 3

In Part 1, we covered typed URLs and the bookmark structure of Firefox 4. In Part 2, we started delving into the moz_places table, ending with an introduction to frecency.


As I noted, frecency is a number generated by Firefox that combines different measures of user behavior with a URL, including bookmarking it, frequency and recency of visits. The result is a single number that tries to quantify how interested the user is in the URL. This means that with a simple SQL query (select * from moz_places order by frecency desc limit 20 for example), we can get a snapshot of the user's current sites of interest. This could be particularly useful for an "acceptable use policy" investigation, where the issue may turn around how much of a user's browsing history is personal vs. work-related. If the FarmVille app is among a user's top 20 sites sorted by frecency, a violation should be pretty easy to prove. Since frecency is based on other known factors, you could go through the user's full behavior to see what exactly they did to get there (type of visit, etc). That information is stored in moz_inputhistory (which we already covered) and moz_historyvisits.


moz_historyvisits
As the names imply, moz_places records the urls that a user visits, and moz_historyvisits record the details of each visit to the url. Since it records each visit to each page in the domain, there will be a lot of records ... which makes a shorthand like frecency very useful. The columns are id (the index), from_visit (the referrer), place_id (the connection back to the URL entry in moz_places), visit_date  (timestamp in PRTime again), visit_type, and session.


Let's step through what this means by actually analyzing my browsing behavior. First, I ran select * from moz_places order by frecency desc limit 20 to grab the top 20 websites from moz_places to look for something interesting. I'm arbitrarily picking http://www.wired.com as my URL of interest. moz_places.id for this url is 485, so I run select * from moz_historyvisits where place_id=485 which yields:



seven visits. Now, for these results we can ignore the first column (id). Next is from_visit, which gives the id of the page I visited before visiting the current page. For the last three visits, it's 0 indicating that I opened a new tab and went straight to Wired. For the others it refers to the immediately prior id number, which is what you would expect if I was only browsing in one tab at the moment. If the numbers were not consecutive, that would imply multiple-tabbed browsing. In the first entry, I came to Wired after visiting the entry in moz_historyvisits.id=941. Looking up that entry, I see that was a visit to place_id=484, which is http://wired.com. Incidentally, that's true for the first four entries. To explain why, see that the visit type for all four www.wired.com entries is 5, indicating a permanent redirect (HTTP 301). For the respective entries for http://wired.com, the visit type is 2, indicating I typed the URL. So, at those times I typed wired.com in the address bar. Firefox logged the visit and took me to http://wired.com, which redirected me to http://www.wired.com and logged another visit. For the last three visits to Wired, I typed www.wired.com directly. Since those were the more recent and since I logged 7 visits to www.wired.com and only 4 to wired.com, www.wired.com was the one that was on top in frecency (8800 vs. 4813) instead of the non-www version, despite the fact that a typed URL gets a frecency bonus over a redirect URL. Also note that just because I came to www.wired.com via a redirect, doesn't mean I didn't intend to go there. Seeing what site took me there is essential to determine what my intentions were.


Obviously this is a trivial example, but it should be pretty apparent how it could be applied to a more interesting investigation.


More tables
There are a set of tables called moz_annos, moz_anno_attributes and moz_items_annos, but as far as I can tell they are used by Firefox extensions and are unlikely to be useful for forensics.


That's everything of interest out of places.sqlite, but there are other tables that have useful information: tables like signons.sqlite. This is where the logins and passwords are stored. If the user hasn't specified a master password for his Firefox profile, then the usernames and passwords will be clearly available through Options > Security > Saved Passwords. If there is a master password set and you don't have it, then you won't be able to get to the list of sites and passwords directly. Nothing stops you from accessing signons.sqlite directly, though.


signons.sqlite contains two tables, moz_logins and moz_disabledHosts. moz_disabledHosts is a list of websites which the user doesn't want Firefox to ask to remember logins. moz_logins is where the sites, usernames and passwords are stored. The columns are id, hostname (the login page), httpRealm, formSubmitUrl (the actual page the username and password get sent to), usernameField (the identifier of the username field), passwordField (the identifier of the password field), encryptedUsername, encryptedPassword, guid (global ID, for login syncing), encType, timeCreated, timeLastUsed, timePasswordChanged, timesUsed.


Setting a master password in Firefox does not change any of the data here, it just blocks you from viewing the decrypted passwords IN Firefox. You can still get lots of useful browsing information from the file, everything except what their username and passwords are. Documentation on signons.sqlite is scanty, but from what I can tell all the timestamp fields are new in Firefox 4. Strangely, these timestamps aren't encoded in PRTime (microseconds since Unix epoch) like the rest of the timestamps. Instead it's miliseconds since epoch, so we get all the timestamps with:


sqlite> select hostname, datetime(timeCreated/1000,'unixepoch'),datetime(timeLastUsed/1000,'unixepoch'),datetime(timePasswordChanged/1000,'unixepoch') from moz_logins;


For my test system, the results are:


This system has credentials saved for two Facebook accounts. For the first, the login was saved on March 17 and last used on March 31. For the second, it was only used once, on April 4. Neither has changed passwords. This can be useful if you need to prove that a user actually logged into a site, rather than just visiting it. If a user clears their browsing history but not saved passwords (which is default behavior), this will still show up as well. It's also yet another way to prove that a user actually intended to go to a site repeatedly instead of it being an accidental click or redirect. 


The usernames and passwords are encrypted, but the master password and decryption keys are stored in key3.db (more info here). Since you have the decryption keys, decrypting the passwords should be possible if needed. There are tools out to do that, so I'll leave that as an exercise to the reader. :)


In Part 4, I'll go into cookies and cached files.

Monday, April 18, 2011

Firefox 4 Browser Forensics, Part 2

In Part 1, we covered typed URLs and the bookmark structure of Firefox 4. Now, I'm going to start digging into moz_places.

moz_places
moz_places is the main table for the browser history. It's columns are id, url, title, rev_host, visit_count, hidden, typed, favicon_id, and frecency[sic].

The first 20 entries of moz_places in my test system.

As we've mentioned before, id is a simple numerical index that connects the links in moz_places to their references in the other tables. The Mozilla Wiki has a handy graphic for reference.


After id is url which is the full URL of the visited page. As you can see from my test results above, it's the exact page visited, not just the domain. That's why we see my click train from http://www.phishtank.com/ to http://www.phishtank.com/login.php and then to http://www.phishtank.com/phish_archive.php. Next is Title which is the stored title of the page.

The Mozilla Wiki notes:
Bookmarks are basically pointers to that history entry, with a few properties to determine their placement in the bookmarks folder hierarchy. The design is such that most properties of a bookmark are derived from the moz_history entry, and not the bookmark.
This is problematic as soon as the same URI is bookmarked more than once. Eg: If you change a bookmark's title, then the title changes anywhere you've bookmarked that URI.
This is no longer the case. As we already discovered, the moz_bookmarks table has it's own title field where the bookmark title is stored. Changing the bookmark title no longer overwrites the title in moz_places.

rev_host is the fully qualified domain name backwards, ending with a period. So, "http://www.phishtank.com/phish_detail.php" gets converted to "moc.knathsihp.www." Firefox uses this so that they can index this and quickly search for subdomains of a primary domain. An investigator may want to use this field for the same purpose. See the Firefox source code. Thanks to FirefoxForensics for this.

visit_count is another field very useful for an investigation. This is an integer that increments for every time the user visits the site. Since the places.sqlite is associated with the user logged into the computer, this only reflects the number of times that particular user account visited the site. Some additional notes from FirefoxForensics:
This counter is incremented when the associated places.sqlite::moz_historyvisits::visit_type is not 0, 4 (embedded), or 7 (download).

Research by the author shows that reloading a page by clicking the 'Reload current page' button, pressing CTRL-R or following a self-referring URL does not increment visit_count.

If the start-up option to 'Show my windows and tabs from last time' is selected, the visit_count of those pages will NOT be incremented when the browser is started and they are loaded.

If the start-up option to 'Show my home page' is selected, the visit_count of the home page WILL be incremented each time the browser is started and they are loaded.

hidden
According to the Mozilla devs, "Hidden uri's are ones that the user didn't specifically navigate to. Those tend to include embedded pages and images. It might include something else, but I'm not 100% sure." At first glance one might want to exclude this from analysis because the user didn't specifically navigate to them, but since it includes images that would be a mistake. If the user navigated to the page containing the image, the images entry here would still give you valuable info like how many times the image has been loaded, etc.

typed is different than the input field in moz_inputhistory If the user started typing and then clicked the quick result, then it'll be listed under moz_inputhistory.input. If the user typed the full url, then it'll be under moz_places.typed.

To give an example, the first time I went to phishtank.com, I typed "www.phishtank.com" into the address bar and it took me to the site. That generated this entry in the database:

The next time I visited, I just typed 'phishtank' before Firefox recognized the URL and I went to the site. That got stored in moz_inputhistory.

favicon_id is an id number that connects to which image in moz_favicons is the favicon (the little graphic in the title bar) for the page.

The last field in moz_places is frecency. That's not a typo for "frequency", it's a combination of "frequency" and "recency". As you'd expect from the name, it's a value generated by both how often the user has visited a place and how recently they visited it. The full algorithm is here. 0 is the default score, and the higher the number the higher it appears on autocomplete results. As the algorithm link shows, it draws on a number of elements of user behavior to determine the frecency. This means that you can use it as a shorthand for determining if a user was interested in the URL and then investigate the underlying behaviors (how often they visited, how often they typed the address vs. redirect vs. following links, if they bookmarked it, etc) to articulate what exactly they did that shows interest in the link.

Frecency turns out to be a pretty complicated topic, so I'll save it (and signons.sqlite) for part 3. Part 4 covers cookies, cache, downloads and more.

Wednesday, April 13, 2011

Firefox 4 Browser Forensics, Part 1

Firefox 4 was released almost a month ago now, and was in open beta for a significant time before that. However, a cursory Google search doesn't reveal much documentation of it's features of forensic interest, or it's changes since the last version.

For this analysis, I'm using Firefox 4.0 (current version as of writing) running on a Windows 7 VM. To examine the sqlite databases, I'm using the sqlite3 command line tool. Sqlite3 runs under Linux and Windows, and you can feed it SQL commands to script common actions, such as searching history for keywords or for browsing during a time range of interest.

Just like Firefox 3, Firefox 4 stores the browser history in an SQLite database. For Windows Vista/7, it's located at :\Users\\AppData\Roaming\Mozilla\Firefox\Profiles\\places.sqlite This database contains the tables moz_anno_attributes moz_annos moz_bookmarks moz_bookmarks_roots moz_favicons moz_historyvisits moz_inputhistory moz_items_annos moz_keywords and moz_places These tables seem unchanged from version 3, as documented on the MozillaZine.

moz_inputhistory
When a person begins typing into the address bar, Firefox searches through it's browser history to try and connect what the user is typing with the websites he has visited recently. It outputs the suggestions with the webpage title and full URL in the suggestions below the address bar. When the user clicks the suggestion, the text they typed to find that url is stored in the moz_inputhistory table. The moz_inputhistory table is of particular interest to forensics because it can show that the person using the user account typed something into the address bar to access the site, rather than following a link or getting forced there by a pop-up.

The column names for this table are place_id, input, use_count, and PRIMARY KEY. Here's an example of the contents of the table, taken from my test system.



Note that none of these are full links. What you see here is what you get when someone starts typing a URL or keyword into the address bar and then clicks on one of the autocomplete terms. It does not seem to record full URLs. So, we get "ars t" and "arstechn" from two of the ways I accessed Ars Technica. It does not store search terms from the search box.

place_id refers to the destination of the input. This number can be converted into the actual website visited via the moz_places table. So, for example, I see that when I typed "ars t" into my address bar, I clicked on the website with id 197. The command > select * from moz_places where id=197; returns the website URL (http://arstechnica.com/) and the website title (Ars Technica) as well as other information that we'll cover when we get to the moz_places table.

The last column, use_count is an odd one. David Koepi looked at Firefox 3.6 and suggested that use count refers to the number of times the same text was typed into the address bar. On my system in Firefox 4, that doesn't seem to be the case as most of the values in use_count are decimals (see screenshot above). Running some tests, it seems to be related to the number of times run ... for example, typing "sans.o" into the address bar the first time adds it to moz_inputhistory and sets use_count to 1. So far so good, but here's where it gets odd. Typing it in four more times changes it to 1.9, then 2.71, 3.439, and 4.0951. As you can see from the other links in my history, many of them end up with values less than 1. This is probably a bug in Firefox, so I wouldn't rely on the value of this for any reason until it's better understood.

This is why it's important to do your own testing, instead of just believing what you read on the Internet.

moz_bookmarks and moz_bookmarks_roots
Bookmarks are also valuable for an investigator to examine because like inputhistory it implies that the user took an active action with the site, in this case marking it for later re-visit. The fields here are id, type, fk, parent, position, title, keyword_id, folder_type, dateAdded, lastModified, and guid. Comparing to the documentation by David Koepi and firefoxforensics, it appears guid has been added for FF4. According to the Mozilla wiki, guid was added in order to have a unique global identifier, rather than the simple incrementing index of id. This means that gid, not id, would be the proper link between bookmarks across computers using the new Sync service in FF4. Note, I haven't tested this.

type is an integer referring to the type of the bookmark. Type 1 is a normal bookmark, 2 is a tag (folders are really just a type of tag). Type 3 is a separator, and so has no useful info. fk is a foreign key, which links to the id in moz_places (where you can find the actual url). parent refers to the id of the containing object (folder), which can tell you where the user categorized the bookmark. position refers to the listing order of the bookmarks, and is not likely to be interesting. title refers to the display name of the bookmark and keyword_id is the index number of the keyword (moz_keywords) associated with the bookmark. It may be useful to find associated bookmarks. folder_type is not likely to be useful, but dateAdded and lastModified would be. These values are exactly what they sound like, stored as PRTime

moz_bookmarks_root defines the root folders in the Firefox bookmark structure, and is not of forensic interest.

In Part 2 I'll get into moz_places and more browsing history. Part 3 has moz_historyvisits and signons.sqlite. Part 4 covers cookies, cache, downloads and more.

Friday, April 8, 2011

Reversing malicious Javascript, part 2

This is part 2 in my adventures reverse-engineering a malicious Javascript I found on my computer. Last time I unraveled some annoying string manipulation that eventually ran return eval(String.fromCharCode()) on a long series of Unicode-encoded characters.

I finally unencoded that long string of Unicode and, as expected, it was more Javascript. I was surprised at how much there was ... several hundred lines of code. This is unusually large for malicious Javascript. Bigger doesn't mean more sophisticated, however!

There are two Javascript functions that are relevant here. The first is called 'zazo', which tries to exploit a vulnerability in the Java Development Kit to load a malicious Java applet that the attacker controls. The payload is gone by now, so I can't tell what it would have done. This is a known vulnerability, CVE-2010-0886, and is described in detail here.

The second function is the reason this malicious script is so long. It includes a version of PluginDetect, a script by Eric Gerds that is designed to determine what version of many common browser plugins are being used. In this case, the malware author has it probe for Java and Adobe Reader versions, although the script only uses the Adobe Reader version. Although this plugin detector is being used maliciously, it's not inherently malicious and there's no reason to think Eric has any connection to the malware author.

After determining what version of Adobe Reader is being used, the script makes a simple calculation: if Reader is older than 8.0, it serves up one malicious pdf. If Reader is between 8 and 9.3.1, it serves up an alternate malicious PDF. If you don't have Reader or if your copy of Reader is up to date, you're safe from this script. Reader version 9.3.1 was released specifically to patch the vulnerability described in CVE-2010-0188, which affected Reader versions 8 to 9.3, so we can be fairly sure that's what this PDF was exploiting.

Interestingly, in part 1 we discovered that this bit of malware knew that it was 2011 and it would need to be modified to function in a different year. Despite that, the three bugs it attempts to exploit are old. The Java bug had a patch available a year ago. With the Reader exploits, one was patched last November and the other only affected versions older than 2006! These are not anywhere close to 0-day attacks. This is exactly why it's so important to keep your software up to date.

Also, Java is very frequently exploited. If you don't absolutely need it, get rid of it. Then hackers will have one less way to attack you and you'll have one less program to keep updated.

Wednesday, April 6, 2011

Reverse engineering a malicious javascript part 1

My antivirus program flagged a malicious javascript a few days ago. At some point in my web browsing, a webpage quietly served up a malicious script in addition to the regular content. It was saved to my browser's cache and quarantined by my antivirus. Being the curious person that I am, I thought I'd try my hand at understanding how it works. Of course, as is typical of malicious scripts it was obfuscated.  Instead of looking like nice Javascript:

<script type="text/javascript">
document.write("<h1>This is a heading</h1>");
document.write("<p>This is a paragraph.</p>");
document.write("<p>This is another paragraph.</p>");
</script>

the malicious script is a mess, deliberately difficult to read (click to enlarge):


The sequence of numbers keeps going for the rest of the script.

Malware authors use tricks like this to keep people like me from understanding how the script works, and to make it more difficult for antivirus software to detect the page. If the av can't penetrate the obfuscation, then if they start detecting this page all the malware author needs to do is obfuscate it differently to generate a new signature. For more information on reverse engineering malware, take a look at this BlackHat presentation (pdf).

The curious thing about obfuscation is that it's designed to be difficult for people to understand yet simple for computers to understand. Luckily for me, that means we can use a javascript engine to translate it all back for us. Didier Stevens has modified Mozilla's Spidermonkey for exactly this purpose. All I need to to is extract the javascript from the rest of the page so I can feed it to the engine. Since this is pretty simple, though, I'm going to do this by hand.

Since the code has no line breaks or anything else useful, I fed it into Eclipse to clean it up and grab the javascript.

Cleaning it up in Eclipse makes the initial part of the script make a lot more sense. Take a look (click to enlarge):


If you know a little Javascript, you can already get an idea of what's going on. We've got a hidden textarea with some text in it. Right now it's meaningless, but this is going to be modified by the Javascript to pull the script together. The applet section makes a reference to a Java applet that would've been housed on the same webserver as this malicious webpage. Since I found this file in my cache, the applet isn't available for me to examine.

Right now it's the content in the script tags that we're going to look at. This is the part of the script that pulls together all the obfuscated components of the script and tells the browser how to execute them to infect itself with whatever piece of badness the author wants to hit me with.

Let's work through this step by step.
var date = new Date();
var f = date.getFullYear()-2009;
First, the script gets the date, pulls the year out, subtracts 2009, and saves it to the variable f. This limits the script to only this year, but the lifetime of an attack like this measures in days at the most so that's not a significant limitation. All this is a complicated way of defining f=2.

Next, we have:
zni = '2011val'.replace(date.getFullYear(),'');
var e = new Function('axlzg','return e'+zni)();
zni is another variable. Here, we take the string '2011val' and then delete the current year, so zni = val

Then, we define a function, e. e produces a string 'axlzg' and also takes the string 'return e' and appends the value of zni. This computes to return eval, which is a Javascript command to evaluate a string as if it was code.

Moving on:
xzjc=document.getElementById('textarea').value;
var content = '';
There's another uninformatively-named variable here, but it's pretty obvious what it does. xzjc grabs the content of the text area, so xzjc = 'tring.from2011har2011ode' The script also defines a variable 'content', which is a blank string. We're getting somewhere now!
var fnxes=e('S'+xzjc.split(date.getFullYear()).join('C'));
This one's a little more complicated. This one's another text-manipulation exercise that will further translate things. Like math, we need to start from inside the parentheses and work outwards.

First, we're taking xzjc from the last line. We put 'S' in front and then split it into separate strings using the current year as the split point, yielding 'String.from' 'har' 'ode'. Then we re-join the fragments using a "separator" of 'C'. Now we have 'String.fromCharCode', which is a Javascript function that takes encoded characters and decodes them to a string. This result is run through the function "e", which takes the string and converts it back to code, so it can execute.

The reason the author is bothering with all this is because String.fromCharCode() is a common function that takes a set of character codes (in this case numbers) and converts them back to letters. For example, "51*f" is 51*2 = 102, which is the Unicode character code for f. Malware authors often use to obfuscate their code (as we'll soon see) so, it's a indicator that antivirus companies will trigger on. In this script, the malware author has to obfuscate their obfuscation method in order to try and evade the antivirus signature. I found this script because it triggered my antivirus, so even all this obfuscation failed.

Let's look at the last couple lines of this script.
content = fnxes(51*f,58.5*f,55*f,49.5*f,58*f,52.5*f,55.5*f,
        55*f,16*f,50.5*f,55*f,50*f,47.5*f,57*f,50.5*f,50*f,52.5*f,
        57*f,50.5*f,49.5*f,58*f,20*f,20.5*f,61.5*f,62.5*f,29.5*f,
        50*f,........ );
     e(content);

There's actually a lot more numbers in there than I'm showing, I'm just cropping it out for simplicity's sake. The script is taking the variable "content" and actually defining it. It's taking each of these numbers and multiplying it by f, which we already learned was 2. Then, it's running fnxes (which is really String.fromCharCode) against it. Now I'm going to turn to Spidermonkey to translate all this crap into real code, it would just be too annoying to do by hand.

So, after we multiply the numbers by 2 and then turn them back into a string, we get the payload. Unfortunately the payload itself is pretty long and complicated, so that'll have to wait for part 2 so I can have time to figure out what's going on.