Tshark抓包脚本


抓包过滤有如下两个格式:

  • ✅主机
tshark -i any -f 'host 192.168.1.1/32' -w $path/timestamp-$host.pcapng
  • ✅网络号
tshark -i any -f 'net 192.168.1.0/24' -w $path/timestamp-$host.pcapng
貌似可用带掩码的方式,直接使用net参数过滤,不区分是主机还是网络号,例如:
tshark -i any -f 'net 192.168.1.1/32' -w $path/timestamp-$host.pcapng
tshark -i any -f 'net 192.168.1.0/24' -w $path/timestamp-$host.pcapng

快速抓单目标

#!/bin/bash

iface="enp1s0"
path="/data"

# 创建目录(如果不存在)
mkdir -p "$path"

# 获取当前时间戳
timestamp=$(date +%Y-%m-%d-%H:%M:%S)

# 读取用户输入
read -p "请输入要抓包的主机或网段 (如 192.168.1.1 或 192.168.0.0/24): " host

# 判断输入是否为空
if [ -z "$host" ]; then
    echo "抓包目标不能为空"
    exit 1
fi

# 构建过滤器
if [[ "$host" =~ / ]]; then
    filter="net $host"
else
    filter="host $host"
fi

# 将斜杠替换为下划线防止文件名出错
safe_host="${host//\//_}"
file="$path/${timestamp}-${safe_host}.pcapng"

echo "开始抓包,使用接口 $iface,过滤条件: $filter"
tshark -i "$iface" -f "$filter" -w "$file"

echo "抓包完成,文件已保存为:$file"

# 是否转换为可阅读文本
read -p "是否转换为txt文本便于AI分析?(y/n, 默认 y): " answer
answer=${answer:-y}

if [[ "$answer" == "y" || "$answer" == "Y" ]]; then
    txt_file="$path/${timestamp}-${safe_host}.txt"
    echo "正在转换格式,请不要操作!"
    tshark -r "$file" \
        -T fields \
        -E header=y \
        -e frame.number \
        -e frame.time_relative \
        -e ip.src \
        -e ip.dst \
        -e tcp.srcport \
        -e tcp.dstport \
        -e _ws.col.Info > "$txt_file"
    echo "已转换为文本:$txt_file"
fi

交互抓包

#!/bin/bash

# 抓包输出目录
output_dir="/data"
mkdir -p "$output_dir"

# 显示可用网卡
echo "=== 可用网卡列表 ==="
tshark -D
echo "==================="

# 选择网卡
read -p "请输入要抓包的网卡编号(例如 1): " iface_num
iface=$(tshark -D | sed -n "${iface_num}p" | cut -d. -f2- | sed 's/^[[:space:]]*//')

if [ -z "$iface" ]; then
    echo "❌ 无效的网卡编号,退出。"
    exit 1
fi

# 输入主机或网段
read -p "请输入抓包目标(IP或CIDR网段,例如 192.168.1.1 或 192.168.0.0/24): " target

if [ -z "$target" ]; then
    echo "❌ 抓包目标不能为空"
    exit 1
fi

# 判断过滤器类型
if [[ "$target" =~ / ]]; then
    filter="net $target"
else
    filter="host $target"
fi

# 选填:抓包持续时间
read -p "是否限制抓包时长?输入秒数(如 30),留空表示手动 Ctrl+C 停止: " duration

# 安全命名(去掉斜线)
timestamp=$(date +%Y-%m-%d-%H-%M-%S)
safe_target="${target//\//_}"
pcap_file="${output_dir}/${timestamp}-${safe_target}.pcapng"

echo "✅ 开始抓包,接口: $iface,过滤: $filter"
if [ -n "$duration" ]; then
    tshark -i "$iface" -f "$filter" -a duration:$duration -w "$pcap_file"
else
    tshark -i "$iface" -f "$filter" -w "$pcap_file"
fi

echo "✅ 抓包完成,文件保存为: $pcap_file"

# 是否导出为 txt
read -p "是否转换为txt文本供AI分析?(y/n, 默认 y): " convert
convert=${convert:-y}

if [[ "$convert" =~ ^[Yy]$ ]]; then
    txt_file="${output_dir}/${timestamp}-${safe_target}.txt"
    tshark -r "$pcap_file" \
        -T fields \
        -E header=y \
        -e frame.number \
        -e frame.time_relative \
        -e ip.src \
        -e ip.dst \
        -e tcp.srcport \
        -e tcp.dstport \
        -e _ws.col.Info > "$txt_file"
    echo "✅ 转换完成,文本保存为: $txt_file"
