Analyzing Malware by Example---Sample 4Download Sample:sample.zipThe password is "infected"
Caution! This is live malware!GoalIn this tutorial you will learn how to analyse and unpack an obfuscated .NET sample.
First OverviewAs I told you we will be using a different PE tool this time. Check out PEStudio from winitor.com.
You will need a Windows system to run it, so open up your analysis VM and open the sample in PEStudio.
PEStudio presents the PE file information, and additionally indicators for the trustfulness of a file. Check out the indicators. Among others they will tell you right away that this file is a .NET file:
Also check out the description of the file and the file name. This sample claims to be Adobe Bridge CS6. If you find a file that is seemingly from a known company, you should check if it is signed.
Download the Systeminternals Suite from Microsoft:
https://technet.microsoft.com/en-us/sysinternals/bb842062.aspxYou will need the programs in there quite often. For our file please use sigcheck.exe. It is a command line program that will tell you if a file is properly signed.
Use the command:
sigcheck.exe -a <sample>
The -a switch will show you full information. The very first line will tell you that our sample is Unsigned. That means our file is likely a trojan horse (or short trojan). A trojan hourse tricks the user into executing it by giving the impression that it does something useful.
Note: Another thing you can compare for a file of a seemingly trustful vendor is the hash. You will see that sigcheck tells you the version of the file. Go to the official website of the vendor and download the file of the same version. Compute the hashes of both, the sample and the official product, and compare them.Ok, let's go back to PEStudio. It also tells you that the file is not signed, but don't trust it when it comes to certificates. Often you will find that PEStudio shows a certificate, although it is not a valid one. Use sigcheck.
If the file was uploaded to Virustotal you will find results about that in PEStudio. At the time I wrote this tutorial the file was not uploaded to VT.
The following categories in PEStudio are the same as in PortexAnalyzer. They show the contents of various PE headers and special section contents. Not surprisingly you will find an mscoree.dll import (reminder: This is a sign that this is a .NET file).
The resources will list several icons. An Icon group resource represents a collection of several icons that have the same image, but different sizes. Use Resource Hacker to see the icons or PortexAnalyzer with the -i switch to extract them. You will find the Adobe Bridge icons here.
The Strings tab in PEStudio lists all strings it found in the file. This can be very useful to find some pieces of information. E.g. you might find the name of a file or a file path. In such a case it is useful to search for the same string in an hex editor and see what is in the same area. PEStudio shows the strings sorted by the Blacklisted column. I find it useful to press the "Size" column in PEStudio twice, so you get the largest strings first. In our case the largest string has 145 characters. One interesting find is the
fcxtjrf.exe string.
Sysinternals has a tool for this too, called strings.exe. It a bit is more flexible than PEStudio. Try it on our sample:
strings.exe -n 10 <sample> | MORE
The -n switch sets the minimum length of a string. Very small strings are often just random. The default string length is 3, which is relatively low.
With the pipe | and MORE you can navigate the output. Press space to scroll down one page. Alternatively you can redirect the output into a file and look at it in a text editor.
strings.exe will show the found strings in the order it finds it in the file. If you scroll down a bit you will find a large string that was not shown by PEStudio (maybe there is a max size restriction). The sysinternal tools are often more relyable, but PEStudio gives a great first overview. That looks pretty much like a base64 string. However, decoding it with base64 will not yield anything useful for now.
Code AnalysisSince we know this is a .NET file let's decompile it with ILSpy.
You will notice that the file has lots of method names with weird characters. This is an obfuscation technique. If you happen to use .NET Reflector on this file, you will run into issues because of that.
Download de4dot here:
http://de4dot.com/In the command line run:
de4dot.exe <sample>
It will tell you that an unknown obfuscator was applied and create a cleaned version of the sample.
In the cleaned version the weird method names have been renamed to method0, method1 etc.
If you find an obfuscated .NET file, de4dot is always worth a try.
Now load the cleaned file into ILSpy.
Take a look at the assembly overview.
Remember the fcxtjrf.exe string? This fcxtjrf is the assembly title. The weirdness of this word is probably the result of an obfuscator that applied random names to everything.
Note: The word assembly in the context of .NET means a collection of CIL and resources in a PE file. This has nothing to do with the language family assembly, which is a human readable representation of machine code.A very important info in this overview is the location of the Main method, which is the entry point for .NET code. ILSpy notes it via a
comment. For our sample it is:
// Entry point: fcxtjrf.Program.Main
If you click on it you will be located to the Main method:
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
The Main class is usually the very first thing to look at. Often you will find the relevant information right there.
The only interesting thing here is the new Form1(). Click on it and then click on InitializeComponent();
private void InitializeComponent()
{
base.SuspendLayout();
base.AutoScaleDimensions = new SizeF(6f, 13f);
base.AutoScaleMode = AutoScaleMode.Font;
base.ClientSize = new Size(292, 273);
base.Name = "Form1";
base.Opacity = 0.0;
base.ShowIcon = false;
base.ShowInTaskbar = false;
this.Text = "Form1";
base.Load += new EventHandler(this.Form1_Load);
base.ResumeLayout(false);
}
Again, most of the code does nothing interesting for us. There is no logic, just initialization of some variables and some settings. The EventHandler however is interesting. Hover with your mouse over base.Load. An information will pop up and tell you that the Load event is invoked before the form is shown the first time. The new EventHandler receives a method named Form1_Load. This method is executed on a Load event. So click on Form1_Load.
Note: If you encounter a class or function that you don't know look it up in the MSDN documentation. If you are not familiar with C# (like me), you will do that very often in the beginning. The more you analyze .NET files the more you will learn about these functions and won't have to look them up anymore.private void Form1_Load(object sender, EventArgs e)
{
Class2.Delegate0 delegate0_ = Class2.delegate0_0;
delegate0_();
}
These lines are both interesting. We are on the right track. At first look up what a delegate is. You will find usage of that very often in obfuscated files. The delegate0_ is a method, any method, we don't know which one yet. But it is initialized with Class2.delegate0_0, so click on that member.
internal static Class2.Delegate0 delegate0_0 = (Class2.Delegate0)Delegate.CreateDelegate(typeof(Class2.Delegate0), Class1.smethod_1(), Class2.smethod_0());
Now it is time to get a paper and a pencil to write important things. Otherwise we will loose track of things.
The delegate0_0 member is initialized using the CreateDelegate method with three parameters. First, look up the method to find out the meaning of the parameters. See here:
https://msdn.microsoft.com/de-de/library/system.delegate.createdelegate%28v=vs.110%29.aspxYou will find several overloaded CreateDelegate methods with three parameters.
We need to know the type of the passed arguments to know which method is used. Hover over the arguments and you will realize this is:
CreateDelegate(Type, Type, string);
Make a note of this on your paper, so you don't look this up several times. We are on the right track!
Also note the location of this line within the file (Class2.delegate0_0).
The documentation tells us the following:
Parameters
type
Type: System.Type
The Type of delegate to create.
target
Type: System.Type
The Type representing the class that implements method.
method
Type: System.String
The name of the static method that the delegate is to represent.
Return Value
Type: System.Delegate
A delegate of the specified type that represents the specified static method of the specified class.
Remember that delegate0_ stands for a method that is executed and this method is created here. The name of this method is the third parameter. Let's investigate this parameter. Click on smethod_0().
The following code contains some weird characters, so I will provide a screenshot.
Now it is time for us to execute some code.
You have two possibilities. If you happen to have a license for Visual Studio, use that. Otherwise, get Mono Develop for Linux. Open a new project, copy and paste the code of smethod_0() in there.
In your Main() method you write an output for the returned value:
public static void Main (string[] args)
{
Console.WriteLine (class2smethod_0());
}
Run the code and you get an output.
Note: Never run any code on an unsafe system if you are not 100% sure that it is harmless. In this case we have a harmless piece of code in method0() of Class2, because there are just some character conversions to decode a string.
The return value of smethod_0 in Class2 is
"locati". Take a note of this.
"locati" is the name of a method that is called in Form1_Load in the line:
delegate0_();
Press the back arrow to get back to delegate0_0 and click on the second parameter, which is smethod_1() in Class1. This parameter returns a Type and this Type is constructed by another delegate.
internal static Type smethod_1()
{
return ((Settings.Delegate2)Delegate.CreateDelegate(typeof(Settings.Delegate2), Class1.MethodInfo_0))(Class5.smethod_0()).GetType(Class1.smethod_0());
}
This might look very confusing, but it is actually equivalent to:
internal static Type smethod_1()
{
Type someType = typeof(Settings.Delegate2);
MethodInfo someInfo = Class1.MethodInfo_0;
Delegate delegate = (Settings.Delegate2)Delegate.CreateDelegate(someType, someInfo);
argument = Class5.smethod_0(); //get the value for argument
value = delegate(argument); //call delegate and pass argument
return value.GetType(Class1.smethod_0()); // get the Type from value
}
It should be more clear now what is actually happening here.
Write this down. This time we create a delegate via
CreateDelegate(Type, MethodInfo).
Click on MethodInfo_0.
internal static MethodInfo MethodInfo_0
{
get
{
Type typeFromHandle = typeof(Assembly);
string name = Class1.smethod_2();
Type[] array = new Type[1];
Type[] array2 = array;
array2[0] = typeof(byte[]);
Type[] types = array2;
return typeFromHandle.GetMethod(name, types);
}
}
Look at the last line. These are the relevant variables for us.
typeFromHandle is Assembly.
The name is "Load", which you will discover by executing Class1.smethod_2()
The array types will contain the Type byte[].
Thus, the constructed method will be:
Assembly.Load(byte[])
Then go back to Class1.smethod_1(). Make notes, in our case we replace the delegate call with Assembly.Load and remove the delegate construction.
internal static Type smethod_1()
{
byte[] argument = Class5.smethod_0(); //get the byte array
value = Assembly.Load(argument); //call Assembly load and pass byte array
return value.GetType(Class1.smethod_0()); // get the Type from delegate2
}
Because we know the type of delegate, we also know the type of our argument and the returned value.
Check Assembly.Load(byte[]) in the documentation.
Our piece of code constructs a .NET assembly by passing a byte array and returns a Type that is defined in that assembly. If you go back to your notes, you will also see that the method
locati() is called on that Type/Class.
That means we have a packed assembly somewhere in this file (remember the large base64 string?). Our sample unpacks it, loads it in memory, and executes a certain method on it.
Check out which method this is by clicking on smethod_0() of Class1.
Again there is a string decoding method that you can just copy & paste in to Visual Studio or Mono Develop to show the returned string.
The string we look for is
"S.huis", which is the name of a class.
UnpackingFor further investigation of the sample we have to unpack the code. There is a tool called .NET Generic Unpacker which will work just fine for our sample:
http://www.ntcore.com/netunpack.phpThis unpacker is dynamic, thus needs a safe environment like a VM, because it executes the code of the sample.
Using a readily available static or dynamic unpacker is usually the fastest and easiest way. However, such tools will not always work (I actually have a sample of the same malware family that cannot be unpacked with .NET Generic Unpacker) and sometimes you won't find any available tools.
For these cases you will want to unpack the code manually. If you take a look at smethod_1() in Class1 you will see that the construction of the byte array is in smethod_0() of Class5. That's your point to start digging into the unpacking part of the sample.
internal static byte[] smethod_0()
{
Random random = new Random(BitConverter.ToInt32(Class4.data(), 0));
byte[] array = new byte[Class4.data().Length - 4];
for (int i = 0; i <= array.Length - 1; i++)
{
array[i] = (Class4.data()[i + 4] ^ Convert.ToByte(random.Next(256) + (int)Class3.byte_0[i % Class3.byte_0.Length] & 255));
}
return array;
}
You will notice that a random number generator is used to get a byte value. This value is XORed with the data and the result shifted by some value.
The call to random.Next(256) will not yield a random number, because the Random generator is seeded with a fixed value. If you apply the same seed, you will always get the same numbers.
Class4.data() is our large base64 string, which is converted to a byte array. This is the packed assembly. The first four bytes of this byte array are also used as seed for the random generator.
If you have Visual Studio I suggest you try to write the unpacker for our sample.
If you don't you will not be able to do this, because the implementation of Random is a different one on Mono .NET. Use the dynamic unpacker in this case to proceed.
For comparison or in case you get stuck, here is my unpacking code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
public static void Main(string[] args)
{
byte[] decoded = class5smethod_0_decode();
File.WriteAllBytes ("dump.out", decoded);
Console.WriteLine("done");
}
internal static byte[] byte_0 = Encoding.UTF8.GetBytes("buchet");
internal static byte[] class5smethod_0_decode()
{
Random random = new Random(BitConverter.ToInt32(data(), 0));
byte[] array = new byte[data().Length - 4];
for (int i = 0; i <= array.Length - 1; i++)
{
array[i] = (byte)(data()[i + 4] ^ Convert.ToByte(random.Next(256) + (int)byte_0[i % byte_0.Length] & 255));
}
return array;
}
internal static byte[] data()
{
return Convert.FromBase64String( //put the large base64 string here );
}
}
}
Open dump.out in a hex editor. If you see the typical MS DOS Stub message and the MZ signature, you successfully unpacked the sample.
I want you to analyse the dump. You already know and wrote down that the entry point for this assembly is
S.huis.locati. So you will start looking there.
The dumped sample is not obfuscated at all and you should not have a hard time to analyse the code.
Try to answer the following questions:
What kind of malware is this (e.g. Downloader, Dropper, Injector, Ransom, Stealer ...; Note that a sample can have several of these types at once)?
The sample has AntiVM/AntiSandbox features. How do they work and how can you circumvent them?
What files must be removed and what processes must be killed to get rid of the malware (your list might not be exhaustive, but find at least a few)?