Malware Anti-Analysis and Bypassing its Techniques
In continuation to the previous article on how malware can evade normal detections and how analysts can bypass those techniques now let's look at some even more complex and smarter detection employed by malware these days.
Let's start with the malware sample (text.exe) by infecting our test machine with it and notice some of the artifacts that it generates.
Become a certified reverse engineer!
From above screenshot, we can see that the process generates a child process of the same name.
From above, we can see that the malware specimen is trying to resolve a DNS name. (Please note that this is on the other machine where all the traffic from the infected machine is routed)
But as soon as we launch tools like Wireshark on the infected machines; we can see that the malicious processes start exiting (above screenshot) which gives an indication that code inside the specimen exits if it finds some known analysis tools to be present on the machine. Clever!
However, let's dig deep into this malware code functionality. Load this specimen inside IDA pro and as stated in the previous article go to names window to see for any strings. We can see a lot of strings corresponds to known analyst's tools like Wireshark, Sandbox, VirtualBox, etc. Before we devise a technique to bypass these detection mechanisms, let's first look at the code that is detecting these tools.
Let's start with detection of Wireshark. Double click on Wireshark.exe string to look for references
and then double click on the sub_408B1D to jump to the function location where the string is embedded.
Right below the Wireshark string, we can see that a function call to sub_408A28 is made. Let's jump to this function code and see what's inside it.
We can see that the code has made calls to CreateToolhelp32shnapshot, Process32First, Process32Next. As per MSDN, CreateToolhelp32shnapshot is used to capture all running processes, threads in system and Process32First is used to parse the first process listed from CreateToolhelp32shnapshot and Process32Next is used to parse the next process in the listing. In other words, this code is enumerating the running processes in the system.
Now code must be comparing the processes names it retrieves from the system to strings we saw above which means that strcmp function will be used. At 408A95 we can see a call to the strcmp function, and it compares strings to with what is passed as an argument. Remember that when this function is called right above a mov instruction was used to place the string 'Wireshark.exe' which is then used as an argument.
Appendix 1 confirms this understanding by running the specimen in OllyDBG.
It's always better to rename function whose functionality is discovered, so let's name the function at sub-408A28 to "CheckProcessPresence."
Remember above analysis is for string 'wireshark.exe' only but it's most likely that specimen must be calling the same function to check for other processes as well. Let's also rename all functions that reference "CheckProcessPresence."
Now since we have renamed the functions, let's look at the function which is calling all these functions. In this function code, you can see various references to other functions which we have just renamed. Let's rename this function to be "AnalysisToolsPresence."
To summarize what we did so far, let visualize via Graph in IDAPro.
Since we are backtracking function calls let's navigate to function which calls "AnalysisToolsPresence." Below is the code for this function.
In the code, we can see the reference to AnalysisToolsPresence, and then we see that the code is comparing the return value and if al is zero a jz instruction is executed to switch the execution to 408CE3 (This means that the analysis tools code is looking for is not present) otherwise if the return value is not zero then TerminateProcess function is called whose function is self-explanatory.
Now when it comes to bypass this detection mechanism there can be a lot of ways in which we can do it:
- Change the hardcoded strings so that they do not match the tools present on system
- Changing the JZ instruction to non-conditional instruction like JMP, so that TerminateProcess will never be called.
But there is another way in which this code can be bypassed. If you have been paying attention from the beginning you might have realized that this specimen keeps checking for running processes on the system and only halts the execution of itself if it found some known strings. This means that specimen is using some other thread to check for Analysis Tools on the system. Let's find if our assumption is correct?
Lets' navigate to the function which is calling the function at 408926.
We can see a call to CreateThread after call to 408CB4(stub where all the function calls are made) which means that our assumption is correct. If we can prevent from this thread being created that the analysis tools presence will not be checked. Let's load this code in OllyDBG.
Put a breakpoint at 408955 and run the specimen. We can disable the execution of CreateThread by patching this instruction with NOP (as is done in the previous article).
Please note that this specimen is patched in memory only. To save this patching to the executable on disk, copy all modifications to the executable and save the file.
After running this patched code, now even after running tools like Wireshark, the specimen continues its execution.
Appendix 1
Checking the assumption of cmp function, putting a breakpoint at 408A95 and then executing the specimen
Below are the contents of the stack and we can see that the code matches strings 'System Process'(one of the processes enumerated by code) with 'joeboxserver.exe'(one of the hard-coded string that code is checking).
Note that the code will run multiple times to match all the processes enumerated from a system with some hard-coded ones.