Wireshark解析自定义加密协议

一、概述

网上撰文写wireshark使用lua脚本解析协议的文章比较多。

笔者最近也因工作需要使用wireshark解析协议。但因网络安全,协议的数据部分被加密了。无法简单的使用lua脚本进行解析。

考虑到加密算法和压缩算法的复杂性,采用调用lua C库的方法,完成解密(解压)。

下面与大家分享下大致思路。

二、目标及思路

协议的大致格式如下:

协议字段 命名
协议版本(1字节) protoVersion
协议命令类型(2字节) protoCmdType
协议加密类型(1字节) protoEncrytionType
协议压缩类型(1字节) protoCompressionType
协议数据长度(4字节) protoDataLength
协议数据(protoDataLength字节) protoData

:本文仅专注于解码,为简化复杂性,暂时不考虑一个UDP/TCP包出现多个自定义数据包的情况。

Lua脚本的伪代码:

local p_multi = Proto("multi","MultiProto");

local f_protoVersion = ProtoField.uint8("multi.protoVersion","Version",base.DEC)
local f_protoCmdType = ProtoField.uint16("multi.protoCmdType","CmdType",base.DEC,{
        [1] = "CmdType_1",
        [2] = "CmdType_2",
        [3] = "CmdType_3",
        })
local f_protoEncrytionType = ProtoField.uint8(
        "multi.protoEncrytionType","EncrytionType",base.DEC,{
        [1] = "EncrytionType_1",
        [2] = "EncrytionType_2",
        [3] = "EncrytionType_3",
        })
local f_protoCompressionType = ProtoField.uint8(
        "multi.protoCompressionType","CompressionType",base.DEC,{
        [1] = "CompressionType_1",
        [2] = "CompressionType_2",
        [3] = "CompressionType_3",
        })
local f_protoDataLength = ProtoField.uint32("multi.protoDataLength","DataLength",base.DEC)

p_multi.fields = { f_protoVersion, f_protoCmdType, f_protoEncrytionType,
        f_protoCompressionType, f_protoDataLength }

function DecodeBufferFunction(protoEncrytionType, protoCompressionType, buf, pos, buf_len)
    local decodeBuf = buf
    -- TODO: This artical job
    return decodeBuf
end

function p_multi.dissector(buf,pkt,root)
    local pos = 0
    local buf_len = buf:len()
    local t = root:add(p_multi,buf(0,buf_len))

    -- 协议版本(1字节)
    t:add(f_protoVersion, buf(pos, 1))
    pos = pos + 1

    -- 协议命令类型(2字节)
    t:add(f_protoCmdType, buf(pos, 2))
    pos = pos + 2

    -- 协议加密类型(1字节)
    t:add(f_protoEncrytionType, buf(pos, 1))
    local protoEncrytionType = buf(pos, 1):uint()
    pos = pos + 1

    -- 协议压缩类型(1字节)
    t:add(f_protoCompressionType, buf(pos, 1))
    local protoCompressionType = buf(pos, 1):uint()
    pos = pos + 1

    -- 协议数据长度(4字节)
    t:add(f_protoDataLength, buf(pos, 1))
    local protoDataLength = buf(pos, 4):uint()
    pos = pos + 4

    -- 协议数据(protoDataLength字节)
    buf = DecodeBuffer(protoEncrytionType, protoCompressionType, buf, pos, buf_len)
    new_buf_len = buf:len()
    -- TODO: add your code
end

本文的主要任务是DecodeBufferFunction的实现。

目前的任务是把原始数据(buf)

变成(decodeBuf)

上图实际上就是原始buf和decodeBuf。这里buf、decodeBuf均为Tvb ("Testy Virtual Buffer"). 另外,为操持两个buf在偏移上的一致,decodeBuf的头部也添加了“协议头”

因而DecodeBufferFunction的任务有3个:

1. 由buf转换成lua c库的数据结构

2. Lua c库完成解码,并返回数据

3. 再由lua c库的数据结构转换成decodeBuf.

当然,如果我们知道Tvb的数据结构格式,可以省去第1、3步。

三、Lua c库的编写

Lua c库的开发可以参考相关资料。本文的开发需要用到LuaBinaries. 可以在:http://luabinaries.sourceforge.net/ 下载。

