The goal of this writeup is to create an additional layer of defense versus analysis.
A lot of malwares utilize this technique in order for the binary analysis make more harder.
Polymorphism is an important concept of object-oriented programming. It simply means more than one form. That is, the same entity (function or operator) behaves differently in different scenarios
www.programiz.com
We can implement polymorphism in C++ using the following ways:
Now, let’s get it working. For this article, we are using a basic class named HEAVENSGATE_BASE and HEAVENSGATE.
Then we will be calling a function on an Instantiated Object.
Normal Declarations
When we examine the function call (Fig2) under IDA, we get the result of:
and when we cross-reference the functions, we will see on screen:
The xref on the .rdata is a call from VirtualTable of the Instantiated object. And the xref on the InitThread is a call to the function (Fig2).
Basic Obfuscation
So, how do we apply basic obfuscation?
We just need to change the declaration of Object to be the “_BASE” level.
Unlike earlier, the pointer points to a class named HEAVENSGATE. But this time we will be using the “_BASE”.
Under the IDA, we can see the following instructions:
Well, technically, it isn’t obfuscated. But the thing is, when an analyzer doesn’t have the .pdb file which contains the symbols name, then it will be harder to follow the calls and purpose of a certain call without using debugger.
This disassembly shows exactly what is going on under the hood with relation to polymorphism. For the invocations of function, the compiler moves the address of the object in to the EDX register. This is then dereferenced to get the base of the VMT and stored in the EAX register. The appropriate VMT entry for the function is found by using EAX as an index and storing the address in EDX. This function is then called. Since HEAVENSGATE_BASE and HEAVENSGATE have different VMTs, this code will call different functions — the appropriate ones — for the appropriate object type. Seeing how it’s done under the hood also allows us to easily write a function to print the VMT.
We can now just see that the direct call (in comparison with Fig5) is now gone. Traces and footprints will be harder to be traced.
Conclusion
Dividing the classes into two: a Base and the Original class, is a time consuming task. It also make the code looks ugly. But somehow, it can greatly add protection to our binary from analysis.