[Wireless][Network] Extracting SSID from Beacon Packet

Beacon Packet에서 SSID 추출


Packet 구조

Monitor mode를 통해 얻은 Beacon 패킷의 구조는 아래와 같다.

Packet은 ” RadioTap + 802.11 Data(Beacon, Probe etc..) ” 형식으로 이루어져 있다.

각각의 프레임이 담고 있는 정보를 대강 정리해 보면,

RadioTap

  • 무선 신호의 주파수
  • 신호 세기 정보 (dbm)
  • 안테나 정보
  • Etc…

802.11 Beacon Frame

  • Type 정보 ( beacon인지 probe인지 등…)
  • Source, Destination Mac address 정보
  • Etc…

802.11 Wireless LAN

  • 802.11b/g/n 등 지원가능한 전송방식 종류
  • 암호화 종류 및 지원 여부
  • SSID
  • channel
  • 지원 가능한 속도 (Mbit/s)
  • WPS 지원 여부
  • Etc…

SSID 추출

802.11 Wireless LAN에서 빨간색 네모 표시된 “53 41 4d 53 55 4e 47” 부분이 SSID 이다. (위치: 74byte)
그리고 앞에 파란색으로 네모 표시된 “07“은 SSID 의 길이 정보로 해당 길이 만큼 SSID 정보를 읽으면 된다. (위치: 73byte)


Source Code

간단한 SSID 출력 프로그램
#include <cstdio>
#include <pcap.h>

enum{CMD, INF};

void procPacket(u_char *arg, const struct pcap_pkthdr *h, const u_char *p)
{
    int len;
    len = 0;
    u_int8_t ssid_len = *(p+73); // SSID length 정보의 위치

    for(int i=0; i < ssid_len; i++){ // SSID length만큼 읽어드림
        printf("%c", *(p+74+i));
    }

    printf("\n");
    return;
}


int main(int argc, char *argv[]){
    pcap_t *handle;
    char *dev = argv[INF];
    char errbuf[PCAP_ERRBUF_SIZE];

    struct bpf_program fp;

    handle = pcap_open_live(dev, BUFSIZ, 0, 100, errbuf);
    if(NULL == handle){
        printf("%s", errbuf);
        return 1;
    }

    if(-1 == pcap_compile(handle, &fp, "type mgt subtype beacon",0,PCAP_NETMASK_UNKNOWN)){
        printf("compile error\n");
        return 1;
    }
    if (pcap_setfilter(handle, &fp) == -1){
        printf("setfilter error\n");
        return 1;
    }

    pcap_loop(handle, -1, procPacket, NULL);

}
사용된 함수별 참고 포스트

 

[libpcap][func] pcap_loop (with pcap_handler)

int pcap_loop(pcap_t *p, int cnt,
                        pcap_handler callback, u_char *user)


pcap_loop 함수는 pcap_open_live 함수pcap_create 함수로 만들어진 pcap_t handler를 cnt번 만큼 반복하는 함수이다.

 

Description[document]

pcap_t *p: packet을 다루기 위한 handler
int cnt: 최대 cnt 만큼의 callback을 반복 한다. (0또는 -1인 경우 무한히 반복)
pcap_handler callback: cnt 만큼 반복할 함수이다.
u_char *user: callback 함수로 전달 되는 포인터이다.


Return

cnt만큼 반복했거나, savefile의 끝에 도달했다면 0을 반환한다.
오류가 발생했을 때 -1을 반환하며, 만약 패킷이 처리되기전에 pcap_breakloop()가 호출 되었다면, -2를 반환한다.
실시간 패킷 캡처 버퍼에서 타임 아웃이 발생해도 에러를 리턴하지 않고 지속적으로 패킷을 받기위해 대기한다.


pcap_handler callback

typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, const u_char *byte);

pcap_handler는 u_char *user, struct pcap_pkthdr *h, uchar *byte를 인자로 가지고 있는 함수 포인터 이다.

 

Description

u_char *user: pcap_loop 에서 전달 받은 포인터 값
struct pcap_pkthdr *h: pcap_pkthdr 구조체 포인터
u_char *byte: 패킷의 시작 byte를 가르킨다


Return


잡담

이제 대부분의 함수들이 모였다. 지금 까지 정리된 것 만으로도 거의 모든 패킷을 캡처 할 수 있으며,
잘만 만든다면야 모든 패킷을 다룰 수 있다.

아, 차후에 pcap_dispatch함수를 정리하면서 pcap_loop와 어떤 점이 다른지 정리해보려고 한다.

[libpcap][func] pcap_setfilter

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)


pcap_setfilter는 pcap_compile 함수를 통해 컴파일된 struct bpf_program의 포인터를 pcap_t(handler)에 적용 시켜주는 함수이다.

 

