2013년 10월 3일 목요일

[Libpcap] Libpcap 정리


#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> // for TCP protocol in transport layer
#include <arpa/inet.h> // include for inet_ntoa()

#include <netinet/in.h> // defined TCP Procotol Type information
                           /* TCP 및 IP Protocol 에 관련된 정보 포함.
                              대표적으로 well-known port number 및 IP 체계 */

#include <pcap.h> // pcap func used errbuf[], errbuf size = 256
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

#define PCAP_CNT_MAX 1
#define PCAP_SNAPSHOT 1024
#define PCAP_TIMEOUT 100
#define FILTER_RULE "host 192.168.100.100" // Filtering taget IP and Port Number

void packet_view(unsigned char* user, const struct pcap_pkthdr *h, const unsigned char *p);

int main(int argc, char* argv[]) {
char* dev;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net;
bpf_u_int32 mask;                         // struct in_addr은 <netinet/in.h>에 정의.
struct in_addr net_addr, mask_addr;
pcap_t *pd;                                 // struct bpf_program은 <pcap-bpf.h>에 정의.
struct bpf_program fcode;
/* struct bpf_program {
u_int bf_len;
struct bpf_insn *bf_insns;
 } */

/* pcap_lookupdev is find network interface. if finding network interface, it return that found network interface name.
    pcap_lookupdev : fail => return null / success => char* pointer (NIC name) */

if(!(dev = pcap_lookupdev(errbuf))) {
perror(errbuf);
exit(1);
}

if(pcap_lookupnet(dev, &net, &mask, errbuf) < 0) {
perror(errbuf);
exit(1);
}

/* pcap_lookupnet이 반환하는 net, mask 값은 network address, 즉 정수형으로 이루어져있기에 이 값을 숫자 사이에 점을 찍는 IP주소표시법으로 구성된 ASCII 문자열을 가리키는 문자열 포인터를 리턴하는 inet_ntoa()를 이용하여 일반적인 IP주소 형태로 바꾸어 출력함. */

net_addr.s_addr = net;
mask_addr.s_addr = mask;

printf("Device : %s\n", dev);
printf("Net Address : %s\n", inet_ntoa(net_addr)); // 네트워크 주소(정수형) => ASCII (우리가 알고있는 IP주소)
printf("Netmask : %s\n", inet_ntoa(mask_addr));

// pcap_open_live (NIC, capture MAX byte, timeout, errbuf)).
   pcap_open_live is open packet capture interface and that pointer return */

if((pd = pcap_open_live(dev, PCAP_SNAPSHOT, 1, PCAP_TIMEOUT, errbuf)) == NULL) {
perror(errbuf);
exit(1);
}

if(pcap_compile(pd, &fcode, FILTER_RULE, 0, mask) < 0) {
perror(pcap_geterr(pd));
exit(1);
}

if(pcap_setfilter(pd, &fcode) < 0) {
perror(pcap_geterr(pd));
exit(1);
}

// packet read 관련 API에서는 패킷을 읽었을때, Demultiplexing이 되지 않은 완전한 구조의 패킷을 넘겨줌
if(pcap_loop(pd, PCAP_CNT_MAX, packet_view, 0) < 0) {
perror(pcap_geterr(pd));
exit(1);
}
pcap_close(pd);
return 0;

}

