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

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

  • Now add this code to the top of the malicious script using Notepad++ then save

Another Example

  • Use SpiderMonkey to de-obfuscate 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:

  • 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

  • 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:

  • 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:

  • Static property tools can assist us to include:

  • 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

  • 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

  • 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

  • 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