Shade is a malware that belongs to the ransomware type. A ransomware is a malicious program that once run, will modify all important data in the computer. The important data may be of any type depending on the particular system it attacks and may include photos, media files, documents and archives. It modifies all the important data that resides in a hard disk, USB disk and/or in the share drives / folders. The modification is performed using Cryptographic algorithms. So, these type of ransomwares are also called CryptoRansomware.
At our Paramount threat research lab (Lakshya Labs), we decided to analyze a sample of Shade Ransomware, as we were getting lot of customer request on removing this infection.
The first step we did was to ensure that the samples that we acquired was not corrupted. We loaded the sample in VirusTotal website to ensure that sample was intact.
Figure 1 - The matching hash values between the sample and the uploaded sample
Figure 2 - All the antivirus vendors detecting this as a ransomware type
We could also find that the all the major anti-virus vendors were detecting this as a ransomware. Once we were sure that the sample was intact, our next step was to run it inside the virtual machine and observe.
As most of the malwares doesn’t run, if it detects the presence of a Virtual machine, we decided to test go with the following scenario. We loaded our sample zoo machine with this ransomware and some sample files along with it.
The below screenshot shows the machine before running the malware. We created three sample files, one image file, one text file and one PDF document.
Figure 3 - The initial shot of the zoo machine
We observed the machine after running the malware. After few minutes of running, we could see that the desktop wallpaper was changed and also the sample files were modified. The screen shot of the same is below.
The three sample files that we kept before the infection were modified and thus rendered useless. We could see that windows was not able to open these files. Screen shots shown below
Figure 4 - Windows could not open the files
We loaded the sample into a debugger tool (in this case the Immunity Debugger) to understand the functionality.
But before loading the sample, we wanted to make sure that the file was not packed. We used e tool called PEiD to find whether the PE header was different.
Figure 5 - PEiD shows the presence of Visual Basic runtime
PEiD found the absence of any packer, however, we found the presence of a Visual Basic run time. Now it was time to load this in the debugger and analyze. After the sample was loaded into the debugger, we landed up in the EntryPoint, as shown below.
Then came, the interesting part. After many trial and error attempts and after setting breakpoints at various places, we found that this ransomware creates a new process and that this new process was responsible for encrypting the data. So we decided to debug this new process deeper to understand on how this ransomware worked. Here was where it turned tricky. Debugging this new process was very difficult and the authors of this code has used a technique called process hallowing that made the process of debugging the target a complex process.
We decided to see how the new process was created initially, before analyzing it.
Figure 6 - Creation of a new sub process.
We found that ‘CreateProcess()’ function was used to create a new process. We tried to put a breakpoint at this function to see how it worked.
Figure 7 - Setting breakpoint at CreateProcess
Voila, the process broke clearly at the ‘CreateProcess()’ function (API). We could see that the arguments that were being passed into the function. The following was the function prototype from the MSDN page.
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
The first and the sixth arguments were very interesting. The first argument ‘lpApplicationName’ here held the value ‘C:\Lab\ZOO\shade.exe’ and the sixth argument ‘dwCreationFlags’ held the value ‘CREATE_SUSPENDED’. After setting the breakpoint just after the successful completion of this function, we could see that a new process was created in suspended state.
Figure 8 - A new shade process is created in suspended state
As mentioned earlier, attaching to this new process did not yield anything interesting, as this malware was using a technique called process hallowing. For example, when we tried to attach to this newly created process, we were thrown with a debugger error.
Figure 9 - Debugger error while attaching to the new process
The debugger was unable to attach to this newly created process as it was unable to understand the PE (Portable Executable) executable file format. We decided to locate the PE header. After seeing the first area within the ‘0x00400000’ memory region, we could locate the MS DOS and PE headers.
Figure 10 - The MS DOS and PE headers
After going further down within the memory area, we were able to locate the PE header and the address of the entry point as well.
Figure 11 - Presence of PE header and information about the entry point
So finally, we were able to locate the ‘AddressOfEntryPoint’ for the newly created process. Basically, the ‘AddressOfEntryPoint’ held the address of the first instruction that gets executed when the process starts running. So, the obvious next step was to set a breakpoint at that address and catch the execution. Unfortunately, it did not work. When a process was put in suspended state, it is not possible to debug it, because the debugger won’t be able to see the process at all.
Figure 12 - The debugger is not able to locate the newly created process
We decided to follow a different approach. We decided to find out the API that was sending the signal to the suspended process to resume. We decided to put a break point on ‘LdrGetProcedureAddress’ function and intercept the API calls. After analyzing many calls, we understood that just before the newly created process gets executed, the flowing API flows were very important for analysis:
‘NtAllocateVirtualMemory()’ -> ‘CreateProcessW()’ -> ‘NtUnmapViewOfSection()’ -> ‘NtWriteVirtualMemory()’ -> ‘NtGetContextThread()’ -> ‘NtSetContextThread()’ -> ‘NtResumeThread()’ -> ‘GetExitCodeProcess()’
The following screenshots represent this analysis.
Figure 13 - 'NtAllocateVirtualMemory()' getting called.
The ‘NtAllocateVirtualMemory()’ function created a new virtual space in the user-mode address region. After this was over, ‘CreateProcessW()’ was called.
Figure 14 - ‘CreateProcessW()’ getting called.
The ‘CreateProcessW()’ function was responsible for creating a new process and a primary thread.
Once a new process was created, the ‘NtUnmapViewOfSection()’ function was called.
Figure 15 - ‘NtUnmapViewOfSection()’ getting called.
The ‘NtUnmapViewOfSection()’ function was used to unmap the view of a section from the virtual address space. After the unmap happened, next the ‘NtWriteVirtualMemory()’ function was called.
Figure 16 - ‘NtWriteVirtualMemory()’ getting called.
The ‘NtWriteVirtualMemory()’ function was responsible for copying data into the memory region. After the data was written, the ‘NtGetContextThread()’ and ‘NtSetContextThread()’ functions were called.
Figure 17 - ‘NtGetContextThread()’ getting called.
Figure 18 - ‘NtSetContextThread()’ getting called.
The ‘NtSetContextThread()’ function was responsible for setting the context to the thread. Finally, the ‘NtResumeThread()’ function was called.
Figure 19 - 'NtResumeThread()' getting called.
The ‘NtResumeThread()’ function was responsible for putting the SUSPENDED process into the execution mode. This made the primary thread in the suspended process to resume, thus triggering the execution of the newly created process.
Figure 20 -'GetExitProcess()' getting called.
Finally, ‘GetExitProcess()’ function was called. As, the newly created process started the execution, the main process was put to exit.
Figure 21 - Finally the process ends.
Overall, we understood that the job of the first program is to perform process hallowing and execute the newly created process or target process. So by now our aim was to analyze the newly created or the target process.
We wanted to also see what is happening in ‘NtAllocateVirtualMemory()’ function call. According to MSDN, the ‘NtAllocateVirtualMemory()’ function call has the following prototype:
_In_ HANDLE ProcessHandle,
_Inout_ PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
We tried to map the values with the following screenshot.
The first value that was being pushed was 0x40 and that corresponded to ‘Protect’. 0x40 holds the value ‘PAGE_EXECUTE_READWRITE’. Clearly, this malware was making a region where it could read, write and as well as execute. The second value 0x1000 held the value ‘MEM_COMMIT’. We went further down to the ‘CreateProcessW()’ to see what was happening.
We could clearly see that a new process was being created with ‘CREATE_SUSPENDED’ flag set. We jumped into ‘NtWriteVirtualMemory()’ API call. As per the undocumented references, ‘NtWriteVirtualMemory()’
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN ULONG NumberOfBytesToWrite,
OUT PULONG NumberOfBytesWritten OPTIONAL
The ‘Buffer’ variable was an important on in the ‘NtWriteVirtualMemory()’ function and that pointed to the address where the possible new process code was present. We could see that in the above screen shot. We could also see the presence of DOS and PE header in the ‘Buffer’ region. So, this was going in the direction as we are expecting.
Our ultimate objective was to debug the newly created process. While through the earlier analysis, we found that ‘NtResumeThread()’ function is the one that is resuming the newly created process to run from the ‘SUSPENDED’ state. Before that we had found that the ’NtSetContextThread()’ function sets the required fields for the thread. The ’NtSetContextThread()’ function uses a CONTEXT structure that has the crucial information on the newly created process.
typedef struct _CONTEXT
} CONTEXT, *PCONTEXT;
The offset of the EAX register was at 0xb0 from the beginning of the structure. The EAX register holds the address of the entry point with respect the SUSPENDED process. So the beginning of the process space (memory address) plus the value 0xb0 will hold the instruction that gets executed first when ‘NtResumeThread()’ function is called. We patched the memory address that this offset was pointing to.
Figure 22 - The CONTEXT structure
From the above picture we could see that the CONTEXT structure starts at the address 0x003E009C. So, the address 0x003E009C plus the 0Xb0 yields 0x3E014C.We wanted to see what is there at the address 0x3E014C.
Figure 23 - The EntryPoint address within the context structure
The value at the 0x3E014C address was 0x004BFFC0. We know that in most cases the PE binaries are loaded into the address 0x00400000. So, 0x004BFFC0 minus the 0x00400000 gives the control over the first instruction (i.e. at offset 0xBFFC0). We patched the instruction at 0x004BFFC0. We patched the memory region using the ProcessHacker tool.
Figure 24 - The first instruction at the EntryPoint is 60 (PUSHAD)
The HEX value at 0Xbffc0 is 60 and it corresponds to PUSHAD instruction. We modified (patch) this to CC (debug) instruction. Basically, the 60 and CC are called OPCODES. OpCodes are nothing but instruction codes in the Intel Microprocessor language.
Figure 25 - Patching the instruction to CC (debug) instruction
Once modified, we changed the opcode at the memory location and changed by writing the opcode. Since the 0xcc belongs to breakpoint opcode, we set the Just-In-Time debugger and used the OllyDebugger as the JIT as shown below.
Figure 26 - Making OllyDbg to act as JIT debugger
Once this was done, we allowed the ‘NtResumeThread()’ function to continue. We could see that the modified thread fired an exception and was waiting for us to debug!
Figure 27 - The newly created process triggers the debug exception
We selected the Debug the program and there you go, we had hunted down the newly created process.
Figure 28 - Landing at the patched memory location of the newly created process
Now we have complete control of the process that was playing the role of encrypting the files. Since, we had full control of this process, it was easy for us to look into on the internal workings of this ransomware.
We will have the in-depth analysis of this in our next blog.