Encrypted Strings

As we’ve already got a nice list of functions called, we can look for those involved in string operations such as MultiByteToWideChar or CompareStringA in order to find encrypted strings. I went with CompareStringA as it takes two input strings so there’s a better chance of finding one that’s recently been decrypted.

I got lucky and the first call to CompareStringA I looked at appeared to be a wrapper function that the bot implements for easy string comparison (taking both strings as input parameters).

A simple string comparison function



By right clicking on the function and selecting “jump to xrefs to”, we can just pick one at random and trace back each of the two string arguments and see if any start off as encrypted.

This one is promising because var_24 (the second parameter to our CompareString function) is a stack variable which is not written to at any point during the function, the only reference is the lea eax, [var_24] in the excerpt above (so it must be written somewhere in sub_40C47C). Even more promising is the fact that right above the lea operation is a mov operation which moves a pointer to some non ASCII data into edx (looks encrypted); the assumption here is that the function sub_40C47C decrypts the data in the pointer stored in edx into the one stored in eax; but is that assumption correct?

Without going overboard with screenshots, I can tell you that the empty stack variable in eax is moved into ebx which is stored and restored by all functions called from inside sub_40C47 (that is, it doesn’t change at any point during the function until the end where its’ value is moved into eax and the original register is restored). If all assumptions and facts remain correct, we can put a breakpoint at the end of the function and eax should be a pointer to a string.

Whoever said assumptions get you nowhere?

If you’re not familiar with WinDbg the command “da poi(eax)” breaks down to “da” (display as ascii) “poi(eax)” (the address pointed to by the value in eax). Which should be (and is) our decrypted string!

Dridex actually uses both ASCII and Unicode strings but the function we have found only deals with ASCII, we could go back and look for a Unicode function called like CompareStringW then repeat the previous process, or we could do a binary search (ALT + B) of the function and hope to get another that looks similar. I liked the look of that “push 2800h; mov ebp, edx” so I grabbed the bytes using the WinDbg command “u 0x0040c485 L2” and entered them into the binary search (make sure to check find all occurrences).

Again, for those unfamiliar with WinDbg “u <address>” disassembles an address and “L2” tells the disassembler to only dissemble two instructions.

After a quick binary search we are presented with two identical functions (the one we’ve already found and another, the unicode version.), it’s almost too easy. All that’s left is to write a script similar to the one in the previous article and dump the encrypted string + the address the decrypter was called from.

In the previous article we went over how to dump the names of the majority of functions dridex resolves dynamically to complicate analysis. Today we will be using some similar methods to get the other main piece of the puzzle (encrypted string).

import idc import idautils import idaapi strings_info = [] def DumpStrings(): for string in strings_info: print("%s at 0x%X" % (string[1], string[0])) def BreakpointHandler(dec_string): call_loc = PrevHead(Dword(GetRegValue("ESP")), 0) string_info = (call_loc, dec_string) if string_info not in strings_info: strings_info.append(string_info) print("Got string: %s @ %x" % (dec_string, call_loc)) def BreakpointHandlerAscii(): dec_string = GetString(Dword(GetRegValue("EAX")), -1, ASCSTR_C) BreakpointHandler(dec_string) def BreakpointHandlerUnicode(): dec_string = GetString(Dword(GetRegValue("EAX")), -1, ASCSTR_UNICODE) BreakpointHandler(dec_string) def main(): func_ascii = 0x0040C4A9 #Last byte of the ascii function func_unicode = 0x0040F1C9 #Last byte of the unicode decrypted function #Lets us use python functions for breakpoint conditions RunPlugin("python", 3) AddBpt(func_ascii) SetBptCnd(func_ascii, "BreakpointHandlerAscii()") print("Breakpoint at: %x" % func_ascii) AddBpt(func_unicode) SetBptCnd(func_unicode, "BreakpointHandlerUnicode()") print("Breakpoint at: %x" % func_unicode) if __name__ == '__main__': main() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import idc import idautils import idaapi strings_info = [ ] def DumpStrings ( ) : for string in strings_info : print ( "%s at 0x%X" % ( string [ 1 ] , string [ 0 ] ) ) def BreakpointHandler ( dec_string ) : call_loc = PrevHead ( Dword ( GetRegValue ( "ESP" ) ) , 0 ) string_info = ( call_loc , dec_string ) if string_info not in strings_info : strings_info . append ( string_info ) print ( "Got string: %s @ %x" % ( dec_string , call_loc ) ) def BreakpointHandlerAscii ( ) : dec_string = GetString ( Dword ( GetRegValue ( "EAX" ) ) , - 1 , ASCSTR_C ) BreakpointHandler ( dec_string ) def BreakpointHandlerUnicode ( ) : dec_string = GetString ( Dword ( GetRegValue ( "EAX" ) ) , - 1 , ASCSTR_UNICODE ) BreakpointHandler ( dec_string ) def main ( ) : func_ascii = 0x0040C4A9 #Last byte of the ascii function func_unicode = 0x0040F1C9 #Last byte of the unicode decrypted function #Lets us use python functions for breakpoint conditions RunPlugin ( "python" , 3 ) AddBpt ( func_ascii ) SetBptCnd ( func_ascii , "BreakpointHandlerAscii()" ) print ( "Breakpoint at: %x" % func_ascii ) AddBpt ( func_unicode ) SetBptCnd ( func_unicode , "BreakpointHandlerUnicode()" ) print ( "Breakpoint at: %x" % func_unicode ) if __name__ == '__main__' : main ( )