在 tshark 中打印 "showname" 字段属性

Print the "showname" field attribute in tshark

上下文

我有一个包含一堆信标帧的 pcap 文件(换句话说,我将我的 Wi-Fi 适配器置于监控模式,在 "wlan.fc.type_subtype == 0x08" 上过滤的同时开始捕获并保存)。

现在,我想以某种方式显示这些数据包的特定字段。其中:

我不太关心表示形式:纯文本、xml、json、csv、X。我没问题。我只是不想要比我真正需要的更多的数据,并且输出需要对人眼(wireshark newb)有意义。

最后,我也想过滤 pcap 以获得唯一的集合并计算出现次数(一些“|sort|uniq -c”会做),但我们现在先不去那里。

我目前的解决方案

第一步可以是,例如:

$ tshark -r capture.pcap -c 1 -T fields -e wlan_mgt.ssid -e wlan.ta -e wlan_mgt.ds.current_channel -e wlan_mgt.rsn.gcs.type -e wlan_mgt.rsn.pcs.type -e wlan_mgt.rsn.akms.type
MySSID  XX:XX:XX:XX:XX:XX   2   4   4   2

(手动)将数字与其文本含义匹配后,您会得到:

这就是我要找的。但是,如前所述,我必须手动完成,这不是一个选项。

问题

在上面你可以看到我目前实现上述目标的方法。通过

tshark -r capture.pcap -c 1 -T pdml

我得到,例如(截图):

<field name="wlan_mgt.rsn.pcs.list" showname="Pairwise Cipher Suite List 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="" value="">
    <field name="wlan_mgt.rsn.pcs" showname="Pairwise Cipher Suite: 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="1027076" value="000fac04">
        <field name="wlan_mgt.rsn.pcs.oui" showname="Pairwise Cipher Suite OUI: 00-0f-ac (Ieee8021)" size="3" pos="112" show="4012" value="000fac"/>
        <field name="wlan_mgt.rsn.pcs.type" showname="Pairwise Cipher Suite type: AES (CCM) (4)" size="1" pos="115" show="4" value="04"/>
    </field>
</field>

...,这告诉我 tshark 确实有我需要的信息(以 "showname" 属性的形式)。

显然,当使用“-T 字段 -e X”时,tshark 输出 "show" 属性中的值。我觉得我想要 "showname" 属性背后的值。不幸的是,在烦人 google 一段时间后,我仍然不知道如何或是否可能。

我也对截然不同的想法持开放态度,但主要的收获是我不能脱离 pcap 文件(排除了 iwlist、kismet 等)。我也最好不要开始编写搜索和替换规则来用它们的文本表示替换无意义的数字。我希望以更清洁的方式解决它。

我一直在搞 tshark 一段时间,直到我决定它无法完成。使用神奇的 C++ 库进行一些编程 libtins 让我到达了我需要去的地方。

来源在下方。享受:)

#include <tins/tins.h>

#include <algorithm>
#include <iostream>
#include <map>
#include <string>

using namespace Tins;
using namespace std;


/*
 * Container class for the data that is retrieved from the beacon.
 */
class Unit {
public:
    /*
     * Constructor. Parses the Dot11Beacon object and takes all the necessary
     * data from it.
     */
    Unit(Dot11Beacon& beacon);
    Unit() = default;
    unsigned getCount();
    void incrementCount();

    /*
     * Prints this object onto the command line, in CSV format
     */
    void print();

private:
    string ssid;
    string bssid;
    unsigned channel;
    unsigned count;

    string gcs; // Group Cipher Suite
    string pcs; // Pairwise Cipher Suite
    string akm; // Authentication suite

    /*
     * Returns a string representation of a RSNInformation::CypherSuites enum value
     */
    string type_to_string(const RSNInformation::CypherSuites& type);

    /*
     * Returns a string representation of a RSNInformation::AKMSuites enum value
     */
    string type_to_string(const RSNInformation::AKMSuites& type);
};

Unit::Unit(Dot11Beacon& beacon) :
    count {1} /* When this unit is created, it has been seen exactly once */ {
    ssid = beacon.ssid();
    bssid = beacon.addr3().to_string();
    channel = unsigned(beacon.ds_parameter_set());

    RSNInformation rsn;
    for(const auto &opt : beacon.options()) {
        if (opt.option() == Dot11::RSN) {
            rsn = beacon.rsn_information();

            // Put all authentication suite types in a string
            const RSNInformation::akm_type& akmTypeList = rsn.akm_cyphers();
            for (const auto& akmIt : akmTypeList) {
                if (akm.size() == 0)
                    akm += type_to_string(akmIt);
                else
                    akm += ";" + type_to_string(akmIt);
            }

            // Put all group cipher types in a string
            const RSNInformation::CypherSuites& gcsType = rsn.group_suite();
            gcs = type_to_string(gcsType);


            // Put all pairwise ciphers in a string
            const RSNInformation::cyphers_type& pcsTypeList = rsn.pairwise_cyphers();
            for (const auto& pcsIt : pcsTypeList) {
                if (pcs.size() == 0)
                    pcs += type_to_string(pcsIt);
                else
                    pcs += ";" + type_to_string(pcsIt);
            }
        }
    }
}

