TR-24 Analysis - Destory RAT family


CIRCL analyzed a malware sample which was only sporadically detected by just a handful antivirus engines, based on heuristic detection. CIRCL analyzed the entire command structure of the malware and was able to attribute this specific malware to the Destory RAT family. The malware is a feature-rich Remote Access Tool.

The malware is used by a specific group of attackers specialized in industrial espionage starting from 2007 (Command Five). CIRCL published this report about Destory RAT family due to the regular confusion with the PlugX malware family. PlugX and Destory RAT malware are technically different for their respective initialization phase, utilized obfuscation techniques and other parts that will be outlined in this document, showing that both families are initially coming from the same malware writers, following the same internal and network communication protocols and using the same code for the vast majority of the code.

All known malware family members (PlugX, Gulpix, Korplug, Destory, Thoper, Sogu, TVT) are briefly discussed in this document, showing differences and similarities that could lead to the assumption that an initial code base has been shared among different teams and used/enhanced for different purposes.

Static Analysis

Sample A


Type Hash
MD5 801389d08baa4144018460fbe95da5ea
SHA1 c1f8738b3d7ef40177becc0ffde9321a03ef961a
SHA-256 217fe60d2ecea69055f93e86225e3596709f2e1baf458476d340726fdc8d5653
ssdeep 3072:G1705dXa7hDQiO1rQVHQ9haRPvo7oAfuSA2GR2N+S8f9:GNcdXL1rQVHehaRPvo7oAW8kQO9

VirusTotal results for sample A

No detections

File characteristics

Meta data

Name:    win3dx.DLL
Size:    206848 bytes
Type:    PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
Date:    0x4E5DB013 [Wed Aug 31 03:52:51 2011 UTC]
EP:      0x10001b10 .text 0/5
CRC:     Claimed: 0x0, Actual: 0x32b19 [SUSPICIOUS]

Resource entries

We will not speculate about the following, instead we leave it to the reader to make an educated guess (or to discuss the possible scenarios):

Name               RVA      Size     Lang         Sublang                  Type

Version info

LegalCopyright: Copyright (C) 2011
InternalName: SafeSvc.exe
CompanyName: SafeSvc
ProductName: SafeSvc
FileDescription: SafeSvc
OriginalFilename: SafeSvc.exe
Translation: 0x0409 0x04b0


Entropy analysis by section reveals high amount of randomness in .data and .rdata section, which is an indicator for compressed/encrypted content, which later turns out to be the case.

Name       VirtAddr     VirtSize     RawSize      Entropy
.text      0x1000       0x2a29e      0x2a400      6.392526
.rdata     0x2c000      0x3f36       0x4000       7.058395    [SUSPICIOUS]
.data      0x30000      0x6aa2e      0xa00        7.302812    [SUSPICIOUS]
.rsrc      0x9b000      0x3e0        0x400        3.064398
.reloc     0x9c000      0x31d6       0x3200       6.426904
SECTION 1 (.text   ):
                virtual size                  : 0002A29E ( 172702.)
                virtual address               : 00001000
                section size                  : 0002A400 ( 173056.)
                offset to raw data for section: 00000400
                offset to relocation          : 00000000
                offset to line numbers        : 00000000
                number of relocation entries  : 0
                number of line number entries : 0
                alignment                     : 0 byte(s)
                Flags 60000020:
                  text only
SECTION 2 (.rdata  ):
                virtual size                  : 00003F36 (  16182.)
                virtual address               : 0002C000
                section size                  : 00004000 (  16384.)
                offset to raw data for section: 0002A800
                offset to relocation          : 00000000
                offset to line numbers        : 00000000
                number of relocation entries  : 0
                number of line number entries : 0
                alignment                     : 0 byte(s)
                Flags 40000040:
                  data only
SECTION 3 (.data   ):
                virtual size                  : 0006AA2E ( 436782.)
                virtual address               : 00030000
                section size                  : 00000A00 (   2560.)
                offset to raw data for section: 0002E800
                offset to relocation          : 00000000
                offset to line numbers        : 00000000
                number of relocation entries  : 0
                number of line number entries : 0
                alignment                     : 0 byte(s)
                Flags C0000040:
                  data only
