Deobfuscate malware - English version

Original post: https://develbranch.com/tutorials/deobfuscate-apt-malware.html

Introduction

Malicious files

I get this malware sample from my friend. I will show my experience with packers and manual unpacking. All about behaviors of malware, I will present in a new article.

1 minute for advertisement: If you have a similar sample, you may contact me by sending an email to contact[at]develbranch.com or my fanpage fb.com/develbranch. I will help you and charge in case of necessity. If you think my article is valuable, you may donate to me so that I have the motivation to write the next article. I accept Paypal

Environment

Behavioral analysis

In all steps of malware analysis, we have to perform behavioral analysis to predict malicious actions partly. To do this, we need:

  • The executable files of the malicious software and/or commands to run malicious code.
  • Build exactly the environment where the malicious code will execute. For example: If the malicious code only runs on the windows server then you will never analyze the behavior if your environment is windows home.

We just have 4 files, no more clue, and we can not execute malware in our system. We need to guess by ourselves!

Detailed analysis

I noticed that the cachuri.dll file was signed by Microsoft. I completely ignored this file.

Signature of cachuri

This file is a module of IIS cache. It is not a default installed module:

cachuri

cachuri.dll imports functions from iisutil2.dll, iisutil2.dll does not have a valid signature. Malicious files

The other files are not executable files at all. So we can guess: cachuri.dll will be loaded by the IIS server (inetsrv). After loading the cachuri.dll, malicious iisutil2.dll is also loaded into the memory region of the process. iisutil2.dll will probably use the remaining two files for some purpose. At this step, we still haven’t run malicious code, it’s just a guess.

Analyze iisutil2.dll

Open iisutil2.dll

This file is obfuscated and it makes me be confused. This is the flow of program:

Find functions: CreateFileW, ReadFile, RtlDecompressBuffer

Get procedure address

Open iisexpressshim.sdb and read:

Read file

Decrypt code by using XOR

Decrypt

Decrypt

There is the structure of decrypted data:

  • the first DWORD: The size of uncompressed data
  • Compressed data

Decrypt

After that, the program uses RtlDecompressBuffer to decompress data. This is the first layer of packer.

