Skip to content

Commit

Permalink
start cip parser
Browse files Browse the repository at this point in the history
  • Loading branch information
pgaulon committed Sep 8, 2015
1 parent 962b305 commit 7f2d730
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 1 deletion.
4 changes: 3 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
if [ $# -eq 0 ]
then
echo "You need to specify the Bro installation directory."
echo "Usage: $0 </path/to/bro>"
echo "Usage: $0 </path/to/bro/>"
exit 1
fi

Expand All @@ -35,6 +35,8 @@ testing/btest/scripts/policy/protocols/enip/
testing/btest/Baseline/scripts.base.protocols.enip.*/
testing/btest/Baseline/scripts.policy.protocols.enip.*/
testing/btest/Traces/enip/
scripts/base/protocols/cip/
src/analyzer/protocol/cip/
"

for varname in $dirs
Expand Down
3 changes: 3 additions & 0 deletions scripts/base/protocols/cip/__load__.bro
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Generated by binpac_quickstart
@load ./main
@load-sigs ./dpd.sig
14 changes: 14 additions & 0 deletions scripts/base/protocols/cip/dpd.sig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by binpac_quickstart

signature dpd_cip {

ip-proto == tcp


# ## TODO: Define the payload. When Bro sees this regex, on
# ## any port, it will enable your analyzer on that
# ## connection.
# ## payload /^CIP/

enable "cip"
}
53 changes: 53 additions & 0 deletions scripts/base/protocols/cip/main.bro
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
##! Implements base functionality for cip analysis.
##! Generates the Cip.log file.

# Generated by binpac_quickstart

module Cip;

export {
redef enum Log::ID += { LOG };

type Info: record {
## Timestamp for when the event happened.
ts: time &log;
## Unique ID for the connection.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;

# ## TODO: Add other fields here that you'd like to log.
};

## Event that can be handled to access the cip record as it is sent on
## to the loggin framework.
global log_cip: event(rec: Info);
}

# TODO: The recommended method to do dynamic protocol detection
# (DPD) is with the signatures in dpd.sig. If you can't come up
# with any signatures, then you can do port-based detection by
# uncommenting the following and specifying the port(s):

# const ports = { 1234/tcp, 5678/tcp };


# redef likely_server_ports += { ports };

event bro_init() &priority=5
{
Log::create_stream(Cip::LOG, [$columns=Info, $ev=log_cip, $path="cip"]);

# TODO: If you're using port-based DPD, uncomment this.
# Analyzer::register_for_ports(Analyzer::ANALYZER_CIP, ports);
}

event cip_event(c: connection)
{
local info: Info;
info$ts = network_time();
info$uid = c$uid;
info$id = c$id;

Log::write(Cip::LOG, info);
}
1 change: 1 addition & 0 deletions src/analyzer/protocol/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_subdirectory(arp)
add_subdirectory(ayiya)
add_subdirectory(backdoor)
add_subdirectory(bittorrent)
# add_subdirectory(cip)
add_subdirectory(conn-size)
add_subdirectory(dce-rpc)
add_subdirectory(dhcp)
Expand Down
63 changes: 63 additions & 0 deletions src/analyzer/protocol/cip/CIP.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "CIP.h"
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "events.bif.h"

using namespace analyzer::cip;

CIP_Analyzer::CIP_Analyzer(Connection* c)

: tcp::TCP_ApplicationAnalyzer("CIP", c)
{
interp = new binpac::CIP::CIP_Conn(this);
had_gap = false;
}

CIP_Analyzer::~CIP_Analyzer()
{
delete interp;
}

void CIP_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();

interp->FlowEOF(true);
interp->FlowEOF(false);
}

void CIP_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}

void CIP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);

assert(TCP());
if ( TCP()->IsPartial() )
return;

if ( had_gap )
// If only one side had a content gap, we could still try to
// deliver data to the other side if the script layer can handle this.
return;

try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}

void CIP_Analyzer::Undelivered(uint64 seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
had_gap = true;
interp->NewGap(orig, len);
}
36 changes: 36 additions & 0 deletions src/analyzer/protocol/cip/CIP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef ANALYZER_PROTOCOL_CIP_CIP_H
#define ANALYZER_PROTOCOL_CIP_CIP_H

#include "events.bif.h"
#include "analyzer/protocol/tcp/TCP.h"
#include "cip_pac.h"

namespace analyzer { namespace cip {

class CIP_Analyzer

: public tcp::TCP_ApplicationAnalyzer {

public:
CIP_Analyzer(Connection* conn);
virtual ~CIP_Analyzer();

// Overriden from Analyzer.
virtual void Done();
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void Undelivered(uint64 seq, int len, bool orig);

// Overriden from tcp::TCP_ApplicationAnalyzer.
virtual void EndpointEOF(bool is_orig);

static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new CIP_Analyzer(conn); }

protected:
binpac::CIP::CIP_Conn* interp;
bool had_gap;
};

} } // namespace analyzer::*

#endif
9 changes: 9 additions & 0 deletions src/analyzer/protocol/cip/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
include(BroPlugin)