fi

交互同时抓多目标

#!/bin/bash

output_dir="/data"
mkdir -p "$output_dir"

# 显示网卡
echo "=== 可用网卡列表 ==="
tshark -D
echo "==================="

read -p "请输入要抓包的网卡编号: " iface_num
iface=$(tshark -D | sed -n "${iface_num}p" | cut -d. -f2- | sed 's/^[[:space:]]*//')
[ -z "$iface" ] && echo "无效网卡编号" && exit 1

read -p "请输入多个抓包目标(用空格分隔,如 192.168.1.1 192.168.0.0/24): " -a targets
[ ${#targets[@]} -eq 0 ] && echo "目标不能为空" && exit 1

read -p "是否限制抓包时长?输入秒数(默认无限制): " duration

for target in "${targets[@]}"; do
    if [[ "$target" =~ / ]]; then
        filter="net $target"
    else
        filter="host $target"
    fi

    timestamp=$(date +%Y-%m-%d-%H-%M-%S)
    safe_target="${target//\//_}"
    pcap_file="${output_dir}/${timestamp}-${safe_target}.pcapng"

    echo "▶ 正在抓包 $target"
    if [ -n "$duration" ]; then
        tshark -i "$iface" -f "$filter" -a duration:$duration -w "$pcap_file"
    else
        tshark -i "$iface" -f "$filter" -w "$pcap_file"
    fi
    echo "✅ 已保存到 $pcap_file"
done

web控制抓单目标

from flask import Flask, render_template_string, request
import subprocess
import os
from datetime import datetime

app = Flask(__name__)
OUTPUT_DIR = "/data"
os.makedirs(OUTPUT_DIR, exist_ok=True)

TEMPLATE = """
<!doctype html>
<title>抓包控制台</title>
<h2>Web 抓包控制台</h2>
<form method="post">
    网卡名: <input name="iface" value="enp1s0"><br><br>
    抓包目标(IP 或 网段): <input name="target"><br><br>
    抓包时长(秒,可选): <input name="duration"><br><br>
    <input type="submit" value="开始抓包">
</form>
{% if msg %}<p style="color:green">{{ msg }}</p>{% endif %}
"""

@app.route("/", methods=["GET", "POST"])
def index():
    msg = ""
    if request.method == "POST":
        iface = request.form["iface"]
        target = request.form["target"]
        duration = request.form.get("duration")
        if "/" in target:
            f = f"net {target}"
        else:
            f = f"host {target}"
        timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
        safe_target = target.replace("/", "_")
        pcap_path = os.path.join(OUTPUT_DIR, f"{timestamp}-{safe_target}.pcapng")
        cmd = ["tshark", "-i", iface, "-f", f, "-w", pcap_path]
        if duration:
            cmd += ["-a", f"duration:{duration}"]
        subprocess.Popen(cmd)
        msg = f"抓包已启动,保存为:{pcap_path}"
    return render_template_string(TEMPLATE, msg=msg)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888)

web控制抓多目标

from flask import Flask, render_template_string, request
import subprocess
import os
from datetime import datetime

app = Flask(__name__)
OUTPUT_DIR = "/data"
os.makedirs(OUTPUT_DIR, exist_ok=True)

TEMPLATE = """
<!doctype html>
<title>多目标抓包平台</title>
<h2>Web 多目标抓包平台</h2>
<form method="post">
    网卡名: <input name="iface" value="enp1s0"><br><br>
    抓包目标(可多个,用空格分隔): <input name="targets"><br><br>
    抓包时长(秒,可选): <input name="duration"><br><br>
    <input type="submit" value="开始抓包">
</form>
{% if msg %}<p style="color:green;white-space:pre-line;">{{ msg }}</p>{% endif %}
"""

@app.route("/", methods=["GET", "POST"])
def index():
    msg = ""
    if request.method == "POST":
        iface = request.form["iface"]
        targets = request.form["targets"].split()
        duration = request.form.get("duration")

        for target in targets:
            if "/" in target:
                f = f"net {target}"
            else:
                f = f"host {target}"
            timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
            safe_target = target.replace("/", "_")
            pcap_path = os.path.join(OUTPUT_DIR, f"{timestamp}-{safe_target}.pcapng")
            cmd = ["tshark", "-i", iface, "-f", f, "-w", pcap_path]
            if duration:
                cmd += ["-a", f"duration:{duration}"]
            subprocess.Popen(cmd)
            msg += f"✅ {target} 抓包启动,保存为 {pcap_path}\n"

    return render_template_string(TEMPLATE, msg=msg)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888)

