Type File virus
Creator Jacky Qwerty
Date Discovered 1997
Place of Origin Peru
Source Language Assembly
Platform MS Windows
File Type(s) .exe
Infection Length 3,298 bytes

Cabanas is a memory resident virus that works on Windows NT by Jacky Qwerty of 29A. It is also "per process" virus, meaning it was capable of staying resident within the host application's private space. It was the first 32-bit Windows virus capable of infecting across all 32-bit windows platforms, including NT and the 9x series (98, 95 and ME). In addition to these features, it was anti-debugging, anti-heuristic, encrypted and semi-stealth.



When a file infected with Cabanas is executed, the execution will start at the original host entry point. Cabanas does not touch the entry point field in the Image File Header. Instead it patches the host program at its entry point. Five bytes at the entry point is replaced with a FAR JMP to the address where the original program ended. This may be considered one if its anti-heuristic features, because the host entry point value in the PE header keeps pointing inside the code section, turning off some heuristic flags. As a result, the first JMP points to the real entry point.

The first function in Cabanas unpacks and decrypts a string table which consists of Win32 KERNEL API names. Cabanas is an armored virus. It uses "Structured Exception Handling" (SEH) as an anti-debugging trick. This prevents debugging from any application-level debugger. When the unpack/decryptor function is ready, the virus calls a routine to get the original Base Address of KERNEL32.DLL. During infection time, the virus searches for GetModuleHandleA and GetModuleHandleW API in the Import Table, respectively. When it finds them, it saves a pointer to the actual DWORD in the .idata list. Because the loader puts the addresses to this table before it executes the virus, Cabanas gets them easily.

If the application does not have a GetModuleHandleA / GetModuleHandleW API import, the virus use a third undocumented way to get the Base Address of KERNEL32.DLL by getting it from the ForwarderChain field in the KERNEL32 import. This will not work under Windows NT, but will work on Windows 95. When the virus has the Base Address/Module Handle of KERNEL32.DLL, it calls its own routine to get the address of GetProcAddress function. The first method is based on the search of the Import Table during infection time. The virus saves a pointer to the .idata section whenever it finds a GetProcAddress import in the host. In most cases Win32 applications import the GetProcAddress API, so the virus should not use a secondary routine to get the same result.

If the first method fails, the virus calls another function which is able to search for GetProcAddress export in KERNEL32. Such function could be called as GetProcAddress-From-ExportsTable. This function is able to search in KERNEL32's Exports Table and find the address of GetProcAddress API. This function is one of the most important ones from the virus point of view and it is compatible with all Win32 based systems. If the entry point of GetProcAddress was returned by the GetProcAddress-From-ExportsTable function, the virus saves this address and use it later on. Otherwise, the GetProcAddress-From-ExportsTable function will be used several times. This function is also saved with "Structured Exception Handling" to avoid from possible exceptions. After this, the virus gets all the API addresses it wants to use in a loop. When the addresses are available, Cabanas is ready to replicate and call its direct action infection routine.

Direct-action Infection

Even though the virus goes through all the files in Windows directory, Windows System directory and in the current directory respectively, the file infection is fast enough to go unnoticed in most systems. This is because the virus works with "memory mapped files", a new feature implemented in Win32 based systems which simplifies file handling and increases system performance.

First the virus gets the name of Windows directory, then it gets the name of Windows System directory and calls the function which searches for non-infected executable images. It searches for non directory entries and check the size of the files it found.

Files with size evenly divisible by 101 are assumed to be infected, so it will avoid them. It also avoids files that are over 64 megabytes. Next the virus checks the file extension, if it matches EXE or SCR (screen saver files), the virus opens and maps the file. If the file is 128 bytes, the file is closed. Then it checks the‘MZ’ marker at the beginning of the image. Next it positions to the possible ‘PE’ header area and checks the ‘PE’ signature. It also checks that the executable was made to run on 386+ machines and looks for the type of the file. DLL files are not infected.

After this, the virus calculates a special checksum which uses the checksum field of PE files Optional Header and the file-stamp field of the Image File Header. If the file seems to be infected the virus closes the file. If not, the file is chosen for infection. Cabanas then closes the file, blanks the file attribute of the file with SetFileAttributeA API and saves the original attributes for later use. This nullifies the "Read Only" attribute for the virus. Then again, it opens and maps the possible host file in read/write mode.

Next it searches for the GetModuleHandleA, GetModuleHandleW and GetProcAddress API imports in the host Import Table and calculates pointers to the .idata section. Then it calls the routine which patches the virus image into the file. This routine first checks that the .idata section has MEM_WRITE characteristics. If not it sets this flag on the section, but only if this section is not located in an executable area. This prevents the virus from turning on suspicious flags on the code section, triggered by some heuristic scanner.

