WFP Driver programming

2015. 3. 19. 12:27Security ★ Development/Windows Driver

반응형

 이번에는 WFP(Windows Filtering Platform)에 대해 간단하게 정리해보겠습니다.  WFP는 NDIS의 다음 버전으로 네트워킹 스택에서 발생하는 여러 정의된 이벤트에 Callout이라는 것을 걸어서 이벤트 핸들링 처럼 처리하는 방식입니다.

 자료는 NDIS보다 훨씬 더 부족합니다.  구글에서 wfp라고 검색하면 유엔세계식량계획이 먼저 나오고 wfp or wndows filt... 풀네임과 기타 키워드를 같이 검색하면 페이지조차 얼마 안됩니다.  처음 막 할때는 막막했습니다... ㅎㅎ  NDIS는 네이버 블로그라도 좀 있는데 WFP는 전멸. ...  NDIS와 마찬가지로 MSDN의 Windows Filtering Platform Sample을 토대로 진행하겠습니다.

 

 그럼 간략하게 WFP의 작성방식부터 보겠습니다.

 WFP는 NDIS와는 다르게 하나의 send, recv함수에서 패킷들을 처리하는 것이 아니라 윈도우즈 네트워크 레이어중에서 특정한 레이어에만 필터(함수)를 등록해주고 그곳에서 발생하는 사건만 처리해주는 방식으로 되어있습니다.

 패킷을 캡쳐하기 위해, 그리고 저는 필터링, 특히 Process name을 기준으로 프로세스 패킷 필터링(캡쳐도 마찬가지로)을 구현하기 위해 IPPACKET, ALE레이어에 callout을 걸어주었습니다.  

 지금 소개하고 있는 부분은 Kernel Mode 부분입니다.  TCP/IP Stack옆의 IOCTL Interface에 Stream/Datagram Data Layer, Inbound/Outbound ALE Layer 등이 있는데 이 레이어중에 필요한 곳에 callout을 등록해줍니다.

 Callout은 해당 레이어에서 작동하게 될 필터를 의미합니다.  필터를 등록할 때에는 주요한 구조체의 필드를 정해주어야 하는데 먼저 LayerKey는 이 필터가 작동할 레이어를 의미하고 Action.type은 필터가 등록할 모드를 의미합니다.  INSPECTION과 TERMINATING이 있는데 INSPECTION은 검사만 TERMINATING은 다시 인젝션을 해주어야합니다.  단순 확인만 하고 싶을 때는 INSPECTION을 씁니다.  Fintercondition이라는 것도 있는데 필터, 즉 패킷을 조건에 따라 처리하고 싶을 때 등록해줍니다.  별도의 블록킹을 해주지 않을 때에는 0으로 초기화 하고 numFilterConditions도 0이 됩니다.  필터를 추가할 때에는 해당 초기화 구문 아래에 추가되어있는 구조체를 정의해주면 됩니다.

 

 패킷을 보기 위해서는 FWPM_LAYER_INBOUND_IPPCKET, FWPM_LAYER_OUTBOUND_IPPCAKET 레이어에 등록해줍니다.  IPv6과 IPv4모두 개별적으로 등록해주어야 확실히 캡쳐할 수 있습니다.  저의 경우 토렌트 트래픽을 막는데에서 자꾸 트래픽이 생겼는데 IPv6을 막아주니까 확실히 막혔습니다.  아무튼 이 레이어에서 패킷 데이터를 볼 수 있습니다.  (윈도우 7은 MAC 헤더를 볼 수 없습니다.)  

 프로세스 ID, NAME을 보기 위해서는 FWPM_LAYER_ALE_AUTH_CONNECT레이어에 필터를 걸어줍니다.  정확한 정의는 MSDN을 참고합니다.  이 두 레이어를 이용하면 프로세스별로 패킷 관리를 해줄 수 있습니다.


 그럼 좀 더 깊게 코드 부분을 보겠습니다.  WFP에서는 inspect.c와 tl_drv.c를 주로 보게됩니다. 

 먼저 inspect.c

 TLInspectALEConnectClassify, TLInspectALERecvAcceptClassify 등으로 정의되어 있는 것이 Callout function입니다.  NTDDI_VERSION 버전에 따라 2개씩 구현되어있습니다.  최소한 IPPACKET레이어에서 패킷은 NDIS처럼 NBL로 관리됩니다.  layerdata를 이용하면 되고 TERMINATING일 경우 마지막에 classifyout에 대한 구조체의 값들을 정해주어야 합니다.  inspect.c는 레이어들에 대한 callout function들이 정의되어있습니다.

 이제 tl_drv.c

 각 함수의 관계를 한번 정리해보시는게 좋습니다.  TLInspectAddFilter는 필터를 실제 등록해주는 부분입니다.  Register가 들어간 함수에서 각 레이어에 대한 등록 처리를 해주고 있고 DriverUnload와 DriverEntry가 있습니다.

 WFP Sample은 레지스트리에 등록한 특정 IP에 대해서 허용과 블록 정책을 적용해주고 있습니다.  이 부분은 레지스트리 호출 부분을 지워주면 됩니다.


 구현에 관해서 몇가지 도움이 될만한 정보로는 

1. ALE레이어에서는 inMetaValues를 통해 ProcessID와 ProcessName을 구할 수 있습니다.  그리고 ID가 유효한 레이어는 RecvAccept 레이어가 아니라 Connect 레이어 입니다.

2. NDIS도 마찬가지이지만 데이터에 접근할 때에는 임의로 offset을 변경하는 것, 즉 TCPheader=IPheader+IPheaderSize; 와 같은 방식은 좋지 않습니다.  WFP, NDIS에서 제공해주는 함수를 쓰도록 합니다.

3. 자료가 많이 부족합니다.  국내 자료는 물론이고 외국 자료도 WPF, C++, JAVA 등에 비하면 턱없이 부족합니다.  StackOverflow에 물어봐도 잘 답변을 안해줍니다.  한번 에러가나면 블루스크린-재부팅-재설치가 기다리고 있어서 확신을 갖기 전에는 코딩, 디버깅을 잘 안 하게 되는데 인터넷 검색하는 시간도 만만치 않습니다.  드라이버 개발에 익숙하지 않으신 분들은 내가 새로 개척한다고 생각하시고 적극적으로 여러 함수들과 방법을 적용해보고 시도를 해보시는것도 좋을 것 같습니다.

 이정도면 패킷 캡쳐와 프로세스별 구분은 어렵지 않게 하실 수 있으실 겁니다.  제가 살짝 경험해본 드라이버 개발은 지식 수준의 어려움보다는 개발 환경, 과정이 열악하다는게 더 힘든 것 같네요.  많은 도움이 되셨길 바라며 NDIS에서도 썻던 부분이지만 저도 학생의 신분으로 이쪽으로 깊게 공부한 것은 아니기 때문에 잘못된 부분이 있으면 알려주시면 감사하겠습니다.  마찬가지로 더 필요하다 생각되면 추가 포스팅을 하겠습니다.  그 외 궁금한 부분은 댓글 달아주세요~