SECTION 4 (.rsrc   ):
                virtual size                  : 000003E0 (    992.)
                virtual address               : 0009B000
                section size                  : 00000400 (   1024.)
                offset to raw data for section: 0002F200
                offset to relocation          : 00000000
                offset to line numbers        : 00000000
                number of relocation entries  : 0
                number of line number entries : 0
                alignment                     : 0 byte(s)
                Flags 40000040:
                  data only
SECTION 5 (.reloc  ):
                virtual size                  : 000031D6 (  12758.)
                virtual address               : 0009C000
                section size                  : 00003200 (  12800.)
                offset to raw data for section: 0002F600
                offset to relocation          : 00000000
                offset to line numbers        : 00000000
                number of relocation entries  : 0
                number of line number entries : 0
                alignment                     : 0 byte(s)
                Flags 42000040:
                  data only

Export table

Flags         : 00000000
Time stamp    : Wed Aug 31 05:52:51 2011
Version       : 0.0
DLL name      : Rfu.
Ordinals base : 1. (00000001)
# of Addresses: 18. (00000012)
# of Names    : 18. (00000012)

  1. 00001DE0 SdxdAnr
  2. 00001E10 SdxdCukhl
  3. 00001C40 SdxdDlfisr
  4. 00001C10 SdxdEfck
  5. 00001E70 SdxdFcfgy
  6. 00001DF0 SdxdFunq
  7. 00001CD0 SdxdGivnlc
  8. 00001E60 SdxdLj
  9. 00001C00 SdxdLjykq
 10. 00001C20 SdxdMs
 11. 000019F0 SdxdPv
 12. 00001E40 SdxdPyyiw
 13. 00001DD0 SdxdRhio
 14. 00001BE0 SdxdTvdxj
 15. 00001E80 SdxdUdfae
 16. 00001E20 SdxdWmnq
 17. 00001BF0 SdxdWqn
 18. 00001D40 SdxdZbvge

Import table

Time stamp    : 00000000: not bound
ForwarderChain: 00000000
DLL name      : 0002F94E: KERNEL32.dll
Name table    : 0002F5E4
Address table : 0002C088
 1. hint=0125 FileTimeToSystemTime
 2. hint=0203 GetLocalTime
 3. hint=0447 SetConsoleTitleA
 4. hint=0107 EnumSystemCodePagesA
 5. hint=02CF HeapFree
 6. hint=0003 AddAtomA
 7. hint=04CB TransmitCommChar
 8. hint=023C GetPrivateProfileIntW
 9. hint=0060 CompareFileTime
10. hint=02CC HeapCompact
11. hint=0098 CreateMailslotA
12. hint=0484 SetProcessWorkingSetSize
13. hint=0424 SetCommMask
14. hint=04B2 Sleep
15. hint=0069 ConvertDefaultLocale
16. hint=0156 FlushConsoleInputBuffer
17. hint=02DC InitAtomTable
18. hint=041E SetCalendarInfoA
19. hint=039A PulseEvent
20. hint=0204 GetLocaleInfoA
21. hint=01C8 GetDateFormatW
22. hint=015C FoldStringW
23. hint=033C LoadLibraryA
24. hint=0245 GetProcAddress
25. hint=01E7 GetFileAttributesExW
26. hint=00FA EnumLanguageGroupLocalesW
27. hint=008F CreateFileW
28. hint=0202 GetLastError
29. hint=0466 SetFilePointer
30. hint=0525 WriteFile
31. hint=0052 CloseHandle
32. hint=0531 WriteProfileStringA
33. hint=015A FlushViewOfFile
34. hint=02F7 IsBadReadPtr
35. hint=0293 GetTickCount

Time stamp    : 00000000: not bound
ForwarderChain: 00000000
DLL name      : 0002FADA: USER32.dll
Name table    : 0002F674
Address table : 0002C118
 1. hint=00EF EnumThreadWindows
 2. hint=0220 OemKeyScan
 3. hint=0265 ReleaseDC
 4. hint=0007 AnimateWindow
 5. hint=010E GetClassInfoW
 6. hint=0112 GetClassNameW
 7. hint=02CA SetWindowTextA
 8. hint=031D ValidateRgn
 9. hint=01A3 GetWindowTextW
10. hint=014B GetMenu
11. hint=00CE DrawTextExA
12. hint=011B GetClipboardViewer
13. hint=009B DefWindowProcA
14. hint=0113 GetClassWord
15. hint=00F8 FindWindowExA
16. hint=0009 AppendMenuA
17. hint=027E SendNotifyMessageW
18. hint=0032 CharPrevA
19. hint=0025 ChangeDisplaySettingsExW
20. hint=0037 CharToOemBuffW
21. hint=0329 WinHelpW
22. hint=010D GetClassInfoExW
23. hint=015F GetMonitorInfoW

