Archived Blog

XP Antivirus: Rogue AV with a custom packer

05.30.2008 - 5:40 PM

Nowadays, most unwanted software, such as malware and rogue products, uses packers to obfuscate itself. In addition to the known packers, some of them use custom packers, using polymorphic techniques to prevent detection. Every two weeks, I will write a little blog about one of those custom packers, and explain some of the tricks they use.

Today, our target is "XP Antivirus", a rogue product, pretending to be security software. It reports infections on your machine when there are none, and if you want to "clean" your computer, you have to register and pay 49.95 US dollars.

Headers

Whenever I receive a file, I usually open it in a PE editor and check the headers, looking for clues that could tell me that the file has been packed.

This looks like a typical non-packed file. No fancy section names or section characteristics appear (like executable rights in the resource section, or write access in the text section). This is mostly done to cheat heuristics. However, the last section's raw size seems to be a lot smaller than its virtual size counterpart. This is not normal, especially for the resource section, and it could be a clue that the data is compressed.

Import Table

Non-packed files usually have a good amount of imported functions, especially if they are quite big. Our target has 15 imported functions from 4 dlls.

I can't believe that a 959KB executable, with a full GUI, online access, hard drive scanning, and more, imports only 15 functions, and that all the others are dynamically loaded.

Those two quick checks alone make me think that there is something fishy in our target, and it's time to load the file into IDA Pro.

Static Analysis with IDA Pro

A quick look through the code, and it's obvious they are using junk code and obfuscated operations to hide their activity. There aren't many functions, so it's not a very tedious job to go through them all, and to analyse them.

I won't detail most of them; otherwise, it would take too much time. Rather, I am going to go quickly through the code flow and comment on interesting code. One of the first things we notice in the loader is the way it grabs the kernel32 imagebase. It's a well-known technique, but they tried to obfuscate it a little. They push ESP and pop it in EDI, and then start doing a loop. At the process entry point, [ESP] has a kernel32 memory address, and this is what they use.

They use SHR and SHL instructions to round the address to the closest page, and start reading a dword at that page. They then go through pages, until the dword matches a hardcoded value. This value is actually the MZ marker, followed by two bytes. The magic value can't be seen in the code; it's calculated using some hardcoded value that gets ROLed before any comparison is done.

The packer then dynamically loads a few API functions, using CRC rather than function names. This is a way to obfuscate the loaded function names. Shellcode uses similar techniques, but the purpose there is to write smaller code, because a 4-byte CRC is smaller than most function names, and it doesn't even require a null byte.

After that, the loader handles the target import table. The function is highly obfuscated, but not everything is, so we don't even have to bother understanding the whole code to rename the function to something meaningful. Import Table handling functions always have some tasks they need to do, in order to work properly, and one of them is easily guessable because of one constant. In this case, the 0x800000000 constant is the culprit. This is used to test whether we are importing by ordinal or by name. Another one is the size of an Image Import Descriptor, nicely hardcoded into the code. This is enough for me to rename the function as "handle_target_imports" and move on to the next part of our loader.

If we keep going through our loader, we are going to see additional interesting code.

I tried scrolling through the code, to find something that could be a jump to the original entry point, but I couldn't find it. I was expecting the jump to be encrypted, because most packers do this. But after I met the code above, I realised that the packer copies some unreferenced bytes into allocated memory (the same memory that contains original headers of the packed file). Those bytes are actually assembly instructions. As most of the loader, it's obfuscated, but it's very easy to understand it. First, we have to generate code, since it isn't referenced; IDA displays it only as bytes. (Pressing "C" does the trick, for people who aren't yet IDA proof). Here is what we get:

The OEP address is hardcoded in an encoded form, as you can see above, and so are many constants. It's trivial to get the important information. One thing to note is that the loader overwrites the whole PE image, using the code on the heap, including headers. In those headers, we see the UPX as section names. There is yet another layer before the final code, because of UPX, but this isn't really a problem. The headers in memory are now very different from the headers on disk, and this is an anti-dump technique.

Indeed, most tools use PE headers from disk to dump processes, because packers often mess with headers in memory to prevent dumping. In this case, the headers on disk are not related to the memory layout anymore, since it has been overwritten. Import Reconstructor will also fail at locating the IAT.

You have to set up your tools so that they don't use the headers from disk, but instead use the ones in memory. Import Reconstructor allows this, and so does LordPE. Once this option is set, you can dump the process correctly and rebuild the imports without problem. Otherwise, you will get a corrupted file on disk. (The Directory Table will be all messed up. Imports, Resources, and the like won't be at the correct location, and the Image Section Headers will be totally wrong.)

The application is now totally unpacked and working, so we can continue our analysis, if necessary. This "packer" is actually more of an obfuscator/encryptor than a packer, but each file protected with it is totally different. I noticed even the jmp to the entry point is morphed. The setup file was also encrypted, and it did not use a jmp eax to reach the entry point.

Security Researcher: Nicolas Brulez

Bookmark This Post: