יום חמישי, 24 במאי 2012

Personal Firewall





הרבה זמן רציתי לכתוב את המאמר הזה ועכשיו הגיע הזמן לסגור עניין למען האמת מדובר על 2 מאמרים בהם אני יסביר כיצד  לממש Personal Firewall בסביבת Windows, לא מדובר על התממשקות לתוכנת ה Firewall הקיימת במערכת ההפעלה, אלא נגיעה בשכבה נמוכה יותר שבעצם נותנת שירותים של Packet Filtering כחלק מה Api של המערכת עצמה.

למה לא יכלת לסגור את כל הסיפור במאמר אחד?
הלוואי, בגרסת Windows Vista ומעלה החליטו לשנות את צורת ההתממשקות, בעצם הקימו פלטפורמה חדשה ומאובזרת שנקראת Windows Filtering Platform ועליה נעבור במאמר הבא בנושא.

נחזור לעניינו, רוב העבודה מתבצעת מול הקובץ iphlpapi.dll  שהוא חיוני עבור מערכת ההפעלה, הוא בעצם חלק מה Api של מערכת ההפעלה ואחראי על המנגנון Ip Helper Api Libary שמאפשר לנו גישה למשאבי הרשת.

שימו לב: התוכנה תומכת רק בגרסאות Windows Server 2003 ו XP, התוכנית חייבת הרשאות גבוהות (Administrator) בשביל לרוץ.

פונקציות ה Api עבור הגדרת Filter:

Interface:
PfCreateInterface(...);
יצירת  Interface ואיתחול.
PfBindInterfaceToIPAddress(...);
חיבור ה interface לכתובת Ip.
PfUnBindInterface(...);
ניתוק כתובת מ Interface.
PfDeleteInterface(...);
הסרת ה Interface.


Filters:
PfAddFiltersToInterface(...);
הוספת Filter חדש ל Interface.
PfRemoveFiltersFromInterface(...);
הסרת ה Filter.


Global Filters:

קבוצה של פילטרים גלובלים עבור ה interface וכל פילטר רגיל התחשב בו, מדובר על 3 דגלים שניתן להרים בסינון:

GF_FRAGMENTS
בדיקת תקינות עובר החבילות.
GF_STRONGHOST
בדיקת תקינות של היעד שממנו הגיעו החבילות.
GF_FRAGCACHE
בדיקת החבילה שנמצאות ב Cache.

בעצם ניתן להוסיף שכבת הגנה נוספת שמאפשרת לזהות הזרקות של חבילות ו Ip Spoofing.

PfAddGlobalFilterToInterface(...);
הוספת Global Filter חדש.
PfRemoveGlobalFilterFromInterface(...);
הסרת ה Global Filter.

על מנת להשתמש בממשק, התוכנית חייבת להיות כתובה ב C / C++ , קיימים 2 סוגים שונים של ניהול זיכרון, Managed Code זיכרון שמוקצה רק תחת ה (Common Language Runtime virtual machine כלומר ה CLR של .Net) ו Unmanaged Code שנמצא מחוץ לסביבה הוירטואלית, פונקציות Api של Windows עובדות עם זיכרון מסוג זה בלבד ולכן חייבים לעשות את החיבור עם המנגנון המובנה ב Compiler של C++ שנקרא C++ Interop שמחבר עם 2 סוגי הזיכרון ומאפשר להפעיל את ה DLL דרך הקוד של .Net.



בגלל שאי אפשר לגשת לפונקציות של ה API ישירות דרך .NET יש לבנות קבוצה של פונקציות שמקבלות את הפרמטרים  של הפילטר מעולם ה Managed Code ומעבירות אותו ל UnManaged Code שמדבר עם ה API, התוכנית עצמה מורכבת מ 3 מחלקות עיקריות:

ndfInterface
יוצר Interface חדש עבור ה Filter עבור ה Adapters.
ndfFilterFlag
פרמטרים קבועים המאפשרים לקבוע את כיון ה Traffic, סוג הפרוטוקולים, פרמטרים עבור GlobalFilter, שאותם נעביר עם הפילטר שנשלח.
ndfFilterManager
החיבור שלנו לעולם ה Managed Code.

