A Curious Case of Malwarebytes



Recently, I installed Malwarebytes on my machine. I played around with it for a little and I noticed that something was off with the scanning of files on disk. Naturally, it tempted me into digging further to identify the root cause but I had also unexpectedly discovered something else that was incredibly strange. So, a couple of issues and hours of investigation later, I would like to present to you what I’ve uncovered in this journey.

What File Scanning?

For those unaware, the EICAR test file is a file developed to test anti-malware solutions by intentionally triggering a detection based on the following ASCII string: X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*. For more information about this, please see here: https://www.eicar.org/.

As I completed the installation of Malwarebytes, I wanted to test it with the classic EICAR file. So I dropped it into disk and waited for a detection notification… and nothing…? Malwarebytes did not bat an eyelid. I escalated it further and explicitly requested a scan on the file… and nothing!

After Googling the issue, here is the summary in the FAQ of why Malwarebytes does not detect EICAR files:

Weird flex, but okay.

After this unique incident, I decided to try and see if it would detect anything that was dropped to disk so I decided to find something that should guarantee a detection: Quasar RAT. I downloaded it and unzipped it, waiting in anticipation for the detection notification… and nothing! Again!

I performed a manual scan on the Quasar.exe file to check if Malwarebytes was actually functional at all and, lo and behold, it picked it up!

Investigating the Issue

Of course, I was not impressed by what I have seen. I wanted to investigate what was causing this problem to resolve it. Having messed with a little bit of kernel driver development before, I knew where to look.

Minifilter Callback Operations

Windows has a special type of drivers called Minifilters which are used for file system operations. To register as a minifilter, the driver must use the FltRegisterFilter registration function. One of the parameters specify the registration context which is a struct that holds the relevant information to be provided to the kernel.