Time stamp    : 00000000: not bound
ForwarderChain: 00000000
DLL name      : 0002FBF4: GDI32.dll
Name table    : 0002F5A8
Address table : 0002C04C
 1. hint=01D2 GetEnhMetaFileDescriptionA
 2. hint=021B GetTextExtentExPointW
 3. hint=022D GetWorldTransform
 4. hint=02A4 SetTextAlign
 5. hint=0221 GetTextExtentPointW
 6. hint=0267 ResetDCW
 7. hint=02B5 StrokeAndFillPath
 8. hint=002E CreateColorSpaceW
 9. hint=0290 SetICMProfileW
10. hint=023E OffsetViewportOrgEx
11. hint=0200 GetPaletteEntries
12. hint=01A7 GetBitmapBits
13. hint=0041 CreateFontW
14. hint=0253 PolyPolyline

Time stamp    : 00000000: not bound
ForwarderChain: 00000000
DLL name      : 0002FD94: ADVAPI32.dll
Name table    : 0002F55C
Address table : 0002C000
 1. hint=022F ReadEventLogW
 2. hint=0176 InitializeAcl
 3. hint=0021 AllocateLocallyUniqueId
 4. hint=01EB ObjectDeleteAuditAlarmA
 5. hint=0147 GetSecurityDescriptorControl
 6. hint=0285 RegisterServiceCtrlHandlerA
 7. hint=0175 ImpersonateSelf
 8. hint=0051 CheckTokenMembership
 9. hint=00FC EnumDependentServicesA
10. hint=00D8 DecryptFileW
11. hint=0136 GetLengthSid
12. hint=0231 RegConnectRegistryA
13. hint=0180 IsTextUnicode
14. hint=003B BackupEventLogW
15. hint=02B6 SetSecurityDescriptorDacl
16. hint=02B9 SetSecurityDescriptorRMControl
17. hint=0158 GetSidSubAuthorityCount
18. hint=023D RegDeleteKeyA


The strings embedded in clear text are corresponding to the import and export section’s names as well as the DLL name itself (‘Rfu.’). This indicates that the remaining strings are encrypted/encoded in some or other way.


Analysis of Sample A

Initialization phase I

In a first initialization phase, the malware performs the following actions:

  1. resolving addresses of wsprintfA and wsprintfW (which is used early for the exception handling in (6))
  2. determining system and operational directories (in dependency of operating system)
  3. determining filename of itself (from the running image)
  4. setting global write Event (used for threat synchronization)
  5. reading config (including hostname of command and control server and file/server name)
  6. setting exception handler

Point 5 is rather interesting: if the malware finds out to be run in demo mode, it would pop-up a message box saying ‘Config Destory’ (as in the incorrect spelling). This is where the malware family name is coming from. PlugX does a similar thing, calling a message box saying “THIS IS A DEMO VERSION!!!”.

Characteristical string decryption

In contrast to many other malicious software that achieves to hide self-revealing strings from the analyst, almost every single string that is being used within this malware is decrypted during runtime, on-the-fly, only when explicitly needed and it is taken care that the decrypted string is wiped immediately after its use.

Example: dec_string=decrypt(enc_string) -> pAddress=GetProcAddress(dec_string) -> wipe(dec_string)

  decrypted_str_wsprintfA = decrypt_string(&decrypted, &encrypted, 10, 0xB9F8BB34, &dec_buffer);
  lpProcName_wsprintfA = return(decrypted_str_wsprintfA);
  wsprintfA = get_procaddress(&user32_dll_0, lpProcName_wsprintfA);
struct_decrypt *__thiscall decrypt_string(struct_decrypt *this, int encrypted, int len, int key, int decrypted)
  int i; 
  int a; 
  int b;
  int c;
  int d;

  this->len = len;
  this->decrypted = decrypted;
  a = key;
  b = key;
  c = key;
  d = key;
  for ( i = 0; i < len; ++i )
    d = 0xFFFFFFF9 * d - 3;
    a = 0xFFFFFFE1 * a - 5;
    b = 0x81 * b + 7;
    c = 0x201 * c + 9;
    *(this->decrypted + i) = (c + b + a + d) ^ *(i + encrypted);
  return this;