הפרמטרים שעוברים בפונקציות נכנסים לאובייקט מסוג PF_FILTER_DESCRIPTOR שמנוהל ב  UnManaged Code והוא עובר ל Api.

ndf.h

// **** proxytype.blogspot.com ****
// Network Filter Interface
// free for anyuse.
// please do not remove this note
// ********************************


#include <windows.h>
#include <Iphlpapi.h>
#include <Fltdefs.h>

#pragma comment(lib, "Iphlpapi.lib")

#pragma once
#using <mscorlib.dll>
#using <System.dll>
using namespace System;
using namespace System::Net;
using namespace System::Collections;
using System::Runtime::InteropServices::Marshal;


namespace ndf
{
private __gc class ndfInterface
{
public:
INTERFACE_HANDLE * myhandle;

   ndfInterface();
int createInterface(IPAddress *ip);

protected:
void dispose();
};


public __gc class ndfFilterFlag
{
public:
//direction of traffic
static const int DIRECTION_IN = 0x0;
static const int DIRECTION_OUT = 0x1;

//set for all types
static const int ANY_TCPUDP_PORTS = 0x00;
static const int ANY_ICMP_TYPES = 0xff;
static const int ANY_ICMP_CODES = 0xff;
     
//set for all supported protocol
static const int PROTOCOL_ANY = 0x00;
static const int PROTOCOL_TCP = 0x06;
static const int PROTOCOL_UDP = 0x17;
static const int PROTOCOL_ICMP = 0x01;

//set for all global filter type
static const int GF_FRAGMENTS  = 2;
static const int GF_STRONGHOST = 8;
static const int GF_FRAGCACHE  = 9;

};

public __gc class ndfFilterManager
{
protected:
bool disposed;
public:

Hashtable * interfaces;
   
        ndfFilterManager();
                 int signFilter(IPAddress *ip, int direction, IPAddress *srcIp, IPAddress *srcMask,
                 IPAddress *dstIp, IPAddress * dstMask, int srcPort, int dstPort, int protocol);

                int signGlobalFilter(IPAddress *ip,int gbFilter);

int removeFilter(IPAddress *ip, int direction, IPAddress *srcIp, IPAddress *srcMask, IPAddress *dstIp, IPAddress * dstMask, int srcPort, int dstPort, int protocol);
int removeGlobalFilter(IPAddress *ip,int gbFilter);

void dispose();
};
}


ndf.cpp

// **** proxytype.blogspot.com ****
// Network Filter Interface
// free for anyuse.
// please do not remove this note
// ********************************


#include "stdafx.h"
#include "ndf.h"



int ndf::ndfInterface::createInterface(IPAddress *ip)
{
   
  myhandle = (INTERFACE_HANDLE *)Marshal::AllocHGlobal(sizeof(INTERFACE_HANDLE)).ToPointer();
   
  //set default rules before adding new rules
  DWORD code = PfCreateInterface(0, 
    PF_ACTION_FORWARD, 
    PF_ACTION_FORWARD, 
FALSE, 
TRUE, 
    myhandle);
  //check if creation success
  if(code != NO_ERROR)
return -1;

  unsigned long longIp = (unsigned long)ip->get_Address();

  code = PfBindInterfaceToIPAddress(*myhandle,PF_IPV4,(PBYTE)&longIp);

  //check if binding success
  if(code != NO_ERROR)
  {
 //delete the interface if binding unsuccess
 PfDeleteInterface(*myhandle);
 return -2;
  }
  
  return 0;


}


ndf::ndfInterface::ndfInterface()
{
   myhandle = NULL;
}

void ndf::ndfInterface::dispose()
{
   //clear the handler and associate memory
   if(myhandle != NULL)
 Marshal::FreeHGlobal(myhandle);


}

ndf::ndfFilterManager::ndfFilterManager()
{
interfaces = new Hashtable();

//set disposed status to false
//when change to true
//dispose complete
disposed = false;

}