web优化(终极版,推荐)

from flask import Flask, render_template_string, request, redirect, url_for, send_from_directory, Response
import subprocess
import os
from datetime import datetime
import threading

app = Flask(__name__)
OUTPUT_DIR = "/data"
os.makedirs(OUTPUT_DIR, exist_ok=True)

tasks = {}
history = []
task_id_counter = 1
log_buffers = {}

#--- 转换 TXT ---
def convert_to_txt(pcap_path, txt_path):
    cmd = [
        "tshark", "-r", pcap_path,
        "-T", "fields", "-E", "header=y",
        "-e", "frame.number", "-e", "frame.time_relative",
        "-e", "ip.src", "-e", "ip.dst",
        "-e", "tcp.srcport", "-e", "tcp.dstport",
        "-e", "_ws.col.Info"
    ]
    with open(txt_path, "w") as f:
        proc = subprocess.Popen(cmd, stdout=f)
        proc.wait()  # 避免产生僵尸进程

def async_convert(task):
    pcap_file = task["file"]
    txt_file = task["txt"]
    convert_to_txt(pcap_file, txt_file)
    for h in history:
        if h["pcap"] == os.path.basename(pcap_file):
            h["txt"] = os.path.basename(txt_file)

#--- 实时输出 tshark ---
def stream_output(tid, proc):
    log_buffers[tid] = []
    for line in iter(proc.stdout.readline, b''):
        log_buffers[tid].append(line.decode())

@app.route("/")
def index():
    return render_template_string(TEMPLATE, tasks=tasks, history=history, default_ip=request.remote_addr)

@app.route("/start", methods=["POST"])
def start_capture():
    global task_id_counter
    iface = request.form["iface"]
    targets = request.form["targets"].split()
    duration = request.form.get("duration") or "3600"
    snaplen = request.form.get("snaplen") or "400"
    auto_txt = "auto_txt" in request.form

    for target in targets:
        capture_filter = f"net {target}" if "/" in target else f"host {target}"
        timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
        safe_target = target.replace("/", "_")
        pcap = f"{timestamp}-{safe_target}.pcapng"
        txt = f"{timestamp}-{safe_target}.txt"
        pcap_path = os.path.join(OUTPUT_DIR, pcap)
        txt_path = os.path.join(OUTPUT_DIR, txt)

        cmd = ["tshark", "-i", iface, "-f", capture_filter, "-s", snaplen, "-w", pcap_path, "-l"]
        if duration:
            cmd += ["-a", f"duration:{duration}"]
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

        tid = task_id_counter
        task_id_counter += 1
        tasks[tid] = {
            "target": target,
            "file": pcap_path,
            "txt": txt_path if auto_txt else None,
            "pid": proc.pid,
            "proc": proc,
            "timestamp": timestamp,
            "auto_txt": auto_txt
        }

        threading.Thread(target=stream_output, args=(tid, proc), daemon=True).start()

    return redirect(url_for("index"))

@app.route("/stop/<int:tid>", methods=["POST"])
def stop_capture(tid):
    task = tasks.pop(tid, None)
    if task:
        proc = task["proc"]
        proc.terminate()
        try:
            proc.wait(timeout=3)
        except subprocess.TimeoutExpired:
            proc.kill()

        history.append({
            "target": task["target"],
            "timestamp": task["timestamp"],
            "pcap": os.path.basename(task["file"]),
            "txt": None
        })

        if task.get("auto_txt"):
            threading.Thread(target=async_convert, args=(task,), daemon=True).start()

    return redirect(url_for("index"))

@app.route("/convert/<pcap>", methods=["POST"])
def manual_convert(pcap):
    txt = pcap.replace(".pcapng", ".txt")
    convert_to_txt(os.path.join(OUTPUT_DIR, pcap), os.path.join(OUTPUT_DIR, txt))
    for h in history:
        if h["pcap"] == pcap:
            h["txt"] = txt
    return redirect(url_for("index"))

@app.route("/logs/<int:tid>")
def show_logs(tid):
    lines = log_buffers.get(tid, ["暂无日志"])
    content = "".join(lines)
    html = f"""
    <html><head>
        <meta charset="utf-8">
        <meta http-equiv="refresh" content="1">
        <title>实时日志</title>
        <style>body {{ background:black; color:lime; padding:10px; }}</style>
    </head><body>
    <pre>{content}</pre>
    </body></html>
    """
    return Response(html, mimetype="text/html")

