banner
Hi my new friend!

ev3_basic_wireshark解析器

Scroll down

wireshark解析器

问题树

image-20250429103306874

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 对于乐高编程特有的命令ev3使用了加载器来打开对应的通信包wireshark -X lua_script:ev3_dissector.lua在wireshark中可以看到指令的通信但是导出成json保存却看不到对应的通信

## 能否利用这个加载器来对一些数据做一下预处理比如说解http跟踪流的base64

## wireshark如何解析http命令

### Wireshark如何体现对协议的解析
#### wiresharkGUI中什么展现什么不展现
##### Wireshark对象导出中的json格式的文件中关于数据包部分什么展现什么不展现

### Wireshark -X可以导入自定义解析器,那么对于http是否使用默认的解析器
#### Wireshark对于http的解析逻辑是什么样的
##### 什么东西是解析出来的 什么东西不是解析出来的原来就是有的


### wireshark对于json的导出逻辑是什么
#### 这是原来的通信包原本的内容吗
##### 通信包在解析前的信息存储形式是什么样的?

wireshark 加载规则

wireshark页面上的数据包有一部分是加载出来的,很多不是原始包内的数据就是这样的,原始包的数据可以通过导出json查看

对于http协议来说

  1. get模式的数据包正常来讲就是明文传输的,wireshark数据流能看到的基本在json中也可以看到

  2. POST协议可能就会出现差异

    这是wireshark中的POST的http数据流的效果

    image-20250429194326750

    在json中的POST格式是这样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    "urlencoded-form": {
    "Form item: \"ReplySuccessPage\" = \"advanced.htm\"": {
    "urlencoded-form.key": "ReplySuccessPage",
    "urlencoded-form.value": "advanced.htm"
    },
    ······
    "Form item: \"AdminPassword\" = \"63c56363c523637c126363ab636bab63c7c76363232363961263c305639a1263c7c76363232363961263c305639a1263c7c76363232363961263c305639a1263\"": {
    "urlencoded-form.key": "AdminPassword",
    "urlencoded-form.value": "63c56363c523637c126363ab636bab63c7c76363232363961263c305639a1263c7c76363232363961263c305639a1263c7c76363232363961263c305639a1263"
    },
    "Form item: \"SessionKey\" = \"1735219692\"": {
    "urlencoded-form.key": "SessionKey",
    "urlencoded-form.value": "1735219692"
    },
    "Form item: \"ConfigSystemAdmin\" = \"Apply\"": {
    "urlencoded-form.key": "ConfigSystemAdmin",
    "urlencoded-form.value": "Apply"
    }
    }
    }

对于非标准通信协议来说

在这个乐高通信协议中倒还是一样

  1. wireshark解析的结果

    image-20250429230436580

  2. json导出的结果

    image-20250429230734626

似乎目前尚且没有能导出加载器加载后的内容的方法

1
tshark -r ev3_basic.pklg -X lua_script:ev3_dissector.lua -Y "ev3" -T json > T.json

这是最接近可能导出加载完加载器之后以json格式导出文件的命令了,但是即便是这样,这个命令导出的json也仍然没有以明文的形式表示EV3 Message(图片中)那样效果的内容

image-20250429221255611

AI总结

在 Wireshark 界面 在导出的 JSON 文件
分层显示(Frame / Ethernet / IP / TCP / HTTP) layers 字段下有对应协议名
每一层字段人类可读、解释得很清楚 每一层字段作为 JSON 键值出现,原始值保存
HTTP报文一行一行展开 HTTP字段拆成单独的 key:value 保存
动态解析部分(比如详细参数解释)只在GUI显示 JSON只保留基本字段,不会包含详细解析描述

蓝牙协议学习

蓝牙核心技术概述(四):蓝牙协议规范(HCI、L2CAP、SDP、RFOCMM)-CSDN博客

HCI、L2CAP、SDP、RFCOMM。对比于英特网五层结构来说:HCI相当于与物理层打交道的协议,L2CAP协议则是链路层相关协议,SDP和RFCOMM则是运输层相关协议,当然其上也有对应的应用层相关的一些协议。SDP用来发现周围蓝牙服务,然后由L2CAP来建立信道链接,然后传输由上层RFCOMM给予的数据分组。

