In Depth Malware Analysis

De-obfuscating Malicious JavaScript

  • First beautify the js

js-beautify loveyou.js > loveyou-beautify.js
  • JavaScript can de-obfuscate its portions during runtime, then supply the resulting scripts to function eval to execute them

  • Browser Specific JS can also use statements such as

document.write
document.body.appendChild
document.parentNode.insertBefore
  • Always search for eval first

  • Execute the script so it de-obfuscates itself as you observe it

  • Use standalone script interpreters SpiderMonkey on REMnux and CScript on Windows

  • If you are decoding a browser script, you'll need to remove non-js (HTML) components

  • You'll need to intercept functions like eval and define the missing objects that would have been supplied by the OS or the app

js -f loveyou.js #get error ActiveXObjects is not definied
  • REMnux includes some definitions in /usr/share/remnux/objects.js

  • Specify it as the first -f parameter in SpiderMonkey

  • Use this as a starting point

  • Modify it based on the needs of the specimen

  • Tweak a copy of the file so you don't break the original

  • Supply objects.js as the first parameter to de-obfuscate loveyou.js

  • js -f /usr/share/remnux/objects.js -f loveyou.js > loveyou-out.js

  • more loveyou-out.js

Alternative

  • Run the script on Windows using CScript to monitor its activities via AMSI

powershell 
logman start AMSITrace -p Microsoft-AntiMalware-Scan-Interface Event1 -o AMSITrace.etl -ets 
script loveyou.js
logman stop AMSITrace -ets 
AMSIScriptContentRetrieval > loveyou-out.txt 

Alternative 2

  • Add the new definition to the type of loveyou.js

  • Use WScript.Echo in CScript instead of print in SpiderMonkey

  • If you just want to see the de-obfuscated script without executing it, delete the lines that contain origional_eval

origional_eval = eval;
eval = function(input_string) {
  WScript.Echo(input_string);
  origional_eval(input_string)
  }
  • Now add this code to the top of the malicious script using Notepad++ then save

Another Example

  • Use SpiderMonkey to de-obfuscate fgg.js

unzip fgg.zip
js -f fgg.js #error
code fgg.js
js -f /usr/share/remnux/objects.js -f fgg.js
  • Take not the error is location is not definied

  • When you open the script you see location.href

  • Could that be the de-obfuscation key?

  • When saving suspicious web pages capture the URLs and headers in case de-obsfucation needs them

  • in fgg.js the URL used as a call out was `http://gitporg.com/cgi-bin/index.cgi?fgg

  • To de-obfuscate fgg.js copy objects.js and then adjust location.href to specify the expected value

  • Adjust This:

location = {
  href:"http://www.example.com/page"
 }
 TO:
 location = {
  href:"http://gitporg.com/cgi-bin/index.cgi?fgg"
 }
  • Supply the edited file when executing fgg.js in SpiderMonkey then examine the output

  • The de-obfuscated script reveals a URL that we couldn't see in the original script

Additional Considerations for de-obfuscating JS

  • Be carful modifying brower scripts if they call arguments.callee because this allows the function to examine its own body

  • Example: var M1FDAB=arguments.callee.toString()

  • You can use the debuggers built into web browsers to de-obfuscate browser scripts as they run

  • For JS designed to run outside the browser try box-js

  • If you encounter scripts for PDF files extract them then de-obfuscate them with SpiderMonkey

Recognizing Packed Malware

  • Malware authors can protect their creations by packing them

  • Packers are tools that compress, obfuscate, encrypt, or otherwise encode the original code

  • the packed program decodes the code into memory when it runs

  • This safeguards the specimen form static analysis techniques

  • Packed programs are also difficult to disassemble and or debug

  • Not all malware is packed

  • The unpacking code extracts the original program to memory and runs the program from memory

                    ---Pack-->                           ---Run-->
Origional program                Unpacking code                      Unpacked program 
stored on its                    --------------                      stored in memory 
authors file system              Packed program stored               of the infected 
                                 as data                             filesystem
  • Packers conceal the original code and can hide some PE header values to further complicate analysis

  • Sections: Regions of code or data the comprise the executable file which will be loaded into memory

  • The entry point: Address of the first instruction in the program (officially called AddressOfEntryPoint)

  • Import Address Table IAT: Pointers to function in external DLLs (That is API calls)

  • Packers differ in techniques, sophistication, and the protection they provide

Types of Packers

  • We will start with UPX which is free and common, simple to unpack

  • It has built in unpacking capabilities

  • However it can be scrambled to complicate unpacking

  • Numerous other packers exist, they tend to be more complex than UPX

  • Other packers:

Armadillio, FSG, Themida
Some use custom private packers
  • Most packers do not include built in unpacking capabilities

Basic Unpacking

  • If possible try to identify the packer, research it and look for unpacking tips

  • You can tell that the executable might be packed if:

The file contains very few readable strings
Byte values in the file or some of its sections are too random (high entropy)
The file has few imports or recognizable funcation 
The embedded strings sometimes reveal the name of the packer
  • Static property tools can assist us to include:

PeStudio, bytehist, Detect it Easy and Exeinfo PE
  • Bytehist generates byte usage histograms to spot packed files

  • Not packed: Less uniform distribution of byte values

  • Packed: More uniform distribution of byte values

  • Detect It Easy and Exeinfo PE try to identify packer names

diec brbbot.exe
  • Determining whether the specimen is packed and identifying the packers are among the first steps in examining malware

Getting Started with Unpacking

  • UPX can usually auto unpack the sample

upx -d brbbot.exe
  • We can try other tools if UPX fails such as Ether and UnpacMe

  • The unpacking scripts for x64db can be helpful sometimes

Disable ASLR

  • Before trying to unpack the specimen disable ASLR

  • The PE header includes the ImageBase field. This is the virtual address where the executable wants to be loaded into memory

  • With ASLR Windows ignores ImageBase, randomizing the base address to make it harder to exploit the process

  • This complicates unpacking and other code analysis techniques

  • Designate the packed executable incompatible with ASLR

  • You can disable ASLR on a per file basis

  • Find the DllCharacteristics field in the PE header within CFF Explorer

  • Disable the DynamicBase Flag in it (DLL Can Move)

  • Save off the modification to the file to create a new file

  • An alternative is to use the command setdllcharacteristics -d

Viewing the unpacked strings

  • Execute the packed specimen allowing it to unpack itself into memory so that you can extract its unprotected strings

  • Look at the strings via Process Hacker via Properties --> Memory --> Strings --> Ok

  • Alternative: Dump the unpacked process from memory

  • Several tools can extract the unpacked program from memory and save it to the file system

  • We need to tweak the PE header of the resulting file so that we can analyze it more easily

  • `Rebuild in the Import Address Table (IAT)

  • Be mindful of the Original Entry Point (OEP)

  • We will use Scylla for this, other relevant tools include PE Tools and Imports Fixer

  • Attach to the process using Scylla then dump the process

  • Use the X64 version of Scylla since we are extracting a 64 bit program

  • Select brbbot.exe process then click dump and save to a file

  • Find the IAT in the packed process and add it to the dumped file