void packet_view(unsigned char* user, const struct pcap_pkthdr *h, const unsigned char *p) {
struct ip *iph;
struct tcphdr *tcph;
struct udphdr *udph;
struct ether_header *etherh;
unsigned short e_type;
int cnt;

/* ethernet header와 IP header의 경우 demultiplexing 과정을 거치기 위해 상위 layer의 protocol type을 지정함. ethernet header의 경우 ether_type, IP header는 ip_p가 각 상위 layer의 protocol type을 알려주기 위해 사용됨. 해당 내용은 OSI 7계층의 encapsulation 참조 */

etherh = (struct ether_header *)p;
e_type = ntohs(etherh->ether_type);

printf("-----------------------------------------  \n");
printf("[[ Layer 2 :: Ethernet Header ]]\n");
        // out Ehternet Header information
printf("[ Destination hardware address :: %02x", etherh->ether_dhost[0]);

for(cnt = 1; cnt < ETHER_ADDR_LEN; cnt++)
printf(":%02x", etherh->ether_dhost[cnt]);

printf("]\n[ Source hardware address      :: %02x", etherh->ether_shost[0]);

for(cnt = 1; cnt < ETHER_ADDR_LEN; cnt++)
printf(":%02x", etherh->ether_shost[cnt]);
printf("]\n[ Type :: %x ]\n", etherh->ether_type);

printf("\t-----------------------------------------  \n");
p += sizeof(struct ether_header);

// IP, ARP, RARP Header analyze
if (e_type == ETHERTYPE_IP) {

iph = (struct ip *)p;

printf("\t[IP Packet!]\n");
printf("\t-----------------------------------------  \n");
printf("\t[ IP version                :: %d ]\n", iph->ip_v);
printf("\t[ Header Length             :: %d ]\n", iph->ip_hl);
printf("\t[ Type of Service           :: %d ]\n", iph->ip_tos);
printf("\t[ Total Length              :: %d ]\n", iph->ip_len);
printf("\t[ Identification            :: %d ]\n", iph->ip_id);
printf("\t[ Flags  ================== ::  ]\n");

if ((iph->ip_off) == IP_RF)
printf("\t[      || Reserved flagment :: 0x%x ]\n", IP_RF);
else if((iph->ip_off) == IP_DF)
printf("\t[      || Don't  flagment   :: 0x%x ]\n", IP_DF);

else if((iph->ip_off) == IP_MF)
printf("\t[      || More flagment     :: 0x%x ]\n", IP_MF);

else
printf("\t[      || Not support               ]\n");

printf("\t[        ========================== ]\n");
printf("\t[ Fragment Offset           :: %d ]\n", iph->ip_off);
printf("\t[           TTL             :: %d ]\n", iph->ip_ttl);
printf("\t[         Protocol          :: %d ]\n", iph->ip_p);
printf("\t[ Header Checksum           :: %d ]\n", iph->ip_sum);
printf("\t[ Source      IP address    :: %s ]\n", inet_ntoa(iph->ip_src));
printf("\t[ Destination IP address    :: %s ]\n", inet_ntoa(iph->ip_dst));
printf("\t-----------------------------------------  \n");

/* TCP Header analyze.
   Transport layer의 type을 알려주는 ip_p는 <netinet/ip.h>, IPPTORO_TCP의 내용은 <netinet/in.h> 참조. */
if (iph->ip_p == IPPROTO_TCP) {

tcph = (struct tcphdr *)(p + iph->ip_hl *4) ;

printf("\t\t[ TCP Packet!]\n");
printf("\t\t-----------------------------------------  \n");
printf("\t\t[ Source      Port :: %d ]\n", ntohs(tcph->source));
printf("\t\t[ Destination Port :: %d ]\n", ntohs(tcph->dest));
}

else if (iph->ip_p == IPPROTO_UDP) {

udph = (struct udphdr *)(p + iph->ip_hl *4) ;

printf("\t\t[ UDP Packet!]\n");
printf("\t\t-----------------------------------------  \n");
printf("\t\t[ Source      Port :: %d ]\n", ntohs(udph->source));
printf("\t\t[ Destination Port :: %d ]\n", ntohs(udph->dest));
}
}

else if(e_type == ETHERTYPE_ARP) {
printf("ARP!!!\n");
}

else if(e_type == ETHERTYPE_REVARP) {
printf("RARP!!!\n");
}

else {
printf("Not Captured packet!\n");
}

}

2013년 9월 29일 일요일

[MFC] MFC에서 UI Thread를 이용한 Dialog 호출 방법

==========================================================
출처 :
 http://blog.naver.com/PostView.nhn?blogId=aladdin07&logNo=150074142378&categoryNo=8&viewDate=&currentPage=1&listtype=0
==========================================================

1.목적
메인 프로그램에서 메뉴 항목을 선택하면 별도의 Dialog가 생성되어 거기서 정보를 입력 받는다.
단, 메인프로그램과 생성된 Dialog는 상호 독립적으로 구동되어야 한다.
(즉, 메인프로그램에서 DoModal()로 Dialog를 구동시키지 않고 별도 Thread로 구동 시켜야 한다)

2.방법
CWinThread Class를 활용한다.

