Press "Enter" to skip to content

Space4u

Segmentation fault 뜰때~ 돌려보자~

메모리 깨먹을 때 Segmentation fault 가 작렬하지요…

배열의 첨자를 벗어나서 접근하거나 포인터를 할당된 메모리를 넘어서 접근할때 나지요.

free에서 죽는 경우가 많고요.

주의할점은 malloc으로 strlen만큼 메모리를 잡고 sprintf를 하면 절대!!!!!! 안됩니다. 
sprintf는 뒤에 무조건 NULL을 하나 더 넣어주기 때문에 strlen보다 1 크게 잡아줘야 합니다.

또.. 

*** glibc detected *** free():invalid point

라는 메시지를 툭~~~ 던지고 몇페이지나 되는 데이터가 주루루루루룩~ 올라가는 경우가 있습니다.
요넘도 비정상적인 메모리 프리가 체크되면 뜨는건데요…

Linux libc 5.4.23이후와 GNU libc 2.x 버전은 환경 변수로 malloc을 조정할 수 있습니다.
쉘에서 export MALLOC_CHECK_=3 해주시면 에러 메시지를 출력하고 종료해 줍니다. 설정 값은 0~3

0 : 아무것도 하지 않고 그냥 프로그램 동작…. 프리하는 순간엔 죽겠죠??? 언젠가 맛팅이 가겠죠…
1 : 에러 메시지 출력
2 : 아무런 메시지 없이 프로그램 종료
3 : 에러 메시지 출력하고 프로그램 종료

긍게 2, 3을 해서 메모리 손상이 감지 됬을때 서 버리는게 낫겠죠? 메시지까지 출력해 준다면(3) 최고겠구요…

쉽게 문제의 코드를 찾는 방법은 valgrind 라는 프로그램을 쓰면됩니다.
정말 찾아내는 순간 쪽팔려서 얼굴이 발그레진다능… ㅋㅋㅋ

valgrind 자기가만든프로그램

요렇게 실행하면 어디쯤에서 메모리를 깨먹는지 대충 알려줍니다…. 영어루… ㅡㅡ;

apt-get instll valgrind apt-get install valgrind 를 하면 우분투에선 걍 설치해주겠죠???? 아님 말구… ^^

gcc 스텍 깨지는거 검사하기

스텍 깨지는거 검사 linux ubuntu 
출처 : http://stackoverflow.com/questions/1345670/stack-smashing-detected
ab@cd-x:$ cat test_overflow.c 
#include <stdio.h>
#include <string.h>

int check_password(char *password){
    int flag = 0;
    char buffer[20];
    strcpy(buffer, password);

    if(strcmp(buffer, "mypass") == 0){
        flag = 1;
    }
    if(strcmp(buffer, "yourpass") == 0){
        flag = 1;
    }
    return flag;
}

int main(int argc, char *argv[]){
    if(argc >= 2){
        if(check_password(argv[1])){
            printf("%s", "Access grainted\n");
        }else{
            printf("%s", "Access denined\n");
        }
    }else{
        printf("%s", "Please enter password!\n");
    }
}
ab@cd-x:$ gcc -g -fno-stack-protector test_overflow.c 
ab@cd-x:$ ./a.out mypass
Access grainted
ab@cd-x:$ ./a.out yourpass
Access grainted
ab@cd-x:$ ./a.out wepass
Access denined
ab@cd-x:$ ./a.out wepassssssssssssssssss
Access grainted

