Wireshark 解析 自定义 加密 协议

一、概述

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

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

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

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

二、目标及思路

协议的大致格式如下:

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

 

wpsF480.tmp:本文仅专注于解码,为简化复杂性,暂时不考虑一个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)

image 

变成(decodeBuf)

image

上图实际上就是原始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;

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

 

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

wpsF492.tmp

删除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。

posted @ 2015-03-20 10:41  _terry  阅读(1200)  评论(0编辑  收藏  举报