Description[document]

pcap_t *p: filter를 적용시킨 handler
bpf_program *fp: pcap_compile 함수를 통해 컴파일 시킨 bpf구조체의 포인터


Return

설정에 성공하면 0을 반환하고 실패하면 -1을 반환한다.
또한 에러 내용은 pcap_geterr() or pcap_perror()로 볼 수 있다. 

 

[libpcap][func] pcap_compile

int pcap_compile(pcap_t *p, struct bpf_program *fp, const char
                             *str,
int optimize, bpf_u_int32 netmask)


pcap_compile은 문자열로 이루어진 필터 표현식을 프로그램으로 컴파일하기 위한 함수이다.
자세한 표현식은 pcap_filter문서를 참고하도록 하자.

 

Description[document]

pcap_t *p: paket을 다루기위한 handler
struct bpf_program *fp: 표현식을 프로그램으로 만들어 포인터로 반환함 (기회가 되면 정리)
char *str: 표현식 포인터
int optimize: 코드 최적화 여부를 선택
bpf_u_int32: 네트워크 넷마스크


Return

컴파일에 성공하면 0을 반환하고 실패하면 -1을 반환한다.
또한 에러 내용은 pcap_geterr() or pcap_perror()로 볼 수 있다. 


부록

  • bpf_program에서 bpf는 Berkeley Packet Filter의 약자로 데이터 링크에 raw 인터페이스 패킷을 제어 할 수 있다.
  • optimize는 gcc에서의 -O옵션과 유사한 의미라고 한다.
    실제 사용함에 있어서 큰 차이는 없겠지만 코딩한 대로 이루어져야하는 네트워크 특성상 0으로 설정하고 사용하는 경우가 잦다.

[libpcap][info] pcap_open_live() Vs pcap_create() & pcap_activate()

pcap_open_live() Vs pcap_create() & pcap_activate()


libpcap에선 handler를 얻기 위해 pcap_open_live() 혹은 pcap_create() & pcap_activate() 함수를 사용한다.
이 함수들의 다른점은 무엇인지 알아보자.

 

What is difference?


본론부터 말하자면, 전혀 다르지 않다.

그 이유는 pcap_open_live() 함수의 소스코드를 보면 알 수 있는데,

[libpcap github]

pcap_t *
pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf)
{
	pcap_t *p;
	int status;
#ifdef HAVE_REMOTE
	char host[PCAP_BUF_SIZE + 1];
	char port[PCAP_BUF_SIZE + 1];
	char name[PCAP_BUF_SIZE + 1];
	int srctype;

	/*
	 * Retrofit - we have to make older applications compatible with
	 * remote capture.
	 * So we're calling pcap_open_remote() from here; this is a very
	 * dirty hack.
	 * Obviously, we cannot exploit all the new features; for instance,
	 * we cannot send authentication, we cannot use a UDP data connection,
	 * and so on.
	 */
	if (pcap_parsesrcstr(device, &srctype, host, port, name, errbuf))
		return (NULL);

	if (srctype == PCAP_SRC_IFREMOTE) {
		/*
		 * Although we already have host, port and iface, we prefer
		 * to pass only 'device' to pcap_open_rpcap(), so that it has
		 * to call pcap_parsesrcstr() again.
		 * This is less optimized, but much clearer.
		 */
		return (pcap_open_rpcap(device, snaplen,
		    promisc ? PCAP_OPENFLAG_PROMISCUOUS : 0, to_ms,
		    NULL, errbuf));
	}
	if (srctype == PCAP_SRC_FILE) {
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\"");
		return (NULL);
	}
	if (srctype == PCAP_SRC_IFLOCAL) {
		/*
		 * If it starts with rpcap://, that refers to a local device
		 * (no host part in the URL). Remove the rpcap://, and
		 * fall through to the regular open path.
		 */
		if (strncmp(device, PCAP_SRC_IF_STRING, strlen(PCAP_SRC_IF_STRING)) == 0) {
			size_t len = strlen(device) - strlen(PCAP_SRC_IF_STRING) + 1;

			if (len > 0)
				device += strlen(PCAP_SRC_IF_STRING);
		}
	}
#endif	/* HAVE_REMOTE */

	p = pcap_create(device, errbuf);
	if (p == NULL)
		return (NULL);
	status = pcap_set_snaplen(p, snaplen);
	if (status < 0)
		goto fail;
	status = pcap_set_promisc(p, promisc);
	if (status < 0)
		goto fail;
	status = pcap_set_timeout(p, to_ms);
	if (status < 0)
		goto fail;
	/*
	 * Mark this as opened with pcap_open_live(), so that, for
	 * example, we show the full list of DLT_ values, rather
	 * than just the ones that are compatible with capturing
	 * when not in monitor mode.  That allows existing applications
	 * to work the way they used to work, but allows new applications
	 * that know about the new open API to, for example, find out the
	 * DLT_ values that they can select without changing whether
	 * the adapter is in monitor mode or not.
	 */
	p->oldstyle = 1;
	status = pcap_activate(p);
	if (status < 0)
		goto fail;
	return (p);
