MalwareReversingThreat Intelligence

Reversing Insanity Protector

The idea came up one year ago when German (@enelpc) told me about reverse engineering his crypter Insanity Protector, at first I tried but I probably left it for knowledge but I’m going right back!.

We will use as final payload the windows calculator (calc.exe) in order to have a final reference of obtaining the final payload or the «malware sample» without the protection layers applied by Insanity Protector. As an anecdote I will use his 2017 public version.

Unpacking Protected binary

We open that binary with PE Studio and we realize that has an high entropy so probably that is some kind of packer.

The important point comes when the CreateProcessInternalW API is called. This API call allows the program to spawn a new process from itself, usually packers use this method to inject actual malware code in memory and execute them using CreateProcessInternalW. So we can use a debugger to place breakpoints on common unpacking functions: VirtualProtect, VirtualAlloc, WriteProcessMemory, CreateProcessInternalW, IsDebuggerPresent etc… (Malware Unpacking Methodology).

Once the breakpoints are set, we can see how the program stops in CreateProcessInternalW. We open process hacker and we would have the binary without the packer. We open the location of the file and the folder also contains a readme.txt with code that we don’t understand, probably some kind of obfuscation.

Currently we have x64dbg with EIP pointing to the NtResumeThread API, so giduf.exe process is suspended and our environment safe. We could open another debugger instance and attach the giduf.exe process, or simply close the current x64dbg instance and start our analysis with the new binary.

Analysis of giduf.exe and unpack final payload

Once the second binary is extracted, we open with PE bear to see the import table. We only have one library corresponding to Visual Basic. The msvbvm60.dll file is defined as the «Visual basic virtual machine v. 6.0» that enables «Microsoft Windows» to run applications written in «Visual Basic» programming language. «Visual Basic» as a programming language lets the programming engineer visually see where he/she puts components, for example buttons and such. Even windows is using the drag and drop technique provided by msvbvm60.dll.

This means that German developed his crypter in visual basic. We could use a Visual Basic decompiler and x64dbg to study crypter. Another option would be to unpack the final payload, without studying crypter, but that wouldn’t be the hard way, would it?. Once we open with the decompiler we can observe a function called CIS() within it we identify some Visual Basic API’s that can help us debug the program and thus understand the code obfuscated.

We are in 32 bits which means that if we put a breakpoint in CreateObject API we should see in the stack a pointer with the string shown in the previous image corresponding an argument.

With this methodology we could follow the decompiled code and at the same time debugging, isn’t it gorgeous?. The main objective is to understand the algorithm used for antivirus evasion and finally get the final payload without running on the machine (as we did with the previous one).

But if we put breakpoints in all API’s we come to the conclusion that the binary starts first by executing the ramol() function. And how do we know this? By the same methodology explained before. We see that inside the ramol() function we have a call to the jenov() function that is passed as an argument the Left API and we see the integer «2». Within the function it calls a time function and if we execute x64dbg, it stops at the jump to an API related to that GetPresentDate call.

If we evaluate the stack, that’s what we have! Good!

If we continue with the code we arrive at a loop where we see an interaction of the readme content by performing an arithmetic operation.

global_68 = CStr(Right(global_68, CLng((CDbl(Len(global_68)) - Val(CStr(2))))))
For var_114 = 1 To CVar(Len(global_68)): var_D4 = var_114 'Variant
  arg_0 = Mid$(CStr(Chr(CLng((Asc(CStr(Mid(global_68, CLng(var_D4), 1))) - &HD)))), 1, CLng(var_D4))
Next var_114 'Variant

The variable var_114 is a counter that we can locate once we debug the Mid API. We also identify the size of the file with a length of 0x1d1d4 (Although in the debugger we can identify the value 0x1d1d2, probably because you are not using the first two characters of the file «20»). It is also deduced that global_68 must be the readme file that calculates with Len function its length, being used as a limit in the interaction loop. Later, var_D4 is set with the counter value, starting with 0x1.

The Mid function allows us to obtain a character from the file according to its position. In this case, as the counter is 0x1, it would obtain the first character (according to the debugger) «O», since the first two characters of the file «20» are obviated.

Then select the character and perform a subtraction by substituting the first value of the readme. 66 in decimal is «B» in ASCII.

After some running we can realize that our new char is in memory, that’s good!.

And so this does all the iteration. Once it finishes we find another loop that interacts with the same buffer as the previous one divided by 2, that is, it will have less iteration size.

For var_164 = 1 To CVar((CDbl(Len(global_68)) / CDbl(2))): var_144 = var_164 'Variant
  var_A4 = CVar(Val("&H" & Mid$(global_68, CLng(((2 * var_144) - 1)), 2))) Xor CVar(Asc(Mid$("†", CLng(((var_144 Mod CVar(Len("†"))) + 1)), 1)))
  var_1A4 = (var_1A4 + Chr(CLng(var_A4))) 'Variant
Next var_164 'Variant

In the first part before running the XOR, perform Mid function with the previously modified buffer. When var_144 = 1, it is multiplied by 2 and 1 is subtracted, being basically Mid(Buff, 1, 2). That is, it will get the first two bytes of the previous buffer, being 0xbf.

In the other part of the XOR we see another Mid operation using a unicode character † U+2020, in memory we would see 0x2020.

It is interesting to put a breakpoint in the function vbaVarXor, and see the return value it has, since the return value will be the character of the result obtained when doing the XOR operation, and consequently the value of the variable var_A4.

If enter the VarXor function we can see with what value the XOR performs, being first 0xbf with 0x86. The last value is always the same throughout the loop.

With this information we can make our script to decode the content of the readme.txt.

file = open("readme.txt", "r")
stage1 = []
for line in file:
    for character in line:
        
        stage1.append(ord(character)-13)

stage2 = []


for i in range(int(len(stage1)/2)):
    try:
        stage2.append(int("0x"+str(chr(stage1[(i*2)+2])) + str(chr(stage1[((i*2)+2)+1])),16) ^ 0x86)
        
    except IndexError:
        pass
    
final = []
for x in range(len(stage2)):
    final.append(chr(stage2[x]))
print("Decode data:  --> "+"".join(str(final)))


with open('binary', 'w') as filehandle:
    for listitem in final:
        filehandle.write('%s' % listitem) 

Finally in the output of the file we generate, we get the final binary! Boom! Calc.exe unpacked.

Another way to unpack the final payload

You can realize at the end of the Visual Basic code an Unknown «function» that passes a global_92(0) variable to the VarPtr function. Simply if we put a breakpoint in that API and analyze the pointer in the stack, in the dump we would have our binary in memory without the packer and ready to save the binary in file.

Thanks for reading and any suggestions or questions contact me on twitter @naivenom