ab@cd-x:$ gcc -g -fstack-protector test_overflow.c 
ab@cd-x:$ ./a.out wepass
Access denined
ab@cd-x:$ ./a.out mypass
Access grainted
ab@cd-x:$ ./a.out yourpass
Access grainted
ab@cd-x:$ ./a.out wepassssssssssssssssss
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xce0ed8]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xce0e90]
./a.out[0x8048524]
./a.out[0x8048545]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc16b56]
./a.out[0x8048411]
======= Memory map: ========
007d9000-007f5000 r-xp 00000000 08:06 5776       /lib/libgcc_s.so.1
007f5000-007f6000 r--p 0001b000 08:06 5776       /lib/libgcc_s.so.1
007f6000-007f7000 rw-p 0001c000 08:06 5776       /lib/libgcc_s.so.1
0090a000-0090b000 r-xp 00000000 00:00 0          [vdso]
00c00000-00d3e000 r-xp 00000000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3e000-00d3f000 ---p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3f000-00d41000 r--p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d41000-00d42000 rw-p 00140000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d42000-00d45000 rw-p 00000000 00:00 0 
00e0c000-00e27000 r-xp 00000000 08:06 4213       /lib/ld-2.10.1.so
00e27000-00e28000 r--p 0001a000 08:06 4213       /lib/ld-2.10.1.so
00e28000-00e29000 rw-p 0001b000 08:06 4213       /lib/ld-2.10.1.so
08048000-08049000 r-xp 00000000 08:05 1056811    /dos/hacking/test/a.out
08049000-0804a000 r--p 00000000 08:05 1056811    /dos/hacking/test/a.out
0804a000-0804b000 rw-p 00001000 08:05 1056811    /dos/hacking/test/a.out
08675000-08696000 rw-p 00000000 00:00 0          [heap]
b76fe000-b76ff000 rw-p 00000000 00:00 0 
b7717000-b7719000 rw-p 00000000 00:00 0 
bfc1c000-bfc31000 rw-p 00000000 00:00 0          [stack]
Aborted
ab@cd-x:$  

서비스(데몬) 조회 및 서비스 실행

우분투에서 ubuntu 서비스 조회

dpkg -l "*sys*log*" | grep ^ii

서비스 중지

service rsyslog stop

서비스 실행

service rsyslog start

서비스 재실행

service rsyslog restart

시간 단위

# 1 millisecond == 1000 microseconds
usleep(1000);
# 1 microsecond == 1000 nanoseconds
nanosleep(1000000);
# 1 microsecond == 1000000 nanoseconds
nanosleep(1000000);

BSD 소켓 프로그래밍(Socket Programming) 예제

원문 : http://kkamagui.springnote.com/pages/551093

들어가기 전에…

0.시작하면서…

 BSD 소켓 프로그래밍은 MFC 소켓에 비하면 쓰기가 까다롭고 알아야할 부분이 많다. 특히나 윈도우 프로그래밍을 하면 MFC 소켓에 익숙해지기때문에 까먹기가 십상이다.

 이번에 NDS 소켓 프로그래밍을 하면서 우연히 다시 볼 기회가 생겨 정리한다.

1.참고 함수들

1.1 select 함수

 Single Thread로 Multi-Socket을 컨트롤 하는 방법은 여러가지가 있겠지만, 가장 대표적인 것이 select이다. select는 아래와 같은 원형을 가지고 있다.

int select(
  int nfds,
  fd_set FAR* readfds,
  fd_set FAR* writefds,
  fd_set FAR* exceptfds,
  const struct timeval FAR* timeout
);

 select 함수는 nfds에 설정된 소켓의 수만큼 소켓을 체크하므로 반드시 가장 큰 소켓 번호 + 1의 크기만큼을 nfds로 넘겨줘야 함을 잊지 말자( ex: fd + 1 ) 

 return 값은 아래와 같은 의미를 가진다.

  • 양수 : readfds or writefds or exceptfds 중에 양수 개의 fd가 이벤트가 발생했다.
    • fds에 이벤트가 발생한 fd만 플래그가 설정되므로 FD_ISSET 매크로를 이용해서 해당 socket을 찾을 수 있다.
    • timeout에 남은 시간이 저장되므로 이를 활용하면 추가적인 처리가 가능하다
  • 0 : timeout이 되었다. timeout 값의 경우 0으로 설정되면 무한대로 대기한다.
  • 음수 : readfds에 닫힌 소켓이 있거나 기타 에러가 발생했다.

 fd_set 및 timeval은 구조체로 아래와 같은 형태를 가진다.

typedef struct fd_set {
  u_int fd_count;
  SOCKET fd_array[FD_SETSIZE];
} fd_set;

 struct timeval {
  long tv_sec;  // second 단위
  long tv_usec; // millisecond 단위
}; 

 timeval은 위에서 보는 것 그대로 second/millisecond 단위로 설정해서 사용하면 된다.

 하지만 fd_set과 같은 경우 어떻게 사용해야할지 좀 막막하다. 다행이 이를 처리해주는 매크로가 있으니 아래와 같다.

  • FD_ZERO( fd_set* fdset ) :  fdset을 초기화. 처음에는 반드시 한번 호출해야 함
  • FD_SET( int fd, fd_set* fdset ) : fdset에 fd 소켓을 등록한다.
  • FD_CLR( int fd, fd_set* fdset ) : fdset에 fd 소켓을 삭제한다.
  • FD_ISSET( int fd, fd_set* fdset ) : fdset에 fd 소켓이 있는가 확인한다.

 자주 사용하는 매크로이니 한번쯤은 읽어두자. 자세한 사용 방법은 아래의 Linux 예제를 보면 된다.