详细路径:http://sourceforge.net/projects/luabinaries/files/5.1.5/Windows%20Libraries/。根据平台和lua版本,可以自选。

本文使用了lua-5.1.5_Win32_dll10_lib.zip

Lua c库的结构定义如下:

typedef struct MyByteArray {
    int size;
    unsigned char values[1];  /* variable part */
} MyByteArray;

wireshark中的Tvb、ByteArray都是以0为起始索引的方式。所以下面MyByteArray也以0为索引。这一点和lua语言提倡的方式不一致。

本文采用vs2010生成一个win32 dll的库,工程名称:decode. 工程向导设置如下:

删除decode.h和decode.cpp中的,示例代码。并在decode.h中,把导出定义改成:

#ifdef DECODE_EXPORTS
#define DECODE_API extern "C" __declspec(dllexport)
#else
#define DECODE_API extern "C" __declspec(dllimport)
#endif

并引入lua-5.1.5_Win32_dll10_lib的库到本工程。

本文使用了《Program in Lua》第28章的部分示例代码。关键源代码如下:

Stdafx.h

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>

// TODO: reference additional headers your program requires here
#include "include/lua.hpp"
#pragma comment(lib, "lib/lua51")


decode.h

#ifdef DECODE_EXPORTS
#define DECODE_API extern "C" __declspec(dllexport)
#else
#define DECODE_API extern "C" __declspec(dllimport)
#endif

typedef struct MyByteArray {
    int size;
    unsigned char values[1];  /* variable part */
} MyByteArray; 

enum EncryptionType
{
    EncryptionType_begin,
    EncryptionType_1 = EncryptionType_begin,
    EncryptionType_2,
    EncryptionType_3,
    // new ...
    EncryptionType_end = EncryptionType_3,
};
enum CompressionType
{
    CompressionType_begin,
    CompressionType_1 = CompressionType_begin,
    CompressionType_2,
    CompressionType_3,
    // new ...
    CompressionType_end = CompressionType_3,
};

DECODE_API int luaopen_decode (lua_State *L);

static int newarray (lua_State *L);
static int setarray (lua_State *L);
static int getarray (lua_State *L);
static int getsize (lua_State *L);
static int decodearray (lua_State *L);

static MyByteArray* decryptarray(unsigned char encryptionType, MyByteArray* myarray);
static MyByteArray* decompressarray(unsigned char encryptionType, MyByteArray* myarray);


decode.cpp

#include "stdafx.h"
#include "decode.h"

static const struct luaL_reg decodeLib [] = {
    {"new", newarray},
    {"set", setarray},
    {"get", getarray},
    {"size", getsize},
    {"decode", decodearray},
    {NULL, NULL}
}; 

int luaopen_decode (lua_State *L) {
    luaL_openlib(L, "decode", decodeLib, 0);
    return 1;
} 

static int newarray (lua_State *L) {
    int n = luaL_checkint(L, 1);
    size_t nbytes =  sizeof(MyByteArray) + (n - 1)*sizeof(unsigned char);
    MyByteArray *a = (MyByteArray *)lua_newuserdata(L, nbytes);
    a->size = n;
    return 1;  /* new userdatum is already on the stack */
}

static int setarray (lua_State *L) {
    MyByteArray *a = (MyByteArray *)lua_touserdata(L, 1);
    int index = luaL_checkint(L, 2);
    unsigned char value = (unsigned char)luaL_checknumber(L, 3); 

    luaL_argcheck(L, a != NULL, 1, "`array‘ expected"); 

    luaL_argcheck(L, 0 <= index && index < a->size, 2,
        "index out of range"); 

    a->values[index] = value;
    return 0;
} 

static int getarray (lua_State *L) {
    MyByteArray *a = (MyByteArray *)lua_touserdata(L, 1);
    int index = luaL_checkint(L, 2); 

    luaL_argcheck(L, a != NULL, 1, "‘array‘ expected"); 

    luaL_argcheck(L, 0 <= index && index < a->size, 2,
        "index out of range"); 

    lua_pushnumber(L, a->values[index]);
    return 1;
}

