Programming/C

[Socket] Socketpair 및 sendmsg, recvmsg

Tribal 2018. 7. 20. 15:21

Socketpair


  일반적으로 socket() 함수를 사용하면 인자로 준 특정 프로토콜에 대해서 1개의 소켓만을 생성한다. socketpair를 이용하면 한 번에 서로가 연결된 소켓 쌍을 생성하는게 가능해진다. 흔히 AF_UNIX를 사용하여 로컬 내부에서 도메인 소켓을 사용하고자 할 때, 외부 네트워크로부터 데이터를 받아 도메인 소켓으로 특정 프로그램에 내용을 전달하는 방식으로 사용한다. 

1
2
3
4
5
6
7
8
9
int sock;
int sock_pair[2];
int result;
 
// generally
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
// socketpair
result = socketpair(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock_pair);
cs



sendmsg, recvmsg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int fd;             // = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
char buf[MAX_BUF];
struct msghdr msg;    // basic msg header
struct iovec iov;    // i/o vercor (about buffer information)
cmsg_fd cmsg;        // control message
 
// register buffer
iov.iov_base = buf;
iov.iov_len  = MAX_BUF;
 
// configure msg
msg.msg_name       = NULL// optional address (= sockaddr_in)
msg.msg_namelen    = 0;    // address size
msg.msg_iov        = &iov; // iov that registered buffer
msg.msg_control    = &cmsg;
msg.msg_controllen = sizeof(cmsg);
msg.msg_flags      = 0;
 
// configure control msg
struct cmsghdr* cptr(CMSG_FIRSTHDR(&msg));
cptr->cmsg_len   = CMSG_LEN(sizeof(int));
cptr->cmsg_level = SOL_SOCKET;
cptr->cmsg_type  = SCM_RIGHTS;
 
memcpy(CMSG_DATA(cptr), &fd, sizeof(int));
if(sendmsg(sockfd, &msg, 0< 0) {
    perror("[-] Failed sendmsg()");
    exit(-1);
}
cs


msghdr, iovec 구조체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * [XSI] Message header for recvmsg and sendmsg calls.
 * Used value-result for recvmsg, value only for sendmsg.
 */
struct msghdr {
        void            *msg_name;      /* [XSI] optional address */
        socklen_t       msg_namelen;    /* [XSI] size of address */
        struct          iovec *msg_iov; /* [XSI] scatter/gather array */
        int             msg_iovlen;     /* [XSI] # elements in msg_iov */
        void            *msg_control;   /* [XSI] ancillary data, see below */
        socklen_t       msg_controllen; /* [XSI] ancillary data buffer len */
        int             msg_flags;      /* [XSI] flags on received message */
};
 
/* Define to have the same layout as a WSABUF */
#ifndef STRUCT_IOVEC_DEFINED
#define STRUCT_IOVEC_DEFINED 1
struct iovec {
    long iov_len;
    char *iov_base;
};
#endif
#else
struct iovec;                                /* Defined in OS headers */
#endif
 
/*
 * Header for ancillary data objects in msg_control buffer.
 * Used for additional information with/about a datagram
 * not expressible by flags.  The format is a sequence
 * of message elements headed by cmsghdr structures.
 */
struct cmsghdr {
        socklen_t       cmsg_len;       /* [XSI] data byte count, including hdr */
        int             cmsg_level;     /* [XSI] originating protocol */
        int             cmsg_type;      /* [XSI] protocol-specific type */
/* followed by  unsigned char  cmsg_data[]; */
};
cs


리버싱할 때 분석 방법


  우선 리버싱을 할 때, 입출력이 되는 부분인 sendmsg와 recvmsg 함수를 찾는다. 해당 함수는 총 3개의 인자를 가지며, 1번째 인자는 socket, 2번째 인자는 struct msghdr인 msg의 주소, 3번째 인자는 flags이다. 현재 중요한건 buffer이기 때문에 msg의 주소를 확인하고 struct msghdr의 구조체에 내용을 따라 값 부분을 확인하고 정의하면 된다. 다음은 IDA로 분석했을 때, 정의된 모습이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
    C code that is converted from Assembly code
    var_38 = unk_5B3944;
    var_34 = 0x5DC;
    var_30 = 0;
    var_2C = 0;
    var_28 = var_38;
    var_24 = 1;
    var_20 = dword_5B3844;
    var_1C = 0x100;
    var_18 = 0;
    recvmsg(sock, &var_30, 0);
*/
 
// var_38 = struct iovec
var_38.iov_base = unk_5B3944;
var_34.iov_len = 0x5DC;
 
// var_30 = struct msghdr
var_30.msg_name = NULL;
var_30.msg_name_len = 0;
var_30.msg_iov = &var_38;
var_30.msg_iovlen = 1;
var_30.msg_control = dword_5B3844;
var_30.msg_controllen = 0x100;
var_30.msg_flags = 0;
cs


  msg의 주소를 확인하면 msg의 주소 + 8의 위치에서 iov의 주소를 얻는게 가능하다. iov의 주소를 얻으면 내부에 저장된 buffer의 주소와 buffer의 크기까지 획득할 수 있다.



참고