wireshark使用加载器

加载器启动分类

命令启动dissector

伴随加载器启动wireshark

1
wireshark -X lua_script:ev3_dissector.lua

配置默认启动项, 允许wireshark启动的时候自动加载自定义加载器

1
2
bashCopyEdit~/.config/wireshark/init.lua      # Linux/macOS
%APPDATA%\Wireshark\init.lua # Windows

在里面加入:

1
dofile("C:\\path\\to\\your\\http_base64_dissector.lua")

然后你只需要双击 .pcapng 文件,Wireshark 启动就会自动加载这个 dissector!

本题中使用了ev3格式的加载器

https://github.com/ev3dev/lms-hacker-tools/tree/master/EV3

Any packets to or from TCP port 5555 will be interpreted as “EV3” protocol. USB interrupt data with class IF_CLASS_UNKNOWN will also be interpreted as “EV3” protocol.
进出 TCP 端口 5555 的任何数据包都将被解释为“EV3” 协议。类为 IF_CLASS_UNKNOWN 的 USB 中断数据也将被解释为“EV3” 协议。

You can also use the filter to search for packets that contain certain types of data. For example if you want to search for all packets that use the LIST_FILES system command, then you would use ev3.sys_cmd == 0x99 for the filter.
您还可以使用筛选条件来搜索包含某些类型数据的数据包。例如,如果要搜索使用 LIST_FILES system 命令,则使用 ev3.sys_cmd == 0x99 作为过滤器。

通过以上方式直接定义出一个EV3的类型(对比没加载器之前)

比如 hitCON CTF'18EV3-Basic题目,实际上使用的蓝牙通信是直接通过16进制来进行传输直接控制机器,机器硬编码了一个映射表,对于传输进去的十六进制会直接变成机器语言执行,但是对于人来说进行wireshark审计就非常不方便, 所以使用加载器(ev3)进行加载,但是实际上并没有改变原来的十六进制实际上的数据包

image-20250429214036605

加载器内对这些用十六进制传输的数据进行了映射,也就是说在使用这个拓展的wireshark打开这个数据包的时候就直接对着相应的数据进行了编码, 让人可以直观的看到是什么意思

翻译前如下:

image-20250429214926106

翻译后如下:

image-20250429214605056

通过对比可以清晰的发现这个Dessector的作用主要有两个:

  1. 定义什么是ev3协议,并且做出归类标记,方便被过滤
  2. 按照ev3的映射关系进行了翻译

自定义加载器可以解决很多问题 dissector

先从简单出发,假设http包只有一个tcp包就完成

生成过程