int ndf::ndfFilterManager::signFilter(IPAddress *ip, int direction, IPAddress *srcIp, IPAddress *srcMask, IPAddress *dstIp, IPAddress * dstMask, int srcPort, int dstPort, int protocol)
{
ndfInterface * handleNum;

// check if ip belong to
// interface inside the
// hash collection 
if(interfaces->Contains(ip))
{
//return object by ip
//and casting the object
//to ndfInterface.
handleNum = static_cast<ndfInterface *>(interfaces->get_Item(ip));
}
else
{
//if no interface found 
//create new one and insert
//to the hash collection
handleNum = new ndfInterface();
if(handleNum->createInterface(ip)!= 0)
return -1;

interfaces->Add(ip, handleNum);
}



//create operation system native filter
//structure, complete the needed fields
PF_FILTER_DESCRIPTOR systemFilter;
systemFilter.dwFilterFlags     = FD_FLAGS_NOSYN;
   systemFilter.dwRule            = 0;
systemFilter.pfatType          = PF_IPV4;
systemFilter.dwProtocol        = protocol;
systemFilter.fLateBound        = 0;
systemFilter.wSrcPort          = srcPort;
systemFilter.wSrcPortHighRange = srcPort;
systemFilter.wDstPort          = dstPort;
systemFilter.wDstPortHighRange = dstPort;

unsigned long srcIpNum    = (unsigned long)srcIp->get_Address();
unsigned long dstIpNum = (unsigned long)dstIp->get_Address();
unsigned long srcMaskNum = (unsigned long)srcMask->get_Address();
unsigned long dstMaskNum = (unsigned long)dstMask->get_Address();

//casting to 8-bit unsigned entity pointer
systemFilter.SrcAddr = (PBYTE) &srcIpNum;
systemFilter.SrcMask = (PBYTE) &srcMaskNum;
systemFilter.DstAddr = (PBYTE) &dstIpNum;
systemFilter.DstMask = (PBYTE) &dstMaskNum;

DWORD code;


//payloads are different between directions
switch (direction)
{
case 0:
code = PfAddFiltersToInterface(*(handleNum->myhandle), 1, &systemFilter, 0, NULL, NULL );
break;
case 1:
code = PfAddFiltersToInterface(*(handleNum->myhandle), 0, NULL, 1, &systemFilter, NULL );
break;
default:
code = ERROR;
}

if(code != NO_ERROR)
return -1;

return 0;
}

int ndf::ndfFilterManager::signGlobalFilter(IPAddress *ip,int gbFilter)
{
ndfInterface * handleNum;

// check if ip belong to
// interface inside the
// hash collection 
if(interfaces->Contains(ip))
{
//return object by ip
//and casting the object
//to ndfInterface.
handleNum = static_cast<ndfInterface *>(interfaces->get_Item(ip));
}
else
{
//if no interface found 
//create new one and insert
//to the hash collection
handleNum = new ndfInterface();
if(handleNum->createInterface(ip)!= 0)
return -1;

interfaces->Add(ip, handleNum);
}


DWORD code;

// add the global filter to
// interface convert the int for
// global_filter system type
code = PfAddGlobalFilterToInterface(*(handleNum->myhandle), (GLOBAL_FILTER)gbFilter);
if(code != NO_ERROR)
return -1;

return 0;
}

