一、概述
网上撰文写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。