The assembly implements a full bind shell over TCP on port 4444 that resolves all Windows APIs dynamically, avoids null bytes, and spawns cmd.exe with stdin, stdout, and stderr redirected over the socket. The shellcode prepares itself for injection in a process, finds kernel32.dll without imports, discovers networking and process-creation functions through export-table parsing, then opens a listening socket and hands full command execution to a remote operator.
—
High-level behavior and purpose
Shellcode builds a stack frame, reserves 0x60 bytes for internal state, and stores important pointers in negative offsets from EBP. That local state tracks module bases, export tables, API addresses, socket handles, and struct pointers.
Main goal:
Open a TCP listener on 0.0.0.0:4444.
Accept one client connection.
Launch cmd.exe.
Redirect all process handles (STDIN, STDOUT, STDERR) to the accepted socket.
Exit cleanly after process creation.
From an adversary capability perspective, the payload provides remote command execution with full interactive shell access on any compromised Windows host that runs the shellcode.
—
Stage 1 – Dynamic kernel32 location and GetProcAddress resolution
Shellcode pushes the ASCII string “GetProcAddress” onto the stack in little-endian chunks without null bytes and stores a pointer at [ebp-0x4]. That string forms the anchor for later export table searches.
Execution then walks internal Windows structures:
1. Reads the Thread Environment Block (TEB) via mov eax, [fs:eax+0x30] to get the Process Environment Block (PEB) base.
2. Follows PEB->Ldr to reach _PEB_LDR_DATA.
3. Walks the InInitializationOrderModuleList list entries to find ntdll.dll, kernelbase.dll, then kernel32.dll.
4. Reads the base address of kernel32.dll from the third list entry and stores it at [ebp-0x8].
That sequence avoids imports and avoids any static module addresses, which defeats ASLR assumptions and import-table based detection.
Next phase walks the PE header and export table of kernel32.dll:
Reads the PE header offset at base+0x3c.
Locates the export directory via the offset at +0x78.
Takes the Name Pointer Table, Ordinal Table, and Address Table pointers.
Iterates through export names and compares each entry against the “GetProcAddress” string using repe cmpsb and a 15-byte length.
When the code finds a match, it resolves the function RVA through the ordinal and address tables, then adds the module base to compute the absolute address.
Shellcode stores the resulting GetProcAddress pointer at [ebp-0x18]. That function becomes the central resolver for everything else.
Tradecraft value:
No import table.
No direct references to GetProcAddress in cleartext in memory after stack unwinds.
Full compatibility with different Windows versions without relocation issues.
—
Stage 2 – Loading ws2_32 and initializing Winsock
With GetProcAddress available, the payload builds “LoadLibraryA” on the stack, again in reverse dword pieces, then calls GetProcAddress on kernel32.dll to get its address and stores it at [ebp-0x1c].
Shellcode then:
Pushes “ws2_32.dll” as “23”, “_2sw” pieces plus a null terminator.
Calls LoadLibraryA to load ws2_32.dll.
Stores the returned module handle at [ebp-0x20].
Next, the payload resolves networking functions using GetProcAddress on ws2_32.dll:
“WSAStartup” → stored at [ebp-0x24].
“WSASocketA” → stored at [ebp-0x28].
Later, “bind”, “listen”, “accept” → stored at [ebp-0x34], [ebp-0x38], [ebp-0x3c].
After resolving WSAStartup, the shellcode:
Prepares parameters for Winsock version 2.2 by loading 0x0190 (MAKEWORD(2,2)) into BX.
Calls WSAStartup with a pointer to a WSADATA buffer on the stack.
That step activates the networking stack for later socket operations.
—
Stage 3 – Socket creation and bind on port 4444
Shellcode sets up a TCP socket:
Prepares arguments: AF_INET = 2, SOCK_STREAM = 1, IPPROTO_TCP = 6, all other pointer parameters null.
Calls WSASocketA with those parameters and stores the resulting socket handle at [ebp-0x2c].
Next, the code builds a sockaddr_in structure on the stack:
Pushes INADDR_ANY as zero.
Pushes port 0x5c11 (little-endian for TCP port 4444).
Pushes AF_INET = 2 as a word.
Stores the pointer to that struct at [ebp-0x30].
Shellcode then:
Resolves bind from ws2_32.dll.
Calls bind(socket, sockaddr_in*, 16) to bind the socket to 0.0.0.0:4444.
Resolves listen.
Calls listen(socket, 0) with backlog zero, sufficient for a single connection.
Resolves accept.
Calls accept(socket, NULL, NULL) to block until an inbound connection arrives.
Stores the accepted client socket handle at [ebp-0x40].
At that point, a remote operator who reaches port 4444 gains a transport for interactive shell control.
—
Stage 4 – Process spawning and handle redirection
Shellcode now prepares process structures for cmd.exe:
Builds ASCII “cmd” by loading 0x646d6363 and shifting right by one byte, which gives “cmd”,0x00.
Stores the pointer to that string at [ebp-0x44].
Next, the code allocates space for a PROCESS_INFORMATION structure by subtracting 0x10 from ESP and stores the pointer at [ebp-0x48].
For the STARTUPINFOA structure, shellcode:
Pushes the client socket handle three times to serve as hStdError, hStdOutput, and hStdInput, which redirects all standard streams to the network.
Pushes zeros for reserved members and sizing fields.
Crafts a value 0x00000101 (through inc and rol) for dwFlags with STARTF_USESTDHANDLES bit set.
Sets cb to 0x44 (size of STARTUPINFOA) and stores the structure pointer at [ebp-0x4c].
Shellcode then resolves CreateProcessA from kernel32.dll using GetProcAddress and stores that pointer at [ebp-0x50].
The CreateProcessA call uses:
lpAppName = NULL.
lpCmdLine = “cmd”.
Security attributes = NULL.
bInheritHandles = TRUE to pass socket handles to the new process.
dwCreationFlags = 0.
Environment and current directory = NULL.
Pointers to STARTUPINFOA and PROCESS_INFORMATION on the stack.
That call spawns cmd.exe with its I/O streams wired to the network socket, which grants remote interactive shell access over the established TCP connection.
Finally, the shellcode resolves ExitProcess from kernel32.dll, stores its pointer at [ebp-0x54], then calls ExitProcess with the return value in EAX, which terminates the hosting process.
—
Tradecraft features and shellcode engineering
Several design choices increase operational value for an adversary:
No imports and no hardcoded addresses
PEB walking and export-table parsing remove direct imports, which frustrates static analysis and makes the payload resilient across Windows builds.
Null-free encoding
Assembly avoids opcodes that contain null bytes (for example, replacing mov eax, [eax] with mov ebx, eax / mov eax, [ebx]). That choice supports injection into string-based buffers and avoids early string termination.
Stack-only data structures
All strings, structs, and function pointers reside on the stack below EBP. No global data section exists, which reduces forensic artifacts.
Self-contained networking and process logic
Shellcode handles Winsock initialization, socket operations, struct creation, and process spawning in one contiguous block. Delivery of that block into any process with network access grants full remote shell capability without further files or dependencies.
Standard port selection
Port 4444 appears frequently in offensive tooling and Metasploit payloads, so many attackers treat it as a comfortable default for bind shells.
—
Capability and threat analysis
From an intelligence viewpoint, the payload grants an operator several concrete abilities once executed on a host:
Accept inbound connections on TCP 4444 from any address.
Deliver an interactive cmd.exe shell over that connection.
Run arbitrary commands with the privileges of the compromised process.
Chain follow-on actions such as lateral movement, credential dumping, and persistence installation.
ATT&CK mapping:
T1059.003 – Command and Scripting Interpreter: Windows Command Shell
The payload launches cmd.exe and hands control to the attacker.
T1059.006 – Native API
Direct use of low-level APIs and export-table parsing fits native API execution.
T1571 – Non-Standard Port
Port 4444 often falls outside regular service ranges.
T1105 – Ingress Tool Transfer and T1068 – Exploitation for Privilege often pair upstream when operators deliver such shellcode via exploits.
The shellcode supports penetration tools, custom backdoors, and post-exploitation frameworks that prefer memory-resident payloads.
—
Detection and defensive implications
Defenders gain several observable anchors:
API behavior
Concentrated use of WSAStartup, WSASocketA, bind, listen, accept, then CreateProcessA(“cmd”) appears in many backdoor patterns. EDR sensors that watch for socket creation followed by shell spawn with handle inheritance gain strong signals.
In-process networking from unusual binaries
When a non-network service suddenly binds to port 4444 and spawns cmd.exe, SOC teams should treat that chain as high-risk.
PEB walking and export-table parsing
Repeated reads of fs:[0x30], _PEB_LDR_DATA lists, and the export directory stand out inside benign office or line-of-business processes. Behavioral engines that track such low-level traversal gain early detection.
Network indicators
Unusual listeners on port 4444, especially on workstations, suggest operator tooling. Network intrusion systems that alert on interactive shells over single TCP streams add another detection layer.
From an education angle, the shellcode offers a full reference path from initial control to a functional bind shell. Analysts who understand each stage gain sharper pattern recognition for memory-resident backdoors and more precise questions for EDR vendors about coverage around PEB walking, export-table traversal, and socket-backed process creation.