include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})

bro_plugin_begin(Bro CIP)
bro_plugin_cc(CIP.cc Plugin.cc)
bro_plugin_bif(events.bif)
bro_plugin_pac(cip.pac cip-analyzer.pac cip-protocol.pac)
bro_plugin_end()
22 changes: 22 additions & 0 deletions src/analyzer/protocol/cip/Plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "plugin/Plugin.h"
#include "CIP.h"

namespace plugin {
namespace Bro_CIP {

class Plugin : public plugin::Plugin {
public:
plugin::Configuration Configure()
{
AddComponent(new ::analyzer::Component("CIP",
::analyzer::cip::CIP_Analyzer::InstantiateAnalyzer));

plugin::Configuration config;
config.name = "Bro::CIP";
config.description = "Common Industrial Protocol analyzer";
return config;
}
} plugin;

}
}
11 changes: 11 additions & 0 deletions src/analyzer/protocol/cip/cip-analyzer.pac
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
refine flow CIP_Flow += {
function proc_cip_message(msg: CIP_PDU): bool
%{
BifEvent::generate_cip_event(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn());
return true;
%}
};

refine typeattr CIP_PDU += &let {
proc: bool = $context.flow.proc_cip_message(this);
};
84 changes: 84 additions & 0 deletions src/analyzer/protocol/cip/cip-protocol.pac
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#
# Useful reference for specs: http://odva.org/
# CIP NETWORKS LIBRARY Volume I: http://www.tud.ttu.ee/im/Kristjan.Sillmann/ISP0051%20Rakenduslik%20Andmeside/CIP%20docs/CIP%20Vol1_3.3.pdf
#
# Binpac for Common Industrial Protocol analyser.
#

##############################
# CONSTANTS #
##############################

enum tag_types {
BOOL = 0x00C1; # 1 byte
SINT = 0x00C2; # 2 bytes
INT = 0x00C3; # 4 bytes
DINT = 0x00C4; # 4 bytes
REAL = 0x00CA; # 4 bytes
DWORD = 0x00D3; # 4 bytes
LINT = 0x00C5; # 8 bytes
};

# E8B means 8-bit element
# C16B means 16-bit class
enum segment_types {
E8B = 0x28;
E16B = 0x29;
E32B = 0x2A;

C8B = 0x20;
C16B = 0x21;

I8B = 0x24;
I16B = 0x25;

A8B = 0x30;
A16B = 0x31;

ANSI = 0x91;
};

enum services {
READ_TAG = 0x4C;
READ_TAG_REPLY = 0xCC;
READ_TAG_FRAGMENTED = 0x52;
WRITE_TAG = 0x4D;
WRITE_TAG_FRAGMENTED = 0x53;
READ_MODIFY_WRITE_TAG = 0x4E;
};

##############################
# RECORD TYPES #
##############################

type Message_Request = record {
srv: uint8;
psize: uint8;
path: uint8[psize * 2];
data: uint16;
} &byteorder=bigendian;

type Message_Reply = record {
srv: uint8;
rsrvd: uint8;
st: uint8;
ext: uint8;
reply: Reply_Data;
} &byteorder=bigendian;

type Reply_Data = record {
type: uint16;
data: case(type) of {
BOOL -> uint8[1];
SINT -> uint8[2];
INT -> uint8[4];
DINT -> uint8[4];
REAL -> uint8[4];
DWORD -> uint8[4];
LINT -> uint8[8];
};
} &byteorder=bigendian;

type CIP_PDU(is_orig: bool) = record {
data: bytestring &restofdata;
} &byteorder=bigendian;
39 changes: 39 additions & 0 deletions src/analyzer/protocol/cip/cip.pac
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Analyzer for Common Industrial Protocol
# - cip-protocol.pac: describes the cip protocol messages
# - cip-analyzer.pac: describes the cip analyzer code

%include binpac.pac
%include bro.pac

%extern{
#include "events.bif.h"
%}

analyzer CIP withcontext {
connection: CIP_Conn;
flow: CIP_Flow;
};

# Our connection consists of two flows, one in each direction.
connection CIP_Conn(bro_analyzer: BroAnalyzer) {
upflow = CIP_Flow(true);
downflow = CIP_Flow(false);
};

%include cip-protocol.pac

# Now we define the flow:
flow CIP_Flow(is_orig: bool) {

# ## TODO: Determine if you want flowunit or datagram parsing:

# Using flowunit will cause the anlayzer to buffer incremental input.
# This is needed for &oneline and &length. If you don't need this, you'll
# get better performance with datagram.

# flowunit = CIP_PDU(is_orig) withcontext(connection, this);
datagram = CIP_PDU(is_orig) withcontext(connection, this);

};

%include cip-analyzer.pac
7 changes: 7 additions & 0 deletions src/analyzer/protocol/cip/events.bif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Generated for cip connections
##
## See `Google <http://lmgtfy.com/?q=cip>`__ for more information about cip
##
## c: The connection
##
event cip_event%(c: connection%);

0 comments on commit 7f2d730

Please sign in to comment.