There is decryption code. Luckily, we have lznt1 decompressor from Google (https://twitter.com/nullandnull/status/772989022079586304). https://github.com/google/rekall/blob/e57446eb8ecbcf5019c1a978f469955a5078c829/rekall-core/rekall/plugins/filesystems/lznt1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'''Decrypt iisexpressshim.sdb'''
import sys
import lznt1

def decrypt(buf):
	out = ''
	index = 0
	for c in buf:
		out += chr(ord(c) ^ (index % 0xff))
		index += 1
	return out

cdata = decrypt(open(sys.argv[1], 'rb').read())
open(sys.argv[1]+'.decrypted', 'wb').write(lznt1.decompress_data(cdata[4:]))

Result

Decrypt

I notice that VirtualSize, RawSize and RawAddress are equal to 0 . We can calculate all VirtualSize, RawSize and RawAddress by using VirtualAddress.

  • VirtualSize = RawSize = (VirtualAddress of the next section - VirtualAddress of this section)
  • RawAddress = VirtualAddress

After fixing it, we can load this file to IDA and check it. This is the main flow of this program: to replace export table of cachuri.dll with new malicious export table.

I try to debug but I don’t have an actor to trigger malicious code.

Analyze iisexpressshim.sdb

I analyze DllGetClassObject and notice that malware uses directory name as a CLSID.

malware CLSID

To create a malicious object, we need a directory with a CLSID. There are main steps:

  • Create a directory and the name of directory is {CB8A1CEF-292D-421E-AC71-88451B5C7F2B}. I choose it randomly.
  • Use this code to create an object.
     CLSID    clsid_malware;
     LPVOID   ppv = NULL;
     HMODULE h = LoadLibraryA("cachuri.dll");
     DLLGETCLASSOBJECT fnDllGetClassObject = (DLLGETCLASSOBJECT)GetProcAddress(h, "DllGetClassObject");
     HRESULT hr = CLSIDFromString(TEXT("{CB8A1CEF-292D-421E-AC71-88451B5C7F2B}"), &clsid_malware);
     hr = fnDllGetClassObject(clsid_malware, IID_IClassFactory, &ppv);
     printf("ppv = %p\n", ppv);
    

    We have a CMalware2 object. I try to analyze all methods of this object and I realize that CMalware2 object is a base object. If I call QueryInterface with {839D7762-5121-4009-9234-4F0D19394F04} , I will create a real malicious object.

malware vtable

This function will decrypt logo.png and load another PE file to memory:

int main(int argc, char** argv) {
	CLSID clsid_malware;
	LPVOID   ppv = NULL;
	HMODULE h = LoadLibraryA("cachuri.dll");
	DLLGETCLASSOBJECT fnDllGetClassObject = (DLLGETCLASSOBJECT)GetProcAddress(h, "DllGetClassObject");
	HRESULT hr = CLSIDFromString(TEXT("{CB8A1CEF-292D-421E-AC71-88451B5C7F2B}"), &clsid_malware);
	hr = fnDllGetClassObject(clsid_malware, IID_IClassFactory, &ppv);
	printf("ppv = %p\n", ppv);
	IUnknown *pMalware = (IUnknown*)ppv;
	hr = CLSIDFromString(TEXT("{839D7762-5121-4009-9234-4F0D19394F04}"), &clsid_malware);

	IMalware *malware_obj;
	hr = pMalware->QueryInterface(clsid_malware, (LPVOID*) &malware_obj);
	printf("main object = %p\n", malware_obj);

	DWORD_PTR vptr = (DWORD_PTR)(*(DWORD_PTR*)malware_obj);
	printf("vtbl = %08x\n", vptr);
	DWORD_PTR ml_fn = *(DWORD_PTR*)(vptr + 3 * sizeof(DWORD_PTR));
	RUNMALWARE run = (RUNMALWARE)(ml_fn - 0x5620 + 0x10B0);
	run((void*)malware_obj); // extract malware
	return 0;
}

malware vtable

  • Malicious code will allocate a memory region via (VirtualAlloc)
  • Decrypt logo.png
  • Fixup relocation
  • Resolve IAT.
  • I use pe_unmapper to dump malicious code to file (https://github.com/hasherezade/pe_recovery_tools/tree/master/pe_unmapper)

Now, we have logo.dll

Analyze logo.dll

This DLL has a exported function: DllEntry.

Pseudocode:

 char __stdcall DllEntry(int a1, int a2, int a3, int a4)
{
  char result; // al

  SetErrorMode(0x8007u);
  result = sub_100569F0((int)sub_100031C0);
  if ( result )
  {
    result = sub_10055E20();
    if ( result )
    {
      while ( 1 )
        Sleep(0xFFFFFFFF);
    }
  }
  return result;
}

After analyzing, I realize that this is another PE loader:

  • Allocate memory and decrypt the embedded PE file.
  • Resolve Import Address Table
  • Execute malicious code at entry point

This loader removes all important fields in PE header. We can not rebuild PE file without header. I use debugger to trace and find the address of entry-point and dump memory to disk. This loader allocates 0x899400 bytes for new PE file

Decrypt PE file

this loader skips 0x4E0000 bytes. I don’t know why.

Decrypt PE file

Decrypt PE file

As I say, all important fields are removed.

Decrypt PE file

Decrypt PE file

Rebuild

We have: Imagebase = 0x800000 + 0x4E0000 = 0xCE0000.

You can consider that this PE file has only 1 section. So we create section header:

NumberOfSections = 1

Decrypt PE file

To resolve and fix IAT, I use Scylla : https://github.com/NtQuery/Scylla

Scylla is a great tool. I can recover all entries in IAT. We need to use Disassemble function to resolve APIs if Scylla can not resolve them.

Fix IAT

Remember:

  • If malicious code uses GetModuleHandle function with NULL parameter (both Ansi and Unicode version), this function MUST return 0xCE0000.

Fix IAT

Correct Import Address Table

Fix IAT

Finally, I rebuild and rebase this file

Fix IAT

Rebase PE file:

Fix IAT

This is the last layer.

Bonus: C&C Server

1
2
3
4
5
6
7
8
cdn.arlialter.com:8888
cdn.arlialter.com:8531
var.alieras.com:8531
fbcn.enantor.com:8531
fbcn.enantor.com:8888
ww1.erabend.com:8888
var.alieras.com:8888
ww1.erabend.com:8531

Donation

If you think my article is valuable, you may donate to me so that I have the motivation to write the next article. I accept Paypal. Thank you so much! https://paypal.me/develbranch