📄 packet.c
字号:
/* Copyright (c) 2002-2005 Vladislav Goncharov.
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// $Id: packet.c,v 1.9 2003/09/12 07:58:36 dev Exp $
/*
* Stateful packet checking engine
*/
#ifdef USE_PACKET_ENGINE
#include <ntddk.h>
#include <ndis.h>
#include <tdikrnl.h>
#include "sock.h"
#if _WIN32_WINNT >= 0x0500
/* include pfhook.h */
typedef ULONG IPAddr, IPMask;
#include <pfhook.h>
#endif
#include "conn_state.h"
#include "filter.h"
#include "ipc.h"
#include "ndis_hk_ioctl.h"
#include "net.h"
#include "obj_tbl.h"
#include "packet.h"
/* prototypes */
static NTSTATUS get_iface(void);
static BOOLEAN filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self,
BOOLEAN packet_unchanged);
static void process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo);
static BOOLEAN process_transp(int direction, UCHAR proto,
struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len,
struct flt_request *log_nfo);
static void process_tcp(int direction, struct tcp_hdr *tcp_hdr, struct flt_request *log_nfo);
static void process_udp(int direction, struct udp_hdr *udp_hdr, struct flt_request *log_nfo);
static void process_icmp(int direction, struct icmp_hdr *icmp_hdr, struct flt_request *log_nfo);
static void init_tcp_states(void);
static void check_packet(struct flt_request *log_nfo);
#if _WIN32_WINNT >= 0x0500
/* for "ipfilterdriver" */
static PF_FORWARD_ACTION hook_proc(
unsigned char *PacketHeader, unsigned char *Packet, unsigned int PacketLength,
unsigned int RecvInterfaceIndex, unsigned int SendInterfaceIndex,
IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop);
static NTSTATUS set_hook(PacketFilterExtensionPtr hook_fn);
static BOOLEAN g_set_hook = FALSE;
#endif
/* globals */
static PDEVICE_OBJECT g_ndis_hk_devobj;
static PFILE_OBJECT g_ndis_hk_fileobj;
// interface of ndis_hk
struct ndis_hk_interface *g_ndis_hk;
static struct filter_nfo g_tdi_fw =
{
sizeof(g_tdi_fw),
filter_packet,
NULL,
NULL,
NULL,
NULL
};
// here's a table with all possible tcp flags (except RST) and connection states
#define MAX_FLAGS 3
static struct
{
UCHAR tcp_flags_set;
UCHAR tcp_flags_not_set;
} g_tcp_states[TCP_STATE_MAX][2][MAX_FLAGS]; // init this array in init_tcp_states
NTSTATUS init_packet(void)
{
NTSTATUS status;
UNICODE_STRING devname;
// connect with ndis_hk
RtlInitUnicodeString(&devname, L"\\Device\\ndis_hk");
status = IoGetDeviceObjectPointer(
&devname,
STANDARD_RIGHTS_ALL,
&g_ndis_hk_fileobj,
&g_ndis_hk_devobj);
if (status == STATUS_SUCCESS)
{
/* using ndis_hk driver */
status = get_iface();
if (status != STATUS_SUCCESS)
{
KdPrint(("[tdi_fw] init_packet get_iface: 0x%x!\n", status));
goto done;
}
// attach our filter!
g_ndis_hk->attach_filter(&g_tdi_fw, TRUE, FALSE); // to bottom of filter stack
}
else {
#if _WIN32_WINNT >= 0x0500
/* try to use "ipfilterdriver" */
status = set_hook(hook_proc);
if (status != STATUS_SUCCESS)
{
KdPrint(("[tdi_fw] init_packet set_hook: 0x%x!\n", status));
goto done;
}
g_set_hook = TRUE;
#endif
}
init_tcp_states();
done:
if (status != STATUS_SUCCESS)
{
// cleanup
free_packet();
}
return status;
}
void free_packet(void)
{
if (g_ndis_hk_fileobj != NULL)
{
// detach our filter!
if (g_ndis_hk != NULL)
{
g_ndis_hk->attach_filter(&g_tdi_fw, FALSE, FALSE);
g_ndis_hk = NULL;
}
ObDereferenceObject(g_ndis_hk_fileobj);
g_ndis_hk_fileobj = NULL;
}
#if _WIN32_WINNT >= 0x0500
if (g_set_hook)
{
set_hook(NULL);
g_set_hook = FALSE;
}
#endif
}
NTSTATUS get_iface(void)
{
PIRP irp;
IO_STATUS_BLOCK isb;
irp = IoBuildDeviceIoControlRequest(IOCTL_CMD_GET_KM_IFACE,
g_ndis_hk_devobj,
NULL, 0,
&g_ndis_hk, sizeof(g_ndis_hk),
TRUE, NULL, &isb);
if (irp == NULL)
{
KdPrint(("[tdi_fw] get_iface: IoBuildDeviceIoControlRequest!\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
return IoCallDriver(g_ndis_hk_devobj, irp);
}
BOOLEAN filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self,
BOOLEAN packet_unchanged)
{
BOOLEAN result = FALSE, log = TRUE;
struct filter_nfo *child;
struct flt_request log_nfo;
PNDIS_BUFFER buffer;
UINT packet_len, buffer_len, buffer_offset, hdr_len;
void *pointer;
struct ether_hdr *ether_hdr;
struct ip_hdr *ip_hdr;
KdPrint(("[tdi_fw] filter_packet: direction = %s; iface = %d; packet = 0x%x\n",
direction == DIRECTION_IN ? "in" : "out", iface, packet));
// get child in filter stack
if (direction == DIRECTION_IN)
child = self->lower;
else
child = self->upper;
memset(&log_nfo, 0, sizeof(log_nfo));
log_nfo.struct_size = sizeof(log_nfo);
log_nfo.direction = direction;
// parse packet
NdisQueryPacket(packet, NULL, NULL, &buffer, &packet_len);
if (packet_len < sizeof(struct ether_hdr))
{
KdPrint(("[tdi_fw] filter_packet: too small packet for ether_hdr! (%u)\n", packet_len));
goto done;
}
/* process ether_hdr */
NdisQueryBuffer(buffer, ðer_hdr, &buffer_len);
if (buffer_len < sizeof(struct ether_hdr))
{
KdPrint(("[tdi_fw] filter_packet: too small buffer for ether_hdr! (%u)\n", buffer_len));
goto done;
}
buffer_offset = 0;
#define PRINT_ETH_ADDR(addr) \
(addr)[0], (addr)[1], (addr)[2], (addr)[3], (addr)[4], (addr)[5]
KdPrint(("[tdi_fw] filter_packet: eth %02x-%02x-%02x-%02x-%02x-%02x -> %02x-%02x-%02x-%02x-%02x-%02x (0x%x)\n",
PRINT_ETH_ADDR(ether_hdr->ether_shost),
PRINT_ETH_ADDR(ether_hdr->ether_dhost),
ether_hdr->ether_type));
// UGLY way to determine IP broadcasts
if (memcmp(ether_hdr->ether_dhost, "\xff\xff\xff\xff\xff\xff", 6) == 0)
log_nfo.packet.is_broadcast = 1;
// go to the next header
if (buffer_len > sizeof(struct ether_hdr))
{
pointer = (char *)ether_hdr + sizeof(struct ether_hdr);
buffer_offset += sizeof(struct ether_hdr);
buffer_len -= sizeof(struct ether_hdr);
}
else {
// use next buffer in chain
do {
NdisGetNextBuffer(buffer, &buffer);
NdisQueryBuffer(buffer, &pointer, &buffer_len);
} while (buffer_len == 0); // sometimes there're buffers with zero size in chain
buffer_offset = 0;
}
if (ntohs(ether_hdr->ether_type) == ETHERTYPE_IP)
{
/* process ip_hdr */
if (buffer_len < sizeof(struct ip_hdr))
{
KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u)\n",
buffer_len));
goto done;
}
ip_hdr = (struct ip_hdr *)pointer;
hdr_len = ip_hdr->ip_hl * 4;
if (buffer_len < hdr_len)
{
KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u vs. %u)\n",
buffer_len, hdr_len));
goto done;
}
// check we've got the first fragment (don't work with another!)
if ((ntohs(ip_hdr->ip_off) & IP_OFFMASK) != 0 && (ip_hdr->ip_off & IP_DF) == 0)
{
KdPrint(("[tdi_fw] filter_packet: got not first fragment\n"));
result = TRUE;
log = FALSE;
goto done;
}
process_ip(direction, ip_hdr, &log_nfo);
// go to the next header
if (buffer_len > hdr_len)
{
pointer = (char *)ip_hdr + hdr_len;
buffer_offset += hdr_len;
buffer_len -= hdr_len;
}
else {
// use next buffer in chain
do {
NdisGetNextBuffer(buffer, &buffer);
NdisQueryBuffer(buffer, &pointer, &buffer_len);
} while (buffer_len == 0); // sometimes there're buffers with zero size in chain
buffer_offset = 0;
}
result = process_transp(direction, ip_hdr->ip_p, ip_hdr, pointer, buffer_len, &log_nfo);
if (!result)
goto done;
// using log_nfo->pid as signal to log it
log = (log_nfo.pid == 0);
}
else
log = FALSE; // don't log not IP packets
result = TRUE; // packet headers looks like valid
done:
if (log){
log_nfo.result = result ? FILTER_PACKET_LOG : FILTER_PACKET_BAD;
log_nfo.pid = (ULONG)-1;
log_request(&log_nfo);
}
if (result)
return child->process_packet(direction, iface, packet, child, packet_unchanged);
else
return FALSE;
}
/* IP header */
void process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo)
{
#define PRINT_IP_ADDR(addr) \
((UCHAR *)&(addr))[0], ((UCHAR *)&(addr))[1], ((UCHAR *)&(addr))[2], ((UCHAR *)&(addr))[3]
KdPrint(("[tdi_fw] process_ip: ip %d.%d.%d.%d -> %d.%d.%d.%d (proto = %d, ipid = 0x%x)\n",
PRINT_IP_ADDR(ip_hdr->ip_src),
PRINT_IP_ADDR(ip_hdr->ip_dst),
ip_hdr->ip_p,
ip_hdr->ip_id));
log_nfo->proto = ip_hdr->ip_p;
if (direction == DIRECTION_IN)
log_nfo->log_bytes_in = ntohs(ip_hdr->ip_len);
else
log_nfo->log_bytes_out = ntohs(ip_hdr->ip_len);
log_nfo->addr.len = sizeof(struct sockaddr_in);
((struct sockaddr_in *)(&log_nfo->addr.from))->sin_family = AF_INET;
((struct sockaddr_in *)(&log_nfo->addr.from))->sin_addr.s_addr = ip_hdr->ip_src;
((struct sockaddr_in *)(&log_nfo->addr.to))->sin_family = AF_INET;
((struct sockaddr_in *)(&log_nfo->addr.to))->sin_addr.s_addr = ip_hdr->ip_dst;
}
/* process TCP, UDP or ICMP header */
BOOLEAN process_transp(int direction, UCHAR proto,
struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len,
struct flt_request *log_nfo)
{
struct tcp_hdr *tcp_hdr;
struct udp_hdr *udp_hdr;
struct icmp_hdr *icmp_hdr;
switch (proto)
{
case IPPROTO_TCP:
/* process tcp_hdr */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -