A Technical Breakdown of ModPOS

ModPOS is the latest in the string of POS malware that’s making the news. As its family name implies, this malware is intent on one: stealing credit card information.

As part of our ongoing quest for complete endpoint security, we decided to research ModPOS since it’s relatively sophisticated and we did not find any other thorough documentation of its technical capabilities.

ModPOS targets Windows XP, which many POS systems are still running, despite the fact that Microsoft no longer supports that OS.

Our analysis shows that the malware quits before communicating outbound when running on later versions of Windows. However, our research has showed that with a few minor adjustments, the ModPOS code is perfectly capable of running on 32-bit versions of Windows 7 (and therefore also on Vista and probably on newer versions too), given elevated privileges.

Peeling Off ModPOS Layers: Initial Look

The malware arrives as an installer that runs in various ways – either by using an exploit or manually by a user.

Since ModPOS uses different packers to protect itself against analysis and reverse engineering, many variants exist and they come in different shapes and sizes.

For our analysis, we picked at random one of the ModPOS samples. This specific sample weighs 80KB and features an icon that resembles a barcode.

Figure 1: ModPOS variant icon

Figure 1: ModPOS variant icon

Layer 1: Installer Evasion Techniques

The sample starts by creating a GUI window using the CreateWindowEx and ShowWindow APIs. These are the same APIs that are used by applications to create and display windows to the user.

The malware though does this creation with a twist – although it creates, it does not visibly display a window since it is very wide but has no height. Furthermore, the ShowWindow function is called with the ‘hidden’ flag. Later it loads a bitmap from its own resources section to the window.

This is done in a way that is transparent to the user but is an important maneuver against many security products which will regard the application as non-malicious based on the mere fact that it has a GUI that displays images.

For sake of research, we played with the dimension parameters and got to see this window:

Figure 2: The “research-tampered” window

Figure 2: The “research-tampered” window

This picture looks random at first, but it actually consists of executable code displayed in a graphical way. In bitmap format every byte is displayed as one pixel, color-coded by its value.

Next, the malware reads the loaded bitmap, manipulates it, copies it into a newly allocated buffer and then jumps to the unpacked code which is now ready to run.

Layer 2: Installing a Driver

The purpose of the newly unpacked code is to install a driver and load it to the kernel. This is a practice used by rootkits and more sophisticated malware, but is uncommon among POS malware.

The code reads a list of installed kernel services from the registry. These services are essentially drivers (.sys files) found in %systemroot%\Drivers (typically C:\Windows\System32\Drivers).
Each of the drivers are first read, then rewritten to disk and finally loaded to the kernel (i.e. started a service).

Using monitoring tools such as Microsoft Sysinternals’ Procmon we can easily see .sys files that are written to the disk. This is probably done as another measure of anti-detection where the files that are re-written will still be considered by anti-virus solutions as legitimate files.

If the driver fails to load, however, ModPOS decides that its file is safe to be replaced by malicious code. At this stage, the failed drivers are replaced by a driver stored inside ModPOS’ binary and then loaded using the ZwLoadDriver routine.

In Windows XP this will be enough to load the driver to the kernel and begin running malicious code through the driver. In Windows 7 as installed on our test machine – the driver fails to load.

Layer 3: Loading the Driver

Since the driver did not load on the installation attempt, we tried running it manually using a tool provided by Windows and executing the following command line:

sc create modpos binPath= modpos.sys type= kernel

(notice the space following the equals sign, this is on purpose).

This command creates a kernel service for the driver named “modpos”, allowing us to later start it using the command:

sc start modpos

At this stage, an error is returned:

“A device attached to the system is not functioning”.

Note that this error is not returned by the SC tool, but by the driver itself. This means that the driver does in fact start and potentially some (or all?) of the malicious code has already run.

Layer 4: Setting Up Kernel Debugging

For debugging and tracing user mode programs we have used the industry standard tools on the same machine where ModPOS is running. Debugging kernel drivers requires setting up another machine to debug the kernel of our target test machine. Setting up kernel debugging on a virtual machine is well documented and is outside the scope of this post.

Layer 5: Breaking (on) the Driver

For this task we call our PE editor for assistance. We use it to find the entry point, and also to find that the code at the entry point is the no-operation like instruction:

8B FF     mov edi,edi

This instruction is used for alignment purposes which are of no concern to us right now. The reason is that we know that it basically does nothing and we can safely overwrite this with a break-to-debugger instruction on two bytes:

90           nop
CC           int 3

Replacing an instruction that does nothing frees us from the need to fix the instruction back to what it was before modification. It does not, however, free us from the need to recalculate the file checksum which is done using the PE editor of choice.

Recalculating the checksum, attaching a remote debugger and running the driver using SC brings us to a stop right at the entry.

Layer 6: Unpacking the Driver

