[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를 후킹한다.
}
}