① Class Wizard를 사용하여 CWinThread를 Base Class로하는 Class(CDlgThread)를 정의한다.
    Header File(DlgThread.h)에는 'DECLARE_DYNCREATE(CDlgThread);'
    Source File(DlgThread.cpp)에는 'IMPLEMENT_DYNCREATE(CDlgThread ,CWinThread)'
    ClassWizard에 의해 자동으로 추가됩니다.

DlgThread.h를 수정한다.
    - protected로 정의된 Constructor DlgThread()와 Deconstructor ~DlgThread()를 public으로
      바꾸어 준다. (이유는 해보면 안다)

      public:
           DlgThread();
           ~DlgThread();

③ Dialog 변수를 정의 한다.
    public:
         CDialog* m_pDlg;

④ CDlgThread의 InitInstance()에서 다음과 같이 독립실행 시킬 Dialog(CMyDlg)를 생성한다.
    이 작업은 Thread에 Dialog를 Binding 시키는 과정이다.
    BOOL CDlgThread::InitInstance()
    {
        m_pDlg = new CMyDlg();
        m_pDlg->ShowWindow( SW_SHOW );
        m_pDlg->UpdateWindow();
        return TRUE;
    }

⑤ CMyDlg Class의 Constructor를 수정한다.
    CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
         : CDialog(CMyDlg::IDD, pParent)
    {
        Create( IDD_MyDlg );    <== 요놈 추가
        //{{AFX_DATA_INIT(CDlgClub)
        // NOTE: the ClassWizard will add member initialization here
        //}}AFX_DATA_INIT
    }

⑥ 필요한 곳에서 다음과 같이하여 Thread를 구동 시키면 된다.

    CDlgThread*  m_pThread = new CDlgThread();
    m_pThread->CreateThread();

※ 상기와 같이하면 UI Thread를 구현할 수 있다..간단하죠~~잉..?
    위와 같이 구동된 Thread Dialog는 OK, CANCEL 버튼을 눌러도 소멸되지 않습니다.
    단지 HIDE 상태로 되는 거죠..
    따라서, 명시적으로 Thread Dialog를 죽이지 않았다면..메인 프로그램이 끝날때까지 Thread로
    남아 있게되는 거죠....요놈을 다시 사용하고 싶을때는 다음 두 줄로 다시 나타나게 만들면 된다.
    m_pThread->m_pDlg->ShowWindow( SW_SHOW );
    m_pThread->m_pDlg->UpdateWindow();
   
※ 명시적으로 Thread Dialog를 종료시킬려면 간단하다.
    Thread Dialog에 WM_QUIT Message를 보내면 된다.
    어떻게..?  PostThreadMessage()를 사용하면 된다.

    m_pThread->PostThreadMessage(WM_QUIT,0,0);

2013년 9월 28일 토요일

[WINPCAP] MFC로 pcap_findalldevs() 함수를 사용했는데 글자가 깨질때

========================================================
출처 : https://kldp.org/node/105221
========================================================












MFC에서 pcap_findalldevs()를 사용했는데 글자가 깨지는건 pcap_findalldevs() 함수가 사용하는 구조체 pcap_if가 가진 변수 name이 '유니코드'가 아닌 '멀티바이트' 형태로 저장하는데 현재 프로젝트가 유니코드로 설정 되어있어서라고 한다.




















위의 그림과 같이 '멀티바이트 문자 집합 사용'으로 바꾸면 아래와 같이
정상적으로 실행된다.



2013년 4월 24일 수요일

[wireshark] .PCAP 파일 포맷(File Format)


패킷 캡쳐시 리눅스 상에서는 Libpcap, 윈도우 상에서는 Winpcap 라이브러리를 사용하여 패킷을 수집할 경우, 확장자가 .PCAP이라는 파일 포맷(File Format)으로 처리된다.

그렇다면 .PCAP이라는 파일 포맷(File Format)은 어떤 모습일까??

아래의 그림과 같이 가장 처음으로 Global Header란 필드(Field)와 그 다음으로 Packet Header와 Packet Data란 필드(Field)가 한 쌍을 이루어 계속해서 나타나는 모습을 띄고 있다.


Global Header
Packet Header
Packet Data
Packet Header
Packet Data
Packet Header
Packet Data