@app.route("/download/<filename>")
def download(filename):
    return send_from_directory(OUTPUT_DIR, filename, as_attachment=True)

#--- 前端模板 ---
TEMPLATE = """
<!doctype html>
<html lang="zh">
<head>
    <meta charset="utf-8">
    <title>WEB抓包控制台</title>
    <!--<link href="http://10.0.1.200/bootstrap.min.css" rel="stylesheet">-->
    <link href="http://192.168.20.100:8080/bootstrap.min.css" rel="stylesheet">
    <style>
        body { padding: 2rem; background-color: #f8f9fa; }
        .card { margin-bottom: 2rem; }
        .table thead th { background-color: #e9ecef; }
        .form-control, .btn { margin-bottom: 1rem; }
        pre { max-height: 300px; overflow-y: scroll; background: #000; color: #0f0; padding: 1rem; }
    </style>
</head>
<body>
<div class="container">
    <h2 class="mb-4 text-primary">WEB抓包控制台</h2>

    <div class="card shadow-sm">
        <div class="card-header">发起新抓包任务</div>
        <div class="card-body">
            <form method="post" action="/start">
                <div class="row">
                    <div class="col-md-3">
                        <label>网卡名</label>
                        <input class="form-control" name="iface" value="enp1s0">
                    </div>
                    <div class="col-md-3">
                        <label>抓包目标(空格分隔)</label>
                        <input class="form-control" name="targets" value="{{ default_ip }}">
                    </div>
                    <div class="col-md-2">
                        <label>时长(秒)</label>
                        <input class="form-control" name="duration" value="3600">
                    </div>
                    <div class="col-md-2">
                        <label>每包最大字节</label>
                        <input class="form-control" name="snaplen" value="400">
                    </div>
                    <div class="col-md-2">
                        <label>选项</label><br>
                        <input type="checkbox" name="auto_txt"> 自动转为TXT
                    </div>
                </div>
                <button type="submit" class="btn btn-success mt-2">开始抓包</button>
            </form>
        </div>
    </div>

    <div class="card shadow-sm">
        <div class="card-header">当前抓包任务</div>
        <div class="card-body">
            {% if tasks %}
            <div class="table-responsive">
                <table class="table table-bordered table-hover align-middle">
                    <thead>
                        <tr><th>ID</th><th>目标</th><th>PID</th><th>操作</th><th>日志</th></tr>
                    </thead>
                    <tbody>
                        {% for tid, t in tasks.items() %}
                        <tr>
                            <td>{{ tid }}</td>
                            <td>{{ t['target'] }}</td>
                            <td>{{ t['pid'] }}</td>
                            <td>
                                <form method="post" action="/stop/{{ tid }}">
                                    <button class="btn btn-danger btn-sm">停止</button>
                                </form>
                            </td>
                            <td><a href="/logs/{{ tid }}" target="_blank" class="btn btn-secondary btn-sm">查看日志</a></td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
            {% else %}
            <p class="text-muted">暂无运行中任务</p>
            {% endif %}
        </div>
    </div>

    <div class="card shadow-sm">
        <div class="card-header">抓包历史记录</div>
        <div class="card-body">
            {% if history %}
            <div class="table-responsive">
                <table class="table table-striped table-bordered align-middle">
                    <thead>
                        <tr><th>时间</th><th>目标</th><th>PCAP 文件</th><th>TXT 文件</th></tr>
                    </thead>
                    <tbody>
                        {% for h in history %}
                        <tr>
                            <td>{{ h['timestamp'] }}</td>
                            <td>{{ h['target'] }}</td>
                            <td><a href="/download/{{ h['pcap'] }}" class="btn btn-outline-primary btn-sm">{{ h['pcap'] }}</a></td>
                            <td>
                                {% if h['txt'] %}
                                <a href="/download/{{ h['txt'] }}" class="btn btn-outline-secondary btn-sm">{{ h['txt'] }}</a>
                                {% else %}
                                <form method="post" action="/convert/{{ h['pcap'] }}">
                                    <button class="btn btn-warning btn-sm">手动转换</button>
                                </form>
                                {% endif %}
                            </td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
            {% else %}
            <p class="text-muted">暂无历史记录</p>
            {% endif %}
        </div>
    </div>
</div>
</body>
</html>
"""

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888)

声明:Ethan's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Tshark抓包脚本


I love you more than I can say. I love you heart and soul. Why must I love you so much?