Right after the decrypted string is no longer of any use, it is wiped by overwriting the memory with zeros:

int __thiscall wipe_memory(int this)
  int result;
  int i; 

  for ( i = 0; i < *(this + 4); ++i )
    *(*this + i) = 0;
    result = i + 1;
  return result;

For the analyst, keeping the strings intact is much more convenient. Patching the function by replacing some instructions with NOPs is an option.

Initialization phase II

The second phase is initiated with a check regarding how the malware has been executed. If the filename of the DLL is not CRYPTBASE.DLL, the following new process is created with CreateProcessW, before the calling process is terminated:

rundll32.exe win3dx.DLL,SdxdPv 0

Calling the file with rundll32.exe has an interesting side effect: the malware is immune against Microsoft AppLocker.

Initialization phase III

The new process is calling function ordinal #11 (SdxdPv), the sole exported function with actual functionality. The next argument ‘0’ is evaluated in the evaluate_commandline() function:

int evaluate_commandline()
  const WCHAR *lpCommandLine; 
  int arg; 
  bool match;
  struct_hMem *hMem;
  int pNumArgs;

  pNumArgs = 0;
  lpCommandLine = GetCommandLineW_0();
  hMem = CommandLineToArgvW_0(lpCommandLine, &pNumArgs);
  if ( pNumArgs >= 4 )
    match = lstrcmpiW_0(hMem->argument, "0") == 0;
    if ( match )
    match = lstrcmpiW_0(hMem->argument, "1") == 0;
    if ( match )
      goto EXIT;
    match = lstrcmpiW_0(hMem->argument, "2") == 0;
    if ( match )
      goto EXIT;
    match = lstrcmpiW_0(hMem->argument, "3") == 0;
    if ( match )
      goto EXIT;

Accordingly, make_persistent_and_run() is executed.

LSTATUS execute_depending_on_elevation_type()
  LSTATUS result;
  struct _OSVERSIONINFOW VersionInformation;
  VersionInformation.dwOSVersionInfoSize = 284;
  if ( GetVersionExW_0(&VersionInformation) )
    if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion )
      if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion != 1 )
        if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion != 2 )
          if ( VersionInformation.dwMajorVersion != 6 || VersionInformation.dwMinorVersion )
            if ( VersionInformation.dwMajorVersion != 6 || VersionInformation.dwMinorVersion != 1 )
              result = make_persistent_in_autorun_and_run();// -> 6.2, 6.3 (Windows 8.x)
              result = make_persistent_and_run_on_Windows_7();// -> 6.1 (Windows 7)
            result = make_persistent_and_run_on_Windows_Vista();// -> 6.0 (Windows Vista)
          result = make_persistent_and_run_on_XP();// -> 5.2 (Server 2003, XP x64)
        result = make_persistent_and_run_on_XP();// -> 5.1 (Windows XP)
      result = return_50();                     // -> 5.0 (Windows 2000)
    result = RtlGetLastWin32Error_0();
  return result;

When the malware author created this decision tree, he might have abused illegal substances. If the malware author thinks this statement is an injustice because the weird logic is just a decompiler artifact, he is invited to contact us.

Depending on the version of Windows the malware is currently running, different junctions are taken to make the software persistent and to install and run it with the highest possible privileges.

The simplest way is taken in make_persistent_in_autorun_and_run(), where the Autorun key in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run is set to call

rundll32.exe win3dx.DLL,SdxdPv 3

The meaning of option ‘3’ is described in the next chapter.

This function is used for the latest Windows operating systems (Windows 8.x), but is also the fallback method for all other methods described here, when administrative privileges are not available or not exploitable.

In make_persistent_and_run_on_XP(), which is used for Windows XP, the malware is installed as a local service when administrative privileges for the user running the current process are available or if the process is started as SYSTEM user. The service’s name in this case is win3dx and is also calling function SdxdPv of the same malware binary in the context of svchost.exe. On successful installation, the service is started. This happens in the function create_and_start_service(), which is also reused in similar contexts.

Without administrative privileges, the aforementioned fallback to make_persistent_in_autorun_and_run() is taken.

On Windows 7, if administrative privileges are available, create_and_start_service() is called.

int make_persistent_and_run_on_Windows_7()
  int result; 
  int is_a_user; 
  int is_an_administrator;
  int elevation_type; 
  result = is_user(&is_a_user);
  if ( !result )
    if ( is_a_user )
      result = is_administrator(&is_an_administrator);
      if ( !result )
        if ( is_an_administrator )
          result = get_token_elevation(&elevation_type);
          if ( !result )
            switch ( elevation_type )
              case TokenElevationTypeDefault:
                result = create_and_start_service();
              case TokenElevationTypeFull:
                result = create_and_start_service();
              case TokenElevationTypeLimited:
                result = use_UAC_evasion();
                result = 1359;
          result = make_persistent_in_autorun_and_run();
      result = create_and_start_service();
  return result;

Otherwise, a classical UAC evasion technique is used (using sysprep.exe and CRYPTBASE.DLL) to run the code. If none of this is possible, installation is done in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run, unless the context is SYSTEM, which would lead to create_and_start_service(), too.

In Windows Vista, the logic is very similar, except that use_UAC_evasion() is replaced with ShellExecute_elevate_with_runas():

  pExecInfo.cbSize = 60;
  pExecInfo.lpFile = L"RUNDLL32.EXE";
  pExecInfo.lpParameters = "rundll32.exe win3dx.DLL,SdxdPv 0";
  pExecInfo.lpVerb = L"RUNAS";
  pExecInfo.nShow = 1;
  pExecInfo.fMask = 0;
  while ( !ShellExecuteExW_0(&pExecInfo) && RtlGetLastWin32Error_0() == ERROR_CANCELLED )

Post-initialization (main loop)

When function SdxdPv is called with option ‘3’, the main part of the malicious software starts. First, WSAStartup is called and privileges are adjusted (SeDebugPrivilege, SeTcbPrivilege). Immediately after, a couple of threads is being started:

  • CXGather::GtProc - thread management and thread communication
  • Cxsniffer::Snifferproc - network sniffer
  • CXSessionServer::SsStartProc - starts pipe communication (CXSessionServer::SsStartPipe)
  • Keylogger - logging user input (rundll32.exe win3dx.DLL,SdxdPv 2)
  • CXOnline::OlStartProc - initializes client-to-server communication

As reference, these are the identified functions by internal thread name:

Thread name Function
CXGather::GtProc Thread control
CXOnline::OlStartProc starts CXOnline::OlStartProcPipe and CXSoHttp::SoWorkProc
CXOnline::OlStartProcPipe initializes pipe communication objects and runs initial commands
CXSessionServer::SsStartPipe starts pipe communication and evaluates received commands evaluate_commands()
CXSessionServer::SsStartProc initializes pipe communication
CXSniffer::SnifferProc starts network sniffer
CXSoHttp::SoWorkProc stars Internet connection
CXFuncShell::ShellT1 CXFuncShell::ShellT2 Remote Shell
CXFuncSystem::SysMessageBoxProc shows message box to the screen
CXFuncTelnet::TelnetT1 CXFuncTelnet::TelnetT2 Asynchronous Telnet server

CXOnline::OlStartProc initializes the pipe communication (CXSessionServer::SsStartPipe). It also collects a some information about the computer, the user, the time, windows configuration and a screenshot and registers the client to the server.

From here, everything turns in an endless loop where commands are being evaluated evaluate_commands() and executed accordingly. The commands are in the following table.

Command switch:

The following table shows the commands the malware has implemented as malicious payload. This list of commands is comparable to other known malware families, for instance

Code Command
0x1000 status: PerformanceCounter
0x1001 start Process, get overview (user, computer, screenshot, …)
0x1002 start pipe communication
0x1003 echo input back (command, payload, …)
0x1005 run dll
0x2000 lock workstation
0x2001 forcefully log off user
0x2002 reboot system forcefully
0x2003 shutdown system forcefully
0x2005 on-screen message
0x3000 collect disk information (drives, types, space)
0x3001 find file
0x3004 read file (with size and access times)
0x3007 write decompressed buffer into file
0x300A create directory
0x300B test if file can be opened
0x300C creates process in hidden window
0x300D copy file
0x300E get environment info
0x300F get malware base directory
0x4000 remote desktop
0x4100 take screenshot and send
0x5000 get process information
0x5002 list processes
0x5004 terminate process
0x6000 service list
0x6002 delete service
0x6003 change service configuration
0x6004 start service
0x6005 stop service
0x7002 remote shell
0x7100 telnet server
0x7101 writeConsoleInput
0x7102 generateConsoleCtrlEvent
0x9000 registry enumerte subkeys
0x9002 create key
0x9003 delete subkey recursively
0x9004 move registry key
0x9005 enumerate values from key
0x9007 query value or create key and set value
0x9008 delete value
0x9009 enumerate subkeys or create key and set value
0xA000 enumerate network resources
0xB000 portmapper
0xC000 SQL query
0xD000 get TCP table
0xD002 get UDP table
0xD004 kill TCP connection

