From ff75da9123be8c484af9e6ceee33290e7fd1122f Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Fri, 22 Feb 2019 21:51:11 +0100 Subject: [PATCH 1/6] Fix windows warnings --- scapy/arch/pcapdnet.py | 7 ++++--- scapy/arch/windows/__init__.py | 4 ++++ scapy/themes.py | 26 ++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index c336b5d85d6..49efc836beb 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -159,9 +159,10 @@ def load_winpcapy(): except OSError: conf.use_winpcapy = False if conf.interactive: - log_loading.warning("wpcap.dll is not installed. " - "Restricted mode enabled ! " - "Visit the Scapy's doc to install it") + log_loading.warning(conf.color_theme.format( + "Npcap/Winpcap is not installed ! See " + "https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501 + , "black+bg_red")) if conf.use_winpcapy: def get_if_list(): diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py index 630240db0d9..f476e8be27c 100755 --- a/scapy/arch/windows/__init__.py +++ b/scapy/arch/windows/__init__.py @@ -593,6 +593,10 @@ class NetworkInterfaceDict(UserDict): @classmethod def _pcap_check(cls): """Performs checks/restart pcap adapter""" + if not conf.use_winpcapy: + # Winpcap/Npcap isn't installed + return + _detect = pcap_service_status() def _ask_user(): diff --git a/scapy/themes.py b/scapy/themes.py index c81712d70cd..6cb18979c35 100644 --- a/scapy/themes.py +++ b/scapy/themes.py @@ -17,7 +17,7 @@ class ColorTable: colors = { # Format: (ansi, pygments) - "normal": ("\033[0m", "noinherit"), + # foreground "black": ("\033[30m", "#ansiblack"), "red": ("\033[31m", "#ansired"), "green": ("\033[32m", "#ansigreen"), @@ -25,8 +25,20 @@ class ColorTable: "blue": ("\033[34m", "#ansiblue"), "purple": ("\033[35m", "#ansipurple"), "cyan": ("\033[36m", "#ansicyan"), - "grey": ("\033[37m", "#ansigrey"), - + "grey": ("\033[37m", "#ansiwhite"), + "reset": ("\033[39m", "noinherit"), + # background + "bg_black": ("\033[40m", "bg:#ansiblack"), + "bg_red": ("\033[41m", "bg:#ansired"), + "bg_green": ("\033[42m", "bg:#ansigreen"), + "bg_yellow": ("\033[43m", "bg:#ansiyellow"), + "bg_blue": ("\033[44m", "bg:#ansiblue"), + "bg_purple": ("\033[45m", "bg:#ansipurple"), + "bg_cyan": ("\033[46m", "bg:#ansicyan"), + "bg_grey": ("\033[47m", "bg:#ansiwhite"), + "bg_reset": ("\033[49m", "noinherit"), + # specials + "normal": ("\033[0m", "noinherit"), # color & brightness "bold": ("\033[1m", "bold"), "uline": ("\033[4m", "underline"), "blink": ("\033[5m", ""), @@ -73,6 +85,10 @@ def __getattr__(self, attr): raise AttributeError() return create_styler() + def format(self, string, fmt): + for style in fmt.split("+"): + string = getattr(self, style)(string) + return string class NoTheme(ColorTheme): pass @@ -88,7 +104,9 @@ def __getattr__(self, attr): after = self.style_normal elif not isinstance(self, BlackAndWhite) and attr in Color.colors: before = Color.colors[attr][0] - after = self.style_normal + after = "".join(Color.colors[x][0] for x in [ + "normal", "reset", "bg_reset" + ]) else: before = after = "" From 776fc1cbb6106eb1a6fb9d7a289994f397c79760 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Fri, 22 Feb 2019 22:43:45 +0100 Subject: [PATCH 2/6] Improve doc: timeline, contrib.description, remove scapy v1, fixes --- CONTRIBUTING.md | 2 ++ doc/scapy/advanced_usage.rst | 8 +++---- doc/scapy/build_dissect.rst | 17 ++++++++++++++ doc/scapy/graphics/scapy_version_timeline.jpg | Bin 0 -> 69112 bytes doc/scapy/installation.rst | 22 ++++-------------- doc/scapy_version_timeline.ods | Bin 0 -> 3745 bytes scapy/arch/pcapdnet.py | 5 ++-- scapy/themes.py | 1 + 8 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 doc/scapy/graphics/scapy_version_timeline.jpg create mode 100644 doc/scapy_version_timeline.ods diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4dada1a99f0..061d1eabd53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,6 +97,8 @@ To be precise, `scapy/layers` protocols should not be importing `scapy/contrib` protocols, whereas `scapy/contrib` protocols may import both `scapy/contrib` and `scapy/layers` protocols. +The detailed requirements are explained in [Design patterns](https://scapy.readthedocs.io/en/latest/build_dissect.html#design-patterns) on Scapy's doc. + ### Features Protocol-related features should be implemented within the same module diff --git a/doc/scapy/advanced_usage.rst b/doc/scapy/advanced_usage.rst index 4d1573efab0..39797ee7b38 100644 --- a/doc/scapy/advanced_usage.rst +++ b/doc/scapy/advanced_usage.rst @@ -906,7 +906,7 @@ To send data through it, the object must call its ``self._gen_data(msg)`` or ``s The Source should also (if possible), set ``self.is_exhausted`` to ``True`` when empty, to allow the clean stop of the ``PipeEngine``. If the source is infinite, it will need a force-stop (see PipeEngine below) -For instance, here is how CLIHighFeeder is implemented: +For instance, here is how CLIHighFeeder is implemented:: class CLIFeeder(CLIFeeder): def send(self, msg): @@ -937,7 +937,7 @@ A ``Drain`` object will receive data from the lower canal in its ``push`` method To send the data back into the next linked Drain / Sink, it must call the ``self._send(msg)`` or ``self._high_send(msg)`` methods. -For instance, here is how TransformDrain is implemented: +For instance, here is how TransformDrain is implemented:: class TransformDrain(Drain): def __init__(self, f, name=None): @@ -969,7 +969,7 @@ A ``Sink`` class receives data like a ``Drain``, from the lower canal in its ``p A ``Sink`` is the dead end of data, it won't be sent anywhere after it. -For instance, here is how ConsoleSink is implemented: +For instance, here is how ConsoleSink is implemented:: class ConsoleSink(Sink): def push(self, msg): @@ -982,7 +982,7 @@ Link objects As shown in the example, most sources can be linked to any drain, on both lower and higher canals. -The use of `>` indicates a link on the low canal, and `>>` on the higher one. +The use of ``>`` indicates a link on the low canal, and ``>>`` on the higher one. For instance diff --git a/doc/scapy/build_dissect.rst b/doc/scapy/build_dissect.rst index 33669987e0f..0c67060b40e 100644 --- a/doc/scapy/build_dissect.rst +++ b/doc/scapy/build_dissect.rst @@ -1128,3 +1128,20 @@ The goal is to keep the writing of packets fluent and intuitive. The basic instr * Use inverted camel case and common abbreviations (e.g. len, src, dst, dstPort, srcIp). * Wherever it is either possible or relevant, prefer using the names from the specifications. This aims to help newcomers to easily forge packets. + +Add new protocols to Scapy +-------------------------- + +New protocols can go either in ``scapy/layers`` or to ``scapy/contrib``. Protocols in ``scapy/layers`` should be usually found on common networks, while protocols in ``scapy/contrib`` should be uncommon or specific. + +To be precise, ``scapy/layers`` protocols should not be importing ``scapy/contrib`` protocols, whereas ``scapy/contrib`` protocols may import both ``scapy/contrib`` and ``scapy/layers`` protocols. + +Scapy provides an ``explore()`` function, to search through the available layer/contrib modules. Therefore, modules contributed back to Scapy must provide information about them, knowingly: + +- A **contrib** module must have defined, near the top of the module (below the license header is a good place) **(without the brackets)** `Example `_ :: + + # scapy.contrib.description = [...] + # scapy.contrib.status = [...] + # scapy.contrib.name = [...] (optional) + +- A **layer** module must have a docstring, in which the first line shortly describes the module. diff --git a/doc/scapy/graphics/scapy_version_timeline.jpg b/doc/scapy/graphics/scapy_version_timeline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e50ff8da249ab7aeb490766655dac4e5d4e6ddcf GIT binary patch literal 69112 zcmeFa2UJu`*Cu?BoJ5ce(nt`L3<46QQ6!0oh-4&ZB!i@;8&Pr+P*4;aC8>ZQQG(>4 zA|OF>4iW_E25lNT&GC)jd%f$wI%~d}f7Wo|K%MG+s%k&AtM;y4p-E$;IpD}eEnO{u zjEoGh1%Ck2Jin`Eu$wag=<5Tg005u?r~rE45EwcEKG>770OVkp4EzDeWd0Q&OD6lb zFe^y&GR6&{_!|vC)3(kFP=j#{;Ijz;Fl+IygCQF5KDZ7LI6&Y4fdd2%5I8{K|49T) z-Mk#VgZ%lgxOxY8^J_Y~1$g`M%ScN~OUg(~%g9K|D9D{T4g4CFth9`hvb3zStSrB* ztg@VpvaBKiP~9c}r&Yqtdza#0Vd=EgzlF)Ar2!xCvVU7Q|F&FckNl{f1Iy+A zNg+L8`T&6g1P%~5K;Qs@eoIN!-!X1pUVb4-5q<%2L0(=_#gpRFGN(_UhKMMgJtM0s zDR)}-M3Ny4p1;tvK*H=M|ITDj#|K% zRrXfu6B@zuuUgrz4xohO?ENBX>DW0qxwwT-h=`s%by{9Q@r;tP#sy6+Z5`c<@e~gM1yLpdhE9{=pa7p&;-kXQrS$EE89*-wHmU|_WvxMUg zH}LtQf@=BXS>?0Lf$}uaa@KdRZg-prpS*vpmMNWJZR8cY9K%ZHVS>K=oCMHVWe(gK z`flhiT;&_J;}F93J~4xw#y8EkrT6B?SHXgQ#tEWAWg6Y?KsYO4s80Fg5ATVh1u14X zBL7HHa{nf`!UN_41~#~Xq@sPymK;M}mrM&+$sWveK{u$^yU6lX$$%~^Tiw?3K zEd#I5FKl`hvcq_fezACF&U(Z5SaaWUba7=!2m{YuS4GjMc#j<(p2B?d^^a9n_Z8Vs+O;cGMOH@<|4~dj!YNi>bB{N5Vm|> zvXVA9Ytn>cD^0hGEgw1w z;ccT)IyVs^wywPI7W!uT{@2ptf;TV3dIFBux(@lg?f)9wZ%}%^Ni5FGf+xxJdaczx zEmw);6)J zs?4dLyhzzppkr?Oz$bODMrS*O?3~hKZ7JZ|>hVmiILvE;_++^)wPHa9hM%k?0pf3> z{7v98@hb+A3;KH3%f= zrI`<#$<#wLuPMMx_pNAqp}~!%tCJo`xB5ZPuF4n1?$WfkWLhqS%$*cqk$ar%I~XCw zqgNdJ{q@W5kJd^{r@9=ZCy%V!Y4LxS^naaFU~i5sr1g~8b?2T^WH}{k8J;t%W_j#M zmp$Y}a4#UyoN&XsOXX3Lhxx4uwyX;ZBTKoWynX^pN3@u>OS$AvOcv_#l@*^>tb9OT zrSN50dhs)%k6LYU@-&&($90Y%?#I+7!w$Hk8#~nF53TM`L6=jgDRuc4p4-^Wx@A#4 zLP!=DTlL%&w2`DK-8RouWfCP`Bhy;!{P?7^sraRpXF#%kxg}%b;3+nxgsnr1f)hnH zY;IrCFM<=yy`^){OXRjwO-GXe9ne-Z^#(Z9X7i5CQYo4^|W06nO5rCHPZgwrGP<^`StgbGiOZ}C=~tePbbS# z_k~}}%JQVriJ^U_4VPL39~qx41kQH{_1xzV+()Z(@YlS_X*;%`-yQfm5h=jC$?xdm z?5le?**5-6UOkyZlD*A0ox{xPKzQG6@@=+E6{YaTnD)Zn8`oOHmf3I(68u(ys&;4l zKf4Qm{3K{L=z5ikw5CDu#XSlWzk@vVFrCURk-zDqnm*Nbb$lcP&zG+dq-c zW6j_MVb^_ftkSuU{Po>)B1>PQy+7;^{CJ5flRs{O!(+LABLK(C>D0n0=psm$w@O{0 z%%~RTY*O2_9e;?m+wIUCuM4Mv-QM3aDkXP!uo{*r?ng@UB2P%dcX6jzhJ@2xm%|2o zd8ww!YGT~GbcEH%6P--A*{nW%7yX#H_FOGDpxZuw>WQdy{&;IQ=i<`0g~$7_!Pt&W z_XXLDUR4pBBF`ZrTzL<(2qqLMoXgOYIDS6Xy zp+BR?1vr_Se7Nt-p=4)>@hzd7yJ%g%MbpQRGH3avJI<*XBkES4J_%QFlcdb{xj8kr)^m-nlUtPE zclVm_`ScUPuFq=Bhir$hdri=RRe8Yg1RKI@6KmlO$kNm}so9V>Y%*?2GZ4ah-83N4RLb_CEchmnHPd<%V^H_f2e1fx@G_h2?2~k?VP>g*4G8}0`0&;U6k$}iODE7N+$T>_nf*2X< zO{8BU0gJXl{JG^AQ5 zac6|C9@cWI%JC%EgD7D?Dowz1oCI_yH52@7mW@VS%2}_FT{d{5yWtQ=WOzd>`b;eV zG$wii`x>74gGy(f1dJDJxV$m$+Yes3Ovl?m)gP)5Ht@lXkWT{cfuYKoWLx4O;{EAF z-m6AuPZ_O5X8JsRXr~E)>6%oL04(01E2@r7jY_+a1h_A3ZIA$m?WS>sS-G2AP2s1u z@Rv!zI)ntIjKti7pjAk~GXxQ;{jb=M)TeLEWnLL!H!ra^tO>&wWOzPkrPC3igc~LL z(BVu1vTi=|E?V=X$gt!aB?{;DJ}%Bpyx(=1sV4*2efLG3Cj)_A^doN{aQI^ z&(o>?7tWji&A82bp0lgtTiVhHH>qXDobS%9zQHkSRYSuDe1&x(LC3tZ3Q}JutEjj+ zge;|6P2E4^ERtHjzduX@+&+$^Q)Mcz=LIS?uow+Y3=FV6Zh07z@0HMZCs5c!7HCN8LREyh6Pl~OJzmF*V zFxUOAdq=H`?3H((0b7+}RqFDQldWf?bP4wrcZa?|TUKeR=4<`?Lmuvw_g2vtZP9S| zv$y{qzF2S7vJwif@~b#CnmD&mpF-@-#I5ZQ5FSZ^Z$Dph$=rbW{6}0~YD{_f`|?%) z?q}0h4;{p0a{(7YukbhPqnI1bvj!tbK_=UFoPy1djQ$>z3C7@J{vJ-*7)LZAOL-H| z-u?<1uOI>8Pzz|K6LB9bf;5KdHNv@v@QvX5j~w5&kEzZ7y(oGANb)bz%9wet<811D zmAXKl>cOIBkw0miG%vH{8W7;4EG6aTFKOrKZSN%M;O!|DZ092-BPlHfs6vB%>>NCt z0{HEnT-?0QiflDBi}1TSo)s~b*O%7!(QtBg(+Tx+G77zX%^}poLCH}Bs>VpC8mt`b z>Er1XV8>1k_ah9G#V~UeNjn1^9GU;2&iP3JQ`8l9TlI zbCHr!Qc{wVmX(r~l>jLu{6o9~?1Ck{{00A!zy&9N2R}ET05@+h{vQe2*?R{DoD~QR zbaPa8wsV%ZcXX7Ou(y+Ql#r2ew3D!Nl#`Kgl#_E*l$SXp>nvk0@XrEz`ur>VKZOF! z4`fMM!_UbMoFcjA?R`V-mu5v&rT+NZzteHLU>D$|_QPo*AuTH*D|-!`Hu;+)41L6mU{)ILOJ^%lP>mZZg0{H;01Gs(*f!`uNkgfx`ehY!$B0i9= z1Gs(*f!`uNkgfx`ehY!$B0i9=1Gs(*f!`uNkgfx`ehY!$B0i9=1Gs(*f!`uNkgfx` zehY!$B0i9=1Gs(*f!`uNkgfx`ehY!$B0i9=1Gs(*f!`uNkgfx`ehY!$B0i9=1Gs(* zf!`uNkgfx`ehY!$B0i9=1Gs(*f!`uNkgfx`ehY!$B0i9=1Gs(*f!`uNkgfx`ehY!$ zB0i9=1Gs(*f!`uNkgosjaQ%D7hLab#O(O{0kMXzj3CIr}`tb{16yPr<&5xb_lyub8 zR5T293=H&i^z@8}S&lFsWXsW_ZzDEV-y@-I-hKKfX^mXKQ6GidBr>Bh?7#Bi$69hiOj7@5!+kdD` zZ<)|BT&fyLG2@b9%5aYQUHX*6Op_D1hp`$yXu|?^86&cbv~RH*%s(kncsAPWO>vd0 z5m^yp`;fVuI$MOCt6!-lm+51cSOQjLfu01gwd zwuf=HKJhBN<%`f`ZU~e`N}RGtoQV`0Uhd=NoRU+oxTaVrZn`!smgevRPR379(yUGo?@VAwY4eK7y+x3=f57TrBZT@Y{$Fw}(Y%_Aot&Br7_U5is9Gmg5K zF6wx_EJ=CjR5RS`&GYV5oT5uq+@2sc-$I1i?Wc``n9taR1*tnEpaR*!#D+Dx>M7X4 z9au#*@?B7uTVVG}SBF?+-Nc3xugh7F574;`C{Lp&dIb|QDnHBTF^EkttwhUrAsMFz zw0UCLM@BR?UR~oo{nAe>1j9;(k!SsJ?#4fF)BD6Bn=1BHoD7Wr6D+J`ZP%iN-J6%$ zY~o0n>Y&2#_3kLHrP?z9%7B$@{hsO4Y&>+X6v7wqh^FzP8qN4>&f$mM>0vL-ZK}MA zzTOIauqd?T$AP?vGsTDyssf;sJguPx-)ruR#58I@eaCB2cyBw-5Z2PAWDjF}JIU)u zMoFW@DMY@yFZ$!4lK+3*GOE5LK)@#UOg~=lt1`HyS)uGL1S`;0;8KA1K`&+2nqX3q zhXK9?^$28DhY#(Uve7MG#M{h;!*V2m1()3Xy|)-8g<&G325e1|FEGWzRGtSXueAHD zh1w3YdssR>%vbZ%u4QN;ixz=i1iqDF#h`I`LG)3~i=pEv)tao4=jw0tslp^9p?}rZrPpB5|{jN5v(6A&%G2DcR3s_)NWxCp7m!OqkXAig1R+ zfst8ygd^ez{wivD!`Y+s1Xlo0hOy7kbqdRnLyw~;KhsM4Nyn&~uFOUeg@~ibV^I59 z*5LQ;owJkNmb2Cq>tg;MMXe&3OC|=G3=`K;ze*LUo}JqqJ@I>$3sReg&tQ6l6gV56 z)1b;~-GZttU{u3H&VVjLoXtngmhuC7b6xe`;$066d;h!;;t&b2J!{$W>Z$m?_AT$l zdKsUxfRVI!K8whO`%7oTzUMN%GZ@o0AQY5Mc7aUx?YN!-ZlD2Pp!d>>8-AfPi5HS*&TYm=t6RVxa=T?DwsMaB{^Bg{xsZ9Ls2R3_+~`jM zL*A^F6O-p(RS%fUo$k3aby>kdSc|Nz5<2&wxrQkg-0f`;(At+aB%C=?Jvce$RoyVbhNG*H}RgQ5xku(ZQ2TQr}3Bsv}vQm zAUkV?;cA3l;07$5(5NPd4*X`Fnq%+uKzpAul@h z&m>?jH4_!AjroXXPgxMrk#8uazcOThwC32$!Oyfp=a38I3`L`K;a}FFSP6sJfJZjt z{cTyo9VilTVFKi#b$Zp-?FP$YLXDPuixSK#A_>hAU)iKFw0l4bK)i?iOH zh6$GDfjqYbE3|~~3b|g;XW2;f+6$7iYdHput+L#tCep+dw8N8F)AiY=d|$UR>y>f| zLbXg7Nx-49+~&CyosbE4T< zlpNWFMa-8Pb&l|zdh;;=*UaLElCRErezxLGXXA|^*{&rgGPMU~J!M#l*#uR^pfZ>| zYV&oZH`{o7U`UVv*=G%p<4N{fYKiXzflyBoY#q)4Ge)SWVjAa|pPdw9cF&Rf#Jo5V zaCP}J8h{xP#8Nm0DhukKnSS;KX_F`-AFF&UH?@{o0L+q{7Ce0|bw@wciqlM5pIUl}`8S`CJO`m82j{}JOfF%Jf z7DcTHdROLk3+JN6bEO5J@)atDYS~BHxGsF?)IF7WNrQUp$l=3%YPy7?Chk^r0+B9c z0YYIltCs0qhE3}?2zz^1XF&Y!d3s8b^vx`s%X}@KIc44;DzL{>yOU37w9$$6Fa)QB zZ%}dbozRb{<9q)Fo47>2LSX7dayJ1WP*Oxb>}U&|zYeuTUpD<_TOwZ$I_7mEAlhIi z?}mXAG=`_~`&qFqj~EHSk?R_*+k~U97mtgG=Y%=GxMX8%7*cpu@{!g>kwb zHkAf#&((3)m9Lbj*ZcxUN)FUGR-46ejB&8T5yTFXxp2$w@qV@e1=PMO$B zfUtA4@QH5+6iY}FNr3j?%BxDVCoXrjooG@Q-}-QtOOBloY>?m1jMyNa)yH{QO=54Z z87AoBm{XAV^18L$Fn7Ss z>!lbKzmIK)5tBF#rYm!}(f6|yUV_zN4feZBolamuvIXBFS8ACW$&Ali*9F4CVxB{9 zk1q5g@hmId12B)e=&kNn2i%3!5XgFSmF@OzP9QP+HSW!;9Df^TcxR3*p%A8lDzm_P zqNrNT%{z+_hjm;$~9Ol_lEFsB!CYWVeoiqo0XS{M6_OPUU~}`1=U+=5iw(O zG!d*tHt5m_>>6U-zn%mP@^Ay&&0~U?Ic!sNRT=)IL1N~R`{1-*pl!;tcJ3Ls-H*j} zTB*Peb7QS_kl&SJ%DMe5JgWzWI7$Lsvb`^2y-_1GT=<~ppv~CzL|kBTL*{Mgah<39 zF%RF0Fwl^Dzn|=y%`Cd!jIscZ+#&&N7jw>hah4-^$YDM-rnDkZCK4jo(SqU@tQK3* z-yyR8u_J@0t660Fc{DU7!+c!HE3pyHwOcK-WO`_*HTqtqTTN`HMRZQO#r1bCT>M8R znlme1g{ZcBJh?;w7PV=@Erdr6;iVdjm3vm{J?@}{0KKS+Po z_8}?ZSJeg^5AN!?1R8Q{?|3DwJh-PGlh~vcNgTHU&qLYpM(jH&J)>nR z;N-2dC`lg3hmX`7+{$m`*EPf#B9)oVaugHVLCu*sYaRTwuWv_l=*qfW!la!~DvkET z&v*Fo?`nPc9htl3z1x*Yz#Z?4ef}7o`tP{NVr-=`iV*#v1}@YzR4Oj-LhLoGR?16O zsZF)q1(e9f??KQKi6_$@+t^IQ>5c1{bq53+Opv>nMG}(fMHuRPys( zpol?1MljX-2~HEy3PrfMU{w~TH5&yBu_rvC`mY-ktcI4u{9V_$oNn^eC@%rVxB4zi zU-no)UyTV?yIa z!Q=A&XK%6SPjD5O{Kv8rRl`lkv8D#~(Wa4RlGP2a*+LrC1NR>v0Y^~VOcuab*D}!R zj=qc4BLSM)tqAND3k3Q-bUt5=g#-*3qHKt)>`1&{#98+EoieN&)1BsT*I*nZAd0ex z1n}k&xi#jc2y75a5-@+x4Lq$!lziYI#RD7DoNh@+BhTUGT9CH zUizlyRHJ7%q(H;=TO%PwHE_Xz`8sxniIvgFfE#ubwN^N3zgK)>yUTQCiQM)37aJ`^ zyADj41U$`~YsQO8K(WgZ)F6ZwvHA=PCPGJVKvYuaOE4!23R_cfFdM?L0eBVcL<=HQZ*(D!l1mI_gEwdEgn&XkR?CC{oS9M zN)*%g@RZt@Q(j7JjGh$~vP-cbtbD3!xbCmlIOP4rGC}OE^-Qqknc(S*7072orrom@ z$hQ9ROw9R5*7c~&&R!1V;lTWOv4mdlj{Sh-)Go6-DLpi7x;Gv#rE%nlts;KvcXFBf zC5(tP4GYIax)cYoiVLd)jVb|sxBDvZS!;WB6O8u!PW9YsRl4H)(R2_})4jnQf-`r8 zFjjqmaCgstM!`4~K3n!s)p@*CzO0=~l!24AZ@z@CPgg)i_Cj6P~dG zdovIsw?X99C&y{O>)Nb&%B0vc-K{?v6z1{RzH|;Lm zHc*(k*yf}p^mbh0b3jYiOp$6>H~w@?o6Y_yHPE@(7CA%$lEFj1{#ar6$hJ*4!FY;i zgK$}xb|d!o920wO1YSQJIqVJqs*W34~Iwj|>Im^4OX&zB113dGtDOx0P z<9mM+lwC^I#q-8B`K0IBpX)T0n5&2T%#S`hZhA(9p(qR2{Bw8yvu*&ELA#Y{yn-nO zSy`BeD%KkI#0z=|jCZ@qPK1q9b+U9i1z25A}kjX@brSM|wz&}Cp=lX>8;Jgv7id#^R{`gRv#8Man5)FTs zuU$}`xNlca?Kt>W$b_X(ZpHQ`G)8HKyd{%Jler*@iNb!Oi<(SyO=IxnwAl;UP!2U+ zZT_{2{5%VeaW@_zZ~`#sC`=Ktu(PoE+#TVqYx1ddkZf7Nlg<&YYn7fl8NaXG_~n;39-W}gaBb6F-&STTbPb)Tm)X&;KbqZIgpN`Iv06eSSeAE3h z_-s~lv?@c}iZPn))14rb_qjqA*>5!+DUN1O=B389G72X8+EgO`n5>OBRs$1C$W#@< zX<6Z25lQUIqw$WzFXgY*4C74;-?{Pbc=b>kAHM0WLWansA3YSI_6{33m%tRALxx(p zQ&l}#FgXY9tUAztx4XWGy%h0zVeg7-7ZiskwhW4}sR zYnpSfNlFX4DYESyTB^|%el=0wJQjvAeoeRwXK{xgu15=5i{#)e9+`SPyU| z-InP&wyo&3xaGj>xX<(xx__!WcVM%i>Uh`eMTXuY9*w2U#AK}_#t`E%AKpf2R)RG+ z>~eFqt-)qe14EBOZbrEk*-Wkb2itUUJ8I30TGmEfE~B-dxirAiFwD~F^ZwearO0#N zn}6zG|7=LK(>6+^*+WZpFt6=H64cHRirN$;3wcEI>gt$9IXisb@h8hgstca!*BETR zphkwLMip($qWUkRv~O?b$6TtG5U=H6;0vppgI~)0^X!;6k#;f+ z$A{NOQ^!j-`cuary*k+U@K}|ygC!f4>(UObY%_j`-1XQ#y0?F_m`J-9g0siAw@oY; zCFP>Z2gP&1V=0HSRz8@d!%-LF-oE36W`~3R+Mn!`pEYYxmeYfwfJr^|$<9Vy_6-M> zYYdCE7tv)CIE>|Bvj_6d+T0rh^ZJiMHN%3R#H;CDGi)RSl6AVU?|43z^wBUBol#n- zNB$C@J4mqFBUSKMKP@I`H(_Tfx1txk(|BTE=F=wMQFjz&!qYWC5$mp-h-)fa<_D=x) znJIse;{T@&{C_QufqX~NE76VNF7mU{s+#%O5Bn)5W`j-(P@DnNPv$%xYtA!E2ys>H6yJ3$==TF8gstH(^z{@va$} zbmY)J)`3Xdjl^2nR3ohrZEf}Yi0;m5AgCGjC1pH;7>6X9em5ZM<`Iwi;$#p5-~{S> z;&T%4PX4^#3@DDcME|EY_>tF04oOP zMPCyhG_O7+0n3PMBw#5EIzI}j*^N_;j0Vx&j(Jx`L;)ax3M)ueQCa4>O!c zObRY~cVj+%2hR}(J=EVyUFN?>ZDg2CG{?!`a#MIVu;{M==_&WeTgE)6K9ze6@eAb% zt8MM)_Lx1=#Ji@v1y1V*&FRAzd-Zw`VGK`al<}594g-fvjwshq8MnS1j8HTFi6O{; zLR0qQSuWFk#D8+5{Zm0a)?bK{FWX=PyEi&Xz=eJiAhr*>!-NKYMrQv-Jg{f_N5oGf z+FwGQrS?xC_G~^u_f9muc=~^+bd9yfXlSPn!qt;U;}t6UkuNIE^W;i1T5kGTL*zFu zg^pJt5%HRGqp{aTm7%a~JEi7fRV^J|BjJP^|> z)bIbgnI6sy=gTabw$^WWR~uIto+siSc^OJ(t%Q?=kIXOtR7Jjv3aW$lNU9QMiU- zb$yhi@D3a7$;yR@?EG3TTous&Yt6C=N+^MP_H*KSu@9NrQ=*Zyf!=XyV#+r{3qI5g z^olDCsMP0f&uazsXN(Kw@`iu)AuIh$VYfEI2_sq!sv(IsTu-p(Xpxqlx&EWq-$}fw z8reK`YO1TG*ZVX}61s*hLM5d)LcP!98#ogA%f7f2Iw^vg8h|?SoaNA~9q>|hSey>8 zS+hP?>6iHBHGi5GUB{tr_ZQDyY}LDHd4pOI6E2ukG@{dhX%(E0BvXuPlqlf}^0+yu zON?OWNWIjO!CP_4u7!gj8@;2L zg3bP~*}~!t&JRb^68hx2EeK|1(a^b9cDk6QAIo6R#u_-b{?DbVEn5517`Q?-u1f!U$a zSW^_T3ogg*aK)QxbvHD(N;bsafkA>_f9y=(lnZs4hD{FfDA<6X;hoWl+gqwm`S8BV zFEcDdOTk-#60++HI!{HP%t{{jY%snbCkXIn0sAU{rK1`Q#yKzzsM^i2IlbPXb5(WV zbhv_3?eMp-n!}jBmV2ib$p#{BD3I-$9g(r#{557?!+fxvPw`|})3RV(hso|}s6rC2 zpOr@8g$gURy{p1BCGU$qr-qgTjsqy@B6|WFH*bA=aqIBptcB~u2R~i!!$mCJK97Wh zA1D{grerCsb}-*o=L5f^;V=4UD+vCm3#Jg8fYT1nTVzBgf7c7wnr$C7@hB2f@U?tk zV%orv-$gYaYm}bex{`&<`UQFYZ?GM5%qe746KRbplc@~Y_7WDM*i$Q=kCFsdI*E)} za(FZ2<`@a+)GV=_?x9HiI>Fn^EpE6p=inc4S=L`2qWcTdWQ^k^j)p=vZ=puMbg+jy ztSa|C^b`NCe{y-}!c{hgAtQ687T1rStSP!bHN$aP)7c%WD z@Wd(6md`9gIS6!@I4k+oCqPk%v%+D8$x$xm5KCcNsI;Q=?<+r2pzt#9RaKU4ye8Uw$^|Mjs8`e(?qfL+K>E&fl8$}U1$^W4)=mrZ6g9d5oq4$p%?eOX|9@xG^E zM#oV%(OkBBM?lr@8O1%P)zW^>vhvkmwpJUZFm{A;JR8m&(FT662MrwgatzU?s49J( z1bA9>GCgqGQFws4xCg1f@pVF**I%92;3xC==W&Bi=;)?Cn!TS$7ncX)Ot@BS6SK?S zkU!M>SaZC&Qn+sFL>CIy34Y%ZXJ>ftuXP=tuKkk9^y?!V8y*;(P|+k2?7h$&3nN3t z%y~Uue1v2wflhF5e!Oc=oyPh=fC4yLqxps3{#@IfV}$oFG%yhyIFH_Xp@>k>h$zOZ zpff?!be;j-gk-2J@zo(-n=!hR#h$Frck&9dUiYmDF5i|=`fC7`z=t4^D(`XR+{l~7 zUVCUYA&TOrMR0nL$7IohsD|yLNZOc+v3a>~{N4}-w2dQgd(2iBb@Vf1HLOs^$Y`O# zfUf2p&#(DGQ*rzUvQ1?(p0%Nac#@Fo31`CF zV0~2S7c9q{-cf*t#GWaoVmEL3TiQwfu5q^5z4XY^*Pz+|YhY>O9N^UmMkb>yW(Lz!fqvt=W1+I>QBU356-& zksVd)BCN?-tw|m3?74A2li^G`+QuPbHC9sltg^$GvY&c`(T7+OC<1vF(wwOYd>mtB0@Lfec;&7Gbzw-`PQ7K_dxqAbCFb>T0o$PJtpzsi)t zCrii%myZSGjpjVhOUPNKTU1vlvP^3Gu#4!LM~{~DV*j?hQl ze7@N0eP!5wuxgV@m-<7_!iChXg%=-K#SPw?D7ADe0emihX)oC*V^fH9Dd>bbWI~l| zlMIX&Yz_|X`+4lKok9avoVN~XB_U4==GtC_72SVlb=xL?pRdwoL4v*AWau*oI|l|m zZTW$WY%!1_>2^Q=?Op%|`c3}AM!TN58G;__ZYINX<_J zVC)d%by0;}1okceO2pozVEnPqL(mO#F}LwSh)RzN}f*;@Jg>`V{^t*k?DIAsOyC#kS^c zu~j@7F@{Mab(a!=efmrEY>Z7I=$P*Ad7Cx_7xsgB(z!-x-t>Cp$9KzdiPBZK6m1t( zd>`|@VFZGuenEl57Ux0ZGo{O$1FN>vWud+CMeWVJgqn8p?$sg5mqPIZ@yE6o*e9#a z&C;aF-j$U%JK9Wawar#<-I9`@rW#z)92EX;qy7~z-v zi;db{vna!|vt!~5#vuz^K!!pb9c$67;5hg6CtZk>O|o2K|8Lf7lCS}GwP zaf49`(}wj9f7zN3jj4WfV%2;-)b3H=1?TJUg#0{tQoDlrj~K`BQ}Oez{DJ_5U;V(! zav4}kx5?25N80?@Vsu_t(}^LluPwW6h@EYpfuuX`cRlel?Z0X!_JT_N7DmQ+>VtY8 z+4~44$G^h$In-`;GIADEH`J(Iv~JCdpbhSBPvLAHY8(>s6U?}>YDRUHi_4r7R^%k!Ht%>Jh4JA-kXiZd~1*BzZhek^NKx#c?g&cP-@UHVD&JC`t#pCj#X- zd=6B;3tv9>i|oo=p`IBw)CAa2-T0#^L_LvBXT%fHS`}Fu>|UBlfTZGow$O&7^gl20cFoxK2e*j#AvsWf{AACuwqFN)sN1O9;LfG7RaPW_<<)fy7kWoEz9gH(B$@*p!(1$B7JzXHyc9>5ruzCF1K| z7R~Q&R-%t;W4-f&Wfw7l*pz25)1i`uA?v9%xK`?EbgseKGOqGe1w|J(-Z>eFJP=y8 zse)=(N6QQZ+QdP5#5SYFnW@Ev(`m6N@wcpak8%fpnClYh*Zyy zb==rY)Z|6%{g+s&RvT1`+vvoLHI7TQT5QQOOA|8hTl$42b2Pd5nfr94V$O~JXci*i zK8kPN+}QLE1VhsaiQYT=+Hq17dz;?&bQG^-$h|CFWqsdD(n2Mtm^fe;(Lrqp?8)wq zts_{MR*o6y&^)R-@=K_7vf}zl=c}{3BLhnzFDpVqQl4M+O>_*bH^2+e))Z}qphmm> zg2_9YD6lrD39*FNB52RJiiTnDLgR%qaZpj$c_oU(3*-Fd0Qqx5D&FuVOded2eg@Bk z)~bH)K~t;InZt!Ns!y9wy0d&CRqU;9Tgcs)WVoJ_-4>NNQ7qC5yNJ66^T%dP^x|`< z2ELlM)*sHPy?kT($tf=v<`w4!hosxL9$(fzCI*hLB6ChOpj0}bOwF!HDw>7SD&u-o z?~VdISkgB0t@C%Y>4G!FouYzpVbD9k2_-`~BPoJS7MMg#+3j)H-tHwWt@PfL`FgE3 z6M2D^OdGR?vWbOx;3TmVP}=qVjD!V!$uW0~DYx-(#*T_5Qf6`L&e3}LK=LzF*Dz&D z=(0^j1Ch21E^RR#i$C|mmDYj&yiZTD4*We1_?jM)d^WAA#mSttSk%cbGT)Ke<~|=4 zAqy_hj?;95l1Vj66f@l-a-o;|lO?`CFBQfy)U>gNc^JyrNVEuby)RNJxQnj(NL>KC ziA`OEjB8^wvBy!L4Y;qn^2wum$+Nb_|s`Nvq8Z6wI$vTCPxmE^+b&sU$CPgfp%` zhI_d?I<6W;FfL2*kb zN7Mw}Ex3FpzRxvMZ7Rx5S!&eANpBd_!ZtMb>cIBmWkV4h(}_F^b+*i=J~#Z`^18?Z zW76j*3fV3~Yc5_D9TvLrhXfM=zYppy^50xK%edWtxM?8kGXfv+C&L%RwK}xEJaO|X z_VGxnlUT{EXfK$rU5vstDpn(74XH!eJxufJqQrlf>pW`8}O zgJyO?dGQW2@?NNE9L(Yxp%}EbWj?8~!C26>?XwU!IJi`zx}V2mf*PYdX1pcx-H|$C z7S`NDcqg1Ae1wk-d;b}h$p#yaYM%eVA-nc`p~2AYnS0ORy^#0pkvJZMO?z`IER2HHvj3U12pUI*B)VK^G%m^CdSl_&ta{sx_0 z_$s)5_*J(XUy3L1ON*05;ykce4mJD5EJ9mZFB4YSr4`Bwu9*(%dQ`>}eAIYA;-zp6 zyU~}4OLxUP^!+T~$v$Ihy?Swl%f|5TVIB+*7P^3(@W3QtYtb)oI>WG|bF)Xy-A6_g zQ!}DXGH{{{ho!B$dE~e_$b+qTzil?pJ%mC6Q(`J$H?R(94a>&Y!>TN!+~b*s^&v~i zJh0Bt7gj6w4;E~xA&m~RBH!LOb0FqQAWVSeao*%29?qe^(C#)c0?e{Dw7cQkRuSPn;yRp0;C&+Ezas3A5(c3r`4 zKFK$NYCc)UsvFOeg7F+yJ7wL!*x(&&u@Wr-JGmgHRPgcJVB4(G82i;U{G46H7~Et} zcg-drTH(_23KfJgL}f;NPT9!12bwU?d7U!8E^(cp9`(?<7RJwpGQ}TVvBtLJgkf64 zL>3Pl3fQ?9nhR1#Cba#_v{?rx-OyCqg>^d=61)QxDy@g}mQ{CJsAbSW^Sg=gQ;1e` zRquJQU#c*maIa6(X4qc>9(tA{Dstua!gM^B$qb{7(sv`?y-b+zN?1%8^C!}Y%*{rp zVsS=j@2EzzyH#i#l@|ubS~KKbVjDzLg=6n7rcv8=m=h=3$;2rjB zdim&d&CO-WuMa!YDaY=dyy|z3?ABLN@)T_ULjN)1B(fG>h7!|3zbu+V#^iA5Hcb=K z1+|4fvz|MhOvdNHmF;k)+x^*eq#u*iLKuV>%;Gc&@PNi}Yo(*fEzmCQ-t4+`R%;U- zo!uEju`X91PJZSONENzLBvF+}HT?@(8f$pbkGlA8myTnR9QC>T0K% z;g@aHPAUkVcDxv==@+GQ-2d^yy(VTX*rigk5?Pr@}J<`Fget14MByf6yEd~ z7f%G9Uyatjf8S{7`m^FRKP4amQ#0Ch@(U7mwHMlA!vymWpI3Pkrc*PN_ZoL8n%*q` z))Oxq!L}U(6GJ1DnmxA0w@iO{8>@SRbcSA*>ThBm74@jHKk;6?Df>3wL{+Lc zqnrFR*}j~$FM4M&CaT#PbS3VOCgBZyGB7t|VUMoy>xcWD+n!5A9IfO02C$zo779HJ zmxE8TBY44opK8;CGv{WL@ph=8wu#3XStbd2Ye8{tAvcmzqOMk9<3I5L1zpO&=*g+SjU(A%xBo2{YBoo&-WJ^v{<#kLzQ8>}+J zhTTN3TqQKfBVr~pZ*fn~;ZI7(Tvxuf{)l}_)i0p}R_k)_&M9RoL9VN=oZ60&EVA|y$hrNmUqniR^=n6V_XO;kiN6_Scch%7VqCDmAp?97nt zGnR2Oi}&cd?)!c2yX$_Q`?{a^e%@!h{+N6^&vTAB&SUxgzQ6BpIXH1Z>pn^Cte=TF zom`E&oi2#4LTywLag9BlvT5^$$dq&LnToV4k6$l6*qGZ`*$1z~_cYTastC8`l<6aj zlYK;Vwp9@+(MZCvO=?7CMrDrm*3~iaUjFfQ-8KAiPs=-*vYd48PPREGw|qa#5@SOz zPv?ur67WdNdU5&A}w}yCDuYzx(k|G$9v08{o(xxw%6I5A~0n1h`mfzI?+?6XiQgamF zz)If4gD?@V*a}V(cD<+l+<+5Xfo}A1a<*``*LtW~C(%-J+RascIA0;r=1k{kCtKVM ztH(YL4^m1yw6>nlyw z4dGzlomV9;4rzBga)^#nC)+IYTT-%g_bpw0&=Z%P$Ilkwh%p3&9po?PJRh zxnr(TbA`G)dQ4h~3W9>~+7GiHBpuzPn{G*(olWW0?<|CDm|TcPCorSw#UK(xdWakT zxEo4xKVMYQ>JYv*)l|%PMzmQb;r59E;R9t}a|WO&*;09SlnI&%yNO0H?KvrI)1eJECSMjP5>B3-_8oR_-!g&| zQ5yY{EmrcDcXZADZYoJuYAs#z37+YncOe&IXCyxBu&HGkm$Yb z#kLSDbp_bt3!~z5$!rm?q02=;b}@ZEZ)M=3HyNn)_-jpk|_&yQC+1vZ(f^nRW`CPDY|8;qM99JqtU$e;%c2PJNpAC zV()JV)44k_zz^JzR*ks9B+@R%hH0W!zb^f=FLc@1)Bu6niY z;$U36?vbK-UAD+WrVqJ=D+5_kC)lPP#8=n6^_RaGZmWxk$Y;4Ulw537+LJV7p&Gcb ztji=p%E3UtQ|b{y81V}q1LP9C`BKhF!#=%zW4g_x;!U$q)%Zaofjsztu!*bSk-oXr zSOA?dI1`1@%|>5tW1cd!yiH%lGKI=KG02@E%|hiY2wCVPd4(K%_r(nB{hlNi}` z6uA3J6)-px2&Fc$?+;RlF6=X^3%vW_bSg@uthBzNE#4|br5q7 zlBE*GoX|1*=oMKbz4j6(v~CM_J6Q-C?oWyJ+Gbty?217A66FLKon+_~%_h=#4>Scz zG%4jmt|}(8HIs>6QkOcyMlU4SeK0qZt`thC$IdQ)M!X?cBP2Y4)+ORorz?gw_#L4? z%XN9V-{+;X$DFB$zapvVqVR|2qQ!0b(l%yFMX*4NN}sjp39i6>27<~TW9Y(=u9Xv| zABi!%GFG}hPNx?CQA4uzgaOaN79ZcNN3TbD;|G|UP$+E$)Alz~-rVQqw`si&PYb2P&4v0J^VFXK{Ch#&%&~4hlhCcSTPQZL=I;QLG zzGk#ZZ!_Y(#@Z|q3$YLh3m)1PN%7TJjD1dWVzK<}BcT2V3Jo#|Ai|IVo~J>}cl-T} z0rS=~nuUU%1o@C5`QqbQUG_KjY;ghAoq$qL-vs5etg#&^Iqqf;+$L?}TC^1l%VpW6 z8r*h$P_<^hwQe;p`fKLLFy385=BC~P!Td~#FNQuWeQrNijYBB1kwBq|HCA4l7R+Kl zsnQzR4=0{#>fWo@-Bl?g68NbFR2T@uilf`B$i;{$;XDxY(=qQPdaU&GhNP_L96 z{)pomTcgd7zF9@sv&%-N4MGn2#7+U-_}X~~BbFr6xs4v1pm)fpe;ZOplB>kN<5;|ce}4$OXg6uNj^ z%O$+;eP}QK^tl+|1mQ$9=Z=(gdZP{61`Gp!Bz?&t7R`66$Ou=_y8>5 zf*X#*SXcET0rp+0z_m`jBbGmh5$oWgh$8q+j*0xSK~Vm zPpwNQBm#-Xd<|)3X zTbT6b1$_CN&1u*G=MF}ihU&>U!+CW=RcTTsXI} zd(rKcx}b`M1$6=?HlcI9#vx3P_V(6}XZMXe-n>#xGYVF6E9(vUcd{ywe@~cbk3H%)l~G#4WfR#uOrEHv^a}bcbC2`eTrd zBW|sQ+uX#&vk#XfxC(P-;l=}M#^(e(XEy4$vivbB3_=XfhuGCwD}?|Sjobcq)%jK?#Fc`=_dM)73Nfu0wg%qw(C>^1mmNX;r%)^5}) zAz|9VQSf#O^Gz~LqxSicmH~CX@y4}B@-oEYedcq|`!?;W zW(eN%=RbG;uChX?=}yWSnPs&pQ+hTvd9st`>&R^diEw5Bf*>}G@5nyd?q3^tF)r@) z+f=2KcK*l3FPvI+e?!&a**Q7m7OV=c%9><=F%CX4Wu4-q0V-QJaV$|p!2NT7Me zfsb@}}1n9~SQz zew2Klw6=@iW6O(#M)6V9W@97?$fubJf+XnL~}=v@H_JA=DND?d9RoU zob@MWO;C}sucSiHzNxtr@ zizUlg7A?EsIoT%)D`z@r=M%nN`%}4p3R6tS#4dU2M)~V%n)=kdWIe4k#L$IWG>IyX zoNB#DpX^g(??Ogs2ERPxc4gaVZ5ZpcT+m3vUHo*zmHkT*z5{fdcT=5Q0UOpK4NJ+0 z>1+AIcG8ae@6W0cW!0UVXg-kqVil;m)9l1hoQW#G>K*tBW%Jx-L*5hDZI+4&nP)?D zPrVIm*nIwKZzq7nGi*`r4WFJ9byv~J`x$vLU=Q<*&w4sk&IEckSwQ9fO1=eB7Ffw!9v zzE2d|t6*;pFrGIQN?)upmgzJW!{{bcAC(rSpa)YjHtuWni9fz7(ey%)3!r!$I1fld zP`F+B7j2se<8_~ll)KWY-)3iOIubt`=2;n(6<#P1u~c}J=v9aFz;%aH2PTIYQXLdv zRr2~6Z)5A+wZ2&w{CHiGmt)AMH}Vf?_iat0m^lX~7V-Od=$(}VQJYp^V#vXa4uEc+ z*ewn%#<`S+86j%kcpP`^3C=X%RupY1%dQ=Nm6>CI-uU8iG497s+DK;&SYz~~Gu@-r z0~@u8w@xS5Ao#yiKdrl5w0Ah>VpjQ#y1=Z0YhYU1XR*Vo_Im}agA*7u8nJ=M$L%Bw zu_Xq{Ja6;q%_nt`-QAUKN@>CuO8noc_`Z^lsuPKX%^Rjwg*=8QATJL{ptAteE5~On zO{b!pom(a^@x?qj%-WVtTWsHY{vd2OzWh=AMql-WaY3-xVZhd+OVjKl4Aoes7y~*t z@(Gma(54)>F_E6JH!kbcChCQA(tG-V@$b8~@go7+0ZPS0fz*vGk}S6sC1j`;K;wCm zDeRf*nkH?vXV1Z@qyuZ1mSS<6FY;ghke*?Djh2R>Hh>^GW(ed50w=N^ATRnvb)rvM zMMs;NDpA4rjCVl1(RB5W)?>D>v94Q0L*N~|fUDVfE&T{`h=JO;R{%G(HfneQshDiT zC1(ZC047q2dqSK6V*^DbcEFShk6Ev2iV-mZV!mprz1*7xq~k*^%80(TZBIVv2S7I$epsu=O9HU9a> z4zj%#N0nj~)o{^m%_HxogYqX4cf{wiL8kTK189V`6HPu+9Af?Idw$#v>xH@88qMd6 z?uQ$VdXMsdM9`1=Lw|+i+`aXvlgJy`z*0A#q$ze*iCLZe% z;dK^f-QZFJQq`tf2US$qfv@^;)xj?vdma0F3$#+DdOhWiH(U8 znYXJJR;>DTYFk*t;dATl7-x30wHV8x znT^fV2Tq2XvW1JqFTJJSKOKA=A)~x$qCh4*QOK*EtBU5YU5;dnylt+wBguWaTyBd9 z({i+H-CB?^bD%Ysn1V>;<1`n&DQWEX-CG9>B*Y>w!#6`V+;zA?+->3hX(*Js(L66Fe?%agXcAL|%s4&PgQc8Hy) z8~D&~ZWN&cb1dOJXS*W<=}d(&Xp2F*o<`1f@Y}5IH{_#mU*4>Rt!?KdBo!4g{*aftHVX#)P5}4sX+4+ z8BoieAmKidjS52~_hcD=@ z4!t`G61Ca9R4)Xic(L>5G{)$~ergik_~oTig(P2Nn_6%y1p*0cG z93n?uBMU;;*p5^|X}i>Bvv?boykW^|zN$qdy?W(uI7ks0im{+CGHspI>h$@thbpX# znw#p9a@`bPaZd?{^e7ztYH&nIuMUK6oMSjreX1yY-}|=!0SB=y{XZWFA|+f_@JeuZ z@goV#IbNrSZ$-d3{e!%-JOmiG;2$`H#@K0*CN*wplofIhv z+-zs^;>DgvdDKJiw%AXSYszw0Emq1&6RMNdouG5`6Mk6~-c1%ovz_m^q+c!YaSryH zjRYAY1&n53g=IoML(}0bBXnf7UB~HEPto3SWz8Lr4ltevj)X~4_bJ{rh8fN_;Invz8t=KVEebNqvzm=CmN9uJ zZt$_QJedRJ@1>c@fdX>mWd;2sc;867Bsr5a2cKHqhPw>+2qC)zH<9=GQ*Nj4X~(%r z+sFdrzKqat74pzFAg=d#n^;J}oZZ0mzj^uEb`!q-L&Kv_i1Ellz!Vk`V*OWOn=z(P zJHx`{^E_eGgoMWFQ+2srP2Rrs_H%jDhVR7o2-R!bHP9RNkMe(yjGUK-3w+nV9Fujr z;bPWUM{8HN(@ z;q!ZgWbh@*Gz7F8=$$(joLD>5^6D?lfC${MeJjMv5t56PpB8|9A#URkK??C_lKOQZ zlq_s#@Js%Q71ztlm|3mY;3C+MNOAw%?e9mAe^Dz}%W*v2)m48XHPE@+?4%hi>HI;S zeNCT0u1E1dPHwhxn{fFvwO+Wd1RAz@mzSJKC)>i?Bk-}KnL<*sZ*FMPrTda+3nbK^ zUviVq4|_&>3O0r{Mx}#<;C@`bX{<(%8;#WNU?_fOfG91z*R|mXD%Gz=c6gWFe;srs zSb1t3#{R@f!+AS4BcSazaSMG`_9Z0@Lb&|H{bAG8^NVnmDnS<=%cgEu!HJmb#tXDg za=}0}8`uo1s}RD5X2AN)?`!b8MwHz}ojx^G@ixgp?yYnw$I0(M=#59FQzHrZ*`c6w8Ij@>4gx(~cV< zWq2AQ-bLIzteO=WFtCNtH^U15cDZP__!qXZftma(+ej9kDd!zp3)sdp`e(NXo0uxL z6yXJ$?;*&p%`OJm<5%oKri~(4Gq~H%QZ4hy(0&Erjh5vFr8T(Ue8fGk^g3F|Eu~(J zc$&CTH~IcO?A#sA_zf0dSRVXevQak%qnL+2{|niOe>paHI`>nPTFiqF(PfqWkMgke zg~vjm=mnl;BaJcOJTq3s@H5nI=apMNg?#&`Zv^Ijb32$>R2IF8!RM;}Dn^HzQn|}u z)KIr74Gx?N^pi8OsErF05zK+kaz*-F&8&Iqb$rI*W45jyhBs}I+XoXg4P&!zUzAzz z4Wb9shETC=BmhcU&=l3%rTX-@l%X{9_LElD=NFHpDjVEYIy_1dKN)l@Mke-EzSgaEt8dGMqAt85ibm+c!c5{S zWdkM&G#>=F8CRV51hBi2^#ByG5zozOHpwUNKdKzKz&fL0QIdc7xqH`s0f*^^RpoP) zBm_icS#Wz02;)ue1aWP`TJ))fibHch_{NI~-difriGo*4E;Xg>Kl)yWs3%y+;-AFR zQPhA2To2No^9-v1nam7v9}4Vg)AHVb$}PYP^``!btSwVPAwS`MfWfMa8SfTOG)Wwy zRFmn)klmuSshttpALVPh?~d0uoFuhByVWsinNz3Wy7QEY@zG6^6d6$4pO+=shaP1D z7!no%%y*bJd@~9vsL|{>h-;VqaFJABeCJa_fo>8@sJ{D(uivAch!tnz; z<4KGZdVHzjmX4U>T7DbCBtKUnM?!RJb_Le4>cNJ^Tlx&tQ>mEVhU`A=dmbv(+Iq?1 zy>=Mc$ArVw5G0r>Z%TJKGm_r%kb$V~-wet78*X`Reb(?oz7=}I%evdDo3xHf2vFv9 zY_HyZWJ`qP%W*=Nu0Z* zIo}UPP>l1$5c$*zeZ!HHbZvx@AD2t3D2$%A+N`)GZftFF@VJn%ZA!S`oB$cD>PH|e zycnYghN@g298I`-FEM}TaBB=c$THUzmb=Ry{}m< zchi1oF^r^y?qTkQ`WUDhLczsu%qe=fNxQS5zFFOH5IVL|#QZ}2=~Z=yLmp(xB((eV zfx`|cb@*;xW(1%iYlaBX=^O4=UXO88e1^w%pJ6(mY`g8UD}E9)7^3Xs{9s~EVCe+Y z8oJE@btS$Tx(yOVv6OyA#(~uPv%PgM+*=xtn;+OA?t0KkF_ts3Wh6ky;$tO$F|JB( z9u)KLM=RBnlwLEmx)*Cdx{fI_6f1_eNfbD>X`SOcvT)r)vJtKXdvSks-khw58w8ny z1p~Dz2pXz(CJw7b&m4fi!W>o2cugOv_2wmZt9Fb;-3)0=m^|q;zHa>$_0D4J@)RcYZL>gqbC%D!H%H^160-$7b|5u4SzvO$QQ7wah7 zlan<9g+6ogRC@}nyrYiDH~q~%udV#_Fq8722**S_e@6(?0ypG~=I2J@HvmSxdte0? zlfjix3c-Cx(+9X4fP#lgm$JFCYOK90u$F=s;GywRp!Ps6)X$e5!?VbLwDk{+mk_=h zD?ty41V#L$*%xuq&SI$zwfY<0;^pp@-rVMJT2tr@IVB8!zXeWZ5`l*`qG=GZEN2Mp zpKvc6SC!-eSS2g&C*=I@aWbU;4n&dxB>eLgUN2@U-RCCKeMTIk;z*7riM{y|LPnmd zaI{F|N(3J{$Xr@0{ekBsT>Hq4L&3@e$e-tsn2H-}1B)Hc5!5Ggh4OIF3*^riex#@T zv}*on=Pw5AzufqM(#Sg`YSje2aI!0~3(au`wWUVvO~(4SymGvKs0rRQ3A+R35e^Y& zc$+E*J9}{ScZR?bWZG~GfDLaS0EIe7I;V12>)DEsGDDMzYb{sK=4?ZEStZ- zeqxZnX~KeSAUcTP2rd)28-Q4%zA!3ZZ(LzjVBG%O?P9{o#JN!fxNWGyhEPmkF58tJ z>%5j;-9xLvc|{CA%B(+ilKil6-9a-YnO&jrnx~NTNt+PdyxKSp(k1*V^KR4O+z5^h zC^9m)0%M%Kf#&bbx$f5e+u>)q0O;g|IR$c{Y9SC0V8YD?dpHot zR(X*I@9-&n+N+I}3G$KRWrxNd6C6lbv-4y1h*lb`wK)GTY~329#{^ zB@Z~Ow-w+Dx=?jm4-fbaPuVH);y(eOUgUWUkV;Q*Cjh+!QrXzH`vU+xEr;M&B^~DD z&VMOLqmQ>G29qw~+l%D-c^1Z<-MaNr^=dq-58dH$?7eFvG>}CAJsqLXQhF(gg2MXhL`QB#|L3T}n&G-2J4FU6$ z%zVzI!->ZyDTQE^{~i>C3b?XA$v|99zA+ELhGY;xHrS8j?!x=PmKRh%{F5a5AHDkp zgCmEoqIv%kr4xULHvDffcJGmNOLCO61bws`aa-7vtI#^1^Ndk3+}aR`6EmafFbDN- zox86fXCY(X`#1LJFn&^#C4<=mjnNnDQX68lUN)C~MAjDTd%wH2>|j!Pr~3Wc;~IYQ zzJbI{)*7$hk zQJ?tLN+D^X4 z!QhMkzD@T>bN|TLNrdQwYN1z$$ZTm4Va~Y5fd90Fei(7CY!L7C1a}#?fw}kj)eJ})uZ7C0oMoV#A(W73>Qu^Wsr&UA>vLpOD-UPc z?ZcwWYd@g}(yyYlKxLl4-W)&A`P4?-h0bX*F<}aoPY5-Z!8Q@r4AM`t579|QIaW7x zo;>$FhN=>vXbzc+y{}ypC+}=y;1~J`?*E(RK-&5@HV)!v;n}_AUwu7Lt>LE?_D7R{ zDTMyfv8)^9tz!HIV^E9yc`|;ZhWKew;H4v8&O0(ubOA{p4b^x1G{vc|y=Ks|F!y$S-kg{vJ*m?JeuQbq%g47uw2?z>-vd zq`taf)|KEr-It3B+zhB+E-H=9UBKSg{aG_GqflLYEmOnaRs9h=Q+jN z1Uy#-q72DiJA79sA*0SuK-yM4aO17(5Ac0;36&%9)B5(Egx_MN|Kzz1{{OWCOUi)~ z`%7l+f6I&ihEn`5_~ICEKqy3{C_;XWOBwY`jEd%qk}~1z9SpBegfF}z{-n+9+Xvp@ zCVK`cU1e3LlL&)f)y@@vAsTo3`dmKz!O8w?_YD~TD8ci2la))daOlfhc*7}|UU1}k z0Y%`KO6V&v#kNmX^MlxHNzzpmtR~({%Sh9}CiS+}u zd!D6bRP7S?$ZcJ0LLw_a3EOwN$la8Jany+vo7e#mAH3P!S)CD|K<^w(%BNN!s_hr` zu`j98H`AsPrg}499Z-KA$8+4&AUtfHAnM6A8m>sGfwCS8aMhZk8FA10FWcQmVh*?O zc|0!B#-}XlJea?GDmm<~J*XCDC@m4ADp;NRDX;UJV*9LNQ=@HM_}V8mGOwG{9tl%G zi6{Fw_z}pOkueldM_4!Bl*u+rnNuv>b@;BJo#}?A2ARvVQ9h(6`x@=?DBQI}Lr#b~ zJA?>Vc|7%n7f!gy=-8{7)6Q;15ex%8CBi_XL!t)!4xg{&QGPElbh|+r{VtXwI3D@~ z{SmI`V_fillo~KKtdPTRu}yBsIYT7`bIt{I)Luy5ADt3exAAGT^1a!xfA_(UfB(Qp zcoZ1_WMJY{KVi%);)XQK8_t1~S($zL4Wml|EDaKvpRhB42o#8HAol?qP6s z^#%lCWMK0BG@%yT3LtoC&GNX|F|+;O|K*(fKc9F1pY%7#A`CZ-iUenBj|a^L*+1;e z1d**~5L*7*-R%V)*4*Cn( zF$P44!05yZ3?bpqvuO0gfBbhoh$v*?_6mj$j zzGM-*+DtYEHv4QJx$~mnO>E;H-Zg^w7I+tm+04kU$4BY(3w|NH;WlE_T87$(lT?n+ zT9TUAM(sFgVwAb-ad_x08gH!s@?!R?q!8l+kRZzfBZUYG`JTy%)=Y;3`%O%Qr?%RzmRYYWDdX~#y^fPf(n3!#OQD28H3<6 zecP+U-Ogz$brnVBv$D~Lb_#T~i|8$tzgmIew3jo$VHUzaHfaM}mC~ggj~m*15jfT+11x$qh2#PAk@KNTGNPyaiQU%dirB*Rx@K7L*po+s)O4}wDqW9W2? z4}CVyHitUWM`Ir1c!IdG{XiH#UfM`}kL;QQX`MG%4j4~P<}*gqm+GwLgFRDl>&Y2%o*&pF%Pa(aGK}bnNgx>;)u!<>#-Q~(m(D_;YyMXY0fnxwq z;U_W-S3xDRr2x?=MJ)La;u(LsnDNthSn;6fkKa!hfBMe<_J;a6FF-cO>43=v#&(~` zQvRAgFRDb$RBlFDX>sA*NxG>_^k(d}czc&y$FDq)4(WSDB~Ah&YmEk-GG#*xA51f? zUX&AHoVn9v^k@TGb!xiyY+)2JfAg6guHE*Mc1tByun&Nk&mgxKA==gTXK+m0m(qKBQDlj8Nx zq?Hr;B8D+tpI~}wKeh6MH~hTL1x5zT-JNT;wn?zqCvT0siT%NSlkmx$XMZy#Mx9i3ADbbbZq zC`XgIl(^%U(%+xV!7(*9-9F6SPylNu+n4h&n{LA_(0NBwM*AQOXI9fL1?WUv4~fAq z#1X2=rtAhTgFW$$21ffC9 z^(Xq3$GGN-Vj6^7nE0Ow(jKh9UPSoyaMM-)4AOpc)KxI$cZ@5)9@YCqHOPvXPs7!p zZuVG~&C~%*>=*4od55ly+gF}h@cIp2$+-K8|BU?zk}bMe1>E3;kyB$d1YSq##F(65 z+k%lfSA`J)H)^wzIePWZ1>p}DF>-4W1Dk!=;z%027A}YJ@rm$$>P5$Bv?vy)Pq4Oq z=_|5FyQF0G6?@FqyxEp`QX@%kuizB&4SA|-D2|U+NnTsNl@70S7H!E>+uDA<^nUnm z75R`((YunneNXk>rtpK38Bg!e5vFWeN4y3KoW8~O%R+io`0e9f!q$o(J0hiDI2Jmt zdX;yl(brVQO=f5P4PQAW4zV2fl-sv#Q)PvMDN@2OCvIDwxv%@EYN~uR1!e3ICq29+ z{JpN;7CX0w8ED7-&dGL$U#*-Z17D32?nQeJGl}ix=EXizt0n zum#wXDRcc zVxtR8vYhcM$fkbaZ2((LghIQOHh5!Z4O)(JtoY0Jijue_T`RP$@al+@R=b~R_lg>r zK1$@X=}Q0o?{SnL#j?e=r!G!*24?6+VujG*)RU>TCoKK0J*fxP+c#CdRL<0pxzn(@ zLEvio06(tENP7xS^O+i=;-lp>Isp^D0X+?>&ihmNvdVK55)EDT;1Ax5s@I+oqdaKv z+qmix2gP7<1r)EbwAg1D{I?8U=*hJKa#F>!j+PA7BvVrias8zlp%*EqHn&XqEneAY zgf)i*hmg|PE>QH-3K=gI5IR#XK6t~(eBfj%ld9v;r!sW%fQb09-ESUrUO7A^M#(df*DL}8rb>mk8th4d{jb#wOVx;*l0g)%meeR4>|v=o|Kpu^pJ4uE^tX; zpO}f+S+x8;L*=jT>#LWVE#pV6v(2w=W29TmW%`}Rg`Q~TnvL&x`p%|y)$cS%e=#kS z#*srd(84V}M%v&Yd}$HbB8by}HY?Y&w=yP14a(gDzq)aarpoZdHqwW@G4|pAFR#yEBME-V?)C79au$SD`bOXi*AI;j=d?LzaGa@_OW68hAz4)<*8H{X7@ zl=UeM_e$#3lO|GOobyjf8-D>IAXnna9Mz30q=H-$`GI!8N#O|N?YKc|JCOW4c&C?@ zhv2`t{M)aF7|;CCe zu2F7jgM?l$U$1nHx6xOr%8i#dX~&PZ;MJOM8H#?B8P%(tC${QWKO6lF@-S9&Orm$Z zvU&UuS%>~^mtqzWpB^5Gol3xFEVb$7BP(-&-*4N(5A&G%djQUlXZUMzbSsdN5MxT~ zFtUlA;TilzHqeX!kb!!*>s)-q2j(b=Vp0rY4Lx((LY1=!qm+pOk8P=_izeiX9+@g{pxn#pDYi*$q#OJBkX9CXQf>}ak&o6e`7s@H;h^%XUoJjA`IziaSMJ<}3&uQxtX*>J)_AHMKV zke$dze#kaE^XFd-{c{&a_j3!*;Sy1if3xE<-;o`c)Ml+f5h0B<@hgA+#Xol;BaMIg zV+7X{i}-fpY2KfIao?Z2;P?2y##>uzo!49SeCzf2cOb)@xB_$5FPzDle{lHl)q5_^ z0!@nxuR+n8Pp_YX6Hxz|pPvYZxuBmYh+L0p^t)oZ1o8VvpzuoJ3BUReeukHSav*?g z#W&avH|`5@3T_$wd))_1(oA5WhTTQOnNExXv_`PtDTaMCEzIC{?rz&{VQa4SJ{j># ztUGvV*Xyd`@HzvgY2s0AL?tyGPS zTwa02mRtBSog~Z(ZZVV z?6|TYPC-q2o1y#3sY~Ik${X<;f&)r!?PpLuRdO3Giq z`C|LtfZ;f3xS3^wF~dbJ<0G|kFm&tv)Se?90&jZ7k55Ux6}!DEtml;Z zzP(CWm`-{_GZYROi^q(JZ?6cR-omQb`o!2*)*BvO|Gv*?&*lfXLcfkDVDfJ6M}z^Y zJrez~>K|@em34 z$-*Asn%p^ekKn!q96?Tf`+tRazgVKrPGnFXl+Y5`W10`e*#hbKSv5d~zsGL*y(}ii z7O09`J;;)=b{GM@nqHDc()#;JUx?AzuTjL zC(vzYx-%RZ{PmXDbynKbp@XtgR(eIZFKslcf7WIu)AKgT)<5oyckF9Ev)wj=#b3!6 z;Lr};N`he1piejgZpcIZD7V3US=LzkWl8R%=Bt{!q+@PbT4{=gec*?kg}s1rwt@;F z2N}9kO*!9@#dgW`RY+d#dcyU5zE0z|>f;xR=GdR{uIs(t%LToZi9KK5*?^*3;l(D% zn=w8N-s*uuxuQWYf|#c!%GxJu?09C>n}ZjGwetHdzU{hOf6`yZmBgKO(Oh@f^CGW% zaK?2bO`FH?T2xmt^D^Ty*eYuT81|u$q1@cDWlF?w5|dT8u}-b~n){QI;sLR6O5>@3 zya0oEK2!Veh}-1L);9Y&rG?}WVQ6!n7ON;q=N&Ui?p6t7BVfv42H zT_G_Z>BIcpr%anQR}0^A$+x?ZpsxVUBA8@TQ;Ww^<8>0N6^c$D|Atjk@K~TS<*~s>v2>*o~3V zd_n7JcwGIHoaut{YxPF?N9_+>M>;kFMU1i@}n7sVe82<)HvQEoF71xS`{C_JZo zqVsNBd4Gj`6>?UsrV62X%_z`F`Sgu+Rd4p1iDk_oFoNO3Cgkm;)!fFns3M7aVz;ps zMH;$WwF3K^j{JOIqGnN0qT8@1<;{ggy`GWvxbiQ{&A&t={0b(5HGw_WSoE$Px;2X#niJL5k zR1VcRFc8rhxAaEx+s_?{Uvu@HVbg`P8YjaeHgrBZNq&Q8i=l=nQ>Un>Flx`bJh7V@ zr>hr*JxZTusC@QnevoBisu6$a^@-SoJL_O_9>1AW_n#*~d$GumAWYigP8MJb=0W6I zd!$RNmZPPbY8J0bKHv4{-0dzAw$le?zw=TlzhfzwagxByW;n0myfjwmgVbtBG7P@E z&-b5GeX6Rh5Mt6=|NZ7g1=kHJV(~^n_8nS0z%Tdndis&7{MU=KGXwh>NK*K%fj6Bo z3iPC`bFa|&x??pmvtP$vKebp@(wlza&7RG>_E+DU__PbvWx*9t19>;E7`sUl?U#y> z>!I9pH=+vrjvY%H*B<$B{K?i73q^;W^#>bQhrnaV?lWJx4a~F93`_USY4m7qzZ$oT zali0LDx12WKlz&b{@9Q`N9G8Rfidg3RKOKD2edDs#(Zf8OP*Tq#^xq-SA$rU zzi}H`s7{z$MnOO*Y9I?5I0K(snZWT-ww#(6tw*@RZK=mm#&Cl+>_#pHv4}fi(M99V zjC&s1@h!N9WxDwXVgteqIVb;XhY)Uf7C9$pM@Gsb{Er|0>zVl9G1S@vkdcY|)zEgo z07}r1as?gF4n4mD3nZucxA;7ms;1_PKcm@YBo^Cs6_|qR7XIG(-w~eSE0rCAfe*=^aAGhsvfN8u zpLUcj*a)AglCol=?;^?{#NIsVd~HB<1TpBEGo z&Z(Qd9oZh%zXc}kRQv3BTG*cXPoRdI7qhVA8YRXa$t!Y$0!Je(6Wgq>f2&tlsecM! zJ8i8KuZ2`&N$i_j*P0AfjbfNa91_mWW=-87Asi`Gj-Umrv5s)1*Alg*rA?VJ?^2HT zo@q?pU0WcWC?NoYtvL$g|NaFVgzd#{{|pYABzB7eyz!>trkX;`VM~_|jCZh4*p^V8 z(gR0=Ufh8UC6ahPODzyYuzmQ);Cwfq$z?;hcCD`SAssTaS7%8D+p_uG?2;K`r@`_q zWKH;pF`&xAF0l81Ch4FjsPJ&IyR3a>0hE0_b=G12p;gp&hs--A5yR1Ax-Ty9B*gO{ zg3ZCd7T4&z{jhpppf+i3*!0N=d9ZE1jAdZuaBI_!jHK3M7A-ue7Yk4%OBVxU^#6+e zZNgQ!yC*iuDbOd|`aQ8bZ)fXDl^^YOOgVpdu_x;8l~2qo*JHy`Pn;DvWr%&860L!* zd9<8Y)aCMf-ngj}@7dFw#~L*wv(d)K0~XffB3JW%H#$*G;-|;wG8hne~2nep&T` zRnqxWQB8soFxaZ+FkYZu!ydSCB`hW%0`Rx46gOra7p(ZJ;|Dohh;7P7&KYF?5;tgR zZ)8n*-UNWdFRD%cA5?9dYh&sI{|N+c9g2Raop6(|F@8VD?Ef^A&<>nC6H}+5m)v!y z{pfe8!i`AAslHi#Tth`g@XOPmp|1AZTrHr~Ud5k%`cG#2{=Mzx%~d03*We$qKe8vn z^A>;nCx-Kld#ybZ!dvmM?Z>$0AH|M2jh6q*QIBqwKpOoy#t0ntDE9?9_)w}BtHr=a zf#Y0Q_0DT>VqVFqeIvz@m}WEA^b_0azv*?N@qVy_T={lT3cJLe2*Qr~$RgzL(^g8>{3M``(0<2-UG=X~vJp28RXa6I|p=i4BhTxcC^F!qux7FijYhPU* zOWgVT`)Tj;m%C42dv$#0`_g@n!zZtz#BnucSVdfG-w&I&DvkmW=P`ihr=a^QFpFyp zdk~1Ei)7#5hw#>U=(^a1>A0gku3Gm_J$xJS@udqwHgXHDBycd&Z>AVv>mG(OH-3Uj z+*N>RRcHpOcJQ!R@Qa~g}r_36LJi&mUoGdfXWVYLF11?5raOW=!eWGMr7Q1 z`dgyUiy9Z|-jVXv;M@cabBZtKDRoa)SbDF_c@l`UzsY}uwwTOMm?1)TCt{Ksij!$O z&t)i{$Qk`?{}@-zlZ~S|5?E6>C>1#bYbTH9QaDU9+uCh%IB1~z0S;L*x2)XqSGN%K z5RZYmj!`ZP#odl~+rs@^v&3m2v^s~bz}E7mt-y>f<7SG0aSaTyo4iL>VCluk#cq&Q zs&zw;pZN?ynKtA`&%>d8S~E)%ek-tG;K2YE_ZF!D_QJar7^xGO+ZkPr$f7-5+||+f zf2)^rpZ0TdBIj6mFS+vrfQA7v*qfLHs}U|EJDO4kP|c3?>9>yQy`Q4gB>h zA3^bWG>0jcH0rS?z|gfZebsYQj^-RXYF}paq`uXO%-(w4kUe*e(vL_!gd7sgA02%N zsspl1?ToA4z7dYS9KI@M?*nGv36C`F^IMD@X~RP%lYvX$CJ5z7)q6MAMSk{Nk}(tS z0U=ltE)e+GABnH)B-baV#ZoF!L%Zb6D*aGvY*venj>Ae!r*6XJ4rDZM^E7 z0A}eTesds>Pm*ivm$B2b(A$y!c$S5t_mOSUfKLaLOB;Ov*s(}t76QxzW9|cP(3H@Y z@%3=SxHcyK-+TyvZqITYN(bw}R7 zkI6R<#Ix7>Ed!|!mJR|$JFeE_>|lz*pMRO%8?9XY%iQDBT|gN;8SXtCSUL?9V7!D1 z;SL$ytofrmU}yX>I)AL4KX%sdmOIMjKll+KPyj!2z)-@a9Rp;JCi|tMO+Mbb$YI;= rW0pvD%IWU=dM5U6t`ZycHGPxM|1ei9p8sCo?jO(nOEwQ~rR)Cy2<{@D literal 0 HcmV?d00001 diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst index 46ae4bf2d02..cc82c8eaec0 100644 --- a/doc/scapy/installation.rst +++ b/doc/scapy/installation.rst @@ -13,14 +13,12 @@ Overview 3. (Optional): `Install additional software for special features <#optional-software-for-special-features>`_. 4. Run Scapy with root privileges. -Each of these steps can be done in a different way depending on your platform and on the version of Scapy you want to use. +Each of these steps can be done in a different way depending on your platform and on the version of Scapy you want to use. Follow the platform-specific instructions for more detail. -At the moment, there are two different versions of Scapy: +Scapy versions +============== -* **Scapy v2.x**. The current up-to-date version. It consists of several files packaged in the standard distutils way. - Scapy v2 <= 2.3.3 needs Python 2.5, Scapy v2 > 2.3.3 needs Python 2.7 or 3.4+. -* **Scapy v1.x (deprecated)**. It does not support Python 3. It consists of only one file and works on Python 2.4, so it might be easier to install. - Moreover, your OS may already have specially prepared packages or ports for it. The last version is v1.2.2. +.. image:: graphics/scapy_version_timeline.jpg .. note:: @@ -94,15 +92,6 @@ Then you can always update to the latest version:: You can run scapy without installing it using the ``run_scapy`` (unix) or ``run_scapy.bat`` (Windows) script or running it directly from the executable zip file (see the previous section). -Installing Scapy v1.2 (Deprecated) -================================== - -As Scapy v1 consists only of one single Python file, installation is easy: -Just download the last version and run it with your Python interpreter:: - - $ wget https://raw.githubusercontent.com/secdev/scapy/v1.2.0.2/scapy.py - $ sudo python scapy.py - Optional software for special features ====================================== @@ -339,9 +328,6 @@ Windows Scapy is primarily being developed for Unix-like systems and works best on those platforms. But the latest version of Scapy supports Windows out-of-the-box. So you can use nearly all of Scapy's features on your Windows machine as well. -.. note:: - If you update from Scapy-win v1.2.0.2 to Scapy v2 remember to use ``from scapy.all import *`` instead of ``from scapy import *``. - .. image:: graphics/scapy-win-screenshot1.png :scale: 80 :align: center diff --git a/doc/scapy_version_timeline.ods b/doc/scapy_version_timeline.ods new file mode 100644 index 0000000000000000000000000000000000000000..1e4a3d2a861ebf738ac1348c93b8cf1fecc86054 GIT binary patch literal 3745 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 BnuP!W literal 0 HcmV?d00001 diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index 49efc836beb..646e43a91e6 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -161,8 +161,9 @@ def load_winpcapy(): if conf.interactive: log_loading.warning(conf.color_theme.format( "Npcap/Winpcap is not installed ! See " - "https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501 - , "black+bg_red")) + "https://scapy.readthedocs.io/en/latest/installation.html#windows", # noqa: E501 + "black+bg_red" + )) if conf.use_winpcapy: def get_if_list(): diff --git a/scapy/themes.py b/scapy/themes.py index 6cb18979c35..be64aaa663a 100644 --- a/scapy/themes.py +++ b/scapy/themes.py @@ -90,6 +90,7 @@ def format(self, string, fmt): string = getattr(self, style)(string) return string + class NoTheme(ColorTheme): pass From 456dd35cba344146d6e2dcde62bcfc7530b8e45d Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Sun, 24 Mar 2019 14:22:58 +0100 Subject: [PATCH 3/6] Change permissions of .svg files --- doc/scapy/graphics/animations/animation-cansend.svg | 0 doc/scapy/graphics/animations/animation-rdcandump.svg | 0 doc/scapy/graphics/animations/animation-scapy-can.svg | 0 doc/scapy/graphics/animations/animation-scapy-canframe.svg | 0 doc/scapy/graphics/animations/animation-scapy-cansockets-mitm.svg | 0 .../graphics/animations/animation-scapy-cansockets-mitm2.svg | 0 .../graphics/animations/animation-scapy-cansockets-sniff.svg | 0 doc/scapy/graphics/animations/animation-scapy-gmlan.svg | 0 .../graphics/animations/animation-scapy-native-cansocket.svg | 0 doc/scapy/graphics/animations/animation-scapy-obd.svg | 0 .../graphics/animations/animation-scapy-python-can-cansocket.svg | 0 doc/scapy/graphics/animations/animation-scapy-rdcandump.svg | 0 doc/scapy/graphics/animations/animation-scapy-rdpcap.svg | 0 doc/scapy/graphics/animations/animation-scapy-uds.svg | 0 doc/scapy/graphics/animations/animation-scapy-uds2.svg | 0 doc/scapy/graphics/animations/animation-scapy-uds3.svg | 0 16 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 doc/scapy/graphics/animations/animation-cansend.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-rdcandump.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-can.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-canframe.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-cansockets-mitm.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-cansockets-mitm2.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-cansockets-sniff.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-gmlan.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-native-cansocket.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-obd.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-python-can-cansocket.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-rdcandump.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-rdpcap.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-uds.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-uds2.svg mode change 100644 => 100755 doc/scapy/graphics/animations/animation-scapy-uds3.svg diff --git a/doc/scapy/graphics/animations/animation-cansend.svg b/doc/scapy/graphics/animations/animation-cansend.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-rdcandump.svg b/doc/scapy/graphics/animations/animation-rdcandump.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-can.svg b/doc/scapy/graphics/animations/animation-scapy-can.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-canframe.svg b/doc/scapy/graphics/animations/animation-scapy-canframe.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm.svg b/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm2.svg b/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm2.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-cansockets-sniff.svg b/doc/scapy/graphics/animations/animation-scapy-cansockets-sniff.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-gmlan.svg b/doc/scapy/graphics/animations/animation-scapy-gmlan.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-native-cansocket.svg b/doc/scapy/graphics/animations/animation-scapy-native-cansocket.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-obd.svg b/doc/scapy/graphics/animations/animation-scapy-obd.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-python-can-cansocket.svg b/doc/scapy/graphics/animations/animation-scapy-python-can-cansocket.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-rdcandump.svg b/doc/scapy/graphics/animations/animation-scapy-rdcandump.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-rdpcap.svg b/doc/scapy/graphics/animations/animation-scapy-rdpcap.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-uds.svg b/doc/scapy/graphics/animations/animation-scapy-uds.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-uds2.svg b/doc/scapy/graphics/animations/animation-scapy-uds2.svg old mode 100644 new mode 100755 diff --git a/doc/scapy/graphics/animations/animation-scapy-uds3.svg b/doc/scapy/graphics/animations/animation-scapy-uds3.svg old mode 100644 new mode 100755 From 2b847ea0137f0eaa8775eb9f91ce8c3ff124c5d9 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Sun, 24 Mar 2019 14:31:20 +0100 Subject: [PATCH 4/6] Split advanced usage into separate files --- doc/scapy/advanced_usage.rst | 1387 ----------------- doc/scapy/build_dissect.rst | 4 + .../animations/animation-scapy-demo.svg | 44 + doc/scapy/graphics/scapy-main-console.png | Bin 74451 -> 0 bytes doc/scapy/index.rst | 17 +- doc/scapy/introduction.rst | 23 +- doc/scapy/layers/automative.rst | 1088 +++++++++++++ doc/scapy/{ => layers}/bluetooth.rst | 4 +- doc/scapy/layers/index.rst | 6 + doc/scapy/layers/pnio.rst | 269 ++++ doc/scapy/layers/sctp.rst | 27 + doc/scapy/usage.rst | 8 - 12 files changed, 1459 insertions(+), 1418 deletions(-) create mode 100644 doc/scapy/graphics/animations/animation-scapy-demo.svg delete mode 100644 doc/scapy/graphics/scapy-main-console.png create mode 100644 doc/scapy/layers/automative.rst rename doc/scapy/{ => layers}/bluetooth.rst (99%) create mode 100644 doc/scapy/layers/index.rst create mode 100644 doc/scapy/layers/pnio.rst create mode 100644 doc/scapy/layers/sctp.rst diff --git a/doc/scapy/advanced_usage.rst b/doc/scapy/advanced_usage.rst index 39797ee7b38..7ab9c8cead2 100644 --- a/doc/scapy/advanced_usage.rst +++ b/doc/scapy/advanced_usage.rst @@ -1072,1390 +1072,3 @@ Several triggering Drains exist, they are pretty explicit. It is highly recommen - TriggeredValve : Let messages alternatively pass or not, changing on trigger - TriggeredQueueingValve : Let messages alternatively pass or queued, changing on trigger - TriggeredSwitch : Let messages alternatively high or low, changing on trigger - -PROFINET IO RTC -=============== - -PROFINET IO is an industrial protocol composed of different layers such as the Real-Time Cyclic (RTC) layer, used to exchange data. However, this RTC layer is stateful and depends on a configuration sent through another layer: the DCE/RPC endpoint of PROFINET. This configuration defines where each exchanged piece of data must be located in the RTC ``data`` buffer, as well as the length of this same buffer. Building such packet is then a bit more complicated than other protocols. - -RTC data packet ---------------- - -The first thing to do when building the RTC ``data`` buffer is to instantiate each Scapy packet which represents a piece of data. Each one of them may require some specific piece of configuration, such as its length. All packets and their configuration are: - -* ``PNIORealTimeRawData``: a simple raw data like ``Raw`` - - * ``length``: defines the length of the data - -* ``Profisafe``: the PROFIsafe profile to perform functional safety - - * ``length``: defines the length of the whole packet - * ``CRC``: defines the length of the CRC, either ``3`` or ``4`` - -* ``PNIORealTimeIOxS``: either an IO Consumer or Provider Status byte - - * Doesn't require any configuration - -To instantiate one of these packets with its configuration, the ``config`` argument must be given. It is a ``dict()`` which contains all the required piece of configuration:: - - >>> load_contrib('pnio_rtc') - >>> raw(PNIORealTimeRawData(load='AAA', config={'length': 4})) - 'AAA\x00' - >>> raw(Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3})) - 'AAA\x00 BBB' - >>> hexdump(PNIORealTimeIOxS()) - 0000 80 . - - -RTC packet ----------- - -Now that a data packet can be instantiated, a whole RTC packet may be built. ``PNIORealTime`` contains a field ``data`` which is a list of all data packets to add in the buffer, however, without the configuration, Scapy won't be -able to dissect it:: - - >>> load_contrib("pnio_rtc") - >>> p=PNIORealTime(cycleCounter=1024, data=[ - ... PNIORealTimeIOxS(), - ... PNIORealTimeRawData(load='AAA', config={'length':4}) / PNIORealTimeIOxS(), - ... Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}) / PNIORealTimeIOxS(), - ... ]) - >>> p.show() - ###[ PROFINET Real-Time ]### - len= None - dataLen= None - \data\ - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0 - | extension= 0 - |###[ PNIO RTC Raw data ]### - | load= 'AAA' - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0 - | extension= 0 - |###[ PROFISafe ]### - | load= 'AAA' - | Control_Status= 0x20 - | CRC= 0x424242 - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0 - | extension= 0 - padding= '' - cycleCounter= 1024 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - - >>> p.show2() - ###[ PROFINET Real-Time ]### - len= 44 - dataLen= 15 - \data\ - |###[ PNIO RTC Raw data ]### - | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' - padding= '' - cycleCounter= 1024 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - -For Scapy to be able to dissect it correctly, one must also configure the layer for it to know the location of each data in the buffer. This configuration is saved in the dictionary ``conf.contribs["PNIO_RTC"]`` which can be updated with the ``pnio_update_config`` method. Each item in the dictionary uses the tuple ``(Ether.src, Ether.dst)`` as key, to be able to separate the configuration of each communication. Each value is then a list of a tuple which describes a data packet. It is composed of the negative index, from the end of the data buffer, of the packet position, the class of the packet as the second item and the ``config`` dictionary to provide to the class as last. If we continue the previous example, here is the configuration to set:: - - >>> load_contrib("pnio") - >>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO() / p - >>> e.show2() - ###[ Ethernet ]### - dst= 06:07:08:09:0a:0b - src= 00:01:02:03:04:05 - type= 0x8892 - ###[ ProfinetIO ]### - frameID= RT_CLASS_1 - ###[ PROFINET Real-Time ]### - len= 44 - dataLen= 15 - \data\ - |###[ PNIO RTC Raw data ]### - | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' - padding= '' - cycleCounter= 1024 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - >>> pnio_update_config({('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [ - ... (-9, Profisafe, {'length': 8, 'CRC': 3}), - ... (-9 - 5, PNIORealTimeRawData, {'length':4}), - ... ]}) - >>> e.show2() - ###[ Ethernet ]### - dst= 06:07:08:09:0a:0b - src= 00:01:02:03:04:05 - type= 0x8892 - ###[ ProfinetIO ]### - frameID= RT_CLASS_1 - ###[ PROFINET Real-Time ]### - len= 44 - dataLen= 15 - \data\ - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PNIO RTC Raw data ]### - | load= 'AAA' - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PROFISafe ]### - | load= 'AAA' - | Control_Status= 0x20 - | CRC= 0x424242L - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - padding= '' - cycleCounter= 1024 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - -If no data packets are configured for a given offset, it defaults to a ``PNIORealTimeIOxS``. However, this method is not very convenient for the user to configure the layer and it only affects the dissection of packets. In such cases, one may have access to several RTC packets, sniffed or retrieved from a PCAP file. Thus, ``PNIORealTime`` provides some methods to analyse a list of ``PNIORealTime`` packets and locate all data in it, based on simple heuristics. All of them take as first argument an iterable which contains the list of packets to analyse. - -* ``PNIORealTime.find_data()`` analyses the data buffer and separate real data from IOxS. It returns a dict which can be provided to ``pnio_update_config``. -* ``PNIORealTime.find_profisafe()`` analyses the data buffer and find the PROFIsafe profiles among the real data. It returns a dict which can be provided to ``pnio_update_config``. -* ``PNIORealTime.analyse_data()`` executes both previous methods and update the configuration. **This is usually the method to call.** -* ``PNIORealTime.draw_entropy()`` will draw the entropy of each byte in the data buffer. It can be used to easily visualize PROFIsafe locations as entropy is the base of the decision algorithm of ``find_profisafe``. - -:: - - >>> load_contrib('pnio_rtc') - >>> t=rdpcap('/path/to/trace.pcap', 1024) - >>> PNIORealTime.analyse_data(t) - {('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [(-19, , {'length': 1}), (-15, , {'CRC': 3, 'length': 6}), (-7, , {'CRC': 3, 'length': 5})]} - >>> t[100].show() - ###[ Ethernet ]### - dst= 06:07:08:09:0a:0b - src= 00:01:02:03:04:05 - type= n_802_1Q - ###[ 802.1Q ]### - prio= 6L - id= 0L - vlan= 0L - type= 0x8892 - ###[ ProfinetIO ]### - frameID= RT_CLASS_1 - ###[ PROFINET Real-Time ]### - len= 44 - dataLen= 22 - \data\ - |###[ PNIO RTC Raw data ]### - | load= '\x80\x80\x80\x80\x80\x80\x00\x80\x80\x80\x12:\x0e\x12\x80\x80\x00\x12\x8b\x97\xe3\x80' - padding= '' - cycleCounter= 6208 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - - >>> t[100].show2() - ###[ Ethernet ]### - dst= 06:07:08:09:0a:0b - src= 00:01:02:03:04:05 - type= n_802_1Q - ###[ 802.1Q ]### - prio= 6L - id= 0L - vlan= 0L - type= 0x8892 - ###[ ProfinetIO ]### - frameID= RT_CLASS_1 - ###[ PROFINET Real-Time ]### - len= 44 - dataLen= 22 - \data\ - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - [...] - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PNIO RTC Raw data ]### - | load= '' - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - [...] - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PROFISafe ]### - | load= '' - | Control_Status= 0x12 - | CRC= 0x3a0e12L - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - |###[ PROFISafe ]### - | load= '' - | Control_Status= 0x12 - | CRC= 0x8b97e3L - |###[ PNIO RTC IOxS ]### - | dataState= good - | instance= subslot - | reserved= 0x0L - | extension= 0L - padding= '' - cycleCounter= 6208 - dataStatus= primary+validData+run+no_problem - transferStatus= 0 - -In addition, one can see, when displaying a ``PNIORealTime`` packet, the field ``len``. This is a computed field which is not added in the final packet build. It is mainly useful for dissection and reconstruction, but it can also be used to modify the behaviour of the packet. In fact, RTC packet must always be long enough for an Ethernet frame and to do so, a padding must be added right after the ``data`` buffer. The default behaviour is to add ``padding`` whose size is computed during the ``build`` process:: - - >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) - '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' - -However, one can set ``len`` to modify this behaviour. ``len`` controls the length of the whole ``PNIORealTime`` packet. Then, to shorten the length of the padding, ``len`` can be set to a lower value:: - - >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=50)) - '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' - >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) - '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' - >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=30)) - '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' - - -SCTP -==== - -SCTP is a relatively young transport-layer protocol combining both TCP and UDP characteristics. The `RFC 3286 `_ introduces it and its description lays in the `RFC 4960 `_. - -It is not broadly used, its mainly present in core networks operated by telecommunication companies, to support VoIP for instance. - - -Enabling dynamic addressing reconfiguration and chunk authentication capabilities ---------------------------------------------------------------------------------- - -If you are trying to discuss with SCTP servers, you may be interested in capabilities added in `RFC 4895 `_ which describe how to authenticated some SCTP chunks, and/or `RFC 5061 `_ to dynamically reconfigure the IP address of a SCTP association. - -These capabilities are not always enabled by default on Linux. Scapy does not need any modification on its end, but SCTP servers may need specific activation. - -To enable the RFC 4895 about authenticating chunks:: - - $ sudo echo 1 > /proc/sys/net/sctp/auth_enable - -To enable the RFC 5061 about dynamic address reconfiguration:: - - $ sudo echo 1 > /proc/sys/net/sctp/addip_enable - -You may also want to use the dynamic address reconfiguration without necessarily enabling the chunk authentication:: - - $ sudo echo 1 > /proc/sys/net/sctp/addip_noauth_enable - - -Automotive Penetration Testing with Scapy -========================================= - -.. note:: - All automotive related features work best on Linux systems. CANSockets and ISOTPSockets in Scapy are based on Linux kernel modules. - The python-can project is used to support CAN and CANSockets on other systems, besides Linux. - This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. - The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. - The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy - on a more powerful machine. - -Protocols ---------- - -The following table should give a brief overview about all automotive capabilities -of Scapy. Most application layer protocols have many specialized ``Packet`` classes. -These special purpose classes are not part of this overview. Use the ``explore()`` -function to get all information about one specific protocol. - -+---------------------+----------------------+--------------------------------------------------------+ -| OSI Layer | Protocol | Scapy Implementations | -+=====================+======================+========================================================+ -| Application Layer | UDS (ISO 14229) | UDS, UDS_* | -| +----------------------+--------------------------------------------------------+ -| | GMLAN | GMLAN, GMLAN_* | -| +----------------------+--------------------------------------------------------+ -| | SOME/IP | SOMEIP, SD | -| +----------------------+--------------------------------------------------------+ -| | BMW ENET | ENET, ENETSocket | -| +----------------------+--------------------------------------------------------+ -| | OBD | OBD, OBD_S0X | -| +----------------------+--------------------------------------------------------+ -| | CCP | CCP, DTO, CRO | -+---------------------+----------------------+--------------------------------------------------------+ -| Transportaion Layer | ISO-TP (ISO 15765-2) | ISOTPSocket, ISOTPNativeSocket, ISOTPSoftSocket | -| | | | -| | | ISOTPSniffer, ISOTPMessageBuilder | -| | | | -| | | ISOTPHeader, ISOTPHeaderEA, | -| | | | -| | | ISOTP, ISOTP_SF, ISOTP_FF, ISOTP_CF, ISOTP_FC | -+---------------------+----------------------+--------------------------------------------------------+ -| Data Link Layer | CAN (ISO 11898) | CAN, CANSocket, rdcandump | -+---------------------+----------------------+--------------------------------------------------------+ - - -Hands-On -^^^^^^^^ - -Send a message over Linux SocketCAN:: - - load_layer('can') - load_contrib('cansocket') - socket = CANSocket(iface='can0') - packet = CAN(identifier=0x123, data=b'01020304') - - socket.sr1(packet, timeout=1) - - srcan(packet, 'can0', timeout=1) - -Send a message over a Vector CAN-Interface:: - - import can - load_layer('can') - conf.contribs['CANSocket'] = {'use-python-can' : True} - load_contrib('cansocket') - from can.interfaces.vector import VectorBus - socket = CANSocket(iface=VectorBus(0, bitrate=1000000)) - packet = CAN(identifier=0x123, data=b'01020304') - socket.sr1(packet) - - srcan(packet, VectorBus(0, bitrate=1000000)) - -System compatibilities ----------------------- - -Dependent on your setup, different implementations have to be used. - -+---------------------+----------------------+-------------------------------------+----------------------------------------------------------+ -| Python \ OS | Linux with can_isotp | Linux wo can_isotp | Windows / OSX | -+=====================+======================+=====================================+==========================================================+ -| Python 3 | ISOTPNativeSocket | ISOTPSoftSocket | ISOTPSoftSocket | -| +----------------------+-------------------------------------+ | -| | ``conf.contribs['CANSocket'] = {'use-python-can': False}`` | ``conf.contribs['CANSocket'] = {'use-python-can': True}``| -+---------------------+------------------------------------------------------------+----------------------------------------------------------+ -| Python 2 | ISOTPSoftSocket | ISOTPSoftSocket | -| | | | -| | ``conf.contribs['CANSocket'] = {'use-python-can': True}`` | ``conf.contribs['CANSocket'] = {'use-python-can': True}``| -+---------------------+------------------------------------------------------------+----------------------------------------------------------+ - -The class ``ISOTPSocket`` can be set to a ``ISOTPNativeSocket`` or a ``ISOTPSoftSocket``. -The decision is made dependent on the configuration ``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}`` (to select ``ISOTPNativeSocket``) or -``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}`` (to select ``ISOTPSoftSocket``). -This will allow you to write platform independent code. Apply this configuration before loading the ISOTP layer -with ``load_contrib("isotp")``. - -Another remark in respect to ISOTPSocket compatibility. Always use with for socket creation. Example:: - - with ISOTPSocket("vcan0", did=0x241, sid=0x641) as sock: - sock.send(...) - - -CAN Layer ---------- - -Setup -^^^^^ - -These commands enable a virtual CAN interface on a Linux machine:: - - from scapy.layers.can import * - import os - - bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" - os.system(bashCommand) - -If it's required, the CAN interface can be set into a ``listen-only`` or ``loopback`` mode with `ip link set` commands:: - - ip link set vcan0 type can help # shows additional information - - -This example shows a basic functions of Linux can-utils. These utilities are handy for -quick checks or logging. - -.. image:: graphics/animations/animation-cansend.svg - -CAN Frame -^^^^^^^^^ - -Creating a standard CAN frame:: - - frame = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') - -Creating an extended CAN frame:: - - frame = CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') - -.. image:: graphics/animations/animation-scapy-canframe.svg - -Writing and reading to pcap files:: - - x = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') - wrpcap('/tmp/scapyPcapTest.pcap', x, append=False) - y = rdpcap('/tmp/scapyPcapTest.pcap', 1) - -.. image:: graphics/animations/animation-scapy-rdpcap.svg -.. image:: graphics/animations/animation-scapy-rdcandump.svg - -CANSocket native -^^^^^^^^^^^^^^^^ - -Creating a simple native CANSocket:: - - conf.contribs['CANSocket'] = {'use-python-can': False} #(default) - load_contrib('cansocket') - - # Simple Socket - socket = CANSocket(iface="vcan0") - -Creating a native CANSocket only listen for messages with Id == 0x200:: - - socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x7FF}]) - -Creating a native CANSocket only listen for messages with Id >= 0x200 and Id <= 0x2ff:: - - socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]) - -Creating a native CANSocket only listen for messages with Id != 0x200:: - - socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}]) - -Creating a native CANSocket with multiple can_filters:: - - socket = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, - {'can_id': 0x400, 'can_mask': 0x7ff}, - {'can_id': 0x600, 'can_mask': 0x7ff}, - {'can_id': 0x7ff, 'can_mask': 0x7ff}]) - -Creating a native CANSocket which also receives its own messages:: - - socket = CANSocket(iface="vcan0", receive_own_messages=True) - -.. image:: graphics/animations/animation-scapy-native-cansocket.svg - -Sniff on a CANSocket: - -.. image:: graphics/animations/animation-scapy-cansockets-sniff.svg - - -CANSocket python-can -^^^^^^^^^^^^^^^^^^^^ - -python-can is required to use various CAN-interfaces on Windows, OSX or Linux. -The python-can library is used through a CANSocket object. To create a python-can -CANSocket object, a python-can ``Bus`` object has to be used as interface. -The ``timeout`` parameter can be used to increase the receive performance of a -python-can CANSocket object. ``recv`` inside a python-can CANSocket object is -implemented through busy wait, since there is no ``select`` functionality on -Windows or on some proprietary CAN interfaces (like Vector interfaces). A small -``timeout`` might be required, if a ``sniff`` or ``bridge_and_sniff`` on multiple -interfaces is performed. - -Ways of creating a python-can CANSocket:: - - conf.contribs['CANSocket'] = {'use-python-can': True} - load_contrib('cansocket') - import can - -Creating a simple python-can CANSocket:: - - socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)) - -Creating a python-can CANSocket with multiple filters:: - - socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, - can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, - {'can_id': 0x400, 'can_mask': 0x7ff}, - {'can_id': 0x600, 'can_mask': 0x7ff}, - {'can_id': 0x7ff, 'can_mask': 0x7ff}])) - -.. image:: graphics/animations/animation-scapy-python-can-cansocket.svg - -For further details on python-can check: https://python-can.readthedocs.io/en/2.2.0/ - -CANSocket MITM attack with bridge and sniff -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This example shows how to use bridge and sniff on virtual CAN interfaces. -For real world applications, use real CAN interfaces. -Set up two vcans on Linux terminal:: - - sudo modprobe vcan - sudo ip link add name vcan0 type vcan - sudo ip link add name vcan1 type vcan - sudo ip link set dev vcan0 up - sudo ip link set dev vcan1 up - -Import modules:: - - import threading - load_contrib('cansocket') - load_layer("can") - -Create can sockets for attack:: - - socket0 = CANSocket(iface='vcan0') - socket1 = CANSocket(iface='vcan1') - -Create a function to send packet with threading:: - - def sendPacket(): - sleep(0.2) - socket0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) - -Create a function for forwarding or change packets:: - - def forwarding(pkt): - return pkt - -Create a function to bridge and sniff between two sockets:: - - def bridge(): - bSocket0 = CANSocket(iface='vcan0') - bSocket1 = CANSocket(iface='vcan1') - bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) - bSocket0.close() - bSocket1.close() - -Create threads for sending packet and to bridge and sniff:: - - threadBridge = threading.Thread(target=bridge) - threadSender = threading.Thread(target=sendMessage) - -Start the threads:: - - threadBridge.start() - threadSender.start() - -Sniff packets:: - - packets = socket1.sniff(timeout=0.3) - -Close the sockets:: - - socket0.close() - socket1.close() - -.. image:: graphics/animations/animation-scapy-cansockets-mitm.svg -.. image:: graphics/animations/animation-scapy-cansockets-mitm2.svg - - -CAN Calibration Protocol (CCP) ------------------------------- - -CCP is derived from CAN. The CAN-header is part of a CCP frame. CCP has two types -of message objects. One is called Command Receive Object (CRO), the other is called -Data Transmission Object (DTO). Usually CROs are sent to an ECU, and DTOs are received -from an ECU. The information, if one DTO answers a CRO is implemented through a counter -field (ctr). If both objects have the same counter value, the payload of a DTO object -can be interpreted from the command of the associated CRO object. - -Creating a CRO message:: - - CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) - CCP(identifier=0x711)/CRO(ctr=2)/GET_SEED(resource=2) - CCP(identifier=0x711)/CRO(ctr=3)/UNLOCK(key=b"123456") - -If we aren't interested in the DTO of an ECU, we can just send a CRO message like this: -Sending a CRO message:: - - pkt = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) - sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)) - sock.send(pkt) - -If we are interested in the DTO of an ECU, we need to set the basecls parameter of the -CANSocket to CCP and we need to use sr1: -Sending a CRO message:: - - cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12") - sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP) - dto = sock.sr1(cro) - dto.show() - ###[ CAN Calibration Protocol ]### - flags= - identifier= 0x700 - length= 8 - reserved= 0 - ###[ DTO ]### - packet_id= 0xff - return_code= acknowledge / no error - ctr= 83 - ###[ PROGRAM_6_DTO ]### - MTA0_extension= 2 - MTA0_address= 0x34002006 - -Since sr1 calls the answers function, our payload of the DTO objects gets interpreted with the -command of our CRO object. - -ISOTP ------ - -ISOTP message -^^^^^^^^^^^^^ - -Creating an ISOTP message:: - - load_contrib('isotp') - ISOTP(src=0x241, dst=0x641, data=b"\x3eabc") - -Creating an ISOTP message with extended addressing:: - - ISOTP(src=0x241, dst=0x641, exdst=0x41, data=b"\x3eabc") - -Creating an ISOTP message with extended addressing:: - - ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x41, data=b"\x3eabc") - -Create CAN-frames from an ISOTP message:: - - ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x55, data=b"\x3eabc" * 10).fragment() - -Send ISOTP message over ISOTP socket:: - - isoTpSocket = ISOTPSocket('vcan0', sid=0x241, did=0x641) - isoTpMessage = ISOTP('Message') - isoTpSocket.send(isoTpMessage) - -Sniff ISOTP message:: - - isoTpSocket = ISOTPSocket('vcan0', sid=0x641, did=0x241) - packets = isoTpSocket.sniff(timeout=0.5) - -ISOTP MITM attack with bridge and sniff -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Set up two vcans on Linux terminal:: - - sudo modprobe vcan - sudo ip link add name vcan0 type vcan - sudo ip link add name vcan1 type vcan - sudo ip link set dev vcan0 up - sudo ip link set dev vcan1 up - -Set up ISOTP:: - -.. note:: - - First make sure you build an iso-tp kernel module. - -When the vcan core module is loaded with "sudo modprobe vcan" the iso-tp module can be loaded to the kernel. - -Therefore navigate to isotp directory, and load module with "sudo insmod ./net/can/can-isotp.ko". (Tested on Kernel 4.9.135-1-MANJARO) - -Detailed instructions you find in https://github.com/hartkopp/can-isotp. - -Import modules:: - - import threading - load_contrib('cansocket') - conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} - load_contrib('isotp') - -Create to ISOTP sockets for attack:: - - isoTpSocketVCan0 = ISOTPSocket('vcan0', sid=0x241, did=0x641) - isoTpSocketVCan1 = ISOTPSocket('vcan1', sid=0x641, did=0x241) - -Create function to send packet on vcan0 with threading:: - - def sendPacketWithISOTPSocket(): - sleep(0.2) - packet = ISOTP('Request') - isoTpSocketVCan0.send(packet) - -Create function to forward packet:: - - def forwarding(pkt): - return pkt - -Create function to bridge and sniff between two buses:: - - def bridge(): - bSocket0 = ISOTPSocket('vcan0', sid=0x641, did=0x241) - bSocket1 = ISOTPSocket('vcan1', sid=0x241, did=0x641) - bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) - bSocket0.close() - bSocket1.close() - -Create threads for sending packet and to bridge and sniff:: - - threadBridge = threading.Thread(target=bridge) - threadSender = threading.Thread(target=sendPacketWithISOTPSocket) - -Start threads are based on Linux kernel modules. The python-can project is used to support CAN and CANSockets on other systems, besides Linux. This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy on a more powerful machine.:: - - threadBridge.start() - threadSender.start() - -Sniff on vcan1:: - - receive = isoTpSocketVCan1.sniff(timeout=1) - -Close sockets:: - - isoTpSocketVCan0.close() - isoTpSocketVCan1.close() - -An ISOTPSocket will not respect ``src, dst, exdst, exsrc`` of an ISOTP message object. - -ISOTP Sockets -------------- - -Scapy provides two kinds of ISOTP Sockets. One implementation, the ISOTPNativeSocket -is using the Linux kernel module from Hartkopp. The other implementation, the ISOTPSoftSocket -is completely implemented in Python. This implementation can be used on Linux, -Windows, and OSX. - -ISOTPNativeSocket -^^^^^^^^^^^^^^^^^ - -**Requires:** - -* Python3 -* Linux -* Hartkopp's Linux kernel module: ``https://github.com/hartkopp/can-isotp.git`` - -During pentests, the ISOTPNativeSockets do have a better performance and -reliability, usually. If you are working on Linux, consider this implementation:: - - conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} - load_contrib('isotp') - sock = ISOTPSocket("can0", sid=0x641, did=0x241) - -Since this implementation is using a standard Linux socket, all Scapy functions -like ``sniff, sr, sr1, bridge_and_sniff`` work out of the box. - -ISOTPSoftSocket -^^^^^^^^^^^^^^^ - -ISOTPSoftSockets can use any CANSocket. This gives the flexibility to use all -python-can interfaces. Additionally, these sockets work on Python2 and Python3. -Usage on Linux with native CANSockets:: - - conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} - load_contrib('isotp') - with ISOTPSocket("can0", sid=0x641, did=0x241) as sock: - sock.send(...) - -Usage with python-can CANSockets:: - - conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} - conf.contribs['CANSocket'] = {'use-python-can': True} - load_contrib('isotp') - with ISOTPSocket(CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel="can0", bitrate=250000)), sid=0x641, did=0x241) as sock: - sock.send(...) - -This second example allows the usage of any ``python_can.interface`` object. - -**Attention:** The internal implementation of ISOTPSoftSockets requires a background -thread. In order to be able to close this thread properly, we suggest the use of -Pythons ``with`` statement. - - -UDS ---- - -The main usage of UDS is flashing and diagnostic of an ECU. UDS is an -application layer protocol and can be used as a DoIP or ENET payload or a UDS packet -can directly be sent over an ISOTPSocket. Every OEM has its own customization of UDS. -This increases the difficulty of generic applications and OEM specific knowledge is -required for penetration tests. RoutineControl jobs and ReadDataByIdentifier/WriteDataByIdentifier -services are heavily customized. - -Use the argument ``basecls=UDS`` on the ``init`` function of an ISOTPSocket. - -Here are two usage examples: - -.. image:: graphics/animations/animation-scapy-uds.svg -.. image:: graphics/animations/animation-scapy-uds2.svg - - -Customization of UDS_RDBI, UDS_WDBI -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In real-world use-cases, the UDS layer is heavily customized. OEMs define there own substructure of packets. -Especially the packets ReadDataByIdentifier or WriteDataByIdentifier have a very OEM or even ECU specific -substructure. Therefore a ``StrField`` ``dataRecord`` is not added to the ``field_desc``. -The intended usage is to create ECU or OEM specific description files, which extend the general UDS layer of -Scapy with further protocol implementations. - -Customization example:: - - cat scapy/contrib/automotive/OEM-XYZ/car-model-xyz.py - #! /usr/bin/env python - - # Protocol customization for car model xyz of OEM XYZ - # This file contains further OEM car model specific UDS additions. - - from scapy.packet import Packet - from scapy.contrib.automotive.uds import * - - # Define a new packet substructure - - class DBI_IP(Packet): - name = 'DataByIdentifier_IP_Packet' - fields_desc = [ - ByteField('ADDRESS_FORMAT_ID', 0), - IPField('IP', ''), - IPField('SUBNETMASK', ''), - IPField('DEFAULT_GATEWAY', '') - ] - - # Bind the new substructure onto the existing UDS packets - - bind_layers(UDS_RDBIPR, DBI_IP, dataIdentifier=0x172b) - bind_layers(UDS_WDBI, DBI_IP, dataIdentifier=0x172b) - - # Give add a nice name to dataIdentifiers enum - - UDS_RDBI.dataIdentifiers[0x172b] = 'GatewayIP' - -If one wants to work with this custom additions, these can be loaded at runtime to the Scapy interpreter:: - - >>> load_contrib("automotive.uds") - >>> load_contrib("automotive.OEM-XYZ.car-model-xyz") - - >>> pkt = UDS()/UDS_WDBI()/DBI_IP(IP='192.168.2.1', SUBNETMASK='255.255.255.0', DEFAULT_GATEWAY='192.168.2.1') - - >>> pkt.show() - ###[ UDS ]### - service= WriteDataByIdentifier - ###[ WriteDataByIdentifier ]### - dataIdentifier= GatewayIP - dataRecord= 0 - ###[ DataByIdentifier_IP_Packet ]### - ADDRESS_FORMAT_ID= 0 - IP= 192.168.2.1 - SUBNETMASK= 255.255.255.0 - DEFAULT_GATEWAY= 192.168.2.1 - - >>> hexdump(pkt) - 0000 2E 17 2B 00 C0 A8 02 01 FF FF FF 00 C0 A8 02 01 ..+............. - -.. image:: graphics/animations/animation-scapy-uds3.svg - -GMLAN ------ -GMLAN is very similar to UDS. It's GMs application layer protocol for -flashing, calibration and diagnostic of their cars. -Use the argument ``basecls=GMLAN`` on the ``init`` function of an ISOTPSocket. - -Usage example: - -.. image:: graphics/animations/animation-scapy-gmlan.svg - - -SOME/IP and SOME/IP SD messages -------------------------------- - -Creating a SOME/IP message -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This example shows a SOME/IP message which requests a service 0x1234 with the method 0x421. Different types of SOME/IP messages follow the same procedure and their specifications can be seen here ``http://www.some-ip.com/papers/cache/AUTOSAR_TR_SomeIpExample_4.2.1.pdf``. - - -Load the contribution:: - - load_contrib("automotive.someip") - -Create UDP package:: - - u = UDP(sport=30509, dport=30509) - -Create IP package:: - - i = IP(src="192.168.0.13", dst="192.168.0.10") - -Create SOME/IP package:: - - sip = SOMEIP() - sip.iface_ver = 0 - sip.proto_ver = 1 - sip.msg_type = "REQUEST" - sip.retcode = "E_OK" - sip.msg_id.srv_id = 0x1234 - sip.msg_id.method_id = 0x421 - -Add the payload:: - - sip.add_payload(Raw ("Hello")) - -Stack it and send it:: - - p = i/u/sip - send(p) - - -Creating a SOME/IP SD message -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In this example a SOME/IP SD offer service message is shown with an IPv4 endpoint. Different entries and options basically follow the same procedure as shown here and can be seen at ``https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_ServiceDiscovery.pdf``. - -Load the contribution:: - - load_contrib("automotive.someip_sd") - -Create UDP package:: - - u = UDP(sport=30490, dport=30490) - -The UDP port must be the one which was chosen for the SOME/IP SD transmission. - -Create IP package:: - - i = IP(src="192.168.0.13", dst="224.224.224.245") - -The IP source must be from the service and the destination address needs to be the chosen multicast address. - -Create the entry array input:: - - ea = SDEntry_Service() - - ea.type = 0x01 - ea.srv_id = 0x1234 - ea.inst_id = 0x5678 - ea.major_ver = 0x00 - ea.ttl = 3 - -Create the options array input:: - - oa = SDOption_IP4_Endpoint() - oa.addr = "192.168.0.13" - oa.l4_proto = 0x11 - oa.port = 30509 - -l4_proto defines the protocol for the communication with the endpoint, UDP in this case. - -Create the SD package and put in the inputs:: - - sd = SD() - sd.set_entryArray(ea) - sd.set_optionArray(oa) - spsd = sd.get_someip(True) - -The get_someip method stacks the SOMEIP/SD message on top of a SOME/IP message, which has the desired SOME/IP values prefilled for the SOME/IP SD package transmission. - -Stack it and send it:: - - p = i/u/spsd - send(p) - - - - -OBD message -------------- - -OBD is implemented on top of ISOTP. Use an ISOTPSocket for the communication with a ECU. -You should set the parameters ``basecls=OBD`` and ``padding=True`` in your ISOTPSocket init call. - -OBD is split into different service groups. Here are some example requests: - -Request supported PIDs of service 0x01:: - - req = OBD()/OBD_S01(pid=[0x00]) - -The response will contain a PacketListField, called `data_records`. This field contains the actual response:: - - resp = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids=3196041235)]) - resp.show() - ###[ On-board diagnostics ]### - service= CurrentPowertrainDiagnosticDataResponse - ###[ Parameter IDs ]### - \data_records\ - |###[ OBD_S01_PR_Record ]### - | pid= 0x0 - |###[ PID_00_PIDsSupported ]### - | supported_pids= PID20+PID1F+PID1C+PID15+PID14+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID0A+PID07+PID06+PID05+PID04+PID03+PID01 - -Let's assume our ECU under test supports the pid 0x15:: - - req = OBD()/OBD_S01(pid=[0x15]) - resp = sock.sr1(req) - resp.show() - ###[ On-board diagnostics ]### - service= CurrentPowertrainDiagnosticDataResponse - ###[ Parameter IDs ]### - \data_records\ - |###[ OBD_S01_PR_Record ]### - | pid= 0x15 - |###[ PID_15_OxygenSensor2 ]### - | outputVoltage= 1.275 V - | trim= 0 % - - -The different services in OBD support different kinds of data. -Service 01 and Service 02 support Parameter Identifiers (pid). -Service 03, 07 and 0A support Diagnostic Trouble codes (dtc). -Service 04 doesn't require a payload. -Service 05 is not implemented on OBD over CAN. -Service 06 support Monitoring Identifiers (mid). -Service 08 support Test Identifiers (tid). -Service 09 support Information Identifiers (iid). - -Examples: -^^^^^^^^^ - -Request supported Information Identifiers:: - - req = OBD()/OBD_S09(iid=[0x00]) - -Request the Vehicle Identification Number (VIN):: - - req = OBD()/OBD_S09(iid=0x02) - resp = sock.sr1(req) - resp.show() - ###[ On-board diagnostics ]### - service= VehicleInformationResponse - ###[ Infotype IDs ]### - \data_records\ - |###[ OBD_S09_PR_Record ]### - | iid= 0x2 - |###[ IID_02_VehicleIdentificationNumber ]### - | count= 1 - | vehicle_identification_numbers= ['W0L000051T2123456'] - - -.. image:: graphics/animations/animation-scapy-obd.svg - - -Test-Setup Tutorials --------------------- - -Hardware Setup -^^^^^^^^^^^^^^ - -Beagle Bone Black Operating System Setup -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. | **Download an Image** - | The latest Debian Linux image can be found at the website - | ``https://beagleboard.org/latest-images``. Choose the BeagleBone - Black IoT version and download it. - - :: - - wget https://debian.beagleboard.org/images/bone-debian-8.7\ - -iot-armhf-2017-03-19-4gb.img.xz - - - After the download, copy it to an SD-Card with minimum of 4 GB storage. - - :: - - xzcat bone-debian-8.7-iot-armhf-2017-03-19-4gb.img.xz | \ - sudo dd of=/dev/xvdj - - -#. | **Enable WiFi** - | USB-WiFi dongles are well supported by Debian Linux. Login over SSH - on the BBB and add the WiFi network credentials to the file - ``/var/lib/connman/wifi.config``. If a USB-WiFi dongle is not - available, it is also possible to share the host's internet - connection with the Ethernet connection of the BBB emulated over - USB. A tutorial to share the host network connection can be found - on this page: - | ``https://elementztechblog.wordpress.com/2014/12/22/sharing-internet -using-network-over-usb-in-beaglebone-black/``. - | Login as root onto the BBB: - - :: - - ssh debian@192.168.7.2 - sudo su - - - Provide the WiFi login credentials to connman: - - :: - - echo "[service_home] - Type = wifi - Name = ssid - Security = wpa - Passphrase = xxxxxxxxxxxxx" \ - > /var/lib/connman/wifi.config - - - Restart the connman service: - - :: - - systemctl restart connman.service - - -Dual-CAN Setup -~~~~~~~~~~~~~~ - -#. | **Device tree setup** - | You'll need to follow this section only if you want to use two CAN - interfaces (DCAN0 and DCAN1). This will disable I2C2 from using pins - P9.19 and P9.20, which are needed by DCAN0. You only need to perform the - steps in this section once. - - | Warning: The configuration in this section will disable BBB capes from - working. Each cape has a small I2C EEPROM that stores info that the BBB - needs to know in order to communicate with the cape. Disable I2C2, and - the BBB has no way to talk to cape EEPROMs. Of course, if you don't use - capes then this is not a problem. - - | Acquire DTS sources that matches your kernel version. Go - `here `__ and switch over to the - branch that represents your kernel version. Download the entire branch - as a ZIP file. Extract it and do the following (version 4.1 shown as an - example): - - :: - - # cd ~/src/linux-4.1/arch/arm/boot/dts/include/ - # rm dt-bindings - # ln -s ../../../../../include/dt-bindings - # cd .. - Edit am335x-bone-common.dtsi and ensure the line with "//pinctrl-0 = <&i2c2_pins>;" is commented out. - Remove the complete &ocp section at the end of this file - # mv am335x-boneblack.dts am335x-boneblack.raw.dts - # cpp -nostdinc -I include -undef -x assembler-with-cpp am335x-boneblack.raw.dts > am335x-boneblack.dts - # dtc -W no-unit_address_vs_reg -O dtb -o am335x-boneblack.dtb -b 0 -@ am335x-boneblack.dts - # cp /boot/dtbs/am335x-boneblack.dtb /boot/dtbs/am335x-boneblack.orig.dtb - # cp am335x-boneblack.dtb /boot/dtbs/ - Reboot - -#. **Overlay setup** - | This section describes how to build the device overlays for the two CAN devices (DCAN0 and DCAN1). You only need to perform the steps in this section once. - | Acquire BBB cape overlays, in one of two ways… - - :: - - # apt-get install bb-cape-overlays - https://github.com/beagleboard/bb.org-overlays/ - - | Then do the following: - - - :: - - # cd ~/src/bb.org-overlays-master/src/arm - # ln -s ../../include - # mv BB-CAN1-00A0.dts BB-CAN1-00A0.raw.dts - # cp BB-CAN1-00A0.raw.dts BB-CAN0-00A0.raw.dts - Edit BB-CAN0-00A0.raw.dts and make relevant to CAN0. Example is shown below. - # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN0-00A0.raw.dts > BB-CAN0-00A0.dts - # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN1-00A0.raw.dts > BB-CAN1-00A0.dts - # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN0-00A0.dtbo -b 0 -@ BB-CAN0-00A0.dts - # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN1-00A0.dtbo -b 0 -@ BB-CAN1-00A0.dts - # cp *.dtbo /lib/firmware - - -#. | **CAN0 Example Overlay** - | Inside the DTS folder, create a file with the content of the - following listing. - - :: - - cd ~/bb.org-overlays/src/arm - cat < BB-CAN0-00A0.raw.dts - - /* - * Copyright (C) 2015 Robert Nelson - * - * Virtual cape for CAN0 on connector pins P9.19 P9.20 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - /dts-v1/; - /plugin/; - - #include - #include - - / { - compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green"; - - /* identification */ - part-number = "BB-CAN0"; - version = "00A0"; - - /* state the resources this cape uses */ - exclusive-use = - /* the pin header uses */ - "P9.19", /* can0_rx */ - "P9.20", /* can0_tx */ - /* the hardware ip uses */ - "dcan0"; - - fragment@0 { - target = <&am33xx_pinmux>; - __overlay__ { - bb_dcan0_pins: pinmux_dcan0_pins { - pinctrl-single,pins = < - BONE_P9_19 (PIN_INPUT_PULLUP | MUX_MODE2) /* uart1_txd.d_can0_rx */ - BONE_P9_20 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* uart1_rxd.d_can0_tx */ - >; - }; - }; - }; - - fragment@1 { - target = <&dcan0>; - __overlay__ { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&bb_dcan0_pins>; - }; - }; - }; - EOF - - -#. | **Test the Dual-CAN Setup** - | Do the following each time you need CAN, or automate these steps if you like. - - :: - - # echo BB-CAN0 > /sys/devices/platform/bone_capemgr/slots - # echo BB-CAN1 > /sys/devices/platform/bone_capemgr/slots - # modprobe can - # modprobe can-dev - # modprobe can-raw - # ip link set can0 up type can bitrate 50000 - # ip link set can1 up type can bitrate 50000 - - Check the output of the Capemanager if both CAN interfaces have been - loaded. - - :: - - cat /sys/devices/platform/bone_capemgr/slots - - 0: PF---- -1 - 1: PF---- -1 - 2: PF---- -1 - 3: PF---- -1 - 4: P-O-L- 0 Override Board Name,00A0,Override Manuf, BB-CAN0 - 5: P-O-L- 1 Override Board Name,00A0,Override Manuf, BB-CAN1 - - - If something went wrong, ``dmesg`` provides kernel messages to analyse the root of failure. - -#. | **References** - - - `embedded-things.com: Enable CANbus on the Beaglebone - Black `__ - - `electronics.stackexchange.com: Beaglebone Black CAN bus - Setup `__ - -#. | **Acknowledgment** - | Thanks to Tom Haramori. Parts of this section are copied from his guide: https://github.com/haramori/rhme3/blob/master/Preparation/BBB_CAN_setup.md - - - -ISO-TP Kernel Module Installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A Linux ISO-TP kernel module can be downloaded from this website: -``https://github.com/hartkopp/can-isotp.git``. The file -``README.isotp`` in this repository provides all information and -necessary steps for downloading and building this kernel module. The -ISO-TP kernel module should also be added to the ``/etc/modules`` file, -to load this module automatically at system boot of the BBB. - -CAN-Interface Setup -~~~~~~~~~~~~~~~~~~~ - -As the final step to prepare the BBB's CAN interfaces for usage, these -interfaces have to be set up through some terminal commands. The bitrate -can be chosen to fit the bitrate of a CAN bus under test. - -:: - - ip link set can0 up type can bitrate 500000 - ip link set can1 up type can bitrate 500000 - -Raspberry Pi SOME/IP setup -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To build a small test environment in which you can send SOME/IP messages to and from server instances or disguise yourself as a server, one Raspberry Pi, your laptop and the vsomeip library are sufficient. - -#. | **Download image** - - Download the latest raspbian image (``https://www.raspberrypi.org/downloads/raspbian/``) and install it on the Raspberry. - -#. | **Vsomeip setup** - - Download the vsomeip library on the Rapsberry, apply the git patch so it can work with the newer boost libraries and then install it. - - :: - - git clone https://github.com/GENIVI/vsomeip.git - cd vsomeip - wget -O 0001-Support-boost-v1.66.patch.zip \ - https://github.com/GENIVI/vsomeip/files/2244890/0001-Support-boost-v1.66.patch.zip - unzip 0001-Support-boost-v1.66.patch.zip - git apply 0001-Support-boost-v1.66.patch - mkdir build - cd build - cmake -DENABLE_SIGNAL_HANDLING=1 .. - make - make install - -#. | **Make applications** - - Write some small applications which function as either a service or a client and use the Scapy SOME/IP implementation to communicate with the client or the server. Examples for vsomeip applications are available on the vsomeip github wiki page (``https://github.com/GENIVI/vsomeip/wiki/vsomeip-in-10-minutes``). - - - -Software Setup -^^^^^^^^^^^^^^ - -Cannelloni Framework Installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Cannelloni framework is a small application written in C++ to -transfer CAN data over UDP. In this way, a researcher can map the CAN -communication of a remote device to its workstation, or even combine -multiple remote CAN devices on his machine. The framework can be -downloaded from this website: -``https://github.com/mguentner/cannelloni.git``. The ``README.md`` file -explains the installation and usage in detail. Cannelloni needs virtual -CAN interfaces on the operator's machine. The next listing shows the -setup of virtual CAN interfaces. - -:: - - modprobe vcan - - ip link add name vcan0 type vcan - ip link add name vcan1 type vcan - - ip link set dev vcan0 up - ip link set dev vcan1 up - - tc qdisc add dev vcan0 root tbf rate 300kbit latency 100ms burst 1000 - tc qdisc add dev vcan1 root tbf rate 300kbit latency 100ms burst 1000 - - cannelloni -I vcan0 -R -r 20000 -l 20000 & - cannelloni -I vcan1 -R -r 20001 -l 20001 & - - diff --git a/doc/scapy/build_dissect.rst b/doc/scapy/build_dissect.rst index 0c67060b40e..feadfde5dff 100644 --- a/doc/scapy/build_dissect.rst +++ b/doc/scapy/build_dissect.rst @@ -1144,4 +1144,8 @@ Scapy provides an ``explore()`` function, to search through the available layer/ # scapy.contrib.status = [...] # scapy.contrib.name = [...] (optional) +- If the contrib module does not contain any packets, and should not be indexed in `explore()`, then you should instead set:: + + # scapy.contrib.status = skip + - A **layer** module must have a docstring, in which the first line shortly describes the module. diff --git a/doc/scapy/graphics/animations/animation-scapy-demo.svg b/doc/scapy/graphics/animations/animation-scapy-demo.svg new file mode 100644 index 00000000000..7e7268f74c6 --- /dev/null +++ b/doc/scapy/graphics/animations/animation-scapy-demo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + demo@scapy:~/github/scapy# demo@scapy:~/github/scapy# s demo@scapy:~/github/scapy# su demo@scapy:~/github/scapy# sud demo@scapy:~/github/scapy# sudo demo@scapy:~/github/scapy# sudo demo@scapy:~/github/scapy# sudo . demo@scapy:~/github/scapy# sudo ./ demo@scapy:~/github/scapy# sudo ./r demo@scapy:~/github/scapy# sudo ./ru demo@scapy:~/github/scapy# sudo ./run_scapy WARNING: No route found for IPv6 destination :: (no default route?) e apyyyyCY//////////YCa | >>> >>> e >>> ex >>> exp >>> expl >>> explo >>> explor >>> explore >>> explore( >>> explore() demo@scapy:~/github/scapy# sudo ./run_scapyINFO: Can't import matplotlib. Won't be able to plot.WARNING: No route found for IPv6 destination :: (no default route?)e aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.2.dev228 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.2.0>>> explore() ┌────────────────────────| Scapy v2.4.2.dev228 |────────────────────────┐ │ │ Chose the type of packets you want to explore: < Layers > < Contribs > < Cancel > └───────────────────────────────────────────────────────────────────────┘ < Layers > < Contribs > < Cancel > │ │ │ │ │ ( ) IPv4 (Internet Protoco │ ( ) Bluetooth 4LE layer │ ( ) Wireless MAC according to IEEE 802.15.4. │ │ ( ) IrDA infrared data co │ ( ) LLMNR (Link Local Multicast Node Resolution). │ (*) Packet class. Binding mechanism. fuzz() method. ^ └─────────────────────────────────────────────── │ (*) Packet class. Binding mechanism. fuzz() method. ^ │ ( ) ASN.1 Packet │ │ ( ) ASN.1 Packet │ │ ( ) Bluetooth layers, sockets and send/receive functions. │ │ ( ) Bluetooth layers, sockets and send/receive functions. │ │ ( ) Classes and functions for layer 2 protocols. │ │ ( ) Classes and functions for layer 2 protocols. │ │ ( ) IPv4 (Internet Protocol v4). │ │ ( ) IPv4 (Internet Protocol v4). │ │ (*) IPv4 (Internet Protocol v4). │ < Ok > < Cancel > ┌───────────────────────────────────────────| Scapy v2.4.2.dev228 |────────────────────────────────────────────┐ Please select a layer among the following, to see all packets contained in it: │ ( ) IPv6 (Internet Protocol v6). │ │ ( ) Wireless LAN according to IEEE 802.11. │ │ ( ) Per-Packet Information (PPI) Protocol │ │ ( ) Bluetooth 4LE layer │ │ ( ) DHCP (Dynamic Host Configuration Protocol) and BOOTP │ │ ( ) DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] │ │ ( ) DNS: Domain Name System. │ │ ( ) Extensible Authentication Protocol (EAP) │ │ ( ) GPRS (General Packet Radio Service) for mobile data communication. │ │ ( ) HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. # noqa: E501 │ │ ( ) IPsec layer │ │ ( ) IrDA infrared data communication. │ │ ( ) ISAKMP (Internet Security Association and Key Management Protocol). │ │ ( ) PPP (Point to Point Protocol) │ │ ( ) L2TP (Layer 2 Tunneling Protocol) for VPNs. │ │ ( ) LLMNR (Link Local Multicast Node Resolution). v └──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ ( ) Packet class. Binding mechanism. fuzz() method. ^ │ (*) IPv4 (Internet Protocol v4). │ < Ok > < Cancel > │ ( ) ASN.1 Packet │ ( ) DHCPv6: Dynamic Host Configuration Protocol for IPv │ ( ) GPRS (General Packet Radio Service) for mobile data communication. < Ok > < Cancel > Packets contained in scapy.layers.inet: Class |Name--------------------------|------- --------------------------|-------------------------------------------ICMP |ICMPICMPerror |ICMP in ICMPIP |IPIPOption |IP OptionIPOption_Address_Extension|IP Option Address ExtensionIPOption_EOL |IP Option End of Options ListIPOption_LSRR |IP Option Loose Source and Record RouteIPOption_MTU_Probe |IP Option MTU ProbeIPOption_MTU_Reply |IP Option MTU ReplyIPOption_NOP |IP Option No OperationIPOption_RR |IP Option Record RouteIPOption_Router_Alert |IP Option Router AlertIPOption_SDBM |IP Option Selective Directed Broadcast ModeIPOption_SSRR |IP Option Strict Source and Record RouteIPOption_Security |IP Option SecurityIPOption_Stream_Id |IP Option Stream IDIPOption_Traceroute |IP Option TracerouteIPerror |IP in ICMPTCP |TCPTCPerror |TCP in ICMPUDP >>> l >>> ls >>> ls( >>> ls(I >>> ls(IP >>> ls(IP) UDP |UDPUDPerror |UDP in ICMP>>> ls(IP) >>> p >>> pk >>> pkt >>> pkt >>> pkt = >>> pkt = >>> pkt = I >>> pkt = IP >>> pkt = IP( >>> pkt = IP(d >>> pkt = IP(ds >>> pkt = IP(dst >>> pkt = IP(dst= >>> pkt = IP(dst=" >>> pkt = IP(dst="w >>> pkt = IP(dst="ww >>> pkt = IP(dst="www >>> pkt = IP(dst="www. >>> pkt = IP(dst="www.s >>> pkt = IP(dst="www.se >>> pkt = IP(dst="www.sec >>> pkt = IP(dst="www.secd >>> pkt = IP(dst="www.secde >>> pkt = IP(dst="www.secdev >>> pkt = IP(dst="www.secdev. >>> pkt = IP(dst="www.secdev.o >>> pkt = IP(dst="www.secdev.or >>> pkt = IP(dst="www.secdev.org >>> pkt = IP(dst="www.secdev.org" >>> pkt = IP(dst="www.secdev.org") >>> pkt = IP(dst="www.secdev.org")/ >>> pkt = IP(dst="www.secdev.org")/I >>> pkt = IP(dst="www.secdev.org")/IC >>> pkt = IP(dst="www.secdev.org")/ICM >>> pkt = IP(dst="www.secdev.org")/ICMP >>> pkt = IP(dst="www.secdev.org")/ICMP( version : BitField (4 bits) = (4) ihl : BitField (4 bits) = (None)tos : XByteField = (0)len : ShortField = (None)id : ShortField = (1)flags : FlagsField (3 bits) = (<Flag 0 ()>)frag : BitField (13 bits) = (0)ttl : ByteField = (64)proto : ByteEnumField = (0)chksum : XShortField = (None)src : SourceIPField = (None)dst : DestIPField = (None)options : PacketListField = ([])>>> pkt = IP(dst="www.secdev.org")/ICMP() >>> pkt. >>> pkt.s >>> pkt.sh >>> pkt.sho >>> pkt.show >>> pkt.show2 >>> pkt.show2( >>> pkt.show2() >>> pkt = IP(dst="www.secdev.org")/ICMP() >>> pkt.show2() >>> >>> s ###[ IP ]### version= 4 ihl= 5 tos= 0x0 len= 28 id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= 0x875a src= 212.83.148.19 dst= 217.25.178.5 \options\###[ ICMP ]### type= echo-request code= 0 chksum= 0xf7ff id= 0x0 seq= 0x0>>> sr >>> sr sr() sr1flood() srbt1() srloop() srp1() srpflood() sr1() srbt() srflood() srp() srp1flood() srploop() sr() sr1flood() srbt1() srloop() srp1() srpflood() >>> sr1 >>> sr1 function(x, promisc, filter, iface, nofilter, args, kargs) sr1() srbt() srflood() srp() srp1flood() srploop() >>> sr1( >>> sr1( >>> sr1(p >>> sr1(pk >>> sr1(pkt >>> sr1(pkt) .... ..... . >>> sr1(pkt) Begin emission: .....Finished sending 1 packets. .* Received 7 packets, got 1 answers, remaining 0 packets<IP version=4 ihl=5 tos=0x0 len=28 id=21006 flags= frag=0 ttl=60 proto=icmp chksum=0x394d src=217.25.178.5 dst=212.83.148.19 |<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>> >>> _ >>> _. >>> _.s >>> _.sh >>> _.sho >>> _.show >>> _.show( x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>>> _.show() >>> _.show() id= 21006 ttl= 60 chksum= 0x394d src= 217.25.178.5 dst= 212.83.148.19 type= echo-reply chksum= 0xffff###[ Padding ]### load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'demo@scapy:~/github/scapy# >>> demo@scapy:~/github/scapy# exit demo@scapy:~/github/scapy# exit + \ No newline at end of file diff --git a/doc/scapy/graphics/scapy-main-console.png b/doc/scapy/graphics/scapy-main-console.png deleted file mode 100644 index 02402168fba9231da8a25fbc999b64e10508fc7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74451 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 z 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≫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 diff --git a/doc/scapy/index.rst b/doc/scapy/index.rst index 6fd46d70f69..d881b6b8040 100644 --- a/doc/scapy/index.rst +++ b/doc/scapy/index.rst @@ -17,17 +17,32 @@ This document is under a `Creative Commons Attribution - Non-Commercial .. toctree:: :maxdepth: 2 + :caption: General documentation introduction installation usage advanced_usage + +.. toctree:: + :maxdepth: 2 + :caption: Extend scapy + extending build_dissect functions - bluetooth +.. toctree:: + :maxdepth: 2 + :caption: Layer-specific documentation + :glob: + + layers/index.rst + +.. toctree:: + :maxdepth: 2 + :caption: About troubleshooting development diff --git a/doc/scapy/introduction.rst b/doc/scapy/introduction.rst index 20b64379fd8..08e7443c316 100644 --- a/doc/scapy/introduction.rst +++ b/doc/scapy/introduction.rst @@ -115,6 +115,9 @@ information is lost in this operation. Quick demo ========== +.. image:: graphics/animations/animation-scapy-demo.svg + :align: center + First, we play a bit and create four IP packets at once. Let's see how it works. We first instantiate the IP class. Then, we instantiate it again and we provide a destination that is worth four IP addresses (/30 gives the netmask). Using a Python idiom, we develop this implicit packet in a set of explicit packets. Then, we quit the interpreter. As we provided a session file, the variables we were working on are saved, then reloaded:: # ./run_scapy -s mysession @@ -187,25 +190,5 @@ Learning Python Scapy uses the Python interpreter as a command board. That means that you can directly use the Python language (assign variables, use loops, define functions, etc.) If you are new to Python and you really don't understand a word because of that, or if you want to learn this language, take an hour to read the very good `Python tutorial `_ by Guido Van Rossum. After that, you'll know Python :) (really!). For a more in-depth tutorial `Dive Into Python `_ is a very good start too. - -For a quick start, here's an overview of Python's data types: - -* ``int`` (signed, 32bits) : ``42`` -* ``long`` (signed, infinite): ``42L`` -* ``str`` : ``"bell\x07\n"`` or ``’bell\x07\n’`` - -* ``tuple`` (immutable): ``(1,4,"42")`` -* ``list`` (mutable): ``[4,2,"1"]`` -* ``dict`` (mutable): ``{ "one":1 , "two":2 }`` - -There are no block delimiters in Python. Instead, indentation does matter:: - - if cond: - instr - instr - elif cond2: - instr - else: - instr diff --git a/doc/scapy/layers/automative.rst b/doc/scapy/layers/automative.rst new file mode 100644 index 00000000000..13778ad8350 --- /dev/null +++ b/doc/scapy/layers/automative.rst @@ -0,0 +1,1088 @@ +***************************************** +Automotive Penetration Testing with Scapy +***************************************** + +.. note:: + All automotive related features work best on Linux systems. CANSockets and ISOTPSockets in Scapy are based on Linux kernel modules. + The python-can project is used to support CAN and CANSockets on other systems, besides Linux. + This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. + The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. + The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy + on a more powerful machine. + +Protocols +--------- + +The following table should give a brief overview about all automotive capabilities +of Scapy. Most application layer protocols have many specialized ``Packet`` classes. +These special purpose classes are not part of this overview. Use the ``explore()`` +function to get all information about one specific protocol. + ++---------------------+----------------------+--------------------------------------------------------+ +| OSI Layer | Protocol | Scapy Implementations | ++=====================+======================+========================================================+ +| Application Layer | UDS (ISO 14229) | UDS, UDS_* | +| +----------------------+--------------------------------------------------------+ +| | GMLAN | GMLAN, GMLAN_* | +| +----------------------+--------------------------------------------------------+ +| | SOME/IP | SOMEIP, SD | +| +----------------------+--------------------------------------------------------+ +| | BMW ENET | ENET, ENETSocket | +| +----------------------+--------------------------------------------------------+ +| | OBD | OBD, OBD_S0X | +| +----------------------+--------------------------------------------------------+ +| | CCP | CCP, DTO, CRO | ++---------------------+----------------------+--------------------------------------------------------+ +| Transportaion Layer | ISO-TP (ISO 15765-2) | ISOTPSocket, ISOTPNativeSocket, ISOTPSoftSocket | +| | | | +| | | ISOTPSniffer, ISOTPMessageBuilder | +| | | | +| | | ISOTPHeader, ISOTPHeaderEA, | +| | | | +| | | ISOTP, ISOTP_SF, ISOTP_FF, ISOTP_CF, ISOTP_FC | ++---------------------+----------------------+--------------------------------------------------------+ +| Data Link Layer | CAN (ISO 11898) | CAN, CANSocket, rdcandump | ++---------------------+----------------------+--------------------------------------------------------+ + + +Hands-On +^^^^^^^^ + +Send a message over Linux SocketCAN:: + + load_layer('can') + load_contrib('cansocket') + socket = CANSocket(iface='can0') + packet = CAN(identifier=0x123, data=b'01020304') + + socket.sr1(packet, timeout=1) + + srcan(packet, 'can0', timeout=1) + +Send a message over a Vector CAN-Interface:: + + import can + load_layer('can') + conf.contribs['CANSocket'] = {'use-python-can' : True} + load_contrib('cansocket') + from can.interfaces.vector import VectorBus + socket = CANSocket(iface=VectorBus(0, bitrate=1000000)) + packet = CAN(identifier=0x123, data=b'01020304') + socket.sr1(packet) + + srcan(packet, VectorBus(0, bitrate=1000000)) + +System compatibilities +---------------------- + +Dependent on your setup, different implementations have to be used. + ++---------------------+----------------------+-------------------------------------+----------------------------------------------------------+ +| Python \ OS | Linux with can_isotp | Linux wo can_isotp | Windows / OSX | ++=====================+======================+=====================================+==========================================================+ +| Python 3 | ISOTPNativeSocket | ISOTPSoftSocket | ISOTPSoftSocket | +| +----------------------+-------------------------------------+ | +| | ``conf.contribs['CANSocket'] = {'use-python-can': False}`` | ``conf.contribs['CANSocket'] = {'use-python-can': True}``| ++---------------------+------------------------------------------------------------+----------------------------------------------------------+ +| Python 2 | ISOTPSoftSocket | ISOTPSoftSocket | +| | | | +| | ``conf.contribs['CANSocket'] = {'use-python-can': True}`` | ``conf.contribs['CANSocket'] = {'use-python-can': True}``| ++---------------------+------------------------------------------------------------+----------------------------------------------------------+ + +The class ``ISOTPSocket`` can be set to a ``ISOTPNativeSocket`` or a ``ISOTPSoftSocket``. +The decision is made dependent on the configuration ``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}`` (to select ``ISOTPNativeSocket``) or +``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}`` (to select ``ISOTPSoftSocket``). +This will allow you to write platform independent code. Apply this configuration before loading the ISOTP layer +with ``load_contrib("isotp")``. + +Another remark in respect to ISOTPSocket compatibility. Always use with for socket creation. Example:: + + with ISOTPSocket("vcan0", did=0x241, sid=0x641) as sock: + sock.send(...) + + +CAN Layer +--------- + +Setup +^^^^^ + +These commands enable a virtual CAN interface on a Linux machine:: + + from scapy.layers.can import * + import os + + bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" + os.system(bashCommand) + +If it's required, the CAN interface can be set into a ``listen-only`` or ``loopback`` mode with `ip link set` commands:: + + ip link set vcan0 type can help # shows additional information + + +This example shows a basic functions of Linux can-utils. These utilities are handy for +quick checks or logging. + +.. image:: ../graphics/animations/animation-cansend.svg + +CAN Frame +^^^^^^^^^ + +Creating a standard CAN frame:: + + frame = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') + +Creating an extended CAN frame:: + + frame = CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') + +.. image:: ../graphics/animations/animation-scapy-canframe.svg + +Writing and reading to pcap files:: + + x = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') + wrpcap('/tmp/scapyPcapTest.pcap', x, append=False) + y = rdpcap('/tmp/scapyPcapTest.pcap', 1) + +.. image:: ../graphics/animations/animation-scapy-rdpcap.svg +.. image:: ../graphics/animations/animation-scapy-rdcandump.svg + +CANSocket native +^^^^^^^^^^^^^^^^ + +Creating a simple native CANSocket:: + + conf.contribs['CANSocket'] = {'use-python-can': False} #(default) + load_contrib('cansocket') + + # Simple Socket + socket = CANSocket(iface="vcan0") + +Creating a native CANSocket only listen for messages with Id == 0x200:: + + socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x7FF}]) + +Creating a native CANSocket only listen for messages with Id >= 0x200 and Id <= 0x2ff:: + + socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]) + +Creating a native CANSocket only listen for messages with Id != 0x200:: + + socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}]) + +Creating a native CANSocket with multiple can_filters:: + + socket = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, + {'can_id': 0x400, 'can_mask': 0x7ff}, + {'can_id': 0x600, 'can_mask': 0x7ff}, + {'can_id': 0x7ff, 'can_mask': 0x7ff}]) + +Creating a native CANSocket which also receives its own messages:: + + socket = CANSocket(iface="vcan0", receive_own_messages=True) + +.. image:: ../graphics/animations/animation-scapy-native-cansocket.svg + +Sniff on a CANSocket: + +.. image:: ../graphics/animations/animation-scapy-cansockets-sniff.svg + + +CANSocket python-can +^^^^^^^^^^^^^^^^^^^^ + +python-can is required to use various CAN-interfaces on Windows, OSX or Linux. +The python-can library is used through a CANSocket object. To create a python-can +CANSocket object, a python-can ``Bus`` object has to be used as interface. +The ``timeout`` parameter can be used to increase the receive performance of a +python-can CANSocket object. ``recv`` inside a python-can CANSocket object is +implemented through busy wait, since there is no ``select`` functionality on +Windows or on some proprietary CAN interfaces (like Vector interfaces). A small +``timeout`` might be required, if a ``sniff`` or ``bridge_and_sniff`` on multiple +interfaces is performed. + +Ways of creating a python-can CANSocket:: + + conf.contribs['CANSocket'] = {'use-python-can': True} + load_contrib('cansocket') + import can + +Creating a simple python-can CANSocket:: + + socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)) + +Creating a python-can CANSocket with multiple filters:: + + socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, + can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, + {'can_id': 0x400, 'can_mask': 0x7ff}, + {'can_id': 0x600, 'can_mask': 0x7ff}, + {'can_id': 0x7ff, 'can_mask': 0x7ff}])) + +.. image:: ../graphics/animations/animation-scapy-python-can-cansocket.svg + +For further details on python-can check: https://python-can.readthedocs.io/en/2.2.0/ + +CANSocket MITM attack with bridge and sniff +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This example shows how to use bridge and sniff on virtual CAN interfaces. +For real world applications, use real CAN interfaces. +Set up two vcans on Linux terminal:: + + sudo modprobe vcan + sudo ip link add name vcan0 type vcan + sudo ip link add name vcan1 type vcan + sudo ip link set dev vcan0 up + sudo ip link set dev vcan1 up + +Import modules:: + + import threading + load_contrib('cansocket') + load_layer("can") + +Create can sockets for attack:: + + socket0 = CANSocket(iface='vcan0') + socket1 = CANSocket(iface='vcan1') + +Create a function to send packet with threading:: + + def sendPacket(): + sleep(0.2) + socket0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) + +Create a function for forwarding or change packets:: + + def forwarding(pkt): + return pkt + +Create a function to bridge and sniff between two sockets:: + + def bridge(): + bSocket0 = CANSocket(iface='vcan0') + bSocket1 = CANSocket(iface='vcan1') + bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) + bSocket0.close() + bSocket1.close() + +Create threads for sending packet and to bridge and sniff:: + + threadBridge = threading.Thread(target=bridge) + threadSender = threading.Thread(target=sendMessage) + +Start the threads:: + + threadBridge.start() + threadSender.start() + +Sniff packets:: + + packets = socket1.sniff(timeout=0.3) + +Close the sockets:: + + socket0.close() + socket1.close() + +.. image:: ../graphics/animations/animation-scapy-cansockets-mitm.svg +.. image:: ../graphics/animations/animation-scapy-cansockets-mitm2.svg + + +CAN Calibration Protocol (CCP) +------------------------------ + +CCP is derived from CAN. The CAN-header is part of a CCP frame. CCP has two types +of message objects. One is called Command Receive Object (CRO), the other is called +Data Transmission Object (DTO). Usually CROs are sent to an ECU, and DTOs are received +from an ECU. The information, if one DTO answers a CRO is implemented through a counter +field (ctr). If both objects have the same counter value, the payload of a DTO object +can be interpreted from the command of the associated CRO object. + +Creating a CRO message:: + + CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) + CCP(identifier=0x711)/CRO(ctr=2)/GET_SEED(resource=2) + CCP(identifier=0x711)/CRO(ctr=3)/UNLOCK(key=b"123456") + +If we aren't interested in the DTO of an ECU, we can just send a CRO message like this: +Sending a CRO message:: + + pkt = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) + sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)) + sock.send(pkt) + +If we are interested in the DTO of an ECU, we need to set the basecls parameter of the +CANSocket to CCP and we need to use sr1: +Sending a CRO message:: + + cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12") + sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP) + dto = sock.sr1(cro) + dto.show() + ###[ CAN Calibration Protocol ]### + flags= + identifier= 0x700 + length= 8 + reserved= 0 + ###[ DTO ]### + packet_id= 0xff + return_code= acknowledge / no error + ctr= 83 + ###[ PROGRAM_6_DTO ]### + MTA0_extension= 2 + MTA0_address= 0x34002006 + +Since sr1 calls the answers function, our payload of the DTO objects gets interpreted with the +command of our CRO object. + +ISOTP +----- + +ISOTP message +^^^^^^^^^^^^^ + +Creating an ISOTP message:: + + load_contrib('isotp') + ISOTP(src=0x241, dst=0x641, data=b"\x3eabc") + +Creating an ISOTP message with extended addressing:: + + ISOTP(src=0x241, dst=0x641, exdst=0x41, data=b"\x3eabc") + +Creating an ISOTP message with extended addressing:: + + ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x41, data=b"\x3eabc") + +Create CAN-frames from an ISOTP message:: + + ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x55, data=b"\x3eabc" * 10).fragment() + +Send ISOTP message over ISOTP socket:: + + isoTpSocket = ISOTPSocket('vcan0', sid=0x241, did=0x641) + isoTpMessage = ISOTP('Message') + isoTpSocket.send(isoTpMessage) + +Sniff ISOTP message:: + + isoTpSocket = ISOTPSocket('vcan0', sid=0x641, did=0x241) + packets = isoTpSocket.sniff(timeout=0.5) + +ISOTP MITM attack with bridge and sniff +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Set up two vcans on Linux terminal:: + + sudo modprobe vcan + sudo ip link add name vcan0 type vcan + sudo ip link add name vcan1 type vcan + sudo ip link set dev vcan0 up + sudo ip link set dev vcan1 up + +Set up ISOTP:: + +.. note:: + + First make sure you build an iso-tp kernel module. + +When the vcan core module is loaded with "sudo modprobe vcan" the iso-tp module can be loaded to the kernel. + +Therefore navigate to isotp directory, and load module with "sudo insmod ./net/can/can-isotp.ko". (Tested on Kernel 4.9.135-1-MANJARO) + +Detailed instructions you find in https://github.com/hartkopp/can-isotp. + +Import modules:: + + import threading + load_contrib('cansocket') + conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} + load_contrib('isotp') + +Create to ISOTP sockets for attack:: + + isoTpSocketVCan0 = ISOTPSocket('vcan0', sid=0x241, did=0x641) + isoTpSocketVCan1 = ISOTPSocket('vcan1', sid=0x641, did=0x241) + +Create function to send packet on vcan0 with threading:: + + def sendPacketWithISOTPSocket(): + sleep(0.2) + packet = ISOTP('Request') + isoTpSocketVCan0.send(packet) + +Create function to forward packet:: + + def forwarding(pkt): + return pkt + +Create function to bridge and sniff between two buses:: + + def bridge(): + bSocket0 = ISOTPSocket('vcan0', sid=0x641, did=0x241) + bSocket1 = ISOTPSocket('vcan1', sid=0x241, did=0x641) + bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) + bSocket0.close() + bSocket1.close() + +Create threads for sending packet and to bridge and sniff:: + + threadBridge = threading.Thread(target=bridge) + threadSender = threading.Thread(target=sendPacketWithISOTPSocket) + +Start threads are based on Linux kernel modules. The python-can project is used to support CAN and CANSockets on other systems, besides Linux. This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy on a more powerful machine.:: + + threadBridge.start() + threadSender.start() + +Sniff on vcan1:: + + receive = isoTpSocketVCan1.sniff(timeout=1) + +Close sockets:: + + isoTpSocketVCan0.close() + isoTpSocketVCan1.close() + +An ISOTPSocket will not respect ``src, dst, exdst, exsrc`` of an ISOTP message object. + +ISOTP Sockets +------------- + +Scapy provides two kinds of ISOTP Sockets. One implementation, the ISOTPNativeSocket +is using the Linux kernel module from Hartkopp. The other implementation, the ISOTPSoftSocket +is completely implemented in Python. This implementation can be used on Linux, +Windows, and OSX. + +ISOTPNativeSocket +^^^^^^^^^^^^^^^^^ + +**Requires:** + +* Python3 +* Linux +* Hartkopp's Linux kernel module: ``https://github.com/hartkopp/can-isotp.git`` + +During pentests, the ISOTPNativeSockets do have a better performance and +reliability, usually. If you are working on Linux, consider this implementation:: + + conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} + load_contrib('isotp') + sock = ISOTPSocket("can0", sid=0x641, did=0x241) + +Since this implementation is using a standard Linux socket, all Scapy functions +like ``sniff, sr, sr1, bridge_and_sniff`` work out of the box. + +ISOTPSoftSocket +^^^^^^^^^^^^^^^ + +ISOTPSoftSockets can use any CANSocket. This gives the flexibility to use all +python-can interfaces. Additionally, these sockets work on Python2 and Python3. +Usage on Linux with native CANSockets:: + + conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} + load_contrib('isotp') + with ISOTPSocket("can0", sid=0x641, did=0x241) as sock: + sock.send(...) + +Usage with python-can CANSockets:: + + conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} + conf.contribs['CANSocket'] = {'use-python-can': True} + load_contrib('isotp') + with ISOTPSocket(CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel="can0", bitrate=250000)), sid=0x641, did=0x241) as sock: + sock.send(...) + +This second example allows the usage of any ``python_can.interface`` object. + +**Attention:** The internal implementation of ISOTPSoftSockets requires a background +thread. In order to be able to close this thread properly, we suggest the use of +Pythons ``with`` statement. + + +UDS +--- + +The main usage of UDS is flashing and diagnostic of an ECU. UDS is an +application layer protocol and can be used as a DoIP or ENET payload or a UDS packet +can directly be sent over an ISOTPSocket. Every OEM has its own customization of UDS. +This increases the difficulty of generic applications and OEM specific knowledge is +required for penetration tests. RoutineControl jobs and ReadDataByIdentifier/WriteDataByIdentifier +services are heavily customized. + +Use the argument ``basecls=UDS`` on the ``init`` function of an ISOTPSocket. + +Here are two usage examples: + +.. image:: ../graphics/animations/animation-scapy-uds.svg +.. image:: ../graphics/animations/animation-scapy-uds2.svg + + +Customization of UDS_RDBI, UDS_WDBI +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In real-world use-cases, the UDS layer is heavily customized. OEMs define there own substructure of packets. +Especially the packets ReadDataByIdentifier or WriteDataByIdentifier have a very OEM or even ECU specific +substructure. Therefore a ``StrField`` ``dataRecord`` is not added to the ``field_desc``. +The intended usage is to create ECU or OEM specific description files, which extend the general UDS layer of +Scapy with further protocol implementations. + +Customization example:: + + cat scapy/contrib/automotive/OEM-XYZ/car-model-xyz.py + #! /usr/bin/env python + + # Protocol customization for car model xyz of OEM XYZ + # This file contains further OEM car model specific UDS additions. + + from scapy.packet import Packet + from scapy.contrib.automotive.uds import * + + # Define a new packet substructure + + class DBI_IP(Packet): + name = 'DataByIdentifier_IP_Packet' + fields_desc = [ + ByteField('ADDRESS_FORMAT_ID', 0), + IPField('IP', ''), + IPField('SUBNETMASK', ''), + IPField('DEFAULT_GATEWAY', '') + ] + + # Bind the new substructure onto the existing UDS packets + + bind_layers(UDS_RDBIPR, DBI_IP, dataIdentifier=0x172b) + bind_layers(UDS_WDBI, DBI_IP, dataIdentifier=0x172b) + + # Give add a nice name to dataIdentifiers enum + + UDS_RDBI.dataIdentifiers[0x172b] = 'GatewayIP' + +If one wants to work with this custom additions, these can be loaded at runtime to the Scapy interpreter:: + + >>> load_contrib("automotive.uds") + >>> load_contrib("automotive.OEM-XYZ.car-model-xyz") + + >>> pkt = UDS()/UDS_WDBI()/DBI_IP(IP='192.168.2.1', SUBNETMASK='255.255.255.0', DEFAULT_GATEWAY='192.168.2.1') + + >>> pkt.show() + ###[ UDS ]### + service= WriteDataByIdentifier + ###[ WriteDataByIdentifier ]### + dataIdentifier= GatewayIP + dataRecord= 0 + ###[ DataByIdentifier_IP_Packet ]### + ADDRESS_FORMAT_ID= 0 + IP= 192.168.2.1 + SUBNETMASK= 255.255.255.0 + DEFAULT_GATEWAY= 192.168.2.1 + + >>> hexdump(pkt) + 0000 2E 17 2B 00 C0 A8 02 01 FF FF FF 00 C0 A8 02 01 ..+............. + +.. image:: ../graphics/animations/animation-scapy-uds3.svg + +GMLAN +----- +GMLAN is very similar to UDS. It's GMs application layer protocol for +flashing, calibration and diagnostic of their cars. +Use the argument ``basecls=GMLAN`` on the ``init`` function of an ISOTPSocket. + +Usage example: + +.. image:: ../graphics/animations/animation-scapy-gmlan.svg + + +SOME/IP and SOME/IP SD messages +------------------------------- + +Creating a SOME/IP message +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example shows a SOME/IP message which requests a service 0x1234 with the method 0x421. Different types of SOME/IP messages follow the same procedure and their specifications can be seen here ``http://www.some-ip.com/papers/cache/AUTOSAR_TR_SomeIpExample_4.2.1.pdf``. + + +Load the contribution:: + + load_contrib("automotive.someip") + +Create UDP package:: + + u = UDP(sport=30509, dport=30509) + +Create IP package:: + + i = IP(src="192.168.0.13", dst="192.168.0.10") + +Create SOME/IP package:: + + sip = SOMEIP() + sip.iface_ver = 0 + sip.proto_ver = 1 + sip.msg_type = "REQUEST" + sip.retcode = "E_OK" + sip.msg_id.srv_id = 0x1234 + sip.msg_id.method_id = 0x421 + +Add the payload:: + + sip.add_payload(Raw ("Hello")) + +Stack it and send it:: + + p = i/u/sip + send(p) + + +Creating a SOME/IP SD message +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this example a SOME/IP SD offer service message is shown with an IPv4 endpoint. Different entries and options basically follow the same procedure as shown here and can be seen at ``https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_ServiceDiscovery.pdf``. + +Load the contribution:: + + load_contrib("automotive.someip_sd") + +Create UDP package:: + + u = UDP(sport=30490, dport=30490) + +The UDP port must be the one which was chosen for the SOME/IP SD transmission. + +Create IP package:: + + i = IP(src="192.168.0.13", dst="224.224.224.245") + +The IP source must be from the service and the destination address needs to be the chosen multicast address. + +Create the entry array input:: + + ea = SDEntry_Service() + + ea.type = 0x01 + ea.srv_id = 0x1234 + ea.inst_id = 0x5678 + ea.major_ver = 0x00 + ea.ttl = 3 + +Create the options array input:: + + oa = SDOption_IP4_Endpoint() + oa.addr = "192.168.0.13" + oa.l4_proto = 0x11 + oa.port = 30509 + +l4_proto defines the protocol for the communication with the endpoint, UDP in this case. + +Create the SD package and put in the inputs:: + + sd = SD() + sd.set_entryArray(ea) + sd.set_optionArray(oa) + spsd = sd.get_someip(True) + +The get_someip method stacks the SOMEIP/SD message on top of a SOME/IP message, which has the desired SOME/IP values prefilled for the SOME/IP SD package transmission. + +Stack it and send it:: + + p = i/u/spsd + send(p) + + + + +OBD message +------------- + +OBD is implemented on top of ISOTP. Use an ISOTPSocket for the communication with a ECU. +You should set the parameters ``basecls=OBD`` and ``padding=True`` in your ISOTPSocket init call. + +OBD is split into different service groups. Here are some example requests: + +Request supported PIDs of service 0x01:: + + req = OBD()/OBD_S01(pid=[0x00]) + +The response will contain a PacketListField, called `data_records`. This field contains the actual response:: + + resp = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids=3196041235)]) + resp.show() + ###[ On-board diagnostics ]### + service= CurrentPowertrainDiagnosticDataResponse + ###[ Parameter IDs ]### + \data_records\ + |###[ OBD_S01_PR_Record ]### + | pid= 0x0 + |###[ PID_00_PIDsSupported ]### + | supported_pids= PID20+PID1F+PID1C+PID15+PID14+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID0A+PID07+PID06+PID05+PID04+PID03+PID01 + +Let's assume our ECU under test supports the pid 0x15:: + + req = OBD()/OBD_S01(pid=[0x15]) + resp = sock.sr1(req) + resp.show() + ###[ On-board diagnostics ]### + service= CurrentPowertrainDiagnosticDataResponse + ###[ Parameter IDs ]### + \data_records\ + |###[ OBD_S01_PR_Record ]### + | pid= 0x15 + |###[ PID_15_OxygenSensor2 ]### + | outputVoltage= 1.275 V + | trim= 0 % + + +The different services in OBD support different kinds of data. +Service 01 and Service 02 support Parameter Identifiers (pid). +Service 03, 07 and 0A support Diagnostic Trouble codes (dtc). +Service 04 doesn't require a payload. +Service 05 is not implemented on OBD over CAN. +Service 06 support Monitoring Identifiers (mid). +Service 08 support Test Identifiers (tid). +Service 09 support Information Identifiers (iid). + +Examples: +^^^^^^^^^ + +Request supported Information Identifiers:: + + req = OBD()/OBD_S09(iid=[0x00]) + +Request the Vehicle Identification Number (VIN):: + + req = OBD()/OBD_S09(iid=0x02) + resp = sock.sr1(req) + resp.show() + ###[ On-board diagnostics ]### + service= VehicleInformationResponse + ###[ Infotype IDs ]### + \data_records\ + |###[ OBD_S09_PR_Record ]### + | iid= 0x2 + |###[ IID_02_VehicleIdentificationNumber ]### + | count= 1 + | vehicle_identification_numbers= ['W0L000051T2123456'] + + +.. image:: ../graphics/animations/animation-scapy-obd.svg + + +Test-Setup Tutorials +-------------------- + +Hardware Setup +^^^^^^^^^^^^^^ + +Beagle Bone Black Operating System Setup +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#. | **Download an Image** + | The latest Debian Linux image can be found at the website + | ``https://beagleboard.org/latest-images``. Choose the BeagleBone + Black IoT version and download it. + + :: + + wget https://debian.beagleboard.org/images/bone-debian-8.7\ + -iot-armhf-2017-03-19-4gb.img.xz + + + After the download, copy it to an SD-Card with minimum of 4 GB storage. + + :: + + xzcat bone-debian-8.7-iot-armhf-2017-03-19-4gb.img.xz | \ + sudo dd of=/dev/xvdj + + +#. | **Enable WiFi** + | USB-WiFi dongles are well supported by Debian Linux. Login over SSH + on the BBB and add the WiFi network credentials to the file + ``/var/lib/connman/wifi.config``. If a USB-WiFi dongle is not + available, it is also possible to share the host's internet + connection with the Ethernet connection of the BBB emulated over + USB. A tutorial to share the host network connection can be found + on this page: + | ``https://elementztechblog.wordpress.com/2014/12/22/sharing-internet -using-network-over-usb-in-beaglebone-black/``. + | Login as root onto the BBB: + + :: + + ssh debian@192.168.7.2 + sudo su + + + Provide the WiFi login credentials to connman: + + :: + + echo "[service_home] + Type = wifi + Name = ssid + Security = wpa + Passphrase = xxxxxxxxxxxxx" \ + > /var/lib/connman/wifi.config + + + Restart the connman service: + + :: + + systemctl restart connman.service + + +Dual-CAN Setup +~~~~~~~~~~~~~~ + +#. | **Device tree setup** + | You'll need to follow this section only if you want to use two CAN + interfaces (DCAN0 and DCAN1). This will disable I2C2 from using pins + P9.19 and P9.20, which are needed by DCAN0. You only need to perform the + steps in this section once. + + | Warning: The configuration in this section will disable BBB capes from + working. Each cape has a small I2C EEPROM that stores info that the BBB + needs to know in order to communicate with the cape. Disable I2C2, and + the BBB has no way to talk to cape EEPROMs. Of course, if you don't use + capes then this is not a problem. + + | Acquire DTS sources that matches your kernel version. Go + `here `__ and switch over to the + branch that represents your kernel version. Download the entire branch + as a ZIP file. Extract it and do the following (version 4.1 shown as an + example): + + :: + + # cd ~/src/linux-4.1/arch/arm/boot/dts/include/ + # rm dt-bindings + # ln -s ../../../../../include/dt-bindings + # cd .. + Edit am335x-bone-common.dtsi and ensure the line with "//pinctrl-0 = <&i2c2_pins>;" is commented out. + Remove the complete &ocp section at the end of this file + # mv am335x-boneblack.dts am335x-boneblack.raw.dts + # cpp -nostdinc -I include -undef -x assembler-with-cpp am335x-boneblack.raw.dts > am335x-boneblack.dts + # dtc -W no-unit_address_vs_reg -O dtb -o am335x-boneblack.dtb -b 0 -@ am335x-boneblack.dts + # cp /boot/dtbs/am335x-boneblack.dtb /boot/dtbs/am335x-boneblack.orig.dtb + # cp am335x-boneblack.dtb /boot/dtbs/ + Reboot + +#. **Overlay setup** + | This section describes how to build the device overlays for the two CAN devices (DCAN0 and DCAN1). You only need to perform the steps in this section once. + | Acquire BBB cape overlays, in one of two ways… + + :: + + # apt-get install bb-cape-overlays + https://github.com/beagleboard/bb.org-overlays/ + + | Then do the following: + + + :: + + # cd ~/src/bb.org-overlays-master/src/arm + # ln -s ../../include + # mv BB-CAN1-00A0.dts BB-CAN1-00A0.raw.dts + # cp BB-CAN1-00A0.raw.dts BB-CAN0-00A0.raw.dts + Edit BB-CAN0-00A0.raw.dts and make relevant to CAN0. Example is shown below. + # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN0-00A0.raw.dts > BB-CAN0-00A0.dts + # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN1-00A0.raw.dts > BB-CAN1-00A0.dts + # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN0-00A0.dtbo -b 0 -@ BB-CAN0-00A0.dts + # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN1-00A0.dtbo -b 0 -@ BB-CAN1-00A0.dts + # cp *.dtbo /lib/firmware + + +#. | **CAN0 Example Overlay** + | Inside the DTS folder, create a file with the content of the + following listing. + + :: + + cd ~/bb.org-overlays/src/arm + cat < BB-CAN0-00A0.raw.dts + + /* + * Copyright (C) 2015 Robert Nelson + * + * Virtual cape for CAN0 on connector pins P9.19 P9.20 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + /dts-v1/; + /plugin/; + + #include + #include + + / { + compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green"; + + /* identification */ + part-number = "BB-CAN0"; + version = "00A0"; + + /* state the resources this cape uses */ + exclusive-use = + /* the pin header uses */ + "P9.19", /* can0_rx */ + "P9.20", /* can0_tx */ + /* the hardware ip uses */ + "dcan0"; + + fragment@0 { + target = <&am33xx_pinmux>; + __overlay__ { + bb_dcan0_pins: pinmux_dcan0_pins { + pinctrl-single,pins = < + BONE_P9_19 (PIN_INPUT_PULLUP | MUX_MODE2) /* uart1_txd.d_can0_rx */ + BONE_P9_20 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* uart1_rxd.d_can0_tx */ + >; + }; + }; + }; + + fragment@1 { + target = <&dcan0>; + __overlay__ { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_dcan0_pins>; + }; + }; + }; + EOF + + +#. | **Test the Dual-CAN Setup** + | Do the following each time you need CAN, or automate these steps if you like. + + :: + + # echo BB-CAN0 > /sys/devices/platform/bone_capemgr/slots + # echo BB-CAN1 > /sys/devices/platform/bone_capemgr/slots + # modprobe can + # modprobe can-dev + # modprobe can-raw + # ip link set can0 up type can bitrate 50000 + # ip link set can1 up type can bitrate 50000 + + Check the output of the Capemanager if both CAN interfaces have been + loaded. + + :: + + cat /sys/devices/platform/bone_capemgr/slots + + 0: PF---- -1 + 1: PF---- -1 + 2: PF---- -1 + 3: PF---- -1 + 4: P-O-L- 0 Override Board Name,00A0,Override Manuf, BB-CAN0 + 5: P-O-L- 1 Override Board Name,00A0,Override Manuf, BB-CAN1 + + + If something went wrong, ``dmesg`` provides kernel messages to analyse the root of failure. + +#. | **References** + + - `embedded-things.com: Enable CANbus on the Beaglebone + Black `__ + - `electronics.stackexchange.com: Beaglebone Black CAN bus + Setup `__ + +#. | **Acknowledgment** + | Thanks to Tom Haramori. Parts of this section are copied from his guide: https://github.com/haramori/rhme3/blob/master/Preparation/BBB_CAN_setup.md + + + +ISO-TP Kernel Module Installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A Linux ISO-TP kernel module can be downloaded from this website: +``https://github.com/hartkopp/can-isotp.git``. The file +``README.isotp`` in this repository provides all information and +necessary steps for downloading and building this kernel module. The +ISO-TP kernel module should also be added to the ``/etc/modules`` file, +to load this module automatically at system boot of the BBB. + +CAN-Interface Setup +~~~~~~~~~~~~~~~~~~~ + +As the final step to prepare the BBB's CAN interfaces for usage, these +interfaces have to be set up through some terminal commands. The bitrate +can be chosen to fit the bitrate of a CAN bus under test. + +:: + + ip link set can0 up type can bitrate 500000 + ip link set can1 up type can bitrate 500000 + +Raspberry Pi SOME/IP setup +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To build a small test environment in which you can send SOME/IP messages to and from server instances or disguise yourself as a server, one Raspberry Pi, your laptop and the vsomeip library are sufficient. + +#. | **Download image** + + Download the latest raspbian image (``https://www.raspberrypi.org/downloads/raspbian/``) and install it on the Raspberry. + +#. | **Vsomeip setup** + + Download the vsomeip library on the Rapsberry, apply the git patch so it can work with the newer boost libraries and then install it. + + :: + + git clone https://github.com/GENIVI/vsomeip.git + cd vsomeip + wget -O 0001-Support-boost-v1.66.patch.zip \ + https://github.com/GENIVI/vsomeip/files/2244890/0001-Support-boost-v1.66.patch.zip + unzip 0001-Support-boost-v1.66.patch.zip + git apply 0001-Support-boost-v1.66.patch + mkdir build + cd build + cmake -DENABLE_SIGNAL_HANDLING=1 .. + make + make install + +#. | **Make applications** + + Write some small applications which function as either a service or a client and use the Scapy SOME/IP implementation to communicate with the client or the server. Examples for vsomeip applications are available on the vsomeip github wiki page (``https://github.com/GENIVI/vsomeip/wiki/vsomeip-in-10-minutes``). + + + +Software Setup +^^^^^^^^^^^^^^ + +Cannelloni Framework Installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Cannelloni framework is a small application written in C++ to +transfer CAN data over UDP. In this way, a researcher can map the CAN +communication of a remote device to its workstation, or even combine +multiple remote CAN devices on his machine. The framework can be +downloaded from this website: +``https://github.com/mguentner/cannelloni.git``. The ``README.md`` file +explains the installation and usage in detail. Cannelloni needs virtual +CAN interfaces on the operator's machine. The next listing shows the +setup of virtual CAN interfaces. + +:: + + modprobe vcan + + ip link add name vcan0 type vcan + ip link add name vcan1 type vcan + + ip link set dev vcan0 up + ip link set dev vcan1 up + + tc qdisc add dev vcan0 root tbf rate 300kbit latency 100ms burst 1000 + tc qdisc add dev vcan1 root tbf rate 300kbit latency 100ms burst 1000 + + cannelloni -I vcan0 -R -r 20000 -l 20000 & + cannelloni -I vcan1 -R -r 20001 -l 20001 & + diff --git a/doc/scapy/bluetooth.rst b/doc/scapy/layers/bluetooth.rst similarity index 99% rename from doc/scapy/bluetooth.rst rename to doc/scapy/layers/bluetooth.rst index 59645b6e0ce..091036dad92 100644 --- a/doc/scapy/bluetooth.rst +++ b/doc/scapy/layers/bluetooth.rst @@ -5,7 +5,7 @@ Bluetooth .. note:: If you're new to using Scapy, start with the :doc:`usage documentation - `, which describes how to use Scapy with Ethernet and IP. + <../usage>`, which describes how to use Scapy with Ethernet and IP. .. warning:: @@ -407,7 +407,7 @@ __ https://github.com/google/eddystone/tree/master/eddystone-url Once :ref:`advertising has been started `, the beacon may then be detected with the `Eddystone Validator`__ (Android): -.. image:: graphics/ble_eddystone_url.png +.. image:: ../graphics/ble_eddystone_url.png __ https://github.com/google/eddystone/tree/master/tools/eddystone-validator diff --git a/doc/scapy/layers/index.rst b/doc/scapy/layers/index.rst new file mode 100644 index 00000000000..5508057d578 --- /dev/null +++ b/doc/scapy/layers/index.rst @@ -0,0 +1,6 @@ +.. Layer-specific documentation + +.. toctree:: + :glob: + + * \ No newline at end of file diff --git a/doc/scapy/layers/pnio.rst b/doc/scapy/layers/pnio.rst new file mode 100644 index 00000000000..b3b37486575 --- /dev/null +++ b/doc/scapy/layers/pnio.rst @@ -0,0 +1,269 @@ +*************** +PROFINET IO RTC +*************** + +PROFINET IO is an industrial protocol composed of different layers such as the Real-Time Cyclic (RTC) layer, used to exchange data. However, this RTC layer is stateful and depends on a configuration sent through another layer: the DCE/RPC endpoint of PROFINET. This configuration defines where each exchanged piece of data must be located in the RTC ``data`` buffer, as well as the length of this same buffer. Building such packet is then a bit more complicated than other protocols. + +RTC data packet +--------------- + +The first thing to do when building the RTC ``data`` buffer is to instantiate each Scapy packet which represents a piece of data. Each one of them may require some specific piece of configuration, such as its length. All packets and their configuration are: + +* ``PNIORealTimeRawData``: a simple raw data like ``Raw`` + + * ``length``: defines the length of the data + +* ``Profisafe``: the PROFIsafe profile to perform functional safety + + * ``length``: defines the length of the whole packet + * ``CRC``: defines the length of the CRC, either ``3`` or ``4`` + +* ``PNIORealTimeIOxS``: either an IO Consumer or Provider Status byte + + * Doesn't require any configuration + +To instantiate one of these packets with its configuration, the ``config`` argument must be given. It is a ``dict()`` which contains all the required piece of configuration:: + + >>> load_contrib('pnio_rtc') + >>> raw(PNIORealTimeRawData(load='AAA', config={'length': 4})) + 'AAA\x00' + >>> raw(Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3})) + 'AAA\x00 BBB' + >>> hexdump(PNIORealTimeIOxS()) + 0000 80 . + + +RTC packet +---------- + +Now that a data packet can be instantiated, a whole RTC packet may be built. ``PNIORealTime`` contains a field ``data`` which is a list of all data packets to add in the buffer, however, without the configuration, Scapy won't be +able to dissect it:: + + >>> load_contrib("pnio_rtc") + >>> p=PNIORealTime(cycleCounter=1024, data=[ + ... PNIORealTimeIOxS(), + ... PNIORealTimeRawData(load='AAA', config={'length':4}) / PNIORealTimeIOxS(), + ... Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}) / PNIORealTimeIOxS(), + ... ]) + >>> p.show() + ###[ PROFINET Real-Time ]### + len= None + dataLen= None + \data\ + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0 + | extension= 0 + |###[ PNIO RTC Raw data ]### + | load= 'AAA' + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0 + | extension= 0 + |###[ PROFISafe ]### + | load= 'AAA' + | Control_Status= 0x20 + | CRC= 0x424242 + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0 + | extension= 0 + padding= '' + cycleCounter= 1024 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + + >>> p.show2() + ###[ PROFINET Real-Time ]### + len= 44 + dataLen= 15 + \data\ + |###[ PNIO RTC Raw data ]### + | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' + padding= '' + cycleCounter= 1024 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + +For Scapy to be able to dissect it correctly, one must also configure the layer for it to know the location of each data in the buffer. This configuration is saved in the dictionary ``conf.contribs["PNIO_RTC"]`` which can be updated with the ``pnio_update_config`` method. Each item in the dictionary uses the tuple ``(Ether.src, Ether.dst)`` as key, to be able to separate the configuration of each communication. Each value is then a list of a tuple which describes a data packet. It is composed of the negative index, from the end of the data buffer, of the packet position, the class of the packet as the second item and the ``config`` dictionary to provide to the class as last. If we continue the previous example, here is the configuration to set:: + + >>> load_contrib("pnio") + >>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO() / p + >>> e.show2() + ###[ Ethernet ]### + dst= 06:07:08:09:0a:0b + src= 00:01:02:03:04:05 + type= 0x8892 + ###[ ProfinetIO ]### + frameID= RT_CLASS_1 + ###[ PROFINET Real-Time ]### + len= 44 + dataLen= 15 + \data\ + |###[ PNIO RTC Raw data ]### + | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' + padding= '' + cycleCounter= 1024 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + >>> pnio_update_config({('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [ + ... (-9, Profisafe, {'length': 8, 'CRC': 3}), + ... (-9 - 5, PNIORealTimeRawData, {'length':4}), + ... ]}) + >>> e.show2() + ###[ Ethernet ]### + dst= 06:07:08:09:0a:0b + src= 00:01:02:03:04:05 + type= 0x8892 + ###[ ProfinetIO ]### + frameID= RT_CLASS_1 + ###[ PROFINET Real-Time ]### + len= 44 + dataLen= 15 + \data\ + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PNIO RTC Raw data ]### + | load= 'AAA' + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PROFISafe ]### + | load= 'AAA' + | Control_Status= 0x20 + | CRC= 0x424242L + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + padding= '' + cycleCounter= 1024 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + +If no data packets are configured for a given offset, it defaults to a ``PNIORealTimeIOxS``. However, this method is not very convenient for the user to configure the layer and it only affects the dissection of packets. In such cases, one may have access to several RTC packets, sniffed or retrieved from a PCAP file. Thus, ``PNIORealTime`` provides some methods to analyse a list of ``PNIORealTime`` packets and locate all data in it, based on simple heuristics. All of them take as first argument an iterable which contains the list of packets to analyse. + +* ``PNIORealTime.find_data()`` analyses the data buffer and separate real data from IOxS. It returns a dict which can be provided to ``pnio_update_config``. +* ``PNIORealTime.find_profisafe()`` analyses the data buffer and find the PROFIsafe profiles among the real data. It returns a dict which can be provided to ``pnio_update_config``. +* ``PNIORealTime.analyse_data()`` executes both previous methods and update the configuration. **This is usually the method to call.** +* ``PNIORealTime.draw_entropy()`` will draw the entropy of each byte in the data buffer. It can be used to easily visualize PROFIsafe locations as entropy is the base of the decision algorithm of ``find_profisafe``. + +:: + + >>> load_contrib('pnio_rtc') + >>> t=rdpcap('/path/to/trace.pcap', 1024) + >>> PNIORealTime.analyse_data(t) + {('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [(-19, , {'length': 1}), (-15, , {'CRC': 3, 'length': 6}), (-7, , {'CRC': 3, 'length': 5})]} + >>> t[100].show() + ###[ Ethernet ]### + dst= 06:07:08:09:0a:0b + src= 00:01:02:03:04:05 + type= n_802_1Q + ###[ 802.1Q ]### + prio= 6L + id= 0L + vlan= 0L + type= 0x8892 + ###[ ProfinetIO ]### + frameID= RT_CLASS_1 + ###[ PROFINET Real-Time ]### + len= 44 + dataLen= 22 + \data\ + |###[ PNIO RTC Raw data ]### + | load= '\x80\x80\x80\x80\x80\x80\x00\x80\x80\x80\x12:\x0e\x12\x80\x80\x00\x12\x8b\x97\xe3\x80' + padding= '' + cycleCounter= 6208 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + + >>> t[100].show2() + ###[ Ethernet ]### + dst= 06:07:08:09:0a:0b + src= 00:01:02:03:04:05 + type= n_802_1Q + ###[ 802.1Q ]### + prio= 6L + id= 0L + vlan= 0L + type= 0x8892 + ###[ ProfinetIO ]### + frameID= RT_CLASS_1 + ###[ PROFINET Real-Time ]### + len= 44 + dataLen= 22 + \data\ + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + [...] + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PNIO RTC Raw data ]### + | load= '' + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + [...] + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PROFISafe ]### + | load= '' + | Control_Status= 0x12 + | CRC= 0x3a0e12L + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + |###[ PROFISafe ]### + | load= '' + | Control_Status= 0x12 + | CRC= 0x8b97e3L + |###[ PNIO RTC IOxS ]### + | dataState= good + | instance= subslot + | reserved= 0x0L + | extension= 0L + padding= '' + cycleCounter= 6208 + dataStatus= primary+validData+run+no_problem + transferStatus= 0 + +In addition, one can see, when displaying a ``PNIORealTime`` packet, the field ``len``. This is a computed field which is not added in the final packet build. It is mainly useful for dissection and reconstruction, but it can also be used to modify the behaviour of the packet. In fact, RTC packet must always be long enough for an Ethernet frame and to do so, a padding must be added right after the ``data`` buffer. The default behaviour is to add ``padding`` whose size is computed during the ``build`` process:: + + >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) + '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' + +However, one can set ``len`` to modify this behaviour. ``len`` controls the length of the whole ``PNIORealTime`` packet. Then, to shorten the length of the padding, ``len`` can be set to a lower value:: + + >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=50)) + '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' + >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) + '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' + >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=30)) + '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' diff --git a/doc/scapy/layers/sctp.rst b/doc/scapy/layers/sctp.rst new file mode 100644 index 00000000000..12d96dbc6c0 --- /dev/null +++ b/doc/scapy/layers/sctp.rst @@ -0,0 +1,27 @@ +**** +SCTP +**** + +SCTP is a relatively young transport-layer protocol combining both TCP and UDP characteristics. The `RFC 3286 `_ introduces it and its description lays in the `RFC 4960 `_. + +It is not broadly used, its mainly present in core networks operated by telecommunication companies, to support VoIP for instance. + + +Enabling dynamic addressing reconfiguration and chunk authentication capabilities +--------------------------------------------------------------------------------- + +If you are trying to discuss with SCTP servers, you may be interested in capabilities added in `RFC 4895 `_ which describe how to authenticated some SCTP chunks, and/or `RFC 5061 `_ to dynamically reconfigure the IP address of a SCTP association. + +These capabilities are not always enabled by default on Linux. Scapy does not need any modification on its end, but SCTP servers may need specific activation. + +To enable the RFC 4895 about authenticating chunks:: + + $ sudo echo 1 > /proc/sys/net/sctp/auth_enable + +To enable the RFC 5061 about dynamic address reconfiguration:: + + $ sudo echo 1 > /proc/sys/net/sctp/addip_enable + +You may also want to use the dynamic address reconfiguration without necessarily enabling the chunk authentication:: + + $ sudo echo 1 > /proc/sys/net/sctp/addip_noauth_enable \ No newline at end of file diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index 9c7840c5622..4dbd4fa4345 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -27,14 +27,6 @@ some features will not be available:: The basic features of sending and receiving packets should still work, though. -Screenshot ----------- - -If you have installed IPython (highly recommended), Scapy will hook to it and you will be able to use auto-completion using the TAB. - -.. image:: graphics/scapy-main-console.png - :align: center - Customizing the Terminal ------------------------ From e75dda6c3063f6350a155e8fb47bbeecae21a4a7 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Sun, 24 Mar 2019 19:52:42 +0100 Subject: [PATCH 5/6] Skip *.gif, *.obs in Codespell test --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0f6765f25bf..aaceb169ee1 100644 --- a/tox.ini +++ b/tox.ini @@ -101,7 +101,7 @@ commands = sphinx-build -W . _build/html [testenv:spell] deps = codespell # inet6, dhcp6 and the ipynb files contains french: ignore them -commands = codespell --ignore-words=.travis/codespell_ignore.txt --skip="*.pyc,*.png,*.raw,*.pdf,*.pcap,*.js,*.html,*.der,*_build*,*inet6.py,*dhcp6.py,*.ipynb,*.svg" scapy/ doc/ test/ .github/ +commands = codespell --ignore-words=.travis/codespell_ignore.txt --skip="*.pyc,*.png,*.raw,*.pdf,*.pcap,*.js,*.html,*.der,*_build*,*inet6.py,*dhcp6.py,*.ipynb,*.svg,*.gif,*.obs" scapy/ doc/ test/ .github/ [testenv:twine] description = "Check Scapy code distribution" From 6a0e497b7bcc4c1be98ff4cdb50ad6e62fa2b09a Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Wed, 27 Mar 2019 20:46:40 +0100 Subject: [PATCH 6/6] Fix version graph for 2.3.3 --- doc/scapy/graphics/scapy_version_timeline.jpg | Bin 69112 -> 20845 bytes doc/scapy_version_timeline.ods | Bin 3745 -> 3764 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/scapy/graphics/scapy_version_timeline.jpg b/doc/scapy/graphics/scapy_version_timeline.jpg index e50ff8da249ab7aeb490766655dac4e5d4e6ddcf..840dbc04a1e67c44fb0afccab659af6eb50a1063 100644 GIT binary patch literal 20845 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 literal 69112 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 diff --git a/doc/scapy_version_timeline.ods b/doc/scapy_version_timeline.ods index 1e4a3d2a861ebf738ac1348c93b8cf1fecc86054..10e0bca6960601bbbff4912fea3b82d94b39a010 100644 GIT binary patch delta 3490 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 knb~;$yP*Fu{oPwrRG3A0{x`4xacrhUGV{_5c6? delta 3449 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 M&n3d1G=F9P0Z|%nSpWb4