Filter by process in Wireshark

Keywords: network wireshark

An introduction

When using wireshark, one of the most frustrating things is filtering out what you need from a large number of data packets. In particular, when we need to analyze the protocol for a particular program, it's perfect to have a process name as a filter. I found an implementation when I checked the materials online, but the version is older, November 2012. The original text is here:

Wireshark ยท Wireshark-dev: Re: [Wireshark-dev] [PATCH] Filter by local process name

I implemented it in a recent version with reference to its code and found it really works.

I am modifying on the basis of this version:

Revision: 0b8acdaf689b6a4bd3d6fc7c14ac20f172831a3e
Author: Oscar Gonzalez de Dios <oscar.gonzalezdedios@telefonica.com>
Date: 2021/7/28 22:38:58
Message:
Fixed trailing whitespaces

----
Modified: epan/dissectors/packet-pcep.c

2. Get the process corresponding to the IP packet

First, associate the process with the port number. Each connection uses a unique port. Under windows, you can use the netstat command to query the source address, destination address, port and process PID for each connection. This is done through process_info.h and process_info.c implementation.

process_info.h

/* process_info.h
 * Process information (pid, process name)
 *
 * $Id$
 *
 * Wireshark - Network traffic analyzer
 * By Bogdan Harjoc <harjoc@gmail.com>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef __PROCESS_INFO_H__
#define __PROCESS_INFO_H__

#include <epan/packet.h>

/** returns the name of the process based on the src:port - dst:port data from tvb */
const char *process_info_lookup(tvbuff_t *tvb);

#endif /* process_info.h */

process_info.c

/* process_info.c
 * Process information (pid, process name)
 *
 * $Id$
 *
 * Wireshark - Network traffic analyzer
 * By Bogdan Harjoc <harjoc@gmail.com>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef _WIN32

#include "config.h"

#include <glib.h>
#include <stdio.h>
#include <stdlib.h>

#include <windows.h>
#include <winsock.h>
#include <Iphlpapi.h>
#include <Psapi.h>

#include "process_info.h"

#include <epan/ipproto.h>

typedef struct netstat {
    struct netstat* next;
    guint loc_ip;
    guint rem_ip;
    guint16 loc_port;
    guint16 rem_port;
    guint16 proto;
    guint pid;
} netstat_t;

typedef struct procinfo {
    struct procinfo* next;
    guint pid;
    char name[1];
} procinfo_t;

#define HASH_SIZE 971

static procinfo_t* process_hash[HASH_SIZE] = { 0 };
static netstat_t* addr_hash[HASH_SIZE] = { 0 };
static netstat_t* pair_hash[HASH_SIZE] = { 0 };

/**
 * Query first if the PID is already in the hash table process_ In hash, return directly if it already exists.
 * Otherwise, open the process by ID, query its process name, and store it in a hash table.
 */
static procinfo_t* lookup_pid(guint pid)
{
    procinfo_t* e;
    HANDLE hproc;
    static wchar_t process_name[MAX_PATH] = { 0 };
    guint len;

    // When querying within a hash table, notice that the effect of loops is to query all procinfo_with the same hash value T-structure
    for (e = process_hash[pid % HASH_SIZE]; e; e = e->next)
        if (e->pid == pid)
            return e;

    // Maybe you need a claim here?
    hproc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (!hproc)
        return NULL;

    len = GetProcessImageFileName(hproc, process_name, MAX_PATH);
    CloseHandle(hproc);
    if (len == 0)
        return NULL;

    e = g_malloc(offsetof(procinfo_t, name) + (len + 1));
    if (!e)
        return NULL;

    /* must convert properly using WideCharToMultiByte here */
    _snprintf(e->name, len + 1, "%.*S", len, process_name);
    e->name[len] = 0;

    e->pid = pid;
    e->next = process_hash[pid % HASH_SIZE];
    process_hash[pid % HASH_SIZE] = e;

    return e;
}