Comparison with other malware from this family

It is pretty interesting to compare this list of commands with the one in Annex D of Command and Control in the Fifth Domain - it is most likely not coincidence that so many function codes are the same for both malwares. That indicates proximity to the Murcy protocol described in the same document:

Match Code Command
  0x1003 Generate Sxl value from the registry key group.
  0x1004 Add Sxl description to registry key.
x 0x2000 Lock computer.
x 0x2001 Log off.
x 0x2002 Reboot.
x 0x2003 Shutdown.
  0x2004 Execute file.
  0x2005 Execute msg.exe.
x 0x3000 Get system drive information.
x 0x3001 File search.
  0x3003 File search.
  0x300A Create directory.
  0x300B Create process.
  0x300C Delete file(s).
  0x3200 Perform file operations.
x 0x5000 Obtain process information.
x 0x5002 Obtain process information.
x 0x5004 Kill process.
x 0x6000 List services.
x 0x6002 Delete service.
x 0x6003 Modify service configuration.
x 0x6004 Start service.
x 0x6005 Stop service.
  0x7000 Input/output generated in the process with a named pipe. Get environment string.
  0x8000 Get environment string.

It is even more interesting to compare the list of commands with the one reverse engineered by CIRCL in 2013 on a PlugX variant page 11. The commands are basically identical between the two malware samples. And even more interesting, comparison between PlugX code and Destory RAT code

PlugX Destory subcommand Description
x   0x1000 status: PerformanceCounter
x   0x1001 start Process, get overview (user, computer, screenshot, …)
x   0x1002 start pipe communication
x   0x1003 echo input back (command, payload, …)
x   0x1005 run dll
x x 0x2000 lock workstation
x x 0x2001 shutdown workstation (forced)
x x 0x2002 reboot workstation
x x 0x2003 shutdown workstation (graceful)
x x 0x2005 show messagebox
x x 0x3000 enumerate drives
x x 0x3001 find file
x   0x3002 find file recursively
x x 0x3004 read file
x x 0x3007 write file
x x 0x300A create directory
x x 0x300C create process on hidden desktop
x x 0x300D file copy/rename/delete/move
x x 0x300E get expanded environment string
x x 0x4000 Remote Desktop capabilities
x   0x4004 send mouse event
x   0x4005 send keyboard event
x   0x4006 send CTRL-Alt-Delete
x x 0x4100 take screenshot
x   0x5000 create process
  x 0x5000 get process information
x   0x5001 enumerate processes
x   0x5002 kill process
  x 0x5002 list processes
  x 0x5004 terminate process
x x 0x6000 query service config
x   0x6001 change service config (forced)
x   0x6002 start service
  x 0x6002 delete service
x   0x6003 control service
x   0x6004 delete service
  x 0x6004 terminate process
x   0x6005 stop service
x x 0x7002 start a cmd shell
x x 0x7100 start telnet server
x x 0x9000 enumerate keys
x   0x9001 create key
x   0x9002 delete key
  x 0x9002 create key
x   0x9003 copy key
  x 0x9003 delete subkey recursively
x   0x9004 enumerate values
  x 0x9004 move registry keys
x   0x9005 set value
  x 0x9005 enumerate values for key
x   0x9006 get value
x   0x9007 delete value
  x 0x9007 query value or create key and set value
  x 0x9008 delete value
  x 0x9009 enumerate subkeys or create key and set value
x x 0xA000 enumerate network resources
x x 0xB000 starts port mapping
x x 0xC000 get data source information
x   0xC001 get driver description
x   0xC002 execute statement
x x 0xD000 get TCP table
x x 0xD001 get UDP table
x x 0xD002 set TCP entry
x   0xE000 starts key logger thread

Despite the fact that there are a lot of similarities between the command structures of both versions, it seems the command layout has changed between versions. The internal version of the analyzed PlugX is 20120123

    mov     dword ptr [ebx], 20120123h

while the version of Destory is 20100921

    mov     dword ptr [ecx], 20100921h