typedef struct _FLT_REGISTRATION {
  USHORT                                      Size;
  USHORT                                      Version;
  FLT_REGISTRATION_FLAGS                      Flags;
  const FLT_CONTEXT_REGISTRATION              *ContextRegistration;
  const FLT_OPERATION_REGISTRATION            *OperationRegistration;    // <--
  PFLT_FILTER_UNLOAD_CALLBACK                 FilterUnloadCallback;
  PFLT_INSTANCE_SETUP_CALLBACK                InstanceSetupCallback;
  PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK       InstanceQueryTeardownCallback;
  PFLT_INSTANCE_TEARDOWN_CALLBACK             InstanceTeardownStartCallback;
  PFLT_INSTANCE_TEARDOWN_CALLBACK             InstanceTeardownCompleteCallback;
  PFLT_GENERATE_FILE_NAME                     GenerateFileNameCallback;
  PFLT_NORMALIZE_NAME_COMPONENT               NormalizeNameComponentCallback;
  PFLT_NORMALIZE_CONTEXT_CLEANUP              NormalizeContextCleanupCallback;
  PFLT_TRANSACTION_NOTIFICATION_CALLBACK      TransactionNotificationCallback;
  PFLT_NORMALIZE_NAME_COMPONENT_EX            NormalizeNameComponentExCallback;

Within this struct, there is one member that is interesting: OperationRegistration.

  UCHAR                            MajorFunction;
  PVOID                            Reserved1;

This struct describes the type of operation that will be registered as a callback (MajorFunction) and the two functions that will handle the callback (PreOperation and PostOperation). The PreOperation handles the callback before the operation is performed and the PostOperation handles the callback after the operation is performed. This struct is used in an array that may specify multiple types of operations.

The Tip of the Iceberg

Using this knowledge, I discovered the registration structure used for FltRegisterFilter:


If we match the offsets of the above struct definition, we can deduce that the two green unk_XXX values are the ContextRegistration and OperationRegistration respectively. We are interested in the second one:

The figure above shows the OperationRegistration struct with the operations IRP_MJ_CREATE (file handle opens) and IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION. What’s strange here is that there is no registered callback registration for IRP_MJ_WRITE (file writes) nor IRP_MJ_CLEANUP (file handle closes). This could track malicious byte patterns being written to a file as well as being able to scan a file after it has been opened and potentially modified. Perhaps this was the issue for failing to have scanned Quasar?

Anyway, the IRP_MJ_CREATE specifies both PreOperation and PostOperation. The PostOperation is only used for clean up so we are not interested in that. Let’s have a look at the PreOperation. The function is actually quite small and didn’t contain any relevant information about file scanning but I noticed the following deferred routine:


Taken By Surprise

If we jump into sub_140005AA0, we can see this:

In the green boxes, there are calls to RtlCompareUnicodeString, with the first argument as the file extension and the second argument as hardcoded strings! Here are the three strings:

Looking back at the flow chart, RtlCompareUnicodeString is defined to return zero if the two provided strings are the same. We can follow the green branches that satisfy this return value and see that execution flows all the way to the end of the function. The red box on the right contains the function that scans the file. If we wanted to bypass the file scanning function, all we need to do is to rename the file extension to Manifest, Config, or etl (case-insensitive)!

It is actually possible to run executable files despite lacking the exe extension name. I believe that the PATHTEXT environment variable plays a role in the CreateProcess function. The (my) PATHTEXT variable is defined as:


From my educated guess, if the executing file has an unassociated file extension, it will iterate through these file types in order (left to right) and verify by analysing the file’s header. In the case of an exe, it will reach the .EXE value, be recognised as an executable by the MZ header signature, and then attempted to be executed as such. Correct me if I’m wrong.


In the following GIF, I will show that malicious files dropped to disk do not get detected. Then, by changing the file name extensions to etl and manifest, Malwarebytes will not also not see them.

Dropper PoC

I’ve also developed a PoC dropper designed to automate this process.


This journey started out quite strange and became even stranger. I have no idea why the implementation is missing some file system operation callbacks. It may explain why Malwarebytes is not scanning files when they are written to disk. I have even less of an idea as to why it was decided that these file extensions were whitelisted from scanning. Perhaps it was an optimisation of some sort? Maybe it was assumed that they weren’t executable? Let me know what you think.

As always, you can find the PoC here on my GitHub: https://github.com/NtRaiseHardError/Antimalware-Research/tree/master/Malwarebytes.

– dtm


(Alice) #2

Correct and disagree with me if I am wrong, but pretty much malwarebytes fucked up when it comes to the name of files and the like? I don’t know the terminology nor do I understand why when written to disk it won’t be detected, but I believe there is a name for this type of fuck up? Anyways, I wonder if the extension could be changed like this:





The file extension is still exe which doesn’t change anything.



Yes @dtm, Malwarebytes is really a weird AV.
Did you bypass their product key authentication system (premium forever) ?



No, I used a trial version.


(Alice) #7

It was an example, but yeah. I figured out that it was still an exe so nothing would change.


(jenn) #8

What an amazing analysis of Malwarebytes. So much was over my head but your comprehension and tenacity is truly inspiring. We are all a little bit smarter thanks to you.


(dmknght) #9

That is a big Wow in 2019. Does Malwarebytes have any anti-debugging technique?



@jennlong Thank you for your kind words! Much appreciated.

@dmknght I’m not sure. This was done with a majority of static analysis and then blackbox dynamic testing to confirm.

1 Like


Someone dropped the ball hard…


(yong) #12

This post was flagged by the community and is temporarily hidden.


(yong) #13

This post was flagged by the community and is temporarily hidden.


(cuckkoo) #14

Malwarebytes is always tripping on stuff like this and from what i’ve experienced i think they created a database of known malwares and they look for the same exact code in the files they scan and that is very sad because no one is going to write exact same code again apart from script-kiddies.

I once scanned my two copies of same malware with slight changes and guess what one was detected and one wasn’t.

1 Like


This is normal antivirus behavior. It’s signature based scanning. And actually, Malwarebytes not detecting Eicar would indicate it doesn’t use signature based scanning.



I think you need to recheck your logic. It is entirely possible that Malwarebytes uses signature-based scanning but does not include the signatures for EICAR and hence, will not detect it. As far as I’m concerned, signature-based scanning is the fastest and efficient methods for detecting known malware and simply not including an engine capable of performing this would be an insult to optimisation.

What you are claiming is simply outrageous and I fail to see any relevant evidence to support it. I also don’t see any sources that explicitly state that Malwarebytes does not use signatures. I am aware that they have a “signature-less” detection method but just to make sure you don’t trip on another incorrect logical conclusion, it does not mean Malwarebytes doesn’t use signatures. From the same thread questioning this “signature-less” method, a staff member replies:

1 Like

closed #17

This topic was automatically closed after 30 days. New replies are no longer allowed.