그럼, 여기서 Global Header와 Packet Header, Packet Data란 무엇을 뜻하는 건지 알아보자.

========================================================

Global Header

typedef struct pcap_hdr_s {
        guint32 magic_number;   /* magic number */
        guint16 version_major;  /* major version number */
        guint16 version_minor;  /* minor version number */
        gint32  thiszone;       /* GMT to local correction */
        guint32 sigfigs;        /* accuracy of timestamps */
        guint32 snaplen;        /* max length of captured packets, in octets */
        guint32 network;        /* data link type */
} pcap_hdr_t;
Global Header는 libpcap file이라는 것을 나타내는 헤더이다. 
실제로 wireshark나 기타 프로그램에서 packet capture를 실행하여 실제로 패킷을 캡쳐한 후 확장자를 .pcap으로 저장하였을 경우 캡쳐한 패킷의 가장 처음 부분에 붙여지는 패킷 캡쳐시의 전체적인 정보이다.

Global Header의 전체 크기는 192bit, 즉 24byte이다.

Record (Packet) Header

typedef struct pcaprec_hdr_s {
        guint32 ts_sec;         /* timestamp seconds */
        guint32 ts_usec;        /* timestamp microseconds */
        guint32 incl_len;       /* number of octets of packet saved in file */
        guint32 orig_len;       /* actual length of packet */
} pcaprec_hdr_t;
Record (Packet) Header는 해당 패킷을 캡쳐했을 당시의 시간과 패킷 길이에 관한 정보로 이루어져 있다.

Record (Packet) Header의 전체 크기는 128bit, 즉 16byte이다.

============================================================================


Q_01. 그럼 Global Header와 Packet Header가 .pcap 파일의 가장 처음에 나오는 값들이라면 
          실제로 캡쳐한 최초 패킷의 데이터는 패킷의 어느위치에서부터 나오는 것인가?

Global Header와 Reacore (Packet) Header의 뒤를 이어 실제 캡쳐한 패킷에 대한 정보가 나오므로, 최초의 패킷 데이터는 Global Header + Packet Header의 크기만큼 이후부터 나오게 된다.
이후 Packet Data의 정보가 끝나면 그 다음 Packet Data에 대한 Packet Header가 새로 추가되어 하나의 쌍을 이루는 형태를 이루게 된다.



Global Header
Packet Header
Packet Data
Packet Header
Packet Data
Packet Header
Packet Data
...
 ----------------------------------------
 |       24byte      |      16byte       |      Packet Data 
 ----------------------------------------           = 실제 캡쳐한 패킷 정보
 | Global Header + Packet Header         (Ethernet 환경에서 캡쳐된 패킷이 대부분이기에 
 |                      => 40byte          |       Ethernet Protocol 정보로 시작된다고 보면 된다)



보통 우리가 쓰는 Internet 환경에서는 DataLink Layer Protocol을 Ethernet Protocol을 쓰기 때문에 Packet Header 부분 뒤에 나오는 Packet Data의 시작 부분에서는 Destination Hardware Address, 즉 목적지 맥주소가 나오게 된다.




실제로 .pcap 확장자로 저장한 캡쳐한 패킷 정보를 HxD 라는 헥사 에디터로 분석할 시 보여지는 16진수형 데이터들이다. 좌측에는 16진수 형태로, 그리고 우측에는 16진수에 해당하는 ASCII Code로 처리되어 보여지게 된다.

좌측에 보여지는 파란색으로 드래그 된 값들이 실제로 16진수로 표현된 Global Header와 최초로 캡쳐된 패킷과 쌍을 이루는 최초 패킷의 Packet Header에 대한 정보이다.

이후 나오는 값인 00 00 C0 9F A0 97 부터는 실제로 캡쳐된 패킷에 대한 정보들에 대한 값이다.


============================================================================

          2) 도서 : SECURITY 네트워크 패킷 포렌식 (최경철 지음, SECU BOOK)

============================================================================





spring에서 mariadb로 연동후 웹에서 접근시 jdbc 오류가 발생할때

* 본문은 [코드로 배우는 스프링 웹 프로젝트, 구멍가게 코딩단 지음] 도서를 공부하면서 정리한 글임을 밝힙니다.