lab5

What is the address of DllMain?

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

Use the Imports window to browse to gethostbyname. Where is the import located?

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.

How many functions call gethostbyname?

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.

Focusing on the call to gethostbyname located at 0x10001757, can you figure out which DNS request will be made?

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.

How many local variables has IDA Pro recognized for the subroutine at 0x10001656? How many parameters has IDA Pro recognized for the subroutine at 0x10001656?

My version of IDA pro detected 23 local variables and one parameter (lpThreadParameter being a function arg):

Use the Strings window to locate the string \cmd.exe /c in the disassembly. Where is it located? In the same area, at 0x100101C8, it looks like dword_1008E5C4 is a global variable that helps decide which path to take. How does the malware set dword_1008E5C4? (Hint: Use dword_1008E5C4’s cross-references.)

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.

A few hundred lines into the subroutine at 0x1000FF58, a series of comparisons use memcmp to compare strings. What happens if the string comparison to robotwork is successful (when memcmp returns 0)?

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.

What does the export PSLIST do?

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.

Use the graph mode to graph the cross-references from sub_10004E79. Which API functions could be called by entering this function? Based on the API functions alone, what could you rename this function?

; 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.

How many Windows API functions does DllMain call directly? How many at a depth of 2?

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.

At 0x10001358, there is a call to Sleep (an API function that takes one parameter containing the number of milliseconds to sleep). Looking backward through the code, how long will the program sleep if this code executes?

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.

At 0x10001701 is a call to socket. What are the three parameters?

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:

Search for usage of the in instruction (opcode 0xED). This instruction is used with a magic string VMXh to perform VMware detection. Is that in use in this malware? Using the cross-references to the function that executes the in instruction, is there further evidence of VMware detection?

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.

If you have the IDA Python plug-in installed (included with the commercial version of IDA Pro), run Lab05-01.py, an IDA Pro Python script provided with the malware for this book. (Make sure the cursor is at (0x1001D988.) What happens after you run the script?With the cursor in the same location, how do you turn this data into a single ASCII string?

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”