2014-03-04
Abstract
Expiro is a file infector that resurfaces from time to time, demonstrating more skills on each new appearance – infecting a service that gives a unique vantage point on traditional malicious activities; running the malware at computer restart without creating a start‑up registry; using different mutexes for different types of infected process; escalating privileges; and executing infected files without calling the CreateProcess or WinExec APIs. Raul Alvarez takes a closer look.
Copyright © 2014 Virus Bulletin
Most advanced file infectors employ a strong encryption algorithm with a mix of anti-debugging and anti-analysis techniques, but once you overcome these challenges, they are straightforward with their file infection routine – searching for files to infect, computing the exact location to put the malware body, configuring the MZ/PE header, and there you have it: a file infector.
That’s what Expiro looks like on the outside: a simple file infector with a straightforward encryption mechanism. But if we delve into its code, we will see something that sets it apart from regular file infectors.
This article will look into Expiro’s simple, but meticulous sequence of steps that leads to the infection of a unique group of files before infecting the rest of the executable files in the system.
Expiro starts by saving all register values to the stack and initializing the variable that contains the decryption key for the polymorphic engine.
For the first pass, the malware decrypts (0x32A00) 207,360 bytes using a key starting halfway through the .vmp0 section. It decrypts each byte from the middle of the .vmp0 section working backwards to the beginning of the section, where the least significant byte of the DWORD decryption key is used to XOR every byte in the given block of encrypted code.
After the first pass, the malware checks whether it has reached the required number of decryption passes. If it has not, it will execute the same decryption algorithm again.
On the second decryption pass, after decrypting 207,360 bytes, Expiro decrypts the same group of bytes back to its original form using the same algorithm. It makes the same decryption pass over and over again six times, always producing the same original form.
For every decryption pass, the malware increments a counter. The same counter is used within the decryption algorithm, combining it with the decryption key to produce a different result. But after six passes, the bytes are still in their original encrypted form. The changes can only be seen after the seventh decryption pass.
After performing the seventh decryption pass, the (0x32A00) 207,360 bytes are properly decrypted. A block of (0x24880) 149,632 bytes from the newly decrypted code is copied to the free space of the .vmp0 section. This is followed by copying another (0xA1C8) 41,416 decrypted bytes to the free space of the .vmp0 section, beyond the first block.
The decrypted code distributed in the free space of the .vmp0 section is not yet functional. Some of its binaries need further tweaking. These blocks of code need patching.
Expiro uses a table that contains keys with corresponding sizes. Each key is processed to produce patched code, with its size determining the number of times the patched code should be applied. The algorithm that produces the patched code also determines where in the decrypted code the patched code should be applied. Once a patch has been applied the correct number of times, the next key from the table is processed to produce patched code, and again applied to the decrypted code where the last patch was applied. The malware continues patching the decrypted code until all the keys from the table have been used.
A common method of getting the imagebase address of kernel32.dll is by parsing the PEB (Process Environment Block). For file infectors, another method is to parse the import table of the host file.
Parsing the import table to locate kernel32 can be done in many ways. Expiro gets the DLL names and checks the seventh and third characters to determine if they match ‘3’ and ‘r’ from ‘kernel32.dll’, respectively. If they match, the rest of the characters in ‘kernel32’ are also checked.
Then, Expiro gets the imagebase of kernel32.dll by zeroing out the least significant DWORD of the address of the first API found.
Afterwards, Expiro resolves the addresses of some of its APIs using their hash values.
Initially, a routine looks for the export table of kernel32.dll and locates the list of exported API names. The malware computes the hash value of each API name using its own hashing algorithm. Each hash value is compared against the hash value of ‘GetCurrentThreadId’ until they match. The address of the GetCurrentThreadId API is resolved by using the index pointing to the matched API name.
A similar routine resolves the following APIs: CreateThread, EnterCriticalSection, GetProcAddress, GetTickCount, InitializeCriticalSection, LeaveCriticalSection and VirtualProtect.
Commonly, the resolved API addresses are stored in a table with consecutive memory locations, but not in this case.
Once all the API addresses have been resolved, it is easy to determine the next actions of the malware by looking at the list of addresses. For Expiro, each resolved API address is scattered in a different location in its virtual space, making it a challenge for analysts wanting to look at them all at once. Figure 1 shows the resolved APIs stored in different memory locations.
Once the initial APIs have been resolved, Expiro spawns a new thread.
Upon execution of the new thread, Expiro executes another decryption algorithm to generate the API names that the malware needs.
The API names are grouped by libraries with the exception of the LoadLibraryA and GetModuleHandleA APIs. Each group of APIs passes through a routine that contains the decryption algorithm and address resolver.
The on-demand decryption algorithm computes each byte of data with a byte taken from the key string ‘V@sna8TbCzTSrs:s[fR@6’. An incrementing pointer determines which byte will be used to decrypt the byte from the given memory location. When the last character from the key string is reached, the pointer will reset to point back to the first character.
There are separate routines for each group of APIs per library (DLL). After decrypting the API names, each routine checks if the module/library already exists by using the GetModuleHandleA API. If it does not exist, it will load the module by calling the LoadLibraryA API. Then, the routine will use the GetProcAddress API to determine the actual API addresses.
Every string or name used by Expiro passes through the on-demand decryption algorithm. All the API names, library names, mutex names, and all other strings use the same decryption. These names and strings are only decrypted when Expiro needs them (see Figure 2).
After getting the addresses of all the required APIs, Expiro checks if it is running under WOW64 (32-bit Windows on 64-bit Windows).
Initially, it checks if the operating system supports WOW64 by checking the OS version using the GetVersionExA API. This is followed by getting its own PID (process id) using the GetCurrentProcessId API, and getting the handle by opening it using the OpenProcess API. Finally, it uses the IsWow64Process API to determine if it is running under WOW64.
This is also the part where Expiro determines the kind of infection needed, since the malware is capable of infecting both 32- and 64-bit executables.
If the malware is not running under WOW64, it can function as a 32-bit piece of malware on 32-bit operating systems, or as a 64-bit piece of malware on 64-bit operating systems.
After determining whether it is running under WOW64, Expiro checks its security access level using the following routines:
The malware gets the username of the current thread using the GetUserNameA API, and compares it to the newly decrypted strings ‘SERVICE’ and ‘SYSTEM’. Expiro skips this routine if the username contains the strings ‘SERVICE’ or ‘SYSTEM’ – it assumes that it has a higher security level if the username contains these strings.
If it doesn’t have the above credentials, it gets the computer name using a simple call to the GetComputerNameA API, and compares it against the username. If the username of the current thread is also the computer name, the malware will again try to skip the current routine.
Otherwise, it proceeds to acquire the environment block of the current process using the GetEnvironmentStrings API. Expiro searches for the strings ‘systemprofile’ and ‘ervice’ within the environment block. Since the malware checks byte by byte, ‘ervice’ will yield to true if it finds either ‘service’ or ‘Service’. Normally, services that run in the system will contain these strings.
If the malware has a higher security access level on the system, it will skip the routine that escalates its privilege level.
After determining the malware’s security access level, Expiro decrypts more strings (‘kkq-vx’ and ‘%s_mtx%u’) and checks if a mutex named ‘kkq-vx-mtx28’ exists, using the OpenMutexA API. If the mutex exists, it will terminate it using a call to the CloseHandle API.
The malware will loop back on the start of this routine to check for the existence of other mutexes by changing the value 28 in ‘kkq-vx-mtx28’. (The value may be different in some infected samples.) The number increments by one each time, until it reaches 99. In simpler terms, Expiro checks for the existence of mutexes ‘kkq-vx-mtx28’ up to ‘kkq-vx-mtx99’. If any of these exist, they will be terminated.
After terminating all of these mutexes, a new routine is started. This time, a mutex named ‘kkq-vx-mtx1’ is created using the CreateMutexA API. This is followed by creating a second mutex named ‘kkq-vx-mtx28’. Finally, the WaitForSingleObject API is called, with parameter WAIT_FOREVER for the mutex ‘kkq-vx-mtx28’ (see Figure 3).
Afterwards, Expiro decrypts another string, ‘gazavat-svc’, and checks if a mutex named ‘gazavat-svc’ exists using another call to the OpenMutexA API. If it exists, it will also be terminated.
The mutex ‘gazavat-svc’ will be used later on, after the infection routine.
After handling the mutexes, and if the security access level of the malware is not high enough, Expiro will try to escalate its privileges using the following routine:
Initially, the malware gets the PID (process ID) using the GetCurrentProcessId API, and gets the handle to the process using the OpenProcess API. Afterwards, it gets the access token of the process, and its data, using the OpenProcessToken and GetTokenInformation APIs, respectively.
Expiro uses the token in conjunction with the security descriptor. After initializing the security descriptor using the InitializeSecurityDescriptor API, the malware sets the discretionary access control list (DACL) using the SetSecurityDescriptorDacl API, followed by setting the owner information of the security descriptor using the SetSecurityDescriptorOwner API. The malware evaluates this information before it performs the escalation.
For the actual escalation, the malware acquires the locally unique identifier (LUID) of the SE_TAKE_OWNERSHIP_NAME privilege using the LookupPrivilegeValueA API, then sets the privilege using the AdjustTokenPrivileges API.
At this point, Expiro should have the necessary security privileges to take ownership of any process or file in the system.
After a considerable number of tasks and routines have been performed, Expiro is ready to choose which file to infect. But the preparation is not yet over: instead of just enumerating the executable files by performing simple calls to the FindFirstFileA and FindNextFileA APIs with ‘*.EXE’ or ‘*.SCR’ as parameters, Expiro wants a different set of executable files: services.
Let’s look into the preparation.
First, Expiro gets the handle for the service control manager database using a call to the OpenSCManagerA API. This is followed by enumerating the services with name and status using the EnumServicesStatusA API with parameters:
dwServiceType = (0x30) SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS
dwServiceState = (0x02) SERVICE_INACTIVE
Basically, Expiro is searching for inactive or stopped services in the system.
Expiro opens the service from the enumerated list using the OpenServiceA API, and retrieves the configuration parameters using the QueryServiceConfigA API. The service’s configuration contains properties including service name, start up type, service status, and path to the executable file of a given service.
Expiro is interested in the physical location of the executable file of a given service. It converts the whole path name to lower case, then checks if it contains ‘.exe’ – making sure that it really is an executable file.
If the path name passes a series of checks, the malware converts it to a Unicode version of the string for use in a call to the SfcIsFileProtected API. Expiro wants to determine if the specified file is protected. If it is protected, the malware will remove the protection.
After removing the file’s protection, the malware checks if the path name contains the strings ‘rsvp.exe’ (Microsoft RSVP), or ‘chrome.exe’ (Google Chrome) – if either of these strings is found, the malware will skip the infection routine.
After the necessary checks, Expiro will perform the infection routine for the selected service’s executable file.
First, it opens the executable file using the CreateFileA API with GENERIC_READ and GENERIC_WRITE access. It acquires the file size using the GetFileSize API, then copies the file to the newly allocated memory using the ReadFile API, effectively creating an exact image of the service’s executable file.
Once the image is loaded into memory, the malware parses the MZ/PE header to point to the import table and locate the DLL names. Expiro tries to locate kernel32.dll using the following routine:
After getting the DLL name from the import table list, the names will be converted to all caps. Then, the first filter is to check for the eighth byte (char ‘2’) – most DLL names fail this check. The second filter is to check for the seventh byte (char ‘3’) – ADVAPI32.DLL can still pass this one. The next filters are ‘L’, ‘E’, ‘K’, and ‘R’. Finally, it saves the location of ‘KERNEL32.DLL’ for later use.
Once kernel32.dll is secured, the PE header of the image is expanded to make room for the new section header. After a few computations, Expiro modifies the new section header with the following sequence:
The new section’s virtual size gets the value (0x7d000) 512,000, which corresponds to the increase in size of the infected file.
The starting relative virtual address (RVA) of the new section is also set, which varies between different infected files. Generally, the RVA of the new section is right after the end of the previous section.
The NumberOfRelocations, NumberOfLineNumbers and PointerToLineNumbers fields are zeroed out.
The malware uses ‘.vmp0’ as the name of the new section, which we already know.
The PointerToRelocations field is zeroed out.
The value (0x7d000) 512,000 is placed in the SizeOfRawData field, similar to the VirtualSize field.
The PointerToRawData or the file offset of the start of the new section is set with a value that depends on the size of the host file.
The section’s characteristics are set to (0xE0000000) Executable | Readable | Writable.
After setting up the new section header, the malware sets the SizeOfImage to 0x81000 (which varies between different infected samples). This is followed by incrementing the number of sections by one, to accommodate the newly added section.
Once the MZ/PE header has been modified and the new section header has been added, Expiro generates random values and patches a block of code containing a copy of the malware body.
This is followed by copying a portion of the image (from the entry point) to the patched block of code in memory.
Then, it copies the (0x7C6CD) 509,645 bytes of code (containing the patched code and the portion of the image) to the start of the new .vmp0 section.
Since the malware code at the new .vmp0 section is not yet encrypted (just recently patched), Expiro runs a simple encryption routine to encrypt the content of the .vmp0 section of the image, byte by byte. The encryption skips the copied portion from the image’s entry point.
After the modification of the service’s executable image in memory, it will release the handle for the physical file using the CloseHandle API.
Afterwards, Expiro creates a ‘.vir’ file, e.g. ‘{service_filename}.vir’, using the CreateFileA API, which is followed by copying the infected service’s image from memory to the .vir file, using the WriteFile API.
Then, the malware frees up the image using the LocalFree API and closes the handle to the .vir file. Expiro copies the .vir file to its .exe counterpart (e.g. it copies ‘XYXYXservice.vir’ to ‘XYXYXservice.exe’), using the CopyFileA API. After a successful copy, the malware deletes the .vir file using the DeleteFileA API.
Expiro checks if the service name of the infected file is found in the string ‘|wscsvc|WinDefend|MsMpSvc|NisSrv|’, which contains the names of the services for Security Center, Windows Defender, Microsoft Antimalware Service and Microsoft Network Inspection, respectively.
If the service name is found, the malware disables the service permanently by calling the ChangeServiceConfigA API with the (dwStartType) SERVICE_DISABLED parameter. Then it exits the current routine.
If the service name of the newly infected file is not in the list, it will set the service’s configuration to the following parameters: (dwServiceType) SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_SHARE_PROCESS, and (dwStartType) SERVICE_AUTO_START, using the ChangeServiceConfigA API. Earlier, in selecting which services to infect, Expiro searched for inactive and stopped services. Once these services are infected, the malware changes their start type to start automatically when the operating system restarts. This is another technique for making sure the malware runs during the boot process.
Expiro then runs the infected service simply by calling the StartServiceA API. One of the markers that indicates that a service is infected is the presence of the mutex named ‘gazavat-svc’ (see Figure 3).
Afterwards, the malware will proceed with the infection of the rest of the inactive services, while the rest of Expiro’s malicious activities will be executed within the service’s process.
Expiro relies heavily on the use of its on-demand decryption algorithm. Although not too complex, it serves its purpose well – not revealing everything all at once.
Expiro is not a new file infector, but it resurfaces from time to time, demonstrating more skills on each new appearance – infecting a service that gives a unique vantage point on traditional malicious activities; running the malware at computer restart without creating a start up registry; using different mutexes for different types of infected process; escalating privileges; and executing the infected files without calling the CreateProcess or WinExec APIs.
Expiro has other malicious activities which are beyond the scope of this article. Simple or not, this is not the last time that we will hear from this malware.