这是一个demo,主要的解决问题如下:

  1. 能够处理http数据流,处理分片传输的http包(tcp分片)

  2. 先使用原生从http_dissector进行解析,然后对解析的结果寻找POST Body在进行base64解码

    以下是拦截监听时候对base64加密http包的处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    -- 定义一个新的 Wireshark 协议 dissector,协议名为 "b64http",显示名为 "HTTP Base64 Inspector"
    local b64_proto = Proto("b64http", "HTTP Base64 Inspector")

    -- 定义一个供显示用的字段,用于展示解码后的 Base64 内容
    local f_decoded = ProtoField.string("b64http.decoded", "Decoded Base64 Content")

    -- 把字段绑定到协议中
    b64_proto.fields = { f_decoded }

    -- 获取已有的 HTTP dissector(内置的 http 协议解析器)
    local http_dissector = Dissector.get("http")

    -- Base64 解码函数(纯 Lua 实现,无需外部库)
    local function decode_b64(data)
    local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -- Base64 字符表
    data = data:gsub('[^'..b..'=]', '') -- 移除所有非 Base64 合法字符
    return (data:gsub('.', function(x)
    if (x == '=') then return '' end -- padding 无需转换
    local r,f='',(b:find(x)-1) -- 找到字符对应的索引(0~63)
    for i=6,1,-1 do
    r=r..(f%2^i - f%2^(i-1) > 0 and '1' or '0') -- 转为6位二进制字符串
    end
    return r
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) -- 每8位转换为1个字节
    if (#x ~= 8) then return '' end
    local c=0
    for i=1,8 do c=c + (x:sub(i,i)=='1' and 2^(8-i) or 0) end
    return string.char(c) -- 转成字符
    end))
    end

    -- 主解析函数(每次抓包调用)
    function b64_proto.dissector(buffer, pinfo, tree)
    pinfo.cols.protocol = "B64HTTP" -- 修改协议列显示名称为 B64HTTP

    -- 使用默认 HTTP dissector 先处理 HTTP 协议
    http_dissector:call(buffer, pinfo, tree)

    -- 如果不是“被访问过”的包(可能是 TCP 片段,还没组装完整),则不处理,避免重复
    if not pinfo.visited then return end

    -- 将整个 buffer 转为字符串,方便文本匹配
    local payload = buffer():string()

    -- 从 HTTP 头部提取 Content-Length,用来确认 body 的长度
    local content_length = payload:match("Content%-Length: (%d+)")
    if not content_length then return end
    content_length = tonumber(content_length)

    -- 找到 HTTP 头部结束位置(\r\n\r\n)
    local header_end = payload:find("\r\n\r\n")
    if not header_end then return end

    local body_start = header_end + 4 -- body 起始位置
    local body_actual_len = #payload - body_start + 1 -- 实际已经收到的 body 长度

    -- 如果实际长度不够 Content-Length,说明数据还没收完,需要等待更多 TCP 数据
    if body_actual_len < content_length then
    pinfo.desegment_len = content_length - body_actual_len -- 告诉 Wireshark 需要继续重组
    return
    end


    -- 提取完整的 body 数据(从 payload 中截取)
    local body = payload:sub(body_start, body_start + content_length - 1)

    -- 从 JSON 中提取 base64 字符串(假设格式是:"file": "....")
    local base64_content = body:match('"file"%s*:%s*"([^"]+)"')
    if not base64_content then return end

    -- 解码 base64 内容
    local decoded = decode_b64(base64_content)

    -- 在 Wireshark 界面上添加新的子树显示解析结果
    local subtree = tree:add(b64_proto, buffer(), "HTTP Base64 Analysis")
    subtree:add(f_decoded, decoded)
    end

    -- 将 dissector 注册到指定 TCP 端口(这里是 8080,表示监听所有 8080 的 TCP 流量)
    local tcp_port = DissectorTable.get("tcp.port")
    tcp_port:add(8080, b64_proto)
  3. 使用python手动生成一个符合这个加载器加载解析base64的数据包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from scapy.all import *
    from scapy.layers.inet import IP, TCP
    from scapy.packet import Raw

    # 构造 HTTP POST 请求内容(包含 Base64 编码字段)
    http_request = (
    "POST /upload HTTP/1.1\r\n"
    "Host: example.com\r\n"
    "Content-Type: application/json\r\n"
    "Content-Length: 28\r\n"
    "\r\n"
    '{ "file": "SGVsbG8gV29ybGQh" }'
    )

    # 构造 TCP/IP 包
    packet = IP(src="192.168.0.2", dst="192.168.0.1") / \
    TCP(sport=12345, dport=8080, flags="PA", seq=1000, ack=1) / \
    Raw(load=http_request)

    # 保存为 .pcapng 文件
    wrpcap("http_base64_example.pcapng", [packet])

    print("✅ 流量包已保存为 http_base64_example.pcapng")

  4. 使用命令打开加载器

    1
    wireshark -X lua_script:http_base64_dissector.lua

效果展示

没有使用加载器

image-20250430003015890

使用加载器之后就出现了新的一行单独展现数据包的内容

image-20250430002923657

实际上http由多个tcp包完成

启动命令

1
wireshark -X lua_script:http_base64_split_dissector.lua http_base64_split_example.pcapng

效果展示

对于多个组成的http POST包直接增加一行进行翻译

image-20250430012227180

使用加载器前

image-20250430012337309

生成过程

由于 Scapy 构造的两个“孤立包”看起来像是 TCP,但 Wireshark 不能对它们做 TCP 重组,所以 HTTP dissector 不会解析出完整 body,自然也不会 Base64 解码逻辑。

