Search code examples
c#c++socketswinsockdetours

How does System.Net.Sockets.Socket.Disconnect disconnect from the socket?


My DLL gets injected into a program and then hooks to connect, send, recv and closesocket functions using Detours. The point is to stop the program from connecting to some server and instead communicate with my DLL directly.

My recv function uses an infinite loop, just waiting for any data to send to the program. When closesocket is called that loop is broken and everything works fine.

But there's one program written in C# that just hangs when I close it. Its error log says:

SocketException: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.

at System.Net.Sockets.Socket.Disconnect (Boolean reuseSocket) [0x00000] in :0

The exception is expected since the socket never connects to anything. But is there any workaround for this? What does System.Net.Sockets.Socket.Disconnect call under the hood? What other function do I need to hook to detect that?

I've tried hooking to shutdown, setsockopt, WSACancelBlockingCall, WSACleanup, WSASend, WSASendDisconnect, WSASendMsg, WSASendTo, WSARecv, WSARecvDisconnect and WSARecvFrom. None of them get called.


Solution

  • System.Net.Sockets.Socket.Disconnect calls DisconnectEx. And as the remarks say:

    Note The function pointer for the DisconnectEx function must be obtained at run time by making a call to the WSAIoctl function with the SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. The input buffer passed to the WSAIoctl function must contain WSAID_DISCONNECTEX, a globally unique identifier (GUID) whose value identifies the DisconnectEx extension function. On success, the output returned by the WSAIoctl function contains a pointer to the DisconnectEx function. The WSAID_DISCONNECTEX GUID is defined in the Mswsock.h header file.

    So if you want to do what I did you have to:

    1. hook to WSAIoctl
    2. check if dwIoControlCode is SIO_GET_EXTENSION_FUNCTION_POINTER
    3. check if lpvInBuffer is WSAID_DISCONNECTEX:
    IsEqualGUID(*((GUID*)lpvInBuffer), WSAID_DISCONNECTEX)
    
    1. store the address to the real DisconnectEx somewhere
    LPFN_DISCONNECTEX Real_DisconnectEx = NULL;
    (...)
    Real_DisconnectEx = *((LPFN_DISCONNECTEX*)lpvOutBuffer);
    
    1. return the address to your function
    *((LPFN_DISCONNECTEX*)lpvOutBuffer) = &Mine_DisconnectEx;