01 개요 : 대개의 악성코드들이 통신기능을 사용하기 때문에 제작/분석을 위해서는 소켓 프로그래밍, 그중에서도 Windows에서 사용되는 Winsock의 기본 흐름을 알아보도록 하겠습니다.
02 물론 Socket Programming은 Linux가 먼저이지만 이를 약간 변형하여 Windows에서는 WInsock을 사용합니다.
03 Winsock을 사용하기 위해서는 2가지 파일의 선언이 필요하며 소켓 프로그래밍을 하기위해서는 항상 이를 선언해주어야합니다.
1) winsock2.h
2) ws2_32.dll
04 틀은 정해져있고 실제로 소켓 프로그래밍을 할때에는 전부 손으로 치기보다는 틀을 복사하여 수정할 부분만 수정하여 사용하기 때문에 전체적인 코드 흐름을 알아두는 것이 중요합니다.
05 전화를 하기위해서는 전화기가 필요하듯이 통신을 하기위해서는 "소켓"이라고 불리워지는 것이 필요합니다.
이 "소켓"을 확인하는 방법은 <그림 1>과 같습니다.
<그림 1>
: <그림 1>에 표시된 한줄한줄이 전부 소켓이라고 보면 됩니다.
06 통신 프로토콜에는 TCP와 UDP가 있는데 간단히 설명하고 넘어가도록 하겠습니다.
1) TCP : 신뢰성 있는 통신으로 3-way handshaking을 통해서 서로 연결이 됩니다. 또한 "연결"을 하기 때문에 <그림 1>과 같이 기록이 남게 됩니다.
2) UDP : 신뢰성 없는 통신으로 한번 보내면 제대로 도착하였는지 조차 확인을 하지 않는 프로토콜입니다. 또한 따로 연결하고 보내는 것이 아니기 때문에 기록이 남는다 하더라도 어디서 누가 요청한 것인지 알 수 가 없습니다.
07 소켓 프로그래밍
1) Server : Server 입장의 소켓 프로그래밍만 할 줄알면 Client의 경우 간단하므로 이 문서에서는 Server의 연결 방식만 알아보도록 하겠습니다.
<그림 2>
: <그림 2>는 아래에 나오는 전체 코드를 흐름에 따라 순서를 매긴 것으로 꼭 확인하고 넘어가도록 해야합니다.
#include <winsock2.h>
#include <stdio.h>
#define BUFSIZE 512
// 소켓 함수 오류 출력 후 종료
void err_quit(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
LocalFree(lpMsgBuf);
exit(-1);
}
// 소켓 함수 오류 출력
void err_display(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
int main(int argc, char* argv[])
{
int retval;
// 윈속 초기화
WSADATA wsa;
if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
return -1;
// socket()
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock == INVALID_SOCKET) err_quit("socket()");
// bind()
SOCKADDR_IN serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(9000);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
retval = bind(listen_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr));
if(retval == SOCKET_ERROR) err_quit("bind()");
retval = listen(listen_sock, SOMAXCONN);
if(retval == SOCKET_ERROR) err_quit("listen()");
SOCKADDR_IN clnt_addr;
int len = sizeof(clnt_addr);
accept(tcp_srv,(SOCKADDR*) &clnt_addr,&len);
// 데이터 통신에 사용할 변수
SOCKET client_sock;
SOCKADDR_IN clientaddr;
int addrlen;
char buf[BUFSIZE+1];
while(1){
// accept()
addrlen = sizeof(clientaddr);
client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen);
if(client_sock == INVALID_SOCKET){
err_display("accept()");
continue;
}
printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
// 클라이언트와 데이터 통신
while(1){
// 데이터 받기
retval = recv(client_sock, buf, BUFSIZE, 0);
if(retval == SOCKET_ERROR){
err_display("recv()");
break;
}
else if(retval == 0){
break;
}
else{
// 받은 데이터 출력
buf[retval] = '\0';
printf("%s", buf);
}
}
// closesocket()
closesocket(client_sock);
printf("\n[TCP 서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}
// closesocket()
closesocket(listen_sock);
// 윈속 종료
WSACleanup();
return 0;
}
2) Client
#include<winsock2.h>
#include<stdlib.h>
#include<stdio.h>
#define BUFSIZE 512
pragma comment(lib,"ws2_32.lib")
void error_exit(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR) lpMsgBuf, msg, MB_ICONERROR);
LocalFree(lpMsgBuf);
exit(-1);
}
int main(int argc, char * argv[])
{
int retval;
WSADATA wsa;
if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
return -1;
MessageBox(NULL, "윈속 초기화", "성공함", MB_OK);
SOCKET tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
if(tcp_sock == INVALID_SOCKET) error_exit("sock() 함수 에러");
MessageBox(NULL, "TCP 소켓 생성 성공", "성공", MB_OK);
SOCKADDR_IN servaddr;
ZeroMemory(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9000);
servaddr.sin_addr.s_addr = inet_addr("59.5.12.146");
retval = connect(tcp_sock, (SOCKADDR *)&servaddr, sizeof(servaddr));
if(retval == SOCKET_ERROR) error_exit("connect()함수 에러");
char buf1[BUFSIZE+1];
int len;
while(true) {
ZeroMemory(buf1, sizeof(buf1));
printf("\n [전송할 문자를 입력하시오] ");
if(fgets(buf1, BUFSIZE+1, stdin) == NULL)
break;
len = strlen(buf1);
if(buf1[len-1] == '\n')
buf1[len-1]='\0';
if(len==0)
break;
retval = send(tcp_sock, buf1, strlen(buf1), 0);
if(retval == SOCKET_ERROR) {
error_exit("send() 함수 에러");
}
retval = recv(tcp_sock, buf1, BUFSIZE, 0);
if(retval == SOCKET_ERROR) {
error_exit("recv() 함수 에러");
}
buf1[retval] = '\0';
printf("[%s:%d 로부터 되돌려 받은 문자열 ] %s" ,
inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port), buf1);
}
closesocket(tcp_sock);
WSACleanup();
return 0;
}
'Security > RCE' 카테고리의 다른 글
(Manual Unpacking 01) UPX Unpacking (0) | 2013.08.14 |
---|---|
(이론 02) EFLAGS (0) | 2013.08.13 |
(분석 01) WinSock 기초 분석 (0) | 2013.06.14 |
(분석 01) Multi Thread 분석 (0) | 2013.06.01 |
(이론 01) VAS Basic (0) | 2013.05.16 |