static guint lookup_addrs(guint16 proto, guint src_ip, guint16 src_port, guint dst_ip, guint16 dst_port)
{
    guint h_local = src_ip + src_port;
    guint h_remote = dst_ip + dst_port;
    guint h_pair = h_local + h_remote;
    netstat_t* e;

    /* src:dst matches src -> dst  or  dst -> src (established/connecting/closed/...) */
    for (e = pair_hash[h_pair % HASH_SIZE]; e; e = e->next)
        if ((e->loc_ip == src_ip && e->rem_ip == dst_ip && e->loc_port == src_port && e->rem_port == dst_port) ||
            (e->loc_ip == dst_ip && e->rem_ip == src_ip && e->loc_port == dst_port && e->rem_port == src_port))
            if (e->proto == proto)
                return e->pid;

    /* src matches src -> 0.0.0.0:0 (listening) */
    for (e = addr_hash[h_local % HASH_SIZE]; e; e = e->next)
        if (e->loc_ip == src_ip && e->loc_port == src_port && e->rem_ip == 0 && e->rem_port == 0)
            if (e->proto == proto)
                return e->pid;

    /* dst matches dst -> 0.0.0.0:0 (listening) */
    for (e = addr_hash[h_remote % HASH_SIZE]; e; e = e->next)
        if (e->loc_ip == dst_ip && e->loc_port == dst_port && e->rem_ip == 0 && e->rem_port == 0)
            if (e->proto == proto)
                return e->pid;

    /* src matches src -> something  or  something -> src (netstat and winpcap see different IP addrs) */
    for (e = addr_hash[h_local % HASH_SIZE]; e; e = e->next)
        if ((e->loc_ip == src_ip && e->loc_port == src_port && e->rem_port == dst_port) ||
            (e->rem_ip == src_ip && e->rem_port == src_port && e->loc_port == dst_port))
            if (e->proto == proto)
                return e->pid;

    /* dst matches dst -> something  or  something -> dst (netstat and winpcap see different IP addrs) */
    for (e = addr_hash[h_remote % HASH_SIZE]; e; e = e->next)
        if ((e->loc_ip == dst_ip && e->loc_port == dst_port && e->rem_port == src_port) ||
            (e->rem_ip == dst_ip && e->rem_port == dst_port && e->loc_port == src_port))
            if (e->proto == proto)
                return e->pid;

    return 0;
}

