基于ASN.1的簡單解析器
以下片段顯示了組成“foo”協議分析器的分析器的不同文件。
*README.txt. *
FOO protocol dissector
----------------------
This trivial dissector is an example for the strugling dissector developer (me included)
of how to create a dissector for a protocol that is encapsulated in UDP packets
for a specific port, and the packet data is ASN1 PER encoded.
The thing that took me a while to figure out was that in order to see my packet
dissected on the detail pane, I had to:
- Tell the compiler which block in the ASN1 definition is a PDU definition by adding
FOO-MESSAGE under the #.PDU directive in the foo.cnf file - Add a call to dissect_FOO_MESSAGE_PDU() function in the dissect_foo() function in the
packet-foo-template.c file.
To build and test it:
- in foo directory, run make
- run make copy_files
- add packet-foo.c and packet-foo.h to epan/dissectors/Makefile.common
- run top level make
CAVEAT: Makefile.nmake was not tested .
You can take it from here :-)
--00--
*foo.asn. *
– FOO PROTOCOL
FOO-PROTOCOL DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
– General definitions
MessageId ::= INTEGER (0..65535)
FlowId ::= INTEGER (0..65535)
MessageData ::= SEQUENCE {
name OCTET STRING(SIZE(10)),
value OCTET STRING(SIZE(10))
}
FOO-MESSAGE ::= SEQUENCE {
messageId MessageId,
flowId FlowId,
messageData MessageData
}
END
*foo.cnf. *
foo.cnf
FOO conformation file
$Id$
#.MODULE_IMPORT
#.EXPORTS
#.PDU
FOO-MESSAGE
#.NO_EMIT
#.TYPE_RENAME
#.FIELD_RENAME
#.END
*packet-foo-template.h. *
/* packet-foo.h
- Routines for foo packet dissection
- Wireshark - Network traffic analyzer
- By Gerald Combs gerald@wireshark.org
- Copyright 1998 Gerald Combs
- SPDX-License-Identifier: GPL-2.0-or-later
- /
#ifndef PACKET_FOO_H
#define PACKET_FOO_H
#endif /* PACKET_FOO_H */
*packet-foo-template.c. *
/* packet-foo.c
- Routines for FOO packet dissection
- Wireshark - Network traffic analyzer
- By Gerald Combs gerald@wireshark.org
- Copyright 1998 Gerald Combs
- SPDX-License-Identifier: GPL-2.0-or-later
- /
#ifdef HAVE_CONFIG_H
include “config.h”
#endif
#include <glib.h>
#include <epan/packet.h>
#include <epan/conversation.h>
#include <stdio.h>
#include <string.h>
#include “packet-per.h”
#include “packet-foo.h”
#define PNAME “FOO Protocol”
#define PSNAME “FOO”
#define PFNAME “foo”
#define FOO_PORT 5001 /* UDP port */
static dissector_handle_t foo_handle=NULL;
void proto_reg_handoff_foo(void);
void proto_register_foo(void);
/* Initialize the protocol and registered fields */
static int proto_foo = -1;
static int global_foo_port = FOO_PORT;
#include “packet-foo-hf.c”
/* Initialize the subtree pointers */
static int ett_foo = -1;
#include “packet-foo-ett.c”
#include “packet-foo-fn.c”
static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
proto_item *foo_item = NULL;
proto_tree *foo_tree = NULL;
int offset = 0;
/* make entry in the Protocol column on summary display */
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, PNAME);
/* create the foo protocol tree */
if (tree) {
foo_item = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
foo_tree = proto_item_add_subtree(foo_item, ett_foo);
dissect_FOO_MESSAGE_PDU(tvb, pinfo, foo_tree);
}
}
/— proto_register_foo ——————————————-/
void proto_register_foo(void) {
/* List of fields */
static hf_register_info hf[] = {
#include “packet-foo-hfarr.c”
};
/* List of subtrees */
static gint *ett[] = {
&ett_foo,
#include “packet-foo-ettarr.c”
};
/* Register protocol /
proto_foo = proto_register_protocol(PNAME, PSNAME, PFNAME);
/ Register fields and subtrees */
proto_register_field_array(proto_foo, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
/— proto_reg_handoff_foo —————————————/
void
proto_reg_handoff_foo(void)
{
static gboolean inited = FALSE;
if( !inited ) {
foo_handle = create_dissector_handle(dissect_foo,
proto_foo);
dissector_add("udp.port", global_foo_port, foo_handle);
inited = TRUE;
}
}
*CMakeLists.txt. *
set( PROTOCOL_NAME foo )
set( PROTO_OPT )
set( EXT_ASN_FILE_LIST
)
set( ASN_FILE_LIST
Foo.asn
)
set( EXTRA_DIST
${ASN_FILE_LIST}
packet-${PROTOCOL_NAME}-template.c
${PROTOCOL_NAME}.cnf
)
set( SRC_FILES
${EXTRA_DIST}
${EXT_ASN_FILE_LIST}
)
set( A2W_FLAGS )
ASN2WRS()
Conformance (.cnf) 文件
.cnf文件告訴編譯器如何處理某些事情,例如跳過某些ASN.1條目的自動生成。它們可以包含以下指令:
#.OPT
Compiler options.
#.MODULE and #.MODULE_IMPORT
Assign Wireshark protocol name to ASN.1 module name.
#.INCLUDE
Include another conformance file.
#.EXPORTS
Export type or information object class.
#.PDU, #.PDU_NEW, #.REGISTER, #.REGISTER_NEW, and #.SYNTAX
Create PDU functions and register them optionally to dissector table.
#.CLASS
Declare or define information object class.
#.ASSIGNED_OBJECT_IDENTIFIER
Declare assigned object identifier.
#.TABLE_HDR, #.TABLE_BODY, and #.TABLE_FTR
User tables.
#.OMIT_ASSIGNMENT, #.NO_OMIT_ASSGN, #.OMIT_ALL_ASSIGNMENTS, #.OMIT_ASSIGNMENTS_EXCEPT, #.OMIT_ALL_TYPE_ASSIGNMENTS, #.OMIT_TYPE_ASSIGNMENTS_EXCEPT, #.OMIT_ALL_VALUE_ASSIGNMENTS, and #.OMIT_VALUE_ASSIGNMENTS_EXCEPT
Ignore assignments from ASN.1 source.
#.NO_EMITand #.USER_DEFINED
See linked text for info.
#.VIRTUAL_ASSGN, #.SET_TYPE, #.MAKE_ENUM, #.MAKE_DEFINES, and #.ASSIGN_VALUE_TO_TYPE
Unknown.
#.TYPE_RENAME, #.FIELD_RENAME, and #.TF_RENAME
Type/field renaming
#.IMPORT_TAG, #.TYPE_ATTR, #.FIELD_ATTR
Type attributes
#.FN_HDR, #.FN_BODY, #.FN_FTR, and #.FN_PARS
Type function modification
#.END
End of directive
#.END_OF_CNF
End of conformance file
Example .cnf File
#.MODULE IMPORT
InformationFramework x509if
#.INCLUDE ../x509if/x509if_exp.cnf
#.EXPORTS +
ObjectName
#.PDU
ObjectName
#.REGISTER
Certificate B “2.5.4.36” “id-at-userCertificate”
#.SYNTAX
ObjectName [FriendlyName]
#.NO_EMIT ONLY_VALS
this can be used with: [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
using NO_EMIT NO_VALS means it won’t generate value_string array for it
Type1
#.USER DEFINED
Type1 [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
#.TYPE_RENAME
#.FIELD_RENAME
#.TYPE_ATTR Ss-Code TYPE = FT_UINT16 DISPLAY = BASE_HEX STRINGS = VALS(ssCode_vals)
This entry will change the hf definition from the auto-generated one for Ss-Code ::= OCTET STRING(SIZE(1))
{ &hf_gsm_map_ss_Code,
{ "ss-Code", "gsm_map.ss_Code",
FT_BYTES, BASE_HEX, NULL, 0, "", HFILL }},
to:
{ &hf_gsm_map_ss_Code,
{ "ss-Code", "gsm_map.ss_Code",
FT_UINT16, BASE_HEX, VALS(ssCode_vals), 0, "", HFILL }},
In the proto_abbr-template.c file the corresponding value string must be inserted. As an example the following would be included in proto_abbr-template.c to define ssCode_vals:
static const value_string ssCode_vals[] = {
{ 0, “ssCodeString 1” }, /* The string for value 0 /
{ 1, “String 2” }, / String for value 1 /
{ 5, “String for value 5” }, / Value String 5 /
{ 0, NULL } / Null terminated array */
}
Note that the NULL value must be the final entry and that the index values need not be consecutive.
Foo is expressed in different ways depending on where you want to insert your code and the ASN.1 code in question.
- Foo
- Foo/foo
- Foo/_item/foo
For Tagged type use:
Foo/_untag
#.FN_HDR Foo
/* This is code to be inserted into the dissector for Foo BEFORE the BER/PER helper is called. */
tvbuff_t *out_tvb;
fragment_data *fd_head;
tvbuff_t *next_tvb = NULL;
#.FN_BODY Foo
/* This here is code to replace the actual call to the helper completely. */
offset = dissect_ber_octet_string(implicit_tag, pinfo, tree, tvb, offset, hf_index, &out_tvb);
/* Putting %(DEFAULT_BODY)s inside #.FN_BODY will insert the original code there. */
#.FN_FTR Foo
/* This is code to be inserted into the dissector for Foo AFTER the ber/per helper has returned called. */
if (foo_reassemble) {
…
}
#.FN_PARS
#.END
#.MODULE IMPORT
InformationFramework x509if
#.INCLUDE ../x509if/x509if_exp.cnf
#.EXPORTS +
ObjectName
#.PDU
ObjectName
#.REGISTER
Certificate B “2.5.4.36” “id-at-userCertificate”
#.SYNTAX
ObjectName [FriendlyName]
#.NO_EMIT ONLY_VALS
this can be used with: [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
using NO_EMIT NO_VALS means it won’t generate value_string array for it
Type1
#.USER DEFINED
Type1 [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
#.TYPE_RENAME
#.FIELD_RENAME
#.TYPE_ATTR Ss-Code TYPE = FT_UINT16 DISPLAY = BASE_HEX STRINGS = VALS(ssCode_vals)
This entry will change the hf definition from the auto-generated one for Ss-Code ::= OCTET STRING(SIZE(1))
{ &hf_gsm_map_ss_Code,
{ "ss-Code", "gsm_map.ss_Code",
FT_BYTES, BASE_HEX, NULL, 0, "", HFILL }},
to:
{ &hf_gsm_map_ss_Code,
{ "ss-Code", "gsm_map.ss_Code",
FT_UINT16, BASE_HEX, VALS(ssCode_vals), 0, "", HFILL }},
In the proto_abbr-template.c file the corresponding value string must be inserted. As an example the following would be included in proto_abbr-template.c to define ssCode_vals:
static const value_string ssCode_vals[] = {
{ 0, “ssCodeString 1” }, /* The string for value 0 /
{ 1, “String 2” }, / String for value 1 /
{ 5, “String for value 5” }, / Value String 5 /
{ 0, NULL } / Null terminated array */
}
Note that the NULL value must be the final entry and that the index values need not be consecutive.
Foo is expressed in different ways depending on where you want to insert your code and the ASN.1 code in question.
- Foo
- Foo/foo
- Foo/_item/foo
For Tagged type use:
Foo/_untag
#.FN_HDR Foo
/* This is code to be inserted into the dissector for Foo BEFORE the BER/PER helper is called. */
tvbuff_t *out_tvb;
fragment_data *fd_head;
tvbuff_t *next_tvb = NULL;
#.FN_BODY Foo
/* This here is code to replace the actual call to the helper completely. */
offset = dissect_ber_octet_string(implicit_tag, pinfo, tree, tvb, offset, hf_index, &out_tvb);
/* Putting %(DEFAULT_BODY)s inside #.FN_BODY will insert the original code there. */
#.FN_FTR Foo
/* This is code to be inserted into the dissector for Foo AFTER the ber/per helper has returned called. */
if (foo_reassemble) {
…
}
#.FN_PARS
#.END
Example packet-protocol-template.h File
Example template.h file. Replace all PROTOCOL/protocol references with the name of your protocol.
/* packet-protocol.h
- Routines for Protocol packet dissection
- $Id$
- Wireshark - Network traffic analyzer
- By Gerald Combs < gerald@wireshark.org>
- Copyright 1998 Gerald Combs
- SPDX-License-Identifier: GPL-2.0-or-later
- /
#ifndef PACKET_PROTOCOL_H
#define PACKET_PROTOCOL_H
#include “packet-protocol-exp.h”
#endif /* PACKET_PROTOCOL_H */
Example packet-protocol-template.c File
Example template.c file. Replace all PROTOCOL/protocol references with the name of your protocol.
/* packet-protocol.c
- Routines for PROTOCOL packet dissection
- $Id$
- Wireshark - Network traffic analyzer
- By Gerald Combs gerald@wireshark.org
- Copyright 1998 Gerald Combs
- SPDX-License-Identifier: GPL-2.0-or-later
- /
#ifdef HAVE_CONFIG_H
include “config.h”
#endif
#include <glib.h>
#include <epan/packet.h>
#include <epan/conversation.h>
#include <stdio.h>
#include <string.h>
#include “packet-ber.h”
#include “packet-protocol.h”
#define PNAME “This Is The Protocol Name”
#define PSNAME “PROTOCOL”
#define PFNAME “protocol”
/* Initialize the protocol and registered fields */
int proto_protocol = -1;
#include “packet-protocol-hf.c”
/* Initialize the subtree pointers */
#include “packet-protocol-ett.c”
#include “packet-protocol-fn.c”
/— proto_register_protocol ———————————————-/
void proto_register_protocol(void) {
/* List of fields */
static hf_register_info hf[] = {
#include “packet-protocol-hfarr.c”
};
/* List of subtrees */
static gint *ett[] = {
#include “packet-protocol-ettarr.c”
};
/* Register protocol */
proto_protocol = proto_register_protocol(PNAME, PSNAME, PFNAME);
/* Register fields and subtrees */
proto_register_field_array(proto_protocol, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
/— proto_reg_handoff_protocol ——————————————-/
void proto_reg_handoff_protocol(void) {
#include “packet-protocol-dis-tab.c”
}
符合性文件指令參考
可以在符合性(.cnf)文件中使用以下指令:
#.END
Asn2wrs一致性文件中的其他一些指令由多行組成。該.END指令用于終止這樣的指令。所有其他“.” 指令(#.INCLUDE除外)自動充當隱式#.END指令,這就是為什么在Wireshark附帶的解剖器的一致性文件中看不到很多#.END指令的原因。
#.EXPORTS
Asn2wrs構造文件中的此偽指令用于從分解器導出用于類型解碼的函數。
#.EXPORTS
TypeName [WITH_VALS|WITHOUT_VALS|ONLY_VALS] [WS_VAR] [NO_PROT_PREFIX]
…
#.END
Syntax
Options:
- WITH_VALS (default): Exports dissection function and value string table if present.
- WITHOUT_VALS: Exports only the dissection function.
- ONLY_VALS: Exports only the value string table.
- WS_VAR and WS_VAR_IMPORT: Used for value string table so as it can be exported from libwireshark.dll.
- NO_PROT_PREFIX: - value string table name does not have protocol prefix
Example
#.EXPORTS
NonStandardParameter
RasMessage WITH_VALS WS_VAR
H323-UU-PDU/h323-message-body ONLY_VALS WS_VAR
#.END
#.FN_BODY
有時,例如當我們具有ANY類型時,我們可能想用自己的代碼替換Asn2wrs生成的任何函數體。這就是該指令允許我們執行的操作。
示例:ANY
Asn2wrs可以處理ANY類型,但是我們必須通過對一致性文件進行一些小的更改來幫助它。假設您有一個看起來像這樣的構造:
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY OPTIONAL
}
為了解決這個問題,我們需要為算法和參數字段指定自己的函數體,我們使用#.FN_BODY指令進行此操作。
這個特定的示例還要求我們在兩個場分解器之間保持某種狀態,即算法中的對象標識符,它指定參數的內容是什么。為此,我們需要一個字符串,用于存儲來自AlgorithmIdentifier /算法的oid,以便我們可以將其拾取并稍后從AlgorithmIdentifier /參數的解剖器內部進行操作。因此,我們必須添加:
static char algorithm_id[64]; /* 64 chars should be enough? */
to the template file as a placeholder to remember which OID we picked up. Then we add to the conformance file:
#.FN_BODY AlgorithmIdentifier/algorithmId
offset = dissect_ber_object_identifier(FALSE, pinfo, tree, tvb, offset,
hf_x509af_algorithm_id, algorithm_id);
#.FN_BODY AlgorithmIdentifier/parameters
offset=call_ber_oid_callback(algorithm_id, tvb, offset, pinfo, tree);
我們在此處為AlgorithmIdentifier / algorithmId指定的解剖器主體將檢索到的OID存儲在我們指定的變量algorithm_id中。稍后,當我們進入AlgorithmIdentifier / parameters的分解器時,我們從靜態變量中選取此OID,然后將其傳遞給ber / oid分解器助手。
此示例來自X509AF解析器。請參閱此處的代碼,以獲取有關如何執行此操作的更多示例。
#.MODULE_IMPORT和#.INCLUDE
Asn2wrs構造文件中的這些指令用于管理對外部類型定義(即IMPORTS)的引用。下面的示例全部來自Wireshark中的X.509身份驗證框架(x509af)解析器源代碼。
示例ASN
這是X509AF剖析器的示例,該剖析器除其他外還從X.509 InformationFramework導入定義:
IMPORTS
Name, ATTRIBUTE, AttributeType, MATCHING-RULE, Attribute
FROM InformationFramework informationFramework
它告訴Asn2wrs編譯器,在外部InformationFramework ASN模塊中聲明了類型“名稱”,“屬性”,“屬性類型”,“匹配規則”和“屬性”,并從該模塊中對其進行了引用。為了使Asn2wrs生成用于解剖的正確代碼,需要通過告訴它們是哪種類型(即它們是INTEGERs還是SEQUENCEs或其他類型)來提供幫助。
為了能夠從此模塊訪問這些功能,重要的是,必須在X509 InformationFramework剖析器中將這些類型聲明為#.EXPORTS,以便將它們導出并鏈接到它們。
#.MODULE_IMPORT
首先,我們需要告訴Asn2wrs Wireshark在此外部導入中的功能使用哪個協議名稱,以便Asn2wrs可以為這些外部功能生成合適的函數調用簽名。為此,我們向配置文件添加了一條指令:
#.MODULE_IMPORT
InformationFramework x509if
其中,InformationFramework是在asn IMPORTS聲明中使用的模塊的ASN名稱,而x509if是我們在Wireshark內部用于該協議的名稱。這告訴Asn2wrs,要調用的Dissect Name的函數名稱將為dissect_x509if_Name(…)。沒有這些知識,Asn2wrs將不知道生成哪個函數名稱。
#.INCLUDE
其次,為了使Asn2wrs生成正確的代碼,它還需要知道所導入的BER類型和這些類型的類,因為這會影響它們在線路上的編碼方式。關于這些導入的類型具有哪種BER屬性的信息是使用一致性文件中的#.INCLUDE指令完成的:
#.NO_EMIT和#.USER_DEFINED
一致性文件中針對Asn2wrs的這兩個指令可用于抑制解剖器和/或value_strings的生成,并且對于協議類似。當在asn定義中存在Asn2wrs無法處理的類型,或者如果我們想在模板文件中處理自己的解剖以進行其他狀態保存或Asn2wrs不知道如何管理的事情時,這將非常有用。
這兩個指令非常相似。兩者之間的唯一區別是,#。NO_EMIT將禁止發出該函數的解剖器,并且將禁止任何value_strings,而#.USER_DEFINED則將發出聲明而不是定義。
即#.USER_DEFINED將發出諸如extern const value_string Type_vals[]; 和的 聲明。 [static] int dissect_Proto_Type(…?);
如果您不需要從任何地方(模板本身除外)完全調用此函數,請使用#.NO_EMIT;如果您在模板中實現該函數但仍希望允許從其他位置調用該函數,請使用#.USER_DEFINED更好。
Syntax
#.USER_DEFINED
TypeName [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
…
#.END
#.NO_EMIT
TypeName [WITH_VALS|WITHOUT_VALS|ONLY_VALS]
…
#.END
選項:
- WITH_VALS(默認值):解剖函數和值字符串表都是用戶定義的,不會發出。
- WITHOUT_VALS:僅解剖功能是用戶定義的,不發出。
- ONLY_VALS:只有值字符串表是用戶定義的,不會發出。
#.PDU和#.PDU_NEW
Asn2wrs構造文件中的此偽指令將圍繞對象分解器生成包裝函數。如果ASN.1定義中確實有一個對象要注冊為協議解析器,或者希望它具有眾所周知的簽名,則這很有用。
功能名稱
創建的包裝函數將全部被命名并具有以下簽名:
static void dissect_ProtocolName_ObjectName(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
請注意,這與dissector_t所有解剖??器入口點使用的簽名完全相同。
用法
為了使Asn2wrs生成此類包裝函數,您只需要在#.PDU聲明之后的行上逐一列出所有對象。
Example
#.PDU
SomeObject
這將導致Asn2wrs在packet-foo.c解剖器文件中創建此包裝器函數:
static void dissect_SomeObject_PDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
dissect_foo_SomeObject(FALSE, ...
}
然后可以從模板文件中調用或引用此函數,甚至將其導出。
#.REGISTER和#.REGISTER_NEW
Asn2wrs構造文件中的此偽指令可用于將對象的解析器注冊到OID。這對于X.509和結構和對象經常與OID關聯的類似協議非常有用。特別是,這里的某些結構在一個字段中編碼OID,然后在另一個字段中編碼內容,然后如何解析該字段取決于先前看到的OID。
Wireshark中文使用教程(開發版)
推薦文章: