Как стать автором
Обновить

Пишем плагин-диссектор для Wireshark – теперь на Lua

Статья написана под впечатлением от ранее опубликованной Пишем плагин-диссектор для Wireshark, в виде её продолжения.

В качестве альтернативы написанию плагинов на Си, Wireshark предлагает API для скриптовых языков – Lua и Python. К сожалению, оказалось, что в Windows-сборке Python до сих пор не поддерживается. Зато есть Lua.

Преимущества скриптового подхода:
  • Никакого шаманства с Makefile, сборками и пересборками плагина
  • Низкий порог входа – код проще для новичков и/или не знакомых с Си
  • Более компактная программа (что на данном примере практически незаметно)
Недостатки:
  • Скорость разбора протокола ниже, по сравнению с диссектором на Си
  • Плагины на Lua нельзя сделать частью дистрибутива Wireshark
Соответственно, алгоритм:
  1. Для быстрого прототипирования и отладки диссектор пишется на Lua/Python
  2. Далее, если необходимо, готовый уже прототип переписывается на Си
  3. Profit!


Напишем аналогичный плагин-диссектор для разбора протокола FOO, используя Lua. Сам протокол описан в RFC оригинальной статье, и здесь для краткости не рассматривается.

Настройка окружения


Первым делом запускаем Wireshark, и в окошке About проверяем, что сборка включает Lua:


Далее, находим файл init.lua в папке Wireshark. Убеждаемся, что расширение Lua включено:
disable_lua = false

Добавляем в конец файла путь к нашему плагину:
FOOPROTO_SCRIPT_PATH = "C:\\!my\\habr\\"
dofile(FOOPROTO_SCRIPT_PATH .. "foo_dissector.lua")

На этом подготовительная работа завершена, все просто.

Код диссектора


Я постарался сохранить авторские имена переменных и сделать вывод в Wireshark максимально похожим. Открываем foo_dissector.lua в текстовом редакторе и пишем:


p_foo = Proto("foo", "FOO Protocol")    -- Определение нового протокола

-- Текстовые отображения некоторых типов данных
packettypes = { "Ping request", "Ping acknowledgment", "Print payload" }
packetbool  = { [0] = "False",  "True" }

-- Заголовки протокола
local f_hdr_version = ProtoField.uint8("foo.hdr.version", 
                      "FOO Header Version", base.DEC, {"Version 1"})

local f_hdr_type    = ProtoField.uint8("foo.hdr.type", 
                      "FOO Header Type",    base.DEC, packettypes)

local f_hdr_flags   = ProtoField.uint8("foo.hdr.flags", "FOO Header Flags", base.HEX)

-- Битовые флаги
local f_hdr_flags_first   = ProtoField.uint8("foo.hdr.flags.first",   
                            "FOO first flag",   base.DEC, packetbool, 0x01)
local f_hdr_flags_second  = ProtoField.uint8("foo.hdr.flags.second",  
                            "FOO second flag",  base.DEC, packetbool, 0x02)
local f_hdr_flags_onemore = ProtoField.uint8("foo.hdr.flags.onemore", 
                            "FOO onemore flag", base.DEC, packetbool, 0x04)

-- Остаток заголовка и данные
local f_hdr_bool = ProtoField.bool  ("foo.hdr.bool", "FOO Header Boolean")
local f_pl_len   = ProtoField.uint32("foo.pl_len",   "FOO Payload Length")
local f_payload  = ProtoField.string("foo.payload",  "FOO Payload", base.STRING)

-- Регистрируем все поля протокола
p_foo.fields = { f_hdr_version,     f_hdr_type,         f_hdr_flags,
                 f_hdr_flags_first, f_hdr_flags_second, f_hdr_flags_onemore,
                 f_hdr_bool,        f_pl_len,           f_payload }

-- Собственно функция диссектора для протокола FOO
function p_foo.dissector(buf, pinfo, tree)
    if buf:len() == 0 then return end
    pinfo.cols.protocol = p_foo.name       -- в колонке Protocol будет название протокола 

    subtree = tree:add(p_foo, buf(0))      -- создаем поддерево

    subtree:add(f_hdr_version, buf(0,1))   -- начинаем добавлять поля
    local ver = buf(0,1):uint()

    if ver == 1 then
       local type_str = packettypes[buf(1,1):uint()]
       if type_str == nil then type_str = "Unknown" end
       pinfo.cols.info = "Type: " .. type_str   -- в колонке Info будет отображаться тип пакета

       subtree:add(f_hdr_type,  buf(1,1))

       subtree:add(f_hdr_flags,         buf(2,1))
       subtree:add(f_hdr_flags_first,   buf(2,1))
       subtree:add(f_hdr_flags_second,  buf(2,1))
       subtree:add(f_hdr_flags_onemore, buf(2,1))

       subtree:add(f_hdr_bool,  buf(3,1))
       subtree:add_le(f_pl_len, buf(4,4))     -- поле длины в Little Endian

       local pl_len = buf(4,4):le_uint()
       subtree:add(f_payload, buf(8,pl_len))  -- данные
    else
       subtree:append_text(string.format(", Unknown version of Foo protocol (0x%02x)", ver))
    end
end

-- регистрируем диссектор на UDP порт 35000
local udp_dissector_table = DissectorTable.get("udp.port")
udp_dissector_table:add(35000, p_foo)

Готово, можно запускать Wireshark.

Результат




Кстати, для отладки удобнее и быстрее использовать текстовую версию «великого рассекателя пакетов» — TShark.

Список источников


Lua Support in Wireshark
www.wireshark.org/docs/wsug_html_chunked/wsluarm.html

Devendra @ Work — Create a Wireshark dissector in Lua
delog.wordpress.com/2010/09/27/create-a-wireshark-dissector-in-lua

Stig Bjørlykke — Lua Scripting in Wireshark
ссылка на PDF: DT06_Bjorlykke_Lua Scripting in Wireshark.pdf
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.