fail:
	if (status == PCAP_ERROR)
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device,
		    p->errbuf);
	else if (status == PCAP_ERROR_NO_SUCH_DEVICE ||
	    status == PCAP_ERROR_PERM_DENIED ||
	    status == PCAP_ERROR_PROMISC_PERM_DENIED)
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", device,
		    pcap_statustostr(status), p->errbuf);
	else
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device,
		    pcap_statustostr(status));
	pcap_close(p);
	return (NULL);
}

보면 pcap_open_live()함수 안에 pcap_create()등의 함수들이 들어가 잇는 걸 볼 수 있다.

위 코드가 이해하기 힘들다면 아래 간략하게 만든 코드를 보자,

pcap_t *
pcap_open_live(const char *source, int snaplen, int promisc, int to_ms, char *errbuf)
{
        pcap_t *p;
        int status;

        p = pcap_create(source, errbuf);
        if (p == NULL)
                return (NULL);
        status = pcap_set_snaplen(p, snaplen);
        if (status < 0)
                goto fail;
        status = pcap_set_promisc(p, promisc);
        if (status < 0)
                goto fail;
        status = pcap_set_timeout(p, to_ms);
        if (status < 0)
                goto fail;

        [some code to make the backwards-compatibility stuff work a little better elided]

        status = pcap_activate(p);
        if (status < 0)
                goto fail;
        return (p);
fail:
        [error-message-generation-code elided]

        pcap_close(p);
        return (NULL);
}

>> 즉, pcap_open_live() 함수는 단순히 pcap_create()pcap_activate()와 같은 handler를 얻어올 때 많이 사용되는 함수들을 묶어 놓은 것에 불과 한 것이다.

 

[libpcap][func] pcap_activate

int pcap_activate(pcap_t *p)


pcap_activate는 pcap_create 함수로 생성된 pcap_t handler를 활성화 시켜주는 함수이다. 

 

Description[document]

pcap_t *p: 활성화 시킬 handler 포인터

 

Return

pcap_activate는 warning없이 성공하면 0을 반환하고, warnings와 함께 성공할 경우 0이 아닌 양수 값을 반환하고,
Error가 발생하면 음수 값을 반환한다.

  • 발생 가능한 warning list

PCAP_WARNING_PROMISC_NOTSUP 무차별(Promiscuous)모드가 설정됬으나, 지원하지 않음
PCAP_WARNING_TSTAMP_TYPE_NOTSUP pcap_set_tstamp_type 함수 호출시 이력한 time stamp형식이 지원되지 않음
PCAP_WARNING  이외 warning 이 발생. pcap_geterr 함수나 pcap_perror와 같은
pcap_t를 인자로 받는 함수로 warning 메시지를 가져올 수 있음
  • 발생 가능한 Error list

PCAP_ERROR_ACTIVATED 전달 받은 handle이 이미 활성화 되어있음
PCAP_ERROR_NO_SUCH_DEVICE 전달 받은 handle이 가르키는 인터페이스가 없음
PCAP_ERROR_PERM_DENIED 캡처할 수 있는 권한이 없음
PCAP_ERROR_PROMISC_PERM_DENIED 캡처할 권한은 있지만 무차별 모드에 대한 권한이 없음
PCAP_ERROR_RFMON_NOTSUP 모니터 모드가 설정 되었으나, 지원하지 않음
PCAP_ERROR_IFACE_NOT_UP 인터페이스가 켜져 있지 않음
PCAP_ERROR  이외 Error 이 발생. pcap_geterr 함수나 pcap_perror와 같은
pcap_t를 인자로 받는 함수로 error 메시지를 가져올 수 있음

[libpcap][func] pcap_open_live

pcap_t *pcap_open_live(const char *device, int snaplen,
                                     int promisc, int to_ms, char *errbuf)


pcap_open_live는 네트워크 상에 packet의 handler를 얻기위해 사용되는 함수이다.

 

Discreption[document]

char *device: 디바이스(인터페이스)의 이름
int snaplen: 스넵샷의 길이를 설정
int promisc: 무차별(promisc)모드 설정
int to_ms: ms(마이크로 세컨트) 간격으로 패킷을 캡처한다.
char *errbuf: 함수의 에러를 저장 할 포인터


Return

pcap_t 구조체, 즉 packet의 handler를 반환한다.
만약 pcap_t를 가져오지 못하면 NULL 를 반환하고 errbuf에 에러 메시지를 담는다.