也就是说这原来的连个tcp包,他们

  • 没有 三次握手(SYN/SYN-ACK/ACK)
  • 没有 TCP 连接状态管理
  • 没有 ACK 回包
  • Wireshark不会认为这两个包属于同一个 TCP 流,无法做重组!

所以需要使用抓包监听来获取真实的流量数据而不仅仅是模拟

  1. 使用flask起一个服务,不可以使用python -m http.server 8080因为只可以接受GET请求,但是问题是现在要的就是POST请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from flask import Flask, request

    app = Flask(__name__)

    @app.route('/upload', methods=['POST'])
    def upload():
    data = request.get_json()
    print("收到数据:", data)
    return "OK"

    if __name__ == '__main__':
    app.run(port=8080)
  2. 使用curl或者requests进行发包,这里选择的是requests

    1
    2
    3
    4
    5
    6
    7
    import requests
    import base64

    data = {
    "file": base64.b64encode(b"Hello World!").decode()
    }
    requests.post("http://127.0.0.1:8080/upload", json=data)
  3. 使用wireshark监听回环地址

    image-20250430012923127

  4. 直接保存为http_base64_split_example.pcapng

构建解析器

这种 dissector 不会取代 HTTP 协议本身,但能在 GUI 中添加额外显示内容

如果加入以下命令可以看到lua console弹出的消息

1
2
function b64_post.dissector(buffer, pinfo, tree)
print(">>> Base64 dissector called on frame:", pinfo.number)

image-20250430013851953

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-- 定义一个新的后解析器(post-dissector)协议,名称为 "b64post",描述为 "Base64 Post Dissector"
local b64_post = Proto("b64post", "Base64 Post Dissector")

-- 定义一个字段,用于展示解码后的 Base64 内容,字段名为 "b64post.decoded",显示名为 "Decoded Base64 Content"
local f_decoded = ProtoField.string("b64post.decoded", "Decoded Base64 Content")

-- 将定义好的字段添加到协议中
b64_post.fields = { f_decoded }

-- 从 JSON 协议解析结果中提取名为 "value.string" 的字段(Wireshark 的 json dissector 生成的字段)
local json_value = Field.new("json.value.string")

-- 定义一个函数:手动实现 Base64 解码(不依赖外部库)
local function decode_b64(data)
-- Base64 编码表
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- 去掉所有非法字符(非 base64 字符)
data = data:gsub('[^'..b..'=]', '')
-- 将每个 base64 字符转换为对应的6位二进制,并拼接所有位
return (data:gsub('.', function(x)
if (x == '=') then return '' end -- padding 不转换
local r,f='',(b:find(x)-1) -- 找到字符索引(从0开始)
for i=6,1,-1 do
r=r..(f%2^i - f%2^(i-1) > 0 and '1' or '0')
end
return r
-- 每8位一组还原为原始字符
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end -- 位数不足8位,忽略
local c=0
for i=1,8 do
c=c + (x:sub(i,i)=='1' and 2^(8-i) or 0)
end
return string.char(c) -- 转换为字符
end))
end

-- 定义解析器的主函数,每次数据包载入后调用
function b64_post.dissector(buffer, pinfo, tree)
-- 获取 JSON 协议中的 value.string 字段
local value_field = json_value()
if not value_field then return end -- 若无该字段,则跳过处理

-- 转换字段为字符串(base64 字符串)
local base64 = tostring(value_field)

-- 可选:判断字符串是否为合法的 base64 字符串(避免误解析)
if not base64:match("^[A-Za-z0-9+/=]+$") then return end

-- 调用 decode_b64 解码
local decoded = decode_b64(base64)
if decoded then
-- 创建一个子树节点,显示解析内容
local subtree = tree:add(b64_post, "Base64 JSON Analysis")
subtree:add(f_decoded, decoded) -- 添加解码后的字段到子树中
end
end

-- 注册该协议为 Wireshark 的后解析器(即所有协议分析完后,再调用这个)
register_postdissector(b64_post)

这是专门针对json做的优化,由于base64数据是包含在json中发送的,所以需要去解析这个json获取base64编码的密文来进行解码

1
local json_value = Field.new("json.value.string")

增强版解析器

