재미있는 코드가 있어서 소개를 할까 합니다. Java 기반의 HTTP decoder입니다.


출처 : http://xeraph.com/5226932


주석 달아 봤습니다. C 언어와 같은 imperative programming에 익숙한 사람에게 유익할 듯 싶습니다.


public class NetworkForensicPuzzle {

    public static void main(String[] args) throws IOException { // C언어의 main 과 비슷.

        try {

            EthernetDecoder eth = new EthernetDecoder(); // Ethernet 환경에서 패킷 분석을 하기 위한 decodet entry point 클래스

            IpDecoder ip = new IpDecoder();

            TcpDecoder tcp = new TcpDecoder(new TcpSessionTracker());

            HttpDecoder http = new HttpDecoder();

            HttpCallback callback = new HttpCallback(); // 사용부터 하고 선언은 뒤에 해도 되네요. 우왕ㅋ굳ㅋ

 

            eth.register(EthernetType.IPV4, ip);      // register라는 메소드는 모든 Decoder의 부모 클래스에서 virtual로 선언을 하고

                                                      // 각각의 하부 클래스에서 implementation(override)한 듯 보임.

                                                      // register의 처음 인자는 조건, 두번째 인자는 그 다음 Decoder인 듯.

            ip.register(InternetProtocol.TCP, tcp);

            tcp.register(80, http);

            tcp.getSegmentCallbacks().register(callback); // tcp segment는 뭘까나?  아무래도 TCP Data 패킷이 잡혔을 때

                                                          // 발생시키는 callback을 의미하는 것 같은데... 맞나?

            http.register(callback);                      // 상기 Decoder와 register 메세지의 형태가 좀 다르네요.

                                                          // register가 callback 클래스까지 명시해 줄 수 있도록 overload된 듯. 

            PacketInputStream is = new PcapFileInputStream(new File("evidence03.pcap")); // 파일로부터 읽어 들여서 stream 생성.

            while (true) {

                PcapPacket packet = is.getPacket(); // 패킷 읽어 들이고(end of file의 경우에는 exception이 일어 나는 듯)

                eth.decode(packet);                 // eth라는 객체에 디코딩을 함(위에서 열거한 클래스를 따라 줄줄이 분석이 됨)

            }

        } catch (EOFException e) {

        }

    }

}

 

public class HttpCallback implements HttpProcessor, TcpSegmentCallback {

    private TcpSegment segment = null;

 

    // TcpSegmentCallback 클래스에서 override해야 하는 메소드로 보임. 뭘 뜻하는지는 잘 모르겠음.

    // TcpSession은 TCP 통신의 송신자, 수신자의 값으로 유일하게 결정되는(may srcIP, dstIP, srcPort, dstPort를 key로 하는) 객채 정보이고

    // TcpSegment는 아마도 하나의 TCP Data 패킷으로 예상이 됨(아님 말구 ㅋㅋ).

    // Direction은 session상에서 보내는 거냐, 아니면 반대로 받는 거냐를 나타내는 인자인듯.

    @Override

    public void getSegment(TcpSession session, TcpSegment segment, Direction direction) {

        this.segment = segment;

    }

 

    // 이놈은 어느 클래스에서 override한 것일까? 나도 모름. 어떤 놈이 파일을 요구하는 겨? ㅋㅋㅋ

    @Override

    public void getFile(String fileName, byte[] fileContent) {

    }

 

    // HttpDecoder 클래스의 decode 메소드 내부에서 HTTP request가 탐지되면 발생하는 callback. 맞나? ㅋㅋ

    @Override

    public void onRequest(HttpRequest request) {

    }

 

    // HttpDecoder 클래스의 decode 메소트 내부에서 HTTP request 및 HTTP response가 모두 탐지되었을 때 발생하는 callback.

    @Override

    public void onResponse(HttpRequest request, HttpResponse response) {

        String contentType = response.getHeaderValue("Content-Type");

        if (contentType.startsWith("image")) // Java에서 String 클래스는 startWith 라는 메소드도 존재하는 듯. 아, 부러워~

            return;

 

        System.out.printf("Content-Type: %s, User-Agent: %s:, Query String: %s\n", contentType, request

                .getHeaderValue("User-Agent"), request.getQueryString());

    }

}


HTTP 통신 과정에서 Content-Type이 "image"인 것을 제외한 나머지 HTTP request에 대한 정보를 출력해 주는 프로그램 같습니다. 제가 알고 있기로 Java는 클래스가 원래 기본적으로 pointer 라서 C++과 같이 포인터 type과 일반 type을 구분해 줄 필요가 없다는 얘기를 들었는데, 코드가 확실히 깔끔한 것 같네요. 하기야 C++에서도 reference type를 사용하면 되겠지만, 원래 C/C++ 프로그래머들이 pointer에 대한 직관적인 interface를 좋아해서리...


아, 그리고 역시나 Java의 garbage collection 의 강력함을 보는 것 같군요, 역시 객체 해제하는 코드는 없어도 되는군요. ㅎㅎ  아무튼 상기과 같은 방식으로의 HTTP Packet Sniffer도 디자인될 수 있다고 생각하시면 될 것 같습니다.


객체의 이벤트를 설정해 주는 방법은 언어에 따라 선호하는 방식이 많이 틀린 듯 합니다.


1. C : callback function의 pointer를 넘긴다.

2. C++ : 클래스 자체에서 virtual로 선언하고 그 하위 클래스에서 override를 시킨다. 실제 사용은 하위 클래스 객체를 생성해서 사용.

3. Pascal(Delphi) : event method도 결국 property이다. property 설정을 해 줘서 call을 하도록 한다.

4. Java : 상기와 같이 event method 들만을 모아서 별도의 클래스를 만들고 콜백 설정은 메소드 설정이 아닌 그 객체 설정으로 처리한다.