static void netstat_update()
{
    guint buf_size = 0;
    guint h;
    char* buf = NULL;
    //netstat_t* e = NULL;
    int ret;
    unsigned i;
    MIB_TCPTABLE_OWNER_PID* mibTable;

    /**
     * Clear addr_hash and pair_ The values of the hash two hash tables.
     */
    for (h = 0; h < HASH_SIZE; h++) {
        while (addr_hash[h]) {
            netstat_t* head = addr_hash[h];
            addr_hash[h] = addr_hash[h]->next;
            g_free(head);
        }
        while (pair_hash[h]) {
            netstat_t* head = pair_hash[h];
            pair_hash[h] = pair_hash[h]->next;
            g_free(head);
        }
    }

    /* Execute netstat */
    ret = GetExtendedTcpTable(NULL, &buf_size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
    if (ret == ERROR_INSUFFICIENT_BUFFER) {
        buf = g_malloc(buf_size);
        if (!buf) return;
        ret = GetExtendedTcpTable(buf, &buf_size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
    }
    if (ret != NO_ERROR) {
        g_free(buf);
        return;
    }

    mibTable = (MIB_TCPTABLE_OWNER_PID*)buf;
    for (i = 0; i < mibTable->dwNumEntries; i++) {
        guint src_ip = _byteswap_ulong(mibTable->table[i].dwLocalAddr);
        guint dst_ip = _byteswap_ulong(mibTable->table[i].dwRemoteAddr);
        guint src_port = _byteswap_ushort((unsigned short)mibTable->table[i].dwLocalPort);
        guint dst_port = _byteswap_ushort((unsigned short)mibTable->table[i].dwRemotePort);
        guint pid = mibTable->table[i].dwOwningPid;

        guint h_local = src_ip + src_port;
        guint h_remote = dst_ip + dst_port;
        guint h_pair = h_local + h_remote;

        netstat_t* e;

        /* Add the following items to the hash table:
           - src->dst and dst->src to pair_hash[src:dst]  -- note that hash(src:dst) == hash(dst:src)
           - src->dst and dst->src to addr_hash[src]
           - src->dst and dst->src to addr_hash[dst]

           dst:src is also added in order to find packets coming in both directions
           netstat_t With the next pointer, you can string together elements of the same hash value.
        */

        //
        // To assume that the package is an outgoing package
        // Netstat_ T.local IP = source ip of IP packet
        // Netstat_ T.remote IP = destination ip of IP packet
        // At the same time, let addr_hash[src_ip + src_port] = addr_hash[dst_ip + dst_port]
        //
        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = src_ip;
        e->rem_ip = dst_ip;
        e->loc_port = src_port;
        e->rem_port = dst_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = addr_hash[h_local % HASH_SIZE];
        addr_hash[h_local % HASH_SIZE] = e;

        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = src_ip;
        e->rem_ip = dst_ip;
        e->loc_port = src_port;
        e->rem_port = dst_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = addr_hash[h_remote % HASH_SIZE];
        addr_hash[h_remote % HASH_SIZE] = e;

        //
        // To assume that the packet is returned
        // netstat_t.local ip = IP packet.destination IP.
        // netstat_t.remote ip = IP packet.source IP
        // At the same time, let addr_hash[src_ip + src_port] = addr_hash[dst_ip + dst_port]
        //
        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = dst_ip;
        e->rem_ip = src_ip;
        e->loc_port = dst_port;
        e->rem_port = src_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = addr_hash[h_local % HASH_SIZE];
        addr_hash[h_local % HASH_SIZE] = e;

        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = dst_ip;
        e->rem_ip = src_ip;
        e->loc_port = dst_port;
        e->rem_port = src_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = addr_hash[h_remote % HASH_SIZE];
        addr_hash[h_remote % HASH_SIZE] = e;

        //
        // pair_hash[src_ip + src_port + dst_ip + dst_port] = e;
        //
        // Suppose it's going out, netstat_ T.local IP = the source ip of the IP packet.
        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = src_ip; 
        e->rem_ip = dst_ip;
        e->loc_port = src_port;
        e->rem_port = dst_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = pair_hash[h_pair % HASH_SIZE];
        pair_hash[h_pair % HASH_SIZE] = e;

        // Assuming the direction of return, netstat_t.local ip = IP packet.destination IP..
        e = g_malloc(sizeof(netstat_t));
        if (!e)
            break;
        e->loc_ip = dst_ip;
        e->rem_ip = src_ip;
        e->loc_port = dst_port;
        e->rem_port = src_port;
        e->proto = IP_PROTO_TCP;
        e->pid = pid;
        e->next = pair_hash[h_pair % HASH_SIZE];
        pair_hash[h_pair % HASH_SIZE] = e;
    }

    g_free(buf);
}

const char* process_info_lookup(tvbuff_t* tvb)
{
    static char buf[64];
    guint ip_vhl = tvb_get_guint8(tvb, 0);
    //guint ip_len = tvb_get_ntohs(tvb, 2);
    guint8 ip_proto = tvb_get_guint8(tvb, 9);
    guint proto_ofs;
    guint src_ip = tvb_get_ntohl(tvb, 12);
    guint dst_ip = tvb_get_ntohl(tvb, 16);
    guint16 src_port;
    guint16 dst_port;
    guint pid;
    procinfo_t* info;

    if (ip_vhl >> 4 != 4)
        return NULL;
    if (ip_proto != IP_PROTO_TCP /* && ip_proto != IP_PROTO_UDP */)
        return NULL;

    proto_ofs = (ip_vhl & 0xf) * 4;

    src_port = tvb_get_ntohs(tvb, proto_ofs + 0);
    dst_port = tvb_get_ntohs(tvb, proto_ofs + 2);

    pid = lookup_addrs(ip_proto, src_ip, src_port, dst_ip, dst_port);
    if (!pid) {
        netstat_update();
        pid = lookup_addrs(ip_proto, src_ip, src_port, dst_ip, dst_port);
        if (!pid)
            return NULL;
    }

    info = lookup_pid(pid);
    if (!info)
        return NULL;
    return info->name;
}

#else /* _WIN32 */

const char* process_info_lookup(tvbuff_t* tvb) { return NULL; }

#endif /* _WIN32 */

The code is very short and the idea is very simple. Use GetExtendedTcpTable to achieve the effect similar to netstat command, get the source IP, source port, target IP, target port, PID of all current connections, and then hash in several hash tables using different methods of IP plus port. When an IP package is parsed, the program matches in this hash table, returns the process name corresponding to the PID when it matches, and executes GetExtendedTcpTable again to refresh the entire hash table and query it.

Third, add a process name to the protocol tree

The unknown added to the protocol tree is the IP Message Protocol section. A new node "Process Info:" is added at the end of the protocol tree after the original data parsing is completed. This modification is made in the packet-ip.c file by this add_process_info function implementation:

static void
add_process_info(proto_tree * tree, tvbuff_t * tvb, gint offset){
  proto_item* process_info_item;
  proto_tree* process_info_tree;
  proto_item* item;
  const char* process_name;

  process_name = process_info_lookup(tvb);
  if (!process_name)
    return;
  process_info_item = proto_tree_add_text_internal(tree, tvb, offset + IPH_SRC, 4, "Process Info: ");
  PROTO_ITEM_SET_GENERATED(process_info_item);

  process_info_tree = proto_item_add_subtree(process_info_item, ett_ip_process_info);
  item = proto_tree_add_string(process_info_tree, hf_ip_process, tvb, offset, 4, process_name);
  PROTO_ITEM_SET_GENERATED(item);
}

It is also simpler, first of all, to check if there is a corresponding process for the IP package, regardless of it.

Otherwise, add a node "Process Info:" to the tree, create a string subnode beneath it, and give it the process name.

This function is called from dissect_ip_v4, the place where IPv4 packets are parsed, is called near the end of this function.

The end result is similar to this:

  Right-click and select Apply in Filter. The contents shown in the top filter are:

ip.process == "\\Device\\HarddiskVolume8\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"

This allows filtering based on the process path.

Four others

Complete modifications can be found here:

TurtleRock: Test. - Gitee.com

Or here:

https://download.csdn.net/download/lanzheng_1113/34964722

The link will prompt for missing symbols, so add iphlpapi.lib to the linker.

The program is currently bugged by several major bugs and BUG s:

  • Only TCP protocol is supported, UDP protocol is not.
  • Some IP packages do not have a Process Info: field after parsing.
  • Occasional crash, seems to be related to memory release (not sure if it is due to modifications in this article, not empty for now)

These questions will be studied when you are free.

Posted by Quest on Wed, 27 Oct 2021 10:05:23 -0700