static int getsize (lua_State *L) {
    MyByteArray *a = (MyByteArray *)lua_touserdata(L, 1);
    luaL_argcheck(L, a != NULL, 1, "`array‘ expected");
    lua_pushnumber(L, a->size);
    return 1;
} 

static int decodearray (lua_State *L) {
    unsigned char encryptionType = (unsigned char)lua_touserdata(L, 1);
    unsigned char compressionType = (unsigned char)lua_touserdata(L, 2);
    MyByteArray *a = (MyByteArray *)lua_touserdata(L, 3); 

    luaL_argcheck(L, EncryptionType_begin<=encryptionType && encryptionType<=EncryptionType_end,
        2, "undefine encryption type");
    luaL_argcheck(L, CompressionType_begin<=compressionType && compressionType<=CompressionType_end,
        2, "undefine compression type");
    luaL_argcheck(L, a != NULL, 3, "`array‘ expected"); 

    MyByteArray* a1 = NULL;
    MyByteArray* a2 = NULL;
    if (NULL != a) {
        a1 = decryptarray(encryptionType, a);
        if (NULL != a1) {
            a2 = decryptarray(compressionType, a1);
        }
    }
    if (NULL != a2) {
        lua_pushlightuserdata(L,a2);
    }
    else {
        lua_pushnil(L);
    }

    return 1;
} 

//////////////////////////////////////////////////////////////////////////
static MyByteArray* decryptarray(unsigned char encryptionType, MyByteArray* myarray) {
    MyByteArray* retarray = NULL;
    switch (encryptionType) {
    case EncryptionType_1:
        // TODO:
        break;
    case EncryptionType_2:
        // TODO:
        break;
    case EncryptionType_3:
        // TODO:
        break;
    default:
        break;
    }
    return retarray;
}
static MyByteArray* decompressarray(unsigned char encryptionType, MyByteArray* myarray) {
    MyByteArray* retarray = NULL;
    switch (encryptionType) {
    case CompressionType_1:
        // TODO:
        break;
    case CompressionType_2:
        // TODO:
        break;
    case CompressionType_3:
        // TODO:
        break;
    default:
        break;
    }
    return retarray;
}

四、调用lua c 库解码

Lua解析脚本的开头需要增加部分代码,以加载新开发的decode.dll库。

Path = "D:\\Documents\\Visual Studio 2010\\Projects\\decode\\Debug\\decode.dll"
decodelib = package.loadlib(path, "luaopen_decode")
if nil ~= decodelib then
    decodelib()
end

DecodeBufferFunction的实现如下:

function DecodeBufferFunction(protoEncrytionType, protoCompressionType, buf, pos, buf_len)
    if nil ~= decode then
        -- construct MyByteArray
        local rawLen = buf_len - pos
        array = decode.new(rawLen)
        for i=0,rawLen-1,1 do
            decode.set(array,i, buf(pos,1):uint())
        end
        -- decode
        array = decode.decode(protoEncrytionType, protoCompressionType, array)
        if nil ~= array then
            -- construct decodeBuf tvb
            local decodeLen = decode.size(array)
            -- construct tvb throught wireshark ByteArray
            local bArray = ByteArray.new(decodeLen + pos)
            for i=0,pos-1,1 do
                bArray.set_index(i, buf(i,1):uint())
            end
            for i=0,decodeLen-1,1 do
                bArray.set_index(pos+i, decode.get(array,i))
            end

            -- reture decode buffer
            return bArray.tvb("decodeBuf")
        end
    end
    return nil
end

五、总结

通过上面的介绍,提供了wireshark中解析自定义加密协议的思路和大致实现。

需要注意的是:

1. LuaBinarires的版本问题。Wireshark采用了lua5.1和lua5.2,因此需要使用对应的LuaBinarires版本进行开发;

2. 开发lua c库,需要使用LuaBinarires的动态库版本。尤其是lua5.2,如果使用静态库进行开发,wireshark在加载decode.dll时会报错:multi lua VMs detected。

时间: 2024-10-06 16:38:45

Wireshark解析自定义加密协议的相关文章

wireshark插件开发 - 自定义协议