Then it goes to the entry point of the image and replaces five bytes with a FAR JMP instruction which will point to the original end of the host. After that it checks the relocation table as some relocations may overwrite the FAR JMP at the entry point. If the relocation table size is not zero the virus calls a special routine to search for such relocation entries in the .reloc area. It clears the relocation type on the relocation record if it points into the FAR JMP area, thus this relocation will not take into account by the loader. The routine also marks the relocation, thus Cabanas will be able to relocate the host later on. Then it encrypts all the information which has to be encrypted in the virus body. Including the table which holds the original 5 bytes from the entry point and its location.

Next the virus calculates the special checksum for self checking purposes and saves this to the time stamp field of the PE header. When everything is ready, the virus calculates the full new size of the file and makes this value divisible by 101. The real virus code is around 3,000 bytes but it will increase the file sizes by larger than that to make sure the infected file size is divisible by 101. Cabanas uses a trick here. The virus does not create a new section header to hold its code, but patches the last section header in the file (usually .reloc) to grow the section body large enough to store the virus code. This makes the infection less risky and less noticeable.

Then the virus changes the SizeOfImage field in the PE header to reflect the changes made to the last section in the file, then unmaps and closes the file. Next it truncates the file at the previously calculated size and restores the original time and date stamp. Finally Cabanas resets the original attribute of the file. When all the possible files have been checked for infection, Cabanas is ready to go memory resident.

Rebuilding the Host, Hooking API Functions and Going Memory Resident

The next phase is to rebuild the host program. The virus locates an internal parameter block which consists of the previously encrypted code from the host (5 bytes) and writes back the 5 original bytes at the entry point. After this, it relocates the code area if needed, by searching in the .reloc section for marked relocation entries. Next the virus hooks API functions and goes memory resident.

The API hooking technique is based on the manipulation of the Import Table. Since the host program holds the addresses of imported functions in its .idata section, all the virus has to do is to replace those addresses to point to its own API handlers.

To make those calculations easy, the virus opens and maps the infected program. Then it allocates memory for its per-process part. The virus allocates a 12232 bytes block and copies itself into this new allocated area. Then it searches for all the possible function names it wants to hook: GetProcAddress, GetFileAttributesA, GetFileAttributesW, MoveFileExA, MoveFileExW, _lopen, CopyFileA, CopyFileW, OpenFile, MoveFileA, MoveFileW, CreateProcessA, CreateProcessW, CreateFileA, CreateFileW, FindClose, FindFirstFileA, FindFirstFileW, FindNextFileA, FindNextFileW, SetFileAttrA, SetFileAttrW. Whenever it finds one of the latter APIs, it saves the original address to its own JMP table and replaces the .idata section's DWORD (which holds the original address of the API) with a pointer to its own API handlers. Finally the virus closes and unmaps the host and starts the application, by jumping into the original entry point in the code section.

Some Win32 applications however may not have imports for some of these file related APIs, they can rather retrieve their addresses by using GetProcAddress and call them directly, thus the virus would be unable to hook this calls. The virus also hooks GetProcAddress for a special purpose. GetProcAddress is used by most applications. When the application calls GetProcAddress the virus new handler first calls the original GetProcAddress to get the address of the requested API. Then it checks if the Module Handle parameter is from KERNEL32 and if the function is one of the KERNEL32 APIs that the virus wants to hook. If so, the virus returns a new API address which will point into its NewJMPTable. Thus the application will still get an address to the virus new handler in such cases as well.

Stealth and fast infection capabilities

Cabanas is a semi-stealth virus: during FindFirstFileA, FindFirstFileW, FindNextFileA and FindNextFileW, the virus checks for already infected programs. If the program is not infected the virus will infect it, otherwise it hides the file size difference by returning the original size for the host program. During this, the virus can see all the file names the application accesses and infects every single clean file.

Since the CMD.EXE (Command Interpreter of Windows NT) is using the above APIs during a DIR command, every non infected file will be infected (if the CMD.EXE was infected previously by Win32.Cabanas). The virus will infect files during every other hooked API request as well.

The virus contains the following text string, never actually displayed during infection:

(c) Win32.Cabanas v1.0 by jqwerty/29A.


Jacky Qwerty himself appears to have created at least one variant named Cabanas 1.1, which appears to be mostly similar. There are a few differences, such as the file size being 3,018 bytes and the text contains a slightly different version number:

(c) Win32.Cabanas v1.1 by jqwerty/29A.

There are also two 3,014-byte variants as well as one more 3,018-byte variant.

Name and Origin

The Cabanas virus takes its name from a school friend of Jacky Qwerty's named Gonzalo Cabanas (Cabañas?). In a comment in the source code, Jacky Qwerty describes him as a best friend he shared music and fashion tastes. From the comment, it appears that Gonzalo Cabanas is no longer alive and the circumstances of his death may have been violent, as it mentions the virus won't "do him justice".

It was coded in 1997 by Jacky Qwerty in Peru. He was 15 years old when he coded this virus.


Jacky Qwerty. 29A Issue 2, Cabanas Source Code.

Peter Szor. F-Secure, Threat Description: Cabanas.

Lord Julus. Interview Jacky Qwerty. 1999.08


Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License