[ConnectEx]
Socket 통신에서 동시다발적으로 여러 클라이언트에게 오는 요청을 효율적으로 처리하기 위해 많은 방법들이 고안되었다.
그 중 대표적으로 리눅스에는 Epoll, 윈도우에는 IOCP가 있다. 
IOCP에는 기존에 있던 소켓 함수들의 확장 모델이 있는데 그 중 하나가 ConnectEx 함수이다.
실제로 국내 모 메신져 회사의 Windows Application도 초기에 connect API를 사용하다가 
패치 후 ConnectEx API를 사용하는것을 보면 상용프로그램에서도 IOCP가 성능을 좌지우지 하는듯 보인다.
 
[ConnectEx 사용법]
ConnectEx API는 기존 API들 처럼 DLL에 정의되어있지 않다. 
런타임시 WSAIoctl API를 통해 ConnectEx를 메모리에 생성한 후 함수 포인터를 가져와 사용해야한다.
 
[ConnectEx 함수 포맷]
 
[ConnectEx 후킹]
먼저 ConnectEx API를 생성해준다.
이후 WSAIoctl을 후킹하고 인자를 검사하여 함수 포인터를 얻겠다는 의미로 dwIoControlCode 인자가 SIO_GET_EXTENSION_FUNCTION_POINTER 인지 확인하고
어떠한 API를 생성할것인지를 알 수 있는 lpvInBuffer 인자가 WSA_CONNECTEX 인지 검사한 후에 조건에 만족한다면 
ConnectEx를 생성하겠다는 것으로 판단할 수 있으므로 실제 ConnectEx의 주소가 들어갈 포인터에 MyConnectEx 함수의 주소를 대입해주면 된다.
LPFN_CONNECTEX Org_Connectex;//해당 함수포인터는 ConnectEx의 원본함수가 된다.
 
BOOl GetConnectEx(){
    GUID guid = WSAID_CONNECTEX;
    DWORD dwBytes;
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        TCHAR buf[100];
        _stprintf(buf, TEXT("error : %x"), GetLastError());
        OutputDebugString(buf);
        return FALSE;
    }
    int rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
        &guid, sizeof(guid),
        &Org_Connectex, sizeof(Org_Connectex),
        &dwBytes, NULL, NULL);
    if(!rc){
        return TRUE;
    }
    else{
        return FALSE;
    }
}
int WSAAPI MyWSAIoctl(
    SOCKET s,
    DWORD dwIoControlCode,
    LPVOID lpvInBuffer,
    DWORD cbInBuffer,
    LPVOID lpvOutBuffer,
    DWORD cbOutBuffer,
    LPDWORD lpcbBytesReturned,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
) {
    int ret = ((PFMyWSAIoctl)OrgWinAPI[WSAIOCTL])(
        s, 
        dwIoControlCode, 
        lpvInBuffer, 
        cbInBuffer, 
        lpvOutBuffer, 
        cbOutBuffer, 
        lpcbBytesReturned, 
        lpOverlapped, 
        lpCompletionRoutine
    );
    if (dwIoControlCode == SIO_GET_EXTENSION_FUNCTION_POINTER && CheckGUID(lpvInBuffer, WSAID_CONNECTEX)) {
        // WSAIoctl의 인자를 검사해 ConnectEx API를 생성하려는 호출인지 판단한다.
        *(DWORD*)lpvOutBuffer = (DWORD)MyConnectex;
        //정상적으로 생성된 ConnectEx의 주소가 들어있는 포인터를 MyConnectEx함수의 주소로 지정해준다.
    }
    return ret;
}
BOOL WSAAPI MyConnectex(
    SOCKET s,
    const sockaddr* name,
    int namelen,
    PVOID lpSendBuffer,
    DWORD dwSendDataLength,
    LPDWORD lpdwBytesSent,
    LPOVERLAPPED lpOverlapped
) {
    BOOL ret = ((PFMyConnectex)Org_Connectex)(
        s, 
        name,
        namelen, 
        lpSendBuffer, 
        dwSendDataLength, 
        lpdwBytesSent, 
        lpOverlapped
    );
    OutputDebugString(TEXT("ConnectEx API Hooking Success"));
 
    return ret;
}
int main(){
    if(GetConnectEx()){//ConnectEx API를 생성한다.
        hook_code(TEXT("Ws2_32.dll"), TEXT("WSAIoctl"), MyWSAIoctl);
        //WSAIoctl API를 후킹한다.
    }
}

'C&C++' 카테고리의 다른 글

CMake Syntax  (0) 2020.01.21
How To Use Unit Test In C++  (0) 2020.01.13
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08
WINAPI Options Using Bit Flag  (0) 2019.08.07
WINAPI CreateProcess Suspend State  (0) 2019.07.11

+ Recent posts