虽然wireshark自带了很多知名协议的解析插件,譬如HTTP.DHCP等等,然而在实际应用环境中,有不少软件之间的通信协议都是私有的,如游戏客户端和服务器之间的交互协议通常都是私有的,wireshark无法具体解析出各种字段之间的含义,只能显示接收到的二进制数据,给协议的分析和问题的排查带来了一定的困难,尤其是协议内容比较复杂时. 本文一个自定义的简单协议入手,分析如何基于wireshark开发自定义协议分析插件. 1.1. 概述 本书使用Go语言来描述协议的交互过程.Go由Google出品

用wireshark解析应用层存储包

工作中经常需要统计服务器上的rtp包接收.发送性能.不想自己再做一套统计软件,打算用现有的wireshark来做分析统计. 先把rtp头存成pcap格式文件,pcap文件格式及怎样存储可以参照这篇文章http://blog.csdn.net/force_eagle/article/details/6681802 然后在wireshark中打开pcap文件,但是问题来了,在wireshark中默认只支持完整的ip报文.这样需要在每个rtp包前加上ip头,可是ip头的大小比rtp头都还要大,这样做会

wireshark解析报文一点根据

wireshark解析报文一点根据 问题:遇到一个问题,net-snmp发出trap的时候,通过wireshark抓包发现只能发送知名端口162的告警报文.处理过程如下: 检查代码:经过代码排查发现net-snmp没有对端口做任何限制. 抓包:经过抓包,发现都发出了UDP报文.对报文进行过滤,过滤条件为 "snmp".发现只有端口为162时,才有报文报文标记为snmp报文,端口为非162时,过滤后,没有snmp报文. 通过对udp报文进行重新解析,发现其中udp报文中的data部分为s

ViewDragHelper完全解析, 自定义ViewGroup神器

Android ViewDragHelper完全解析, 自定义ViewGroup神器 ViewDragHelper实战, 自己打造的Drawerlayout

Spring源码解析——自定义标签解析

再讲解析自定义标签之前,先要知道怎么实现自定义标签的,接下来就来看下吧. 1.第一步,定义一个实体类,如图1: 图1 2.第二步,定义一个xsd(xsd是什么不知道的自行百度),如图2: 图2 3.第三步,生成spring.handlers和spring.schemas文件,一定要放在resources/META-INF下面,因为解析的时候只会到这个目录下面去找,先看下spring.handlers,如图3: 图3 在看下spring.schemas,如图4: 图4 4.第四步,实现Abstra

SuperSocket 1.6.4 通过FixedHeaderReceiveFilter解析自定义协议

SuperSocket 提供了一些通用的协议解析工具, 你可以用他们简单而且快速的实现你自己的通信协议: TerminatorReceiveFilter (SuperSocket.SocketBase.Protocol.TerminatorReceiveFilter, SuperSocket.SocketBase) CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter, SuperSo

【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)

在传输消息时,用Java内置的方法和工具确实很用,如:对象序列化,RMI远程调用等.但有时候,针对要传输的特定类型的数据,实现自己的方法可能更简单.容易或有效.下面给出一个实现了自定义构建和解析协议消息的Demo(书上例子). 该例子是一个简单的投票协议.这里,一个客户端向服务器发送一个请求消息,消息中包含了一个候选人的ID,范围在0~1000.程序支持两种请求:一种是查询请求,即向服务器询问候选人当前获得的投票总数,服务器发回一个响应消息,包含了原来的候选人ID和该候选人当前获得的选票总数:另

[写代码]解析自定义数据库文件的思路

从文件中getilne一整条数据,里面有各种信息,首先要分析数据的构成,然后看清楚数据的分隔符.再找找小规律就可以写出来了.这里举个小例子: 比如你要解析这样的数据: 1 0 2015 12 25 20 00 ^圣诞节^ 3 2 0 2014 06 08 22 11 ^I dont know.^ 3 可以这么写: 1 #include <iostream> 2 #include <fstream> 3 #include <cstdlib> 4 #include <

wireshark解析三次握手和四次挥手

一. TCP/IP协议族 TCP/IP是一个协议族,通常分不同层次进行开发,每个层次负责不同的通信功能.包含以下四个层次: 1. 链路层,也称作数据链路层或者网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节. 2. 网络层,也称作互联网层,处理分组在网络中的活动,例如分组的选路.网络层协议包括IP协议(网际协议).ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议). 3.