int ndf::ndfFilterManager::removeFilter(IPAddress *ip, int direction, IPAddress *srcIp, IPAddress *srcMask, IPAddress *dstIp, IPAddress * dstMask, int srcPort, int dstPort, int protocol)
{
ndfInterface * handleNum;

// check if ip belong to
// interface inside the
// hash collection 
if(interfaces->Contains(ip))
//return object by ip
//and casting the the object
//to ndfInterface.
handleNum = static_cast<ndfInterface *>(interfaces->get_Item(ip));
else
//if not information found
//return error
return -1;


//create operation system native filter
//structure and complete the nedded fields
PF_FILTER_DESCRIPTOR systemFilter;
systemFilter.dwFilterFlags     = FD_FLAGS_NOSYN;
   systemFilter.dwRule            = 0;
systemFilter.pfatType          = PF_IPV4;
systemFilter.dwProtocol        = protocol;
systemFilter.fLateBound        = 0;
systemFilter.wSrcPort          = srcPort;
systemFilter.wSrcPortHighRange = srcPort;
systemFilter.wDstPort          = dstPort;
systemFilter.wDstPortHighRange = dstPort;

unsigned long srcIpNum    = (unsigned long)srcIp->get_Address();
unsigned long dstIpNum = (unsigned long)dstIp->get_Address();
unsigned long srcMaskNum = (unsigned long)srcMask->get_Address();
unsigned long dstMaskNum = (unsigned long)dstMask->get_Address();

//casting to 8-bit unsigned entity pointer
systemFilter.SrcAddr = (PBYTE) &srcIpNum;
systemFilter.SrcMask = (PBYTE) &srcMaskNum;
systemFilter.DstAddr = (PBYTE) &dstIpNum;
systemFilter.DstMask = (PBYTE) &dstMaskNum;

DWORD code;

//payloads are different between directions
switch (direction)
{
case 0:
  code = PfRemoveFiltersFromInterface(*(handleNum->myhandle), 1, &systemFilter, 0, NULL);
break;
case 1:
  code = PfRemoveFiltersFromInterface(*(handleNum->myhandle), 0, NULL, 1, &systemFilter);
  break;
default:
code = ERROR;
}

if(code != NO_ERROR)
return -1;

return 0;


}

int ndf::ndfFilterManager::removeGlobalFilter(IPAddress *ip,int gbFilter)
{
       ndfInterface *handleNum;

// check if ip belong to
// interface inside the
// hash collection
if(interfaces->Contains(ip))
//return object by ip
//and casting the object
//to ndfInterface.
handleNum = static_cast<ndfInterface *>(interfaces->get_Item(ip));
else
{
//if not exsits create an new
//object inside the collection 
       // and attach the request ip
handleNum = new ndfInterface();

//check if the creation of the
//handle acomplish if there is
//error throw -1
if(handleNum->createInterface(ip)!= 0)
return -1;
interfaces->Add(ip, handleNum);
}

DWORD code;
// remove the global filter from
// interface convert the int for
// global_filter system type
code = PfRemoveGlobalFilterFromInterface(*(handleNum->myhandle), (GLOBAL_FILTER)gbFilter);
if(code != NO_ERROR)
return -1;
            
return 0;
}

void ndf::ndfFilterManager::dispose()
{
if(!disposed)
{
disposed = true;
ndfInterface *handleNum;
IDictionaryEnumerator* Enumerator = interfaces->GetEnumerator();

//run through the collection
//and unbind and remove the interface
while ( Enumerator->MoveNext() )
{
handleNum = static_cast<ndfInterface *>(Enumerator->Value);
PfUnBindInterface(*(handleNum->myhandle));
PfDeleteInterface(*(handleNum->myhandle));
}
//active the garbage collector
GC::SuppressFinalize(this);
}
}


test.cs

using System;
using System.Net;
using ndf;

namespace test
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
            ndfFilterManager mymanager = new ndfFilterManager();
            mymanager.signFilter(IPAddress.Parse("192.168.1.12"),
                ndfFilterFlag.DIRECTION_OUT,
                IPAddress.Parse("192.168.1.12"),
                IPAddress.Parse("255.255.255.255"),
                IPAddress.Parse("212.199.164.135"),
                IPAddress.Parse("255.255.255.255"),
                ndfFilterFlag.ANY_TCPUDP_PORTS,
                80,
                ndfFilterFlag.PROTOCOL_TCP);
       
          Console.ReadLine();
                   mymanager.dispose();
}
}
}




:סרט הדגמה


קוד להורדה:

סיכום:
ראינו כיצד לממש Personal FireWall בעזרת פונקציות API של מערכת ההפעלה.
נתראה בחלק הבא.

אין תגובות:

הוסף רשומת תגובה