为了支持多层嵌套和多个字段的 Base64 解码,可以将原脚本扩展为以下功能:

  • 遍历所有 json.value.string 字段;
  • 对每个字段尝试 base64 解码;
  • 显示每一个成功解码的内容;
  • 同样使用当前的 base64 解码函数。

下面是改进后的完整脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
-- 定义协议和字段
local b64_post = Proto("b64post", "Base64 Post Dissector")
local f_decoded = ProtoField.string("b64post.decoded", "Decoded Base64 Content")
b64_post.fields = { f_decoded }

-- 引用所有 json.value.string 字段(可能有多个)
local json_values = { Field.new("json.value.string") }

-- Base64 解码函数
local function decode_b64(data)
local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data = data:gsub('[^'..b..'=]', '') -- 过滤非法字符
return (data:gsub('.', function(x)
if x == '=' then return '' end
local r, f = '', (b:find(x) - 1)
for i = 6, 1, -1 do
r = r .. (f % 2^i - f % 2^(i-1) > 0 and '1' or '0')
end
return r
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if #x ~= 8 then return '' end
local c = 0
for i = 1, 8 do
c = c + (x:sub(i, i) == '1' and 2^(8 - i) or 0)
end
return string.char(c)
end))
end

-- 主解析函数
function b64_post.dissector(buffer, pinfo, tree)
local has_output = false
local subtree = nil

-- 遍历所有匹配字段
for _, getter in ipairs(json_values) do
local field = getter()
if field then
local base64 = tostring(field)

-- 检查是否可能是 base64 编码
if base64:match("^[A-Za-z0-9+/=]+$") then
local decoded = decode_b64(base64)
if decoded and decoded:match("%C") then -- %C: 非空白字符
if not subtree then
subtree = tree:add(b64_post, "Base64 JSON Analysis")
end
subtree:add(f_decoded, decoded)
has_output = true
end
end
end
end
end

-- 注册为后解析器
register_postdissector(b64_post)

加载器无法运用在http跟踪流的窗口!

Wireshark 中的“跟踪流(Follow Stream)”功能是原始 TCP 字节流的重建视图,它展示的是通信双方“看到”的原始数据,不经过 dissector 插件处理(也就是说,不调用 dissector() 函数)。这是设计上的决策,目的是让用户看到协议最原始的内容(用于取证、协议逆向等)

wireshark各类端口过滤命令

1
2
3
4
tcp.dstport == 1234 过滤目标端口
tcp.srcport == 1234 过滤源端口
port == 1234 过滤端口
dst port == 1234 同上

数据包标号:Frame Number

其他文章
cover
python_异步编程笔记
  • 25/05/02
  • 00:00
  • 代码审计
cover
GPG密钥过期
  • 25/04/23
  • 19:44
  • 协议学习
目录导航 置顶
  1. 1. wireshark解析器
    1. 1.1. 问题树
    2. 1.2. wireshark 加载规则
      1. 1.2.1. 对于http协议来说
      2. 1.2.2. 对于非标准通信协议来说
      3. 1.2.3. 似乎目前尚且没有能导出加载器加载后的内容的方法
      4. 1.2.4. AI总结
    3. 1.3. 蓝牙协议学习
    4. 1.4. wireshark使用加载器
      1. 1.4.1. 加载器启动分类
        1. 1.4.1.1. 命令启动dissector
        2. 1.4.1.2. 配置默认启动项, 允许wireshark启动的时候自动加载自定义加载器
      2. 1.4.2. 本题中使用了ev3格式的加载器
    5. 1.5. 自定义加载器可以解决很多问题 dissector
      1. 1.5.1. 先从简单出发,假设http包只有一个tcp包就完成
        1. 1.5.1.1. 生成过程
          1. 1.5.1.1.1. 以下是拦截监听时候对base64加密http包的处理
        2. 1.5.1.2. 效果展示
      2. 1.5.2. 实际上http由多个tcp包完成
        1. 1.5.2.1. 启动命令
        2. 1.5.2.2. 效果展示
        3. 1.5.2.3. 生成过程
        4. 1.5.2.4. 构建解析器
        5. 1.5.2.5. 增强版解析器
    6. 1.6. 加载器无法运用在http跟踪流的窗口!
    7. 1.7. wireshark各类端口过滤命令
请输入关键词进行搜索