Custom Sample

Analysis of Custom Sample from Zero2Auto course w/ Config Extractor and Yara Rules

This sample has three stages:

  • The first executable drops the decrypted resource section into a suspended process and then resumes said process (main_bin.exe)

  • The new process will inject into svchost.exe and call CreateRemoteThread, which will grab the PNG file holding our next stage

  • The parital PNG file will now contain a exeutable which will call MessageBox

Analysis - Stage 1:

Looking at the resource section we see some data which appears to have pretty high entropy. I am going to assume this will contain encrypted shellcode or another encrypted executable. We also see it only imports Kernel32.dll which is suspicious (libraries need to be loaded during runtime)

Immediately after opening IDA we can the sample passing random strings to the function call decrypt_api_hash. However, this random string is our APIs hashed, during runtime we can see what is being loaded in. In the screenshot, I have labeled the API hashes with their corresponding names (kernel32.dll, FindResourceA, LoadResourceA, SizeOfResourceA, and LockResource)

Looking at the function call to decrypt_api_hash, we can see data being moved into local variables on the stack. This data combined will hold our decryption key. It will then find the index of the character and adds 0D or 13 to our index. This is a simple ROT13 encryption, I created a quick python script that will decrypt the API hashes.

These are all the APIs called within main_bin.exe:

  • kernel32.dll

  • FindResourceA

  • LoadResource

  • SizeofResource

  • LockResource

  • CreateProcessA

  • VirtualAlloc

  • VirtualAllocEx

  • SetThreadContext

  • GetThreadContext

  • ReadProcessMemory

  • WriteProcessMemory

  • ResumeThread

The sample then proceeds to use the following APIs to load a resource (which we believe to be an encrypted payload). After the call to LockResource, the sample will have a pointer to the resource section.

The sample then loads in another API this time VirtualAlloc and then calls VirtualAlloc passing in the size of the resource section. The sample then calls _memmove to copy the resource section into the allocated region of memory. The sample then calls _memset which in this case allocated a region of memory (size 258 bytes) with all zeros.

We then begin a loop routine using the region of memory allocated to zeros (looping 256 times or 100h) starting from "00" and incrementing 1 every loop. It will create a string from the hex values "00-FF"

The above is generated in memory. Looking back through my course material, I find out this to be the beginning of RC4. More specifically I think this is KSA (key scheduling) which creates an array of 256 entries.

Following the loop, we store a pointer to our array of 256 bits into ECX and then store the first 12 bytes of the resource section into EAX, if we take a look at the first 12 bytes we can see a random string that might be used as a RC4 key "kkd5YdPM24VBXmi".

Before:

After:

This key is used to scramble the data and create our PRGA (Pseudo Random Generation Algorithm). Which is just our keystream

Next, we begin yet another loop routine using our keystream data from above, this time used to decrypt the resource using our generated keystream and XORing it. After stepping through the loop we can see can clearly see an MZ header indicating that the resource section did indeed hold an encrypted executable.

After the decryption loop, we call a subroutine (This is where the main execution of the payload begins). To start the sample calls GetModuleFileNameA to retrieve the path to the executable. Following is a comparison to 4550h which is checking for the PE header.

We can see the sample loading in more API using the function call decrypt_api_hash (it loads in kernel32.dll and CreateProcessA). After which it calls CreateProcessA passing in "4" which starts the process in a suspended state and passes the path of the executable (itself) to CreateProcess. We can also see a call to _memset which allocates a region of memory to zero (this region of memory will hold our PROCESS_INFO structure)

Once again we can see it loading in more API that it needs (VirtuallAlloc and GetThreadContext). It then calls VirtualAlloc, which allocates memory in the suspended process, and finally GetThreadContext which passes our allocated memory which is a pointer to the CONTEXT structure used for GetThreadContext.

Next, the sample loads ReadProcessMemory, WriteProcessMemory, and VirtualAllocEx all of which can be used to write our decrypted resource into our suspended process. When the sample calls ReadProcessMemory it passes in the base address of itself. After which VirtualAllocEx is used to allocate more space/commit the allocated region of memory. Then WriteProcessMemory is called and passes in the allocated space and then passes the buffer which points to the decrypted executable however, only a section of the executable is written.

Next begins a loop where it will write the rest of the executable to the allocated region of memory in the suspended process.

After writing the entire executable to the memory region it loads in SetThreadContext and ResumeThread. This will now begin execution from within our process created and begin executing our decrypted executable contained within the resource section. This will conclude the analysis of the first stage.

Stage 1:

  • It will load the resource into memory using FindResource, LoadResource, and LockResource

  • It will then begin 3 consecutive loops which are used for the initialization of RC4 (KSA and PRGA) decryption of the resource section

  • After decrypting the executable it creates a process in a suspended state and writes the decrypted executable into the suspended process and then resume execution of said process

Analysis - Stage 2:

There isn't much useful information all the import functions are loaded at runtime and the strings don't tell us anything interesting.

Opening the 2nd stage in IDA and starting at "main" we see a call to GetModuleFileNameA and two calls to _strtok (used to break a string into tokens using a delimiter). It retrieves the file path to itself and passes the return value to _strtok and then passes the value "\\" We can determine that the delimiter is going to be a backslash "\". The loop routine checks each token created until it reaches the last value "\" (which will be the file name). Once it gets the file name it loops through each character