This version value is contained in several functions taking care of communication back to the C&C server.

So while we are already comparing different samples of malware belonging to this family, we were triggered to compare other samples belonging to this family, according to AV vendors. Often, the names PlugX (Gulpix, Korplug), Destory, Thoper, TVT, and Sogu are used synonymously, as mentioned for instance in That’s why we collected the following samples:

Name Hash
PlugX (MS, Ikarus)
Gulpix (Avast)
Korplug (Symantec)
(decrypted: 6F7AB6849E505D1028A217685AF6FDCD)
Destory 801389d08baa4144018460fbe95da5ea
Thoper.B (MS) af5395a22d67bf61294b538c8c5eda5b
Thoper.E (MS) 281da60d42e35fb61bad400edfd94df0
Sogu (Symantec) 40d6d6a65898256dad7d5a679cab5999
TVT (Ikarus) 642332869cdb6bda8156a43a2779e99d

We briefly analyzed these samples regarding a set of aspects. The result is displayed in the following table:

Feature PlugX Destory Thoper.B Thoper.E Sogu TVT
is .dll x x     x x
is .exe     x x    
contains debug info (pdb) x          
using signed code x          
global decryption x          
on-the-fly decryption   x x x x x
o-t-f decryption of function names   x x x x  
contains junk code   x x x x x
has exports   x (18) x (1) x (27) x (69) x (18)
large command func x x   x x x
version 20120123 x          
version 20100921   x x x x x
comparable # of features x x x x x x
Number of functions ~ 470 ~ 680 ~ 700 ~ 660 ~ 710 ~ 800

PlugX in the version we analyzed appears to be the most recent of all these, comparing function codes, version string and the interesting (code-signed) start-up phase. But it is not as careful as it’s siblings when it comes to encryption, because it only globally decrypts all strings.

Destory, Thoper and Sogu have almost the same number of functions and are very similar, even if some are run as a DLL and others not.

TVT looks like a hybrid where encrypted and non-encrypted strings are used. Function names for GetProcAddress are always in unencrypted, all other strings use the same on-the-fly decryption method as in the other samples (except PlugX).

If Antivirus vendors have more information about the history of this entire family or if they want to work on this with us, the are welcome to contact us.


The communication is done in HTTP on port 80. The communication mechanism is proxy aware and uses the system wide proxy configuration, if enabled. The author(s) of the malware are lacking attention to detail in the case of the user agent: the string is lacking a closing bracket:

Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1;

Initial requests are done in the format:


Accept:          */*
X-Session:       0
X-Status:        0
X-Size:          61456
X-Sn:            1
User-Agent:      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1;
Content-Length:  0
Connection:      Keep-Alive
Cache-Control:   no-cache

This is congruent with the facts in the aforementioned document from Command and Control in the Fifth Domain. The document has detailed information about the protocol.


Indicators of Compromise following Malware Information Sharing Platform format. If you are an organization/company based in Luxembourg or a TI accredited CERTs, you can request an access to the MISP platform.

MISP UUID is 53886b57-a3d8-43a7-8f4d-472a950d2109.

category type value
Payload installation md5 801389d08baa4144018460fbe95da5ea
Payload installation sha1 c1f8738b3d7ef40177becc0ffde9321a03ef961a
Payload installation sha256 217fe60d2ecea69055f93e86225e3596709f2e1baf458476d340726fdc8d5653
Payload installation filename win3dx.DLL
Artifacts dropped pattern-in-memory win3dx.DLL
Network activity hostname
Network activity ip-dst
Network activity hostname
Network activity ip-dst
Network activity ip-dst
Network activity user-agent Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1;

A MISP XML file is available if you want to import the indicators into MISP or any other threat indicators sharing platform.


  • CIRCL recommends to review the IOCs of this report and compare them with servers in the infrastructure of your organization which produce log files including proxies, A/V and system logs.

Server Intel

The server ( with the PTR record used for this campaign is hosted at AS24544 Pang International Limited in Hong Kong. The subnet is announced by the AS24544 starting from 2012-05-06.

Previously (in 2013), the ( and server was hosted at NETSEC-HK Unit 1205-1207 in Hong Kong.

Classification of this document

TLP:WHITE information may be distributed without restriction, subject to copyright controls.


CIRCL thanks CERT-BUND for sharing Sample A.



  • Version 1.0 June 3, 2014 initial release (TLP:WHITE)