unsigned Unit::getCount() {
    return count;
}

void Unit::incrementCount() {
    count += 1;
}

void Unit::print() {
    string ssid_to_print;
    if (ssid.length() == 0) {
        ssid_to_print = "<ZERO_LENGTH>";
    } else if (!isprint(ssid[0])) {
        ssid_to_print = to_string(static_cast<int>(ssid[0]));
    } else {
        ssid_to_print = ssid;
    }
    if (find(ssid_to_print.begin(), ssid_to_print.end(), ',') != ssid_to_print.end()) {
        ssid_to_print = "\"" + ssid_to_print + "\"";
    }
    cout <<  ssid_to_print << ","
         << bssid << ","
         << to_string(channel) << ","
         << to_string(count) << ","
         << gcs << ","
         << pcs << ","
         << akm << endl;
}

string Unit::type_to_string(const RSNInformation::CypherSuites& type) {
    switch (type) {
    case RSNInformation::CypherSuites::CCMP:
        return "CCMP";
        break;
    case RSNInformation::CypherSuites::TKIP:
        return "TKIP";
        break;
    case RSNInformation::CypherSuites::WEP_104:
        return "WEP_104";
        break;
    case RSNInformation::CypherSuites::WEP_40:
        return "WEP_40";
        break;
    }
}

string Unit::type_to_string(const RSNInformation::AKMSuites& type) {
    switch (type) {
    case RSNInformation::AKMSuites::PMKSA:
        return "PMKSA";
        break;
    case RSNInformation::AKMSuites::PSK:
        return "PSK";
        break;
    }
}

/*
 * Class that reads the pcap, keeps track of the units and writes out one
 * beacon frame in pcap format for each unique AP it finds. This file is called
 * "unique_beacons.pcap"
 */
class PCAPParser {
public:
    /*
     * Constructor. It takes the exact parameters that it will pas on to its
     * FileSniffer object (a FileSniffer is actually just a file reader).
     */
    PCAPParser(const string& pcapFilename, const string& filter);

    /*
     * Start reading the file.
     */
    bool run();

    /*
     * Print CSV header and ask all of our collected Unit objects to print themselves
     */
    void print();

private:
    FileSniffer sniffer;
    PacketWriter writer;
    map<string, Unit> apMap; // stands for Access Point Map

    bool handler(PDU&);
};

PCAPParser::PCAPParser(const string& pcapFilename, const string& filter) :
    sniffer {pcapFilename, filter},
    writer {"unique_beacons.pcap", PacketWriter::RADIOTAP}  {
    for (auto it = apMap.begin(); it != apMap.end(); it++) {
        it->second.print();
    }
}

bool PCAPParser::run() {
    sniffer.sniff_loop( [this] (PDU& pdu) { return (bool) this->handler (pdu); } );
    return true;
}

bool PCAPParser::handler(PDU& pdu) {
    Dot11Beacon& beacon = pdu.rfind_pdu<Dot11Beacon>();

    // An ESSID may span multiple BSSID's. Also, it's nice to keep track of what
    // channels an AP has been on. Therefore, the combination of SSID, BSSID and
    // channel is considered key.
    const string& ssid = beacon.ssid();
    const string& mac = beacon.addr3().to_string();
    const unsigned channel = unsigned(beacon.ds_parameter_set());
    const string key = ssid + mac + to_string(channel);
    if (apMap.find(key) == apMap.end()) { // we've got a new one
        Unit unit(beacon);
        apMap[key] = unit;
        writer.write(pdu);
    } else {
        apMap[key].incrementCount();
    }
    return true;
}

void PCAPParser::print() {
    // Print the headers for the CSV output
    cout << "SSID,BSSID,Current_channel,Count,Group_Cipher,Pairwise_Ciphers,Authentication_Suite" << endl;

    // Ask each of the units to print themselves for the CSV output
    for (auto it = apMap.begin(); it != apMap.end(); it++) {
        it->second.print();
    }
}

int main(int argc, char *argv[]) {
    if(argc != 2) {
        std::cout << "Usage: " << *argv << " <PCAP_FILE>\n";
        return 1;
    }

    PCAPParser pcapParser(argv[1], "wlan type mgt subtype beacon");
    pcapParser.run();
    pcapParser.print();
}

编译:

g++ pcapreader.cpp -o pcapreader -O3 -std=c++11 -lpthread -ltins

输出为:

$ ./pcapreader capture.pcap
SSID,BSSID,Current_channel,Count,Group_Cipher, Pairwise_Ciphers,Authentication_Suite
MyWiFi,XX:XX:XX:XX:XX:XX,13,2,TKIP,TKIP;CCMP,PSK
... 
...

最后说明:如果你打开unique_beacons.pcap,你可能会发现很多[Malformed Packet]。显然,如果一些标记参数接收错误,仍然可以成功解析帧。您可以尝试修改代码,以便它只将完全完整的帧写出到 pcap 文件。