ECX holds the number of characters in our file name which is passed to the function xor_loop. Looking at the section of code we only see shifts and xor's instructions with a constant "0EDB88320h". Looking this constant up on google, I found it to be used in CRC32 checksums. So this code basically creates a checksum table and stores it into crc32_checksum_table.

After storing our checksum table into a dword. We then follow another loop routine which loops through the string and creates a hash of the string using the checksum table. This entire function call is pretty much all CRC32 checksum besides generating our checksum table.

Following the loop, we are returned with a hashed value which is then compared to the value "B925C42D" which we can assume is a CRC32 hash value. We can assume this is checking the file name of the sample. As I know it injected into svchost.exe, I assumed this hash value was svchost.exe and to my surprise it was:

First, we are going to follow the path if the hashes do not match. So returning to "main" we take our hashed value and compare it to the hard-coded value "0B925C42Dh" (which is looking for a specific filename). If the hashes match we will call "call_load_api". Otherwise, we don't jump and we'll call load_api.

Looking at this function we see LoadLibrary being called with the parameter "kernel32.dll". We then start a loop routine that iterates over the functions exported within kernel32.dll. We then push the function name to the function call crc32_hashing. These functions will continue to loop through different function names within kernel32.dll until the hashes match (which in this case was IsDebuggerPresent ) we then return to "main" and call IsDebuggerPresent.

Returning to main we have yet another function call this time to process_looping. This function loads in more APIs (CreateToolhelp32Snapshot, Process32First, and Process32Next).

It will then convert the process name to lower case and then into the CRC32 hashing algorithm which is then used to compare against 4 different hashed values. (one of which is process hacker.exe - "7C6FFE70", x32dbg - "D2F05B7D", x64dbg.exe - "659B537E", wireshark.exe - "47742A22"). This function appears to be looking for analysis tools and if found will exit.

After the anti-analysis check, the sample loads in a few more APIs needed (CreateProcessA, WriteProcessMemory, ResumeThread, VirtualAllocEx, VirtualAlloc, CreateRemoteThread).

Once the sample has loaded in the APIs we return to "main". We then call process_create which as you can guess will be used to create a process. Within this function using we generate our file path using a rotate left and XORing the byte with the value A2 (C:\Windows\System32\svchost.exe). Once it generates the string it will call CreateProcess using the string generated while also passing the parameter 4 (which starts the process in a suspended state)

From here we get the path to the file name using GetModuleFileName. We then call VirtualAlloc and _memmove to copy itself into the newly allocated region of memory. Finally, we call VirtualAllocEx to change the protection rights

We then call WriteProcessMemory to write itself into the suspended process memory space and then call CreateRemoteThread passing in the call (call_load_api) as one of the parameters. This is the end of the main and we will begin analysis with call_load_api.

So moving back and analyzing call_load_api. We can see it passing in more hashed APIs values. We can determine the APIs are InternetOpenA, InternetOpenUrlA, InternetReadFile, and InternetCloseHandle. After it loads in the APIs. It stores a value into a local variable.

"DA 1B 1B 5B 6B FF AE AE 5B 4A 6B 1B 0A 7A CA BA BE 6A AA 8A AE 7B 4A 2B AE 8A 98 0A 8A CF 18 28"

The loop proceeding the string will extract one byte at a time, rotate left by 4 (swapping hex character "DA" -> "AD"), and xor with 0xC5

This will generate a string "https://pastebin.com/raw/mLem9DGk"

We then pass the decrypted string into a function "open_internet". We make a few calls to OpenInternetA, OpenInternetUrlA, HttpQueryInfoA to open a request to our URL, and then call HTTPQueryInfoA and pass in the flag "5" which is used to return the content length. Once it makes a connection it will call InternetReadFile and write the buffer to the specified location. After stepping over InternetReadFile we can see:

Once returning back to call_load_api we call another function - which calls open_internet again with our new URL, reading a file and writing it to the allocated memory. We believie this to be our new file which looks like a PNG file. Returning from open_internet we store a random data string and get the length of the string. We then begin to decrypt this data using yet another rotate left and xor the byte with 1F. The result of the data is:

We then convert the string to wide characters and load in more APIs needed (GetTempPathW, CreateDirectoryW, CreateFileW, and WriteFile). It will then call GetTempPath and append cruloader to the directory path. We then call CreateDirectoryW and pass in our temp path. We then call CreateFileW with the name output.jpg. We then call WriteFile (writing what appears to be an image). We can verify this:

We then move data into local a variable and call string length. We again begin a loop sequence using rotate left and xor the byte with the value 9A. The result is:

The above string is used to locate where the executable is hidden within the PNG file. We then begin another xor loop which will decrypt a section of our image file. The section it decrypts contains an executable. We can go ahead and save it to a file and come back and review it later.

We call create_process which we have analyzed before and all it does is create a process named svchost.exe in a suspended state.

We then call load_api which we have also already analyzed and we know it loads in (WriteProcessMemory, CreateProcess, ResumeThread, VirtualAlloc, VirtualAllocEx, CreateRemoteThread. Which will be used to inject into the process we created (svchost.exe)

Analysis - Stage 3:

The final payload appears to create a message box saying:

Checking the file in Detect It Easy we can see our string displayed in the MessageBoxA. Checking the file in IDA there is no obfuscation of APIs and only calls MessageBoxA.

I created a partial config extractor, check it out on my GitHub

That will conclude my analysis of the custom sample from Zero2Auto. By Vitali Kremez and 0verfl0w. If you are interested in learning more about malware analysis I highly encourage you to check out their amazing course!

Last updated