-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtutorial_packet_craft_n_edit.html
324 lines (310 loc) · 20.7 KB
/
tutorial_packet_craft_n_edit.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
---
title: Tutorials
layout: default
section: tutorials
---
<h1><a href="tutorials.html">Tutorials</a></h1>
<h2>Part 5: Packet crafting and editing</h2>
<p>This part explains how to edit existing packet and craft new ones</p>
<p>Table of contents:</p>
<ul>
<li><a href="tutorial_intro.html">Part 1: Introduction and basics</a></li>
<li><a href="tutorial_pcap_files.html">Part 2: Reading and writing
pcap files</a></li>
<li><a href="tutorial_live_capture.html">Part 3: Capturing and sending
packets</a></li>
<li><a href="tutorial_packet_parsing.html">Part 4: Packet parsing</a></li>
<li>Part 5: Packet crafting and editing
<ol>
<li><a href="#intro">Introduction</a></li>
<li><a href="#packet_editing">Packet editing</a></li>
<li><a href="#packet_creation">Packet creation</a></li>
<li><a href="#run_example">Running the example</a></li>
</ol>
</li>
</ul>
<h2 id="intro">Introduction</h2>
<p>In the previous <a href="tutorial_packet_parsing.html">Packet
editing</a> tutorial we demonstrated how PcapPlusPlus parses packets
and how to read data out the various layers. In this tutorial we'll
focus on editing packets - meaning change existing protocol data, add
more data and add/remove layers, and also how to craft new packets
from scratch.</p>
<p>Of course we won't go over all the protocols, we'll focus on a few
which are popular:</p>
<ul>
<li>Ethernet</li>
<li>VLAN</li>
<li>IPv4</li>
<li>TCP</li>
<li>UDP</li>
<li>HTTP</li>
<li>DNS</li>
</ul>
<p>For further information about these protocols and the other protocols
supported in PcapPlusPlus please go to the <a href="Documentation/index.html">API
documentation</a></p>
<p><a href="Documentation/index.html"></a></p>
<p></p>
<h2 id="packet_editing">Packet editing</h2>
<p>In this part of the tutorial we'll read a packet from a pcap file,
let PcapPlusPlus parse it, and then see how we can edit and change the
data in each layer. Let's start by writing a <code>mai<span style="font-family: "trebuchet ms",arial,sans-serif;">n()</span></code>
method and add the includes that we need:</p>
<pre><code class="cpp">#if !defined(WIN32) && !defined(WINx64)
#include <in.h> // this is for using ntohs() and htons() on non-Windows OS's
#endif
#include "stdlib.h"
#include "Packet.h"
#include "EthLayer.h"
#include "VlanLayer.h"
#include "IPv4Layer.h"
#include "TcpLayer.h"
#include "HttpLayer.h"
#include "UdpLayer.h"
#include "DnsLayer.h"
#include "PcapFileDevice.h"
int main(int argc, char* argv[])
{
// We'll write our code here
}
</code>
</pre>
<p>As you can see we added an include to <code class="cpp">Packet.h</code>
which contains the basic parsed packet structures, to <code class="cpp">PcapFileDevice.h</code>
which contains the API for reading/writing from/to pcap files and to
all of the layers which we will edit and add data from. In addition we included
<code class="cpp">in.h</code> for using <code class="cpp">htons()</code> and
<code class="cpp">ntohs()</code> which we'll use later. This include is relevant for non-Windows
operating systems only.</p>
<p>Now let's read the packet from the pcap file. This pcap file contains
only 1 packet, so we'll open the reader, read the packet and close the
reader:</p>
<pre> <code class="cpp">// use the IFileReaderDevice interface to automatically identify file type (pcap/pcap-ng)
// and create an interface instance that both readers implement
pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader("1_http_packet.pcap");
// verify that a reader interface was indeed created
if (reader == NULL)
{
printf("Cannot determine reader for file type\n");
exit(1);
}
// open the reader for reading
if (!reader->open())
{
printf("Cannot open input.pcap for reading\n");
exit(1);
}
// read the first (and only) packet from the file
pcpp::RawPacket rawPacket;
if (!reader->getNextPacket(rawPacket))
{
printf("Couldn't read the first packet in the file\n");
return 1;
}
// close the file reader, we don't need it anymore
reader->close();</code>
</pre>
<p>The next step is to let PcapPlusPlus parse the packet by creating an
instance of the <code class="cpp">Packet</code> class and giving it
in the constructor a pointer to the <code class="cpp">RawPacket</code>
instance we have:</p>
<pre> <code class="cpp">// parse the raw packet into a parsed packet
pcpp::Packet parsedPacket(&rawPacket);</code>
</pre>
<p>You may notice this is exactly the same packet as we used in the <a
href="tutorial_packet_parsing.html">Packet parsing</a> tutorial but
this time we won't just read data from the various layers but actually
change it. First thing we'll do is get the <strong>Ethernet</strong>
layer and change it:</p>
<pre><code class="cpp">// now let's get the Ethernet layer
pcpp::EthLayer* ethernetLayer = parsedPacket.getLayerOfType<pcpp::EthLayer>();
// change the source dest MAC address
// change the source dest MAC address
ethernetLayer->setDestMac(pcpp::MacAddress("aa:bb:cc:dd:ee:ff"));</code>
</pre>
<p>As you can see, we changed the destination MAC address to <span style="font-family: monospace;">"aa:bb:cc:dd:ee"</span><code
class="cpp"></code>.
We used the <code class="cpp">setDestMac()</code> method exposed
in <code>EthLayer</code> to do that and we gave it a <code>MacAddress</code>
class instance we created with the new MAC address we want.</p>
<p>Ethernet layer is quite simple, let's move to a more complex
layer - <strong>IPv4</strong>, and see what data we can change there:</p>
<pre><code class="cpp">// let's get the IPv4 layer
pcpp::IPv4Layer* ipLayer = parsedPacket.getLayerOfType<pcpp::IPv4Layer>();
// change source IP address
ipLayer->setSrcIpAddress(pcpp::IPv4Address(std::string("1.1.1.1")));
// change IP ID
ipLayer->getIPv4Header()->ipId = htons(4000);
// change TTL value
ipLayer->getIPv4Header()->timeToLive = 12;</code>
</pre>
<p>First we changed the source IPv4 address to <code>"1.1.1.1"</code>
using the method <code>setSrcIpAddress()</code> and provided it
an instance of the class <code>IPv4Address</code> with the value
of <code>"1.1.1.1"</code>. Then, we used the <code class="cpp">getIPv4Header()</code>
method which casts the raw packet bytes to a struct called <code class="cpp">iphdr*</code> in the same way we did in the packet
parsing tutorial, but this time instead of reading values we're
changing them. It is very important to understand that the <code>iphdr*</code>
object gives access to the actual packet bytes so it can be both
read and manipulated, and each change affects the actual packet
data. When setting fields which are wider than 1 byte it's
important to write in network order and that's why we're using <code>htons()</code>
to set the IP ID to 4000.</p>
<p>Let's move on to the next layer - <strong>TCP</strong>:</p>
<pre><code class="cpp">// let's get the TCP layer
pcpp::TcpLayer* tcpLayer = parsedPacket.getLayerOfType<pcpp::TcpLayer>();
// change source port
tcpLayer->getTcpHeader()->portSrc = htons(12345);
// add URG flag
tcpLayer->getTcpHeader()->urgFlag = 1;
// add MSS TCP option
uint16_t mssValue = htons(1460);
tcpLayer->addTcpOptionAfter(pcpp::TCPOPT_MSS, PCPP_TCPOLEN_MSS, (uint8_t*)&mssValue, NULL); </code></pre>
<p>
</p>
<p>We start by using the method <code>getTcpHeader()</code> which
casts the raw packet bytes to a struct <code class="cpp">tpchdr*</code>
which contains all of the TCP fields. Again, like we saw in <code>IPv4Layer</code>, this method gives access to the actual
packet bytes so every change we do changes the packet. So
we changed the source port to 12345 and set the URG flag. </p>
<p>Now let's take a look at the 2 bottom lines in the code snippet
above. <code>TcpLayer </code>exposes an API to read, add
and remove TCP options. The packet we're editing already has 3 TCP
options: NOP, NOP and Timestamp. We'd like to add a fourth one of
type MSS with MSS value of 1460 and we want it to appear first (before
the existing TCP options). So we use the <code>addTcpOptionAfter()</code>
method and give it the following parameters: TCP option type
(MSS), TCP option length (PcapPlusPlus already has a macro for
that: <code>PCPP_TCPOLEN_MSS</code>), the TCP option value which
is 1460 cast to a byte array, and the layer we want to put our
option after (in this case we set <code>NULL</code> which means this option
will be inserted as the first one). That's it! with 2 lines of
code we managed to add a TCP option!</p>
<p>Now let move on to the last layer in this packet: <strong>HTTP</strong>. Let's see the code:</p>
<pre><code class="cpp" ?="">// let's get the HTTP layer
pcpp::HttpRequestLayer* httpRequestLayer = parsedPacket.getLayerOfType<pcpp::HttpRequestLayer&hl;();
// change the request method from GET to TRACE
httpRequestLayer->getFirstLine()->setMethod(pcpp::HttpRequestLayer::HttpTRACE);
// change host to www.google.com
httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD)->setFieldValue("www.google.com");
// change referer value to www.aol.com
httpRequestLayer->getFieldByName(PCPP_HTTP_REFERER_FIELD)->setFieldValue("www.aol.com");
// remove cookie field
httpRequestLayer->removeField(PCPP_HTTP_COOKIE_FIELD);
// add x-forwarded-for field
pcpp::HttpField* xForwardedForField = httpRequestLayer->insertField(httpRequestLayer->getFieldByName(PCPP_HTTP_HOST_FIELD), "X-Forwarded-For", "1.1.1.1");
// add cache-control field
httpRequestLayer->insertField(xForwardedForField, "Cache-Control", "max-age=0");</code></pre>
<p>We already discussed the highlights of the <code>HttpLayer</code> API in the
<a href="tutorial_packet_parsing.html">Packet parsing</a> tutorial so we
won't repeat all of it again. But as you can see the
API provides setters for all of the relevant data:</p>
<ul>
<li><code>HttpRequestFirstLine</code> exposes is a setter for the HTTP method <code>setMethod()</code>
where we change it to <code>TRACE</code>. Similar methods exist for the URI and version
</li>
<li>When retrieving HTTP fields, the <code>HttpField</code> class exposes a method of <code>setFieldValue()</code>
for setting the field value (demonstrated above for <code>"Host"</code> and <code>"Referer"</code> fields)
</li>
<li><code>HttpLayer</code> exposes methods for adding new fields: <code>insertField()</code> and <code>addField()</code>,
and methods for removing existing fields: <code>removeField()</code>. Here we demonstrated how to add <code>"X-Forwarded-For"
</code> and <code>"Cache-Control"</code> header fields (and set their values) and how to remove the <code>"Cookie"</code> header field
</li>
</ul>
<p>So far we've seen editing of existing layers. But what about adding new layers or removing existing ones?</p>
<p>Of course this is also possible using the <code>Packet</code> class API. Let's demonstrate how to add a VLAN layer between the Ethernet and IPv4 layer:</p>
<pre><code class="cpp">// create a new vlan layer
pcpp::VlanLayer newVlanLayer(123, false, 1, PCPP_ETHERTYPE_IP);
// add the vlan layer to the packet after the existing Ethernet layer
parsedPacket.insertLayer(ethernetLayer, &newVlanLayer);
</code></pre>
<p>First we created a new <code>VlanLayer</code> instance and gave it the necessary parameters which are VLAN ID (123), CFI (false), priority (1) and the Ether type for the next layer (IPv4). Then we added this layer to the packet right after the Ethernet layer using <code>insertLayer()</code> method. Nice and simple :)</p><p>In the same way we added a new layer we can also remove layers from the packet using the <code>Packet::removeLayer()</code> method.</p>
<p>We've made quite a lot of changes to the packet. Let's save it to a pcap file and view the result in Wireshark. But before doing that let's first instruct the packet to re-calculate all of the layers' calculated fields:</p>
<pre><code class="cpp">// compute all calculated fields
parsedPacket.computeCalculateFields();
// write the modified packet to a pcap file
pcpp::PcapFileWriterDevice writer("1_modified_packet.pcap");
writer.open();
writer.writePacket(*(parsedPacket.getRawPacket()));
writer.close();</code>
</pre>
<p>Now let's open <code>"1_modified_packet.pcap"</code> in Wireshark and view the result:</p>
<img alt="" src="resources/PacketEdit1.png"><br><br>
<img alt="" src="resources/PacketEdit2.png"><br><br>
<img alt="" src="resources/PacketEdit3.png"><br><br><br><br>
<h2 id="packet_creation">Packet Creation</h2>
<p>In this part of the tutorial we'll build a packet from scratch, create the different layers one by one and eventually save it to a pcap file to verify packet data is the same as expected.</p>
<p>Let's start by creating the first layer - an <strong>Ethernet </strong>layer:</p>
<pre><code class="cpp">// create a new Ethernet layer
pcpp::EthLayer newEthernetLayer(pcpp::MacAddress("00:50:43:11:22:33"), pcpp::MacAddress("aa:bb:cc:dd:ee"));</code>
</pre>
<p>What we did here is create a new instance of the <code>EthLayer</code> class and give it the necessary parameters which are source and dest MAC addresses (both are instances of the <code>MacAddress</code> class instantiated with the MAC address string). Rather easy right?</p>
<p>Now let's move on to the second layer - <strong>IPv4</strong>:</p>
<pre><code class="cpp">// create a new IPv4 layer
pcpp::IPv4Layer newIPLayer(pcpp::IPv4Address(std::string("192.168.1.1")), pcpp::IPv4Address(std::string("10.0.0.1")));
newIPLayer.getIPv4Header()->ipId = htons(2000);
newIPLayer.getIPv4Header()->timeToLive = 64;</code>
</pre>
<p>Here we created a new instance of the <code>IPv4Layer</code> and gave it the necessary parameters which are source and dest IP addresses (both are instances of the <code>IPv4Address </code>class instantiated with the IP address string). Next, we wanted to set the IP ID and TTL values of this layer. As you can see we do that using the same API we used in the
<a href="#packet_editing">Packet editing</a> part: call the <code>getIPv4Header()</code> method to get an instance of the <code>iphdr*</code> struct (which is actually a pointer to the packet raw data cast to <code>iphdr*</code>) and set the IP ID and TTL values. Since IP ID is 2-bytes long we use <code>htons()</code> to convert from host to network order.</p>
<p>Now let's move on to the third layer - <strong>UDP</strong>:</p>
<pre><code class="cpp">// create a new UDP layer
pcpp::UdpLayer newUdpLayer(12345, 53);</code>
</pre>
<p>As you can see, this is quite straight forward: we created a new instance of <code>UdpLayer</code> and gave it in the constructor the source and dest UDP ports.</p>
<p>Let's move on to the fourth and last layer - <strong>DNS</strong>:</p>
<pre><code class="cpp">// create a new DNS layer
pcpp::DnsLayer newDnsLayer;
newDnsLayer.addQuery("www.ebay.com", pcpp::DNS_TYPE_A, pcpp::DNS_CLASS_IN);</code>
</pre>
<p>Here we first created an instance of <code>DnsLayer</code> with the default constructor (without any parameters). Then we added a DNS query record to the layer using the <code>addQuery()</code> method and gave it the query parameters which are: query name ("www.ebay.com"), query type (type A means IPv4 address) and query class (class IN means Internet).</p>
<p>That's it! we created 4 layers, now let's add them to a new packet. Let's first create a new <code>Packet</code> instance:</p>
<pre><code class="cpp">// create a packet with initial capacity of 100 bytes (will grow automatically if needed)
pcpp::Packet newPacket(100);</code>
</pre>
<p>The value 100 we gave in the constructor is the expected length of the packet (in bytes). When we give this number a buffer of size 100 is automatically created and will be used to store the packet raw data. Of course, if our packet exceeds 100 bytes this buffer will be automatically extended, but this has a performance cost, so in applications that require high performance it's better to allocate this buffer in advanced (meaning setting the buffer size in the constructor). Now it's time to add all the layers we created to the packet, we'll use the <code>addLayer()</code> method for that:</p>
<pre><code class="cpp">// add all the layers we created
newPacket.addLayer(&newEthernetLayer);
newPacket.addLayer(&newIPLayer);
newPacket.addLayer(&newUdpLayer);
newPacket.addLayer(&newDnsLayer);</code>
</pre>
<p>We're almost done. All that is left is to call the <code>computeCalculateFields()</code> method to calculate the layers' calculated fields:</p>
<pre><code class="cpp">// compute all calculated fields
newPacket.computeCalculateFields();</code>
</pre>
<p>Our packet is ready! Now let's save it to a pcap file and open this file in Wireshark to verify the packet looks exactly like we built it:</p>
<pre><code class="cpp">// write the new packet to a pcap file
pcpp::PcapFileWriterDevice writer2("1_new_packet.pcap");
writer2.open();
writer2.writePacket(*(newPacket.getRawPacket()));
writer2.close();</code>
</pre>
<p>Now let's compile the code, run it and open the <code>"1_new_packet.pcap"</code> file in Wireshark:</p>
<img alt="" src="resources/NewPacket.png">
<p>As you can see, the packet looks exactly as expected.</p>
<p></p><p></p>
<h2 id="run_example">Running the example</h2>
<p>All code that was covered in this tutorial can be found <a href="https://github.com/seladb/PcapPlusPlus/tree/master/Examples/Tutorials/Tutorial-PacketCraftAndEdit">here</a>.
In order to compile and run the code please first download and
compile PcapPlusPlus code or downloaded a pre-compiled version
from <a href="https://github.com/seladb/PcapPlusPlus/releases/latest">the
latest PcapPlusPlus release</a>. Then follow these instruction,
according to your platform:</p>
<ul>
<li>Linux and Mac OSX - make sure PcapPlusPlus is installed (by running <strong>sudo make install</strong> in PcapPlusPlus main directory). Then either change the <code>Makefile.non_windows</code>
file name to <code>Makefile</code> and run <code>make all</code>,
or run <code>make -f Makefile.non_windows all</code></li>
<li>Windows using MinGW or MinGW-w64 - either change the <code>Makefile.windows</code>
file name to <code>Makefile</code> and run <code>make all</code>,
or run <code>make -f Makefile.windows all</code></li>
<li>Windows using Visual Studio 2015 - there is a Visual Studio
2015 solution containing all tutorials <a href="https://github.com/seladb/PcapPlusPlus/tree/master/mk/vs2015/Tutorials.sln">here</a>.
Just open it and compile all tutorials</li>
</ul>
<p>In all options the compiled executable will be inside the
tutorial directory (<code>[PcapPlusPlus
Folder]/Examples/Tutorials/Tutorial-PacketCraftAndEdit</code>)</p>
<script>function topFunction() { document.body.scrollTop = 0; document.documentElement.scrollTop = 0; }</script>
<p><a onclick="topFunction()" href="#">Go to top ↑</a></p></body></html>