- Define a type before using it
- For type name and field name :
--- Use only alphanumerics and _
--- Do not start with number
--- Do not use keywords (int8, string, void, any, this, struct, enum, if, while ...)
--- Do not use global or pinfo
- spare byte not displayed and not usable - char, schar, uchar - bool1, bool8, bool16, bool32 take care of byte order - int2 to int32, int40, int48, int64 take care of byte order - uint1 to uint32, uint40, uint48 take care of byte order - float32, float64 take care of byte order - string, string(<nb_bytes>) manage zero as end of string - string_nl, string_nl(<nb_bytes>) idem string + manage "\n" or "\r\n" as end of string - raw(<nb_bytes>) bytes displayed as hexa dump and not usable by wsgd must start on an entire byte position nb_bytes could be * in some cases - padding_bits permits to move to the next entire byte position
<int_or_float_type_name>{ns=<No Statement value>} <field_name> ; If the read value is equal to the specified value : - No_Statement will be displayed. - Transform, Display and Constrains specifications are ignored NB: must be specified before the Transform, Display and Constrains specifications.
<int_or_float_type_name>{q=<quantum>:o=<offset>} <field_name> ; <int_or_float_type_name>{q=<quantum>} <field_name> ; <int_or_float_type_name>{o=<offset>} <field_name> ; The resulting value = read_value * quantum + offset. Quantum and offset values could be specified with an expression (without variable), e.g : 3.1415927/180.0 <type_name>{tei=integer expression (use "this")} <field_name> ; <type_name>{tef=float expression (use "this")} <field_name> ; The specified expression must return the appropriate type. E.g: uint16{tei=2*this-47} <field_name> ; # since this is an integer, the expression gives an integer uint16{tef=2*this-47.0} <field_name> ; # because of 47.0, the expression gives a float float32{tef=2*this-47} <field_name> ; # since this is a float, the expression gives a float <type_name>{tei=a_previous_field > 0 ? this/another_field : 2*this-47} <field_name> ; <type_name>{tef=a_function_which_returns_a_float(this)} <field_name> ;
<int_type_name>{d=hex} <field_name> ; <int_type_name>{d=oct} <field_name> ; <int_type_name>{d=bin} <field_name> ; <any_type_name>{d=printf format %22.32s} <field_name> ; <any_type_name>{de=string expression (use "this")} <field_name> ; NB: display specifications must always appears AFTER transform specifications (if any)
An error is displayed if the value does NOT match.
For int or float only.
<int_or_float_type_name>{min=<val_min>:max=<val_max>} <field_name> ; <int_or_float_type_name>{min=<val_min>} <field_name> ; <int_or_float_type_name>{max=<val_max>} <field_name> ; NB: constraints specifications must always appears AFTER transform and display specifications (if any) Min and max values could be specified with an expression (without variable), e.g : -(3.0/4)*3.1415927 Could use many constaints (specified in the good order), e.g: uint6{min=2:max=9}{min=12:max=19}
The global byte order is specified with byte_order command.
This byte order specification apply only to the specified field.
<type_name>{byte_order=big_endian} <field_name> ; <type_name>{byte_order=little_endian} <field_name> ;
<type_name>[12] <array_field_name> ; <type_name>[<field_name>] <array_field_name> ; <type_name>[<field_name> - 12] <array_field_name> ; <type_name>[<field_name> < 36 ? 0 : 16] <array_field_name> ; Only at the end of the message AND if Generic Dissector knows the size of the message (look at this) : <type_name>[*] <array_field_name> ; * means any number of element <type_name>[+] <array_field_name> ; + means any number of element, at least 1 If the array is not at the end of the message, look at loop_size.
alias <new_type_name> <already_existent_type_name> ; alias <new_type_name> <already_existent_type_name>{q=<quantum>}{min=<val_min>:max=<val_max>} ; alias <new_type_name> <already_existent_type_name>{d=hex}{min=<val_min>:max=<val_max>} ;
Take care of byte order
enum<nb_bits 1 to 32> <enum_type_name> { <symbolic_value_name> <integer_value or - > # - means last value + 1 (zero if first value) ... } # To define an identic enum with a different size. enum<nb_bits 1 to 32> <enum_type_name> as <already_existent_enum_type_name> ; # To define a new enum starting from an existent one. enum<nb_bits 1 to 32> <enum_type_name> expand <already_existent_enum_type_name> { <other_symbolic_value_name> <integer_value> ... } # Could use <enum_type_name>::<symbolic_value_name> in any expression/condition.
bitfield must be understood like a C bitfield (even there is no standard about its implementation).
Take care of byte order.
Fields could be unsigned integers, bool1 or enum (without signed values).
bitfield<nb_bits 8 16 24 or 32> <bitfield_type_name> { uint3{d=bin} field1 ; # lower level bits hide uint2 field2_unused ; uint15{q=2.34:o=-117.35}{min=-105.17} field3 ; <enum_type> field4 ; bool1 field5 ; ... # higher level bits }
Could also use var and set inside Bit field.
More info
struct <struct_type_name> { <type_name> <field_name> ; <command_name> <command_parameter> ; if ((<field_name> + 20 < 572) && (...) || (...)) { <anything that could be specified in a struct> } else { <anything that could be specified in a struct> } while ((<field_name> % 20 < 2**3) && (...) || (...)) { <anything that could be specified in a struct> continue ; break ; } do { <anything that could be specified in a struct> continue ; break ; } while ((to_string(<field_name>) + "20" != print("%d", 572)) && (...) || (...)) ; # repeat until the given size have been effectively read # Use loop_size_bits if bit size is needed loop_size_bytes <field_name>+20 { <anything that could be specified in a struct> continue ; break ; } # Inline struct and bitfield struct { <anything that could be specified in a struct> } <field_name> ; bitfield<nb_bits 8 16 24 or 32> { <anything that could be specified in a bitfield> } <field_name> ; ... } Optionaly, you can put a print specification : struct <struct_type_name> print (<printf format and arguments>) { ... } The printf arguments could be fields specified inside the struct.
switch <switch_type_name> <optional and deprecated switched_type_name> { case <integer, enum or string value 1> : <anything that could be specified in a struct> ... case <integer, enum or string value n> : <anything that could be specified in a struct> default : <anything that could be specified in a struct> } # Example with enum : enum16 T_Operation { CREATION 0 MODIFICATION 1 DELETION 2 } switch T_Operation_switch # T_Operation { case T_Operation::MODIFICATION : print ("MODIFICATION value"); case T_Operation::DELETION : print ("DELETION value"); case T_Operation::CREATION : print ("CREATION value"); default : print ("default value"); } struct xxx { ... T_Operation operation; ... T_Operation_switch(operation) oper_switch; # or T_Operation_switch(operation) ""; # or directly inline without previous definition/declaration switch(operation) { case T_Operation::MODIFICATION : print ("MODIFICATION value"); case T_Operation::DELETION : print ("DELETION value"); case T_Operation::CREATION : print ("CREATION value"); default : print ("default value"); } } # Example with string : switch T_string_switch { case "" : print ("empty value"); case "str1" : print ("str1 value"); case "WEFZ AS" : print ("WEFZ AS value"); default : print ("default case"); } struct xxx { ... T_string_switch("a string value") string_switch; T_string_switch(a_string_field) string_switch; T_string_switch(a_string_variable) string_switch; ... }
switch_expr <switch_type_name> { case (<any expression>) : <anything that could be specified in a struct> ... case (<any expression>) : <anything that could be specified in a struct> default : <anything that could be specified in a struct> } # Example : switch_expr T_switch_expr { case (a_field > 10 && a_variable != 0) : print ("1st case"); case (a_field <= 10 && a_variable != 0) : print ("2nd case"); case (a_variable == 0) : print ("3rd case"); case (a_string_variable == "hello") : print ("hello case"); default : print ("default case"); } struct xxx { ... T_switch_expr my_switch; # or directly inline without previous definition/declaration switch_expr { ... } }
- enum<nb_bits 1 to 32> <enum_type_name>; - bitfield<nb_bits 8 16 24 or 32> <bitfield_type_name>; - struct <struct_type_name>; - switch <switch_type_name>;
- include <file_name> ; include the specified description file Not available inside a struct, switch ... - byte_order (little_endian | big_endian | as_host) ; specify the data byte order for int (also enum and bitfield) and float - big endian (also known as network, motorola) : the bytes are not inverted - little endian (also known as intel) : the bytes are inverted
var <type> <variable name> = <value or expression> ; permits to declare, initialize and display a variable. variable = field except it is initialized from expression and not from the captured packet. <type> could be any int, float, string and enum E.g: var string str = "Hello world!"; var string[2] str_array = "Hello both world!"; var uint32{q=3}{d=the int=0x%04x} integer = 23; var float32 flt = 136.234; var float32[2] flt_array = 136.136; var string str2 = print("flt = %3f and integer = ", flt) + to_string(integer); var <an enum type> oper = <an enum type>::<symbolic name>; var uint8{d=bin} integer_bit_1 = integer & 0x01; var uint8{d=bin} integer_bit_2_3 = (integer & 0x06) >> 1;
The variable must NOT already exist.
set <variable name> = <expression> ; E.g: set Size = 0xa * 3 + 3 ; set Size = Size-10 ; set Size_bit_part = (Size & 0xc) >> 2;
The variable must already exist.
The variable could be a field (but does NOT change the value seen by wireshark).
const <type> <variable name> = <value or expression> ; permits to declare and initialize a constant. <type> could be any int, float, string and enum Constants must be named with "::". Constants could NOT have/be : - array, - specifications no_statement, transform, display, constrains ... - struct, switch, ... E.g: const uint16 konst::int = 2; const string konst::str = "abcdefgh - abcdefgh"; const T_Type_message konst::enum = T_Type_message::cs_start; const float64 Math::PI = 3.1415927; const float64 Math::deg_to_rad = Math::PI / 180; const string konst::str_fct = string.replace_all (konst::str, "bc", "xyz");
The constant must NOT already exist.
Built-in constants :
system::os = "win" or "linux" system::bits = 32 or 64 system::osbits = "win32" ... "linux64" shark::progfile_dir = "C:\wireshark\dev\svn_sources_release-1.8--win32\wireshark-gtk2" shark::plugin_dir = "C:\wireshark\dev\svn_sources_release-1.8--win32\wireshark-gtk2\plugins\1.8.3" shark::datafile_dir = "C:\wireshark\dev\svn_sources_release-1.8--win32\wireshark-gtk2" shark::systemfile_dir = "C:\wireshark\dev\svn_sources_release-1.8--win32\wireshark-gtk2" shark::profiles_dir = "C:\Users\Olivier\AppData\Roaming\Wireshark\profiles" shark::persdatafile_dir = "C:\Users\Olivier\Documents"
When you want to use a variable or field inside an expression or set command, you simply use its name.
But it could happen that there is many variables/fields with the same name.
In these cases, you can use the full name or the partial ending name.
E.g. :
struct time { uint32 value; var uin16 sec = ...; var uin16 min = ...; ... } struct T_my_msg { ... struct { ... time expected_time; } expected_data ; struct { ... time time; } measured_data ; # "value" means the last variable/field called "value", so it is "measured_data.time.value" if (value != expected_data.expected_time.value) { ... } # full name if (value != expected_time.value) { ... } # partial ending name }
function <return_type> <function_name> (<direction> <type> <name> [ = <default_value> ], ...) { <anything that could be specified in a struct> ... return <expression>; } <direction> = in, out, in_out <type> = any existing simple type (numeric or enum or string) or any <return_type> = <type> or void <default_value> = default value used if parameter is not specified NB: if a default value is specified on a parameter, then all following parameters must have a default value. E.g.: function string sec_to_dhms (in int64{min=0} value) { hide var int64 value_work = value; hide var uint16 sec = value_work % 60; set value_work = (value_work - sec) / 60; hide var uint16 min = value_work % 60; set value_work = (value_work - min) / 60; hide var uint16 hour = value_work % 24; set value_work = (value_work - hour) / 24; hide var uint16 days = value_work; return print("%d days %02d:%02d:%02d", days, hour, min, sec); } function string ms_to_dhms (in int64{min=0} value) { hide var int64 value_work = value; hide var uint16 ms = value_work % 1000; set value_work = (value_work - ms) / 1000; hide var string str = sec_to_dhms(value_work) + print(".%03d", ms); return str; } ... int64 milli_sec_time; var string milli_sec_time_str = ms_to_dhms(milli_sec_time); # or int64{de=ms_to_dhms(this)} milli_sec_time; NB: a function which returns void (ie nothing) must be called like this : call <function_name> (<the parameters>); NB: All variable/fields created during a function are erased at the end of the function. They are still visible/usable inside wireshark, but you can NOT use them into fdesc format description.
- string to_string (<field_variable_value_expression int/float/string>) ; - <int_or_float> to_numeric (in string str_containing_numeric_expression) ; fatal if result is not numeric - <float> to_float (in string/int numeric_expression) ; fatal if result is not numeric - <int> to_integer (in string/float numeric_expression, in int8 base = 0) ; fatal if result is not numeric
- string getenv (in string env_variable_name) ;
- string date.get_string_from_days (in uint16{min=1583} ref_year, in uint32 number_of_days); - string date.get_string_from_seconds (in uint16{min=1583} ref_year, in uint40 number_of_seconds); so, if you have unix time (seconds since 1970/01/01) use : date.get_string_from_seconds(1970, unix_time);
- uint string.length (in string str_source); - int64 string.find (in string str_source, in string str_to_find); # returns string::npos if not found - string string.substr (in string str_source, in uint index, in int64 count = string::npos); - string string.erase (in string str_source, in uint index, in int64 count = string::npos); - string string.insert (in string str_source, in uint index, in string str_to_insert); - string string.replace(in string str_source, in uint index, in int64 count, in string str_to_insert); - string string.replace_all(in string str_source, in string str_old, in string str_new);
Sometimes, the input data is NOT directly usable (using int16, string ...) because it is encoded in a way that Generic dissector does NOT understand.
It is necessary to decode the input data before use it.
With decoder command, you can provide a function which decode the data following your rules.
Decoder
Decoder could append more data than asked
internal_frame
Decoder and internal_frame data reading rules
Built-in decoders :
- decoder_aes (ecb only) key (16/24/32 bytes) must be specified by ascii string variable "decoder_aes_key", you must read the possible padding bytes
- decoder_base64 ignore space, tab, CR, LF
- decoder_utf8
- decoder_utf16be (big endian)
- decoder_utf16le (little endian)
- output -- or ++ ; modify the output level - save_position <position_name> ; save the current data packet position - goto_position <previously_saved_position_name> ; - move_position_bytes <position_offset> ; - move_position_bits <position_offset> ; - [debug_]print "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; - chat "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; - note "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; - warning "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; - error "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; - fatal "<string>" or <field name> or ("<printf format string>", <expression>, <expression> ...) ; print the specified string or the specified field value or printf chat, note and warning will appear on gray, blue and yellow lines error and fatal will declare an error and appear on red line colored lines will appear into "Analyze/Expert info composite" menu fatal is supposed to stop the dissection - [debug_]print (byte_order|output|position) ; print specified internal data
WITHOUT any effect on wireshark at this time.
- check_eof_distance_bytes <number of bytes before end of data> ; - check_eof_distance_bits <number of bits before end of data> ; verify the distance between the current position and the end of packet - [debug_]print (alias|struct|enum|switch) ; print specified internal data - [debug_]print all ; print all internal data specified above - [debug_]print (help | syntax) ;
- hide <any_type> <any_field> ; permits to hide the field/variable (NOT displayed and could NOT filter on it)
Only if you have specified MANAGE_WIRESHARK_PINFO into <file>.wsgd.
This is specific Wireshark internal data.
Use "print pinfo.*;" to know which pinfo data are available.
The available pinfo data could change depending on the wireshark version.
E.g: - pinfo.srcport, pinfo.destport, pinfo.match_port - pinfo.current_proto - pinfo.fd.num - pinfo.fd.pkt_len, pinfo.fd.cap_len - pinfo.fd.abs_ts, pinfo.fd.rel_ts - ...
NB: modify (with set command) these variables does NOT change wireshark values.
Only if you have specified SUBFIELD into <file>.wsgd.
<subfield type> <subfield name> ; subproto(<nb_bytes>) <field_name> ; must start on an entire byte position # or insproto(<nb_bytes>) <field_name> ; must start on an entire byte position
This permits to specifiy which data is give to the sub protocol.
The sub dissector/protocol could also be called explicitely :
subproto(<nb_bytes>){dissector=<proto abbrev name>} <field_name> ; must start on an entire byte position # or insproto(<nb_bytes>){dissector=<proto abbrev name>} <field_name> ; must start on an entire byte position
Example : mux.wsgd mux.fdesc and subproto mux_over.wsgd mux_over.fdesc