W32.Xpaj.B – An Upper Crust File Infector
It is not very common for a file infector to do more than simply introduce trivial modifications to the files it infects. Virus authors usually avoid complex modifications to the files because of the possibility of corruption. W32.Xpaj.B is one of exceptions.
W32.Xpaj.B is an entry-point obscuring, polymorphic file infector. The virus is not completely new and shares some of its characteristics with its predecessor, W32.Xpaj, first seen in June 2008. What sets this creature apart is the amount of effort its authors have invested into hiding their malicious code in the files it infects.
W32.XPaj.B is more sophisticated than your average file infector. To make finding its malicious code difficult, the virus avoids putting any obvious signs in the infected files. Unlike most simple viruses, it doesn’t attempt to execute the virus code by hijacking control when the infected file is started. Instead, the virus overwrites some subroutines from the infected files with its own code and then stores a copy of the overwritten subroutines that will execute after it gains control. However, this infection method does not guarantee that the virus code will execute every time an infected file is executed. When the virus overwrites a subroutine that is used for a specific task, the virus code will only execute when the user uses this functionality in the application.
However, it’s possible that overwritten subroutines may be unreachable after infection. Infecting select subroutines—as opposed to the entry point of the application—is not without risk to the author of Xpaj; rather, it’s entirely possible that the newly overwritten subroutines may not actually be executed during runtime. To improve its chances of being executed, the virus usually overwrites more than one subroutine and also redirects some other unrelated calls to point to its own code.
Here is an example of modifications to the code section during an infection:
The following diagram illustrates the extent of the code modifications and the relationship between different parts.
Notice that subroutines overwritten by the virus are moved to a new location in the file and executed from there. For copies to work correctly in the new location, the infector has to analyze these subroutines and patch any instructions that refer to other blocks of code when moving within these subroutines.
The infection code is further complicated by the fact that its authors decided to include the ability to infect .dll files. When an application running as a process executes, the executable and all of the DLL libraries that it uses are loaded into memory so that they don’t overlap. At the time the DLLs are created it’s not possible to predict the location in memory where they will be loaded. For this reason the PE files include information about patching that may be used by the operating system when the files are loaded into memory. This information is contained in the PE file as the fix-up table. For the infected DLLs to work correctly, W32.Xpaj.B stores relevant information from the fix-up table and uses it for patching copied subroutines at runtime. It also rebuilds the fix-up table for infected files to avoid corruption of its own viral code when the operating system loads an infected DLL into memory.
Another feature used by the infector to further hide the presence of malicious code is the ability to insert the encrypted viral body before other structures commonly present in PE files, such as resources and a fix-up table. When moving resources in the file during infection, the virus updates the relevant resource directories.
When the infected file executes and the virus code gains control, it saves some registers to preserve the required portion of the processor state. It then modifies permissions on the area of memory containing the viral code (making it executable), decrypts it, and then executes it. After the virus is started, it restores saved registers and executes the copies of the overwritten subroutines. This allows for the infected executable to continue working.
The only non-encrypted virus code is located in the overwritten subroutines. However, on its own, this code doesn’t contain complete functionality to modify memory protection and decrypt the virus body. Instead, the code implements a small number of basic operations and an interpreter that is controlled by an encrypted array that is stored along with the virus body.
The content of this encrypted array can be interpreted as a sequence of instructions for a very simple, stack-based, virtual machine implemented by the virus. The W32.Xpaj.B variant we analyzed uses just seven basic operations, such as pushing constants onto the stack, adding two numbers at the top of the stack, simple conditional jumps, and calls to native code. The sequence of operations encoded by the array forms a program that locates the address of the ZwProtectVirtualMemory API function, calls this API to modify memory protection containing the virus body, and then constructs and executes the decrypter.
When implementing W32.Xpaj.B, the authors weren’t able to avoid some issues that may result in corruption of the infected files. The corruptions introduced by the virus may lead to consequences ranging from incorrect results produced by affected applications to crashing programs and perhaps even the whole system. Unfortunately, due to the nature of these corruptions the final behavior is not easily predicted.
For those of you running Symantec antivirus products, our latest definitions enable disinfection of files infected by this threat, as well as any files that may spread the infection. When handling files corrupted during infection, the product will remove the virus. However, it may not be able to fix the file corruptions introduced by the infector. These corrupted files should be manually restored from backup copies.