Aerosol Posted December 13, 2014 Report Posted December 13, 2014 IntroductionIn this article we will look at malware that propagate to other machines using USB removable devices. Unlike most malware which make use of vulnerable Network Services to spread to other machines in the network, these malware are specifically designed to infect USB removable devices.We will discuss in depth the methods used by a malware to automatically detect any USB removable device connected to the machine and then infect it. The infection routine is a topic for another article; here, we will just analyze the techniques used for detection of removable USB devices.Such techniques have been used in malware like Stuxnet and Conficker. These malware make extensive use of Windows Messages and the Win32 APIs related to them. A good understanding of these APIs would be helpful while reading this article.Threads CreationMalware creates 3 threads as shown below:Thread #1: This is used to create the Registry Keys used by the malware to make it persistent even after the OS is rebooted.Thread #2: This thread has two main subroutines as shown below.Monitor Windows MessagesFirst, let us check the second Thread:CMP BYTE PTR DS:[C20941],0JNZ SHORT 00C18CDACMP BYTE PTR DS:[C20942],0JNZ SHORT 00C18CDACALL 00C18CE5 ; To find if any removable device is already connected to the machine and then infect itCALL 00C18DBB ; To set up a handler that will detect if any removable device is connected to the machineWe will analyze the subroutine at 00C18DBB which is used to create a Window for monitoring the WM_DEVICECHANGE message.Here is the subroutine at 00C18DBB:The lines of code below are used to initialize the Local Variables with the GUID (Globally Unique Identifier) of a removable USB device: {53f5630d-b6bf-11d0-94f2-00a0c91efb8b}MOV DWORD PTR SS:[EBP-10],53F56307MOV WORD PTR SS:[EBP-C],0B6BFMOV WORD PTR SS:[EBP-A],11D0MOV BYTE PTR SS:[EBP-8],94MOV BYTE PTR SS:[EBP-7],0F2MOV BYTE PTR SS:[EBP-6],BLMOV BYTE PTR SS:[EBP-5],0A0MOV BYTE PTR SS:[EBP-4],0C9MOV BYTE PTR SS:[EBP-3],1EMOV BYTE PTR SS:[EBP-2],0FBMOV BYTE PTR SS:[EBP-1],8BIn the memory dump, it will look like as shown below:0148FFA0 07 63 F5 53 BF B6 D0 11 94 F2 00 A0 C9 1E FB 8B cõS¿¶Ð”ò. Éû‹The next 2 lines of code are used to initialize the values that correspond to the NotificationFilter’s DEV_BROADCAST_HDR structure. We will look into this in more depth when we analyze the call to RegisterDeviceNotificationA API.MOV DWORD PTR SS:[EBP-3C],20MOV DWORD PTR SS:[EBP-38],5After this we have:The lines of code below are used to register a new Class with the name, gdkWindowTopClass and the WindowProc routine at address, 0C18EC7.MOV DWORD PTR SS:[EBP-6C],30MOV DWORD PTR SS:[EBP-68],EBXMOV DWORD PTR SS:[EBP-64],0C18EC7 ; Pointer to WindowProc routineMOV DWORD PTR SS:[EBP-60],EBXMOV DWORD PTR SS:[EBP-5C],EBXMOV DWORD PTR SS:[EBP-58],EBXMOV DWORD PTR SS:[EBP-54],EBXMOV DWORD PTR SS:[EBP-50],EBXMOV DWORD PTR SS:[EBP-4C],EBXMOV DWORD PTR SS:[EBP-48],EBXMOV DWORD PTR SS:[EBP-44],0C1F8D4 ; ASCII "gdkWindowTopClass"MOV DWORD PTR SS:[EBP-40],EBXMOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]CALL DWORD PTR DS:[C1C2B0] ; USER32.RegisterClassExAOnce the class is registered, it will create a Window with that class name:PUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH EBXPUSH 0C1F8D4 ; ASCII "gdkWindowTopClass"PUSH EBXCALL DWORD PTR DS:[C1C2B4] ; USER32.CreateWindowExACreateWindowExA will create a window with the class name gdkWindowTopClass and return a handle to the newly created window.However, before returning, CreateWindow will send WM_CREATE, WM_GETMINMAXINFO, WM_NCCREATE and some more messages to the Window Procedure corresponding to the Class Name.Here is a list of window messages which will help us in analyzing the Window Procedure:List Of Windows Messages - The Official Wine WikiWindow ProcedureWe need to set a breakpoint at the WindowProc address (in this case, 0C18EC7) and we break at this subroutine once we execute the call to CreateWindowExA as shown below:Once we break at this Window Procedure, the stack looks like as shown below:Here the value 0×24 corresponds to the Window Message, WM_GETMINMAXINFO.As mentioned above, CreateWindowExA will pass a few Window Messages which need to be processed by the Window Procedure before the Window is created.Here is a description of the Window Procedure code:PUSH EBPMOV EBP,ESPSUB ESP,13CAND DWORD PTR SS:[EBP-38],0PUSH ESIPUSH EDIPUSH 0AMOV ESI,DWORD PTR SS:[EBP+8]POP ECXXOR EAX,EAXLEA EDI,DWORD PTR SS:[EBP-34]REP STOS DWORD PTR ES:[EDI]MOV EDI,DWORD PTR SS:[EBP+C] ; EDI will hold the code of the Window MessageCMP EDI,1 ; Check if it is a WM_CREATE messageJNZ SHORT 00C18F02PUSH EAXMOV DWORD PTR SS:[EBP-C],0C ; Size of the Structure passed to RegisterDeviceNotificationAMOV DWORD PTR SS:[EBP-8],5 ; Corresponds to the device type (in this case, DBT_DEVTYP_DEVICEINTERFACE)LEA EAX,DWORD PTR SS:[EBP-C]JMP 00C18FA9CMP EDI,219 ; Check if it is a WM_DEVICECHANGE messageJNZ 00C18FD0CMP DWORD PTR SS:[EBP+10],8000 ; Check if the Device Event is DBT_DEVICEARRIVALJNZ 00C18FB3MOV EAX,DWORD PTR SS:[EBP+14]CMP DWORD PTR DS:[EAX+4],2 ; Check if the device type is a Logical Volume DBT_DEVTYP_VOLUMEJNZ 00C18FEBPUSH DWORD PTR DS:[EAX+C] ; dbcv_unitmask corresponding to the Drive Letter of the Logical VolumeCALL 00C18FFFMOVSX EAX,AL ; AL will hold the ASCII value of the Drive Letter assigned to the removable devicePUSH EAXLEA EAX,DWORD PTR SS:[EBP-13C]PUSH 0C1F8E8 ; ASCII "%c:"PUSH EAXCALL 00C1B89C ; JMP to msvcrt.sprintfIf the Window Message is not equal to either of the values defined above then it will call the default Window Proc, DefWindowProc, with the same parameters that were passed to the WindowProc routine.As can be seen from the code above, the Window Procedure can handle the WM_CREATE and WM_DEVICECHANGE messages. If there is any other Window Message then it will be passed to the default window procedure, DefWindowProc.The return value of DefWindowProc will depend on the message that was processed by the DefWindowProc routine.Stack arguments for the call to DefWindowProcA:For example, the stack arguments when we return from DefWindowProc after processing the message WM_GETMINMAXINFO are:Here, 0×81 corresponds to the WM_NCCALCSIZE message.This message is again processed by DefWindowProc since the Window Procedure does not handle this Window Message.Stack arguments after the WM_NCCALCSIZE message is processed:0148F7DC 7E418734 RETURN to USER32.7E4187340148F7E0 005302800148F7E4 00000001 // WM_CREATE0148F7E8 000000000148F7EC 0148F928WM_CREATE is the last Window Message processed by WindowProc before creating the Window and returning.The section of code below will be executed when the window message is WM_CREATE:CMP EDI,1JNZ SHORT 00C18F02PUSH EAXMOV DWORD PTR SS:[EBP-C],0C ; size of the structureMOV DWORD PTR SS:[EBP-8],5 ; device typeLEA EAX,DWORD PTR SS:[EBP-C]JMP 00C18FA9This will set the values for the standard header used in a device event.PUSH EAXPUSH ESICALL DWORD PTR DS:[C1C2B8] ; USER32.RegisterDeviceNotificationARegisterDeviceNotificationA will return the Device Notification Handle if the call succeeds, or else it returns a NULL value.The stack arguments:The second argument on the stack above is a pointer to a data structure that specifies the type of Device for which notifications will be sent to the Window Procedure.This data structure in our case looks like:If we compare this with the DEV_BROADCAST_HDR structure:typedef struct _DEV_BROADCAST_HDR { DWORD dbch_size; DWORD dbch_devicetype; DWORD dbch_reserved;} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;We see that in our case, the size of the structure is 0xC bytes and the type of the device is DBT_DEVTYP_DEVICEINTERFACE (corresponding to the value 0×5).Once we return from the RegisterDeviceNotificationA routine, we see that the value of the EAX register is 0 (the return value) which means that no Device Notification handle was returned.After this, the Window Procedure processes the WM_CREATE message as shown below:And returns the handle of the newly created Window:The handle to the Window in our case is: 0x002D032C.Device Notification HandlerNow, we have another call to the RegisterDeviceNotificationA API as shown below:The stack arguments are:The second argument on the stack points to the following data structure:If we again compare the above data structure with _DEV_BROADCAST_HDR,we can see that the size of the structure is 0×20 bytes and the device type for which notifications will be sent is: DBT_DEVTYP_DEVICEINTERFACE.Also, we can see the device GUID present in this structure: 53f5630d-b6bf-11d0-94f2-00a0c91efb8b.Once we return from the call to RegisterDeviceNotificationA API, the Device Notification Handle is stored in the EAX register as shown below:In our case, the handle is 0x000E3738. This confirms that the call to RegisterDeviceNotificationA was successful.WM_DEVICECHANGE HandlerAfter this, the code will use GetMessage API to pop one message at a time from the Thread’s message queue.The stack arguments are:GetMessage takes only one argument which is a pointer to the MSG Structure that will be populated with the information about the Window Message retrieved from the Thread’s message queue.If there are no messages in the Message Queue of the current Thread, then the execution will be passed to the other Thread.For instance, at present I did not connect any USB removable device to the machine in between the execution of the Thread, and there are no messages in the Thread’s queue. As a result of this, after we execute the call to GetMessageA, we break at the new Thread (in our case, Thread #3) as shown below:In order to analyze further, I connected a USB removable storage device to the machine while the second Thread was active.Now, when the call to GetMessage is executed it will populate the MSG structure at, 0148FF94 with the WM_DEVICECHANGE message information (since I connected a USB removable device):This is how the MSG structure looks like:typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt;} MSG, *PMSG, *LPMSG;In our case, 0x0036027C is the handle to the Window created before (this handle is different from the one mentioned previously because I started another debug session).0×0219 is the code corresponding to the Window Message retrieved by GetMessage from the Thread’s queue.The next parameters depend on the type of Message retrieved from the Queue.In our case, the message is WM_DEVICECHANGE which has the following structure:LRESULT CALLBACK WindowProc(HWND hwnd, // handle to window UINT uMsg, // WM_DEVICECHANGE WPARAM wParam, // device-change event LPARAM lParam ); // event-specific dataIn our case the device change event is 0×7, which corresponds to DBT_DEVNODES_CHANGED meaning a device has been added or removed from the system.It is important to note that the GUID of the USB removable device I have connected to the machine is not the same as the GUID registered by RegisterDeviceNotificationA (that is, {53f5630d-b6bf-11d0-94f2-00a0c91efb8b}).You can check the Device GUID for the removable device connected to the machine by looking up the registry key:HKEY_LOCAL_MACHINESYSTEMCurrentControlSetEnumUSBSTORAs a result of this, WM_DEVICECHANGE structure is not the same as what the code expects it to be (we will look into this further).After this, TranslateMessage and DispatchMessage are called which will dispatch this message to the Window Procedure corresponding to the Window as shown below:The arguments on the stack:It passes a pointer to the WM_DEVICECHANGE structure to the Window Procedure.Once the call to DispatchMessage is executed, we break at the WindowProc routine again:Stack arguments are as shown below:We will perform static code analysis of the section of code that is executed when the type of message is WM_DEVICECHANGE:Let us understand these in depth:CMP EDI, 219EDI points to the value at EBP+C which is the message code. In our case, it is WM_DEVICECHANGE (0×219).CMP DWORD PTR SS:[EBP+10],8000This is the next value on the stack. If we compare this with the WM_DEVICECHANGE structure, we see that it corresponds to the device event.0×8000 is the device event for: DBT_DEVICEARRIVAL (a device or piece of media has been inserted and is now available).MOV EAX, DWORD PTR SS:[EBP+14]It is moving the pointer to event specific data in EAX (referred to as lparam in the WM_DEVICECHANGE structure above).This is the DEV_BROADCAST_HDR structure:typedef struct _DEV_BROADCAST_HDR { DWORD dbch_size; DWORD dbch_devicetype; DWORD dbch_reserved;} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;CMP DWORD PTR DS:[EAX+4], 2The second DWORD in the structure above corresponds to dbch_devicetype. It is comparing this with 2 (DBT_DEVTYP_VOLUME).If the device type connected is a Logical Volume, then it parses the DEV_BROADCAST_VOLUME structure to find the unitmask corresponding to the Logical Volume drive letter.PUSH DWORD PTR DS:[EAX+C]typedef struct _DEV_BROADCAST_VOLUME { DWORD dbcv_size; DWORD dbcv_devicetype; DWORD dbcv_reserved; DWORD dbcv_unitmask; WORD dbcv_flags;} DEV_BROADCAST_VOLUME, *PDEV_BROADCAST_VOLUME;Unitmask is the bit sequence corresponding to the drive letter.Next, it calls the subroutine at 00C18FFF which is used to convert the unitmask to the corresponding Drive Letter's ASCII value.Once we have the Drive Letter of the USB removable device connected to the machine that triggered the WM_DEVICECHANGE message, we find the infection routine at 00C190D3.It is important to note that this infection routine is the same for both the cases, infecting an already connected removable device and a newly connected removable device. Below you can see the first few lines of code of the infection subroutine:Enumeration of Logical DrivesNow, let us analyze the first subroutine of the second thread at address 0x00C18CE5. This subroutine will help us understand how the malware finds out if any removable device is already connected to the machine before it proceeds to infect it.The code first calls GetLogicalDriveStringsA to find out all the Logical Volumes already connected and accessible to the machine:Stack arguments are:Once the call to GetLogicalDriveStringsA has executed, all the Drive Letters will be populated in the buffer at address, 00148FEA4.It then checks each drive letter in the list above to find any USB removable device already connected to the machine:CALL DWORD PTR DS:[C1C0DC] ; kernel32.GetLogicalDriveStringsATEST EAX,EAXJE SHORT 00C18DB7PUSH EBXLEA EBX,DWORD PTR SS:[EBP-10C] ; pointer to output buffer of GetLogicalDriveStringsAMOV AL,BYTE PTR DS:[EBX] ; AL will hold the ASCII value of the drive letterMOV BYTE PTR SS:[EBP-8],ALLEA EAX,DWORD PTR SS:[EBP-8]PUSH EAX ; pointer to the Drive LetterCALL 00C19016USB Device DetectionLet us analyze the subroutine at 00C19016:It opens a handle to the device name corresponding to the Drive Letter. A device name must be in the format, \.Device Name, in order to open a handle to it using CreateFileA API.So, it allocates memory using calloc and then uses sprintf to prepare the device name as shown below (in this case, \.C:)Below is the call to CreateFileA which will open a handle to the device:Once this call is executed successfully, we have the handle to the device name: \.C:.Next, it calls DeviceIOControl to query the device handle retrieved above:Stack arguments are:Here, 0×164 is the handle corresponding to the device: \.C:Here is an explanation of the code:PUSH 0C ; size of the Input BufferPUSH EAXPUSH 2D1400 ; IO Control Code corresponding to MASS_STORAGEPUSH ESIMOV DWORD PTR SS:[EBP+8],EBXMOV DWORD PTR SS:[EBP-10],EBXMOV DWORD PTR SS:[EBP-C],EBXCALL DWORD PTR DS:[C1C15C] ; kernel32.DeviceIoControlPUSH ESIMOV EDI,EAXCALL DWORD PTR DS:[C1C158] ; kernel32.CloseHandleThe control code passed to the DeviceIOControl subroutine above is 0x2D1400. To understand the meaning of this control code, we can use the following site:Downloads:OSR Online IOCTL DecoderIt allows you to enter any IOCTL value in hex format and then tells you the corresponding decoded value.In our case, 0x2D1400 corresponds to MASS_STORAGE or the mnemonic IOCTL_STORAGE_QUERY_PROPERTY.If we look up the above IOCTL code on MSDN here:IOCTL_STORAGE_QUERY_PROPERTY control code (Windows)We see that the value of Output Buffer depends on the Input Buffer. In our case, the Input Buffer is:And it has a size of 0xC bytes.The input buffer points to the STORAGE_QUERY_PROPERTY structure as shown below:typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; BYTE AdditionalParameters[1];} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;If we compare this structure with the Input Buffer above, then we can see that the value of the PropertyID is 0 which corresponds to StorageDeviceProperty.More details here:STORAGE_PROPERTY_QUERY structure (Windows)The output buffer corresponding to StorageDeviceProperty is STORAGE_DEVICE_DESCRIPTOR as documented here.Now, we have the format of the Output Buffer:typedef struct _STORAGE_DEVICE_DESCRIPTOR { DWORD Version; DWORD Size; BYTE DeviceType; BYTE DeviceTypeModifier; BOOLEAN RemovableMedia; BOOLEAN CommandQueueing; DWORD VendorIdOffset; DWORD ProductIdOffset; DWORD ProductRevisionOffset; DWORD SerialNumberOffset; STORAGE_BUS_TYPE BusType; DWORD RawPropertiesLength; BYTE RawDeviceProperties[1];} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;Once the call to DeviceIOControl has been executed, the output buffer at 0148FA7C looks like as shown below:Comparing the output buffer with the structure of STORAGE_DEVICE_DESCRIPTOR, we have:Version: 0×28Size: 0x9CSTORAGE_BUS_TYPE: 0×3At offset, 0x1C in the Output Buffer, we have the STORAGE_BUS_TYPE.After returning from the call to DeviceIOControl we have the following section of code:PUSH ESIMOV EDI,EAXCALL DWORD PTR DS:[C1C158] ; kernel32.CloseHandleCMP EDI,EBXPOP EDIJE SHORT 00C190C3CMP DWORD PTR SS:[EBP-3F4],7JNZ SHORT 00C190C3MOV BL,1PUSH DWORD PTR SS:[EBP-4]CALL 00C1B94C ; JMP to msvcrt.freePOP ECXPOP ESIMOVZX EAX,BLPOP EBXLEAVERETNIt closes the Device Handle and then compares the STORAGE_BUS_TYPE with 0×7.The STORAGE_BUS_TYPE enum is defined here:STORAGE_BUS_TYPE enumeration (Windows)The value corresponding to 0×7 is BusTypeUSB.It means, that the code is checking whether the Drive Letter corresponds to a USB removable device or not.In the code above, we can see that it will set the value of EAX to 1 if it finds a USB removable device. After returning from this subroutine, it checks the value of EAX and if it is not 0, it then calls the infection subroutine at 0x00C190D3.It is important to note that this is the same infection routine that was called above when a USB removable device is connected to the machine and triggers the WM_DEVICECHANGE event.ConclusionAfter reading this article, you should be able to identify malware which make use of similar techniques to detect and infect USB removable devices.It will also help in writing signatures to detect malware which make use of the techniques discussed in this article.ReferencesMSDN-the microsoft developer networkSource Quote