2.윈도우(Window) 환경

2.1 서버(Server)

#include <winsock2.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT 2924
#define DEFAULT_BUFFER_SIZE 4096 

 int main()
{
    char Buffer[DEFAULT_BUFFER_SIZE + 1];
    WSAData wsd; 

     if(WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
        printf("Winsock 초기화 에러!\n");
        return -1;
    } 

     SOCKET ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

     if (ls == INVALID_SOCKET) {
        printf("소켓 생성 실패!\n");
        return -1;
    } 

    sockaddr_in service;
    memset(&service, 0, sizeof(service));
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;
    service.sin_port = htons(DEFAULT_PORT);
   
    if (bind(ls, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
        printf("bind 실패!\n");
        return -1;
    }
   
    if (listen(ls, 1) == SOCKET_ERROR) {
        printf("listen() 실패!\n");
        return -1;
    } 

     SOCKET as; 
     printf("클라이언트 연결 대기.\n"); 

     while (1) {
        as = accept(ls, NULL, NULL);
        if (as == SOCKET_ERROR) continue;
        printf("클라이언트 연결됨.\n"); 

         int nbyte = recv(as, Buffer, DEFAULT_BUFFER_SIZE, 0); 

         if (nbyte <= 0) {
            printf("recv 에러!\n");
            break;
        }
       
        Buffer[nbyte] = '\0';
        printf("에코 : %s\n", Buffer); 

         send(as, Buffer, nbyte, 0); 

         if (strncmp(Buffer, "quit", 4) == 0) {
            printf("클라이언트의 요청에 의해 서버 종료\n");
            break;
        }
       
        closesocket(as);
        printf("클라이언트 연결 해제.\n새로운 클라이언트 연결 대기.\n");
    } 

     closesocket(ls);
    WSACleanup();
   
    return 0;
} 

2.2 클라이언트(Client)

#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_PORT 2924
#define DEFAULT_BUFFER_SIZE 4096
int main(int argc, char** argv)
{
    char Buffer[DEFAULT_BUFFER_SIZE + 1];
    WSAData wsd;
    if (argc != 2) {
        printf ("사용법 : %s [IP 주소]\n", argv[0]);
        return -1;
    }
   
    if(WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
        printf("Winsock 초기화 에러!\n");
        return -1;
    }
   
    SOCKET cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   
    if (cs == INVALID_SOCKET) {
        printf("소켓 생성 실패!\n");
        return -1;
    }
   
    sockaddr_in client;
    memset(&client, 0, sizeof(client));
 
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = inet_addr(argv[1]);
    client.sin_port = htons(DEFAULT_PORT);
   
    if (connect(cs, (SOCKADDR *)&client, sizeof(client)) == SOCKET_ERROR) {
            printf("connect 에러!\n");
            return -1;
    }
    printf("입력 : ");
    gets(Buffer);
    send(cs, Buffer, strlen(Buffer), 0);
    int nbyte = recv(cs, Buffer, DEFAULT_BUFFER_SIZE, 0);
    Buffer[nbyte] = '\0';
    printf("에코 : %s", Buffer);
    closesocket(cs);
    WSACleanup();
   
    return 0;
} 

3.Linux or Unix or NDS

3.1 서버(Server)

 윈도우쪽 소스를 조금 수정했다.

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int main()
{
    char Buffer[256 + 1];
    int ls = socket(AF_INET, SOCK_STREAM, 0);
    if (ls == INVALID_SOCKET) {
        printf("소켓 생성 실패!\n");
        return -1;
    }
    sockaddr_in service;
    memset(&service, 0, sizeof(service));
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;
    service.sin_port = htons(DEFAULT_PORT);
   
    if (bind(ls, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
        printf("bind 실패!\n");
        return -1;
    }
   
    if (listen(ls, 1) == SOCKET_ERROR) {
        printf("listen() 실패!\n");
        return -1;
    }
    int as;
    printf("클라이언트 연결 대기.\n");
    while (1) {
        as = accept(ls, NULL, NULL);
        if (as == SOCKET_ERROR) continue;
        printf("클라이언트 연결됨.\n");
        int nbyte = recv(as, Buffer, DEFAULT_BUFFER_SIZE, 0);
        if (nbyte <= 0) {
            printf("recv 에러!\n");
            break;
        }
       
        Buffer[nbyte] = '\0';
        printf("에코 : %s\n", Buffer);
        send(as, Buffer, nbyte, 0);
        if (strncmp(Buffer, "quit", 4) == 0) {
            printf("클라이언트의 요청에 의해 서버 종료\n");
            break;
        }
       
        close(as);
        printf("클라이언트 연결 해제.\n새로운 클라이언트 연결 대기.\n");
    }
    close(ls);
   
    return 0;
} 

3.2 클라이언트(Client)

 NDS에서 사용하는 예제를 조금 수정했다.

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int main(void)
{ 
     //////////////////////////////////////////////////////////////////////////
    // Let's send a simple HTTP request to a server and print the results! 

     // store the HTTP request for later
    const char * request_text =
        "GET / HTTP/1.1\r\n\r\n";
//        "Host: www.akkit.org\r\n"
//        "User-Agent: Nintendo DS\r\n\r\n"; 

     // Find the IP address of the server, with gethostbyname
    // DNS를 이용해서 Name으로 IP를 얻는다.
    // 2007/10/24 현재, 아직 잘 안되는 것 같다.
    iprintf( "DNS Resolve Start\n" );
    //struct hostent * myhost = gethostbyname( "www.google.org" );
    //iprintf("Found IP Address![www.google.org] [%08X]\n",
    //        myhost->h_addr_list[0] );
 
    // Tell the socket to connect to the IP address we found, on port 80 (HTTP)
    struct sockaddr_in sain;
    sain.sin_family = AF_INET;
    sain.sin_port = htons(80);
    // Host Resolve가 끝났으면 아래와 같이 사용한다.
    //sain.sin_addr.s_addr= *( (unsigned long *)(myhost->h_addr_list[0]) );
    // 아래는 google의 IP 주소이다.
    sain.sin_addr.s_addr = inet_addr( "72.14.235.99" );   
   
    // Create a TCP socket
    int my_socket;
    fd_set readfd;
    fd_set tempfd;
    struct timeval stTime;
    struct timeval stTempTime;
    int iRet;
   
    stTime.tv_sec = 5;
    stTime.tv_usec = 0;
Retry:
   
    my_socket = socket( AF_INET, SOCK_STREAM, 0 );
    iprintf("Created Socket!\n");
   
    iprintf( "Try To Connect\n" );
    connect( my_socket,(struct sockaddr *)&sain, sizeof(sain) );
    iprintf("Connected to server!\n"); 

     // send our request
    send( my_socket, request_text, strlen(request_text), 0 );
    iprintf("Sent our request!\n"); 

     // Print incoming data
    iprintf("Printing incoming data:\n"); 

     int recvd_len;
    char incoming_buffer[256]; 

     iprintf("Recv Start\n");
    FD_ZERO( &readfd );
    FD_SET( my_socket, &readfd );
    while( 1 )
    {
        tempfd = readfd;
        stTempTime = stTime;
        iRet = select( my_socket + 1, &tempfd, NULL, NULL, &stTempTime );
        if( iRet > 0 )
        {
            recvd_len = recv( my_socket, incoming_buffer, 255, 0 );
            iprintf("Recv End Size[%d]\n", recvd_len );
            if( recvd_len > 0 )
            { // data was received!
                incoming_buffer[ recvd_len ] = 0; // null-terminate
                iprintf( incoming_buffer );
            }
        }
        // Time Expired
        else if( iRet == 0 )
        {
            iprintf( "Time Expired If You Press B, Exit Receiving Process\n" );
            if( ~REG_KEYINPUT & KEY_B )
            {
                break;
            }
        }
        else
        {
            iprintf( "Error~~!!\n" );
            break;
        }
    }
   
    iprintf("Other side closed connection!\n"); 

     shutdown(my_socket,0); // good practice to shutdown the socket. 

     close(my_socket); // remove the socket.
   
    iprintf( "Press Any A Key To Retry\n" );
    while( REG_KEYINPUT & KEY_A ) ;
    
    goto Retry; 

     while(1); 

     return 0;
} 

4.마치면서…

 간단하게나마 코드 조각을 정리해 보았다. 이제 구글링할 필요없이 바로 붙여넣고 쓰면된다. 다시한번 네트워크의 세계로 빠져보자.