The first routine of the driver is unpacking the code. This time the code is self-modifying (or “polymorphic” code), meaning that parts of the code decrypt the next part while running.

This is done in loops that manipulate code. At the end of each loop a JNE (jump if not zero) instruction jumps back to the beginning of the loop until that loop is finished and execution continues to the next one.
What we need in order to unpack the code is to wait for any branch instruction that is not a JNE.

After some loops of the aforementioned kind – we encounter a call instruction into unpacked code:

Figure 3: the call into the unpacked code

Figure 3: the call into the unpacked code

At this stage the code is unpacked and we can create a dump and view it in IDA.

Figure 4: the code dump

Figure 4: the code dump

A few things are clear from this picture:

  1. The unpacking code is right before the start_of_unpacked_code routine (shown in brown on the navigation bar)
  2. There is one call to another function (which we named unpacked_entry)
  3. When (and if) the code finishes here it will return the value 0xC0000001, which in turn will display the known error message about a device not functioning. As such, the fact that we saw that error earlier does not mean that the malware did not run.
Figure 5: The unpacked main function and its possible outcomes

Figure 5: The unpacked main function and its possible outcomes

This function does quite a lot of unpacking and moving code segments between buffers. What interests us is what happens at the end, right before the function returns.

The function can either return 1, return 2 or call (and execute) a newly allocated buffer.

We have labeled (as shown in Figure 4) the return 2 code portion “fail_and_return_2” and the jump code “jump_to_unpacked_code“.

In the particular ModPOS sample we analyzed, on a Windows 7 test machine, the code reached “fail_and_return_2“, but examining the allocated buffer showed a code that could potentially run. At this stage, we pointed the EIP register to the “jump_to_unpacked_code” address.

This code portion includes pushing parameters for the function, and then a call eax instruction which jumps right into allocated code.

Creating a dump of this new allocated code, loading it into IDA, naming imported functions and some of the more obvious code parts shows us this picture:

Figure 6: dump of the newly allocated code

Figure 6: dump of the newly allocated code

Once again, we have an unpacked_entry function which at the end jumps to the unpacked_main code.

Layer 7: Persistence

The kernel code is also responsible for ModPOS’ persistence in the system. This code installs a new service into the registry, with its own binary file as the path, and sets its mode to automatically start on windows startup.

This operation is similar to the “sc” command we used manually earlier.

Since the program is installed as a kernel service it is, essentially, a rootkit.

Back to the Surface: User Mode Injection

This newly allocated code is responsible for decoding other pieces of code to be injected into user-mode processes.

The driver code uses the ZwOpenProcess API to gain access to the remote process. Since the code is running in kernel space it has full access to every process in the system.

The code is written to the memory of the new process using the APIs ZwAllocateVirtualMemory and ZwWriteProcessMemory.

Finally, the remote thread is started by calling ZwQueueApcThread.

The malware injects a few different code parts into different processes. The method is identical and so are the anti-analysis measures used to protect those injected malicious code threads. We decided to focus on the one injected into the web browser’s process, in this instance it was iexplore.exe.

User Mode Injected Thread

Creating a dump and analysing the newly injected thread shows us a code with many dynamic calls and a few cleartext strings.

The final anti-analysis trick that is in use by ModPOS is the function search routine.
Instead of looking for modules and functions by their names, ModPOS scans the entire process’ memory space looking for its required API addresses. The search is done using a hashing algorithm and is in place in order to make it harder for researchers and security products to guess which Windows functions this program wants to call.

A live debugging of the program allows us to skip the search algorithm altogether and find out which functions are actually resolved when the function exits.

The final image we would like to show is the point just before the malware communicates with its C&C server:

Figure 7: final instructions before the malware’s outbound communication

Figure 7: final instructions before the malware’s outbound communication

The IP address of the C&C server is shown on the bottom of our debugger window in figure 7. It is worth mentioning that by the time of analysis the specific server has already been taken down.


Removing ModPOS can be done by deleting the service (given its name) and restarting the system. This can be done manually through the registry editor, under a key found in


or using the command:

sc delete <service_name>

Note that the “sc” tool also supports a stop command. However, the stop command basically requests the service to stop rather than forcefully stopping it. It will have no effect on ModPOS driver.

Conclusion and thoughts

This post demonstrates reverse engineering and debugging a malware program that begins in user mode, installs code as a kernel service and injects code back to user mode.

We decided to focus our research on some parts of the code, but ModPOS has a lot more to investigate if one is up for the challenge.

It is obvious that the creators of ModPOS had security products detection evasion in mind throughout all stages of infection and operation.

Anti-debugging, however, was completely absent, either because using these techniques might have raised red flags with certain security solutions or simply because hiding from security researchers was not the main goal of these particular threat actors.

Learn more about enSilo’s endpoint security solution.

Sign Up for a Demo Today

Related Blog Posts