Click IAT Autosearch, Get Imports, Fix Dump and point to brbbot_dump.exe to generate brbbot_dump_SCY.exe
  • We could perform static analysis on the dumped file, however it will not run, probably because of the entry point field

  • The unpacked code is probably in NPX0 which is no longer empty

  • The other sections that probably contain the unpacking code and the packed program are still present

  • We did not adjust the entrypoint

  • Dumping the process offers a fast way of unpacking the sample

  • Allow the malicious program to unpack itself then dump it

  • This approach is fast and doesn't require a lot of precision

  • The resulting file contains decoded strings and code and is well suited for static analysis

  • However in many cases the file wont be runnable due to the incorrect entry point value and other PE header issues

Using Debuggers for Dumping

  • Using a debugger for a more controlled approach to unpacking

  • Load the packed sample into a debugger (x64db)

  • Locate the end of the un-packer and set a breakpoint there

  • Note: This process can be time consuming and challenging with some packers

  • Run the specimen to let it unpack the original program into memory and pause at the end of the unpacker

  • Single step to let the process jump to the unpacked code (OEP)

  • Note: Most times it will be a jmp right before a lot of add byte ptr ds:[ax/rax], al instructions

  • Dump the unpacked process at that time (OllyDumpEx)

  • Fix up the PE header paying attention to the IAT and EP

  • Set a breakpoint (F2) at the end of the unpacker then run the specimen (F9) to reach it

  • Once paused at the breakpoint single step (F7 or F8) to jump to the beginning of the unpacked code and pause again

  • Note: F7 and F8 are the same unless the instruction is a call in which case you can execute this function in one step (F8, step over) or single step through each of its instructions (F7, step into)

  • Confirm that you're looking at unpacked code by searching this region for the referenced strings and intermodular calls (right click, search for, current region, string references)

  • Use OllyDumpEx to dump the process after jumping to the OEP

  • Press Get RIP as OEP to set the entry point of the new file so it matches the address of the instruction where you're paused

  • Double click section UPX1 and add MEM_WRITE to its flags to preempt an access violation when running the dumped file

  • Activate x64dbg's Scylla plugin to fix the IAT in the dumped file

  • Overview

  • Dumping the process from within a debugger offers a powerful way of bypassing packers in a controlled manner

  • Exit the Scylla plugin and x64db. Run the dumped and fixed file to confirm that it works

Debugging Packed Malware

  • We can debug malware without dumping its unpacked code

  • Sometimes dumping unpacked malware from memory can be too time consuming or impractical

  • Even if you dump the process you might not be able to get the file to run due to OEP, IAT, or other problems

  • It is useful to know how to use a debugger to navigate through the process that began its life as a packed program without dumping it

  • Run the packed specimen in the debugger allowing it to unpack itself without worrying about finding the end of the unpacker

  • Without dumping we need to locate the unpacked code in x64db

  • We could preemptively set breakpoints on API calls that we know unpacked code will make, for example: SetBPX ReadFile

  • From the memory map we can find the section where the executable code for the sample is mostly likely located, click on that segment, and follow in disassembler

  • Note this is only possible if you run the sample prior and it doesn't auto quit

  • You can now examine code that was unpacked into NPX0 for instance by extracting intermodular API calls

  • You can search for the resulting references for interesting API calls and then examine their context in the CPU's disassembler tab

  • Once you search for intermodular calls right click on the interesting ones and select Follow in Disassembler

  • Once you are back in the disassembler and can see the call you want set a hardware breakpoint on the call so you can spy on the results

  • Restart the program and then run it to hit the BP and confirm that you decoded the file

Last updated