pax_global_header00006660000000000000000000000064126365503650014525gustar00rootroot0000000000000052 comment=6d4483b76f916d2c993cf163836d893a3ec951df binwalk-2.1.1/000077500000000000000000000000001263655036500131555ustar00rootroot00000000000000binwalk-2.1.1/.gitignore000066400000000000000000000000001263655036500151330ustar00rootroot00000000000000binwalk-2.1.1/API.md000077500000000000000000000134271263655036500141220ustar00rootroot00000000000000Description =========== The binwalk python module can be used by any python script to programmatically perform binwalk scans and obtain the results of those scans. The classes, methods and objects in the binwalk modules are documented via pydoc, including examples, so those interested in using the binwalk module are encouraged to look there. However, several common usage examples are provided here to help jump-start development efforts. Binwalk Scripting ================= Each of binwalk's features (signature scans, entropy analysis, etc) are implemented as separate modules. These modules can be invoked via `binwalk.scan`. In fact, the binwalk command line utility can be duplicated nearly entirely with just two lines of code: ```python import binwalk binwalk.scan() ``` The `scan` function accepts both args and kwargs, which correspond to the normal command line options accepted by the binwalk command line utility, providing a large amount of freedom in how you choose to specify binwalk options (if none are specified, `sys.argv` is used by default). For example, to execute a signature scan, you at the very least have to specify the `--signature` option, as well as a list of files to scan. This can be done in a number of ways: ```python binwalk.scan('--signature', 'firmware1.bin', 'firmware2.bin') binwalk.scan('firmware1.bin', 'firmware2.bin', signature=True) binwalk.scan('firmware1.bin', 'firmware2.bin', **{'signature' : True}) binwalk.scan(*['firmware1.bin', 'firmware2.bin'], signature=True) binwalk.scan(*['--signature', 'firmware1.bin', 'firmware2.bin',]) ``` All args and kwargs keys/values correspond to binwalk's command line options. Either args or kwargs, or a combination of the two may be used, with the following caveats: * All command line switches passed via args must be preceded by hyphens * All file names must be passed via args, not kwargs There is one available API argument which is not exposed via the command line: the `string` argument. When `string` is set to True, data to be scanned can be passed directly to the binwalk module, rather than a file name: ```python data = "This is some data to scan for signatures" binwalk.scan(data, signature=True, string=True) ``` Accessing Scan Results ====================== `binwalk.scan` returns a list of objects. Each object corresponds to a module that was run. For example, if you specified `--signature` and `--entropy`, then both the `Signature` and `Entropy` modules would be executed and you would be returned a list of two objects. The two attributes of greatest interest for each object are the `results` and `errors` objects. Each is a list of `binwalk.core.module.Result` and `binwalk.core.module.Error` instances, respectively. Each `Result` or `Error` instance may contain custom attributes set by each module, but are guaranteed to have at least the following attributes (though modules are not required to populate all attributes): | Attribute | Description | |-------------|-------------| | offset | The file offset of the result/error (usually unused for errors) | | description | The result/error description, as displayed to the user | | module | Name of the module that generated the result/error | | file | The file object of the scanned file | | valid | Set to True if the result is valid, False if invalid (usually unused for errors) | | display | Set to True to display the result to the user, False to hide it (usually unused for errors) | | extract | Set to True to flag this result for extraction (not used for errors) | | plot | Set to False to exclude this result from entropy plots (not used for errors) | binwalk.core.module.Error has the additional guaranteed attribute: | Attribute | Description | |-------------|-------------| | exception | Contains the Python exception object if the encountered error was an exception | Thus, scan results and errors can be programatically accessed rather easily: ```python for module in binwalk.scan('firmware1.bin', 'firmware2.bin', signature=True, quiet=True): print ("%s Results:" % module.name) for result in module.results: print ("\t%s 0x%.8X %s" % (result.file.path, result.offset, result.description)) ``` Note the above use of the `--quiet` option which prevents the binwalk module from printing its normal output to screen. Each module object will also have an additional `extractor` attribute, which is an instance of the `binwalk.modules.extractor.Extractor` class. Of particular use is `binwalk.modules.extractor.Extrctor.output`, a dictionary containing information about carved/extracted data: ```python for module in binwalk.scan('firmware1.bin', 'firmware2.bin', signature=True, quiet=True, extract=True): for result in module.results: if module.extractor.output.has_key(result.file.path): # These are files that binwalk carved out of the original firmware image, a la dd if module.extractor.output[result.file.path].carved.has_key(result.offset): print "Carved data from offset 0x%X to %s" % (module.extractor.output[result.file.path].carved[result.offset]) # These are files/directories created by extraction utilities (gunzip, tar, unsquashfs, etc) if module.extractor.output[result.file.path].extracted.has_key(result.offset): print "Extracted data from offset 0x%X to %s" % (module.extractor.output[result.file.path].extracted[result.offset][0]) ``` Module Exceptions ================= The only expected exception that should be raised is that of binwalk.ModuleException. This exception is thrown only if a required module encountered a fatal error (e.g., one of the specified target files could not be opened): ```python try: binwalk.scan() except binwalk.ModuleException as e: print ("Critical failure:", e) ``` binwalk-2.1.1/INSTALL.md000077500000000000000000000076551263655036500146250ustar00rootroot00000000000000Before You Start ================ Binwalk supports Python 2.7 - 3.x. Although most systems have Python2.7 set as their default Python interpreter, binwalk does run faster in Python3. Installation procedures for both are provided below. Installation ============ Installation follows the typical Python installation procedure: ```bash # Python2.7 $ sudo python setup.py install ``` ```bash # Python3.x $ sudo python3 setup.py install ``` **NOTE**: Older versions of binwalk (e.g., v1.0) are not compatible with the latest version of binwalk. It is strongly recommended that you uninstall any existing binwalk installations before installing the latest version in order to avoid API conflicts. Dependencies ============ Besides a Python interpreter, there are no installation dependencies for binwalk. All dependencies are optional run-time dependencies, and unless otherwise specified, are available from most Linux package managers. Although all binwalk run-time dependencies are optional, the `python-lzma` module is highly recommended for improving the reliability of signature scans. This module is included by default in Python3, but must be installed separately for Python2.7: ```bash $ sudo apt-get install python-lzma ``` Binwalk uses [pyqtgraph](http://www.pyqtgraph.org) to generate graphs and visualizations, which requires the following: ```bash # Python2.7 $ sudo apt-get install libqt4-opengl python-opengl python-qt4 python-qt4-gl python-numpy python-scipy python-pip $ sudo pip install pyqtgraph ``` ```bash # Python3.x $ sudo apt-get install libqt4-opengl python3-opengl python3-pyqt4 python3-pyqt4.qtopengl python3-numpy python3-scipy python3-pip $ sudo pip3 install pyqtgraph ``` Binwalk's `--disasm` option requires the [Capstone](http://www.capstone-engine.org/) disassembly framework and its corresponding Python bindings: ```bash # Python2.7 $ sudo apt-get install python-pip $ sudo pip install capstone ``` ```bash # Python3.x $ sudo apt-get install python3-pip $ sudo pip3 install capstone ``` Binwalk relies on multiple external utilties in order to automatically extract/decompress files and data: ```bash # Install standard extraction utilities $ sudo apt-get install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools ``` ```bash # Install sasquatch to extract non-standard SquashFS images $ sudo apt-get install zlib1g-dev liblzma-dev liblzo2-dev $ git clone https://github.com/devttys0/sasquatch $ (cd sasquatch && make && sudo make install) ``` ```bash # Install jefferson to extract JFFS2 file systems $ sudo pip install cstruct $ git clone https://github.com/sviehb/jefferson $ (cd jefferson && sudo python setup.py install) ``` ```bash # Install ubi_reader to extract UBIFS file systems $ sudo apt-get install liblzo2-dev python-lzo $ git clone https://github.com/jrspruitt/ubi_reader $ (cd ubi_reader && sudo python setup.py install) ``` ```bash # Install unstuff (closed source) to extract StuffIt archive files $ wget -O - http://my.smithmicro.com/downloads/files/stuffit520.611linux-i386.tar.gz | tar -zxv $ sudo cp bin/unstuff /usr/local/bin/ ``` Note that for Debian/Ubuntu users, all of the above dependencies can be installed automatically using the included `deps.sh` script: ```bash $ sudo ./deps.sh ``` Installing the IDA Plugin ========================= If IDA is installed on your system, you may optionally install the binwalk IDA plugin: ```bash $ python setup.py idainstall --idadir=/home/user/ida ``` Likewise, the binwalk IDA plugin can be uninstalled: ```bash $ python setup.py idauninstall --idadir=/home/user/ida ``` Uninstalling Binwalk ==================== If binwalk has been installed to a standard system location (e.g., via `setup.py install`), it can be removed by running: ```bash # Python2.7 $ sudo python setup.py uninstall ``` ```bash # Python3 $ sudo python3 setup.py uninstall ``` Note that this does _not_ remove any of the manually installed dependencies. binwalk-2.1.1/LICENSE000066400000000000000000000020751263655036500141660ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2010-2015 Craig Heffner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. binwalk-2.1.1/README.md000066400000000000000000000025011263655036500144320ustar00rootroot00000000000000Description =========== Binwalk is a fast, easy to use tool for analyzing, reverse engineering, and extracting firmware images. Installation ============ Binwalk follows the standard Python installation procedure: ```bash $ sudo python setup.py install ``` If you're running Python 2.x, you'll also want to install the Python lzma module: ```bash $ sudo apt-get install python-lzma ``` For instructions on installing optional dependencies, see [INSTALL.md](https://github.com/devttys0/binwalk/blob/master/INSTALL.md). Usage ===== Basic usage is simple: ```bash $ binwalk firmware.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 TRX firmware header, little endian, header size: 28 bytes, image size: 14766080 bytes, CRC32: 0x6980E553 flags: 0x0, version: 1 28 0x1C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5494368 bytes 2319004 0x23629C Squashfs filesystem, little endian, version 4.0, compression: xz, size: 12442471 bytes, 3158 inodes, blocksize: 131072 bytes, blocksize: 131072 bytes, created: 2014-05-21 22:38:47 ``` For additional examples and descriptions of advanced options, see the [wiki](https://github.com/devttys0/binwalk/wiki). binwalk-2.1.1/deps.sh000077500000000000000000000102031263655036500144430ustar00rootroot00000000000000#!/bin/bash REQUIRED_UTILS="wget tar python" APTCMD="apt-get" YUMCMD="yum" APT_CANDIDATES="git build-essential libqt4-opengl mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools zlib1g-dev liblzma-dev liblzo2-dev sleuthkit" PYTHON2_APT_CANDIDATES="python-lzo python-lzma python-pip python-opengl python-qt4 python-qt4-gl python-numpy python-scipy" PYTHON3_APT_CANDIDATES="python3-pip python3-opengl python3-pyqt4 python3-pyqt4.qtopengl python3-numpy python3-scipy" PYTHON3_YUM_CANDIDATES="" YUM_CANDIDATES="git gcc gcc-c++ make openssl-devel qtwebkit-devel qt-devel gzip bzip2 tar arj p7zip p7zip-plugins cabextract squashfs-tools zlib zlib-devel lzo lzo-devel xz xz-compat-libs xz-libs xz-devel xz-lzma-compat python-backports-lzma lzip pyliblzma perl-Compress-Raw-Lzma" PYTHON2_YUM_CANDIDATES="python-pip python-opengl python-qt4 numpy python-numdisplay numpy-2f python-Bottleneck scipy" APT_CANDIDATES="$APT_CANDIDATES $PYTHON2_APT_CANDIDATES" YUM_CANDIDATES="$YUM_CANDIDATES $PYTHON2_YUM_CANDIDATES" PIP_COMMANDS="pip" # Check for root privileges if [ $UID -eq 0 ] then SUDO="" else SUDO="sudo" REQUIRED_UTILS="sudo $REQUIRED_UTILS" fi function install_sasquatch { git clone https://github.com/devttys0/sasquatch (cd sasquatch && make && $SUDO make install) $SUDO rm -rf sasquatch } function install_jefferson { $SUDO pip install cstruct git clone https://github.com/sviehb/jefferson (cd jefferson && $SUDO python2 setup.py install) $SUDO rm -rf jefferson } function install_unstuff { mkdir -p /tmp/unstuff cd /tmp/unstuff wget -O - http://my.smithmicro.com/downloads/files/stuffit520.611linux-i386.tar.gz | tar -zxv $SUDO cp bin/unstuff /usr/local/bin/ cd - rm -rf /tmp/unstuff } function install_ubireader { git clone https://github.com/jrspruitt/ubi_reader (cd ubi_reader && $SUDO python setup.py install) rm -rf ubi_reader } function install_pip_package { PACKAGE="$1" for PIP_COMMAND in $PIP_COMMANDS do $SUDO $PIP_COMMAND install $PACKAGE done } function find_path { FILE_NAME="$1" echo -ne "checking for $FILE_NAME..." which $FILE_NAME > /dev/null if [ $? -eq 0 ] then echo "yes" return 0 else echo "no" return 1 fi } # Make sure the user really wants to do this echo "" echo "WARNING: This script will download and install all required and optional dependencies for binwalk." echo " This script has only been tested on, and is only intended for, Debian based systems." echo " Some dependencies are downloaded via unsecure (HTTP) protocols." echo " This script requires internet access." echo " This script requires root privileges." echo "" echo -n "Continue [y/N]? " read YN if [ "$(echo "$YN" | grep -i -e 'y' -e 'yes')" == "" ] then echo "Quitting..." exit 1 fi # Check to make sure we have all the required utilities installed NEEDED_UTILS="" for UTIL in $REQUIRED_UTILS do find_path $UTIL if [ $? -eq 1 ] then NEEDED_UTILS="$NEEDED_UTILS $UTIL" fi done # Check for supported package managers and set the PKG_* envars appropriately find_path $APTCMD if [ $? -eq 1 ] then find_path $YUMCMD if [ $? -eq 1 ] then NEEDED_UTILS="$NEEDED_UTILS $APTCMD/$YUMCMD" else PKGCMD="$YUMCMD" PKGCMD_OPTS="-y install" PKG_CANDIDATES="$YUM_CANDIDATES" PKG_PYTHON3_CANDIDATES="$PYTHON3_YUM_CANDIDATES" fi else PKGCMD="$APTCMD" PKGCMD_OPTS="install -y" PKG_CANDIDATES="$APT_CANDIDATES" PKG_PYTHON3_CANDIDATES="$PYTHON3_APT_CANDIDATES" fi if [ "$NEEDED_UTILS" != "" ] then echo "Please install the following required utilities: $NEEDED_UTILS" exit 1 fi # Check to see if we should install modules for python3 as well find_path python3 if [ $? -eq 0 ] then PKG_CANDIDATES="$PKG_CANDIDATES $PKG_PYTHON3_CANDIDATES" PIP_COMMANDS="pip3 $PIP_COMMANDS" fi # Do the install(s) cd /tmp sudo $PKGCMD $PKGCMD_OPTS $PKG_CANDIDTES install_pip_package pyqtgraph install_pip_package capstone install_sasquatch install_jefferson install_unstuff install_ubireader binwalk-2.1.1/images/000077500000000000000000000000001263655036500144225ustar00rootroot00000000000000binwalk-2.1.1/images/README.md000066400000000000000000000001271263655036500157010ustar00rootroot00000000000000This is just a directory to store screenshots used in the github Wiki / documentation. binwalk-2.1.1/images/binwalk_ida_plugin_output.png000066400000000000000000004215601263655036500224020ustar00rootroot00000000000000PNG  IHDRW,Ⱥ iCCPICC ProfileHWXS[R -)7Az;FHCBP ]DPTtEĶ@ւbA{_QQł 7I}7s9Ν , U 'a&&%3Ih"wTTGy uKI QrDk<Po0+O VB.Z*Rh_ SY,a: |v: ؚ!;Ł 993!VBlC:Jò\B٬9g9dGЇ! 'kfC ~jD$*_q|/C7bš/ eG-K(h//8v gFG"Q 1+xÕ-ȈMD[y+@!ʊ T1j#GK8B.M-sDyaVl:^yA2_,+J 8`.?n3WOo ;jfFnj^˃ LVq&+$J{/ȋqq|`1l`&~O6X@XhF=#|ǀD\ rA>zK&͗zd 6[wc*J'D1l:6!')pE B(,a,j]xː~ƥ&\2ek ';baLK:E3400'[܀! Ă$0V=@ֳ<l` 0'p\&F/x {0  !tDE qF<$ F$#bd)A!9A."]C Q 6jNDQo4Eh.Z.EWehCϠћh&10=s|H,KÄ+ŪX#|ױ.q:- p6/Wo@#h,`B"!0PD(%&#;H$2&D'7Ĺĕĭă&b'8H"4H$wR$E#6NzId]-9L ɥSkg!9%9#9WH9r Q)&wJ,%RF9@9Gy@y+///"?Y'HLnOT9՗:*VSwoi41͋Lˣ>*8 *)RS4RVXXxDbKiRqJteH{/*?W!pTT9Ct_:~ޫJT5Q VT-Qݯڮ:f6[BZc3ٌՌÌ[y[1k>WRTYᯑV^&i9Ysf9Ƴ?<e5WkV֠v@{Y~NS:}t]]Ӻ/jLof6 k7я/?Ѐblf`P0pa=#9#g MFFM?7Q7 6)05y`J3452aF4s62ja;gW_@--x[-:'&LOpےjmoYkmŰ *z5pbĵ['~vζe}F&ĦЦ捭-۶.n]k{ {}C2fNB}NN)N[n;:G9tBpqYr哫ka׿,ݲ=d2;iפw}w.Gv.O=Ogc//ngfޙ_X}|u? H tD Zt;X;\<2?%Z8CI9Wg[f̜=S`!(tn w4QC*<괉Mſ=+?Ίudl9sVyVP\|.{nE9ӃM3gzg4?xFs.8ջ '.^<~ReummǮ8\9^wjCKGcSiTXtXML:com.adobe.xmp 1 1 2 ؀@IDATxwO])(BRij3+ mҔJB) !֝}uZ=s{~oqB     @n٤ePYy er듽a@K*I@@@@@@+L+W'"     %%g      p .YJg@@@@@d\dl@@@@@D@wԩ뎒      s%@@@@@@x u      @"<\Ij@@@@@@ !$B      W@@@@@HH+ P     $"ÕD`F@@@@@L2HU}}Q6[D@tV)'27M3[?C@@@@@@2e!       pw+MoPfThqn&Ys F]u B+Y VOܑ:A2@@@@@r%}Z+     \r.oC =aySœ (1qywKWyNZzx#     %8+oUmˣOilGaaU?ʹa(L﮼:|M,WV=CFw_Ut?Vo=:=ɢGq=}FF~mkS&&{2؍Y3kNJd*4X@jn7$RJiUSf]Yϩr@yش|ɡX2O^_)7z}uʯTUHՁXZ<S9sbB kzszhk=,n(a;gyUҜK W*:]MVINא6W(Zk=_6n,ӦZfu5Y9~e7*/= )jQ89nh z"}UM`͓\ubKe;RF|0 *9~SyG~c|Tz;:}kV+k@@@@@ @b <<#     8ɕӣ}ʘaN{wӳŭ1%:qruzO*ZTfYz'Ek+lڧQϕ7jy\Y:h\py\YMZV^o }\\7?̫aj.wˠK^ZzZEҺUUv_*kV+y j#дMh[ϷYϕNJ'e0{gMIGw4_63G4w`yl}nۄzh.7+مr*4b7(뷷>Ϩ+X 9lF6=*ZϦͿ*ʤLꪱ"    -?-@@@@@HD =WVDi)e\y5x-gmBbiRseֳcGUo0[b[_ݩXϕK6ZίŻ,Z5귯wl\Hgx]"Z(tJjiۺdz9!^gWX~II?n2,[2ˈ=Yy-U'ze\BsZcQ_4}K4Z늵?5_8^;U.ͷ*3@@@@@8#@ϕ3!     I $Jӣ}seP{JbPq_wA] }J`b:|oJ@?ao4ʖ%BZ[ȳ;hc=~f=.Fg qw;C\4lU}ulشH?V2*7{IٯΒ9NO55_LS9tZEgTmDN񆓻ۃeji냬_5?Y>CPrseXJJ @@@@@b Pck0     $!+;W nOvSΕ ѮF_X ,#2j}&/Td>1_Vz<ŕjTY9CvRޓǶLL <{BPE ȩꪵʄ T6y^ %+埮d 7\ *yGi>Hwyqc2WbbtiO|ڼzfgw^9_m2,ٶ[w4}ڟGywӔhg@q8տ7΃*7SV%@@@@@r@@@@@@Ns{._P6Myb }zrwݞ0vbo2k^4u6PyÕ͖,[*2 tr1`e^VS%kZ ~7ݧ|_eXŗ)䘬>dg"\~ڜ9f/lvIR߁g`.e+ ݪ9jqy}cbu}U@@@@@ !*C@@@@@HXU:u&$գ̑      U#@ϕ@@@@@@@b}ݷz J:u/      pE sy@@@@@<\l@@@@@h\;     [+[!     -Õ+c@@@@@@b ;;wD@@@@@HD+P     $$zHLBC@@@@@/@ϕ&       p%Q&      x߄@@@@@@ QW-Ot"@@@@@@@@@@@@s SQ%@@@@@@J"0T#      p%!@@@@@@Dx       @B<\IH:@@@@@@ $C5      WR@@@@@HD+P     $$ÕTC@@@@@J"0T#      p%!@@@@@@Dx       @B<\IH:@@@@@@ $C5      WR@@@@@HD+P     $$ÕTC@@@@@J"0T#      &Tyv]VP>d[P a'TF9-Mӻ*; ZuK et {x}/R8sDBcPnɨhª)s\r\/ߏ*.QyR-e 6o=WOjQ?Gە9m4Sο~r*+NGkV]O.Y76lOֳ]s}co*]e5nS^[3{V5 \6WGrdHC+#Pf Π 9w(e {T#MxU9;_3c_R9ӣcm|SO wV̦NEV׺+\s}$r8xrwָ9Y+PkQQrSn~&bmRx)e[l1W>\ڶv^U1=C.>0o ?Z7ewt*~|Slʫ޺[#O{}Y~bEVmj[sb   #8@@@@@@ -&f_kXm[j 鯜xAer22l˷&{"֍HVy2 w否Ӕe V>Pi!Rmϩ~lY﹨*'_ɾ8MVl;G.{GL"ݜ0~՞D)UE>3ƒھේ.'=,qj*hGj^ ַڙ+W6n>2e93:}M޲ϱJ}ϔo3IΕ+?T rw_    p5$C@@@@@@N7EK>NY9kKMLl{hjfVyOYkkw5 kQcřyAJٝ&Ynn5s{y5[KPbbg¶*V9oCkr H<Un2@n䪃%~[yM7NJ&{<'Vidʶ](?rs_hPmGg(b-ړRwgmwe~kB~qz䭛i=ș+j{U1XNe`m=b=EV;>,>R1 o)vsH^A(n:GW_W}qzFc wƄ3 o5Mn=D+^,jsuY#zlU`=b3[X>ΗXO! o=vkRx(>;5Cͷm̳bw    psJO@@@@@,RaЬsj-G}I?<^8_ʅ| W ?D/RX?zBPiQ :Mk{U?kAŊxC?pk)zvνQ4Gz⭁@:7tFFB7oS`C܉L[F)S%;.8 Z0˗)lK e精S9='^f.1cM]ɚީ\e6k]thW.J4w]r ɯK(R}X>ÕK4R.z<KdiHiu;fzdmAyGm~>!t}m ,}{,F=;|mF_'I3Øc&]sl*EY(o*vo# *B,v4_rhޯZ-@@@X?2W      ptVۀ݀4ڡ(eo+.];ۢϙc ],|oCm{g>E*'Uߨ{UaTpTNl'gBdBt꠵(a=2MW/o?mp"Q:S[+H@%k‘v~:rXL1eP 9T>خڦ8r𠽳5]/kDCw{ΪvΟ#+j{)oJA%TxE;Ee[}EE@@@jptL       f7N&?jZ& c-_Tzr\j3~ʽGF)[m,` m5:kz[URgݢ޽?h#y2Όu3_nUho-tjDv@D?D_=f8-sYhPޛמY=f)?x4nϳpկD֓r*'5>0O CĔ,I-t_ͮh/utu9|ںal=TJAZS},}k>sFiwKrtU[j K"=u[PX>kG^VCQ}^ʿw}Z]\ݬe^oYϸҩ~iW ZkG+{^9YYezQ>VN>7dL}Y^/B@@@*pUvX      6nsZ>[a-B=jX(Z6;tU*>WP雦RyQ3,گ= *3[(gi|ruIɒu(26_J_ۿl?&n-e.Qauf]+&X9v<wW}χ(Nٻ&G\-k!Zb^_/>ur']|Տ{n_|9>`,ԮuZwe|jg ٔ"jݽ'a}my|/QP]#O=e)~M1Zό{tw]'8k%}ѯEEMId-Xώ_|}sXlTUbwrԸ}U#w}-W.i;͞9 \})G     i(Õ4e      p pL9"@@@@@HC!.F@@@@@O+Wg!     @ p% qY5     \}<\>S@@@@@P+i˪@@@@@@rD      <\IC\V     WWϔ#B@@@@@4Jj@@@@@xr}     WU#     'Õ3@@@@@@ {ݧjI=(sQ,U5m%|7l@T2OU{N٧MY_|R*N~;D9ʈDw?XӋ6\'r7,/~?Ws]Fw=7R&z:T6.tBϿ's٤Bn(O wawr\뇿 e\i"8;@91      E \;jA      =Wb2      [+5G@@@@@J@LF@@@@@b p%      @<\I      @l`@@@@@HB+I1@@@@@-Õ#     Ip% &#     x[q@@@@@@ $d@@@@@@ Wbk0     $!Õ$     Jl @@@@@@$x@@@@@@ PZik3d>j3[ ;߅i @@7ȡ^S:Sc}fp'+t}{ƖXGr.矜}ޥݛtsQe˃=MTfvVϨd'%mwдGSyB{cﴎgj;~ʗ zb_(Sw|ʄui%GΏ{I)s]f㗛7*'D?QeLc@8/{Sc2 >|;]yʷ6S\w .'kz]]4oRy]'R6kAR?"     쩧֪k}zQfrR)=.ݧEzZ 1+U? (F];5X)ճ/rr-.7Xj R,/;Z\6Zx;zz;:@Xe#!(wM"wjuC*U2ͪ/7^ t8YNˬ-Vn5d|'5߇*+;>Z$0L%LՕ `-m2Wv{:Q"՗c(r_Ϛ3)T.[~eU@ZKݳs-oMwCVOJ5\ڊl/iSqֳDq9V^qe.A}?xɍ*߳]ve 8&}Q՗#| `pf`?86jG'}f 7v+/pu޽hγת|buWkG+K9ʝuSM/BiAEStWBv2eMӻjZuK ~'?mdg+rgy}c'%~?J؁_P6e) up:{eQqi^     W@ޠZH<n-zy:hQ4L=91U|ZȜ.FH'=~:k z}AKWy'+Jʯ"TH?;ki5sS=, )Xq/cYUZƬ?rd Sy:kqrGcNJVZrЎZ& Hwa[=T ?ۗh/ 9c4Uo+ׇݠrlK fS? &?+nˆwiX#R.3+rsp zΥ+.=sO^Rȭv]t'XS&{i#oj__],7݇)rY^Fqk }}_q3"X|itv,pLqMֿersT?چ~T3n:*wz~LJ]ib 7_ ۷nʶm'.aY YKuK@y~UMo4vOĔf,}/J?O&WŊ2ǡ:*MpęW#KX5C~09""#|`m @n){aJz8<t5JӝzV\JqcWLߴ*]M"X*+/dy]vSﳧJY&@@@@@ifGOy/Hz#:6gŪBbf-af&Z.5HOrS+9G6k4kZ(h̉5*}lqa_[ӎsМG*Xˣ҆4; m:o宾kʟO9~S[ysy2_ ?CZρE;?e߯釜JcY2ڌGY *e+l}I==>Kmoe[tW>b ~wcF:G?fl6,;{6'|j>SRYƝ1N9⬖.\7sf3=V TӉ}Z [ѽsjz(#`t.ue>q|RYoւ2cN*feV~>ۙ^TUyWh嶏k[h7~ @IDAT5MӔXB:<ʱ_P<,o=U Tv}˽6%&̶89˼>yڪN/ NTnWus-pp^hIv=i)<|*lQ^Y]=:z]7-ٮJ7rZY셕rYSʕp (_k8=VTyznow'}O9HuoS_<,ɮuSee'쾸G;ClýE5}[*?}}v|F/o7ou PfP>_®ϝ`_6MUpIyO=,Q+ܶJ{Ԛ "w}gH4˖ۯSegSώ(=LJӇr[OUP9xr\oTۘ[j}λ2{AZߨl lE9jZ~2Q9BgR2rqdmeVՔ94Ą.WylgsYf'h`;#<l:]Yܟf:A^^e&?$(UF8yZ fZgVٰb9ev6v>ve=C[T^jϱ v<) _1vGG#K5}_ ku)Dj'|*ͮ3+v{tf 6Kczcs?3^ZߢE`o{2=k q :w~ToOc7k {Bxt_Zk 74ͷ=s{ >Y^kAcG#@ɣf5 UIxMtywEQk_,AW/k-OUV{2=;t:hA* *D;1eaF嫝׶077Z[댾<= [oڿ _>Brˌ1ۀ$|4_+[KGON*crEQzJkϨs~*e͟>mhVs Rwu3Ӯa-NJtO/Zޕ7Squ^ew)"=[gO(lI2v^5__''Tvrvi?Z1oeѺWܭ]﷖5n!oX˴n}5zJ%}*+c5cѓXz_eݴ1\/GNz;Q~LJ?O16}Wwϯ{ʭ6>R)@v#U_%*D90g Oi{STnl7rSV{rpMz~ϸ׍&毯Rі~hr/Xbgi!<M:l4?r;c[,c P^Q7QvZ9Uj`G^*Ϯ2̚ИCx/׾7Eyʻ^0~w|QxwkӔkꪬ.~Ͽ?:_|{i?mGaMu@wi3}LG{tN􈍪T+l# vLܸ9^ی_u]g& mohueF]u>NYϾ5~߄6kv!^?q?=ܧ{{P8v;8zϾQvަN@fZu[#l|s!     W@j=O>"k(~ߴ))gl ϰ5}Te{NUU"{TtXӿ9O͉ʖy#&Qg-5ն#~~")7{e-GZ[duty*=_u=a]96g|~>|Q}ƶvYʅ)>F=>j-hR}_~Qę5Qh-h~+ kv_j;!N:om$SZ zz'GuKv*kLU~}ʯ>DU.檿_er+.R.kק*Sc9_{Xqo[4/wrj}G)kxKu/>Cjc*c _>Pݢ9x7NJykj#%ow ?V^u96.2\[N}vUxG?gzi|Gj_W     17H%7jMjSv$}ʉlz֢3c9\ZޝWew7cTyt S6r XQ۬RylX*k: 2Y@@уʅ#Kp!牶)ʭAhj_R=^3~eO"Yz{3m}L`$0nu,9nEZ(mngQ;4)ʖnQ@;>Ѧ`p?A屧 )}?˓$K卷U$~-ݵ2SU6h-_DN<,k"VdDPܾR8qۊ<_kRx8翸oWiȖQlk- {*ϱߥߡvaq~سVo=c*L)N"畆ڧv /sᵁş¯O:wOH=x M/y=S|2.QU^X4 fp7\ӫ\;V'v^?uwȯs!M&j|\^l](Q}v4M4'(,a=f75\|kWR2킯D7zy?~$~`lv_e=ޜr=/TcW,_ꤾ};}I@wUwar0c/As      ayo8Z]gOxKϧE} cvFe']i7g'YOWNXK3=VX eȪ_TS֞TϓYUE廚T[pu%SЦSo.fN_W|pZ8swZe?K٠pe֢lXڼ{٩\ R.iiw>ڕNk+G̴ve;km * Qx<)ӗӊ?\9c.;~7T o4Q2qQ1i-cm/]*5/R9`V)k*#wB^U+[Ue;Y[E嗮Ϡs~}oyð7j))kb=d-P'->T<=rGU>MTԥ5 {[9kʺW"w<{]{nI8 ڢۗovɑ>_S87V=m!8Gvkfw}.cnZaY4g7*g[Z=WE2?P--ZeDܽ|/68{^9@+Ծ+5|hvugAS|P c/tPk6Fc/_B3U_c9[NR.%{e~,vu';Iird[.H+Κ2T ۓ^/ {2ʽ\tOx6G^O;e'l?3ePÔeݟv e^v^Y6wh9^QVʐ]cۧ; (rV+'UNh-B]VU+ FF")(؊?QG7JJ;E@Q }elFl|>.g74ܞ3X;e[}4t凕[[.5<7= {Щ']T,2<,j{ǀ/iJ?ƖM7W\)so6B;>ʡ]X{ st4o2Jk^GT +c9sYm4J=o X r 藕l,=vl_mFmP\Ymdž{vyUN4`kR|vdT}>ewyV}o'VMrVR}AehMfX#8*ܤ+F}n U.嗶UyESr&M瓠rUҾ9O 6Zn[i3מwQ'OrG~mχWn1:G)Uu>Bo3E|ݣr{Sz}h8Unrgeo0VYoݟ_>0DsWk^3sv_?23sţverQC(&x毥&E>Svjo;Tn;-8~Bgo=W;3/{rxUM5?+w=]?;-qz<.ʵ&TV[8}v+ؿ;=}AT.e%Ϟs7i2)>_٧m_fioų4=V)?FE+쾷WU.uJi͇1K*|g;Q}}'WjgNpU^ 믹.R}*Wg::M2Kەʃ?R~͖rS#C߫'#V s3 eѕŎN4}7ty,%lZhoX[iIv\#fv?/Pe(iב}{N;XoZvevWv?ݑvw3Mckewqv qo_/S\';G׼u)~2}L:ϛotF9ee0q.W{NEfޓk^Wy/A9Pל^~ɒ>_Dk7~ʈ!!<|wceӠ\ZσfVyds~fOUU?Ҭv^/g0H%Mobw7v)GM+M93T>ԣ@oK7)Ùiȣl i1ت`[FO;_e%)צo갗k}7QF;eUW}>,q?ۏ?y-]՚𼃯~y@@@@@@Yj\5$ \>?&hcW?,Xi 5zI?&i6KTNn[Fi.iUWj?w S]^\\?itr̲vcլAی2{iYzYXճHįc36smXqt<{yMyz\={ '`VcOIkLǗ&@ϕKbn@@@@@\  [aP.ݵZʦ*vC)oO7 |SqeE߯U{TWj{\'~ߞ3u7<~!"]*wy2PC~'Oi3RY\s<\}IB| X;)2$h+:G9cʸX+Lw)   Т_aXkJkJE@@@@@ #zr5\f9h@@@@@V+ʱ     dH\ɐF@@@@@or[9C@@@@@ )+s      _x+r      !r%Cv@@@@@oX@@@@@2_dA#     |!     @˕ y9h@@@@@V/Wc9@@@@@Ȑ|!O;      劷r,     R/W2i@@@@@@[\V@@@@@@ C J<4     x++ʱ     dH\ɐF@@@@@or[9C@@@@@ )+s      _x+r      !r%Cv@@@@@oX@@@@@2_dA#     |!     @˕ y9h@@@@@V/Wc9@@@@@Ȑ|!O;      劷r,     R/W2i@@@@@@[\V@@@@@@ C J<4     x++ʱ     dH\ɐF@@@@@or[9C@@@@@ )+s      _x+r      !r%Cv@@@@@oX@@@@@2_dA#     |!     @˕ y9h@@@@@V/Wc9@@@@@Ȑ|!O;      劷r,     R/W2i@@@@@@[\V@@@@@@ C J<4     x++ʱ     dH\ɐF@@@@@or[9C@@@@@ )+s      _x+r      !r%Cv@@@@@oX@@@@@2_dA#     |!     @˕ y9h@@@@@V/Wc9@@@@@Ȑ|!O;      劷r,     R?,'~zUʓVU1UN9]%e.z)B]H&g{dym͋(9"w+ Rf𿾄ʙNWV_[y]\rÔi+Z^dG{>oʳQh޷UCqfzFYs>Se ~S.]}TYyR*}4<7y [aUƽrMlʄ˫5+֏ON*(^Avzַc#-U+=r5R*˄.WV()fdϩ$N{?]`z#)rlq{7>RZݥgVwB4r ;-V>\z쇔V2}.roڛg?4@@@ҏq$      @ ^\M+WfW`kX2e-8RyrV”:[ֲF:rR"Jӛ#U3Bda3Iw:+OlO%;wz]=ZZ/أ+U'熨| *vto;v?YΞoݝ+Qfj#l7Tl NgV*'<WVOnBYw+f4uJm?@?h6|<~=_*#hKy RYJ_.wowNVܷT~:dϥۛ~r;9*%Ǝ_(Wg*ܮm˨c}r}Xн~՞|_VWֿ>7ƒ~%[ʎ(8*fχB>];P󍊵`+s5\hSoϑɭ˩G4=\N- 9U9cŽZk^4UL[>/2D_o{\+jv?F Aw޵RevkkCIy靖wrjRzߩ<~6_38ύ6-G(oNar I=v}t렡o^XQ!k^(zk-/,qe"M˖*oac T;vM#Gl8JF7FUy:;/n`[p}b2Jc\yl_٨Q!U+H|L%v|eT؆UJ7Tx*؝Ker%Z}EE@@@ = k3=ǂ     @ /uNϋYK_jZ_j/jA+-Uemm+q~Ld-eZ"zӧZEvj,62w˿_l:O_hD! .rEVGC+?X~X9&{Mu߃J'4:y3yL;b5=Sʾ(=*raۏ+MUQv yQYhɟy=vlv="/Pyа/*Zg7X-_k帥?(lmP"9*(oIWbWuݚOQFu:|}=4oO85Fٯʜs='b☁6ߗi;ݯ̝S(r[[όa#=7/u|]|1m9zv仿 y]qy !ӯJ}tVt7cMriBsI}U~Q2fKy9ʓg糬v{msզcZOe   @MG!     ,zr52\f*!     O+rD      |@@@@@ҟ_s!     @ Jj@@@@@H|)G     i(+i˪@@@@@@ J;     _!.F@@@@@'+rD      |@@@@@ҟ_s!     @ Jj@@@@@H|)G     i(+i˪@@@@@@ J;     _!.F@@@@@'+rD      |@@@@@ҟ_s!     @ _Om֬MRx"Kn,1+㔉>zsSeHYvYVzV:_'Th22z}kQXg!9{\3v??>n~J_@OYHr-?5sS׭n)n3˟ϻwr\gʳ k-Ӿ3@8z\s!     YjUZ@@@@@@d蹒 @@@@@@|_q@@@@@@ \I      @|\8      _$d@@@@@@ _`@@@@@HF/Wb2      _/Wk0     $#+1@@@@@/+5G@@@@@˕d     ˕#     J2@LF@@@@@ J| @@@@@@dr% &#     .e<*޳fCQ*~Xㇿ;"FfY0Vϧ0A􎹚)c}\X:eA؊z`T ͒)>ZQ e(}479ҡݕ)NyUf7ZY.]g/MkX2TFTCT߶R.%މ!|/yRxMerST9՟jT63eB n\՞D*?;pEE3ì?l+?L=+,-].\C^{^YcϽ36k'cacE Mk9+) o,z4j+ )Ly,Խкyo}`fjw}yO/I :CL;\׾r_*!;H6vnQ֒xs֖ƕ$)Ho-6ir-vMy!w(rE ߮b ]*Z|^`iV-Aұ1 i\aϥSk,v|T]2՗ꨜOuQy5K)_?T>{|־TZ\3[swa6ܣb+s竢d&E* Ni뇄({=ULŵMS>w}et5ZרY9ݧPت*w~NJG%+bum)o)}v2XZ'OMQjoڤ|b*ūR/|Oմڛ0d8v55%v=pV;K:hgKx˺RM>W>?OzjaE  @2?-S.@@@@@@30bl1X'/j]0=\K8o[Kj{w}d[ o8LS[ƥ+ݤ[SߎYr>T>;;=>c-[ZgXsCYك~ikV *[d&uThv9_-7 n\vFm=zWfqTB>Ԯ`79-^F߽J.W_S5~Ux&!,m4beVv+u'.TJ́w~KuuEGoO{y{ugd+Xy=_6Mb=VSgsb]_>٧K^   HP      H$N"d=MlX [2%.lf_%<]?̷fZgo2.TZBk *M}UhYc:8φoPo=\\)]޳%oloa9|sJc?(?RVhˤΟ&3Th/ndG_McUn&i}Rx#:'X˙k]?:針ߕJ΢RʆSNqݏ~1:QO;>Cn9zmYkiz7i;Z4UL~*>dʥV_[!Y[Oȃ-T:t|}i{A7~=֓kcd\AH em?E5iRv=*{.ڗ34 =*߸(i>\5{+5jWU6xOKn4rֽ`]V0f>]{5>M}{T>c*?\g^?14}Ũi[vf 5?V]kO5Ю}xji? g.S.Yco@#>ve^r*?uأ͗%dm#d=v>f'n|TwE{v|ӎ7=W3EޢFy/S-T.#_嚫~P;A4>rʿbj\|_W;{I{nCKqPYEs[!FDl~iCU?dc+9Q{> ϯKS>kT4]O=m%ǃSc:=FivDӱEU_7Okl7eeݦeo\;}qM//7Ѯ^7dg$41_ -NՔuq  pR#q     \ +Qmݢq ykRy5,xAnkaNK^КhYx}RkG=w8ѱ~tL4>ֲ%KU.n^,iacOв=OL? xgngRߺI}ۿʪ(8g )<^N\Z@mn1T}΋oMJ`qI|[͗w:bkS=%kJX um=n6+ [r?. ;^vTs&a> |֐TJזKUwe Vи RoUf {ߔxkձ2SٿsEf[R,|b8epkykT=W캏hd-]:QMhWn{\/GOvۏ~+iS6[SPk1:s0+U-w_KY`-~=O%GC}}@[|VVYgey)f*uM`'7P{Fi#yR?ǖ73+żUC]a>Wm»L{X3\=>im*{ϱU+vɀW'LS}}\a?OR}˔ca\Unyd^fݥ{mUIGj*\k칳A7Ϋ7yAh1'A:icc?sZMջ{ m&`Ǚf8/4b׉vT5gUi{bB^Y3=onjv㻕|EG>-O|eI˨*~ڽzss*/[h=,=cѾkzݕ3{S!*˷_Y7nyT N#Bs߲=]S_okaaMei% R}?vAm̎bspۃ^*?Zȹ2{ު\qG[#Gdܶ/y_QNeָ>r‹L)D  @ '[?kF@@@@@ҕkh|t̨`-=>(>Rzܻ%}SŦ\aO6՗r֯G7P&*Z.`-U~~Z=H'jcmp1,qΏA%XCJw/R{u_ZK5uE&MZX,i-'x%۶ >zh]A|mJOXRz򉶖V/>ܪ=Yiѫv"TN~`[wfm;ۮSҞ'~Ynig;-ǟ#M/8zJϭ[ ٜzh}TdD̥谫`-R=hlǔ'bjj:ܫtXq/_ Py- }VŞ;̽>wo@Pw˅3JEV+<` Ybۿ֯Ц5zU޾ۭ}sIeរ'ؑ5h%8*{l;g2UD!@\)rv      WUӰ/i e5O/GVj=IZo%ͺ{kzK9{h4Z8zP[Gk;ȫ?~6Mm ]~ZlY^C)]>~^S~ckkiTe=2EaXw"U3V ߳]k)}h;^ZJ}~BW~J&ky@9{z7zl?b-J%c=VJq>*]57+4Ӛ)\{,* t&X >U{\w=?:$` [=H_Vpbei_i\v?SV,mϭP~:,9hO-wjϑ5Q}a7A4+r1ʷzxVz<]Z o e&xkkpwv^,CݥTj-ǎyX|n?g @{AX{m|i؋],%eEW-ϼ5K+lC_ʄ*\L2ߵb/Ĵ~/n_/4G\c`@f;}=:\/8-]xɹuԽboP%_Хn@@ uϺ{ @@@@@$0+zlb-KqaKPe-6be!ڇkk)'n=ޮJ._z{]KLuh֤D=V-pX>WYܳu|SCY黱{<CZʖY`l {n3^r'`v:-Qǭgɬ^.ꟹr[ޛ{vwd}4 usx*_){4Q keC՞jUq.-*GH{vNo_U8P5_TG~wwkp`,}˞ӽɦ;{JY%QNkO,Q5=g\U<3TZh)]/fhG{\-W^H޲ųd_Zغd6y]qH;olr[|a~vեl`ڐZMX{ڹUuߩ?ed`gw~Ui϶*ɭ_=Pr ]XӁT1e{+W~WlKq0hݷ gq4z=/r+c3Ŏ\bw4k3SݗLU>}i)sϧ֓'yZs*J ='<=65۷+j(wU|NY4){>kM?7+00ЮMٝ+NJ;-sU~Q VVf E#_dE@*;i31D@4pi6      իO媉\$8@kux<_~b5q*&ge^r*֢a@uZV}(4IhA;߳C罯Js2\N!~U:MNZg׷u\j-Gj覬`+?ޮߒ+k-lSuA-=P9/kuF\{;!oʃp~~ʽ(t7xV!T:Ğ7cy+Ȟхunu5_!nGs*i-:s~T^, 8M[ݯg/qod_{ lӠ*;VZjӏ(_;eUck嘟m&sÝ<ɮ'hnPئ]rkiy=h;\;|TQMȜ>`ƺ,5Uy콱UxrvW>'S}*ҁsy2~d4ɳNwBqV?9KVe-3U4~}t8ǟHB7hg\y4@{ iynDnP߶}UEF{)ȹSfRNz ם|1*J+ 뭬S_'Q>sxZ3{8ύj#>5{H&gqNv܍)5*lsrׯ㧽fw=W,&7ZQUg;7n>M_2ҮkR5ovj;ջ;̝Rv}yNwӿ<zվu_^D[_gص](A&culQj=;~*\!*}{kY<]E= p4C?SLYl=QQJWʇ;uS+զT  p|/v       ..J8^@@H^ liX2{('ܗMS T@ej\tԞ7;Kr\I@r v@@@@@.X9@@o:\yMy`\U;oT~Gv@.=W.5B@@@@@ @ϕp9@@HULei}KjYi$pJąyNoowڎLѪQ#2'r\?|u]ZmX|!_-uzσݔKS^U9t.=3l]:>)]^kebZҡݕuC^U}ϝo3 "0dr>|#J-bVʥLmUZ Mv݇RNʻr$qkف]>mv*Iw&pܡ=x@gm" jaVdK6יaJ!!#KwsVVQYëq`ϫ#kwfdL=>r}H@"|i#zR[=g%e͔eYfbY-Uw]W!i9/ER󟖺Z=/-Y}Y: sUgi'uV}K2_{i9}w&΍ǕM||^  g:8@@@@@HmKrZ\Nni-04|P|(ͷgz 8-̣uc4_$;œonQ>Z"}E>äRAZn|$7A~~b=nrl­gȘTy))go4})3PXy~}ykۧ|RL^`)X_5GYR Uyyʘ ZΧtyւȲZET=Tٽd:ʥFUz5^;޴y'Zrߵ嬅ylYui&̹Ů鸟T?/$D,]R*VU<ž+=SYl]>*.KuTNzϕ^`cYr+]M Tcze:f^[}a7ܣ,V2w*.!/*K&bl18y Qt ULŵ4jk[y2kEF]K-}nцQy7["^-lsJ#` vu>zRB@D 2MX)     dkv/ڡ+xKY-ܿvcL(뀖?+aϕ΢R8bu;c- Wk=Xq/b*x*&ȣEn(?kyUDܡ~ZnZNnmo}`a=PvuszVj6ZBkWEV~~]xLn&]D+Bۯ>U2/תn'4$j]}_Xv[e3?>y8tIƊڒ"Xء|ޭzj0{kpo ݲ%4?D3+7G2Gy- V:r5Gv_t1|?^?w`) my_wRO~͐_$y6=vrhEk!E{8YrZ&BY% ^2q=j^k؃817!M6uJk_G9UW%9+΃5Wx]&$IE #y*W v>8~ǓB~^tJ^|_Eg_*zx2|0ڢ3u`$j]`w m_|S0m>uvtOqE`wZc X 3yjs~O A.8Ή$sc&᷀&ф- Zg.;^:*;| -+aԟ,Qr~wy<.ϓhzϪZo<ԑK"B@:9 :.- ! B@! B@! Bu0{M}Dc#j_ ,Ӊ#VN 10=HLjwkt1r S_fVu?fd<ؕ?؎c:\ cЍ*ma:{̈DƧЗ;WidܹVC(2Oj33яʛ";m#1R4#4_xoU[*[kvi"Mh%3aΌKNcjrFOQݭ6#e:y U{dV(⑭@Ч*nY%ճ4f4WԥῈ׋\k$ 64:23z(e)B 2"蹰[1'p؍?CA|>>\pZɞ?͈![;ZYVWЏ6:e)[r-V2HBnz~뮳HhS'27EYq?ם7+Az}kUwuVscC[օo6#z oBywR7v-Uޤ0 |>: KȜ4mMa0+lyC/sqbǁؿq<9hD_Ev*|F0ȩWek9n7LC<@ZŎ q[aA ցT֡$:vso#~]ʷ ;{S-L~ߛgr@Wp%(X{0q6ȟOo ~΃\GVӱxP&柈z|9h:zi}G@:9[8r>Eʞ#fN-=hXy9p^sa}k-[G,tfgFo!ȓD! =|sϽf! B@! B@! B@U^_`*-Fz)3 v.>0[p0Q[D>ڞ*qhPmŢ4 aFmހlP>(3ȟԞzmȔހ@IDAT`'`d _$F";`Rn_^P.s7STc*:cfN┉!15}2PCD,I΅s~H=-7 :#lwCAn>= IPʵ.n8ժD'z?mlN6)r|zEVz6 ם-3ggF'-pw}}!>P }n ]Tv2J|ߚNWV^u 4Gm[24;VoQHݷv[^_a:P]RH}>UaP뻛_Cu5^F} ;ܨ/XܹayGydf`cTS ߱Ա4]]}WR>{o=;yOvO\(E (߾>w:t:Jhʱ4~NJ}=^IfG۳7]rQAUfH%B@! ^lnJ;B@! B@! B@! L` sgOMPϠ~ۤ0w3F15 03L KI`ֿ9 wfK냒O`yCpk q}󳦺{pB\jkQQ=?%_!`N'p\c@ɷ} s;3==Ɲb])>/րV5oO8xT-#jd.ΰ k({,yU(;Pj9mᎍKQZ٨cB*8G Ц[@ZBK܅'Z3LCo-W9= ׷CѠ%m8O WA@h4/9І.jԣ׭Ǹ/u*&j$a}:sK*׳<Ѳ$P#w-Hь|u-l701-N4j&U8:[80F~ QO)SbfT.UT7y|lLodty]&j-=-ڌ&9.L؛'Ptà5pJyoH2K滖;~3uҽ_m;hnQ<2^ <["w7U^쏙5s_w l+R^! Yo޻n^&B@! B@! B@"4܉r$#+3AOl>ujʈ XgJ*'In$g+o~~&GBo"sv6T ܲ(TŐ p#ܞ84ʥMxhHd'/YLymG9ELB=?-KG+Ua7Uj;V w"ަYl|"~snVʄ\pq%ZUalٿ1gov yK}|(*ۅR:ѼTŝ*)>pfb-_GŮl/׹?k~|"MA:@zsݱfQ[nj[?s|=9UK%uҊj[Z)4.,./sZuqܼk ߯ڒx{g #BEC׏ %iaeȲT%)?kApi7jA?\/Vwr^3ڨzsygPo% 㺴~ixoHD5̿Tafg92;@z|u6g˚p=1OR@KK;&;ed&\A];- cSYxh-,[Mt$B@! rv! B@! B@! B@fex9܊qƣk{tW1̵sefX~F0a@'x(Up̕d= H+eߴh`^P͈. LIFj|.u9ڟͦ3b*%ߟ 7g#GGq^I~uԐ'avdX޿<#6gJQoU"[Lޗ1"Y7IJ{~By;rEΜ'jF3 o /w07#5@S4_ܯk|꾬?ǛW"CMNv(WRg]Qoɘ 4he8B~sc1kYZWm`_WOfov@#;.`O(Ԙ>?ZIT^0 W7* zlu}Coo dc93F5ބ^[y6'#̃V>mt]޵>C92CKVށ|U3gk?>fp}ٍE>s 'i8({51mqz1WzP8+IQ5'ԑ\/;t֜?|Gpϊ6+:n0t*O{i~ȧ_.x(kS_o省Ox_{M/n=:,|?9h._q?y6-1#q|?h"+126rmejUTa4}xءnzC'fІB;QԍEѯKF<0-qbs6 Qj8:?'ߟF&oyhdy- |~vo*M`ZX J=[M63 t>׷^{&|~T#!O8hGp1q=lꓠ8@9zX4gSOc,9hᳫ'?()lۿ́nnz;9=|ǡC=y95*N|>}+S]1b{vmcUxOxMKߜN^5[j%s3y::ĤHSYjtpwg ןN[oj6ؿ'GB{!7; ~9]-~cB | Q|Qzp vؙΫ[vޚ ~\5}3n%:f@z@ZeG{N_@tqXۇp.\Sie칞c}ŁmR( z,8\Tex/$B@! "`vB@! B@! B@!6P޹]u7~GA! B@!}1;PGWWOCgֶJ"&2`g>1 @m.?[Q;\|+o̡! B O+yH焀B@! B@! B@F@~3B@! @ ~vˤot@nڻ=6NŖ/B@spVB@! B@! B@7\yCot[! B@#`7̽6f!sQՉBcڎV* 'W&(&KJԎB! B' ;W[,(B@! B@! B@$[u+SqN+u ! B@! B@! B$ ;W*%B@! B@! B@ϕ"+ ! B@! B@! o%ϕE ! B@! B@! E,kĩ#w^| َ^H@czj4$bdvPU- @=j29Z3E,\>Z̨zZg &#awr>baX .5 CWe%I háK.IGo'L;P:g&^Dƪ`{?&I'] ouЙпR8Ua9Z=D;kA_v_Yx$]}>$.?$K0 7&Iy!$]BgV~|YIC}8@*:)D\ioF~?vt7?vyjUuM\9!?~jü~֞:ݳcU;gq?QA3;2z_Kאq?;ҹ Л?r= B)Ϩ՜k*ڐ~@_3\e@NK]9@ٵp-HB@! )?-s#B@! B@! B@ ta? F^vM{6# / 2saGpHvb՟Qn5#t0اCFݫ}vBYC $ԫ'AWv+4:U.ۯcx[{]Hk[8ysby\hn]rQmp،K3IE;lff`4גB ;A7miէ2k"yq D3厗xGK0r=F2w`5,+ݾKM>3`;^.)DxG .wNܯySo\kSrr>yټ6\;+TjbQxzOWe`L #5<><3ݽ(zφo=6&L_ b ǷL,B@! @dJh$C! B@! B@! i tXev+U1;V.$Ncx5F\o3Ɉp lU{'Z {&OSjYbR4.8|N^/JDdIښ$^߅Cqbs*|}e߼x3"a?IeQ=n y,vtI&o'1O eO@5LF9?^!Niv&z_@| ڪM,kUt_es%;VZN-yNUBβ';V 1Q!*rԤ w|9{NUotU_e_ Iv#ڜHﲐr4wh⸳|)״Tf!`GM R+;OG[gVt՟`OXDt_薕-\SuqϬN U9oJGwPf>`PKvGlc5q}Me[}sm-mhl;5Đxp߽ a,9b-n!߬x~zj|q d ݸ^s5õs>{p܌9o"G\KWZs|ҭ8!koWkL4I&\W-*܈E #y*W v>8~ǓB~^tJ^|_Eg_*zx2|0ڢ3u`$j]`w m_|S0m>uvtOqE`wZc X 3yjs~O A.8Ή$sc&᷀&ф- Zg.;^:*;| -+aԟ,Qr~wy<.ϓhzϪZo<ԑK"B@:9 :.- ! B@! B@! Bu0{M}Dc#j_ ,Ӊ#VN 10=HLjwkt1r S_fVu?fd<ؕ?؎c:\ cЍ*ma:{̈DƧ4Bq<$9͌"ը84F?*o^ﴁKьpӜ!A{WvnXoC٥9P7l*hp̄`:3.9iO)u? /Fuڌe%[WYЋCVN,@Ч*n깇[Qc[QPn|-hϙ}vH%\/vsْ0 q[R@Ig`=Yoݲ?Sch\-X4#]=r|`Xrz~կ«L=ZdOsOpD|yλ3ь rfe%{Z nSSYF%M20If\o׏̷umWf?(::N}"G}~főײ;\oFB{ jn,s~hкPfD]_mC~-Uޤ0 |>: KȜ4mMa0+lyC/sqbǁؿq< ў0Yǜ~UVᅱvc4/s*P9:q[aA }&%Ցo6x3VPUTpۜmgcp=<gDy/=Mw8?1y`#;r {YaN#QoCd"9>p#ٳ~Q`=Pwփm$x3|+ܳO爙SKxGϢ>y2V?@s\yd׽\X"=-B\1>3 [9RM 쩎u|$׾O3{bUi3|{Ԫȟӑ>DU7 -*&r# 7^9 vܐc~ MM9#8gkBpGwsmb֏?`eY\/B'B@!{{KB@! B@! B@! x%U0#3Z=XSfv%]}h)#ajw~ sza,|Ƶ=UTm ЈڊEiJՍT9ښ٠|zQfΝ?=# ;ڊ)ݽkN8:%@g,o57//G@.s7STJt)ZLM?͜>EΘ~þl t_U+0S$ <ޕn{Z8ot0F(7vC÷8)J}z4;MΡk]ݦqUOXv?ٜlҷo{^ACFz]"վz!J^*W]8#R ,ƐS8ZD>7tjM`s;%>oMK+s\:|Ԇ_%sѕ4;VoQHݷv[^_a:P]RH}>UaP뻛_CuMٯ0aGCeXmhǎܹa|oCģҿ݃M|6adzF'^g'taNJJ>luRFxz|rL2++A67z㺣g{l od˅R{.zCG@Qʬd/ЄPnӠ+zq'5C vo T{?~twm EMT!D! xU)! B@! B@! B o3 H7 P=Q6BMn@ 3񠩿M#^~7c$;Ñ_93D쁱$49o[ f;NAraBu2R!p5ݾg5WbǡE5|ۇ;8:38הǸS+#jF#_]߾;#OeDE~va eo# EqC-w5qޥ{(l}MqzlQZ;a?t| h1SKh Н$CV0vI6aߞtE-]@h]g Z*0N XM{=._-K®Zԣ wqҌ>WF8Ji1tQ3%D_¡1=,c؎z~J3K>rc[el|@?d3$ǩZ̞q(aRb4Jyoyۿp tNnܙ>zVB@!޼wݜ~M! B@! B@! /EfiIFWpg$!@]|ԔpTOI4;V>ᵼ&۸<Z$}PZEJyVrnO~& p]y<4$tFyp;h<&CP9^P7+JePi8teƜUU֕q) g7Oѹ]X*S;[MUܩ~k-fB~T"xX7'rDt47l DnEnbO._)8;0|N*&Q!\w,8߮) OhT;KX 뎃NXuk'׭'][P~P'0זs݋< ]:-~ h@>/AM O?RVDׂI vΩ4X/7jx 8wؾW']0G9& H}~A_ [Y~g@+z'Yٹ`&\'>+Ԅ;0wde ?KާHYs8ޯ[XC}9а?Ǔ5m.Y^篎~| ؽ+--v0s <bR98UEKgހ&uq!}p>Pq&|!L~xqx. G/eP>}f9iyhv"_tԿWfPo% 㺴~ixoHD5̿Tafg92;@z|u6g˚p=1OR@KK;&;ed&\A];- cSYxh-,[Mt$B@! rv! B@! B@! B@fex9܊Ɉqƣk{tW1̵sefX~F0aT1 (| tYy w\K֣ЎbQMn*?$ #5I }߁fS1}c۳@##GPue/$:j!vJ k8 }:sتWot(O] 7ї;ߛIZe)jqgQ5jXu_sԟ^ 6Id;%_gJ?޻vES%cj', L J ժ6XjizC;'N v, X?6_ח0.dy`Zl_[ \u>hx-<8 ,_6%|2v/_?١ Z@ +a_I<{+tC>>ך9k[/FC77 ~T#!Sb249]=B8<6u\G:r&'A5Nk?ޯrmf{`^wwϮfwPz zb|ܦ=o6i8zΏ p\;cLu8 = ǧVŝ-e(939tm'Gи6_ן밝\ѩl$&ElR렃>L&wwL}݂$U{=9{ ؑ^7̩j{_[dp}|ÄS#΄Pv^ڷ v\v81iv/O4*;[vrҧj>sJK-cWe4 .oB7nPWcyN.ǃ}~$B@! ^WՐ#B@! B@! B@zέSp=r B@! B@ف:zB:-T!7 p'ז=a2lsvqeQߊj[xd5'! y\ӷG:'B@! B@! B@5ӜyHB@! xn['OD_&Ր~SBuEq*7h^w}! B+*B@! B@! B@dzB@! B@ 7_Tqe!5 #N, ݸ~ v P`H?I5A7]R2lvR_! o=ٹb@! B@! B@! B ' wܪ{\as^K! B@! B@! o%ٹVV(! B@! B@! B YW! B@! B@! x+ U.J! B@! B@! -fYX{'Nٸ+U^XvϘBC#PS')#MjjYjlW)͟)b9bF E:K655TD "?hu ~W)hTjԼ*'A}B/U( e/jaG 70gC#dn]r LR%?l>a=yrg3_y&J&^DUЍ8>L8R{O߻fA 3pG9s0z43' 띵j}:g \؋8ǀqu'9 Yg-KI*PI_A7_:teҼpޘ'I9׿2|HBo@W"v5C'eP e!jo,t[`eh.5)I@n}Q\/n+Y>ߪB@! B@! B@! @Nx+񌸜Ӈ]OH%}UG!EXڛEnyNB?H3"5w y3 6rMipCX !R(3O:7}Kz_Le!傲۾ 4yBR'݋uq{N?4#I 0ޖ`3w(,[Ifd>voV;dGedFG(woeFױշňa cQ%10K9/x Vn}:*bF|Fn2b[*5lr/?:sI*Ofdq+CO Wk>XlDS*;vX @K $[ϡ?K8Fqq ݡC;CY@r G&Bc9݈\M,ʠ!|~~#r՜G#=)WF֌/c3Gcj-X7&eJ{#R+eJ<یDC~ CժloRTV$;7wP33]ĠuIA/Q>ݫ}vBYC $ԫ'AWv+4:U.ۯcx[{]Hk[8yއz<[,Dž֥ȏjˈf\2I/a+-]|7k-0,jL #/=E+$[xbq)ܱbqǦ8ӥ7띠_g$}M,+n_ͥ&rv/hMwtysab<]F#r;'W"O>n^Crr6>5&|WcJM,JsPZMFcb7mh&q~Eby{5厌2(*+(u*X$6ÝeOvOaC/c֣BPUT){r-Z4_{;؆o˜2r3%NA:R.˓wdH/#t. 9?z4@IDAT78 =@* 9dۡI?qCJb m$׹@xN9'ϗ| Mt֨B5\OGT㺼f7-ݩ499Հ_Bkٓa[5q$t.x^']D+Bۯ>U2/תn'4$j]}_Xv[e3?>y8tIƊڒݭ"Xء|ޭzj̞~12ijqiz&tC~e[}ԓ@;n364IGqaZbHtчpg2ƅonr{ā9z9Ga9}y<r]ҚnQNCUIPگ8\uhxLZ$UBC=?utGh|?3UދU?')7񤐟.gD=~ٗJ F,Ln;v݅m,ZCA۹@> Ez.z]yS\lDk5VC/̡dڜk_s"zn-I4{Kˎ׆ n%-nfCs?եD@Ob;ve}4X&>|uvI}AX'ݑ9i*Oۚ `Vx5*U#^0ܹ  y<:hD_Ev*|F0zq ULbGcΆ-0yP~:tDTNy {Ϡ Zʷ ;{S-r#\7ٯ"JP`46ݝl?~<0=0C^c(CL?rtY?h{ju3\oC'q^[}(=G̜Z;z6Γs#7xaoYuf8݊ϑRmaOu 3#a\-<߰5$t/d} >sBAKJ{`C.Wݪ2297)HS}oȯ8ٚP?/\`XXY:3KI"B@^RB@! B@! B@! */AhlȌClx#=֔] }caZHݿ\^- "?M`~+@#߇j+_( #W70RhkFgD9wF߯'h+Ft;#I3u<64DwYu{0R Ƌ/L2$f )Jdy:cfN┉!15}2PCD,I΅s~H=-7 :#lwCAFZ>= IPʵ.n8ժD'z?mlN6TfUKk5Y~>n9.owW$lu9CpJ9*\:oQ ~7GB ^ːޕNI\ln'ćien5U@|Ԇ_%sOcE6D}[aW5o<,5%5_P5 5P< #΍_e+0RS{:s3) %B~ö@w[堹w:ܜ6;I<*{=h;*4gF<{Ynt"uFxBvs϶Zw8-eG+̊fJ';xM)gMf$9\ʶFvZ shW㏵+*Ƴ 졜E8D $5] ꂍQO-~NJ^SF8ttW2^I>百>[G@k?rრ|^Kdr-yixL8 VM*tpn8}i/V6V#U +!F_R&Ƚaկ%aW-Gw؆j#miF fkaugi1tQ3%D_¡1=,c؎z~J3K>rc[e~'s2Qkif6)q1`5u6<0hcs@1d[:edfbExt/Wd[~fTE=z6O᧌ϖ]gM!=cfͿ\<B/ۊB@! r/ ! B@! B@! B, w8 D4b@2b?YI;I|cfNJۿ߇E~-/F6:; # B`, 5U1$(w\+9'?ri.< :Qd}.}l;ڳh<_Iv߳pG+Ua7Uj;V Hͻ[?loSya{b6~>P9^P7+JePi8PvCvNX7[;ἥ>PQxzέJM~v8S8>{?_3d3lb?*ve{~a,_m":֛E6r#IZC5g[FcR翗K+ηqJlihrgi\>> Xy]>_&,纵Гg"ܹy}_%= %\"B,F,Nw9KP°ːeJS~>ׂ믃/n9ՂF 9WwВ_^;W̉/zsO ᚟4y,9^˺׽~ϕZ^Ӣv7'gG(phJhKSxo}[җpD5= y=}ߨsESƶ 7zwvQMxkbF+$\P]81qaux4lVhc ~~:Kw m/j9ɕ/qWO_&(^;?Ǘ2Xv݆R.| L 9c ݂֗q*(+ץ5s[5N$LԲ~}LsZąo*-+_ڊycҟrs|^70FfIݺےxzj-qv s Z-z 6BJE@D@D@2" " " " " " " " " " " "@H J; QKOon]vZƐR-GS]NfFEdS@r2"V#£Q޻0蘊Fd[%s Fr.\BS[1z54{Pƈ" XaFBb91_2ڍM8fTؑ?;Ko6N 574d+4kІ;29/3 \hђHC' ?iNԌPC߸r߯V+=,`^PRc2g֞QƷd5, ׭ op!s;?V؏Ӭ\xr}+a~P.iӨ!#7@sfB)\- y6_FoQǼ\HN%/FH/1}qw?t m|{_#wa$i㑿<,|γIY}C|\v.OBZׄg7f ܪ {g\_+{?ҡ ׋쮌A~-Rd%1Gaob=u($r)QA1\/;l_So< :.tʰ{~|};8q/%gW'oݏK·Ycs\ϟ |/ɿ<*s !n lFF9loeh,b7_ЮlPY-쀞5μ? {š] ?s\%?vŌy+ag.#q88<<f h쑝7[E; Ow'Gs.+k[84{s؁CO61Z/I| /\|dcZb#ᶒAw|utײݰ*ǟo8zn(alQqm u"7cgq>w[/G8hOM"~ FNΟ㰢H>vR-|Ls8'6ǧğc7^t2~.yj|;;+r gwٍp{=~QH [ Ӗ'Rfcxgkq-۱N+)yu s/2tD0hpxø^EQp}SW~ ;T'*.ؿ.sz_m?Ň!CnVcG{?ƄB\ЋoJ-:b |yMޭYQ AR"" " "p8ݭ :" " " " " " " " " " " " 0oЮj5>QD@D@D@D@D@D 7֥AstjU/ȚkCh؉ZtşPߒ Wqd]NՈdiڹo'" " " " " " " " " " "_sf;s['kE[UV=)jkKh6Nͽn/" " "pohʽ᮫ܧs>qj@p+.ͼkf8Qj kCkЍMA( /!r y+FBWVl>MkN刀<s偿@F0obj5fdKD@D@D@D@D@D@D@D@D@D@D@Hڹ@VuJD@D@D@D@D@D@D@D@D@D@D +EV<ǕS" " " " " " " " " " " E%[l궆A2&8 јxhL8d k:#M9;7 tY\P$|S{ꍏ}"no11_ lP׫;0vRP3[@'H:\ޔ~~Ď)b5N!;R=& (>ptqOuc& ڷY9k/s8r4U@zk3SMqf{>J9SV]Wh3жVBr9pg@:{ZV5~В7/'1}  sTraug{gr&#g+}I 84}\T iVL^]uohd+׿|/6Ōu!ĞYσt]olhTA4$akYPIKC uJfLU7׋sk5tivΙH ^]I7_]8گM6#:r[Cg? gخu8Cr3J@ݐIFwr #.o"# :H# Ny' N^9QHyQռ`/9}>F8Aw1_y(4h?R`*aM\W܅BQl;eK- CM<ꁰ{mS6cö|S9sa0&^_ )U{>jSr9/y#4gEkzwI\@{9]LM܊A|~sK]feU3kq[wsЏB]&%KiڿpV:mCIU([8WnF;3]gɖèuߡ!Uf\3+ՙViK^=KF~1*=UgsX ?xo^8?eee^~p?P2QE@D@D@*Gˬ_qa?\y:f4zE_Hu,n<\xb ӞDSCk63v+t-jO_nW2ox -Щ͑^⼗g͇Ε7KBs)dʅ6aD[흻a3.7%c3C-G~idal^X:uRYhH56G?帣ݷ򟿸 ;WJsڛ{6NY{I3#=6AOaݘpņ81n,]Ή+9>'3qjoOAMĝk&OO :.XA\.jdqZpF>?l7,>X8 9.צrh;hErrx蘂㬩M@G{`I)?։[Sݠ1?i4nV^9hĝゾ=jG8k܋F-mKݍ# yHhS_35jEЎA)2}kGX=ns-9r~^<\pIگqzǩhƐ971N|of_ <Zj?o&w ^|u'}eq~ӾSv|ٸUԂJD@D@D@ ~{ וD@D@D@D@D@D@D@D@D@D@D@D^p5#Qܵ? 2ߘğf{C4l -=2Rk׌x0d㎌]A#GܲPFx͂]xj5xNNd.p;ywF$2>4򻩎4埆K 8,fG<;$ Y?w-ExBg0F: k? MܹebJj@s; p-@c&pI|zYΡ Ui; hqúU;%wH91VfuajhۼJzϿ˨}KE\kp"zR_0txac j s I|ȼ>t&F|IܿL4 #]痸i؋r{kpsuwkL|.79Q%8o ch {){o9>zЋ\}_eXi|.[˪pwZyeJC cW?u+:}◰u']E^nM33Q0?aNh աo=ψ9'uvس7p@Wy|,7`R~;sV J9q/&4{b9\cļT#0k7q0^>[@|¿'51oJrܮ1l7Yy/Ć Љ@}k;cΫfyuw؃Bހyʷ v;;R?m\?wF• O>ؔrgSo-Q\Cg1pn1g4ΗL=Fn֏4O8 xo;jv8ʢO[OcxN^y2>@nq\YdԼ\X8Egh7, Ch@es7`OZ/ϻ'r//fON8}Azg>Т|2_\ gm.uJ!cds!!MHgsFWJ™/aqv">k("-?#Fw;,#Svv~F2ݔ1N1pa/3c\QgD:±Yrvty cx?6m\ycfB#Z3m{#]͏];CA8FZ%o>=lwA}K󅶙yl6N9s`ɯ򙙙cH V2zW;Jsf[V*O [FcGaGpX*z|bN1$Pklx޾5-wmsmorp&lNJ@ە8 3 ]ދ=}WN{4.yd+9Os\ǥuPE>- b†S,;q(p~N ] >Y[C3\5d;&v{[\53(TЦ~ɞNiш ~A{|>{Uh GE:wF98)h!X`$9\ɫrg?*GVZUbJ+ؿovsCr9?r FVHM/ s¿ P{seG_)ǕJCǠ_ ;yfϮ{W|ЪI[."|h½A/8PnW~wp ]Lwدoֳ#wR(Wۗm;+]RX߭(44E .wd Y&9in5P BbvRq}&Ώ/J5v G{Cg /og(qq*V>M~7t(ClEvOK6GS&!~Ih_Ag:c8>m$v{%ow fW)Öbw~Sj k6JWn3ㅫ%2͔*;m=JٯN;ʋd,]7cD@D@D@D@D@D@D@D@D@D@D@DܽxD%111@_'ԧ>#6V R&%۸/3v##/BkṈ^^gu ؁ BM < 0Bt?FI4\' a K~;]MOW8#z- vJ@] ]Uoǩ6 ikt]{̓Z:dr|x!8~<-_&cg͌9+מ5>(Ӛכ79+'hq,umN\wiHl?D-|5s;l= k#秡:En u=۩H#4We-׸42;KV1nmu+i?u]Z.g=K3 Mp8:ttՠٹ}rߊ/7kRmyf.ש}|w>Q{]'Yd@}Ah6WC_Mw C] ¸+ۧ/[/]z᝟qM,|nCS) S׏nAGK@in/MT%v庴~c+׉ԟZvگϑiT -TwE\yK[q0y,{]R@ww Ȍ>[7Scx[OS%NbnaQk8XEYvaF3YH@0~]@D@D@D@D@D@D@D@D@D@D@D@m[BSf 2r+t #G- OonU!gZݧ#~/@r21*"<~CcZfg oN,0O~H_h!:8AF_TQl߰ƌGY0r[p^*B"Cv8Z_}:}74,:\#tL_އ Û{p8go-y9Rȱ!p'$w~4]5s1HC&IF{B_|w3t'#عһ ?hVo{4;:?w0fĞloƷdˡ@H/A5`>IgF[Gǭ\xr}+a~Q1G`/ ~2#pa5}/%ݸ.\Xj+[s8snUΞQ1|Z6珷sa$IQ*fB'u( >ڮbgϡ< ֘7|42|0MV;7/0G{^dws96϶E.ա~5&(썿s֪yj\Ϩȷo$r)QA9?ʑ=gr7o-o=x^._u12^߅>Ҏ82p}uqW3s# izt m^btz|  ;9jFF9ljw7x盡 XGlfVճl[=k@:|Ϝf港QK~h|j^p_ 28\ߑ_u<_\P:t<m|缳% ~硱A#!/Om1v}s+k[8=n:40Wgop`h'ly>t'/$}ڱK%6n+ m4z+tVٝwv*U Y\a᳅r{q ;ǫq|E8VFg=u"v\Le#>@`d*ՂW}c^ᬄJ[gi5 U)2b[`0W,Y]#= ?55iIsކCq.nZycs?B^U̘琿v߲a`yZ(9 ߃߾a.aK##F} -DsGc{UC}G) yçBw;vDlo>mi?| 6~ofm5v7.uZӡ`LчwPs~>LVj17ǕݐhJs0 \ y7c@MŠ/6-U*" " "p8ݥ2" " " " " " " " " " " "@+D H@fi]4@V*I c$uDh- ]9uS8YR;\޼s%.jD@D@D@8\7HZpjZ#" " " " " @cqq/)Yk;S1Aڨƈ5ڹrPB" " " " " " " " " " " \y " " " " "JK3ZU&D 3:am|-0E0XN!oŨ@誳֊-ۧ)y)סvЀ k5 7*4W/h& 1EƯvm M1E k! ?ҶEw`^סK]ʸ=Pn} mCmk/s8r4U@-L8o*80S%؃Vʙ86YQw]OBۮZ mWq}ϓ+}ZnFF?m2z87kp*@q|i'G;"4Ҭp^uohd+׿|/6gVAzCs7t*P5o?qPv&]. -)I@?3n>" " " " " " " " " " " "pW Ε(F\L `{aOGO} q_ԉ۞Va#קo!?(n@~RQA *4O* zV ԓ؃sPhЙB==rYW~ :4(P4c"#QӂENz0Kf:)e 1[I>Ϙ;2¼7b%D_&r\Y| {Su/'D!@1r Ё?? ]뀓:2;?Ba~qץ3w\;n2~p7N780Chiތl3pkj`}: o8Dž?}C)_+ǭw[]!:30co=?MvLq\C='Ry۟(+ݠ 7\xG?:p5w=+qv  A78)Y<iOq١5;VQB_y7v}wTHkmq˳CF? xsK!ڤP%㐉cW:PZlXhJ ש!#}fB3t)wʏjwa֮Ny^6&ϐWKmޟI x{ Ƅ.6ā}cD wN\)}ҧ޸~~6N)h\+hTBVE,poY|xy" " " " " 7\Z/" " " " " " " " " " "p qXM 6oΘedx)5,jv`Dl- w }oC.g塉;;`nvC%-? 9\9Q6p2"YI撷!d`8nҎ%ߣA51ΕLw6˛H -}HM&~ 獭 F(}AzfSzzw}<9̼uxߟ;P@9PJgu1/vsW,h|SdΫ%ߗ\ШF~)wzgjuR3yHڱb Z8] MQ_AcgwgJ|p"\aܑɝ%:|DC:,&h y6Ac/v~1 I0曢"ϽUg,̰rWC-J}a,|R9/b`gXb~{M&ۗݷ{t2Ny3h6hKett*yX d+]G/uz=>EDE˿zHO yނ۷PCtFf s7u]8MIchFc?_ދ=}G~\:Ļd`TlH:5ЮFD;9/Gqs* A-/Juzy:!1cW͌$ MNQj\)/^" ֩+U-m zANKTi4i|Np'9\ɫrg?*GpWZonx@gs}NJs:8\tQ/{3oTD@D@D@D@R SȔ[D@D@D@D@D@D@D@D@D@D@D@Df.7r~on5v7;Gb#? e<6?0`WIjb6=-SmSz}o苏oLW~]\<`eDysOnfgt6PI]/ q9j*ngS4eĠ=7I)}i/w]+H震~BFwfmx#jyy&'֮l-s?o(w\ryϝb/3CC}v@5׮Gn{35a b"B`o^2SM}*6tU9Kv|!pHMz@|3lтsA+亵k/ׇfE.ڟF\˯[QWO:/l|r(_о>u[qJb5kߤ 0g9ȽgzqT4E qapgM0k*Љ^q;PT }mr6vVP 7lEQ힖z(ᔇdxo8>0VPy }.[|ӭכ}5XXt 8*" " " " " " " " " " " Y ̊N]#m`PO[);/XJEDlN+;X ?fd Xހ_"8L7Q B g;me@3NF' a ˄?\U@w+kszW/ߪKPikt]{̓Z:dr|x!8~<-_&cg͌<-];l{]'Yd@}Ah&a{h8(~*=;"KqYWWrV%1l#];-jwxHl1~ w{|R,'+??;/<j{Rx{pmf!Կ|.HMޝ]TXF|oHIF߅L U׏վNYC %4QK5AD@D@D@D@QTY" " " " " " " " " " " " IY`VFZθ~z+t2j9vr23R,z8{:q ۻ0蘊F[%8O~1ptsJ\q+Ot~j5B؎" XaƿIF?{T񭟡;uF81/.#.G< b+=g ^F@Ɉu}WG 黡aסlǸ!`r K/?Ԫ$xC Ή-9Ǒ1t.Q8h=/k5nn8jq_Wc={9:04))4Jήgkg|Kٹ^ʫJ/ბ_@}sz/uw.9.S4Ub1|&ΏV@Nr1#B~m0" #G.ALfKGpO>cauܹJW1^8b2z#4w]~z.1mz.lb'L\gʵ {A?np>6UA~>O*2TW>w:~C]9c^x?fKFdqhLQ9]q>¶o$r)QA16?|N2;(c^:.b?:[eؽG? MC>vE83+ޟ4%CClά1 9?w_地1\~?v1PK,52ϡg{s+Cev`gfZMTϲmauw}=hn5خ.6/f X ? 6_wy\#+@Wgm0c0^3\9FbA5G $AwaݐظZNJ#yC9zn(AۻT>=ڧ ͉7Rv1^# ڕ@%`u@V"`޶%EjJR[D@D@D@D@D@Dߌk.?SzA܄;6.w@&ΕLjE@D@D@D@D@D@D@D@D@D@D@Lf+Hؿ5Mqu#M u<s@ΕZE@D@D@D@D@cnKNQӣPz kCkЍMA( DD@D@D@D!&+WE@D@D@D@D@D@D@D@D@D@D@yۖۿUY" " " " " " " " " " " "Ε솫" " " " " " " " " " " #?x+ WwE@D@D@D@D@D@D@D@D@D@D@G%[l궆A2&8 јxhL8d k:#M9;7 tY\P$|S{ꍏ}"no11ѻQІ~%B=Xqv5|f2&>c<_a;"92@T>ptquc&ቤ9B^tD)i ݫ:vg =>T }r68_.|]ڮą7= 9F%ç3֝1#t9˥]~Ђ0 ~dpko^T"+hҀOp:;vPEh6Y1ްxWq^=l?JJ==Y= ͑ ҩԃBiHּfc@Yؙt4Pd&̤E@D@D@D@D@7 WD@D@D@D@D@D@D@D@D@D@D@;߹ňѺ!#y-p(Ap×[1Tzu>.~ MJ.-Ug./~wd+N8Rb9;!󰬏gnqj.F+.$} Pgj#?&_h7O :.XA\cκjm܁ }wTD@D@D@D@D@ڹDuAD@D@D@D@D@D@D@D@D@D@DvǛug#7gLcmPrK)5,jvh<zcla(c\W{u>[t.LlbruG6# ܉6k-~ޏB/% I˻`o4_C> $7\)l|ycn9em S߆z>-v0Ou~B]@i+o,ż,ϺU,j4"O/$n 󎓈y_Phl짽B'<힛5pl]ڂŒ eTj(<Ϳ'ұO8[<~Y@9)R! @o}4U;ArӆѤ@~e$0Y-ow<4(vcFN?~: @K`uyE?뺶 Sͅ(J.>a?q[WKA_MVq[Cgq$84hMhI{Zn(yPQN>X8P+]$.g`_)1Κ:K4~t3;G艌O=6Þ0ϻ!` esvvrX?c/@C:+(z 쒽x4?wx}yM8f>a]6D"qQ/2yk8f&.[HoMilG B^ S|wȘ6/bB:ד 9wW]Ƿ$&_qe4?Y=&F/_U=iRNKEI?MÞX;(驪ɅҤ ^|@"CI REUDPD&EE:J(!l<X)P3is7眙0F,amz=iR/8m/zH E}79>]%_a3SN])ya_LBUfw6Ccua0lqM5}` z;n羳9|[ˣp?5~;_p}sͫ@>5*h&+;o{?.Bd gKν6r0<[qh\Sf26ŭCrz65Ⱥebv/r]SLO:g#sKPKb*\&^?hnY8nע#`GIg S!lɼ Șӻ #PnakkF?y3=E a,=+Ȝz79ɬ]:6ag6*NOOjuCap3s=Y|~VU#v CnXN!g؊m>?/V-+r-v_2jr=V3~o>!DxNۖmf ES?!b`ʥp}7/a{t堶d0/6:-ya#twyZYHF1G vs?es* C#m}^. ͽ>^91FN^{fpUhZDqNB8xvF]"0Ϝo"Vs׻zWdtY>Ufz.EqPyehZ{,| L޵z`@*ňO Z{$ٽo.F=FX8iÞMWAWe{B" " " " "NE@D@D@D@D@D@D@D@D@D@D@D@'ry,g'p.A/5"G@#a|o5[z}_k6y&8#Ɲho5ptr'(Dϯv4e;Qvom/etu9C8sZMzbW*S:/ߣѵսfݻvmWz`ӟ:5˛힛ir ken>/+zHQZnP}}B&8GKM'>Ā_ 0/p4~l0S=h6oWIߤG#]ؽc}.WݡP'nPj3t=j84Ysߔ{o5sڵ춥/ڿXfdVVTUۈ%o\^n<%`W+ۈĈ[7EҺ~y/'Όzf,\3Ƶ$E/àՆN,K\ʥ(m^5!lA%>۩ E߷&!AjyO%nwlov|pSEp@mTD@D@D@D@D@D@D@D@D@D@D@61+(%Ayz"SpӯPߦԷ[\4-H=yCJ(7Sl,e@_߰<PGo7& ӭ8׍Un4[N9H /ъ+"hszP+7;_ڌ9 :{>#<΁;h1t@hEP/$#dVgɈlٹU0&*8zwtC㌏e ZAZ +7}hj*ǁ{?P~JZ˛YN q7&Fc )8Ϻ7y=-dq-rq[TmgD̯Gu%"Mw=Np۞7 #Kcpw6qE吟Kpf3/Uo gX}oE#}t|°߽deI؂MqǷ!S|cgD~CW`5 I{rL*a#jyC]>KoK]s>(>SLMu22\sr}J^m /4? 7@Nn|wX>ȷXwASvbfqj<պOY"F;A۵ " " " "  `'-HD@D@D@D@D@D@D@D@D@D@D@D 1+C܊?-= *U8r2S,nd͍:qۧ(%<hȲuЭ}sŭ ʛV[ bFrQ%8^$9>~^}toօ:ex߈,3 Fp=0{"SVÇSgaOOsHN?r*W:`~g^L_np5|b`ԯ~kSu]׿& `/My$56BBgrޅEWd>wEu}?-84>(|ץq㻢X#;I^zsa7?œkKA1ŇalSCW1߸8ZOxj/YC,w ~8IWW(g=e Nw t^Р'~pp<_l{M1V>[_2K㡉 ԘX>MAZ C`{xիJWCݝy_4/~W|RpŚ} ]ȍ8o`gZq|4gߟ=F;\wMl_ߝ\yhԫKP7?ѧ [@ n_ ?2'>ݤIk^xHs$>J)yǧ5pݵjlysM:to MqPr-t@׹'N$wDD@D@D@D@^Ɵ0w52}ע MXD@D@D@D@D@Dڊsw-YuoH}]'M>}_{Cue" " " " D@+V݊ܟD@D@D@D@D@D [v67}&RF@ܧrX]@PJpU" " " " " 01 +cQЩ¡+ЯZ~TH"WK:SiVA㬷V x(r\?W" " " " " " " " " " " vu" " " " " " " " " " " pns˥tXS@W5{.mAIC'C㣨廅԰iI҉(BWOtCV)I*^WecG>z~S9)dtq|ƔxvoC?JƵI1XC.rN S =sǨdI.}YI\ M?罡mGv]Pu$w=7N9dڡ5HƄF-:A&rK*,QcQאpf=*9|14oЁC+f*e#jWUtlPMrnҽU/#WFe<\yξ_9"?`nu,W}?-}e#˫/_G-ޏK: 4`/WW " " "pOE@D@D@D@D@D@D@D@D@D@D@D d=r%s{ӳss 1cD;QhalF1<'ǧ~:@(! mF]dfDKnftO87J.S :/=Q,+$5a͉sp ̉]lQus㷎C=w?N˷ x2=.v_8$0AґyM&ҳ2߸4>v (#ͲΗۣ29s>M"tP-5a~nxec<޷ _?ԧH}вONF&C9݈jH qv eΑΣ) @}ף` ;/h ܗ~:V@f/ g4ԜoC)S@IDAT>7kĤSJOz.Jo;;@M2lWWVi^G5PhF ĵV ~zT{Go4{4~[G=kt/cJ뫥q"Zӣ=闌C&%/AyFO墶燯.moQ1~D8m7Q@m95q+'H>Sr T.6Sogc S8xjW0 *5q+}X^$blQQ|N~o_=wޙ%]A^|)pGX$sFE/_= u_ֳHE@D@D@D@D }vj)" " " " " " " " " " "5d˶Ae$jk޲bJ!}74zfhнy!@aJ$dV5>ޣI0g yLMydz:z(NS[ʼnp1\F7h۠OUiR]O2r#>$? \_ \e Fd+?؝,'ч>G=>Hh7:rrn# 6zB/G02Y(\.mqaޕ+qjuT8|y8tyoB'nE/|.9ߔp}&.d'n Qoס<:z*hE^lO@2$I}ɡB 5$ōn}`p?<6.:mёܶ~sa[`mg|:I'.Y}94Еr. >g|5ǮOqyԧ6 r{qN$??fa#r;%R *}@i6G}67+~*9Oq;0>O"S'<@OCqc%" " " @ܾL" " " " " " " " " " " "p'܉VƱ ^q I2_$ՇItHcUozUZ_>/DPzL =Tzl?04?;*`.O ? -@ڝC뮨{/heF 8_3f]Kw9þDpK$ %tEjд-n97N8;SoزaGg:+$0L9 MGl%`N췧g0zN~EB/Y<14ycl d_:7Ze_ǔ0P¯gv_ =T8߽$cC G#Hzce7O.t]RV)p0?ϻf$+V9/1ztcrwrChPڽZWFk>S)z&׍-0IO\7=֞awEhaAЦ}e#Dv vyZ:O"4pxM;-փZo,=zkI -9!۪Sa2ʝk?֊O+tE?k=͟fUm1o25BCOpĵK8~c+=n|yl@~W̔Sopn6AUoāFiا`]ؽ}h7y-[\ylq}_.l[tu;ͩZGv$ 7N@>ͫaa O>QA3Q^!P>GJ {zкogwB 'j' 3W-4%q~_UCioCKʔ|~~ 7^9vF ? ijbW<އ+ͼ8/l>{{H]tVGvo!(S"" " "{{g9ãU=3Z`cMɰ1O Sk;Vn"떉ٽ vMU7 gCT*u29x~3}p\ߘN2< 5IJ=ԝ]PHLO-Q qkYz,5,`a pIϽ4wMX88L\s=nIN|q@V~6ױ;GWQu6Pu\j]'Gk^FzkO'BkUt5K\ \|`*z_^M6at>{o7!Y'iԿmf/Z_ztK|^6 +S.}Ŭ?pUy} {ܻ5_~`KfCj|F@KCO -2hjTxĈXɹ2r~9k6C>WFN/es.ܜ֓yqHbk/B ʳsύAd]hS-[ĊK>wwEF#?]nϨRWEWRw L޵z`@*UZ闡?ւ\%O6пHykC}HM+Gwu`ct1:)5bگsF8ttQWI9r" }ǺάY5zm:i!F>ʵ} vmsOUf%oS-m5bz~+~fXKQ`NX\nBBPJD@D@D@n_]n0u;5 >OHРЗ:%}ްۤ[z|_kk"7`fXwy8z~gN!TiV =K2~wn?oەgMGc|گf6ANh}c8>n5AFa_h>rL1aRi=xwdm!\tg{[IR(}k>F-UaF\l5 W>}sMˡ^1^vxNXuS>[HzV(l=8?A?3|ոS8e zO,"V~Fw>rkhcl'sr29hltzv*ymxo nyoGAvگcd*<SH2K懖;37[u{9)wՃ3ڷe?cpFZ>FLr9OA.%/:R(gQ}%p7瘕H1D9D!_Ma t%iZ*{"? Oҳu!Ê6Goʋ~kvA]]ݩݭqx;#`VTyG:i6 88 O qA;Bgg9{-q|x7{:1s/`o( z^X&o_M5S[=V{+i-ovd:ǹ$ҋ\Yk)?p[4 h(4X.b?s ?tDM-wS1oi~='^adiLrqЉ+[PvS~ȿT%? %|.0Cp:b$tݰбyoZ5pν$)#/c; >w=6@7j7tF>Z9 wd'o(/(8:7%_)Ͽ-D#r:(r0#ed) Ϊ:>%|I2:p5{iKJ Oh FZ4|Ӷ<T;42ۙ竉q^n /4? 7@Nn2wy ضIݹ\yb2-|+{y׼d|6^IPWzB90R?IJ,/\|)eg4zQPav?s]Z3QΦrvgoy5 澴x[O"63?쟟3\i\gS5AKbmK߸ߢlҰlc8V"" " "'|Hg@cVWCϭ^= t{hUt9q,dXNz0ͥTQq|tb ó F!֡VM| j1\b#9R E/&Y<c[`zw6Ӫȩ(xkOL$o Q~'OǏa)67ϼ#؃ k2/^B/ V7:ߒqv4'琿q ^42 &<z&.c+Oi5%:؞\|^4؞.܋>\D䧿s1.=B lmP;.BO^Y}>赬 0<;s{@ocj0EZ?K!kC'>n.-fŬR=tLU7?ΣKeCB9 Ec7v'?o9S,W64 \2Xd'y xz_2Ko&&Pcbxh.2^ g^UWz ~I?p^|W"І~׾.Yڧp|ƅܸsx\+?@7 ghXndE^8;^rxD&Nl-l}%vȜp<ӡ6)pzhH&Hs$>pTJyB9&I>kd:՜ ׄm΢k!y_m "{^{ƗvW4(ݏ7tMRP~˹M-Oag?a@x==9}M:dLshɋw i116l?9A'fxN {*4CW/Wa{p^;=cP$NEu@ɵ]ySD'G~ۉęx&soC{!7gkȷ̙>{ĘSб}:י9&B9gEaa؋̂2"ѧz@yG^81/ a&tD`7ō+bDHM8wyK"|,2acf}χmTߙq]?< 6~.ND:۾kW5>:7Tܝɵ8 u4Ӛ9t9u߇2 #ir$FD@D@D!ȕ{Fi" " " " " " " " " " " w4q4 @0ɵ E wv~RK~ Fgr/" " " " " " " " " " "pOP=u4XA`fq6C%.!j (K@~x ekƇ@o$$mBP}O@+-$Sn9ٯ"W۪-ϕ"~E@D@D@D@D@D@D@D@D@D@D@KϕD@D@D@D@D@D@D@D@D@D@D@rKv;\M5 =1tU2Ϝ1h|24>Z[(A @$XNM-tUP$jK7d5uU^=vDKCvpdHY<24Aoh,%%#SG~BFlg7|]d_4|0xPO[gE9K蠊UA$CM!CˎIī'ݙ(VP*8"SgS 6oaL)W~ь8o#M5a5Z3pfС'*@K.[YfÐ?}r188wVHpNDcGzvЦB7*r], И'P"ݎPD) 1ܕoB7>dڡ5HƄv{ ]%~|(cK]I™(zsbaWh'uV̠TF+h{7_WK$7 &]-" " " " "pȭA8h" " " " " " " " " " " ")Y\ܞ':QhalF1#"{uSQta|/ Gw-G=oHmo9z\@Uu3ᅨ7lg輰ǡ^'^um:vBuE~n~Ô%KBaN'bcB Jz?N㼴 xʜAQmn/ 漶 75񨌣3C1[KnP:,\rFhK_=gp=z'fpG6>dIȑnsy2ϭ >KL["Pb^oυ1o9џCkk -G{@ uXA~P"W9_˦n9xhkb04Ӎ[t>Ͽv \=utLY\:EP?W뗿sO n~P4˅/?(3hy+c>ڎ'Pq:~!7t`~迧0BAsI݉?-f@ d0_py9~1H/"iB'1z?CL:)6>CYzOݜ2b2_Ѯrס^V}}1ۋ(oba$ *Ʒ44M ՟b8J Y}ey~Dˊ%a?ߧt=BLK[a~m`hM/Hj+8|#FA z"(k}þS!zMY8MLrdfq.oΣ&rk[ڄH>S-qF.*r_Ik<ƙ?sxϷ+TjV+| Y^5" {߽3S#(r>G ne`tmښ3MS,u=\o34NrϣGJ$**v^o{4 FUX#lDOŝgpu2'ڻ^NN ?AFJfܑ(9# qVv $3τG1\ ]RɉFʞxC}Y_&s g4h>'y<ᣍxI_@~v~P?B?=TڪM:_ sA+S4)m"w:7Xr=k uٟ4f/nXOX#I؛-M8)Pbq jsٌ>&'bD$~n3]0=@Mn+cqwZu@9u 庿Ѵ(Чa/^p7lD6: K-8>?soݙD60-!xnd0;T8c$~<"n |{huk JA;&Z=O c1xy8dp~Li1-ʽW+O9}D(썡5۵)O =Ou\~r)/.[I-S@<8=w~(xo ~a+c[~^۠f㽪5`2q #{A7l 51p7pMٽ)%e'!g9'K 䢼 7N ZϳЄn {̔nJ[ǝ3=ot]m^_^cןez(l(4t[`oݘp?mwWb@&/LTTν6"z4Gm zB6|+QM-4=??t?dL^ cYWz}?~(:_7=#]f== Vy|,E׻U~/|g:urzU[CrLO)pddfj(?[Gzޞjz *mt(CbwlvFq|$?4lKy6GC|>1ܮp|G3w'G7 y[ƯOmTED@D@D@D@D@D@D@D@D@D@D@Xv*-G2a)nb4쟖ӳNv,E-{Aӯ횪fz?[\Z*Z*Czm7|SSrVWFN/eP91FN^6N(5sύAd]hS-[ĊK>)wEF#?]~RWEWRw L޵z`@*ňO Z{$ٽo.F kĊsF8ttQ/WJ[/T*" " " " _*[D@D@D@D@D@D@D@D@D@D@D@Dz.כwry'!R[#r$>HMP)mum󜇙ab3b`܉aCBXU@G>P+i9GqM? # okϨg-o@FŦ]zKR.(Dϵv(T8|4ZMUܭ]Aj6)=/YW*S:/>] Znݽkv6SQy(+78bn2#Ŏ_Gi7K,Ȍϳ&)Q{p4{ВkcQ/B_٨㿡_'uug̶uzWz zW\Ӊl'pPá١D8Ck羵k#ږhcoDAW9_&Put/ZY>gktbĊ"Q޺HG |D{)qfd3gacO<_A{ 6Yh+?X9K_3PG &g#2 w!ǟ 9O|[2,Qé E߷_D~_[}QйTYg?)vf7.{jh" " " "  F9f%3!zyz"S_Mo t%iZ*VXI? gSl,e@_(߰<Pћɔjk GŝvyvTM8߸=rrMm2Eۂ^S"Vq>7yήWArSMO ϠM緀z:bn drz([d|w2Z:cDJ*oFsg=; |ߋСqPZ ȴ/pp[h@Gyj=}z3WpϺhD5h![t(:ø HF|8ZV-j۟޼^x:U;w=Op۞Шx1y^r=>&ྵ(v 3lF-l3Kb-0Cp:b$tݰбyoZup/Yvҧ?B/h ~)6G|kj/5#lӜpNޓ7|`_ Q꒯_x\"X'yG%C3]FkF@i4μ8>%|I2罴~Zvr?҂A+k:瓧myg;hDwh@e#N]dz Ge7jRkj^9pA_Q|2|qۢ(_>hB'? w-oa-[ovw G…x0uD|w6ko})wN/" " " " >*@cV5=;Z\= t{hUt9q,dXu2.:*OQЉ5 ;X7Ky Аe[[75p8J'Jp0Imo(OutL@h= rlȷ)䏜oGD\m~& ;.tY^MxNcA.9tPccpCGo3=A돜VAS?̫[Yރ c _-/ FygkS~nB{CIGd|qmB9tTF#oC_yIhxo' q.>6/@LX\:=`{^%`̟ec9vDDJm=nq'SP:/%\gӻ}>tA8^˺_P^:,56 ++s+p&]_qNH~xWQ F~}v5?`|W[`d'ًVo~:#:,CxvPbFPzq?=b\G-'<|w ~8I,+d Nw t^Р'\̃~EvZo熷د/~@s*D]$e /?$ j1^UWz ~ ~W|a6s2ƺ@njgf8j_ar:p.yb/Y:3О|l<jpݍnj7}wrynQb/B݌D&Nl-l}%vȜp<|.7;v'箁nj2L1+|'BJ)[()U{Pϩd&|_,BV&_cT)J:XvۉבԼޕKF&" " " " " " " " " " " wZTAi\9Q[qTcFΪ}t[H}lA/ID@D@D@D@'\ED@D@D@D@D@D@D@D@D@D@D s4:;v67}&Rƽs4R}\}:}D@+ԥ ц9K,xpp PhWGAGX?$AL@+ץdy)4۠q[<`p]c+Sks\dh:qg)e綠|椏G㓡QBajX4$RvthoZ'Q[!ɔ$h]2#ZB nC/ DObQSZx}s28>ycJʞO|MF(?GۛR1KF@~G;%B[ڨ0Ԋ 88@1t*8"SS oSgA'qDj6kf>34-͠COT-neE CenHoLRb~CqcxA z?m9juI-2G6_qv7I~~/wХQhLzMƦ,$ǐV to~iրz ߋ۹ZtLvTYأ!zTxsbaWh'uV̠TF+h{7_WK$7 &]-" " " " "p{mVn<s{ӳssExC"J|ZmۿQ̈mHbDT{:@(! mF]dfDKnftOr[S@. MԌ>`48#T.nAtLiNCȲ><h{ ǔpx! 8+$tk亱 - etCPMI? :>aV/ڙIX;ü82]K*sEbK黑,bƣ2zΜmSl-ɜ&F{maK_ئ@IDAT6d[?og}hY-8}Šޞ -cXί=`ZZCi=%,u#a3/Sr`pGe BiVXT51ՏF\ĭ oO݋@:G:ڎn,."+_O n~P4˅/?(3hy+c>VO<*uE7Ǚse"# ߏ{ #|14ԭCARw[D@D@D@D@D@D@D@D@D@D@D@r@'ߟ'ch< /B *2^6.f]={!&WzCwtQzp~}M9yoHT1%qC1 m Z7^O+]4#s==u5p1cQPk5JAS^$֩Cy +:%pTe ukD}~QzOk]KRj(s$v :Q+,WpFYA$7je(>S07=S'_7e1/n3b9'Wfp/stys5tn-Жs^r2>!+#'.UMk<ƙ?sx6G`P[io;{ՒEE1+A9|{gF&" " " " "pP}pu " " " " " " " " " " " 5,"gt/u;enzˊ)Qv?1n`zCw;@=޲”H*v^o{4 FU X'qGS)dL?AoO0AqzIW?/󂜌hI;9Ogq"st'8իB5 ߬W>A;L)G? k}/N A;?mn&p}/[׹ ڕ)xaLĉ;AŊ#Y_c,N_pԏnDJu#I`g)8x 4=Ҕ)V* p}{"ggʉHd(-舦EΉFѧa/^pcCG!u)FJ'~eF,pKH;&*rQ$~<"/5BV~f˰{oqCirpCA\Tr8nl#z2I8 ?کFZL5r0Sq-h. {ch vmJkG@CϓC\J/V6x)q qV?[c&~1wйkOAcC|+=u FxW]oN};dG{whS|3qL,C IE pe"$%%ĕnn*C$T2L{z>>v8g>kzao};+t$h߾0J5hKvfeŪ\"h'%UD{/f3?9's\ОO9x卓^'w*lP Τv*7F%B`皟<ĮE+hWlboZc#+Ӽ3vo˧ޥ]o~L.@m_3v@E_@@@C      |a#8FԦ̴mNo-g|!6b/hcџLKc#~^\})E6F622&mڏM/֫ z# 8WYrG AotN+mbيvQu/)kz)i~k]}O}A6vuYF OU97sw[lX_ϛ1GKҼMlr09/v/adKFX)}&*.>mbsFnMjK7ŔSl>-棛[m6{0U19?ptg"*`7uϵ3hx]'U _:G{MS\~݊M~눇T>Xq=S;Ǩ=IWg4*O69}'fϋOq#2~ Sr8ee@g#޿M=_˧xiհV^Vu{Ūuw(H2wYY|ϝ,)[y{f9RNїPЁzՖF?Mm\3O囟ϦNPη\8}_=k'cݗy{=b lxKwS|mIOε },TvC1s׫\5T)$A-.-<<>e=w۪~ЈHLk^Cvٌ}ڱ>'#9YgUX+u;gΞO)igM[2I[GU82T~=5cڙjWoo_.S|vcB{,h.-OҞ/$& ϶= Ye l|W{>CxwQA0A@@֡       Pl†WFoD%cld;?Χ\U9xNUvmtO,6m~IV@*٠/#F6".B$m#ccݘ={~{gռNF߯ʩ%lfg{ qӪb@!]?qU^<0~WtT*>?,lDlϖyjݟt ;UUd#lh#۔sױAl[MNtǗeT߹&7f)hZ2~KvNx|uc(κs5&+^][cʷVo\ ))=[>7@z4s dLäm;rhî˯a"l73՞7{Vݰ~FXW-[mdv19x7c/cXp47v<&V#\y:zO:Jq&%78|ӊ5EQs[Gj=WhWa$\mVe_LZ޳Xa*V>gUasbGľ92hB־~c*fz;ŋ&=U%!>oSz0gIzs;z*keSs3l5j>ku/lϗԢ?*Eqgi_ϿRn/kQQ0)|;! h]ƊV}R|L&؝X݁@@ `Rj@@@@@@qyG|v޽lŁ oH_\/VYȧlA}_a[FΫI`h4n[[>עElVbm^ոU$'Yb+n~n"_FWhmn6R'PE kܿKvHMK-Sl{)]vǢ]BYRؕ_=lSd#jxO.6' ɪy]8ϻ9)z(m|pm]6dbX}vcVob#UvBsGPRvCnWW+1ɇjvIR5o~(sG pxb~:nߥ h>})Wk+|wbZ2ֲb]+Q- Ulzj[9_{'Ȗ^Ԯ{>&VW{F뛨L%RU/O *6TuћZfՅw?_AvϚ[T~Z*Npb%RMm~y Ƕ/_Χ0kak1a>}OHuA|vk+Ǫ,Rܟm'qs?fg/R E@B0α*      p Y9l-Ml$bhz0M6Vj{@}}Ѣ28*ed~~xvoXu WϙQ=K4I]bBs e&sq o ;5E~wkܯ8v㵊 46e. (o榹)àEbҲINS6bCKb~8J&Ci2[>o#Oի՞IƚD^xivd=%ioiu{v=Iw-RyM|v׻v]n賌>ޮr+:h&?j?_oN4YJ1|Q幋UMYJ,a;>Qv,Y%6T}>i{ܧe>`6ؤl WMvso폫 w=HT7ަ8=GGLq-ګ^@UUV,uKIm_ƞW'5?svg{) PHR+[SK/W߳]%wdf-Oą=*XET_2%@c3sb}\ld'6)Y4#ݞC-o@v)ӣVfofn_.x__x-yޯk}P*\*7d>jFn?{-w= n-վ}4 FGҫo̱E=VsٿU?V+fA_뫨Q=Tvu*dszŊ84þ`Imn4ˠxTq}m޳;%Z|+c>'8NO?ۖ7yO˿O˴˩c+[sWxFSߚ['1EYj0oy֞Ywϡo_#+&u.]CzTD bgt* 8W11Wy6v]^Uq w IAsU`Al5NJ7MO~r)k7B%Kى>}7}P'u̽7mWLg%_];)V}e~I1).bw+^Pn˓_UcRY呷YY="-X_uY_]̙G^`;QUN?Tnٞ5Pd`]YT:ܛב=u䯭3A@@ sv!     G4y펦~@@@ ׾5XT}윤#קòg{H-}Xl   $@ʱt69@@@@@8n   Pt2ۻv6gׂԍs)  ^̕Co@@@@@@ s:   @BМ g{GVv˞[qœyr˪+I10A@(dϡ#     ?/jkwk      P\)f'E@@@@@q%:?F@@@@@b&+s      @\AWl}_mM(PO7{Z(f cUԦb'?=!Ŝ*|b$Tul'iPJG x|x2~T1s1ְIѪ߲b׫_|OR,RW♷5ۿ}tka>rpT]m+*Ir~׺8c^aWRIWVw5 z{ nTsWvݤTyc+Y֯;٭/uP5Ci~5u4-ݟO>T1-s]O/޷jrUY2WkSO?*~W{6W,8wB{2u2'ޞ/RNCj)OV,SRlQ&o_:lϻ4V=dC)?l@@@Z/      p$>seF`QΗhTOjQmȶHOP#&/W ~.[FKޣ"@s'cTŖ!0uYY6vEmm$o 5s[P陪U̩>=Jn2W<)Nzb53n2w>r ;!jde]9Rk<{_-Ř*F2_ᗑj 2N?hE<[ })v]S\K>xE 0o}*߿8o]CثVGb`/YZE'+RcP+EJZFݩ8ENvT?oU9vp?$caYٮשV&ɕU3: -ukxtbnk;Df}\1pHw](O[͌VNq(֎sgj+?VUj??~b[*)}g}*SvmzU'1lyejWD 5잻b'lt#'|<_hqoaZ}ŬUo}O v|f}>g^7Y֦i/wZ՗=vj_}>{~5{_s3Vԩ=Z>\Y[Ze`뷐ȝʤg   p r D@@@@@@uWI7{/m84{w[Z5s_yU,i`,OY64~&%.HQ#}*23|rR[{Go]][1ngYnpoYUUlĩ>+oPwXYC?iiG^J!% š6gLˬ8.ǽܮ;L~NxCٳּ7vUq_{*&*^m=-3g??jk{֍̮:^}o|@Nikmo~cZ>m/|7%Z֊u Xdm5[ v:jr.Rֵ| ZF}t ?V}|;T)9*c*ghio|~/]KͰh}@2:$,.3XU]|O 1u u4OTҊjӻs;T}9ykkC\*okxv9s7U.nfmZqx}ܜZGMP|g+>Vw6W \QTΉ[s۝QE˻xO&-oŧ+J deVy>O*Oaw/]/o^ה~> ݳj8{gׇ%nW)N~gŝ;2оNd>*=SoVbgŲwjF)w-}rUw`_T z;`}njnqA{>T67Njzݦ'wP̠yLj}oPlTR!4 ~C]Rv&fq=f(>raB=;Sh|]] *DV5cG*^Q; TTu0L@@84       w ʍha#jSfH6׾7tӺF~T@UOϱkof/5I.LZޢJw{#YʝwTl5bUk?ozAU55 ;J&Lill5mD7*\ Pg;mg)'׺Vm_Zq Q:F\wXo#q}vaadԯKFX)}&*.>mbHT<>>a_f{M[n|6>9~ { mDn*ɪaAtuDz=/^ekfNNo⣽).?n&5:!Ŋw3E}j~ߓtU6ɮ<^t}>/~>ϲǕ_1Mwʙex6|xiU{Ω$tˊnXI?+lO[$FV޾od9R}Xg˓*v:P}U ukFyz|t j+R=kIX^^iBkEwRgE;;{;|st 1ݐ*iy@ܻf*Ww Ua_e6cߠv,|@ſoI|lox zvSjYGӖLR֑s'z[ܯ¼fL;SeU:2ŧ.Pw:v,΢RQ/yR^ZOb=(O}l{~ݳپϝu]rF>sGUCxwQA0A@@֡       Pl†WFoD%cld;?Χ\U9xNUvmtO,m$]ko*UQ߆^5M7SU-KJA6p6M976NOr6%[p#N/UsyVF߯ʩ%lfg{ qӪbFT{<`e~Xc]Q:<D{lm1&'~$;ޤ, ]T?}'փ)z{M/ȗUx_żUIL֊Cg.PŔ銍X?(FOxFrǼi#&>6EU1(Q}|iMqGWmصB[x7AfϪjPz9>/>͘.X:?͍f|iŚOߢ #2l{^[f+̍/J-YmAs+P[3*^削sbGľ^Ɗz(&ѯآxঊ>csI{GIH]7⃡?KjCݟ;z*keSs3l5;Z_R/E_[T*)_bT!˷e8Jm5۷e}_lr݉E(@@/ F@@@@@+xJgh{V]̑bez|:6e[ Q2G*w$v/jq6ƓJW_~$5J5ڮ-kbUAݴ-x4D1:nߥ h.vК'ؼ=/{A]+Q- Ulz7mm|u [zS*X]SOlzX met/O *AEykUBXSܒ*Rqℋ+9? ohPh{uc>}v>{PY;}t[ )oŭ[{BR{'__[1>Ve(m;1>X,  @߆QtU@@@@@@M ʱLeoob#CԃiYUHЯ? T-j;I_`*2_Jiwg5]r~UZN14+'V^X@^ꀗ|y^#OR F*4Z=GXۭ%?~űU|^(',82j#βlsˀzq o m榹)ΠEbҲINS6bC_wiՉ-LXi{NKTW?2S (zb) l[/(v߆Zn5k#_F/>ݮ翶Ԡ,qj*7YJ1|Q幋Unzr".`wJb O~isr?0ke,UE{\bC+[zg=~JZ~kΊMʆ0d}Oqu!giGUv5NQ9?1S{=ݫيe|UU߶o/c'5?svg{)==T}pa-T<2G,~W]`1Edf-O=*XET_2 ]냍1v6m ֥v/"lyҸ} imnd9OkwRy;R_LL;\\>O;i_=B abɢ1VėTc3{yq k߰uJV9t:T: &埶XT{~l=߭x5VX@@D ҔE      ka#6o#GرŅ}fِ׍PsmDWFdNNSو*'wWqtsoĝJdr++H\IO&ŀ7oWƪ_4vgҒ}{gȘl]8ZkbRߡe lI3Kԝ;{+JnU'>u)k$oLnϮ_^>6R33(/&e.^JE{mi%DmdV&qU[+NƝ%2S9sqhZfClDfsIvF?ZݪXkTSF6:HOlRvifG-o@v]OYkU~mտ:oʫU,[F}j_{aR2lPmhu*x޻7LNl$ǫ_N^U޹ 'cUn~Ɋ%|iFn?{-辘r-վ}4 }Q{Ɬv̱{~X+b7jŌ?(2WQ*R 2ٹDk=bGvYa߇\C}xju=oj\x_k9?,w*v 'S϶Mo/߀$J2r󖼧8\{<x˦OQ[sw+V<E {~5}=2-eol#+&u.]CzTD bgt* 8W11Wy6v]^Uq kxj}*4wM>zX^ vVwT'(nqbv#X=\oO\Xs(wۇ]>(\|]{xV*5bEoWT3iR)v|(ZQ HVB@@ s=     2W+[E@@",pz?"|]j쩻'̙ߞ.*"@@b,@J1>:     v6k      L̕bv9\@@@@@NWcm@@@@@(fRN8      Jt~     LW p@@@@@@ :~\Ώ@@@@@@ J1;.     D'+6     3~\)f'E@@@@@q%:?F@@@@@b&+s      ?D      Pqp@@@@@ǕX@@@@@?"     @tk#     @1Ǖbv9\@@@@@NWcm@@@@@(fRN8      Jt~     LW p@@@@@@ :~\Ώ@@@@@@ J1;.     D'+6     3~\)f'E@@@@@q%:?F@@@@@b&+s      ?D      Pqp@@@@@ǕX@@@@@?"     @tk#     @1Ǖbv9\@@@@@NWcm@@@@@(fRN8      Ĺ?n      A̕0T#     n@@@@@@ ߄@@@@@@ @+[( Z"Uvv c/     \l@@@@@@ @AumbNN[oTKxʖ9N_.bL<7nR\fbzzb 90A@@@@8rd9{     EP} XvjO^CdR~e$1cV]/v1_V13cbwwl7_ M)&Wp0(&     GL>b@@@@@@͛̕682w_ox.C! ]F.w÷ivN J{x{JIDAT-Qbrn>n//ky֫T"     p\9R@@@@@@\xl٬'YJ ]x+\= 8H{qyq1ޕ#mZZje/sm/mGsX}@@@@@8Rd)y     ER _JffTy(RCFAr5+` 9}לX8m-eׇΣߝ)[?R]?Y~E#@@@@@.`vrv     EY _tp (Cy ^&͹\9'ڇO9:Heo]MYx+*ID{wJv6zrˉ     5Z/      pXeK&^ vvVvMZݑ)NzASuة땊%)PI3]Cf+-')tɾ {w>7hwVm%|ty{=     m?E(=      pgڵXp'}>!q)6s`6b9"[R}݊[-W*NK +^[7Qׯ]}<=)ފ>5ewy1wDys.%>]O@@@@@.@JQ?@@@@@@̕`Lb~FZ^^ vW}'S5/VoVJO*y7<8Š1޻P?r>W,ʼn-#u0mžIf>]qHeWo1գʝ;Nlxe;XR17shW| {MVq*iqbUlZζemZ tO>Qism޹otdo3.$;c\:oڍ/d˙"     P/E@@@@@8"2W/pr .iyx+.R*u;ZFKNg\{Ro-^,_YŦgڏ>s4s~SGޗ`,}KJ1ړ-#dۜ/9Z(~ej|+LT9tqWegorlS*Xygi]qBd/~<Us8oFFf5C@@@@@H RNE@@@@@#-?s%;W,%!*ܰCܭ{-sl˜ d[5_j/.Qtqe3[+>P-'2{*]2JU Mʜt~bbhdhg( ~;^PQ}[kT&rGNy)+)w(ylT|K{=-2Qι2dV+^~w[TLh)y&AX{>m+{]Oy6N@@@@@"'@J;et@@@@@@̕`(2.e侚%oFC\o?kU*ʉ mWML岜voJ⧿*8O3uqtc;6mz++2rOwNcc՘      P\)@@@@@@)/s%e> 2"\}nKXJC޻s1Q {wIZT2Tj|-XFI/c"C\-/!_J5Ҿ͊#YwBve+}ʳ~YLlxI7vz]@@@@@(jd3F@@@@@@ \ x4q2\.;Pkoˀp2b7HW9jnUw;@(sõ2\;vZQy敊~bE-ݣۏs [vzr_ Jގz.We0Oz4s     e2W٣      p۷}9E;A2Uve2Z\m3 ,y6'!oejC3n{+⌻Ph,cȗS\V\[N;112_۵Oke-`ڵ"ŲǕX      sp?@@@@@(8;;;3\Ɖ]v,",CUwLog*}?V\&b-"Be%oK p lkDD@@@@@#+@ʑg      PewbnₗRn\A؂r_摻ʁw+(.ZU1XVyP&W*ewBÖQx;W m򶦄     @ s;z     G@ _lp rnʊV\B$o|7yH>m؈ޅҝWWޙ2[]~B1nD@@@@@+E}@@@@@82WՃm۶)*YzR\ukýcezK&͗zd 6[wc*J'D1l:6!')pE B(,a,j]xː~ƥ&\2ek ';baLK:E3400'[܀! Ă$0V=@ֳ<l` 0'p\&F/x {0  !tDE qF<$ F$#bd)A!9A."]C Q 6jNDQo4Eh.Z.EWehCϠћh&10=s|H,KÄ+ŪX#|ױ.q:- p6/Wo@#h,`B"!0PD(%&#;H$2&D'7Ĺĕĭă&b'8H"4H$wR$E#6NzId]-9L ɥSkg!9%9#9WH9r Q)&wJ,%RF9@9Gy@y+///"?Y'HLnOT9՗:*VSwoi41͋Lˣ>*8 *)RS4RVXXxDbKiRqJteH{/*?W!pTT9Ct_:~ޫJT5Q VT-Qݯڮ:f6[BZc3ٌՌÌ[y[1k>WRTYᯑV^&i9Ysf9Ƴ?<e5WkV֠v@{Y~NS:}t]]Ӻ/jLof6 k7я/?Ѐblf`P0pa=#9#g MFFM?7Q7 6)05y`J3452aF4s62ja;gW_@--x[-:'&LOpےjmoYkmŰ *z5pbĵ['~vζe}F&ĦЦ捭-۶.n]k{ {}C2fNB}NN)N[n;:G9tBpqYr哫ka׿,ݲ=d2;iפw}w.Gv.O=Ogc//ngfޙ_X}|u? H tD Zt;X;\<2?%Z8CI9Wg[f̜=S`!(tn w4QC*<괉Mſ=+?Ίudl9sVyVP\|.{nE9ӃM3gzg4?xFs.8ջ '.^<~ReummǮ8\9^wjCKGcSiTXtXML:com.adobe.xmp 1 1 2 ؀@IDATxw`Uޛ ^C HGOP ʲuu6]]]NBo! 37ᒄB{ڜ3sfbumwӐ?! B@"}lvF)yf(iVqKӅ#a*-E%DfZFDGv#B@lfX`BC@}T"֜M< Hς\2JiB@6zPZ"B@!  =Sj€%+3 T TV :} 4=] VVKB('E.?\rRN! l>Z5[ |deZ˦YqƟ<.ǯJ!Pa x3" {HDžB@\9>|BLFr{󓷔 OMJ! @Ix+B@x%`}DQ4- `M{ vͷU|I~9~Ul_m|Cl ٵ:_ TF  x>x! W'l.5. ]Vuq>[X =v`(>n8+Zֿ!v?IW~ݗڳf946}x@͑.q{#5=G.@\:.#"@! (M>3V`\P>vx仡[ShU+H; ;&j t'ӡ*w*q<%/_(?D$7sﭗzkA5,aP_ӼՈlCm}5L=?bkj肅1/(h ~=w&КL7S=[W!~Qy~UįMC+^NtXõr#%[a9)Кh\ފ/A! WF*6u>&삽fW"/F4z4{+@w^>ς(|[DY$@F`ahcm`[acm~p7xX.&+>mJU~3y;JuN ! B ]ٴ^P>Qą8/*P_4JͮsHhxsmƯ_p5kהgثFflMtRs&sk&Ψ~ݽ(?q(ۼ^U #G3w8Req&ypHd52f{02v^ {`կcɿ Æ%n_6Ȥm u%r_с߻=oe?|6@_)t~ ƇMc?hm.F7RNni9QgFgP/M /yu-4m|T $>]qkr_{Ա-褏DdY KҎк??U;x>OBט61C~ďY:i7/a]u_[g o'x⟈ gğO,=m?nC0Cv  Oy˧_^pNҤ/GڭgG@zs5<ϳ 6v> lFƣ}}__ Ѿmv6t76lAC;2{{='oW#w9ֿiN_ߐ^!/1zd_!o~Yħ?ʑ巇9Ep$ >f5 ⌥k]u#nSKaGsbba3CH~PIU~@3\@0.nӻtOA'Kz},%d^؅zۚ mۅ3maswԴ쎜iޡx0 ٴ/a'g Oiֳ3{OP耈6 ݴEea6<+ڴ +6'.+hι/8\ :0?t]Yhtϴ9JY;)R3o|景_.o o׋Uَѫ8g;ѩ)ϟƕ8[L'!AV|?M_ qa>eG}A[Ez;Ϙ@ ,4f0EVv`:{`>߹=EZ ?yB86d4wފ7h?;m/yb3E2ye8gg<'Ȟ)H9w^]xN`j -6-MnqhfHc5qO~Owx۰x\N\B< ( YB@!PT#/UsM}z,>2^, r}L UU{ Aա/U"lOF^й_f&sF;Mfv;g| S8g3aV4X9<عwE}Ụُ:`4OǙ7i=|9&)>ƕz (Zi]RgmYMpq$ٯ5lW.:v!a8ه ,g,d?fnGpԿ[Co]ˮs `P<PyLA>BT t .voPXƙG.}FG|`:dǶ g*Y~^KyV3zCy!5Gefg/r[z,| ^^qYϫ<5a B_Dˇ36`G=b8Ϗ&(yg}!窬T ;\@uڏstxoQ9sy̚!X̶?=WYdu9قZ ]/>w87J{4}l7{/Q~Q>Fu;>=$Zԛo}2|\ؼa;gv*//~5]0x}4}o!o x _r ! Bt0\.ε#2kV.*9gV2u7]n9+[;LE̚t+P򜮣K^;j|?Zj4G:vwKY ېo z@Va =o7OU_f=(2v^NǤڥ_0 t #6@< 3B@! @1ˈJW'_A }v>3wg~I2w||o>L503m xd233ބ jן#;5 1R1=P#:=M^ wޛV+W&{lyܓޟˆU(hA/::3Ev:xt=h\;v ;xxJG6~>tzV=`{y#(&"Vz?CGf ={!GNЀK~8=iՂu/sjزyxk^Kp>p/#`? :hhC\]?# ͪVidB@!P|* s AmX$8c [#6na8o6V ԦgB|Y1FLݟ#j1ȷ foٰOuTCTk?R+*=H=͝'Y0fw]k=emX(cMJ|V{Q%‘ 7O?z{:OCq@?ymP3tEwY3Vm!Ⱥ;ɨ U3t>߭Op]~!XWx=R=12T#H>rQ 36#&W߆vrY%s9?3}ZuP&XVY8Yqf=?NĎZ82|VCsH,=3aY?_ iFm?g\횜y+[_T!ٳmz?߅N"ڹTCm՜g[A!~OHKrrPYSNTR46hSvǟA&[@@a\^ODojp=: /Haw T??f'N}v/6sƚv-}~iπҌXBI:ԇ3 v֬e0go ۦ#lwzt.Ln`(ߴn(ԑv8<.eZ!DfnhLh?<T-Qݎ?QbyzUU(фǥűK1{#"MJT! (5^Baz>=hxdj-@ۼwgu͗Ϊ˩R{\}LG YB (y \ `jvcBߚ9W-= =ނґmѥa8IĢD(E=@H_ ^_GTUPMޤdȧ2^^j+hkA[>S(UGC! )s̓ۃa%6<͵J N< 9ȦB@!P:'!}v5;=rl^a{/wAtt2v=2^iB@\@߼"r6/K+0zD(ȶB@!P*|R R[W^! RY! &`} ˵}z(g;ʴ~J> y[! ({};W vS?iahbB@! @ނYk@o G?ў1kDuT91'=tRn k IqwV 5:@V.GPؖbs=}bf&bOis2]B! (.uwVɽUz  l! Ez CP-p{Br<="}xA@teDMwB}KΕT]d*v?5ߠ[/Rƾ:xVB! # ŁNԢӮ=<z-aS6g˔x@9#=B@!pր&zժu:ե jQ*^֥,IE[հC'ʗ{-j >~n*@}|==e:][/wda`ޢ?=r~?g@ާJfR叀\ߞ^^ˡasiٸMo Qc񀔿sJz$Bbg=`5B#!뙎G7 zJHH!XxdiY \,\5 B3אxwڞlwX@%,WB[Pq;F6%RV{p ʆ}Z~|8ۺ^ *tԅI}oL#~JB@\YLUϰFr$Nn5A7G!W܎]t;!P{`fM=Zh(iZ?;p wi5Y1]&lRhB3Ҡo@CFmׂ`'/; 5\0wgB]N.nvu/YN5~?ۯ9DEɆ@Vl@XٝK1ש5ͪC>д@3O#WΏ#phO0ևytS?4A{kPޝo>jRMj/3M\qOA=! :BTz::$rUoVTm[[sO ޯA^}~/aaOj?8z5P3=wM/P-1vzS6Anb~T?.gӌ7l8_f"lW[Д%DJ @VM]ROF֍]դ߂8C;ݲPB@! ( o8YBzĢfzU52RU9PJmXGs@#^MQmA^+WI>@m黠YcfHko$gq.1.9޴!ԶLѧsGl8q%n2pƞu3CIx6"y8C9럆<\uRj&1(χa:3^Տ؋~OW&tk P2y=_V533v<񙛔gۼ= P)qW 3sX]3c9 cIp+;q\ 1fZ˷T|v0lϡʱK`Gf`;uZi!k4؁ͺC}jAoWv565 iQG 3,ևL4Ǘ/SvB[4!rh [#ZX<^5iIhZ Tࠧ2]_>:33*w-^H;ϡN: [dؾͻ@wa!n&Oe3'N/a[Nϵ7I>|tO:8jEʧ6t¨ᯱmV G[;p-yv#*/43]hp/a;mu-]{6O{`J @QX?xV_g,`]w@C!Ԟ<(Ϲޭu{܍B@! ()k@ԈF?C. 1#o9W7 z(3Uu?zG-U0*D} jqWu!wi$#ޞhF͟$̌CH}wMZղ-ԖuF:=$:D W4=/LAw$h@H kq>֜ysxo79Us `FC |S!=: ~34s-=v^ȂZ]7v茹Q=A#n|jfUUqĖzpZKxYsۚxK7hݫx[TzF\{~Fz~PSꮇfMue B!  Og;{m m+?`SHB@! HZ`y.)*.F yg`CF8V>szCծoM`MT9w>G^bHeTܢi_õ>|-#ݵJD5Y\`7aH~MvY؁3~)[ 5Ay_H7O/BPGPsC \;>ndvۯ77όdH ^{\w$8-/߿=*Om;\uO#>[@ֹCw;[Njf6SyFt9/wVm̈́TߞVjL{NW\b])%5vF[fr:CsrI @=tX߿]_՚ROZC}=ˍx@4oQ! BjMIm/sʣ`ttUA=`C&Slz#z Z['!iѷ==v?,L^?YୡA:8\GHMۍmԇҖ@Sp;g߾P5 jƛCχѲF jwu/=tS}qa}| #)Ⓘi |ph{zpe`w x+vԕFm8lޙ<]å[~#>S?YJAC&>)׎oc$;Kr,Cwgã=.ŵgr Am ;73k Rf;c=`y[kI㚥wEkc#4$,KU-&ة?_8%"=VvlmXI2mm-Lg2d1B@!Prl!Lo?02zF}̥+Vp%xɥ~[t0gE%5Y\zo=My UUhċT&QB@G޿/ۭ'Lжrpd+z@]:r"*?g(=B@! .K.鰞ݨ6[ψǦ\zad6Hg֐Ǫґ0@컠5~T_| ! z֍[~ZiG95|^+˪,*B@=ڳ1vt0F r>,I{NI/vXvT1 [I|ֶ84!S=.)8֙]1I(κ[. ~X3F__E@۹GtP! ( = \ Ю 52_좣zrqP*WhH/I-cJ U=/Xn%zx8:?S,vydG=0P(tm-Z}〫T!  XF Q]wԳG@T=QtS x B@! JhOQNW%DIT]x@rRm! BTdh~Je! B"HE>w! WmEp_GPdB@! @%Z< KτB@Y2)F&BH=3! e @졑 ! (dR~L! @% 2{haB@! /c+=B@!Pf iB@K@ JτB@Y>ee0ܿz)t0hBk_\*gnP.O3C >4%O]^]+ڦ1B! :C" B@!P ?@Ig@fVPQ|_~51$%f):7oe,i;:p^؇eK6B%=K լ \-=! .x@.B@! Dy@j ĩ)䚒] z@TEB@  \Z! 8B$7=:'A#jC}J B@x@BO ! E{@.D'O =v2Bp7Vs Bt`c;W]=WWִogt`C~!)4bkWnu8O@|rtV dA}tSжD']Y61+oB!_סEBCiHur {O[6л_: ;vYo4FP8ucw ͊@lO[#Vo:k- bfJ M:wi6zFOzC"yӺD'ӫ㦻AM=!@ [%r450cf"gBwb:鄡-aWC&ϯu3?BE;Rv;Oy[ ݔ@"З"T!EB@!P8b.-(nll*41gd|BWQA= 5}YK {͖OIuߜYK' xt,oLR2gQ|'HM ^)zp=vw=;aWڹ?gviYg k&6 \緡W(GgÞy|tќ[!?;^K-s:ș?C\<8/՝ZwxN> 쇻B;onVEXFdx/gAf쎯6NQˀY-ӫ̈́;z4c9͎z˱tl)C=*-C՘|qB.&.'+L5L |v>lS ǢOor 3ޏ<1PKYǑx|x2#f=Gӟx?-]ssk*w#'σٰuBGu -{D5׸a 񸰙~RnE lq8~Hל>И7zXO_{<_(Kfm@\_{胓c91/tBg}ic`%BRpVS! (&*=҃q$m($nl\Po㚄o8t톽vlƙ{4ΐl16!4Vبf1˿vrz 93td"rxf6sQu#(1[!gi\ 4třn-n9y=?k)ՠdq҆"g֮XhmtY804]k۫%ʖ c;1gࡣpF#w -!&Y]=>Q-b•ɿͰt/\C%.쩷1_@A> (<.寃#! B2){ -qFvh5\{JŷШu d|+f`w@B}sY1݀˛ [S2IDAT1>;b! Z @6~f*s;޲ջ O~=Lz "ԺsZ'G QhU;ZUNӓR3T9s2_ة+J>UltNCM߂9m%C #qclUږFM*30s,[φ?fXlć4l>O7=G5fx筿 i#ȱ_*dS!x@rM! BtpV:2{ c쿆/`XǙ)kFri6ԯ+g՚k߻a 4$HŰMQ}HWmΉk6xcQ}f j&\FR?RWGLqzӫ= <0p|IA( -'FsFd3vC/﬇|9.|S-Hdt83e%{˃БGBc߮{ ߓx\[UJ~4Nǭ76c\?Ч, ;ߔoZ_~d<A\;t& z[cr8^zfS} nv0 H wrFRB@"P< J@դ=LlKB6.訮CUR>or]oєlfF"_ ~0OUmf/ל00[E,du53O~52` 8^x*3p7<̬Attq4o1Oj SC Z۞}!_΄.ش[!ʩ$~ `Pᮅִ _;}j ֤)\L19WFS|ȩnAgFЯ欀n۸WX%&Llef_ ;޽ ]՛Czp!U!  ! B@"e0iׁ3RܷJ>]Eӳ*\f! tё8! BD DJB@! bT$N! (Q2)QRB@!p12B@! J?FPʅ /YW=W K! N@< HB@!PBdRB`Z! B;xg#)B@! @ H jB@d❍! %D@ %VB@! w6"B@XV! N@ HB@!PBdRB`Z! B;xg#)B@! @ H jB@d❍! %D@ %VB@! w6"B@W$`"_%\!hK((c\I;Ѣy-Ɔ8B@! Jx@J m*vEAkFB:Vq+Mȅߣe.<3b+s !P 8w…kg;N`,l]?=|>F-aV x@"*B@ȉ'v>>1*B@=Vy/6›M wx1l$R•$i/ר|"_LtMЎa%qK(B@""lٕB@!Y\c{M;K[6ޤnr,٠kwg>X ZUk'a{n&De/1HB@oʟ$+}]9[AO$C0kom= 9Sb~C]mz=w6 <cWstў,gZL3|)7Bݏ /{;`y glLDNsEq}jYg[|`3I}GGmX8>dw4 \jIBBH3k1k3,[~XZE]q>v7{5*B%>8 7w7q߱gRl|ԁ=xғ;:VOf>@< " B@! @qp+UQ{т㉜avm;GVm=7^G߃$=o.{wdgЗR7vq~ iЭBS'bgV}ط>p:ѳgcS?i(wƋЩ`a͡zׄڒtj`3K=m߳ql9,9ğh/)|O|g9kj! z<*#"T%JTP&;[;^sůdz#uu_c=ulR0LQ~:6=6/!:TT< Yv"B@$PWvg&ND"T"8ÿg1?]}&? L\}ay)M ;AUGzLHW y8ZC`zouG!:f<a?h\ K>sU93xqorY IEcݹ%4G+AJ'cc}mA64aѳЇ? lx\Ȗ3e]ƣS=~C[u= K~ك|7?e/- zn;ZaNPm"?e'H9! @9(T@# u:,l.ySdS]9sMGYpCdu^$8Uڋf^<;yL=C iMON|j=ظ,Ƒ=/N<uk ;u=2{Iә? ؿmKy0zp*;xGC{^̴|1_h|_5Cp=^x.O\y#>nC>Pm-}/w>sb֖W}Ww ?AjB@! x14qИi-]`G54D!{~,Tb,$fnEA?NYv-ՍC߄sRlm߿Zwҋym%g/>Α}ֵuzn}뭧>}v&M`#@#CS}{ iJ9AQwq:y&1B@C#{!WtVo!C7G@W2=aSprgw0<<ן6 xZc{bt/o2Cߺ77Bx:g%uAu$Xkl -@< %EVB@! 4\O9W}.tccgz$c}BA/=n|!׍=d?|]ޛ nИ ݸʑrT lwRR+lcm\5ݢnl#Vw> {r7m>۱j%pKl\3XR"5 :ֳ"fRzB7h581ٸʋ!Pq w fݯ1^Уݽw|3:! B@\\#'B@HQIY! B䲰I!! B(YJ[lY"pGA4 U ! (4B@! J@< E%xUrB@ B@< @K7B@%2)KGC"BH9M! e @ѐ!  dRAtS! @Y" t4-B@! *T-B@!P, iB@ B@ @K7B@%2)KGC"BH9M! e @ѐ!  dRAtS! @Y" t4-B@! *T-B@!PH[3vvzѐk#C}J '`"kEٿ:u0Ԇx\z,B?o+,׌6 u\@c9mjJzcqy@)#x# *#a`@+@䮷,E歌6m{'rݯg_!  +һt"@qn':A #v ~Bcs?B A5v\~jhth2Fl>znPUD?|s>8jHz^8?=UKyD F6B@!PE! *'ʭyt ~8o ǠFAFȵ&ϡCv5%rM?o?u*r! IyʊGO?[HS@ ÿF+=z[PdΔ/vȪa:}{mxǠȻC~'˙ǤO;9|eʍE,GLKXX[?U $:L!0Gad_Ίڔm!uxayx_ݳp{[sqOET@s>Cg>О0~kkf]M^S..t',$vB> ]+ k-'z"A*bxv'>+]icOoNE]s~͡.?(4ߕB@!  ?Q{㉡V=,4mC4Q8$v@.{hAݡAKqR*;#?B{F]4Vc_| ݙUz7@sn߀(aA{b _zl7Du9ts ڻO )w`]V#h$h53߆5~` g` '!acGRd1sb !ppCu+˄q]DovX γq͆}yH[9UWF( M+6zd"hXz@Zw)bRDR\! (<5,|DH[4uĉHxO5᳹GEBc~+#>=®Si},tܑș~[;sMJ[=T>,^[gyD<sèq#g6R#N',0ѳN{(bUq9,5;=3L]=OA3pF#G|01wp#E۟U"(x\TFPkGZ-W+5P< 厪6˜#-jq_QlB@! A{@rtV3Ј&'$CNX#H9!J?sMj(\k8Շ*G3<ūJCZuVx!whC_:s9}CIG ̧huȖ(eՇ_B 1\)/a-Q<+RB@!P**\m>\%@?EW?jm{@X{`[1ëN7ӎܞ|{㣱g zZ(9ѵjFL3w?K{m{s_ՖKUfyBLC%#B@\|BUU:uo +Z}28|qa!h=븶pC:k k:f-lv֩(+IU>v[to3$B@!PTX׃[/ p$jji<\Ss{H_{W':_?B۽8Џ#[`H,뫵.ՊDj?ztƷoϑ|0#uy er$dRȂUsOW&ų?eKrG@׶oSt3~t3\{׹-T^gL/76uCiwTQ #v BK/h: Y{(GQ ZShB@!pp2sY(g"vg,:MBkא|mO4(UsՑ9@_Ͽ^aďe Ro~yӧz;CaRr]J B@! /}#")L ջA ݱ jfr9|4G!ꭔ#}Ȃ)_7=-1'qGPzjrHX`ڋ6㹟¾DޙUY+D.cԼmtעjf&^d*nij . BK* |M"|}s:32Wp>u~0jO$0Ȥm[Kfi/j엹7=KXz1o+]Zf+?@DX(]>=hAD,Z1zfg\} zv-z h5 4'>aDP_!~kv}+!_4WQ7m muh[ꓽ`# ޥ:(,[7o ظIhIg8@t Aydf`j^D@D@D 7Iyz3Eל_z]," " % oi(." " "8߉<gbcqhu#" " "`# B1'$FD@D@DF@b" " " N" IՍ  D@D@D@D@@ 68 'V7" " " 62@l,p NnD@D@D@ldX(&" " "$2@Z݈PLD@D@DI\ԏȸw/Y >C;C+Aoys;u4@h͠\ D@IiU–%n@Cm_2W " " "L8v!LgAS+CLKH߽f#t+=aC,AZq|hf)fxY"2g\Eڷ^=hҖ"Yv"҃ؗry@ۏN< g%| U " "p+ f2G Wn>HW j w4,]ZL`"{@p^_ Mu[w.}W()&/" 3SczG. û ABK]K=Ѕ'*A:n\dz>SEQOA4g']T)()k$n$T(epà[== c'#YKێ]fA}ZBnOG|j+^S1f&4r? =#i 6j[ж]ɞ_uQЄd.ȣo?.),t 2@O~ÝP,<㼰gXɺQqf]¡|Wؗ~~}/?1ië243w> E{#}; 2ٯٟP>r[:$ChUzp'?#Q %@Z(ߺYMGQ%۰mФPn`G" " " "P(B]u/W6w(.Bj }=@zi2&AUV8#ig!P]Z@B7nNdQ VBϚjhИiQW/@B *3hWȮw$:c~:mhFma8gKSXwպH8ks? }8BñC!OGЇgwN^;tP/(kO5 i]'~T7139= K|6ܒ{+=!7QT$M,4ӻy@b{! D@nHM$ןaq#{]Z|S ި#ש|BwthޭgPk^eѸnoiϢ Aet.\Gu|kk1[VCweހr| L,GnqJz,A1`w&ү̊G@jZ(hw:BHEzA{o)dqnږA wC'ghw]uw (%ԣ>v#χzuh{nH3Hx#5==&zeGz8~7Ȏ"K{?ֿ[3ل;ۇUèO|lg64(H>WL >W(Zlg _ν:=f7"qkrzJH:lЦ~JO5߸Kx#|&tWb9ޕFՈGvb~ߛe(yqkA32X~"Аy3|DCx,=!e ؼ3{c]|~O2;4z00 ~.hsn.؛HS`8].Z~n"Mݘ;ϝP:HgMF̿YЈ3 0yo VF*ə\g=x+pFvv;X]i?tڝÑ_lK\K؀[ֈtjuvfFVJ<>Ծ'҅ Y.R@P&" " "p%wuz@I CW~ ͛oBc<4|EQk?. /awǻ벷Chd_5gDJ2 >|x;h4zBbf+֫5s'q-unPjc~+!AHIDz./x٧2ayէD?? mkgm?:z~ؠp鐆joއ_C3[ΆAë>뺻!]fD@D@D@ NfY뗘nUc'r;g%}!p6$4Z֫>_ ܸ|ޝ"vtbZ‘miTG| HfHX~^5j-f8*wL󭠌 BkT\ i ͇]ZҬЂ[tVLuΠNg?|=Mdz:ß-F#T5/r|_ wx&=پM.Q6򛰈| s~-^@o _-ޱc7|{ Dާ1IA.A~0AX<)_ah#N .d:Ds'? L].Y/x@q'93qǠͣ&9s)3uhCm~Ab]T 3<m_sڢMH>k0>ǵw!swI5!" " " lݼ\&Td\=;{yxd?]$" "pYy@LD@Hq45OO>( SD@D@S>| ]O|3VR< j" " " wނ{,Ւm-Rتe;dl8jYD@D@D v([D@D@Dqd8ZC@0p cE@D@D@bE@D@D@G@تe;dl8jYD@D@D v([D@D@DqH̴5IENDB`binwalk-2.1.1/setup.py000077500000000000000000000151511263655036500146750ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import shutil import tempfile import subprocess from distutils.core import setup, Command from distutils.dir_util import remove_tree MODULE_NAME = "binwalk" SCRIPT_NAME = MODULE_NAME # Python2/3 compliance try: raw_input except NameError: raw_input = input # cd into the src directory, no matter where setup.py was invoked from os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "src")) def which(command): # /usr/local/bin is usually the default install path, though it may not be in $PATH usr_local_bin = os.path.sep.join([os.path.sep, 'usr', 'local', 'bin', command]) try: location = subprocess.Popen(["which", command], shell=False, stdout=subprocess.PIPE).communicate()[0].strip() except KeyboardInterrupt as e: raise e except Exception as e: pass if not location and os.path.exists(usr_local_bin): location = usr_local_bin return location def find_binwalk_module_paths(): paths = [] try: import binwalk paths = binwalk.__path__ except KeyboardInterrupt as e: raise e except Exception: pass return paths def remove_binwalk_module(pydir=None, pybin=None): if pydir: module_paths = [pydir] else: module_paths = find_binwalk_module_paths() for path in module_paths: try: remove_tree(path) except OSError as e: pass if not pybin: pybin = which(MODULE_NAME) if pybin: try: sys.stdout.write("removing '%s'\n" % pybin) os.remove(pybin) except KeyboardInterrupt as e: pass except Exception as e: pass class IDAUnInstallCommand(Command): description = "Uninstalls the binwalk IDA plugin module" user_options = [ ('idadir=', None, 'Specify the path to your IDA install directory.'), ] def initialize_options(self): self.idadir = None self.mydir = os.path.dirname(os.path.realpath(__file__)) def finalize_options(self): pass def run(self): if self.idadir is None: sys.stderr.write("Please specify the path to your IDA install directory with the '--idadir' option!\n") return binida_dst_path = os.path.join(self.idadir, 'plugins', 'binida.py') binwalk_dst_path = os.path.join(self.idadir, 'python', 'binwalk') if os.path.exists(binida_dst_path): sys.stdout.write("removing %s\n" % binida_dst_path) os.remove(binida_dst_path) if os.path.exists(binwalk_dst_path): sys.stdout.write("removing %s\n" % binwalk_dst_path) shutil.rmtree(binwalk_dst_path) class IDAInstallCommand(Command): description = "Installs the binwalk IDA plugin module" user_options = [ ('idadir=', None, 'Specify the path to your IDA install directory.'), ] def initialize_options(self): self.idadir = None self.mydir = os.path.dirname(os.path.realpath(__file__)) def finalize_options(self): pass def run(self): if self.idadir is None: sys.stderr.write("Please specify the path to your IDA install directory with the '--idadir' option!\n") return binida_src_path = os.path.join(self.mydir, 'scripts', 'binida.py') binida_dst_path = os.path.join(self.idadir, 'plugins') if not os.path.exists(binida_src_path): sys.stderr.write("ERROR: could not locate IDA plugin file '%s'!\n" % binida_src_path) return if not os.path.exists(binida_dst_path): sys.stderr.write("ERROR: could not locate the IDA plugins directory '%s'! Check your --idadir option.\n" % binida_dst_path) return binwalk_src_path = os.path.join(self.mydir, 'binwalk') binwalk_dst_path = os.path.join(self.idadir, 'python') if not os.path.exists(binwalk_src_path): sys.stderr.write("ERROR: could not locate binwalk source directory '%s'!\n" % binwalk_src_path) return if not os.path.exists(binwalk_dst_path): sys.stderr.write("ERROR: could not locate the IDA python directory '%s'! Check your --idadir option.\n" % binwalk_dst_path) return binida_dst_path = os.path.join(binida_dst_path, 'binida.py') binwalk_dst_path = os.path.join(binwalk_dst_path, 'binwalk') if os.path.exists(binida_dst_path): os.remove(binida_dst_path) if os.path.exists(binwalk_dst_path): shutil.rmtree(binwalk_dst_path) sys.stdout.write("copying %s -> %s\n" % (binida_src_path, binida_dst_path)) shutil.copyfile(binida_src_path, binida_dst_path) sys.stdout.write("copying %s -> %s\n" % (binwalk_src_path, binwalk_dst_path)) shutil.copytree(binwalk_src_path, binwalk_dst_path) class UninstallCommand(Command): description = "Uninstalls the Python module" user_options = [ ('pydir=', None, 'Specify the path to the binwalk python module to be removed.'), ('pybin=', None, 'Specify the path to the binwalk executable to be removed.'), ] def initialize_options(self): self.pydir = None self.pybin = None def finalize_options(self): pass def run(self): remove_binwalk_module(self.pydir, self.pybin) class CleanCommand(Command): description = "Clean Python build directories" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): try: remove_tree("build") except KeyboardInterrupt as e: raise e except Exception: pass try: remove_tree("dist") except KeyboardInterrupt as e: raise e except Exception: pass # The data files to install along with the module install_data_files = [] for data_dir in ["magic", "config", "plugins", "modules", "core"]: install_data_files.append("%s%s*" % (data_dir, os.path.sep)) # Install the module, script, and support files setup(name = MODULE_NAME, version = "2.1.1", description = "Firmware analysis tool", author = "Craig Heffner", url = "https://github.com/devttys0/%s" % MODULE_NAME, requires = [], packages = [MODULE_NAME], package_data = {MODULE_NAME : install_data_files}, scripts = [os.path.join("scripts", SCRIPT_NAME)], cmdclass = {'clean' : CleanCommand, 'uninstall' : UninstallCommand, 'idainstall' : IDAInstallCommand, 'idauninstall' : IDAUnInstallCommand} ) binwalk-2.1.1/src/000077500000000000000000000000001263655036500137445ustar00rootroot00000000000000binwalk-2.1.1/src/.gitignore000066400000000000000000000000131263655036500157260ustar00rootroot00000000000000build dist binwalk-2.1.1/src/binwalk/000077500000000000000000000000001263655036500153735ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/__init__.py000066400000000000000000000004611263655036500175050ustar00rootroot00000000000000__all__ = ['scan', 'execute', 'ModuleException'] from binwalk.core.module import Modules, ModuleException # Convenience functions def scan(*args, **kwargs): with Modules(*args, **kwargs) as m: objs = m.execute() return objs def execute(*args, **kwargs): return scan(*args, **kwargs) binwalk-2.1.1/src/binwalk/config/000077500000000000000000000000001263655036500166405ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/config/extract.conf000077500000000000000000000065231263655036500211720ustar00rootroot00000000000000####################################################################################################################################### # Default extraction rules, loaded when --extract is specified. # # :::: # # Note that %e is a place holder for the extracted file name. # # The %% place holder is used when a unique file path is required. # For example '%%squashfs-root%%' will be replaced with 'squashfs-root-0' # if 'squashfs-root' already exists. # # The following file types are handled internally by extractor plugins: # # o zlib # o cpio # o Raw LZMA/deflate streams # # There are also alternative extractors for the following file formats, implemented as plugins: # # o gzip # o lzma # o xz # ####################################################################################################################################### # Assumes these utilities are installed in $PATH. ^gzip compressed data:gz:gzip -d -f '%e':0,2 ^lzma compressed data:7z:7z e -y '%e':0,1 ^xz compressed data:xz:7z e -y '%e':0,1 ^bzip2 compressed data:bz2:bzip2 -d '%e' ^compress'd data:Z:gzip -d '%e' ^zip archive data:zip:7z x -y '%e' -p '':0,1 ^posix tar archive:tar:tar xvf '%e' ^rar archive data:rar:unrar e '%e' ^rar archive data:rar:unrar -x '%e' # This is for the 'free' version ^arj archive data.*comment header:arj:arj -y e '%e' ^lha:lha:lha ei '%e' ^iso 9660:iso:7z x '%e' -oiso-root ^microsoft cabinet archive:cab:cabextract '%e' ^stuffit:sit:unstuff '%e' # Try unsquashfs first, or if not installed, sasquatch ^squashfs filesystem:squashfs:unsquashfs -d '%%squashfs-root%%' '%e':0:False ^squashfs filesystem:squashfs:sasquatch -p 1 -le -d '%%squashfs-root%%' '%e':0:False ^squashfs filesystem:squashfs:sasquatch -p 1 -be -d '%%squashfs-root%%' '%e':0:False # Try cramfsck first; if that fails, swap the file system and try again ^cramfs filesystem:cramfs:cramfsck -x '%%cramfs-root%%' '%e':0:False ^cramfs filesystem:cramfs:cramfsswap '%e' '%e.swap' && cramfsck -x '%%cramfs-root%%' '%e.swap':0:False # Extract EXT filesystems using sleuth kit ^linux ext:ext:tsk_recover -i raw -f ext -a -v '%e' '%%ext-root%%':0:False # Try mounting the file system (this requires root privileges) ^squashfs filesystem:squashfs:mkdir squashfs-root && mount -t squashfs '%e' squashfs-root:0:False ^cramfs filesystem:cramfs:mkdir cramfs-root && mount -t cramfs '%e' cramfs-root:0:False ^ext2 filesystem:ext2:mkdir ext2-root && mount -t ext2 '%e' ext2-root:0:False ^romfs filesystem:romfs:mkdir romfs-root && mount -t romfs '%e' romfs-root:0:False # Use sviehb's jefferson.py tool for JFFS2 extraction ^jffs2 filesystem:jffs2:jefferson -d '%%jffs2-root%%' '%e':0:False # Use ubi_reader tool for UBIFS extraction ^ubifs filesystem superblock node:ubi:ubireader_extract_files -o '%%ubifs-root%%' '%e':0:False ^ubi erase count header:ubi:ubireader_extract_files -o '%%ubifs-root%%' '%e':0:False # These were extractors used from FMK that still need suitable replacements. #^bff volume entry:bff:/opt/firmware-mod-kit/src/bff/bffxtractor.py '%e' #^wdk file system:wdk:/opt/firmware-mod-kit/src/firmware-tools/unwdk.py '%e' # Extract, but don't run anything ^elf,:elf private key:key certificate:crt html document header xml document:xml binwalk-2.1.1/src/binwalk/core/000077500000000000000000000000001263655036500163235ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/core/C.py000066400000000000000000000131011263655036500170530ustar00rootroot00000000000000import os import sys import glob import ctypes import ctypes.util import binwalk.core.common from binwalk.core.compat import * class Function(object): ''' Container class for defining library functions. ''' def __init__(self, **kwargs): self.name = None self.type = int for (k, v) in iterator(kwargs): setattr(self, k, v) class FunctionHandler(object): ''' Class for abstracting function calls via ctypes and handling Python 2/3 compatibility issues. ''' PY2CTYPES = { bytes : ctypes.c_char_p, str : ctypes.c_char_p, int : ctypes.c_int, float : ctypes.c_float, bool : ctypes.c_int, None : ctypes.c_int, } RETVAL_CONVERTERS = { None : int, int : int, float : float, bool : bool, str : bytes2str, bytes : str2bytes, } def __init__(self, library, function): ''' Class constructor. @library - Library handle as returned by ctypes.cdll.LoadLibrary. @function - An instance of the binwalk.core.C.Function class. Returns None. ''' self.name = function.name self.retype = function.type self.function = getattr(library, self.name) if has_key(self.PY2CTYPES, self.retype): self.function.restype = self.PY2CTYPES[self.retype] self.retval_converter = self.RETVAL_CONVERTERS[self.retype] else: self.function.restype = self.retype self.retval_converter = None #raise Exception("Unknown return type: '%s'" % self.retype) def run(self, *args): ''' Executes the library function, handling Python 2/3 compatibility and properly converting the return type. @*args - Library function arguments. Returns the return value from the libraray function. ''' args = list(args) # Python3 expects a bytes object for char *'s, not a str. # This allows us to pass either, regardless of the Python version. for i in range(0, len(args)): if isinstance(args[i], str): args[i] = str2bytes(args[i]) retval = self.function(*args) if self.retval_converter is not None: retval = self.retval_converter(retval) return retval class Library(object): ''' Class for loading the specified library via ctypes. ''' def __init__(self, library, functions): ''' Class constructor. @library - Library name (e.g., 'magic' for libmagic), or a list of names. @functions - A dictionary of function names and their return types (e.g., {'magic_buffer' : str}) Returns None. ''' self.settings = binwalk.core.settings.Settings() self.library = ctypes.cdll.LoadLibrary(self.find_library(library)) if not self.library: raise Exception("Failed to load library '%s'" % library) for function in functions: f = FunctionHandler(self.library, function) setattr(self, function.name, f.run) def find_library(self, libraries): ''' Locates the specified library. @libraries - Library name (e.g., 'magic' for libmagic), or a list of names. Returns a string to be passed to ctypes.cdll.LoadLibrary. ''' lib_path = None prefix = binwalk.core.common.get_libs_path() if isinstance(libraries, str): libraries = [libraries] for library in libraries: system_paths = { 'linux' : [os.path.join(prefix, 'lib%s.so' % library), '/usr/local/lib/lib%s.so' % library], 'cygwin' : [os.path.join(prefix, 'lib%s.so' % library), '/usr/local/lib/lib%s.so' % library], 'win32' : [os.path.join(prefix, 'lib%s.dll' % library), '%s.dll' % library], 'darwin' : [os.path.join(prefix, 'lib%s.dylib' % library), '/opt/local/lib/lib%s.dylib' % library, '/usr/local/lib/lib%s.dylib' % library, ] + glob.glob('/usr/local/Cellar/*%s*/*/lib/lib%s.dylib' % (library, library)), } for i in range(2, 4): system_paths['linux%d' % i] = system_paths['linux'] # Search the common install directories first; these are usually not in the library search path # Search these *first*, since a) they are the most likely locations and b) there may be a # discrepency between where ctypes.util.find_library and ctypes.cdll.LoadLibrary search for libs. for path in system_paths[sys.platform]: binwalk.core.common.debug("Searching for '%s'" % path) if os.path.exists(path): lib_path = path break # If we failed to find the library, check the standard library search paths if not lib_path: lib_path = ctypes.util.find_library(library) # Use the first library that we can find if lib_path: binwalk.core.common.debug("Found library '%s' at: %s" % (library, lib_path)) break else: binwalk.core.common.debug("Could not find library '%s'" % library) # If we still couldn't find the library, error out if not lib_path: raise Exception("Failed to locate libraries '%s'" % str(libraries)) return lib_path binwalk-2.1.1/src/binwalk/core/__init__.py000066400000000000000000000000001263655036500204220ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/core/common.py000066400000000000000000000375621263655036500202020ustar00rootroot00000000000000# Common functions used throughout various parts of binwalk code. import io import os import re import sys import ast import hashlib import platform import operator as op from binwalk.core.compat import * # The __debug__ value is a bit backwards; by default it is set to True, but # then set to False if the Python interpreter is run with the -O option. if not __debug__: DEBUG = True else: DEBUG = False def MSWindows(): # Returns True if running in a Microsoft Windows OS return (platform.system() == 'Windows') def debug(msg): ''' Displays debug messages to stderr only if the Python interpreter was invoked with the -O flag. ''' if DEBUG: sys.stderr.write("DEBUG: " + msg + "\n") sys.stderr.flush() def warning(msg): ''' Prints warning messages to stderr ''' sys.stderr.write("\nWARNING: " + msg + "\n") def error(msg): ''' Prints error messages to stderr ''' sys.stderr.write("\nERROR: " + msg + "\n") def get_module_path(): root = __file__ if os.path.islink(root): root = os.path.realpath(root) return os.path.dirname(os.path.dirname(os.path.abspath(root))) def get_libs_path(): return os.path.join(get_module_path(), "libs") def file_md5(file_name): ''' Generate an MD5 hash of the specified file. @file_name - The file to hash. Returns an MD5 hex digest string. ''' md5 = hashlib.md5() with open(file_name, 'rb') as f: for chunk in iter(lambda: f.read(128*md5.block_size), b''): md5.update(chunk) return md5.hexdigest() def file_size(filename): ''' Obtains the size of a given file. @filename - Path to the file. Returns the size of the file. ''' # Using open/lseek works on both regular files and block devices fd = os.open(filename, os.O_RDONLY) try: return os.lseek(fd, 0, os.SEEK_END) except KeyboardInterrupt as e: raise e except Exception as e: raise Exception("file_size failed to obtain the size of '%s': %s" % (filename, str(e))) finally: os.close(fd) def strip_quoted_strings(string): ''' Strips out data in between double quotes. @string - String to strip. Returns a sanitized string. ''' # This regex removes all quoted data from string. # Note that this removes everything in between the first and last double quote. # This is intentional, as printed (and quoted) strings from a target file may contain # double quotes, and this function should ignore those. However, it also means that any # data between two quoted strings (ex: '"quote 1" you won't see me "quote 2"') will also be stripped. return re.sub(r'\"(.*)\"', "", string) def get_quoted_strings(string): ''' Returns a string comprised of all data in between double quotes. @string - String to get quoted data from. Returns a string of quoted data on success. Returns a blank string if no quoted data is present. ''' try: # This regex grabs all quoted data from string. # Note that this gets everything in between the first and last double quote. # This is intentional, as printed (and quoted) strings from a target file may contain # double quotes, and this function should ignore those. However, it also means that any # data between two quoted strings (ex: '"quote 1" non-quoted data "quote 2"') will also be included. return re.findall(r'\"(.*)\"', string)[0] except KeyboardInterrupt as e: raise e except Exception: return '' def unique_file_name(base_name, extension=''): ''' Creates a unique file name based on the specified base name. @base_name - The base name to use for the unique file name. @extension - The file extension to use for the unique file name. Returns a unique file string. ''' idcount = 0 if extension and not extension.startswith('.'): extension = '.%s' % extension fname = base_name + extension while os.path.exists(fname): fname = "%s-%d%s" % (base_name, idcount, extension) idcount += 1 return fname def strings(filename, minimum=4): ''' A strings generator, similar to the Unix strings utility. @filename - The file to search for strings in. @minimum - The minimum string length to search for. Yeilds printable ASCII strings from filename. ''' result = "" with BlockFile(filename) as f: while True: (data, dlen) = f.read_block() if not data: break for c in data: if c in string.printable: result += c continue elif len(result) >= minimum: yield result result = "" else: result = "" class GenericContainer(object): def __init__(self, **kwargs): for (k,v) in iterator(kwargs): setattr(self, k, v) class MathExpression(object): ''' Class for safely evaluating mathematical expressions from a string. Stolen from: http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string ''' OPERATORS = { ast.Add: op.add, ast.UAdd: op.add, ast.USub: op.sub, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor } def __init__(self, expression): self.expression = expression self.value = None if expression: try: self.value = self.evaluate(self.expression) except KeyboardInterrupt as e: raise e except Exception as e: pass def evaluate(self, expr): return self._eval(ast.parse(expr).body[0].value) def _eval(self, node): if isinstance(node, ast.Num): # return node.n elif isinstance(node, ast.operator): # return self.OPERATORS[type(node.op)] elif isinstance(node, ast.UnaryOp): return self.OPERATORS[type(node.op)](0, self._eval(node.operand)) elif isinstance(node, ast.BinOp): # return self.OPERATORS[type(node.op)](self._eval(node.left), self._eval(node.right)) else: raise TypeError(node) class StringFile(object): ''' A class to allow access to strings as if they were read from a file. Used internally as a conditional superclass to InternalBlockFile. ''' def __init__(self, fname, mode='r'): self.string = fname self.name = "String" self.args.size = len(self.string) def read(self, n=-1): if n == -1: data = self.string[self.total_read:] else: data = self.string[self.total_read:self.total_read+n] return data def tell(self): return self.total_read def write(self, *args, **kwargs): pass def seek(self, *args, **kwargs): pass def close(self): pass def BlockFile(fname, mode='r', subclass=io.FileIO, **kwargs): # Defining a class inside a function allows it to be dynamically subclassed class InternalBlockFile(subclass): ''' Abstraction class for accessing binary files. This class overrides io.FilIO's read and write methods. This guaruntees two things: 1. All requested data will be read/written via the read and write methods. 2. All reads return a str object and all writes can accept either a str or a bytes object, regardless of the Python interpreter version. However, the downside is that other io.FileIO methods won't work properly in Python 3, namely things that are wrappers around self.read (e.g., readline, readlines, etc). This class also provides a read_block method, which is used by binwalk to read in a block of data, plus some additional data (DEFAULT_BLOCK_PEEK_SIZE), but on the next block read pick up at the end of the previous data block (not the end of the additional data). This is necessary for scans where a signature may span a block boundary. The descision to force read to return a str object instead of a bytes object is questionable for Python 3, but it seemed the best way to abstract differences in Python 2/3 from the rest of the code (especially for people writing plugins) and to add Python 3 support with minimal code change. ''' # The DEFAULT_BLOCK_PEEK_SIZE limits the amount of data available to a signature. # While most headers/signatures are far less than this value, some may reference # pointers in the header structure which may point well beyond the header itself. # Passing the entire remaining buffer to libmagic is resource intensive and will # significantly slow the scan; this value represents a reasonable buffer size to # pass to libmagic which will not drastically affect scan time. DEFAULT_BLOCK_PEEK_SIZE = 8 * 1024 # Max number of bytes to process at one time. This needs to be large enough to # limit disk I/O, but small enough to limit the size of processed data blocks. DEFAULT_BLOCK_READ_SIZE = 1 * 1024 * 1024 def __init__(self, fname, mode='r', length=0, offset=0, block=DEFAULT_BLOCK_READ_SIZE, peek=DEFAULT_BLOCK_PEEK_SIZE, swap=0): ''' Class constructor. @fname - Path to the file to be opened. @mode - Mode to open the file in (default: 'r'). @length - Maximum number of bytes to read from the file via self.block_read(). @offset - Offset at which to start reading from the file. @block - Size of data block to read (excluding any trailing size), @peek - Size of trailing data to append to the end of each block. @swap - Swap every n bytes of data. Returns None. ''' self.total_read = 0 self.block_read_size = self.DEFAULT_BLOCK_READ_SIZE self.block_peek_size = self.DEFAULT_BLOCK_PEEK_SIZE # This is so that custom parent classes can access/modify arguments as necessary self.args = GenericContainer(fname=fname, mode=mode, length=length, offset=offset, block=block, peek=peek, swap=swap, size=0) # Python 2.6 doesn't like modes like 'rb' or 'wb' mode = self.args.mode.replace('b', '') super(self.__class__, self).__init__(fname, mode) self.swap_size = self.args.swap if self.args.size: self.size = self.args.size else: try: self.size = file_size(self.args.fname) except KeyboardInterrupt as e: raise e except Exception: self.size = 0 if self.args.offset < 0: self.offset = self.size + self.args.offset else: self.offset = self.args.offset if self.offset < 0: self.offset = 0 elif self.offset > self.size: self.offset = self.size if self.args.offset < 0: self.length = self.args.offset * -1 elif self.args.length: self.length = self.args.length else: self.length = self.size - self.args.offset if self.length < 0: self.length = 0 elif self.length > self.size: self.length = self.size if self.args.block is not None: self.block_read_size = self.args.block self.base_block_size = self.block_read_size if self.args.peek is not None: self.block_peek_size = self.args.peek self.base_peek_size = self.block_peek_size # Work around for python 2.6 where FileIO._name is not defined try: self.name except AttributeError: self._name = fname self.path = os.path.abspath(self.name) self.seek(self.offset) def _swap_data_block(self, block): ''' Reverses every self.swap_size bytes inside the specified data block. Size of data block must be a multiple of self.swap_size. @block - The data block to swap. Returns a swapped string. ''' i = 0 data = "" if self.swap_size > 0: while i < len(block): data += block[i:i+self.swap_size][::-1] i += self.swap_size else: data = block return data def reset(self): self.set_block_size(block=self.base_block_size, peek=self.base_peek_size) self.seek(self.offset) def set_block_size(self, block=None, peek=None): if block is not None: self.block_read_size = block if peek is not None: self.block_peek_size = peek def write(self, data): ''' Writes data to the opened file. io.FileIO.write does not guaruntee that all data will be written; this method overrides io.FileIO.write and does guaruntee that all data will be written. Returns the number of bytes written. ''' n = 0 l = len(data) data = str2bytes(data) while n < l: n += super(self.__class__, self).write(data[n:]) return n def read(self, n=-1): '''' Reads up to n bytes of data (or to EOF if n is not specified). Will not read more than self.length bytes. io.FileIO.read does not guaruntee that all requested data will be read; this method overrides io.FileIO.read and does guaruntee that all data will be read. Returns a str object containing the read data. ''' l = 0 data = b'' if self.total_read < self.length: # Don't read more than self.length bytes from the file if (self.total_read + n) > self.length: n = self.length - self.total_read while n < 0 or l < n: tmp = super(self.__class__, self).read(n-l) if tmp: data += tmp l += len(tmp) else: break self.total_read += len(data) return self._swap_data_block(bytes2str(data)) def peek(self, n=-1): ''' Peeks at data in file. ''' pos = self.tell() data = self.read(n) self.seek(pos) return data def seek(self, n, whence=os.SEEK_SET): if whence == os.SEEK_SET: self.total_read = n - self.offset elif whence == os.SEEK_CUR: self.total_read += n elif whence == os.SEEK_END: self.total_read = self.size + n super(self.__class__, self).seek(n, whence) def read_block(self): ''' Reads in a block of data from the target file. Returns a tuple of (str(file block data), block data length). ''' data = self.read(self.block_read_size) dlen = len(data) data += self.peek(self.block_peek_size) return (data, dlen) return InternalBlockFile(fname, mode=mode, **kwargs) binwalk-2.1.1/src/binwalk/core/compat.py000066400000000000000000000034351263655036500201650ustar00rootroot00000000000000# All Python 2/3 compatibility stuffs go here. from __future__ import print_function import sys import string PY_MAJOR_VERSION = sys.version_info[0] if PY_MAJOR_VERSION > 2: string.letters = string.ascii_letters def iterator(dictionary): ''' For cross compatibility between Python 2 and Python 3 dictionaries. ''' if PY_MAJOR_VERSION > 2: return dictionary.items() else: return dictionary.iteritems() def has_key(dictionary, key): ''' For cross compatibility between Python 2 and Python 3 dictionaries. ''' if PY_MAJOR_VERSION > 2: return key in dictionary else: return dictionary.has_key(key) def get_keys(dictionary): ''' For cross compatibility between Python 2 and Python 3 dictionaries. ''' if PY_MAJOR_VERSION > 2: return list(dictionary.keys()) else: return dictionary.keys() def str2bytes(string): ''' For cross compatibility between Python 2 and Python 3 strings. ''' if isinstance(string, type('')) and PY_MAJOR_VERSION > 2: return bytes(string, 'latin1') else: return string def bytes2str(bs): ''' For cross compatibility between Python 2 and Python 3 strings. ''' if isinstance(bs, type(b'')) and PY_MAJOR_VERSION > 2: return bs.decode('latin1') else: return bs def string_decode(string): ''' For cross compatibility between Python 2 and Python 3 strings. ''' if PY_MAJOR_VERSION > 2: return bytes(string, 'utf-8').decode('unicode_escape') else: return string.decode('string_escape') def user_input(prompt=''): ''' For getting raw user input in Python 2 and 3. ''' if PY_MAJOR_VERSION > 2: return input(prompt) else: return raw_input(prompt) binwalk-2.1.1/src/binwalk/core/display.py000066400000000000000000000211361263655036500203450ustar00rootroot00000000000000# Code to handle displaying and logging of results. # Anything in binwalk that prints results to screen should use this class. import sys import csv as pycsv import datetime import binwalk.core.common from binwalk.core.compat import * class Display(object): ''' Class to handle display of output and writing to log files. This class is instantiated for all modules implicitly and should not need to be invoked directly by most modules. ''' SCREEN_WIDTH = 0 HEADER_WIDTH = 80 DEFAULT_FORMAT = "%s\n" def __init__(self, quiet=False, verbose=False, log=None, csv=False, fit_to_screen=False): self.quiet = quiet self.verbose = verbose self.fit_to_screen = fit_to_screen self.fp = None self.csv = None self.num_columns = 0 self.custom_verbose_format = "" self.custom_verbose_args = [] self._configure_formatting() if log: self.fp = open(log, "a") if csv: self.csv = pycsv.writer(self.fp) def _fix_unicode(self, line): ''' This is a hack, there must be a better way to handle it. In Python3, if the environment variable LANG=C is set, indicating that the terminal is ASCII only, but unicode characters need to be printed to the screen or to a file (e.g., file path, magic result format string), then an UnicodeEncodError exception will be raised. This converts the given line to ASCII, ignoring conversion errors, and returns a str. ''' return bytes2str(line.encode('ascii', 'ignore')) def _fix_unicode_list(self, columns): ''' Convenience wrapper for self.log which is passed a list of format arguments. ''' if type(columns) in [list, tuple]: for i in range(0, len(columns)): try: columns[i] = self._fix_unicode(columns[i]) except AttributeError: pass return columns def format_strings(self, header, result): self.result_format = result self.header_format = header if self.num_columns == 0: self.num_columns = len(header.split()) def log(self, fmt, columns): if self.fp: if self.csv: try: self.csv.writerow(columns) except UnicodeEncodeError: self.csv.writerow(self._fix_unicode_list(columns)) else: try: self.fp.write(fmt % tuple(columns)) except UnicodeEncodeError: self.fp.write(fmt % tuple(self._fix_unicode_list(columns))) self.fp.flush() def add_custom_header(self, fmt, args): self.custom_verbose_format = fmt self.custom_verbose_args = args def header(self, *args, **kwargs): file_name = None self.num_columns = len(args) if has_key(kwargs, 'file_name'): file_name = kwargs['file_name'] if self.verbose and file_name: md5sum = binwalk.core.common.file_md5(file_name) timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") if self.csv: self.log("", ["FILE", "MD5SUM", "TIMESTAMP"]) self.log("", [file_name, md5sum, timestamp]) self._fprint("%s", "\n", csv=False) self._fprint("Scan Time: %s\n", [timestamp], csv=False, filter=False) self._fprint("Target File: %s\n", [file_name], csv=False, filter=False) self._fprint("MD5 Checksum: %s\n", [md5sum], csv=False, filter=False) if self.custom_verbose_format and self.custom_verbose_args: self._fprint(self.custom_verbose_format, self.custom_verbose_args, csv=False, filter=False) self._fprint("%s", "\n", csv=False, filter=False) self._fprint(self.header_format, args, filter=False) self._fprint("%s", ["-" * self.HEADER_WIDTH + "\n"], csv=False, filter=False) def result(self, *args): # Convert to list for item assignment args = list(args) # Replace multiple spaces with single spaces. This is to prevent accidentally putting # four spaces in the description string, which would break auto-formatting. for i in range(len(args)): if isinstance(args[i], str): while " " in args[i]: args[i] = args[i].replace(" " , " ") self._fprint(self.result_format, tuple(args)) def footer(self): self._fprint("%s", "\n", csv=False, filter=False) def _fprint(self, fmt, columns, csv=True, stdout=True, filter=True): line = fmt % tuple(columns) if not self.quiet and stdout: try: try: sys.stdout.write(self._format_line(line.strip()) + "\n") except UnicodeEncodeError: line = self._fix_unicode(line) sys.stdout.write(self._format_line(line.strip()) + "\n") sys.stdout.flush() except IOError as e: pass if self.fp and not (self.csv and not csv): self.log(fmt, columns) def _append_to_data_parts(self, data, start, end): ''' Intelligently appends data to self.string_parts. For use by self._format. ''' try: while data[start] == ' ': start += 1 if start == end: end = len(data[start:]) self.string_parts.append(data[start:end]) except KeyboardInterrupt as e: raise e except Exception: try: self.string_parts.append(data[start:]) except KeyboardInterrupt as e: raise e except Exception: pass return start def _format_line(self, line): ''' Formats a line of text to fit in the terminal window. For Tim. ''' delim = '\n' offset = 0 self.string_parts = [] # Split the line into an array of columns, e.g., ['0', '0x00000000', 'Some description here'] line_columns = line.split(None, self.num_columns-1) if line_columns: # Find where the start of the last column (description) starts in the line of text. # All line wraps need to be aligned to this offset. offset = line.rfind(line_columns[-1]) # The delimiter will be a newline followed by spaces padding out the line wrap to the alignment offset. delim += ' ' * offset if line_columns and self.fit_to_screen and len(line) > self.SCREEN_WIDTH: # Calculate the maximum length that each wrapped line can be max_line_wrap_length = self.SCREEN_WIDTH - offset # Append all but the last column to formatted_line formatted_line = line[:offset] # Loop to split up line into multiple max_line_wrap_length pieces while len(line[offset:]) > max_line_wrap_length: # Find the nearest space to wrap the line at (so we don't split a word across two lines) split_offset = line[offset:offset+max_line_wrap_length].rfind(' ') # If there were no good places to split the line, just truncate it at max_line_wrap_length if split_offset < 1: split_offset = max_line_wrap_length self._append_to_data_parts(line, offset, offset+split_offset) offset += split_offset # Add any remaining data (guarunteed to be max_line_wrap_length long or shorter) to self.string_parts self._append_to_data_parts(line, offset, offset+len(line[offset:])) # Append self.string_parts to formatted_line; each part seperated by delim formatted_line += delim.join(self.string_parts) else: formatted_line = line return formatted_line def _configure_formatting(self): ''' Configures output formatting, and fitting output to the current terminal width. Returns None. ''' self.format_strings(self.DEFAULT_FORMAT, self.DEFAULT_FORMAT) if self.fit_to_screen: try: import fcntl import struct import termios # Get the terminal window width hw = struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')) self.SCREEN_WIDTH = self.HEADER_WIDTH = hw[1] except KeyboardInterrupt as e: raise e except Exception: pass binwalk-2.1.1/src/binwalk/core/idb.py000066400000000000000000000104151263655036500174340ustar00rootroot00000000000000# Special overrides/workarounds for running as an IDA plugin import io import os import logging class ShutUpHashlib(logging.Filter): ''' This is used to suppress hashlib exception messages if using the Python interpreter bundled with IDA. ''' def filter(self, record): return not record.getMessage().startswith("code for hash") try: import idc import idaapi LOADED_IN_IDA = True logger = logging.getLogger() logger.addFilter(ShutUpHashlib()) except ImportError: LOADED_IN_IDA = False def start_address(): return idaapi.get_first_seg().startEA def end_address(): last_ea = idc.BADADDR seg = idaapi.get_first_seg() while seg: last_ea = seg.endEA seg = idaapi.get_next_seg(last_ea) return last_ea class IDBFileIO(io.FileIO): ''' A custom class to override binwalk.core.common.Blockfile in order to read data directly out of the IDB, rather than reading from the original file on disk, which may or may not still exist. Requests to read from files that are not the current IDB are just forwarded up to io.FileIO. ''' def __init__(self, fname, mode): if idc.GetIdbPath() != fname: self.__idb__ = False super(IDBFileIO, self).__init__(fname, mode) else: self.__idb__ = True self.name = fname self.idb_start = 0 self.idb_pos = 0 self.idb_end = end_address() if self.args.size == 0: self.args.size = end_address() if self.args.offset == 0: self.args.offset = start_address() elif self.args.offset < 0: self.args.length = self.args.offset * -1 self.args.offset = end_address() + self.args.offset if self.args.length == 0 or self.args.length > (end_address() - start_address()): self.args.length = end_address() - start_address() def read(self, n=-1): if not self.__idb__: return super(IDBFileIO, self).read(n) else: data = '' read_count = 0 filler_count = 0 # Loop to read n bytes of data across IDB segments, filling # segment gaps with NULL bytes. while n and self.idb_pos <= self.idb_end: segment = idaapi.getseg(self.idb_pos) if not segment: filler_count += 1 self.idb_pos += 1 n -= 1 else: if filler_count: data += "\x00" * filler_count filler_count = 0 if (self.idb_pos + n) > segment.endEA: read_count = segment.endEA - self.idb_pos else: read_count = n try: data += idc.GetManyBytes(self.idb_pos, read_count) except TypeError as e: # This happens when trying to read from uninitialized segments (e.g., .bss) data += "\x00" * read_count n -= read_count self.idb_pos += read_count if filler_count: data += "\x00" * filler_count filler_count = 0 return data def write(self, data): if not self.__idb__: return super(IDBFileIO, self).write(data) else: # Don't actually write anything to the IDB, as, IMHO, # a binwalk plugin should never do this. But return the # number of bytes we were requested to write so that # any callers are happy. return len(data) def seek(self, n, whence=os.SEEK_SET): if not self.__idb__: return super(IDBFileIO, self).seek(n, whence) else: if whence == os.SEEK_SET: self.idb_pos = self.idb_start + n elif whence == os.SEEK_CUR: self.idb_pos += n elif whence == os.SEEK_END: self.idb_pos = self.idb_end + n def tell(self): if not self.__idb__: return super(IDBFileIO, self).tell() else: return self.idb_pos binwalk-2.1.1/src/binwalk/core/magic.py000066400000000000000000001115571263655036500177670ustar00rootroot00000000000000# A pure Python replacement for libmagic. Supports most libmagic features, plus # several additional features not provided by libmagic. Tailored specifically # for quickly searching blocks of data for multiple embedded signatures. __all__ = ['Magic'] import re import struct import datetime import binwalk.core.common import binwalk.core.compat class ParserException(Exception): ''' Exception thrown specifically for signature file parsing errors. ''' pass class SignatureResult(binwalk.core.module.Result): ''' Container class for signature results. ''' def __init__(self, **kwargs): # These are set by signature keyword tags. # Keyword tags can also set any other object attributes, # including those in binwalk.core.module.Result. self.jump = 0 self.many = False self.adjust = 0 self.strlen = 0 self.string = False self.invalid = False self.once = False self.overlap = False # These are set by code internally self.id = 0 # Kwargs overrides the defaults set above super(self.__class__, self).__init__(**kwargs) self.valid = (not self.invalid) class SignatureLine(object): ''' Responsible for parsing signature lines from magic signature files. ''' # Printed strings are truncated to this size MAX_STRING_SIZE = 128 def __init__(self, line): ''' Class constructor. Responsible for parsing a line from a signature file. @line - A line of text from the signature file. Returns None. ''' self.tags = {} self.text = line self.regex = False # Split the line on any white space; for this to work, backslash-escaped # spaces ('\ ') are replaced with their escaped hex value ('\x20'). # # [offset] [data type] [comparison value] [format string] # 0 belong 0x12345678 Foo file type, # >4 string x file name: %s, parts = line.replace('\\ ', '\\x20').split(None, 3) # Sanity check on the split line if len(parts) not in [3, 4]: raise ParserException("Invalid signature line: '%s'" % line) # The indentation level is determined by the number of '>' characters at # the beginning of the signature line. self.level = parts[0].count('>') # Get rid of the indentation characters and try to convert the remaining # characters to an integer offset. This will fail if the offset is a complex # value (e.g., '(4.l+16)'). self.offset = parts[0].replace('>', '') try: self.offset = int(self.offset, 0) except ValueError as e: pass # self.type is the specified data type ('belong', 'string', etc) self.type = parts[1] self.opvalue = None self.operator = None # Each data type can specify an additional operation to be performed on the # data being scanned before performing a comparison (e.g., 'belong&0xFF' will # AND the data with 0xFF before the comparison is performed). # # We support the following operators: for operator in ['&', '|', '*', '+', '-', '/', '~', '^']: # Look for each operator in self.type if operator in self.type: # If found, split self.type into the type and operator value (self.type, self.opvalue) = self.type.split(operator, 1) # Keep a record of the specified operator self.operator = operator # Try to convert the operator value into an integer. This works for # simple operator values, but not for complex types (e.g., '(4.l+12)'). try: self.opvalue = int(self.opvalue, 0) except ValueError as e: pass # Only one operator type is supported, so break as soon as one is found break # If the specified type starts with 'u' (e.g., 'ubelong'), then it is unsigned; else, it is signed if self.type[0] == 'u': self.signed = False self.type = self.type[1:] else: self.signed = True # Big endian values start with 'be' ('belong'), little endian values start with 'le' ('lelong'). # The struct module uses '>' to denote big endian and '<' to denote little endian. if self.type.startswith('be'): self.type = self.type[2:] self.endianess = '>' elif self.type.startswith('le'): self.endianess = '<' self.type = self.type[2:] # Assume big endian if no endianess was explicitly specified else: self.endianess = '>' # Check the comparison value for the type of comparison to be performed (e.g., # '=0x1234', '>0x1234', etc). If no operator is specified, '=' is implied. if parts[2][0] in ['=', '!', '>', '<', '&', '|', '^', '~']: self.condition = parts[2][0] self.value = parts[2][1:] else: self.condition = '=' self.value = parts[2] # If this is a wildcard value, explicitly set self.value to None if self.value == 'x': self.value = None # String values need to be decoded, as they may contain escape characters (e.g., '\x20') elif self.type == 'string': # String types support multiplication to easily match large repeating byte sequences if '*' in self.value: try: p = self.value.split('*') self.value = p[0] for n in p[1:]: self.value *= int(n, 0) except KeyboardInterrupt as e: raise e except Exception as e: raise ParserException("Failed to expand string '%s' with integer '%s' in line '%s'" % (self.value, n, line)) try: self.value = binwalk.core.compat.string_decode(self.value) except ValueError as e: raise ParserException("Failed to decode string value '%s' in line '%s'" % (self.value, line)) # If a regex was specified, compile it elif self.type == 'regex': self.regex = True try: self.value = re.compile(self.value) except KeyboardInterrupt as e: raise e except Exception as e: raise ParserException("Invalid regular expression '%s': %s" % (self.value, str(e))) # Non-string types are integer values else: try: self.value = int(self.value, 0) except ValueError as e: raise ParserException("Failed to convert value '%s' to an integer on line '%s'" % (self.value, line)) # Sanity check to make sure the first line of a signature has an explicit value if self.level == 0 and self.value is None: raise ParserException("First element of a signature must specify a non-wildcard value: '%s'" % (line)) # Set the size and struct format value for the specified data type. # This must be done, obviously, after the value has been parsed out above. if self.type == 'string': # Strings don't have a struct format value, since they don't have to be unpacked self.fmt = None # If a string type has a specific value, set the comparison size to the length of that string if self.value: self.size = len(self.value) # Else, truncate the string to self.MAX_STRING_SIZE else: self.size = self.MAX_STRING_SIZE elif self.type == 'regex': # Regular expressions don't have a struct format value, since they don't have to be unpacked self.fmt = None # The size of a matching regex is unknown until it is applied to some data self.size = self.MAX_STRING_SIZE elif self.type == 'byte': self.fmt = 'b' self.size = 1 elif self.type == 'short': self.fmt = 'h' self.size = 2 elif self.type == 'quad': self.fmt = 'q' self.size = 8 # Assume 4 byte length for all other data types else: self.fmt = 'i' self.size = 4 # The struct module uses the same characters for specifying signed and unsigned data types, # except that signed data types are upper case. The above if-else code sets self.fmt to the # lower case (unsigned) values. if not self.signed: self.fmt = self.fmt.upper() # If a struct format was identified, create a format string to be passed to struct.unpack # which specifies the endianess and data type format. if self.fmt: self.pkfmt = '%c%c' % (self.endianess, self.fmt) else: self.pkfmt = None # Check if a format string was specified (this is optional) if len(parts) == 4: # %lld formats are only supported if Python was built with HAVE_LONG_LONG self.format = parts[3].replace('%ll', '%l') # Regex to parse out tags, which are contained within curly braces retag = re.compile(r'\{.*?\}') # Parse out tag keywords from the format string for match in retag.finditer(self.format): # Get rid of the curly braces. tag = match.group().replace('{', '').replace('}', '') # If the tag specifies a value, it will be colon delimited (e.g., '{name:%s}') if ':' in tag: (n, v) = tag.split(':', 1) else: n = tag v = True # Create a new SignatureTag instance and append it to self.tags self.tags[n] = v # Remove all tags from the printable format string self.format = retag.sub('', self.format).strip() else: self.format = "" class Signature(object): ''' Class to hold signature data and generate signature regular expressions. ''' def __init__(self, id, first_line): ''' Class constructor. @id - A ID value to uniquely identify this signature. @first_line - The first SignatureLine of the signature (subsequent SignatureLines should be added via self.append). Returns None. ''' self.id = id self.lines = [first_line] self.title = first_line.format self.offset = first_line.offset self.regex = self._generate_regex(first_line) try: self.confidence = first_line.tags['confidence'] except KeyError: self.confidence = first_line.size def _generate_regex(self, line): ''' Generates a regular expression from the magic bytes of a signature. The regex is used by Magic._analyze. @line - The first SignatureLine object of the signature. Returns a compile regular expression. ''' restr = "" # Strings and single byte signatures are taken at face value; # multi-byte integer values are turned into regex strings based # on their data type size and endianess. if line.type == 'regex': # Regex types are already compiled expressions. # Note that since re.finditer is used, unless the specified # regex accounts for it, overlapping signatures will be ignored. return line.value if line.type == 'string': restr = line.value elif line.size == 1: restr = chr(line.value) elif line.size == 2: if line.endianess == '<': restr = chr(line.value & 0xFF) + chr(line.value >> 8) elif line.endianess == '>': restr = chr(line.value >> 8) + chr(line.value & 0xFF) elif line.size == 4: if line.endianess == '<': restr = (chr(line.value & 0xFF) + chr((line.value >> 8) & 0xFF) + chr((line.value >> 16) & 0xFF) + chr(line.value >> 24)) elif line.endianess == '>': restr = (chr(line.value >> 24) + chr((line.value >> 16) & 0xFF) + chr((line.value >> 8) & 0xFF) + chr(line.value & 0xFF)) elif line.size == 8: if line.endianess == '<': restr = (chr(line.value & 0xFF) + chr((line.value >> 8) & 0xFF) + chr((line.value >> 16) & 0xFF) + chr((line.value >> 24) & 0xFF) + chr((line.value >> 32) & 0xFF) + chr((line.value >> 40) & 0xFF) + chr((line.value >> 48) & 0xFF) + chr(line.value >> 56)) elif line.endianess == '>': restr = (chr(line.value >> 56) + chr((line.value >> 48) & 0xFF) + chr((line.value >> 40) & 0xFF) + chr((line.value >> 32) & 0xFF) + chr((line.value >> 24) & 0xFF) + chr((line.value >> 16) & 0xFF) + chr((line.value >> 8) & 0xFF) + chr(line.value & 0xFF)) # Since re.finditer is used on a per-signature basis, signatures should be crafted carefully # to ensure that they aren't potentially self-overlapping (e.g., a signature of "ABCDAB" could # be confused by the byte sequence "ABCDABCDAB"). The longer the signature, the less likely an # unintentional overlap is, although files could still be maliciously crafted to cause false # negative results. # # Thus, unless a signature has been explicitly marked as knowingly overlapping ('{overlap}'), # spit out a warning about any self-overlapping signatures. if not binwalk.core.compat.has_key(line.tags, 'overlap'): for i in range(1, line.size): if restr[i:] == restr[0:(line.size-i)]: binwalk.core.common.warning("Signature '%s' is a self-overlapping signature!" % line.text) break return re.compile(re.escape(restr)) def append(self, line): ''' Add a new SignatureLine object to the signature. @line - A new SignatureLine instance. Returns None. ''' # This method is kind of useless, but may be a nice wrapper for future code. self.lines.append(line) class Magic(object): ''' Primary class for loading signature files and scanning blocks of arbitrary data for matching signatures. ''' def __init__(self, exclude=[], include=[], invalid=False): ''' Class constructor. @include - A list of regex strings describing which signatures should be included in the scan results. @exclude - A list of regex strings describing which signatures should not be included in the scan results. @invalid - If set to True, invalid results will not be ignored. Returns None. ''' # Used to save the block of data passed to self.scan (see additional comments in self.scan) self.data = "" # A list of Signature class objects, populated by self.parse (see also: self.load) self.signatures = [] # A set of signatures with the 'once' keyword that have already been displayed once self.display_once = set() self.dirty = True self.show_invalid = invalid self.includes = [re.compile(x) for x in include] self.excludes = [re.compile(x) for x in exclude] # Regex rule to replace backspace characters (an the preceeding character) # in formatted signature strings (see self._analyze). self.bspace = re.compile(".\\\\b") # Regex rule to match printable ASCII characters in formatted signature # strings (see self._analyze). self.printable = re.compile("[ -~]*") # Regex rule to find format strings self.fmtstr = re.compile("%[^%]") # Regex rule to find periods (see self._do_math) self.period = re.compile("\.") def _filtered(self, text): ''' Tests if a string should be filtered out or not. @text - The string to check against filter rules. Returns True if the string should be filtered out, i.e., not displayed. Returns False if the string should be displayed. ''' filtered = None # Text is converted to lower case first, partially for historical # purposes, but also because it simplifies writing filter rules # (e.g., don't have to worry about case sensitivity). text = text.lower() for include in self.includes: if include.search(text): filtered = False break # If exclusive include filters have been specified and did # not match the text, then the text should be filtered out. if self.includes and filtered == None: return True for exclude in self.excludes: if exclude.search(text): filtered = True break # If no explicit exclude filters were matched, then the # text should *not* be filtered. if filtered == None: filtered = False return filtered def _do_math(self, offset, expression): ''' Parses and evaluates complex expressions, e.g., "(4.l+12)", "(6*32)", etc. @offset - The offset inside self.data that the current signature starts at. @expressions - The expression to evaluate. Returns an integer value that is the result of the evaluated expression. ''' # Does the expression contain an offset (e.g., "(4.l+12)")? if '.' in expression and '(' in expression: replacements = {} for period in [match.start() for match in self.period.finditer(expression)]: # Separate the offset field into the integer offset and type values (o and t respsectively) s = expression[:period].rfind('(') + 1 # The offset address may be an evaluatable expression, such as '(4+0.L)', typically the result # of the original offset being something like '(&0.L)'. o = binwalk.core.common.MathExpression(expression[s:period]).value t = expression[period+1] # Re-build just the parsed offset portion of the expression text = "%s.%c" % (expression[s:period], t) # Have we already evaluated this offset expression? If so, skip it. if binwalk.core.common.has_key(replacements, text): continue # The offset specified in the expression is relative to the starting offset inside self.data o += offset # Read the value from self.data at the specified offset try: # Big and little endian byte format if t in ['b', 'B']: v = struct.unpack('b', binwalk.core.compat.str2bytes(self.data[o:o+1]))[0] # Little endian short format elif t == 's': v = struct.unpack('h', binwalk.core.compat.str2bytes(self.data[o:o+2]))[0] # Bit endian long format elif t == 'L': v = struct.unpack('>i', binwalk.core.compat.str2bytes(self.data[o:o+4]))[0] # struct.error is thrown if there is not enough bytes in self.data for the specified format type except struct.error as e: v = 0 # Keep track of all the recovered values from self.data replacements[text] = v # Finally, replace all offset expressions with their corresponding text value v = expression for (text, value) in binwalk.core.common.iterator(replacements): v = v.replace(text, "%d" % value) # If no offset, then it's just an evaluatable math expression (e.g., "(32+0x20)") else: v = expression # Evaluate the final expression value = binwalk.core.common.MathExpression(v).value return value def _analyze(self, signature, offset): ''' Analyzes self.data for the specified signature data at the specified offset . @signature - The signature to apply to the data. @offset - The offset in self.data to apply the signature to. Returns a dictionary of tags parsed from the data. ''' description = [] tag_strlen = None max_line_level = 0 previous_line_end = 0 tags = {'id' : signature.id, 'offset' : offset, 'invalid' : False, 'once' : False} # Apply each line of the signature to self.data, starting at the specified offset for n in range(0, len(signature.lines)): line = signature.lines[n] # Ignore indentation levels above the current max indent level if line.level <= max_line_level: # If the relative offset of this signature line is just an integer value, use it if isinstance(line.offset, int): line_offset = line.offset # Else, evaluate the complex expression else: # Format the previous_line_end value into a string. Add the '+' sign to explicitly # state that this value is to be added to any subsequent values in the expression # (e.g., '&0' becomes '4+0'). ple = '%d+' % previous_line_end # Allow users to use either the '&0' (libmagic) or '&+0' (explcit addition) sytaxes; # replace both with the ple text. line_offset_text = line.offset.replace('&+', ple).replace('&', ple) # Evaluate the expression line_offset = self._do_math(offset, line_offset_text) # Sanity check if not isinstance(line_offset, int): raise ParserException("Failed to convert offset '%s' to a number: '%s'" % (line.offset, line.text)) # The start of the data needed by this line is at offset + line_offset. # The end of the data will be line.size bytes later. start = offset + line_offset end = start + line.size # If the line has a packed format string, unpack it if line.pkfmt: try: dvalue = struct.unpack(line.pkfmt, binwalk.core.compat.str2bytes(self.data[start:end]))[0] # Not enough bytes left in self.data for the specified format size except struct.error as e: dvalue = 0 # Else, this is a string else: # Wildcard strings have line.value == None if line.value is None: # Check to see if this is a string whose size is known and has been specified on a previous # signature line. if binwalk.core.compat.has_key(tags, 'strlen') and binwalk.core.compat.has_key(line.tags, 'string'): dvalue = self.data[start:(start+tags['strlen'])] # Else, just terminate the string at the first newline, carriage return, or NULL byte else: dvalue = self.data[start:end].split('\x00')[0].split('\r')[0].split('\n')[0] # Non-wildcard strings have a known length, specified in the signature line else: dvalue = self.data[start:end] # Some integer values have special operations that need to be performed on them # before comparison (e.g., "belong&0x0000FFFF"). Complex math expressions are # supported here as well. #if isinstance(dvalue, int) and line.operator: if line.operator: try: # If the operator value of this signature line is just an integer value, use it if isinstance(line.opvalue, int) or isinstance(line.opvalue, long): opval = line.opvalue # Else, evaluate the complex expression else: opval = self._do_math(offset, line.opvalue) # Perform the specified operation if line.operator == '&': dvalue &= opval elif line.operator == '|': dvalue |= opval elif line.operator == '*': dvalue *= opval elif line.operator == '+': dvalue += opval elif line.operator == '-': dvalue -= opval elif line.operator == '/': dvalue /= opval elif line.operator == '~': dvalue = ~opval elif line.operator == '^': dvalue ^= opval except KeyboardInterrupt as e: raise e except Exception as e: raise ParserException("Operation '" + str(dvalue) + " " + str(line.operator) + "= " + str(line.opvalue) + "' failed: " + str(e)) # Does the data (dvalue) match the specified comparison? if ((line.value is None) or (line.regex and line.value.match(dvalue)) or (line.condition == '=' and dvalue == line.value) or (line.condition == '>' and dvalue > line.value) or (line.condition == '<' and dvalue < line.value) or (line.condition == '!' and dvalue != line.value) or (line.condition == '~' and (dvalue == ~line.value)) or (line.condition == '^' and (dvalue ^ line.value)) or (line.condition == '&' and (dvalue & line.value)) or (line.condition == '|' and (dvalue | line.value))): # Up until this point, date fields are treated as integer values, # but we want to display them as nicely formatted strings. if line.type == 'date': ts = datetime.datetime.utcfromtimestamp(dvalue) dvalue = ts.strftime("%Y-%m-%d %H:%M:%S") # Generate the tuple for the format string dvalue_tuple = () for x in self.fmtstr.finditer(line.format): dvalue_tuple += (dvalue,) # Format the description string desc = line.format % dvalue_tuple # If there was any description string, append it to the list of description string parts if desc: description.append(desc) # Process tag keywords specified in the signature line. These have already been parsed out of the # original format string so that they can be processed separately from the printed description string. for (tag_name, tag_value) in binwalk.core.compat.iterator(line.tags): # If the tag value is a string, try to format it if isinstance(tag_value, str): # Generate the tuple for the format string dvalue_tuple = () for x in self.fmtstr.finditer(tag_value): dvalue_tuple += (dvalue,) # Format the tag string tags[tag_name] = tag_value % dvalue_tuple # Else, just use the raw tag value else: tags[tag_name] = tag_value # Some tag values are intended to be integer values, so try to convert them as such try: tags[tag_name] = int(tags[tag_name], 0) except KeyboardInterrupt as e: raise e except Exception as e: pass # Abort processing soon as this signature is marked invalid, unless invalid results # were explicitly requested. This means that the sooner invalid checks are made in a # given signature, the faster the scan can filter out false positives. if not self.show_invalid and tags['invalid']: break # Look ahead to the next line in the signature; if its indent level is greater than # that of the current line, then track the end of data for the current line. This is # so that subsequent lines can use the '>>&0' offset syntax to specify relative offsets # from previous lines. try: next_line = signature.lines[n+1] if next_line.level > line.level: if line.type == 'string': previous_line_end = line_offset + len(dvalue) else: previous_line_end = line_offset + line.size except IndexError as e: pass # If this line satisfied its comparison, +1 the max indentation level max_line_level = line.level + 1 else: # No match on the first line, abort if line.level == 0: break else: # If this line did not satisfy its comparison, then higher # indentation levels will not be accepted. max_line_level = line.level # Join the formatted description strings and remove backspace characters (plus the preceeding character as well) tags['description'] = self.bspace.sub('', " ".join(description)) # This should never happen if not tags['description']: tags['display'] = False tags['invalid'] = True # If the formatted string contains non-printable characters, consider it invalid if self.printable.match(tags['description']).group() != tags['description']: tags['invalid'] = True return tags def match(self, data): ''' Match the beginning of a data buffer to a signature. @data - The data buffer to match against the loaded signature list. Returns a list of SignatureResult objects. ''' return self.scan(data, 1) def scan(self, data, dlen=None): ''' Scan a data block for matching signatures. @data - A string of data to scan. @dlen - If specified, signatures at offsets larger than dlen will be ignored. Returns a list of SignatureResult objects. ''' results = [] matched_offsets = set() # Since data can potentially be quite a large string, make it available to other # methods via a class attribute so that it doesn't need to be passed around to # different methods over and over again. self.data = data # If dlen wasn't specified, search all of self.data if dlen is None: dlen = len(data) for signature in self.signatures: # Use regex to search the data block for potential signature matches (fast) for match in signature.regex.finditer(data): # Take the offset of the start of the signature into account offset = match.start() - signature.offset # Signatures are ordered based on the length of their magic bytes (largest first). # If this offset has already been matched to a previous signature, ignore it unless # self.show_invalid has been specified. Also ignore obviously invalid offsets (<1) # as well as those outside the specified self.data range (dlen). if (offset not in matched_offsets or self.show_invalid) and offset >= 0 and offset < dlen: #if offset >= 0 and offset < dlen: # Analyze the data at this offset using the current signature rule tags = self._analyze(signature, offset) # Generate a SignatureResult object and append it to the results list if the # signature is valid, or if invalid results were requested. if (not tags['invalid'] or self.show_invalid) and not self._filtered(tags['description']): # Only display results with the 'once' tag once. if tags['once']: if signature.title in self.display_once: continue else: self.display_once.add(signature.title) # Append the result to the results list results.append(SignatureResult(**tags)) # Add this offset to the matched_offsets set, so that it can be ignored by # subsequent loops. matched_offsets.add(offset) # Sort results by offset results.sort(key=lambda x: x.offset, reverse=False) return results def load(self, fname): ''' Load signatures from a file. @fname - Path to signature file. Returns None. ''' # Magic files must be ASCII, else encoding issues can arise. fp = open(fname, "r") lines = fp.readlines() self.parse(lines) fp.close() def parse(self, lines): ''' Parse signature file lines. @lines - A list of lines from a signature file. Returns None. ''' signature = None for line in lines: # Split at the first comment delimiter (if any) and strip the result line = line.split('#')[0].strip() # Ignore blank lines and lines that are nothing but comments. # We also don't support the '!mime' style line entries. if line and line[0] != '!': # Parse this signature line sigline = SignatureLine(line) # Level 0 means the first line of a signature entry if sigline.level == 0: # If there is an existing signature, append it to the signature list, # unless the text in its title field has been filtered by user-defined # filter rules. if signature and not self._filtered(signature.title): self.signatures.append(signature) # Create a new signature object; use the size of self.signatures to # assign each signature a unique ID. signature = Signature(len(self.signatures), sigline) # Else, just append this line to the existing signature elif signature: #signature.append(sigline) signature.lines.append(sigline) # If this is not the first line of a signature entry and there is no other # existing signature entry, something is very wrong with the signature file. else: raise ParserException("Invalid signature line: '%s'" % line) # Add the final signature to the signature list if signature: if not self._filtered(signature.lines[0].format): self.signatures.append(signature) # Sort signatures by confidence (aka, length of their magic bytes), largest first self.signatures.sort(key=lambda x: x.confidence, reverse=True) binwalk-2.1.1/src/binwalk/core/module.py000066400000000000000000000774531263655036500202020ustar00rootroot00000000000000# Core code relating to binwalk modules and supporting classes. # In particular, the Module class (base class for all binwalk modules) # and the Modules class (main class for managing and executing binwalk modules) # are most critical. import io import os import sys import time import inspect import argparse import traceback import binwalk.core.statuserver import binwalk.core.common import binwalk.core.settings import binwalk.core.plugin from threading import Thread from binwalk.core.compat import * class Option(object): ''' A container class that allows modules to declare command line options. ''' def __init__(self, kwargs={}, priority=0, description="", short="", long="", type=None, dtype=None, hidden=False): ''' Class constructor. @kwargs - A dictionary of kwarg key-value pairs affected by this command line option. @priority - A value from 0 to 100. Higher priorities will override kwarg values set by lower priority options. @description - A description to be displayed in the help output. @short - The short option to use (optional). @long - The long option to use (if None, this option will not be displayed in help output). @type - The accepted data type (one of: io.FileIO/argparse.FileType/binwalk.core.common.BlockFile, list, str, int, float). @dtype - The displayed accepted type string, to be shown in help output. @hidden - If set to True, this option will not be displayed in the help output. Returns None. ''' self.kwargs = kwargs self.priority = priority self.description = description self.short = short self.long = long self.type = type self.dtype = dtype self.hidden = hidden if not self.dtype and self.type: if self.type in [io.FileIO, argparse.FileType, binwalk.core.common.BlockFile]: self.dtype = 'file' elif self.type in [int, float, str]: self.dtype = self.type.__name__ else: self.type = str self.dtype = str.__name__ def convert(self, value, default_value): if self.type and (self.type.__name__ == self.dtype): # Be sure to specify a base of 0 for int() so that the base is auto-detected if self.type == int: t = self.type(value, 0) else: t = self.type(value) elif default_value or default_value is False: t = default_value else: t = value return t class Kwarg(object): ''' A container class allowing modules to specify their expected __init__ kwarg(s). ''' def __init__(self, name="", default=None, description=""): ''' Class constructor. @name - Kwarg name. @default - Default kwarg value. @description - Description string. Return None. ''' self.name = name self.default = default self.description = description class Dependency(object): ''' A container class for declaring module dependencies. ''' def __init__(self, attribute="", name="", kwargs={}): self.attribute = attribute self.name = name self.kwargs = kwargs self.module = None class Result(object): ''' Generic class for storing and accessing scan results. ''' def __init__(self, **kwargs): ''' Class constructor. @offset - The file offset of the result. @size - Size of the result, if known. @description - The result description, as displayed to the user. @module - Name of the module that generated the result. @file - The file object of the scanned file. @valid - Set to True if the result if value, False if invalid. @display - Set to True to display the result to the user, False to hide it. @extract - Set to True to flag this result for extraction. @plot - Set to Flase to exclude this result from entropy plots. @name - Name of the result found (None if not applicable or unknown). Provide additional kwargs as necessary. Returns None. ''' self.offset = 0 self.size = 0 self.description = '' self.module = '' self.file = None self.valid = True self.display = True self.extract = True self.plot = True self.name = None for (k, v) in iterator(kwargs): setattr(self, k, v) class Error(Result): ''' A subclass of binwalk.core.module.Result. ''' def __init__(self, **kwargs): ''' Accepts all the same kwargs as binwalk.core.module.Result, but the following are also added: @exception - In case of an exception, this is the exception object. Returns None. ''' self.exception = None Result.__init__(self, **kwargs) class Module(object): ''' All module classes must be subclassed from this. ''' # The module title, as displayed in help output TITLE = "" # A list of binwalk.core.module.Option command line options CLI = [] # A list of binwalk.core.module.Kwargs accepted by __init__ KWARGS = [] # A list of default dependencies for all modules; do not override this unless you # understand the consequences of doing so. DEFAULT_DEPENDS = [ Dependency(name='General', attribute='config'), Dependency(name='Extractor', attribute='extractor'), ] # A list of binwalk.core.module.Dependency instances that can be filled in as needed by each individual module. DEPENDS = [] # Format string for printing the header during a scan. # Must be set prior to calling self.header. HEADER_FORMAT = "%-12s %-12s %s\n" # Format string for printing each result during a scan. # Must be set prior to calling self.result. RESULT_FORMAT = "%-12d 0x%-12X %s\n" # Format string for printing custom information in the verbose header output. # Must be set prior to calling self.header. VERBOSE_FORMAT = "" # The header to print during a scan. # Set to None to not print a header. # Note that this will be formatted per the HEADER_FORMAT format string. # Must be set prior to calling self.header. HEADER = ["DECIMAL", "HEXADECIMAL", "DESCRIPTION"] # The Result attribute names to print during a scan, as provided to the self.results method. # Set to None to not print any results. # Note that these will be formatted per the RESULT_FORMAT format string. # Must be set prior to calling self.result. RESULT = ["offset", "offset", "description"] # The custom data to print in the verbose header output. # Note that these will be formatted per the VERBOSE_FORMAT format string. # Must be set prior to calling self.header. VERBOSE = [] # If set to True, the progress status will be automatically updated for each result # containing valid file and offset attributes. AUTO_UPDATE_STATUS = True # Modules with higher priorities are executed first PRIORITY = 5 # Modules with a higher order are displayed first in help output ORDER = 5 # Set to False if this is not a primary module (e.g., General, Extractor modules) PRIMARY = True def __init__(self, parent, **kwargs): self.errors = [] self.results = [] self.parent = parent self.target_file_list = [] self.status = None self.enabled = False self.previous_next_file_fp = None self.current_target_file_name = None self.name = self.__class__.__name__ self.plugins = binwalk.core.plugin.Plugins(self) self.dependencies = self.DEFAULT_DEPENDS + self.DEPENDS process_kwargs(self, kwargs) self.plugins.load_plugins() try: self.load() except KeyboardInterrupt as e: raise e except Exception as e: self.error(exception=e) try: self.target_file_list = list(self.config.target_files) except AttributeError as e: pass def __enter__(self): return self def __exit__(self, x, z, y): return None def load(self): ''' Invoked at module load time. May be overridden by the module sub-class. ''' return None def unload(self): ''' Invoked at module load time. May be overridden by the module sub-class. ''' return None def reset(self): ''' Invoked only for dependency modules immediately prior to starting a new primary module. ''' return None def init(self): ''' Invoked prior to self.run. May be overridden by the module sub-class. Returns None. ''' return None def run(self): ''' Executes the main module routine. Must be overridden by the module sub-class. Returns True on success, False on failure. ''' return False def callback(self, r): ''' Processes the result from all modules. Called for all dependency modules when a valid result is found. @r - The result, an instance of binwalk.core.module.Result. Returns None. ''' return None def validate(self, r): ''' Validates the result. May be overridden by the module sub-class. @r - The result, an instance of binwalk.core.module.Result. Returns None. ''' r.valid = True return None def _plugins_pre_scan(self): self.plugins.pre_scan_callbacks(self) def _plugins_new_file(self, fp): self.plugins.new_file_callbacks(fp) def _plugins_post_scan(self): self.plugins.post_scan_callbacks(self) def _plugins_result(self, r): self.plugins.scan_callbacks(r) def _build_display_args(self, r): args = [] if self.RESULT: if type(self.RESULT) != type([]): result = [self.RESULT] else: result = self.RESULT for name in result: value = getattr(r, name) # Displayed offsets should be offset by the base address if name == 'offset': value += self.config.base args.append(value) return args def _unload_dependencies(self): # Calls the unload method for all dependency modules. # These modules cannot be unloaded immediately after being run, as # they must persist until the module that depends on them is finished. # As such, this must be done separately from the Modules.run 'unload' call. for dependency in self.dependencies: try: getattr(self, dependency.attribute).unload() except AttributeError: continue def next_file(self, close_previous=True): ''' Gets the next file to be scanned (including pending extracted files, if applicable). Also re/initializes self.status. All modules should access the target file list through this method. ''' fp = None # Ensure files are close to prevent IOError (too many open files) if close_previous: try: self.previous_next_file_fp.close() except KeyboardInterrupt as e: raise e except Exception: pass # Add any pending extracted files to the target_files list and reset the extractor's pending file list self.target_file_list += self.extractor.pending # Reset all dependencies prior to continuing with another file. # This is particularly important for the extractor module, which must be reset # in order to reset its base output directory path for each file, and the # list of pending files. self.reset_dependencies() while self.target_file_list: next_target_file = self.target_file_list.pop(0) # Values in self.target_file_list are either already open files (BlockFile instances), or paths # to files that need to be opened for scanning. if isinstance(next_target_file, str): fp = self.config.open_file(next_target_file) else: fp = next_target_file if not fp: break else: if self.config.file_name_filter(fp) == False: fp.close() fp = None continue else: self.status.clear() self.status.total = fp.length break if fp is not None: self.current_target_file_name = fp.path self.status.fp = fp else: self.current_target_file_name = None self.status.fp = None self.previous_next_file_fp = fp self._plugins_new_file(fp) return fp def clear(self, results=True, errors=True): ''' Clears results and errors lists. ''' if results: self.results = [] if errors: self.errors = [] def result(self, r=None, **kwargs): ''' Validates a result, stores it in self.results and prints it. Accepts the same kwargs as the binwalk.core.module.Result class. @r - An existing instance of binwalk.core.module.Result. Returns an instance of binwalk.core.module.Result. ''' if r is None: r = Result(**kwargs) # Add the name of the current module to the result r.module = self.__class__.__name__ # Any module that is reporting results, valid or not, should be marked as enabled if not self.enabled: self.enabled = True self.validate(r) self._plugins_result(r) # Update the progress status automatically if it is not being done manually by the module if r.offset and r.file and self.AUTO_UPDATE_STATUS: self.status.total = r.file.length self.status.completed = r.offset self.status.fp = r.file for dependency in self.dependencies: try: getattr(self, dependency.attribute).callback(r) except AttributeError: continue if r.valid: self.results.append(r) if r.display: display_args = self._build_display_args(r) if display_args: self.config.display.format_strings(self.HEADER_FORMAT, self.RESULT_FORMAT) self.config.display.result(*display_args) return r def error(self, **kwargs): ''' Stores the specified error in self.errors. Accepts the same kwargs as the binwalk.core.module.Error class. Returns None. ''' exception_header_width = 100 e = Error(**kwargs) e.module = self.__class__.__name__ self.errors.append(e) if e.exception: sys.stderr.write("\n" + e.module + " Exception: " + str(e.exception) + "\n") sys.stderr.write("-" * exception_header_width + "\n") traceback.print_exc(file=sys.stderr) sys.stderr.write("-" * exception_header_width + "\n\n") elif e.description: sys.stderr.write("\n" + e.module + " Error: " + e.description + "\n\n") def header(self): ''' Displays the scan header, as defined by self.HEADER and self.HEADER_FORMAT. Returns None. ''' self.config.display.format_strings(self.HEADER_FORMAT, self.RESULT_FORMAT) self.config.display.add_custom_header(self.VERBOSE_FORMAT, self.VERBOSE) if type(self.HEADER) == type([]): self.config.display.header(*self.HEADER, file_name=self.current_target_file_name) elif self.HEADER: self.config.display.header(self.HEADER, file_name=self.current_target_file_name) def footer(self): ''' Displays the scan footer. Returns None. ''' self.config.display.footer() def reset_dependencies(self): # Reset all dependency modules for dependency in self.dependencies: if hasattr(self, dependency.attribute): getattr(self, dependency.attribute).reset() def main(self): ''' Responsible for calling self.init, initializing self.config.display, and calling self.run. Returns the value returned from self.run. ''' self.status = self.parent.status self.modules = self.parent.executed_modules # A special exception for the extractor module, which should be allowed to # override the verbose setting, e.g., if --matryoshka has been specified if hasattr(self, "extractor") and self.extractor.config.verbose: self.config.verbose = self.config.display.verbose = True if not self.config.files: binwalk.core.common.debug("No target files specified, module %s terminated" % self.name) return False self.reset_dependencies() try: self.init() except KeyboardInterrupt as e: raise e except Exception as e: self.error(exception=e) return False try: self.config.display.format_strings(self.HEADER_FORMAT, self.RESULT_FORMAT) except KeyboardInterrupt as e: raise e except Exception as e: self.error(exception=e) return False self._plugins_pre_scan() try: retval = self.run() except KeyboardInterrupt as e: raise e except Exception as e: self.error(exception=e) return False self._plugins_post_scan() return retval class Status(object): ''' Class used for tracking module status (e.g., % complete). ''' def __init__(self, **kwargs): self.kwargs = kwargs self.clear() def clear(self): for (k,v) in iterator(self.kwargs): setattr(self, k, v) class ModuleException(Exception): ''' Module exception class. Nothing special here except the name. ''' pass class Modules(object): ''' Main class used for running and managing modules. ''' def __init__(self, *argv, **kargv): ''' Class constructor. @argv - List of command line options. Must not include the program name (e.g., sys.argv[1:]). @kargv - Keyword dictionary of command line options. Returns None. ''' self.arguments = [] self.executed_modules = {} self.default_dependency_modules = {} self.status = Status(completed=0, total=0, fp=None, running=False, shutdown=False, finished=False) self.status_server_started = False self.status_service = None self._set_arguments(list(argv), kargv) def cleanup(self): if self.status_service: self.status_service.server.socket.shutdown(1) self.status_service.server.socket.close() def __enter__(self): return self def __exit__(self, t, v, b): self.cleanup() def _set_arguments(self, argv=[], kargv={}): for (k,v) in iterator(kargv): k = self._parse_api_opt(k) if v not in [True, False, None]: if not isinstance(v, list): v = [v] for value in v: if not isinstance(value, str): value = str(bytes2str(value)) argv.append(k) argv.append(value) else: argv.append(k) if not argv and not self.arguments: self.arguments = sys.argv[1:] elif argv: self.arguments = argv def _parse_api_opt(self, opt): # If the argument already starts with a hyphen, don't add hyphens in front of it if opt.startswith('-'): return opt # Short options are only 1 character elif len(opt) == 1: return '-' + opt else: return '--' + opt def list(self, attribute="run"): ''' Finds all modules with the specified attribute. @attribute - The desired module attribute. Returns a list of modules that contain the specified attribute, in the order they should be executed. ''' import binwalk.modules modules = {} for (name, module) in inspect.getmembers(binwalk.modules): if inspect.isclass(module) and hasattr(module, attribute): modules[module] = module.PRIORITY return sorted(modules, key=modules.get, reverse=True) def help(self): ''' Generates formatted help output. Returns the help string. ''' modules = {} help_string = "\nBinwalk v%s\nCraig Heffner, http://www.binwalk.org\n" % binwalk.core.settings.Settings.VERSION help_string += "\nUsage: binwalk [OPTIONS] [FILE1] [FILE2] [FILE3] ...\n" # Build a dictionary of modules and their ORDER attributes. # This makes it easy to sort modules by their ORDER attribute for display. for module in self.list(attribute="CLI"): if module.CLI: modules[module] = module.ORDER for module in sorted(modules, key=modules.get, reverse=True): help_string += "\n%s Options:\n" % module.TITLE for module_option in module.CLI: if module_option.long and not module_option.hidden: long_opt = '--' + module_option.long if module_option.dtype: optargs = "=<%s>" % module_option.dtype else: optargs = "" if module_option.short: short_opt = "-" + module_option.short + "," else: short_opt = " " fmt = " %%s %%s%%-%ds%%s\n" % (25-len(long_opt)) help_string += fmt % (short_opt, long_opt, optargs, module_option.description) return help_string + "\n" def execute(self, *args, **kwargs): ''' Executes all appropriate modules according to the options specified in args/kwargs. Returns a list of executed module objects. ''' run_modules = [] orig_arguments = self.arguments if args or kwargs: self._set_arguments(list(args), kwargs) # Run all modules for module in self.list(): obj = self.run(module) # Add all loaded modules that marked themselves as enabled to the run_modules list for (module, obj) in iterator(self.executed_modules): # Report the results if the module is enabled and if it is a primary module or if it reported any results/errors if obj.enabled and (obj.PRIMARY or obj.results or obj.errors): run_modules.append(obj) self.arguments = orig_arguments return run_modules def run(self, module, dependency=False, kwargs={}): ''' Runs a specific module. ''' try: obj = self.load(module, kwargs) if isinstance(obj, binwalk.core.module.Module) and obj.enabled: obj.main() self.status.clear() # If the module is not being loaded as a dependency, add it to the executed modules dictionary. # This is used later in self.execute to determine which objects should be returned. if not dependency: self.executed_modules[module] = obj # The unload method tells the module that we're done with it, and gives it a chance to do # any cleanup operations that may be necessary. We still retain the object instance in self.executed_modules. obj._unload_dependencies() obj.unload() except KeyboardInterrupt as e: # Tell the status server to shut down, and give it time to clean up. if self.status.running: self.status.shutdown = True while not self.status.finished: time.sleep(0.1) raise e return obj def load(self, module, kwargs={}): argv = self.argv(module, argv=self.arguments) argv.update(kwargs) argv.update(self.dependencies(module, argv['enabled'])) return module(self, **argv) def dependencies(self, module, module_enabled): import binwalk.modules attributes = {} for dependency in module.DEFAULT_DEPENDS+module.DEPENDS: # The dependency module must be imported by binwalk.modules.__init__.py if hasattr(binwalk.modules, dependency.name): dependency.module = getattr(binwalk.modules, dependency.name) else: raise ModuleException("%s depends on %s which was not found in binwalk.modules.__init__.py\n" % (str(module), dependency.name)) # No recursive dependencies, thanks if dependency.module == module: continue # Only load dependencies with custom kwargs from modules that are enabled, else madness ensues. # Example: Heursitic module depends on entropy module, and sets entropy kwargs to contain 'enabled' : True. # Without this check, an entropy scan would always be run, even if -H or -E weren't specified! # # Modules that are not enabled (e.g., extraction module) can load any dependency as long as they don't # set any custom kwargs for those dependencies. if module_enabled or not dependency.kwargs: depobj = self.run(dependency.module, dependency=True, kwargs=dependency.kwargs) # If a dependency failed, consider this a non-recoverable error and raise an exception if depobj.errors: raise ModuleException("Failed to load " + dependency.name + " module") else: attributes[dependency.attribute] = depobj return attributes def argv(self, module, argv=sys.argv[1:]): ''' Processes argv for any options specific to the specified module. @module - The module to process argv for. @argv - A list of command line arguments (excluding argv[0]). Returns a dictionary of kwargs for the specified module. ''' kwargs = {'enabled' : False} last_priority = {} longs = [] shorts = "" parser = argparse.ArgumentParser(add_help=False) # Hack: This allows the ListActionParser class to correllate short options to long options. # There is probably a built-in way to do this in the argparse.ArgumentParser class? parser.short_to_long = {} # Must build arguments from all modules so that: # # 1) Any conflicting arguments will raise an exception # 2) The only unknown arguments will be the target files, making them easy to identify for m in self.list(attribute="CLI"): for module_option in m.CLI: parser_args = [] parser_kwargs = {} if not module_option.long: continue if module_option.short: parser_args.append('-' + module_option.short) parser_args.append('--' + module_option.long) parser_kwargs['dest'] = module_option.long if module_option.type is None: parser_kwargs['action'] = 'store_true' elif module_option.type is list: parser_kwargs['action'] = 'append' parser.short_to_long[module_option.short] = module_option.long parser.add_argument(*parser_args, **parser_kwargs) args, unknown = parser.parse_known_args(argv) args = args.__dict__ # Only add parsed options pertinent to the requested module for module_option in module.CLI: if module_option.type == binwalk.core.common.BlockFile: for k in get_keys(module_option.kwargs): kwargs[k] = [] for unk in unknown: kwargs[k].append(unk) elif has_key(args, module_option.long) and args[module_option.long] not in [None, False]: # Loop through all the kwargs for this command line option for (name, default_value) in iterator(module_option.kwargs): # If this kwarg has not been previously processed, or if its priority is equal to or # greater than the previously processed kwarg's priority, then let's process it. if not has_key(last_priority, name) or last_priority[name] <= module_option.priority: # Track the priority for future iterations that may process the same kwarg name last_priority[name] = module_option.priority try: kwargs[name] = module_option.convert(args[module_option.long], default_value) except KeyboardInterrupt as e: raise e except Exception as e: raise ModuleException("Invalid usage: %s" % str(e)) binwalk.core.common.debug("%s :: %s => %s" % (module.TITLE, str(argv), str(kwargs))) return kwargs def kwargs(self, obj, kwargs): ''' Processes a module's kwargs. All modules should use this for kwarg processing. @obj - An instance of the module (e.g., self) @kwargs - The kwargs passed to the module Returns None. ''' if hasattr(obj, "KWARGS"): for module_argument in obj.KWARGS: if has_key(kwargs, module_argument.name): arg_value = kwargs[module_argument.name] else: arg_value = module_argument.default setattr(obj, module_argument.name, arg_value) for (k, v) in iterator(kwargs): if not hasattr(obj, k): setattr(obj, k, v) else: raise Exception("binwalk.core.module.Modules.process_kwargs: %s has no attribute 'KWARGS'" % str(obj)) def status_server(self, port): ''' Starts the progress bar TCP service on the specified port. This service will only be started once per instance, regardless of the number of times this method is invoked. Failure to start the status service is considered non-critical; that is, a warning will be displayed to the user, but normal operation will proceed. ''' if self.status_server_started == False: self.status_server_started = True try: self.status_service = binwalk.core.statuserver.StatusServer(port, self) except Exception as e: binwalk.core.common.warning("Failed to start status server on port %d: %s" % (port, str(e))) def process_kwargs(obj, kwargs): ''' Convenience wrapper around binwalk.core.module.Modules.kwargs. @obj - The class object (an instance of a sub-class of binwalk.core.module.Module). @kwargs - The kwargs provided to the object's __init__ method. Returns None. ''' with Modules() as m: kwargs = m.kwargs(obj, kwargs) return kwargs def show_help(fd=sys.stdout): ''' Convenience wrapper around binwalk.core.module.Modules.help. @fd - An object with a write method (e.g., sys.stdout, sys.stderr, etc). Returns None. ''' with Modules() as m: fd.write(m.help()) binwalk-2.1.1/src/binwalk/core/plugin.py000066400000000000000000000211331263655036500201730ustar00rootroot00000000000000# Core code for supporting and managing plugins. import os import sys import imp import inspect import binwalk.core.common import binwalk.core.settings from binwalk.core.compat import * class Plugin(object): ''' Class from which all plugin classes are based. ''' # A list of case-sensitive module names for which this plugin should be loaded. # If no module names are specified, the plugin will be loaded for all modules. MODULES = [] def __init__(self, module): ''' Class constructor. @module - A handle to the current module that this plugin is loaded for. Returns None. ''' self.module = module if not self.MODULES or self.module.name in self.MODULES: self._enabled = True self.init() else: self._enabled = False def init(self): ''' Child class should override this if needed. Invoked during plugin initialization. ''' pass def pre_scan(self): ''' Child class should override this if needed. ''' pass def new_file(self, fp): ''' Child class should override this if needed. ''' pass def scan(self, module): ''' Child class should override this if needed. ''' pass def post_scan(self): ''' Child class should override this if needed. ''' pass class Plugins(object): ''' Class to load and call plugin callback functions, handled automatically by Binwalk.scan / Binwalk.single_scan. An instance of this class is available during a scan via the Binwalk.plugins object. Each plugin must be placed in the user or system plugins directories, and must define a class named 'Plugin'. The Plugin class constructor (__init__) is passed one argument, which is the current instance of the Binwalk class. The Plugin class constructor is called once prior to scanning a file or set of files. The Plugin class destructor (__del__) is called once after scanning all files. By default, all plugins are loaded during binwalk signature scans. Plugins that wish to be disabled by default may create a class variable named 'ENABLED' and set it to False. If ENABLED is set to False, the plugin will only be loaded if it is explicitly named in the plugins whitelist. ''' SCAN = 'scan' NEWFILE = 'new_file' PRESCAN = 'pre_scan' POSTSCAN = 'post_scan' MODULE_EXTENSION = '.py' def __init__(self, parent=None): self.scan = [] self.pre_scan = [] self.new_file = [] self.post_scan = [] self.parent = parent self.settings = binwalk.core.settings.Settings() def __enter__(self): return self def __exit__(self, t, v, traceback): pass def _call_plugins(self, callback_list, obj=None): for callback in callback_list: try: try: callback() except TypeError: if obj is not None: callback(obj) except KeyboardInterrupt as e: raise e except Exception as e: binwalk.core.common.warning("%s.%s failed: %s" % (callback.__module__, callback.__name__, e)) def _find_plugin_class(self, plugin): for (name, klass) in inspect.getmembers(plugin, inspect.isclass): if issubclass(klass, Plugin) and klass != Plugin: return klass raise Exception("Failed to locate Plugin class in " + plugin) def list_plugins(self): ''' Obtain a list of all user and system plugin modules. Returns a dictionary of: { 'user' : { 'modules' : [list, of, module, names], 'descriptions' : {'module_name' : 'module pydoc string'}, 'enabled' : {'module_name' : True}, 'path' : "path/to/module/plugin/directory" }, 'system' : { 'modules' : [list, of, module, names], 'descriptions' : {'module_name' : 'module pydoc string'}, 'enabled' : {'module_name' : True}, 'path' : "path/to/module/plugin/directory" } } ''' plugins = { 'user' : { 'modules' : [], 'descriptions' : {}, 'enabled' : {}, 'path' : None, }, 'system' : { 'modules' : [], 'descriptions' : {}, 'enabled' : {}, 'path' : None, } } for key in plugins.keys(): if key == 'user': plugins[key]['path'] = self.settings.user.plugins else: plugins[key]['path'] = self.settings.system.plugins if plugins[key]['path']: for file_name in os.listdir(plugins[key]['path']): if file_name.endswith(self.MODULE_EXTENSION): module = file_name[:-len(self.MODULE_EXTENSION)] try: plugin = imp.load_source(module, os.path.join(plugins[key]['path'], file_name)) plugin_class = self._find_plugin_class(plugin) plugins[key]['enabled'][module] = True plugins[key]['modules'].append(module) except KeyboardInterrupt as e: raise e except Exception as e: binwalk.core.common.warning("Error loading plugin '%s': %s" % (file_name, str(e))) plugins[key]['enabled'][module] = False try: plugins[key]['descriptions'][module] = plugin_class.__doc__.strip().split('\n')[0] except KeyboardInterrupt as e: raise e except Exception as e: plugins[key]['descriptions'][module] = 'No description' return plugins def load_plugins(self): plugins = self.list_plugins() self._load_plugin_modules(plugins['user']) self._load_plugin_modules(plugins['system']) def _load_plugin_modules(self, plugins): for module in plugins['modules']: try: file_path = os.path.join(plugins['path'], module + self.MODULE_EXTENSION) except KeyboardInterrupt as e: raise e except Exception: continue try: plugin = imp.load_source(module, file_path) plugin_class = self._find_plugin_class(plugin) class_instance = plugin_class(self.parent) if not class_instance._enabled: continue try: self.scan.append(getattr(class_instance, self.SCAN)) except KeyboardInterrupt as e: raise e except Exception as e: pass try: self.pre_scan.append(getattr(class_instance, self.PRESCAN)) except KeyboardInterrupt as e: raise e except Exception as e: pass try: self.post_scan.append(getattr(class_instance, self.POSTSCAN)) except KeyboardInterrupt as e: raise e except Exception as e: pass try: self.new_file.append(getattr(class_instance, self.NEWFILE)) except KeyboardInterrupt as e: raise e except Exception as e: pass except KeyboardInterrupt as e: raise e except Exception as e: binwalk.core.common.warning("Failed to load plugin module '%s': %s" % (module, str(e))) def pre_scan_callbacks(self, obj): return self._call_plugins(self.pre_scan) def new_file_callbacks(self, fp): return self._call_plugins(self.new_file, fp) def post_scan_callbacks(self, obj): return self._call_plugins(self.post_scan) def scan_callbacks(self, obj): return self._call_plugins(self.scan, obj) binwalk-2.1.1/src/binwalk/core/settings.py000066400000000000000000000154111263655036500205370ustar00rootroot00000000000000# Code for loading and accessing binwalk settings (extraction rules, signature files, etc). import os import binwalk.core.common as common from binwalk.core.compat import * class Settings: ''' Binwalk settings class, used for accessing user and system file paths and general configuration settings. After instatiating the class, file paths can be accessed via the self.paths dictionary. System file paths are listed under the 'system' key, user file paths under the 'user' key. Valid file names under both the 'user' and 'system' keys are as follows: o BINWALK_MAGIC_FILE - Path to the default binwalk magic file. o PLUGINS - Path to the plugins directory. ''' # Release version VERSION = "2.1.1" # Sub directories BINWALK_USER_DIR = ".binwalk" BINWALK_MAGIC_DIR = "magic" BINWALK_CONFIG_DIR = "config" BINWALK_PLUGINS_DIR = "plugins" # File names PLUGINS = "plugins" EXTRACT_FILE = "extract.conf" BINARCH_MAGIC_FILE = "binarch" def __init__(self): ''' Class constructor. Enumerates file paths and populates self.paths. ''' # Path to the user binwalk directory self.user_dir = self._get_user_dir() # Path to the system wide binwalk directory self.system_dir = common.get_module_path() # Build the paths to all user-specific files self.user = common.GenericContainer(binarch=self._user_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE), magic=self._magic_signature_files(user_only=True), extract=self._user_path(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE), plugins=self._user_path(self.BINWALK_PLUGINS_DIR)) # Build the paths to all system-wide files self.system = common.GenericContainer(binarch=self._system_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE), magic=self._magic_signature_files(system_only=True), extract=self._system_path(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE), plugins=self._system_path(self.BINWALK_PLUGINS_DIR)) def _magic_signature_files(self, system_only=False, user_only=False): ''' Find all user/system magic signature files. @system_only - If True, only the system magic file directory will be searched. @user_only - If True, only the user magic file directory will be searched. Returns a list of user/system magic signature files. ''' files = [] user_binarch = self._user_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE) system_binarch = self._system_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE) if not system_only: user_dir = os.path.join(self.user_dir, self.BINWALK_USER_DIR, self.BINWALK_MAGIC_DIR) files += [os.path.join(user_dir, x) for x in os.listdir(user_dir)] if not user_only: system_dir = os.path.join(self.system_dir, self.BINWALK_MAGIC_DIR) files += [os.path.join(system_dir, x) for x in os.listdir(system_dir)] # Don't include binarch signatures in the default list of signature files. # It is specifically loaded when -A is specified on the command line. if user_binarch in files: files.remove(user_binarch) if system_binarch in files: files.remove(system_binarch) return files def find_magic_file(self, fname, system_only=False, user_only=False): ''' Finds the specified magic file name in the system / user magic file directories. @fname - The name of the magic file. @system_only - If True, only the system magic file directory will be searched. @user_only - If True, only the user magic file directory will be searched. If system_only and user_only are not set, the user directory is always searched first. Returns the path to the file on success; returns None on failure. ''' loc = None if not system_only: fpath = self._user_path(self.BINWALK_MAGIC_DIR, fname) if os.path.exists(fpath) and common.file_size(fpath) > 0: loc = fpath if loc is None and not user_only: fpath = self._system_path(self.BINWALK_MAGIC_DIR, fname) if os.path.exists(fpath) and common.file_size(fpath) > 0: loc = fpath return fpath def _get_user_dir(self): ''' Get the user's home directory. ''' try: # This should work in both Windows and Unix environments return os.getenv('USERPROFILE') or os.getenv('HOME') except KeyboardInterrupt as e: raise e except Exception: return '' def _file_path(self, dirname, filename): ''' Builds an absolute path and creates the directory and file if they don't already exist. @dirname - Directory path. @filename - File name. Returns a full path of 'dirname/filename'. ''' if not os.path.exists(dirname): try: os.makedirs(dirname) except KeyboardInterrupt as e: raise e except Exception: pass fpath = os.path.join(dirname, filename) if not os.path.exists(fpath): try: open(fpath, "w").close() except KeyboardInterrupt as e: raise e except Exception: pass return fpath def _user_path(self, subdir, basename=''): ''' Gets the full path to the 'subdir/basename' file in the user binwalk directory. @subdir - Subdirectory inside the user binwalk directory. @basename - File name inside the subdirectory. Returns the full path to the 'subdir/basename' file. ''' try: return self._file_path(os.path.join(self.user_dir, self.BINWALK_USER_DIR, subdir), basename) except KeyboardInterrupt as e : raise e except Exception: return None def _system_path(self, subdir, basename=''): ''' Gets the full path to the 'subdir/basename' file in the system binwalk directory. @subdir - Subdirectory inside the system binwalk directory. @basename - File name inside the subdirectory. Returns the full path to the 'subdir/basename' file. ''' try: return self._file_path(os.path.join(self.system_dir, subdir), basename) except KeyboardInterrupt as e : raise e except Exception: return None binwalk-2.1.1/src/binwalk/core/statuserver.py000066400000000000000000000053331263655036500212700ustar00rootroot00000000000000# Provides scan status information via a TCP socket service. # Currently only works for signature scans. import sys import time import errno import threading import binwalk.core.compat # Python 2/3 compatibility try: import SocketServer except ImportError: import socketserver as SocketServer class StatusRequestHandler(SocketServer.BaseRequestHandler): def handle(self): message_format = "%s %3d%% [ %d / %d ]" last_status_message_len = 0 status_message = '' message_sent = False self.server.binwalk.status.running = True while True: time.sleep(0.1) try: self.request.send(binwalk.core.compat.str2bytes('\b' * last_status_message_len)) self.request.send(binwalk.core.compat.str2bytes(' ' * last_status_message_len)) self.request.send(binwalk.core.compat.str2bytes('\b' * last_status_message_len)) if self.server.binwalk.status.shutdown: self.server.binwalk.status.finished = True break if self.server.binwalk.status.total != 0: percentage = ((float(self.server.binwalk.status.completed) / float(self.server.binwalk.status.total)) * 100) status_message = message_format % (self.server.binwalk.status.fp.path, percentage, self.server.binwalk.status.completed, self.server.binwalk.status.total) elif not message_sent: status_message = "No status information available at this time!" else: continue last_status_message_len = len(status_message) self.request.send(binwalk.core.compat.str2bytes(status_message)) message_sent = True except IOError as e: if e.errno == errno.EPIPE: break except Exception as e: binwalk.core.common.debug('StatusRequestHandler exception: ' + str(e) + '\n') except KeyboardInterrupt as e: raise e self.server.binwalk.status.running = False return class ThreadedStatusServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): daemon_threads = True allow_reuse_address = True class StatusServer(object): def __init__(self, port, binwalk): self.server = ThreadedStatusServer(('127.0.0.1', port), StatusRequestHandler) self.server.binwalk = binwalk t = threading.Thread(target=self.server.serve_forever) t.setDaemon(True) t.start() binwalk-2.1.1/src/binwalk/magic/000077500000000000000000000000001263655036500164535ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/magic/archives000066400000000000000000000342161263655036500202100ustar00rootroot00000000000000# ----------------------------Archive Formats-------------------------------------- # POSIX tar archives 257 string ustar\000 POSIX tar archive >8 byte !0 >>8 string x \b, owner user name: "%.32s" >40 byte !0 >>40 string x \b, owner group name: "%.32s" 257 string ustar\040\040\000 POSIX tar archive (GNU) >8 byte !0 >>8 string x \b, owner user name: "%.32s" >40 byte !0 >>40 string x \b, owner group name: "%.32s" # Incremental snapshot gnu-tar format from: # http://www.gnu.org/software/tar/manual/html_node/Snapshot-Files.html 0 string GNU\x20tar- GNU tar incremental snapshot data, >0 string x version: "%s" # JAR archiver (.j), this is the successor to ARJ, not Java's JAR (which is essentially ZIP) 14 string \x1aJar\x1b JAR (ARJ Software, Inc.) archive data 0 string JARCS JAR (ARJ Software, Inc.) archive data # PKZIP multi-volume archive 0 string PK\x07\x08PK\x03\x04 Zip multi-volume archive data, at least PKZIP v2.50 to extract # ZIP compression (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu) 0 string PK\003\004 Zip archive data, >6 leshort &0x01 encrypted >4 byte 0x00 v0.0 >4 byte 0x09 at least v0.9 to extract, >4 byte 0x0a at least v1.0 to extract, >4 byte 0x0b at least v1.1 to extract, >0x161 string WINZIP WinZIP self-extracting, >4 byte 0x14 >>30 ubelong !0x6d696d65 at least v2.0 to extract, >18 lelong !0 >>18 lelong <0 {invalid} >>18 lelong x compressed size: %d, >>18 lelong x {jump:%d} >22 lelong !0 >>22 lelong <0 {invalid} >>22 lelong x uncompressed size: %d, >30 byte <0x2D {invalid} >30 byte >0x7A {invalid} >26 leshort x {strlen:%d} >30 string x name: {string}%s # ZIP footer 0 string PK\x05\x06 End of Zip archive >20 leshort >0 >>20 leshort x \b, comment: >>20 leshort x {strlen:%d} >>22 string x {string}"%s" # ARJ archiver (jason@jarthur.Claremont.EDU) 0 uleshort 0xea60 ARJ archive data, >2 leshort x header size: %d, >5 byte <1 {invalid} >5 byte >16 {invalid} >5 byte x version %d, >6 byte <1 {invalid} >6 byte >16 {invalid} >6 byte x minimum version to extract: %d, >8 byte <0 {invalid} flags, >8 byte &0x04 multi-volume, >8 byte &0x10 slash-switched, >8 byte &0x20 backup, >9 byte <0 {invalid}, >9 byte >4 {invalid}, >9 byte 0 compression method: stored, >9 byte 1 compression method: compressed most, >9 byte 2 compression method: compressed, >9 byte 3 compression method: compressed faster, >9 byte 4 compression method: compressed fastest, >10 byte <0 {invalid} >10 byte >4 {invalid} >10 byte 0 file type: binary, >10 byte 1 file type: 7-bit text, >10 byte 2 file type: comment header, >10 byte 3 file type: directory, >10 byte 4 file type: volume label, >34 byte !0 >>34 string x {name:%s} >>34 string x original name: "%s", >0xC ledate x original file date: %s, >0x10 lelong <0 {invalid} >0x10 lelong x compressed file size: %d, >0x14 lelong <0 {invalid} >0x14 lelong x uncompressed file size: %d, >7 byte 0 os: MS-DOS >7 byte 1 os: PRIMOS >7 byte 2 os: Unix >7 byte 3 os: Amiga >7 byte 4 os: Macintosh >7 byte 5 os: OS/2 >7 byte 6 os: Apple ][ GS >7 byte 7 os: Atari ST >7 byte 8 os: NeXT >7 byte 9 os: VAX/VMS >7 byte >9 {invalid} >7 byte <0 {invalid} # RAR archiver (http://kthoom.googlecode.com/hg/docs/unrar.html) 0 string \x52\x61\x72\x21\x1A\x07\x00 RAR archive data, first volume type: >9 ubyte <0x72 {invalid} >9 ubyte >0x7B {invalid} >9 ubyte 0x72 MARK_HEAD >9 ubyte 0x73 MAIN_HEAD >9 ubyte 0x74 FILE_HEAD >9 ubyte 0x75 COMM_HEAD >9 ubyte 0x76 AV_HEAD >9 ubyte 0x77 SUB_HEAD >9 ubyte 0x78 PROTECT_HEAD >9 ubyte 0x79 SIGN_HEAD >9 ubyte 0x7A NEWSUB_HEAD >9 ubyte 0x7B ENDARC_HEAD # HPACK archiver (Peter Gutmann, pgut1@cs.aukuni.ac.nz) 0 string HPAK HPACK archive data # JAM Archive volume format, by Dmitry.Kohmanyuk@UA.net 0 string \351,\001JAM JAM archive # LHARC/LHA archiver (Greg Roelofs, newt@uchicago.edu) 2 string -lzs LHa 2.x? archive data [lzs] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh\40 LHa 2.x? archive data [lh ] [NSRL|LHA2] >6 string !- {invalid} 2 string -lhd LHa 2.x? archive data [lhd] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh2 LHa 2.x? archive data [lh2] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh3 LHa 2.x? archive data [lh3] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh4 LHa (2.x) archive data [lh4] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh5 LHa (2.x) archive data [lh5] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh6 LHa (2.x) archive data [lh6] [NSRL|LHA2] >6 string !- {invalid} 2 string -lh7 LHa (2.x) archive data [lh7] [NSRL|LHA2] >6 string !- {invalid} # cpio archives # # The SVR4 "cpio(4)" hints that there are additional formats, but they # are defined as "short"s; I think all the new formats are # character-header formats and thus are strings, not numbers. #0 string 070707 ASCII cpio archive (pre-SVR4 or odc) 0 string 070701 ASCII cpio archive (SVR4 with no CRC), >110 byte 0 {invalid} >94 byte <0x30 {invalid} >94 byte >0x66 {invalid} >54 byte <0x30 {invalid} >54 byte >0x66 {invalid} >110 string x file name: "%s", >94 string x file name length: "0x%.8s", >54 string x file size: "0x%.8s" 0 string 070702 ASCII cpio archive (SVR4 with CRC) >110 byte 0 {invalid} >94 byte <0x30 {invalid} >94 byte >0x66 {invalid} >54 byte <0x30 {invalid} >54 byte >0x66 {invalid} >110 string x file name: "%s", >94 string x file name length: "0x%.8s", >54 string x file size: "0x%.8s" # HP Printer Job Language # The header found on Win95 HP plot files is the "Silliest Thing possible" # (TM) # Every driver puts the language at some random position, with random case # (LANGUAGE and Language) # For example the LaserJet 5L driver puts the "PJL ENTER LANGUAGE" in line 10 # From: Uwe Bonnes # 0 string \033%-12345X@PJL HP Printer Job Language data, >0 string x "%s" #------------------------------------------------------------------------------ # # RPM: file(1) magic for Red Hat Packages Erik Troan (ewt@redhat.com) # 0 ubelong 0xedabeedb RPM >4 byte <1 {invalid} >4 byte >99 {invalid} >4 byte x v%d >6 beshort 0 bin >6 beshort 1 src >8 beshort 1 i386 >8 beshort 2 Alpha >8 beshort 3 Sparc >8 beshort 4 MIPS >8 beshort 5 PowerPC >8 beshort 6 68000 >8 beshort 7 SGI >8 beshort 8 RS6000 >8 beshort 9 IA64 >8 beshort 10 Sparc64 >8 beshort 11 MIPSel >8 beshort 12 ARM >10 string x "%s" # IBM AIX Backup File Format header and entry signatures 0 ulelong 0xea6b0009 BFF volume header, >4 uleshort x checksum: 0x%.4X, >6 leshort <0 {invalid} >6 leshort 0 {invalid} >6 leshort x volume number: %d, >8 ledate x current date: %s, >12 ledate x starting date: %s, >20 string x disk name: "%s", >36 string x file system name: "%s", >52 string x user name: "%s" 2 uleshort 0xea6b BFF volume entry, >22 lelong <0 {invalid} >22 lelong 0 directory, >22 lelong >0 >>22 lelong x file size: %d, >>54 lelong <0 {invalid} >>54 lelong 0 {invalid} >>54 lelong x compressed size: %d, >58 lelong !0 {invalid} >62 byte 0 {invalid} >62 byte !0x2e >>62 byte !0x2f {invalid} >62 string x file name: "%s" 2 uleshort 0xea6c BFF volume entry, compressed, >22 lelong <0 {invalid} >22 lelong 0 directory, >22 lelong >0 >>22 lelong x file size: %d, >>54 lelong <0 {invalid} >>54 lelong 0 {invalid} >>54 lelong x compressed size: %d, >58 lelong !0 {invalid} >62 byte 0 {invalid} >62 byte !0x2e >>62 byte !0x2f {invalid} >62 string x file name: "%s" 0 uleshort 0xea6d BFF volume entry, AIXv3, >22 lelong <0 {invalid} >22 lelong 0 directory, >22 lelong >0 >>22 lelong x file size: %d, >>54 lelong <0 {invalid} >>54 lelong 0 {invalid} >>54 lelong x compressed size: %d, >58 lelong !0 {invalid} >62 byte 0 {invalid} >62 byte !0x2e >>62 byte !0x2f {invalid} >62 string x file name: "%s" #------------------------------------------------------------------------------ # From Stuart Caie (developer of cabextract) # Microsoft Cabinet files 0 string MSCF\0\0\0\0 Microsoft Cabinet archive data # According to libmagic comments, CAB version number is always 1.3 >25 byte !1 {invalid} >24 byte !3 {invalid} >8 lelong x \b, %u bytes >28 leshort 0 {invalid} >28 leshort 1 \b, 1 file >28 leshort >1 \b, %u files # InstallShield Cabinet files 0 string ISc( InstallShield Cabinet archive data # TODO: Version number checks should be made more specific for false positive filtering >5 byte&0xf0 =0x60 version 6, >5 byte&0xf0 <0x60 version 4/5, >5 byte&0xf0 >0x60 {invalid} version, >12 lelong <0 {invalid} offset, >12 lelong >100000 {invalid} offset, >(12.l+40) lelong x %u files # Windows CE package files 0 string MSCE\0\0\0\0 Microsoft WinCE install header >20 lelong 0 \b, architecture-independent >20 lelong 103 \b, Hitachi SH3 >20 lelong 104 \b, Hitachi SH4 >20 lelong 0xA11 \b, StrongARM >20 lelong 4000 \b, MIPS R4000 >20 lelong 10003 \b, Hitachi SH3 >20 lelong 10004 \b, Hitachi SH3E >20 lelong 10005 \b, Hitachi SH4 >20 lelong 70001 \b, ARM 7TDMI >52 leshort 1 \b, 1 file >52 leshort >1 \b, %u files >56 leshort 1 \b, 1 registry entry >56 leshort >1 \b, %u registry entries 0 string \x00\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 LBR archive data >12 string !\x00x00 {invalid} # Parity archive reconstruction file, the 'par' file format now used on Usenet. 0 string PAR\0 PARity archive data >48 leshort =0 - Index file >48 leshort >0 - file number %d # Felix von Leitner 0 string d8:announce BitTorrent file # BSA archives, based on http://forum.step-project.com/topic/5033-ramifications-of-bsa-extraction-in-mod-organizer/page-16 0 string BSA\x00\x67 BSA archive, version: 103, >8 byte !0x24 {invalid} >8 byte 0x24 folder records offset: %d 0 string BSA\x00\x68 BSA archive, version: 104, >8 byte !0x24 {invalid} >8 byte 0x24 folder records offset: %d binwalk-2.1.1/src/binwalk/magic/binarch000066400000000000000000000074771263655036500200230ustar00rootroot00000000000000# MIPS prologue # addiu $sp, -XX # sw XX, XX($sp) # 27 BD FF XX # AF BX XX XX 1 string \xFF\xBD\x27 MIPSEL instructions, function prologue >6 byte !0xAF {invalid} >5 byte&0xE0 !0xA0 {invalid} 0 string \x27\xBD\xFF MIPS instructions, function prologue >4 byte !0xAF {invalid} >5 byte&0xE0 !0xA0 {invalid} # MIPS epilogue # jr $ra # addiu $sp, XX # # addiu $sp, XX # jr $ra 0 ubelong 0x03e00008 MIPS instructions, function epilogue >4 ubeshort !0x27BD {invalid} 0 ubeshort 0x27BD MIPS instructions, function epilogue >2 ubelong !0x03e00008 {invalid} 0 ulelong 0x03e00008 MIPSEL instructions, function epilogue >6 uleshort !0x27BD {invalid} 0 uleshort 0x27BD MIPS instructions, function epilogue >2 ulelong !0x03e00008 {invalid} # MIPS16e # nop (x4) # TODO: Produces false positives when scanning ARM Thumb code #0 string \x65\x00\x65\x00\x65\x00\x65\x00 MIPS16e instructions, nops{jump-to-offset:8} #0 string \x00\x65\x00\x65\x00\x65\x00\x65 MIPSEL16e instructions, nops{jump-to-offset:8} # save a0-a1, XX # addiu XX, XX 0 string \xf0\x08\x64 MIPS16e instructions, function prologue >4 byte !0x01 {invalid} # move $sp, $s1 # restore XX, XX, XX # jrc $ra 0 ubeshort 0x65B9 MIPS16e instructions, function epilogue >3 byte !0x64 {invalid} >4 ubeshort !0xE8A0 {invalid} 0 uleshort 0x65B9 MIPSEL16e instructions, function epilogue >3 byte !0x64 {invalid} >4 uleshort !0xE8A0 {invalid} # jrc $ra # nop 0 ubelong 0xe8a06500 MIPS16e instructions, function epilogue 0 ulelong 0xe8a06500 MIPSEL16e instructions, function epilogue # PowerPC prologue # mflr r0 0 ubelong 0x7C0802A6 PowerPC big endian instructions, function prologue 0 ulelong 0x7C0802A6 PowerPC little endian instructions, function prologue # PowerPC epilogue # blr 0 ubelong 0x4E800020 PowerPC big endian instructions, function epilogue 0 ulelong 0x4E800020 PowerPC little endian instructions, function epilogue # TODO: Add ARM Thumb dectection # ARM prologue # STMFD SP!, {XX} # 0 ubeshort 0xE92D ARMEB instructions, function prologue >4 byte&0xF0 !0xE0 {invalid} >8 byte&0xF0 !0xE0 {invalid} 0 uleshort 0xE92D ARM instructions, function prologue{adjust:-2} >5 byte&0xF0 !0xE0 {invalid} >9 byte&0xF0 !0xE0 {invalid} # ARM epilogue # MOV R0, XX # LDMFD SP!, {XX} 0 ubeshort 0xE1A0 ARMEB instructions, function epilogue >4 beshort !0xE8BD {invalid} 0 uleshort 0xE1A0 ARM instructions, function epilogue{adjust:-2} >4 leshort !0xE8BD {invalid} # Ubicom32 prologue # move.4 -4($sp)++, $ra 0 ubelong 0x02FF6125 Ubicom32 instructions, function prologue # Ubicom32 epilogues # calli $ra, 0($ra) # ret ($sp)4++ 0 ubelong 0xF0A000A0 Ubicom32 instructions, function epilogue 0 ubelong 0x000022E1 Ubicom32 instructions, function epilogue # AVR8 prologue # push r28 # push r29 0 ubelong 0x93CF93DF AVR8 instructions, function prologue 0 ubelong 0x93DF93CF AVR8 instructions, function prologue # AVR32 prologue # pushm r7,lr # mov r7,sp 0 string \xEB\xCD\x40\x80\x1A\x97 AVR32 instructions, function prologue # SPARC epilogue # ret # restore XX 0 string \x81\xC7\xE0\x08\x81\xE8 SPARC instructions, function epilogue # x86 epilogue # push ebp # move ebp, esp # sub esp, XX # # push ebp # move ebp, esp # push edi # push esi 0 string \x55\x89\xE5\x83\xEC Intel x86 instructions, function prologue 0 string \x55\x89\xE5\x57\x56 Intel x86 instructions, function prologue 0 string \x90\x90\x90\x90\x90\x90\x90\x90 Intel x86 instructions, nops{jump:8}{overlap} # unlk a6 # rts # link a6, XX 0 string N^NuNV Motorola Coldfire instructions, function prologue/epilogue # mov.l @r15+, r9 # rts # mov.l @r15 0 string \xF6\x69\x0B\x00\xF6\x68 SuperH instructions, little endian, function epilogue (gcc) 0 string \x69\xF6\x00\x0B\x68\xF6 SuperH instructions, big endian, function epilogue (gcc) binwalk-2.1.1/src/binwalk/magic/bincast000066400000000000000000000002601263655036500200170ustar00rootroot00000000000000# This file is depreciated and intentionally left blank. # It is included solely to ensure that previously installed # versions of this file are overwritten during an upgrade. binwalk-2.1.1/src/binwalk/magic/binwalk000066400000000000000000000002601263655036500200230ustar00rootroot00000000000000# This file is depreciated and intentionally left blank. # It is included solely to ensure that previously installed # versions of this file are overwritten during an upgrade. binwalk-2.1.1/src/binwalk/magic/bootloaders000066400000000000000000000006021263655036500207110ustar00rootroot00000000000000 #---------------------------Bootloaders-------------------------------- # CFE bootloader 0 string CFE1 CFE boot loader >4 string !CFE1 {invalid} >40 string CFE1CFE1 {invalid} # U-Boot boot loader 0 string U-Boot\x20 U-Boot version string, >7 byte <48 {invalid} >7 byte >57 {invalid} >8 byte !0x2E {invalid} >0 string x "%s" binwalk-2.1.1/src/binwalk/magic/code000066400000000000000000000002601263655036500173060ustar00rootroot00000000000000# This file is depreciated and intentionally left blank. # It is included solely to ensure that previously installed # versions of this file are overwritten during an upgrade. binwalk-2.1.1/src/binwalk/magic/compressed000066400000000000000000000220741263655036500205470ustar00rootroot00000000000000 #------------------Compression Formats----------------------------- # Stuffit archives are the de facto standard of compression for Macintosh # files obtained from most archives. (franklsm@tuns.ca) # Found in some firmware updates. 0 string SIT! StuffIt Archive (data) >2 string x \b: %s 0 string SITD StuffIt Deluxe (data) >2 string x \b: %s 0 string Sef StuffIt Deluxe Segment (data) >2 string x \b: %s # Newer StuffIt archives (grant@netbsd.org) 0 string StuffIt StuffIt Archive # AFX compressed files (Wolfram Kleff) 2 string -afx AFX compressed file data >6 string !- {invalid} # bzip2 0 string BZh91AY&SY bzip2 compressed data, block size = 900k 0 string BZh81AY&SY bzip2 compressed data, block size = 800k 0 string BZh71AY&SY bzip2 compressed data, block size = 700k 0 string BZh61AY&SY bzip2 compressed data, block size = 600k 0 string BZh51AY&SY bzip2 compressed data, block size = 500k 0 string BZh41AY&SY bzip2 compressed data, block size = 400k 0 string BZh31AY&SY bzip2 compressed data, block size = 300k 0 string BZh21AY&SY bzip2 compressed data, block size = 200k 0 string BZh11AY&SY bzip2 compressed data, block size = 100k # lzop from 0 string \x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a lzop compressed data, >9 beshort >0x093F {invalid} >9 beshort <0x0940 >>9 byte&0xf0 =0x00 - version 0. >>9 beshort&0x0fff x \b%03x, >>9 beshort&0x0fff <1 {invalid} >>13 byte 1 LZO1X-1, >>13 byte 2 LZO1X-1(15), >>13 byte 3 LZO1X-999, >>14 byte =0x00 os: MS-DOS >>14 byte =0x01 os: Amiga >>14 byte =0x02 os: VMS >>14 byte =0x03 os: Unix >>14 byte =0x05 os: Atari >>14 byte =0x06 os: OS/2 >>14 byte =0x07 os: MacOS >>14 byte =0x0A os: Tops/20 >>14 byte =0x0B os: WinNT >>14 byte =0x0E os: Win32 >9 beshort >0x0939 >>9 byte&0xf0 =0x00 - version 0. >>9 byte&0xf0 =0x10 - version 1. >>9 byte&0xf0 =0x20 - version 2. >>9 beshort&0x0fff x \b%03x, >>15 byte 1 LZO1X-1, >>15 byte 2 LZO1X-1(15), >>15 byte 3 LZO1X-999, >>17 byte =0x00 os: MS-DOS >>17 byte =0x01 os: Amiga >>17 byte =0x02 os: VMS >>17 byte =0x03 os: Unix >>17 byte =0x05 os: Atari >>17 byte =0x06 os: OS/2 >>17 byte =0x07 os: MacOS >>17 byte =0x0A os: Tops/20 >>17 byte =0x0B os: WinNT >>17 byte =0x0E os: Win32 # lzip 0 string LZIP lzip compressed data, >4 ubyte 0 {invalid} # Current version is still 1.x >4 ubyte >4 {invalid} >4 byte x version: %d # lrzip 0 string LRZI lrzip compressed data # LZO 0 string \211LZO\000\015\012\032\012 LZO compressed data # 7-zip archiver, from Thomas Klausner (wiz@danbala.tuwien.ac.at) # http://www.7-zip.org or DOC/7zFormat.txt # 0 string 7z\274\257\047\034 7-zip archive data, >6 byte <0 {invalid} >6 byte 0 >>7 byte 0 {invalid} >6 byte >20 {invalid} >6 byte x version %d >7 byte x \b.%d # standard unix compress # Disabled until a python alternative can be foudn for the compress binwalk plugin. #0 string \x1f\x9d\x90 compress'd data, 16 bits # http://tukaani.org/xz/xz-file-format.txt 0 string \xFD\x37\x7a\x58\x5a\x00 xz compressed data # gzip (GNU zip, not to be confused with Info-ZIP or PKWARE zip archiver) # Edited by Chris Chittleborough , March 2002 # * Original filename is only at offset 10 if "extra field" absent # * Produce shorter output - notably, only report compression methods # other than 8 ("deflate", the only method defined in RFC 1952). #0 string \037\213\x08 gzip compressed data 0 string \x1f\x8b\x08 gzip compressed data >3 byte &0x01 \b, ASCII >3 byte&0xE0 !0x00 \b, {invalid}invalid reserved flag bits >8 byte 2 \b, maximum compression >8 byte 4 \b, fastest compression >8 byte 1 \b, {invalid}invalid extra flags >8 byte 3 \b, {invalid}invalid extra flags >8 byte >4 \b, {invalid}invalid extra flags >3 byte &0x02 \b, has header CRC >3 byte&0x04 0x04 >>10 leshort x \b, has %d bytes of extra data >3 byte&0xC =0x08 \b, has original file name >>10 string x \b: "%s"{name:%s} >3 byte &0x10 \b, has comment >>3 byte&0xC 0 >>>10 string x \b: "%s" >9 byte =0x00 \b, from FAT filesystem (MS-DOS, OS/2, NT) >9 byte =0x01 \b, from Amiga >9 byte =0x02 \b, from VMS >9 byte =0x03 \b, from Unix >9 byte =0x04 \b, from VM/CMS >9 byte =0x05 \b, from Atari >9 byte =0x06 \b, from HPFS filesystem (OS/2, NT) >9 byte =0x07 \b, from MacOS >9 byte =0x08 \b, from Z-System >9 byte =0x09 \b, from CP/M >9 byte =0x0A \b, from TOPS/20 >9 byte =0x0B \b, from NTFS filesystem (NT) >9 byte =0x0C \b, from QDOS >9 byte =0x0D \b, from Acorn RISCOS >3 byte &0x20 \b, encrypted{invalid} # Dates before 1992 are {invalid}, unless of course you're DD-WRT in which # case you don't know how to set a date in your gzip files. Brilliant. >4 lelong =0 \b, NULL date (1970-01-01 00:00:00) >4 lelong <0 {invalid} >4 lelong >0 >>4 lelong <694224000 {invalid} >>4 lelong =694224000 {invalid} >>4 lelong >694224000 \b, last modified: >>>4 ledate x %s >>>4 lelong x {epoch:%d} # Supplementary magic data for the file(1) command to support # rzip(1). The format is described in magic(5). # # Copyright (C) 2003 by Andrew Tridgell. You may do whatever you want with # this file. # 0 string RZIP rzip compressed data >4 byte x - version %d >5 byte x \b.%d >6 belong x (%d bytes) # JAR 0 belong 0xcafed00d JAR compressed with pack200, >5 byte x version %d. >4 byte x \b%d # New LZMA format signature # See lzma file for LZMA signatures 0 string \xFFLZMA\x00 LZMA compressed data (new), >6 byte&0x10 0 single-block stream >6 byte&0x10 0x10 multi-block stream 0 string \xff\x06\x00\x00\x73\x4e\x61\x50\x70\x59 Snappy compression, stream identifier #0 beshort 0x7801 Zlib header, no compression 0 beshort 0x789c Zlib compressed data, default compression 0 beshort 0x78da Zlib compressed data, best compression 0 beshort 0x785e Zlib compressed data, compressed binwalk-2.1.1/src/binwalk/magic/console000066400000000000000000000123701263655036500200430ustar00rootroot00000000000000# From Gurkan Sengun , www.linuks.mine.nu 12 string GameBoy\x20Music\x20Module Nintendo Gameboy Music Module #------------------------------------------------------------------------------ # gameboy: file(1) magic for the Nintendo (Color) Gameboy raw ROM format # 0x104 ubelong 0xCEED6666 Gameboy ROM, >0x134 byte !0 >>0x134 string x name: "%.16s" >0x146 byte 0x03 \b,[SGB] >0x147 byte 0x00 \b, [ROM ONLY] >0x147 byte 0x01 \b, [ROM+MBC1] >0x147 byte 0x02 \b, [ROM+MBC1+RAM] >0x147 byte 0x03 \b, [ROM+MBC1+RAM+BATT] >0x147 byte 0x05 \b, [ROM+MBC2] >0x147 byte 0x06 \b, [ROM+MBC2+BATTERY] >0x147 byte 0x08 \b, [ROM+RAM] >0x147 byte 0x09 \b, [ROM+RAM+BATTERY] >0x147 byte 0x0B \b, [ROM+MMM01] >0x147 byte 0x0C \b, [ROM+MMM01+SRAM] >0x147 byte 0x0D \b, [ROM+MMM01+SRAM+BATT] >0x147 byte 0x0F \b, [ROM+MBC3+TIMER+BATT] >0x147 byte 0x10 \b, [ROM+MBC3+TIMER+RAM+BATT] >0x147 byte 0x11 \b, [ROM+MBC3] >0x147 byte 0x12 \b, [ROM+MBC3+RAM] >0x147 byte 0x13 \b, [ROM+MBC3+RAM+BATT] >0x147 byte 0x19 \b, [ROM+MBC5] >0x147 byte 0x1A \b, [ROM+MBC5+RAM] >0x147 byte 0x1B \b, [ROM+MBC5+RAM+BATT] >0x147 byte 0x1C \b, [ROM+MBC5+RUMBLE] >0x147 byte 0x1D \b, [ROM+MBC5+RUMBLE+SRAM] >0x147 byte 0x1E \b, [ROM+MBC5+RUMBLE+SRAM+BATT] >0x147 byte 0x1F \b, [Pocket Camera] >0x147 byte 0xFD \b, [Bandai TAMA5] >0x147 byte 0xFE \b, [Hudson HuC-3] >0x147 byte 0xFF \b, [Hudson HuC-1] >0x148 byte 0 \b, ROM: 256Kbit >0x148 byte 1 \b, ROM: 512Kbit >0x148 byte 2 \b, ROM: 1Mbit >0x148 byte 3 \b, ROM: 2Mbit >0x148 byte 4 \b, ROM: 4Mbit >0x148 byte 5 \b, ROM: 8Mbit >0x148 byte 6 \b, ROM: 16Mbit >0x148 byte 0x52 \b, ROM: 9Mbit >0x148 byte 0x53 \b, ROM: 10Mbit >0x148 byte 0x54 \b, ROM: 12Mbit >0x149 byte 1 \b, RAM: 16Kbit >0x149 byte 2 \b, RAM: 64Kbit >0x149 byte 3 \b, RAM: 128Kbit >0x149 byte 4 \b, RAM: 1Mbit #>0x14e long x \b, CRC: %x #------------------------------------------------------------------------------ # genesis: file(1) magic for the Sega MegaDrive/Genesis raw ROM format # 0x100 string SEGA Sega MegaDrive/Genesis raw ROM dump, >0x120 string x Name: "%.16s", >0x110 byte !0 >>0x110 string x "%.16s", >0x1B0 string RA with SRAM # From: "Nelson A. de Oliveira" # Nintendo .nds 192 string \044\377\256Qi\232 Nintendo DS Game ROM Image # Nintendo .gba 0 string \056\000\000\352$\377\256Qi Nintendo Game Boy Advance ROM Image #------------------------------------------------------------------------------ # Sony Playstation executables (Adam Sjoegren ) : 0 string PS-X\x20EXE Sony Playstation executable # Area: >113 string x (%s) #------------------------------------------------------------------------------ ## Microsoft Xbox executables .xbe (Esa Hyyti ) 0 string XBEH Microsoft Xbox executable (XBE), ## probabilistic checks whether signed or not >0x0004 ulelong =0 >>10 ulelong =0 >>>16 ulelong =0 \b, not signed >0x0004 ulelong >0 >>10 ulelong >0 \b, signed >>>16 ulelong >0 \b, signed ## expect base address of 0x10000 >0x0104 ulelong !0x10000 {invalid} >0x0104 ulelong =0x10000 >>(0x0118-0x0FF60) ulelong&0x80000007 0x80000007 \b, all regions >>(0x0118-0x0FF60) ulelong&0x80000007 !0x80000007 >>>(0x0118-0x0FF60) ulelong >0 (regions: >>>>(0x0118-0x0FF60) ulelong &0x00000001 NA >>>>(0x0118-0x0FF60) ulelong &0x00000002 Japan >>>>(0x0118-0x0FF60) ulelong &0x00000004 Rest_of_World >>>>(0x0118-0x0FF60) ulelong &0x80000000 Manufacturer >>>(0x0118-0x0FF60) ulelong >0 \b) # -------------------------------- # # Microsoft Xbox data file formats # # http://home.comcast.net/~admiral_powerslave/filestructure.html 0 string XIP0 XIP, Microsoft Xbox data, >12 lelong x total size: %d >16 lelong !0 {invalid} >24 lelong !0 {invalid} 0 string XTF0\x00\x00\x00 XTF, Microsoft Xbox data binwalk-2.1.1/src/binwalk/magic/crypto000066400000000000000000000121651263655036500177230ustar00rootroot00000000000000# Type: OpenSSL certificates/key files # From: Nicolas Collignon 0 string -----BEGIN\x20CERTIFICATE PEM certificate >22 string !----- {invalid} 0 string -----BEGIN\x20CERTIFICATE\x20REQ PEM certificate request 0 string -----BEGIN\x20RSA\x20PRIVATE PEM RSA private key 0 string -----BEGIN\x20DSA\x20PRIVATE PEM DSA private key 0 string -----BEGIN\x20EC\x20PRIVATE PEM EC private key # Type: OpenSSH key files # From: Nicolas Collignon 0 string SSH\x20PRIVATE\x20KEY OpenSSH RSA1 private key, >28 byte !0 >>28 string x version "%s" >28 byte 0 {invalid} 0 string ssh-dss\x20 OpenSSH DSA public key 0 string ssh-rsa\x20 OpenSSH RSA public key 0 string ecdsa-sha2-nistp256\x20 OpenSSH ECDSA (Curve P-256) public key 0 string ecdsa-sha2-nistp384\x20 OpenSSH ECDSA (Curve P-384) public key 0 string ecdsa-sha2-nistp521\x20 OpenSSH ECDSA (Curve P-521) public key # Type: Certificates/key files in DER format # From: Gert Hulselmans 0 string \x30\x82 Private key in DER format (PKCS#8), >4 string !\x02\x01\x00 {invalid} >2 beshort <0 {invalid} >2 beshort x header length: 4, sequence length: %d 0 string \x30\x82 Certificate in DER format (x509 v3), >4 string !\x30\x82 {invalid} >2 beshort <0 {invalid} >2 beshort x header length: 4, sequence length: %d # GnuPG # The format is very similar to pgp 0 string \001gpg GPG key trust database >4 byte x version %d # Not a very useful signature #0 beshort 0x9901 GPG key public ring # This magic is not particularly good, as the keyrings don't have true # magic. Nevertheless, it covers many keyrings. #------------------------------------------------------------------------------ # Mavroyanopoulos Nikos # mcrypt: file(1) magic for mcrypt 2.2.x; 0 string \0m\3 mcrypt 2.5 encrypted data, >4 byte 0 {invalid} >4 string x algorithm: "%s", >>&1 leshort <1 {invalid} >>&1 leshort >0 keysize: %d bytes, >>>&0 byte 0 {invalid} >>>&0 string >\0 mode: "%s", 0 string \0m\2 mcrypt 2.2 encrypted data, >3 byte 0 algorithm: blowfish-448, >3 byte 1 algorithm: DES, >3 byte 2 algorithm: 3DES, >3 byte 3 algorithm: 3-WAY, >3 byte 4 algorithm: GOST, >3 byte 6 algorithm: SAFER-SK64, >3 byte 7 algorithm: SAFER-SK128, >3 byte 8 algorithm: CAST-128, >3 byte 9 algorithm: xTEA, >3 byte 10 algorithm: TWOFISH-128, >3 byte 11 algorithm: RC2, >3 byte 12 algorithm: TWOFISH-192, >3 byte 13 algorithm: TWOFISH-256, >3 byte 14 algorithm: blowfish-128, >3 byte 15 algorithm: blowfish-192, >3 byte 16 algorithm: blowfish-256, >3 byte 100 algorithm: RC6, >3 byte 101 algorithm: IDEA, >3 byte <0 {invalid} >3 byte >101 {invalid} >3 byte >16 >>3 byte <100 {invalid} >4 byte 0 mode: CBC, >4 byte 1 mode: ECB, >4 byte 2 mode: CFB, >4 byte 3 mode: OFB, >4 byte 4 mode: nOFB, >4 byte <0 {invalid} >4 byte >4 {invalid} >5 byte 0 keymode: 8bit >5 byte 1 keymode: 4bit >5 byte 2 keymode: SHA-1 hash >5 byte 3 keymode: MD5 hash >5 byte <0 {invalid} >5 byte >3 {invalid} #------------------------------------------------------------------------------ # pgp: file(1) magic for Pretty Good Privacy # #0 beshort 0x9900 PGP key public ring #0 beshort 0x9501 PGP key security ring #0 beshort 0x9500 PGP key security ring #0 beshort 0xa600 PGP encrypted data 0 string -----BEGIN\040PGP PGP armored data, >15 string PUBLIC\040KEY\040BLOCK- public key block >15 string MESSAGE- message >15 string SIGNED\040MESSAGE- signed message >15 string PGP\040SIGNATURE- signature 0 string Salted__ OpenSSL encryption, salted, >8 belong x salt: 0x%X >12 belong x \b%X binwalk-2.1.1/src/binwalk/magic/ecos000066400000000000000000000042221263655036500173270ustar00rootroot00000000000000# eCos kernel exception handlers # # mfc0 $k0, Cause # Cause of last exception # nop # Some versions of eCos omit the nop # andi $k0, 0x7F # li $k1, 0xXXXXXXXX # add $k1, $k0 # lw $k1, 0($k1) # jr $k1 # nop 0 string \x00\x68\x1A\x40\x00\x00\x00\x00\x7F\x00\x5A\x33 eCos kernel exception handler, architecture: MIPSEL, >14 leshort !0x3C1B {invalid} >18 leshort !0x277B {invalid} >12 uleshort x exception vector table base address: 0x%.4X >16 uleshort x \b%.4X 0 string \x00\x68\x1A\x40\x7F\x00\x5A\x33 eCos kernel exception handler, architecture: MIPSEL, >10 leshort !0x3C1B {invalid} >14 leshort !0x277B {invalid} >8 uleshort x exception vector table base address: 0x%.4X >12 uleshort x \b%.4X 0 string \x40\x1A\x68\x00\x00\x00\x00\x00\x33\x5A\x00\x7F eCos kernel exception handler, architecture: MIPS, >12 beshort !0x3C1B {invalid} >16 beshort !0x277B {invalid} >14 ubeshort x exception vector table base address: 0x%.4X >18 ubeshort x \b%.4X 0 string \x40\x1A\x68\x00\x33\x5A\x00\x7F eCos kernel exception handler, architecture: MIPS, >8 beshort !0x3C1B {invalid} >12 beshort !0x277B {invalid} >10 ubeshort x exception vector table base address: 0x%.4X >14 ubeshort x \b%.4X binwalk-2.1.1/src/binwalk/magic/encoding000066400000000000000000000003551263655036500201670ustar00rootroot00000000000000# Base64 index tables 0 string ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ Base64 standard index table 0 string ACEGIKMOQSUWYBDFHJLNPRTVXZacegikmoqsuwybdfhjlnprtvxz0246813579=+/ Base64 SerComm index table binwalk-2.1.1/src/binwalk/magic/executables000066400000000000000000000645311263655036500207130ustar00rootroot00000000000000 #------------------Standard file formats------------------------------------ #------------------------------------------------------------------------------ # elf: file(1) magic for ELF executables # # We have to check the byte order flag to see what byte order all the # other stuff in the header is in. # # What're the correct byte orders for the nCUBE and the Fujitsu VPP500? # # updated by Daniel Quinlan (quinlan@yggdrasil.com) 0 string \177ELF ELF, >4 byte 0 {invalid} >4 byte 1 32-bit # only for MIPS - in the future, the ABI field of e_flags should be used. >>18 leshort 8 >>>36 lelong &0x20 N32 >>18 leshort 10 >>>36 lelong &0x20 N32 >>18 beshort 8 >>>36 belong &0x20 N32 >>18 beshort 10 >>>36 belong &0x20 N32 >4 byte 2 64-bit >4 byte >2 >>4 byte x unknown ELF class: 0x%X >5 byte !1 >>5 byte !2 {invalid} >5 byte 1 LSB # The official e_machine number for MIPS is now #8, regardless of endianness. # The second number (#10) will be deprecated later. For now, we still # say something if #10 is encountered, but only gory details for #8. >>18 leshort 8 # only for 32-bit >>>4 byte 1 >>>>36 lelong&0xf0000000 0x00000000 MIPS-I >>>>36 lelong&0xf0000000 0x10000000 MIPS-II >>>>36 lelong&0xf0000000 0x20000000 MIPS-III >>>>36 lelong&0xf0000000 0x30000000 MIPS-IV >>>>36 lelong&0xf0000000 0x40000000 MIPS-V >>>>36 lelong&0xf0000000 0x60000000 MIPS32 >>>>36 lelong&0xf0000000 0x70000000 MIPS64 >>>>36 ulelong&0xf0000000 0x80000000 MIPS32 rel2 >>>>36 ulelong&0xf0000000 0x90000000 MIPS64 rel2 # only for 64-bit >>>4 byte 2 >>>>48 lelong&0xf0000000 0x00000000 MIPS-I >>>>48 lelong&0xf0000000 0x10000000 MIPS-II >>>>48 lelong&0xf0000000 0x20000000 MIPS-III >>>>48 lelong&0xf0000000 0x30000000 MIPS-IV >>>>48 lelong&0xf0000000 0x40000000 MIPS-V >>>>48 lelong&0xf0000000 0x60000000 MIPS32 >>>>48 lelong&0xf0000000 0x70000000 MIPS64 >>>>48 ulelong&0xf0000000 0x80000000 MIPS32 rel2 >>>>48 ulelong&0xf0000000 0x90000000 MIPS64 rel2 >>16 leshort 0 no file type, >>16 leshort 1 relocatable, >>16 leshort 2 executable, >>16 leshort 3 shared object, # Core handling from Peter Tobias # corrections by Christian 'Dr. Disk' Hechelmann >>16 leshort 4 core file >>16 uleshort &0xff00 processor-specific, >>18 leshort 0 no machine, >>18 leshort 1 AT&T WE32100 - wrong byte order,{invalid} >>18 leshort 2 SPARC - wrongbyte order,{invalid} >>18 leshort 3 Intel 80386, >>18 leshort 4 Motorola >>>36 lelong &0x01000000 68000 - wrong byte order,{invalid} >>>36 lelong &0x00810000 CPU32 - wrong byte order,{invalid} >>>36 lelong 0 68020 - wrong byte order,{invalid} >>18 leshort 5 Motorola 88000 - wrong byte order,{invalid} >>18 leshort 6 Intel 80486, >>18 leshort 7 Intel 80860, >>18 leshort 8 MIPS, >>18 leshort 9 Amdahl - wrong byte order,{invalid} >>18 leshort 10 MIPS (deprecated), >>18 leshort 11 RS6000 - wrong byte order,{invalid} >>18 leshort 15 PA-RISC - wrong byte order,{invalid} >>>50 leshort 0x0214 2.0 >>>48 leshort &0x0008 (LP64), >>18 leshort 16 nCUBE, >>18 leshort 17 Fujitsu VPP500, >>18 leshort 18 SPARC32PLUS, >>18 leshort 20 PowerPC, >>18 leshort 22 IBM S/390, >>18 leshort 36 NEC V800, >>18 leshort 37 Fujitsu FR20, >>18 leshort 38 TRW RH-32, >>18 leshort 39 Motorola RCE, >>18 leshort 40 ARM, >>18 leshort 41 Alpha, >>18 uleshort 0xa390 IBM S/390 (obsolete), >>18 leshort 42 Hitachi SH, >>18 leshort 43 SPARC V9 - wrong byte order,{invalid} >>18 leshort 44 Siemens Tricore Embedded Processor, >>18 leshort 45 Argonaut RISC Core, Argonaut Technologies Inc., >>18 leshort 46 Hitachi H8/300, >>18 leshort 47 Hitachi H8/300H, >>18 leshort 48 Hitachi H8S, >>18 leshort 49 Hitachi H8/500, >>18 leshort 50 IA-64 (Intel 64 bit architecture) >>18 leshort 51 Stanford MIPS-X, >>18 leshort 52 Motorola Coldfire, >>18 leshort 53 Motorola M68HC12, >>18 leshort 62 AMD x86-64, >>18 leshort 75 Digital VAX, >>18 leshort 97 NatSemi 32k, >>18 uleshort 0x9026 Alpha (unofficial), >>20 lelong 0 {invalid} invalid version >>20 lelong 1 version 1 >>36 lelong 1 MathCoPro/FPU/MAU Required >5 byte 2 MSB # only for MIPS - see comment in little-endian section above. >>18 beshort 8 # only for 32-bit >>>4 byte 1 >>>>36 belong&0xf0000000 0x00000000 MIPS-I >>>>36 belong&0xf0000000 0x10000000 MIPS-II >>>>36 belong&0xf0000000 0x20000000 MIPS-III >>>>36 belong&0xf0000000 0x30000000 MIPS-IV >>>>36 belong&0xf0000000 0x40000000 MIPS-V >>>>36 belong&0xf0000000 0x60000000 MIPS32 >>>>36 belong&0xf0000000 0x70000000 MIPS64 >>>>36 ubelong&0xf0000000 0x80000000 MIPS32 rel2 >>>>36 ubelong&0xf0000000 0x90000000 MIPS64 rel2 # only for 64-bit >>>4 byte 2 >>>>48 belong&0xf0000000 0x00000000 MIPS-I >>>>48 belong&0xf0000000 0x10000000 MIPS-II >>>>48 belong&0xf0000000 0x20000000 MIPS-III >>>>48 belong&0xf0000000 0x30000000 MIPS-IV >>>>48 belong&0xf0000000 0x40000000 MIPS-V >>>>48 belong&0xf0000000 0x60000000 MIPS32 >>>>48 belong&0xf0000000 0x70000000 MIPS64 >>>>48 ubelong&0xf0000000 0x80000000 MIPS32 rel2 >>>>48 ubelong&0xf0000000 0x90000000 MIPS64 rel2 >>16 beshort 0 no file type, >>16 beshort 1 relocatable, >>16 beshort 2 executable, >>16 beshort 3 shared object, >>16 beshort 4 core file, #>>>(0x38+0xcc) string >\0 of '%s' #>>>(0x38+0x10) belong >0 (signal %d), >>16 ubeshort &0xff00 processor-specific, >>18 beshort 0 no machine, >>18 beshort 1 AT&T WE32100, >>18 beshort 2 SPARC, >>18 beshort 3 Intel 80386 - wrong byte order,{invalid} >>18 beshort 4 Motorola >>>36 belong &0x01000000 68000, >>>36 belong &0x00810000 CPU32, >>>36 belong 0 68020, >>18 beshort 5 Motorola 88000, >>18 beshort 6 Intel 80486 - wrong byte order,{invalid} >>18 beshort 7 Intel 80860 - wrong byte order,{invalid} >>18 beshort 8 MIPS, >>18 beshort 9 Amdahl, >>18 beshort 10 MIPS (deprecated), >>18 beshort 11 RS6000, >>18 beshort 15 PA-RISC >>>50 beshort 0x0214 2.0 >>>48 beshort &0x0008 (LP64) >>18 beshort 16 nCUBE, >>18 beshort 17 Fujitsu VPP500, >>18 beshort 18 SPARC32PLUS, >>>36 belong&0xffff00 &0x000100 V8+ Required, >>>36 belong&0xffff00 &0x000200 Sun UltraSPARC1 Extensions Required, >>>36 belong&0xffff00 &0x000400 HaL R1 Extensions Required, >>>36 belong&0xffff00 &0x000800 Sun UltraSPARC3 Extensions Required, >>18 beshort 20 PowerPC or cisco 4500, >>18 beshort 21 cisco 7500, >>18 beshort 22 IBM S/390, >>18 beshort 24 cisco SVIP, >>18 beshort 25 cisco 7200, >>18 beshort 36 NEC V800 or cisco 12000, >>18 beshort 37 Fujitsu FR20, >>18 beshort 38 TRW RH-32, >>18 beshort 39 Motorola RCE, >>18 beshort 40 ARM, >>18 beshort 41 Alpha, >>18 beshort 42 Hitachi SH, >>18 beshort 43 SPARC V9, >>18 beshort 44 Siemens Tricore Embedded Processor, >>18 beshort 45 Argonaut RISC Core, Argonaut Technologies Inc., >>18 beshort 46 Hitachi H8/300, >>18 beshort 47 Hitachi H8/300H, >>18 beshort 48 Hitachi H8S, >>18 beshort 49 Hitachi H8/500, >>18 beshort 50 Intel Merced Processor, >>18 beshort 51 Stanford MIPS-X, >>18 beshort 52 Motorola Coldfire, >>18 beshort 53 Motorola M68HC12, >>18 beshort 73 Cray NV1, >>18 beshort 75 Digital VAX, >>18 beshort 97 NatSemi 32k, >>18 ubeshort 0x9026 Alpha (unofficial), >>18 ubeshort 0xa390 IBM S/390 (obsolete), >>18 ubeshort 0xde3d Ubicom32, >>20 belong 0 {invalid}invalid version >>20 belong 1 version 1 >>36 belong 1 MathCoPro/FPU/MAU Required # Up to now only 0, 1 and 2 are defined; I've seen a file with 0x83, it seemed # like proper ELF, but extracting the string had bad results. >4 byte <0x80 >>8 byte !0 >>>8 string x ("%s") >8 byte 0 >>7 byte 0 (SYSV) >>7 byte 1 (HP-UX) >>7 byte 2 (NetBSD) >>7 byte 3 (GNU/Linux) >>7 byte 4 (GNU/Hurd) >>7 byte 5 (86Open) >>7 byte 6 (Solaris) >>7 byte 7 (Monterey) >>7 byte 8 (IRIX) >>7 byte 9 (FreeBSD) >>7 byte 10 (Tru64) >>7 byte 11 (Novell Modesto) >>7 byte 12 (OpenBSD) >>7 byte 97 (ARM) >>7 ubyte 255 (embedded) # Some simple Microsoft executable signatures 0 string MZ\0\0\0\0\0\0 Microsoft executable, >0x3c lelong <4 {invalid} >(0x3c.l) string !PE\0\0 MS-DOS >(0x3c.l) string PE\0\0 portable (PE) 0 string MZ Microsoft executable, >0x3c lelong <4 {invalid} >(0x3c.l) string !PE\0\0 {invalid} >(0x3c.l) string PE\0\0 portable (PE) #------------------------------------------------------------------------------ # bFLT: file(1) magic for BFLT uclinux binary files # # From Philippe De Muyter # # Additional fields added by Craig Heffner # 0 string bFLT BFLT executable >4 belong <1 {invalid} >4 belong >4 {invalid} >4 belong x version %d, >8 ubelong x code offset: 0x%.8X, >12 ubelong x data segment starts at: 0x%.8X, >16 ubelong x bss segment starts at: 0x%.8X, >20 ubelong x bss segment ends at: 0x%.8X, >24 ubelong x stack size: %d bytes, >28 ubelong x relocation records start at: 0x%.8X, >32 ubelong x number of reolcation records: %d, >>36 belong&0x1 0x1 ram >>36 belong&0x2 0x2 gotpic >>36 belong&0x4 0x4 gzip >>36 belong&0x8 0x8 gzdata # Windows CE package files 0 string MSCE\0\0\0\0 Microsoft WinCE installer >20 lelong 0 \b, architecture-independent >20 lelong 103 \b, Hitachi SH3 >20 lelong 104 \b, Hitachi SH4 >20 lelong 0xA11 \b, StrongARM >20 lelong 4000 \b, MIPS R4000 >20 lelong 10003 \b, Hitachi SH3 >20 lelong 10004 \b, Hitachi SH3E >20 lelong 10005 \b, Hitachi SH4 >20 lelong 70001 \b, ARM 7TDMI >52 leshort 1 \b, 1 file >52 uleshort >1 \b, %u files >56 leshort 1 \b, 1 registry entry >56 uleshort >1 \b, %u registry entries #------------------------------------------------------------------------------ # motorola: file(1) magic for Motorola 68K and 88K binaries # # 68K # # These signatures are useless without further sanity checking. Disable them until # that can be implemented. #0 beshort 0x0208 mc68k COFF #>18 beshort ^00000020 object #>18 beshort &00000020 executable #>12 belong >0 not stripped #>168 string .lowmem Apple toolbox #>20 beshort 0407 (impure) #>20 beshort 0410 (pure) #>20 beshort 0413 (demand paged) #>20 beshort 0421 (standalone) #0 beshort 0x0209 mc68k executable (shared) #>12 belong >0 not stripped #0 beshort 0x020A mc68k executable (shared demand paged) #>12 belong >0 not stripped #------------------------------------------------------------------------------ # Sony Playstation executables (Adam Sjoegren ) : 0 string PS-X\x20EXE Sony Playstation executable, # Area: >113 string x "%s" #------------------------------------------------------------------------------ # cisco: file(1) magic for cisco Systems routers # # Most cisco file-formats are covered by the generic elf code 0 string \x85\x01\x14 Cisco IOS microcode, >7 byte 0 {invalid} >7 string x for "%s" 0 string \x85\x01\xcb Cisco IOS experimental microcode, >7 byte 0 {invalid} >7 string x for "%s" # EST flat binary format (which isn't, but anyway) # From: Mark Brown 0 string ESTFBINR EST flat binary # These are not the binaries themselves, but string references to them # are a strong indication that they exist elsewhere... #0 string /bin/busybox Busybox string reference: "%s"{one-of-many} #0 string /bin/sh Shell string reference: "%s"{one-of-many} # Mach-O's 0 string \xca\xfe\xba\xbe\x00\x00\x00\x01 Mach-O universal binary with 1 architecture 0 string \xca\xfe\xba\xbe\x00\x00\x00\x02 Mach-O universal binary with 2 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x03 Mach-O universal binary with 3 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x04 Mach-O universal binary with 4 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x05 Mach-O universal binary with 5 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x06 Mach-O universal binary with 6 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x07 Mach-O universal binary with 7 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x08 Mach-O universal binary with 8 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0a Mach-O universal binary with 9 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0b Mach-O universal binary with 10 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0c Mach-O universal binary with 11 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0d Mach-O universal binary with 12 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0e Mach-O universal binary with 13 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x0f Mach-O universal binary with 14 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x10 Mach-O universal binary with 15 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x11 Mach-O universal binary with 16 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x12 Mach-O universal binary with 17 architectures 0 string \xca\xfe\xba\xbe\x00\x00\x00\x13 Mach-O universal binary with 18 architectures # The magic bytes for Java .class files is 0xcafebabe, but AFAIK all major version numbers are less than 255 # and all minor version numbers are 0. This gives us three more bytes we can signature on. 0 string \xca\xfe\xba\xbe\x00\x00\x00 Compiled Java class data, >6 beshort x version %d. >4 beshort x \b%d # Which is which? >4 belong 0x032d (Java 1.0/1.1) #>4 belong 0x032d (Java 1.1) >4 belong 0x002e (Java 1.2) >4 belong 0x002f (Java 1.3) >4 belong 0x0030 (Java 1.4) >4 belong 0x0031 (Java 1.5) >4 belong 0x0032 (Java 1.6) >4 belong >0x0050 {invalid} # Summary: HP-38/39 calculator 0 string HP38Bin HP 38 binary >7 string A (Directory List) >7 string B (Zaplet) >7 string C (Note) >7 string D (Program) >7 string E (Variable) >7 string F (List) >7 string G (Matrix) >7 string H (Library) >7 string I (Target List) >7 string J (ASCII Vector specification) >7 string K (wildcard) >7 byte <0x41 {invalid} >7 byte >0x4B {invalid} 0 string HP39Bin HP 39 binary >7 string A (Directory List) >7 string B (Zaplet) >7 string C (Note) >7 string D (Program) >7 string E (Variable) >7 string F (List) >7 string G (Matrix) >7 string H (Library) >7 string I (Target List) >7 string J (ASCII Vector specification) >7 string K (wildcard) >7 byte <0x41 {invalid} >7 byte >0x4B {invalid} 0 string HP38Asc HP 38 ASCII >7 string A (Directory List) >7 string B (Zaplet) >7 string C (Note) >7 string D (Program) >7 string E (Variable) >7 string F (List) >7 string G (Matrix) >7 string H (Library) >7 string I (Target List) >7 string J (ASCII Vector specification) >7 string K (wildcard) >7 byte <0x41 {invalid} >7 byte >0x4B {invalid} 0 string HP39Asc HP 39 ASCII >7 string A (Directory List) >7 string B (Zaplet) >7 string C (Note) >7 string D (Program) >7 string E (Variable) >7 string F (List) >7 string G (Matrix) >7 string H (Library) >7 string I (Target List) >7 string J (ASCII Vector specification) >7 string K (wildcard) >7 byte <0x41 {invalid} >7 byte >0x4B {invalid} # Summary: HP-48/49 calculator 0 string HPHP48 HP 48 binary >8 leshort 0x2911 (ADR) >8 leshort 0x2933 (REAL) >8 leshort 0x2955 (LREAL) >8 leshort 0x2977 (COMPLX) >8 leshort 0x299d (LCOMPLX) >8 leshort 0x29bf (CHAR) >8 leshort 0x29e8 (ARRAY) >8 leshort 0x2a0a (LNKARRAY) >8 leshort 0x2a2c (STRING) >8 leshort 0x2a4e (HXS) >8 leshort 0x2a74 (LIST) >8 leshort 0x2a96 (DIR) >8 leshort 0x2ab8 (ALG) >8 leshort 0x2ada (UNIT) >8 leshort 0x2afc (TAGGED) >8 leshort 0x2b1e (GROB) >8 leshort 0x2b40 (LIB) >8 leshort 0x2b62 (BACKUP) >8 leshort 0x2b88 (LIBDATA) >8 leshort 0x2d9d (PROG) >8 leshort 0x2dcc (CODE) >8 leshort 0x2e48 (GNAME) >8 leshort 0x2e6d (LNAME) >8 leshort 0x2e92 (XLIB) >8 leshort <0x2911 {invalid} >8 leshort >0x2e92 {invalid} 0 string HPHP49 HP 49 binary >8 leshort 0x2911 (ADR) >8 leshort 0x2933 (REAL) >8 leshort 0x2955 (LREAL) >8 leshort 0x2977 (COMPLX) >8 leshort 0x299d (LCOMPLX) >8 leshort 0x29bf (CHAR) >8 leshort 0x29e8 (ARRAY) >8 leshort 0x2a0a (LNKARRAY) >8 leshort 0x2a2c (STRING) >8 leshort 0x2a4e (HXS) >8 leshort 0x2a74 (LIST) >8 leshort 0x2a96 (DIR) >8 leshort 0x2ab8 (ALG) >8 leshort 0x2ada (UNIT) >8 leshort 0x2afc (TAGGED) >8 leshort 0x2b1e (GROB) >8 leshort 0x2b40 (LIB) >8 leshort 0x2b62 (BACKUP) >8 leshort 0x2b88 (LIBDATA) >8 leshort 0x2d9d (PROG) >8 leshort 0x2dcc (CODE) >8 leshort 0x2e48 (GNAME) >8 leshort 0x2e6d (LNAME) >8 leshort 0x2e92 (XLIB) >8 leshort <0x2911 {invalid} >8 leshort >0x2e92 {invalid} 0 string \x23\x21/ Executable script, >6 byte !0x2F >>7 byte !0x2F {invalid} >2 string x shebang: "%s" 0 string \x23\x21\x20/ Executable script, >7 byte !0x2F >>8 byte !0x2F {invalid} >3 string x shebang: "%s" binwalk-2.1.1/src/binwalk/magic/filesystems000066400000000000000000000615501263655036500207540ustar00rootroot00000000000000#--------------------File Systems--------------------- # Minix filesystems 0x410 string \x7f\x13\x00\x00\x00\x00 Minix filesystem, V1, little endian, >0x402 beshort x %d zones >0x1e string minix \b, bootable >0x1e string !minix >>0x1e string !\x00\x00\x00\x00\x00 {invalid} 0x410 string \x13\x7f\x00\x00\x00\x00 Minix filesystem, V1, big endian, >0x402 beshort x %d zones >0x1e string minix \b, bootable >0x1e string !minix >>0x1e string !\x00\x00\x00\x00\x00 {invalid} 0x410 string \x8f\x13\x00\x00\x00\x00 Minix filesystem, V1, little endian, 30 char names, >0x402 beshort x %d zones >0x1e string minix \b, bootable >0x1e string !minix >>0x1e string !\x00\x00\x00\x00\x00 {invalid} 0x410 string \x13\x8f\x00\x00\x00\x00 Minix filesystem, V1, big endian, 30 char names, >0x402 beshort x %d zones >0x1e string minix \b, bootable >0x1e string !minix >>0x1e string !\x00\x00\x00\x00\x00 {invalid} #0x410 leshort 0x2468 Minix filesystem, V2, little endian, #>0x402 beshort x %d zones #>0x1e string minix \b, bootable #0x410 beshort 0x2468 Minix filesystem, V2, big endian, #>0x402 beshort x %d zones #>0x1e string minix \b, bootable # YAFFS 0 string \x03\x00\x00\x00\x01\x00\x00\x00\xFF\xFF YAFFS filesystem # EFS2 file system - jojo@utulsa.edu 0 lelong 0x53000000 EFS2 Qualcomm filesystem super block, little endian, >8 string !EFSSuper {invalid}, >4 leshort&0x01 1 NAND >4 leshort&0x01 0 NOR >4 leshort x version 0x%x, >24 lelong x %d blocks, >16 lelong x 0x%x pages per block, >20 lelong x 0x%x bytes per page 0 belong 0x53000000 EFS2 Qualcomm filesystem super block, big endian, >8 string !SSFErepu {invalid}, >4 beshort&0x01 1 NAND >4 beshort&0x01 0 NOR >4 beshort x version 0x%x, >24 belong x %d blocks, >16 belong x 0x%x pages per block, >20 belong x 0x%x bytes per page # TROC file system 0 string TROC TROC filesystem, >4 lelong x %d file entries >4 lelong <1 {invalid} # PFS file system 0 string PFS/ PFS filesystem, >5 byte !0x2E {invalid} >4 string x version %s, >14 leshort x %d files # MPFS file system 0 string MPFS MPFS filesystem, Microchop, >4 byte <0 {invalid} >5 byte <0 {invalid} >4 byte x version %d. >5 byte x \b%d, >6 leshort <0 {invalid} >6 leshort x %d file entries # cramfs filesystem - russell@coker.com.au 0 lelong 0x28cd3d45 CramFS filesystem, little endian, >4 lelong <0 {invalid} >4 lelong >1073741824 {invalid} >4 ulelong x size: %u >8 lelong &1 version 2 >8 lelong &2 sorted_dirs >8 lelong &4 hole_support >32 ulelong x CRC 0x%.8X, >36 ulelong x edition %u, >40 lelong <0 {invalid} >40 ulelong x %u blocks, >44 lelong <0 {invalid} >44 ulelong x %u files >4 ulelong x {jump:%u} >4 ulelong x {size:%u} 0 belong 0x28cd3d45 CramFS filesystem, big endian >4 belong <0 {invalid} >4 belong >1073741824 {invalid} >4 belong x size %u >8 belong &1 version 2 >8 belong &2 sorted_dirs >8 belong &4 hole_support >32 ubelong x CRC 0x%.8X, >36 belong x edition %u, >40 belong <0 {invalid} >40 ubelong x %u blocks, >44 belong <0 {invalid} >44 ubelong x %u files >4 ubelong x {jump:%u} >4 ubelong x {size:%u} # http://www.dubeiko.com/development/FileSystems/UBI/ubidesign.pdf 0 string UBI! UBI volume ID header, >4 ubyte x version: %d, >5 ubyte x type: %d, >8 ubelong x volume id: %d, >8 ubelong >256 {invalid} >12 ubelong x size: %d >12 ubelong !0 {invalid} >28 string !\x00*12 {invalid} # http://lxr.free-electrons.com/source/fs/ubifs/ubifs-media.h 0 string UBI\x23 UBI erase count header, >4 ubyte x version: %d, >5 string !\x00*3 {invalid} >8 ubequad x EC: 0x%lX, >16 ubelong x VID header offset: 0x%X, >20 ubelong x data offset: 0x%X # dummy jump - actual jump value is determined in UBIValidPlugin >20 ubyte x {jump:0} # http://lxr.free-electrons.com/source/fs/ubifs/ubifs-media.h 0 lelong 0x06101831 UBIFS filesystem >20 ubyte <6 {invalid} >20 ubyte >7 {invalid} # Only look for superblock and master nodes >22 leshort !0 {invalid} # 2 bytes of padding should be filled with NULLs >20 ubyte 6 superblock node, >20 ubyte 7 master node, >4 ulelong x CRC: 0x%X, # Master node >20 ubyte 7 >>24 lequad x highest inode: %d, >>32 lequad x commit number: %d # Superblock node >20 ubyte 6 >>24 leshort !0 {invalid} >>28 ulelong x flags: 0x%X, >>32 lelong x min I/O unit size: %d, >>36 lelong x erase block size: %d, >>40 lelong x erase block count: %d, >>44 lelong x max erase blocks: %d, >>80 lelong x format version: %d, >>84 uleshort >2 {invalid} >>84 uleshort x compression type: >>84 uleshort 0 none >>84 uleshort 1 lzo >>84 uleshort 2 zlib >>86 leshort !0 invalid padding,{invalid} >>128 string !\x00*3968 invalid padding{invalid} # JFFS2 file system # If used with binwalk's smart signature feature (on by default, -S to disable) # this signature can potentially lead to missing some JFFS2 file systems if there # are multiple JFFS2 file systems in a target file and there are no other identified # files in between the JFFS2 file systems. This is an unlikely scenario however, and # the below signatures are much improved in terms of readability and accuracy in the # vast majority of real world scenarios. 0 uleshort 0x1985 JFFS2 filesystem, little endian >2 uleshort !0xE001 >>2 uleshort !0xE002 >>>2 uleshort !0x2003 >>>>2 uleshort !0x2004 >>>>>2 uleshort !0x2006 >>>>>>2 uleshort !0xE008 >>>>>>>2 uleshort !0xE009 {invalid} >4 lelong 0 {invalid} >4 lelong <0 {invalid} >4 lelong x {many}{jump:%d} 0 ubeshort 0x1985 JFFS2 filesystem, big endian >2 ubeshort !0xE001 >>2 ubeshort !0xE002 >>>2 ubeshort !0x2003 >>>>2 ubeshort !0x2004 >>>>>2 ubeshort !0x2006 >>>>>>2 ubeshort !0xE008 >>>>>>>2 ubeshort !0xE009 {invalid} >4 belong 0 {invalid} >4 belong <0 {invalid} >4 belong x {many}{jump:%d} # Squashfs, big endian 0 string sqsh Squashfs filesystem, big endian, >28 beshort >10 {invalid} >28 beshort <1 {invalid} >30 beshort >10 {invalid} >28 beshort x version %d. >30 beshort x \b%d, >28 beshort >3 compression: >>20 beshort 1 \bgzip, >>20 beshort 2 \blzma, >>20 beshort 3 \bgzip (non-standard type definition), >>20 beshort 4 \bxz, >>20 beshort 0 \b{invalid}, >>20 beshort >4 \b{invalid}, >28 beshort <3 >>8 belong x size: %d bytes, >>8 belong x \b{jump:%d} >>8 belong x \b{size:%d} >28 beshort 3 >>63 bequad x size: %ld bytes, >>63 bequad x \b{jump:%ld} >>63 bequad x \b{size:%ld} >28 beshort >3 >>40 bequad x size: %ld bytes, >>40 bequad x \b{jump:%ld} >>40 bequad x \b{size:%ld} >4 belong x %d inodes, >28 beshort >3 >>12 belong x blocksize: %d bytes, >28 beshort <2 >>32 beshort x blocksize: %d bytes, >28 beshort 2 >>51 belong x blocksize: %d bytes, >28 beshort 3 >>51 belong x blocksize: %d bytes, >28 beshort <4 >>39 bedate x created: %s >28 beshort >3 >>8 bedate x created: %s # Squashfs, little endian 0 string hsqs Squashfs filesystem, little endian, >28 leshort >10 {invalid} >28 leshort <1 {invalid} >30 leshort >10 {invalid} >28 leshort x version %d. >30 leshort x \b%d, >28 leshort >3 compression: >>20 leshort 1 \bgzip, >>20 leshort 2 \blzma, >>20 leshort 3 \bgzip (non-standard type definition), >>20 leshort 4 \bxz, >>20 leshort 0 \b{invalid}, >>20 leshort >4 \b{invalid}, >28 leshort <3 >>8 lelong x size: %d bytes, >>8 lelong x {size:%d} >28 leshort 3 >>63 lequad x size: %ld bytes, >>63 lequad x {size:%ld} >28 leshort >3 >>40 lequad x size: %ld bytes, >>40 lequad x {size:%ld} >4 lelong x %d inodes, >28 leshort >3 >>12 lelong x blocksize: %d bytes, >28 leshort <2 >>32 leshort x blocksize: %d bytes, >28 leshort 2 >>51 lelong x blocksize: %d bytes, >28 leshort 3 >>51 lelong x blocksize: %d bytes, >28 leshort <4 >>39 ledate x created: %s >28 leshort >3 >>8 ledate x created: %s >28 leshort <3 >>8 lelong x {jump:%d} >28 leshort 3 >>63 lequad x {jump:%ld} >28 leshort >3 >>40 lequad x {jump:%ld} # Squashfs with LZMA compression 0 string sqlz Squashfs filesystem, big endian, lzma compression, >28 beshort >10 {invalid} >28 beshort <1 {invalid} >30 beshort >10 {invalid} >28 beshort x version %d. >30 beshort x \b%d, >28 beshort >3 compression: >>20 beshort 1 \bgzip, >>20 beshort 2 \blzma, >>20 beshort 3 \bgzip (non-standard type definition), >>20 beshort 4 \blzma (non-standard type definition), >>20 beshort 0 \b{invalid}, >>20 beshort >4 \b{invalid}, >28 beshort <3 >>8 belong x size: %d bytes, >>8 belong x {size:%d} >28 beshort 3 >>63 bequad x size: %ld bytes, >>63 bequad x {size:%ld} >28 beshort >3 >>40 bequad x size: %ld bytes, >>40 bequad x {size:%ld} >4 belong x %d inodes, >28 beshort >3 >>12 belong x blocksize: %d bytes, >28 beshort <2 >>32 beshort x blocksize: %d bytes, >28 beshort 2 >>51 belong x blocksize: %d bytes, >28 beshort 3 >>51 belong x blocksize: %d bytes, >28 beshort <4 >>39 bedate x created: %s >28 beshort >3 >>8 bedate x created: %s >28 beshort <3 >>8 belong x {jump:%d} >28 beshort 3 >>63 bequad x {jump:%ld} >28 beshort >3 >>40 bequad x {jump:%ld} # Squashfs 3.3 LZMA signature 0 string qshs Squashfs filesystem, big endian, lzma signature, >28 beshort >10 {invalid} >28 beshort <1 {invalid} >30 beshort >10 {invalid} >28 beshort x version %d. >30 beshort x \b%d, >28 beshort >3 compression: >>20 beshort 1 \bgzip, >>20 beshort 2 \blzma, >>20 beshort 3 \bgzip (non-standard type definition), >>20 beshort 4 \bxz, >>20 beshort 0 \b{invalid}, >>20 beshort >4 \b{invalid}, >28 beshort <3 >>8 belong x size: %d bytes, >>8 belong x {size:%d} >28 beshort 3 >>63 bequad x size: %ld bytes, >>63 bequad x {size:%ld} >28 beshort >3 >>40 bequad x size: %ld bytes, >>40 bequad x {size:%ld} >4 belong x %d inodes, >28 beshort >3 >>12 belong x blocksize: %d bytes, >28 beshort <2 >>32 beshort x blocksize: %d bytes, >28 beshort 2 >>51 belong x blocksize: %d bytes, >28 beshort 3 >>51 belong x blocksize: %d bytes, >28 beshort <4 >>39 bedate x created: %s >28 beshort >3 >>8 bedate x created: %s >28 beshort <3 >>8 belong x {jump:%d} >28 beshort 3 >>63 bequad x {jump:%ld} >28 beshort >3 >>40 bequad x {jump:%ld} # Squashfs for DD-WRT 0 string tqsh Squashfs filesystem, big endian, DD-WRT signature, >28 beshort >10 {invalid} >28 beshort <1 {invalid} >30 beshort >10 {invalid} >28 beshort x version %d. >30 beshort x \b%d, >28 beshort >3 compression: >>20 beshort 1 \bgzip, >>20 beshort 2 \blzma, >>20 beshort 3 \bgzip (non-standard type definition), >>20 beshort 4 \bxz, >>20 beshort 0 \b{invalid}, >>20 beshort >4 \b{invalid}, >28 beshort <3 >>8 belong x size: %d bytes, >>8 belong x {size:%d} >28 beshort 3 >>63 bequad x size: %ld bytes, >>63 bequad x {size:%ld} >28 beshort >3 >>40 bequad x size: %ld bytes, >>40 bequad x {size:%ld} >4 belong x %d inodes, >28 beshort >3 >>12 belong x blocksize: %d bytes, >28 beshort <2 >>32 beshort x blocksize: %d bytes, >28 beshort 2 >>51 belong x blocksize: %d bytes, >28 beshort 3 >>51 belong x blocksize: %d bytes, >28 beshort <4 >>39 bedate x created: %s >28 beshort >3 >>8 bedate x created: %s >28 beshort <3 >>8 belong x {jump:%d} >28 beshort 3 >>63 bequad x {jump:%ld} >28 beshort >3 >>40 bequad x {jump:%ld} # Squashfs for DD-WRT 0 string hsqt Squashfs filesystem, little endian, DD-WRT signature, >28 leshort >10 {invalid} >28 leshort <1 {invalid} >30 leshort >10 {invalid} >28 leshort x version %d. >30 leshort x \b%d, >28 leshort >3 compression: >>20 leshort 1 \bgzip, >>20 leshort 2 \blzma, >>20 leshort 3 \bgzip (non-standard type definition), >>20 leshort 4 \bxz, >>20 leshort 0 \b{invalid}, >>20 leshort >4 \b{invalid}, >28 leshort <3 >>8 lelong x size: %d bytes, >>8 lelong x {size:%d} >28 leshort 3 >>63 lequad x size: %ld bytes, >>63 lequad x {size:%ld} >28 leshort >3 >>40 lequad x size: %ld bytes, >>40 lequad x {size:%ld} >4 lelong x %d inodes, >28 leshort >3 >>12 lelong x blocksize: %d bytes, >28 leshort <2 >>32 leshort x blocksize: %d bytes, >28 leshort 2 >>51 lelong x blocksize: %d bytes, >28 leshort 3 >>51 lelong x blocksize: %d bytes, >28 leshort <4 >>39 ledate x created: %s >28 leshort >3 >>8 ledate x created: %s >28 leshort <3 >>8 lelong x {jump:%d} >28 leshort 3 >>63 lequad x {jump:%ld} >28 leshort >3 >>40 lequad x {jump:%ld} # Non-standard Squashfs signature found on some D-Link routers 0 string shsq Squashfs filesystem, little endian, non-standard signature, >28 leshort >10 {invalid} >28 leshort <1 {invalid} >30 leshort >10 {invalid} >28 leshort x version %d. >30 leshort x \b%d, >28 leshort >3 compression: >>20 leshort 1 \bgzip, >>20 leshort 2 \blzma, >>20 leshort 3 \bgzip (non-standard type definition), >>20 leshort 4 \bxz, >>20 leshort 0 \b{invalid}, >>20 leshort >4 \b{invalid}, >28 leshort <3 >>8 lelong x size: %d bytes, >>8 lelong x {size:%d} >28 leshort 3 >>63 lequad x size: %ld bytes, >>63 lequad x {size:%ld} >28 leshort >3 >>40 lequad x size: %ld bytes, >>40 lequad x {size:%ld} >4 lelong x %d inodes, >28 leshort >3 >>12 lelong x blocksize: %d bytes, >28 leshort <2 >>32 leshort x blocksize: %d bytes, >28 leshort 2 >>51 lelong x blocksize: %d bytes, >28 leshort 3 >>51 lelong x blocksize: %d bytes, >28 leshort <4 >>39 ledate x created: %s >28 leshort >3 >>8 ledate x created: %s >28 leshort <3 >>8 lelong x {jump:%d} >28 leshort 3 >>63 lequad x {jump:%ld} >28 leshort >3 >>40 lequad x {jump:%ld} # ext2/ext3 filesystems - Andreas Dilger # ext4 filesystem - Eric Sandeen # volume label and UUID Russell Coker # http://etbe.coker.com.au/2008/07/08/label-vs-uuid-vs-device/ 0x438 uleshort 0xEF53 Linux EXT filesystem, >0x43A leshort >4 {invalid}invalid state >0x43A leshort 3 {invalid}invalid state >0x43A leshort <0 {invalid}invalid state >0x43C leshort >3 {invalid}invalid error behavior >0x43C leshort <0 {invalid}invalid error behavior >0x43C lelong >1 {invalid}invalid major revision >0x43C lelong <0 {invalid}invalid major revision >0x43C lelong x rev %d >0x43E leshort x \b.%d, # No journal? ext2 >0x438+36 lelong&0x04 0 ext2 filesystem data >>0x43A leshort&0x01 0 (mounted or unclean) # Has a journal? ext3 or ext4 >0x438+36 lelong &0x0000004 # and small INCOMPAT? >>0x438+40 lelong <0x0000040 # and small RO_COMPAT? >>>0x438+44 lelong <0x0000008 ext3 filesystem data # else large RO_COMPAT? >>>0x438+44 lelong >0x0000007 ext4 filesystem data # else large INCOMPAT? >>0x438+40 lelong >0x000003f ext4 filesystem data >0x438+48 ubelong x \b, UUID=%08x >0x438+52 ubeshort x \b-%04x >0x438+54 ubeshort x \b-%04x >0x438+56 ubeshort x \b-%04x >0x438+58 ubelong x \b-%08x >0x438+60 ubeshort x \b%04x >0x438+64 byte !0 >>0x438+64 string x \b, volume name "%s" #romfs filesystems - Juan Cespedes 0 string -rom1fs-\0 romfs filesystem, version 1 >8 belong >10000000 {invalid} >8 belong <1 {invalid} >8 belong x size: %d bytes, >16 string x {name:%s} >16 string x named "%s" >8 belong x {size:%d} >8 belong x {jump:%d} # Wind River MemFS file system, found in some VxWorks devices 0 string owowowowowowowowowowowowowowow Wind River management filesystem,{overlap} >30 string !ow {invalid}, >32 belong 1 compressed, >32 belong 2 plain text, >32 belong <1 {invalid} >32 belong >2 {invalid} >36 belong x %d files # Wind River MemFS file system, found in some VxWorks devices 0 string OWOWOWOWOWOWOWOWOWOWOWOWOWOWOW Wind River management filesystem,{overlap} >30 string !OW {invalid}, >32 lelong 1 compressed, >32 lelong 2 plain text, >32 lelong <1 {invalid} >32 lelong >2 {invalid} >36 lelong x %d files # netboot image - Juan Cespedes 0 lelong 0x1b031336 Netboot image, >4 lelong&0xFFFFFF00 0 >>4 lelong&0x100 0x000 mode 2 >>4 lelong&0x100 0x100 mode 3 >4 lelong&0xFFFFFF00 !0 unknown mode {invalid} 18 string WDK\x202.0\x00 WDK file system, version 2.0 32769 string CD001 ISO >6144 string !NSR0 9660 CD-ROM filesystem data, >6144 string NSR0 UDF filesystem data, >6148 string 1 version 1.0, >6148 string 2 version 2.0, >6148 string 3 version 3.0 >6148 byte >0x33 {invalid} version, >6148 byte <0x31 {invalid} version, >38 byte !0 >>38 string x volume name: "%s", >2047 string \000CD001\001EL\x20TORITO\x20SPECIFICATION bootable # updated by Joerg Jenderek at Nov 2012 # DOS Emulator image is 128 byte, null right padded header + harddisc image 0 string DOSEMU\0 DOS Emulator image >0x27E leshort !0xAA55 {invalid} >0x27E leshort 0xAA55 #offset is 128 >>19 byte 128 >>>(19.b-1) byte 0x0 >>>>7 lelong >0 \b, %d heads >>>>11 lelong >0 \b, %d sectors/track >>>>15 lelong >0 \b, %d cylinders # From: Alex Beregszaszi 0 string COWD\x03 VMWare3 disk image, >32 lelong x (%d/ >36 lelong x \b%d/ >40 lelong x \b%d) 0 string COWD\x02 VMWare3 undoable disk image, >32 byte !0 >32 string x "%s" # TODO: Add header validation 0 string VMDK VMware4 disk image 0 string KDMV VMware4 disk image #-------------------------------------------------------------------- # Qemu Emulator Image # Lines written by Friedrich Schwittay (f.schwittay@yousable.de) # Updated by Adam Buchbinder (adam.buchbinder@gmail.com) # Made by reading sources, reading documentation, and doing trial and error # on existing QCOW files 0 string QFI\xFB QEMU QCOW Image # BSD 2.x file system image; used in RetroBSD for PIC32. 0 string FS\x3C\x3C BSD 2.x filesystem, >1020 string !\x3E\x3EFS {invalid}(missing FSMAGIC2), >8 lelong*1024 x size: %d bytes, >8 lelong*1024 x {size:%d} >8 lelong*1024 x {jump:%d} >8 lelong x total blocks: %d, >972 lelong x free blocks: %d, >968 ledate x last modified: %s >980 byte !0 >>980 string x \b, last mounted on: "%s" # Simple file system found in Foscam camera firmware 0 beshort 0xbd9a Foscam WebUI filesystem, >2 uleshort x checksum: 0x%X, >16 lelong <3 {invalid}invalid first file name length, >16 lelong >127 {invalid}invalid first file name length, >20 byte 0 {invalid}invalid first file name, >20 byte !0x2E >>20 byte !0x2F >>>20 byte <65 {invalid}invalid first file name, >>>20 byte >122 {invalid}invalid first file name, >16 lelong x {strlen:%d} >20 string x first file name: "{string}" binwalk-2.1.1/src/binwalk/magic/firmware000066400000000000000000000751441263655036500202250ustar00rootroot00000000000000 #--------------------------Firmware Formats--------------------------- # uImage file # From: Craig Heffner, U-Boot image.h header definitions file 0 belong 0x27051956 uImage header, header size: 64 bytes, >4 ubelong x header CRC: 0x%X, >8 bedate x created: %s, >12 belong <1 {invalid} >12 ubelong x image size: %d bytes, >16 ubelong x Data Address: 0x%X, >20 ubelong x Entry Point: 0x%X, >24 ubelong x data CRC: 0x%X, >28 byte 0 OS: {invalid}invalid OS, >28 byte 1 OS: OpenBSD, >28 byte 2 OS: NetBSD, >28 byte 3 OS: FreeBSD, >28 byte 4 OS: 4.4BSD, >28 byte 5 OS: Linux, >28 byte 6 OS: SVR4, >28 byte 7 OS: Esix, >28 byte 8 OS: Solaris, >28 byte 9 OS: Irix, >28 byte 10 OS: SCO, >28 byte 11 OS: Dell, >28 byte 12 OS: NCR, >28 byte 13 OS: LynxOS, >28 byte 14 OS: VxWorks, >28 byte 15 OS: pSOS, >28 byte 16 OS: QNX, >28 byte 17 OS: Firmware, >28 byte 18 OS: RTEMS, >28 byte 19 OS: ARTOS, >28 byte 20 OS: Unity OS, >29 byte 0 CPU: {invalid}invalid CPU, >29 byte 1 CPU: Alpha, >29 byte 2 CPU: ARM, >29 byte 3 CPU: Intel x86, >29 byte 4 CPU: IA64, >29 byte 5 CPU: MIPS, >29 byte 6 CPU: MIPS 64 bit, >29 byte 7 CPU: PowerPC, >29 byte 8 CPU: IBM S390, >29 byte 9 CPU: SuperH, >29 byte 10 CPU: Sparc, >29 byte 11 CPU: Sparc 64 bit, >29 byte 12 CPU: M68K, >29 byte 13 CPU: Nios-32, >29 byte 14 CPU: MicroBlaze, >29 byte 15 CPU: Nios-II, >29 byte 16 CPU: Blackfin, >29 byte 17 CPU: AVR, >29 byte 18 CPU: STMicroelectronics ST200, #>30 byte x image type: %d, >30 byte 0 image type: {invalid} Image, >30 byte 1 image type: Standalone Program, >30 byte 2 image type: OS Kernel Image, >30 byte 3 image type: RAMDisk Image, >30 byte 4 image type: Multi-File Image, >30 byte 5 image type: Firmware Image, >30 byte 6 image type: Script file, >30 byte 7 image type: Filesystem Image, >30 byte 8 image type: Binary Flat Device Tree Blob >31 byte 0 compression type: none, >31 byte 1 compression type: gzip, >31 byte 2 compression type: bzip2, >31 byte 3 compression type: lzma, >32 string x image name: "%s" #IMG0 header, found in VxWorks-based Mercury router firmware 0 string IMG0 IMG0 (VxWorks) header, >4 belong <1 {invalid} >4 belong x size: %d #Mediatek bootloader signature #From xp-dev.com 0 string BOOTLOADER! Mediatek bootloader #CSYS header formats 0 string CSYS\x00 CSYS header, little endian, >8 lelong x size: %d 0 string CSYS\x80 CSYS header, big endian, >8 belong x size: %d # wrgg firmware image 0 string wrgg02 WRGG firmware header, >6 string x name: "%s", >48 string x root device: "%s" # trx image file 0 string HDR0 TRX firmware header, little endian, >4 lelong <1 {invalid} >4 ulelong x image size: %d bytes, >8 ulelong x CRC32: 0x%X, >12 uleshort x flags: 0x%X, >14 uleshort !1 >>14 uleshort !2 {invalid} >14 uleshort 2 version: %d, header size: 32 bytes, >>16 ulelong x loader offset: 0x%X, >>20 ulelong x linux kernel offset: 0x%X, >>24 ulelong x rootfs offset: 0x%X, >>28 ulelong x bin-header offset: 0x%X >14 uleshort 1 version: %d, header size: 28 bytes, >>16 ulelong x loader offset: 0x%X, >>20 ulelong x linux kernel offset: 0x%X, >>24 ulelong x rootfs offset: 0x%X 14 string U2ND BIN-Header, >4 ulelong !0 {invalid} >22 string !\x00*10 {invalid} >0 string x board ID: %.4s, >18 ubyte 0 hardware version: 4702, >18 ubyte 1 hardware version: 4712, >18 ubyte 2 hardware version: 4712L, >18 ubyte 3 hardware version: 4704, >18 ubyte >3 hardware version: unknown (code: 0x%.2X), >11 ubyte x firmware version: %d. >12 ubyte x \b%d. >12 ubyte x \b%d, >8 ubyte <80 >>8 ubyte x build date: 20%.2d- >8 ubyte >79 >>8 ubyte x build date: 19%.2d- >9 ubyte x \b%.2d- >10 ubyte x \b%.2d # Ubicom firmware image 0 belong 0xFA320080 Ubicom firmware header, >12 ubelong x checksum: 0x%X, >24 belong <0 {invalid} >24 belong x image size: %d # The ROME bootloader is used by several RealTek-based products. # Unfortunately, the magic bytes are specific to each product, so # separate signatures must be created for each one. # Netgear KWGR614 ROME image 0 string G614 Realtek firmware header, ROME bootloader, >4 beshort 0xd92f image type: KFS, >4 beshort 0xb162 image type: RDIR, >4 beshort 0xea43 image type: BOOT, >4 beshort 0x8dc9 image type: RUN, >4 beshort 0x2a05 image type: CCFG, >4 beshort 0x6ce8 image type: DCFG, >4 beshort 0xc371 image type: LOG, >6 byte x header version: %d, >10 ubyte >12 {invalid} month >12 ubyte >31 {invalid} day >8 ubyte >3000 {invalid} year #month >10 byte x created: %d/ #day >12 byte x \b%d/ #year >8 beshort x \b%d, >16 belong x image size: %d bytes, >22 ubyte x body checksum: 0x%X, >23 ubyte x header checksum: 0x%X # Linksys WRT54GX ROME image 0 belong 0x59a0e842 Realtek firmware header, ROME bootloader, >4 ubeshort 0xd92f image type: KFS, >4 ubeshort 0xb162 image type: RDIR, >4 ubeshort 0xea43 image type: BOOT, >4 ubeshort 0x8dc9 image type: RUN, >4 ubeshort 0x2a05 image type: CCFG, >4 ubeshort 0x6ce8 image type: DCFG, >4 ubeshort 0xc371 image type: LOG, >6 byte x header version: %d, >10 ubyte >12 {invalid}invalid month >12 ubyte >31 {invalid}invalid day >8 ubyte >3000 {invalid}invalid year #month >10 byte x created: %d/ #day >12 byte x \b%d/ #year >8 beshort x \b%d, >16 belong x image size: %d bytes, >22 ubyte x body checksum: 0x%X, >23 ubyte x header checksum: 0x%X # PackImg tag, somtimes used as a delimiter between the kernel and rootfs in firmware images. 0 string --PaCkImGs PackImg section delimiter tag, >10 string !-- {invalid} # If the size in both big and little endian is greater than 512MB, consider this a false positive >16 ulelong >0x20000000 >>16 ubelong >0x20000000 {invalid} >16 lelong <0 >>16 belong <0 {invalid} >16 lelong >0 >>16 lelong x little endian size: %d bytes; >16 belong >0 >>16 belong x big endian size: %d bytes #------------------------------------------------------------------------------ # Broadcom header format # 0 string BCRM Broadcom header, >4 lelong <0 {invalid} >4 lelong x number of sections: %d, >>8 lelong 18 first section type: flash >>8 lelong 19 first section type: disk >>8 lelong 21 first section type: tag # Berkeley Lab Checkpoint Restart (BLCR) checkpoint context files # http://ftg.lbl.gov/checkpoint 0 string Ck0\0\0R\0\0\0 BLCR >16 lelong 0 {invalid} >16 ulelong >7 {invalid} >16 lelong 1 x86 >16 lelong 3 alpha >16 lelong 5 x86-64 >16 lelong 7 ARM >8 lelong x context data (little endian, version %d) 0 string \0\0\0C\0\0\0R BLCR >16 lelong <2 {invalid} >16 ulelong >8 {invalid} >16 belong 2 SPARC >16 belong 4 ppc >16 belong 6 ppc64 >16 belong 7 ARMEB >16 belong 8 SPARC64 >8 belong x context data (big endian, version %d) # Aculab VoIP firmware # From: Mark Brown 0 string VoIP\x20Startup\x20and Aculab VoIP firmware >35 string x format "%s" #------------------------------------------------------------------------------ # HP LaserJet 1000 series downloadable firmware file 0 string \xbe\xefABCDEFGH HP LaserJet 1000 series downloadable firmware # From Albert Cahalan # really le32 operation,destination,payloadsize (but quite predictable) # 01 00 00 00 00 00 00 c0 00 02 00 00 0 string \1\0\0\0\0\0\0\300\0\2\0\0 Marvell Libertas firmware #--------------------------------------------------------------------------- # The following entries have been tested by Duncan Laurie (a # lead Sun/Cobalt developer) who agrees that they are good and worthy of # inclusion. # Boot ROM images for Sun/Cobalt Linux server appliances 0 string Cobalt\x20Networks\x20Inc.\nFirmware\x20v Paged COBALT boot rom >38 string x V%.4s # New format for Sun/Cobalt boot ROMs is annoying, it stores the version code # at the very end where file(1) can't get it. 0 string CRfs COBALT boot rom data (Flat boot rom or file system) # # Motorola S-Records, from Gerd Truschinski # Useless until forther improvements can be made to the signature. #0 string S0 Motorola S-Record; binary data in text format #Windows CE Binary Image Data Format aka B000FF #More information on the format: #http://msdn.microsoft.com/en-us/library/ms924510.aspx #http://forum.xda-developers.com/showthread.php?t=801167 0 string B000FF Windows CE image header, >7 ulelong x image start: 0x%X, >11 ulelong x image length: %d #Windows CE RomImage 63 string \x00ECEC Windows CE memory segment header, >4 ulelong x TOC address: 0x%X # -------------------------------- # ZynOS ROM header format # From openwrt zynos.h. 6 string SIG ZynOS header, header size: 48 bytes, >3 byte <0x7F rom image type: >>3 byte <1 {invalid}, >>3 byte >7 {invalid}, >>3 byte 1 ROMIMG, >>3 byte 2 ROMBOOT, >>3 byte 3 BOOTEXT, >>3 byte 4 ROMBIN, >>3 byte 5 ROMDIR, >>3 byte 6 6, >>3 byte 7 ROMMAP, >3 byte >0x7F ram image type: >>3 byte >0x82 {invalid}, >>3 byte 0x80 RAM, >>3 byte 0x81 RAMCODE, >>3 byte 0x82 RAMBOOT, >4 ubelong >0x40000000 {invalid} >4 belong <0 {invalid} >4 belong 0 {invalid} >4 belong x uncompressed size: %d, >8 belong >0x40000000 {invalid} >8 belong <0 {invalid} >8 belong 0 {invalid} >8 belong x compressed size: %d, >14 ubeshort x uncompressed checksum: 0x%X, >16 ubeshort x compressed checksum: 0x%X, >12 ubyte x flags: 0x%X, >12 byte &0x40 uncompressed checksum is valid, >12 ubyte &0x80 the binary is compressed, >>12 byte &0x20 compressed checksum is valid, >35 ubelong x memory map table address: 0x%X # Firmware header used by some VxWorks-based Cisco products 0 string CI032.00 Cisco VxWorks firmware header, >8 lelong >1024 {invalid} >8 lelong <0 {invalid} >8 lelong x header size: %d bytes, >32 lelong >1024 {invalid} >32 lelong <0 {invalid} >32 lelong x number of files: %d, >48 lelong <0 {invalid} >48 lelong x image size: %d, >64 string x firmware version: "%s" # Simple VxWorks reference strings #0 string VxWorks VxWorks string referece: #>0 string x "%s" #0 string vxworks VxWorks string referece: #>0 string x "%s" #0 string VXWORKS VxWorks string referece: #>0 string x "%s" # Firmware header used by some TV's 0 string FNIB ZBOOT firmware header, header size: 32 bytes, >8 ulelong x load address: 0x%.8X, >12 ulelong x start address: 0x%.8X, >16 ulelong x checksum: 0x%.8X, >20 ulelong x version: 0x%.8X, >24 lelong <1 {invalid} >24 ulelong x image size: %d bytes # Firmware header used by several D-Link routers (and probably others) 0 string \x5e\xa3\xa4\x17 DLOB firmware header,{jump:108} >(7.b+12) string !\x5e\xa3\xa4\x17 {invalid}, #>>12 string x %s, >(7.b+40) string x boot partition: "%s" # TP-Link firmware header structure; thanks to Jonathan McGowan for reversing and documenting this format 4 string TP-LINK\x20Technologies TP-Link firmware header, #>-4 lelong x header version: %d, >0x94 beshort x firmware version: %d. >0x96 beshort x \b%d. >0x98 beshort x \b%d, >0x18 string x image version: "%s", #>0x74 belong x image size: %d bytes, >0x3C belong x product ID: 0x%X, >0x40 belong x product version: %d, >0x70 ubelong x kernel load address: 0x%X, >0x74 ubelong x kernel entry point: 0x%X, >0x7C ubelong x kernel offset: %d, >0x80 ubelong x kernel length: %d, >0x84 ubelong x rootfs offset: %d, >0x88 ubelong x rootfs length: %d, >0x8C ubelong x bootloader offset: %d, >0x90 ubelong x bootloader length: %d # Header format from: http://skaya.enix.org/wiki/FirmwareFormat 0 string \x36\x00\x00\x00 Broadcom 96345 firmware header, header size: 256, >4 string !Broadcom >>4 string !\x20\x20\x20\x20 {invalid} >41 beshort !0x2020 >>41 beshort !0x0000 >>>41 string x firmware version: "%.4s", >45 beshort !0x0202 >>45 beshort !0x0000 >>>45 string x board id: "%s", >236 ubelong x ~CRC32 header checksum: 0x%X, >216 ubelong x ~CRC32 data checksum: 0x%X # Xerox MFP DLM signatures 0 string %%XRXbegin Xerox DLM firmware start of header 0 string %%OID_ATT_DLM_NAME Xerox DLM firmware name: >19 string x "%s" 0 string %%OID_ATT_DLM_VERSION Xerox DLM firmware version: >22 string x "%s" 0 string %%XRXend Xerox DLM firmware end of header # Generic copyright signature 0 string Copyright Copyright string: >9 byte 0 {invalid} >0 string x "%s" 0 string copyright Copyright string: >9 byte 0 {invalid} >0 string x "%s" # Sercomm firmware header 0 string sErCoMm Sercomm firmware signature, >7 leshort x version control: %d, >9 leshort x download control: %d, >11 string x hardware ID: "%s", >44 uleshort x hardware version: 0x%X, >58 uleshort x firmware version: 0x%X, >60 uleshort x starting code segment: 0x%X, >62 uleshort x code size: 0x%X # NPK firmware header, used by Mikrotik 0 belong 0x1EF1D0BA NPK firmware header, >4 lelong <0 {invalid} >4 lelong x image size: %d, >14 string x image name: "%s", >(48.l+58) string x description: "%s" # Ubiquiti firmware signatures 0 string UBNT Ubiquiti firmware header, header size: 264 bytes, >0x108 belong !0 {invalid}, >0x104 ubelong x ~CRC32: 0x%X, >4 byte 0 {invalid}, >4 string x version: "%s" 0 string GEOS Ubiquiti firmware header, header size: 264 bytes, >0x108 belong !0 {invalid}, >0x104 ubelong x ~CRC32: 0x%X, >4 byte 0 {invalid}, >4 string x version: "%s" 0 string OPEN Ubiquiti firmware header, third party, >0x108 belong !0 {invalid}, >0x104 ubelong x ~CRC32: 0x%X, >4 byte 0 {invalid}, >4 string x version: "%s" 4 string \x00\x00\x00\x00PART Ubiquiti partition header, >0 byte x header size: 56 bytes, >8 byte 0 {invalid} >8 string x name: "%s", >44 ubelong x base address: 0x%.8X, >52 belong x data size: %d bytes{size:%d} 4 string \x00\x00\x00\x00END\x2e Ubiquiti end header, header size: 12 bytes, >12 belong !0 {invalid}, >8 ubelong x cumulative ~CRC32: 0x%.8X # Found in DIR-100 firmware 0 string AIH0N AIH0N firmware header, header size: 48, >12 belong x size: %d, >8 belong !0 executable code, >>8 belong x load address: 0x%X, >32 string x version: "%s" 0 belong 0x5EA3A417 SEAMA firmware header, big endian, >4 beshort !0 {invalid} >6 beshort x meta size: %d, >8 belong <1 {invalid} >8 belong x image size: %d 0 lelong 0x5EA3A417 SEAMA firmware header, little endian, >4 leshort !0 {invalid} >6 leshort x meta size: %d, >8 lelong <1 {invalid} >8 lelong x image size: %d 0 belong 0x4D544443 NSP firmware header, big endian, >16 belong <1 {invalid} >16 belong x header size: %d, >20 belong <1 {invalid} >20 belong x image size: %d, >20 belong x {size:%d} >4 belong <1 {invalid} >4 ubelong x kernel offset: %d, >12 belong <1 {invalid} >12 belong x header version: %d, 0 lelong 0x4D544443 NSP firmware header, little endian, >16 lelong <1 {invalid} >16 lelong x header size: %d, >20 lelong <1 {invalid} >20 lelong x image size: %d, >20 lelong x {size:%d} >4 lelong <1 {invalid} >4 ulelong x kernel offset: %d, >12 lelong <1 {invalid} >12 lelong x header version: %d, # http://www.openwiz.org/wiki/Firmware_Layout#Beyonwiz_.wrp_header_structure 0 string WizFwPkgl Beyonwiz firmware header, >20 string x version: "%s" 0 string BLI223WJ0 Thompson/Alcatel encoded firmware, >32 byte x version: %d. >33 byte x \b%d. >34 byte x \b%d. >35 byte x \b%d, >44 belong x size: %d, >48 ubelong x crc: 0x%.8X, >35 byte x try decryption tool from: >35 byte x http://download.modem-help.co.uk/mfcs-A/Alcatel/Modems/Misc/ 16 string \xd9\x54\x93\x7a\x68\x04\x4a\x44\x81\xce\x0b\xf6\x17\xd8\x90\xdf UEFI PI firmware volume # http://android.stackexchange.com/questions/23357/\ # is-there-a-way-to-look-inside-and-modify-an-adb-backup-created-file/\ # 23608#23608 0 string ANDROID\040BACKUP\n Android Backup >15 string 1\n \b, version 1 >17 string 0\n \b, uncompressed >17 string 1\n \b, compressed >19 string none\n \b, unencrypted >19 string AES-256\n \b, encrypted AES-256 # http://forum.xda-developers.com/showthread.php?p=47818657 8 string imgARMcC Roku aimage SB # Boot ROM images for Sun/Cobalt Linux server appliances 0 string Cobalt\ Networks\ Inc.\nFirmware\ v Paged Sun/COBALT boot rom, >38 string x version: "%.4s" # Simple eCos string signatures 0 string ecos eCos RTOS string reference: >0 string x "%s" 0 string eCos eCos RTOS string reference: >0 string x "%s" 0 string ECOS eCos RTOS string reference: >0 string x "%s" # ZyXEL config signatures 6 string dbgarea ZyXEL rom-0 configuration block, name: "%s", >16 ubeshort x compressed size: %d, >14 ubeshort x uncompressed size: %d, >18 ubeshort+16 x data offset from start of block: %d 6 string spt.dat ZyXEL rom-0 configuration block, name: "%s", >16 ubeshort x compressed size: %d, >14 ubeshort x uncompressed size: %d, >18 ubeshort+16 x data offset from start of block: %d 6 string autoexec.net ZyXEL rom-0 configuration block, name: "%s", >16 ubeshort x compressed size: %d, >14 ubeshort x uncompressed size: %d, >18 ubeshort+16 x data offset from start of block: %d # Obfuscated Arcadyan firmware 0x68 string \x00\xD5\x08 Obfuscated Arcadyan firmware, >0x6B byte !0 {invalid} signature trailing byte [0x%X],{invalid} # None of the known Arcadyan signatures bytes have a NULL byte >0 byte 0 {invalid} >1 byte 0 {invalid} >2 byte 0 {invalid} >3 byte 0 {invalid} >0 ubelong x signature bytes: 0x%X, >0x70 string !\x00\x00\x00\x00\x00\x00 {invalid} padding bytes{invalid} # Digi firmware images 0xC0 string Digi Digi International firmware, >0xC8 beshort !0x4253 >>0xC8 beshort !0x4950 >>>0xC8 beshort !0x4944 >>>>0xC8 beshort !0x444f >>>>>0xC8 beshort !0x4443 >>>>>>0xC8 beshort !0x4f53 >>>>>>>0xC8 beshort !0x4f43 >>>>>>>>0xC8 beshort !0x4646 >>>>>>>>>0xC8 beshort !0x5350 {invalid}invalid header, >0xD4 ubelong x load address: 0x%.8X, >0xDC ubelong x entry point: 0x%.8X, # Lancom firmware signatures, courtesy of christophvw 0 string ELSF LANCOM firmware header, >22 string x model: "%s", >18 string x firmware version: "%.4s", >12 ubyte 255 Rel, >12 ubyte 253 alpha, >12 ubyte 220 PR, >12 ubyte >0 >>12 ubyte <220 RC%d, >12 ubyte >220 >>12 ubyte-220 <237 RU%d, >12 ubyte 0 dev >17 ubyte >0 >>17 ubyte x build %d #get build date >7 ubyte !63 #date is stored as string >>7 string x ("%.8s") 0 string ELSO LANCOM OEM file 0 string ELSB LANCOM firmware loader, >22 string x model: "%s", >18 string x loader version: "%.4s", 0 string ELSC LANCOM WWAN firmware >4 ubyte 3 >>5 beshort 0 >>7 string x \b, "%s" 0 string ELSP LANCOM file entry >(198.L+202) belong !2 >>(198.L+202) belong !3 {invalid} >202 string @(RECENT_FIRMWARE)/ \b, file name: >>221 string x "%s" >>221 string x \b{name:%s} >(198.L+202) belong 2 >>(198.L+206) belong <1 {invalid} >>(198.L+206) belong x \b, file size: %d bytes >>(198.L+206) belong x \b{size:%d} >(198.L+202) belong 3 >>&4 string @(RECENT_FIRMWARE)/ \b, alias: >>&23 string x "%s" >>(&0.L+4) belong x \b, file size: %d bytes # Another Broadcom firmware header... # The header seems to be always 0x100 bytes length and there is more information than the one displayed (not sure about the meaning). # Used for example in the EchoLife HG556a router 0x0 string \x38\x00\x00\x00 Broadcom firmware header >0x4 string !Broadcom Corporatio {invalid} >0x18 string x %s. >0x8E string x Model: %s. >0xA2 string x Firmware version: %s. # QNAP encrypted firmware 0 string icpnas QNAP encrypted firmware footer >10 string x , model: %s >26 string x , version: %s >42 uleshort !0 >>42 string x , date: %s # Mediatek 0 string SF_BOOT\x00\x00\x00\x00\x00 Mediatek Serial Flash Image >12 lelong <1 {invalid} >12 lelong >1 {invalid} >12 lelong x Version %d 0 string EMMC_BOOT\x00\x00\x00 Mediatek EMMC Flash Image >12 lelong <1 {invalid} >12 lelong >1 {invalid} >12 lelong x Version %d 0 string NOR_BOOT\x00\x00\x00\x00 Mediatek NOR Flash Image >12 lelong <1 {invalid} >12 lelong >1 {invalid} >12 lelong x Version %d 0 string BRLYT\x00\x00\x00 Mediatek Boot Header >8 lelong <1 {invalid} >8 lelong >1 {invalid} >8 lelong x Version %d 0 string BBBB Boot section{overlap} >8 lelong x Start 0x%X >12 lelong x End 0x%X >16 lelong &0x1 Load-by-Bootrom >16 lelong &0x80000000 Internal-RAM >16 lelong &0x7ffffff0 {invalid} 0 string FILE_INFO\x00\x00\x00 Mediatek File Info >12 lelong <1 {invalid} >12 lelong >1 {invalid} >16 leshort 0 File Type: NONE >16 leshort 1 File Type: ARM-Bootloader >16 leshort 2 File Type: ARM-External-Bootloader >16 leshort 10 File Type: Root-Certificate >16 leshort 256 File Type: Primary-MAUI >16 leshort 264 File Type: VIVA >16 leshort 769 File Type: SECURE_RO_ME >18 byte 0 Flash Type: NONE >18 byte 1 Flash Type: NOR Flash >18 byte 2 Flash Type: NAND Sequential Flash >18 byte 3 Flash Type: NAND_TTBL >18 byte 4 Flash Type: NAND_FDM50 >18 byte 5 EMMC-Boot-Region >18 byte 6 EMMC-Data-Region >18 byte 7 Flash Type: Serial Flash >18 byte 255 Flash Type: Device-End >18 byte >20 {invalid} >19 byte 0 No Signature >19 byte 1 Signature Type: PHASH >19 byte 2 Signature Type: SINGLE >19 byte 3 Signature Type: SINGLE and PHASH >19 byte 4 Signature Type: MULTI >19 byte 5 Signature Type: TYPE_NUM >19 byte 255 Signature Type: TYE_END >19 byte >20 {invalid} >20 lelong x Load Address: 0x%X >24 lelong x File Length: %d >28 lelong x Maximum Size: %d >32 lelong x Content Offset: 0x%X >36 lelong x Signature Lenght: %d >40 lelong x Jump Offset: %d >44 lelong &0x1 POST_BUILD_DONE >44 lelong &0x2 XIP (Execute In Place) >44 lelong &0x4 SLT >44 lelong &0xffffff00 {invalid} # Android bootimg # https://android.googlesource.com/platform/system/core.git/+/master/mkbootimg/bootimg.h 0 string ANDROID! Android bootimg >8 ulelong x \b, kernel size: %d bytes >12 ulelong x \b, kernel addr: 0x%X >16 ulelong x \b, ramdisk size: %d bytes >20 ulelong x \b, ramdisk addr: 0x%X >48 string x \b, product name: "%s" binwalk-2.1.1/src/binwalk/magic/hashing000066400000000000000000000010001263655036500200060ustar00rootroot00000000000000# CRC32 polynomial table 0 string \x00\x00\x00\x00\x77\x07\x30\x96\xEE\x0E\x61\x2C\x99\x09\x51\xBA CRC32 polynomial table, big endian 0 string \x00\x00\x00\x00\x96\x30\x07\x77\x2C\x61\x0E\xEE\xBA\x51\x09\x99 CRC32 polynomial table, little endian # SHA256 constant k 0 string \x42\x8a\x2f\x98\x71\x37\x44\x91\xb5\xc0\xfb\xcf\xe9\xb5\xdb\xa5 SHA256 hash constants, big endian 0 string \x98\x2f\x8a\x42\x91\x44\x37\x71\xcf\xfb\xc0\xb5\xa5\xdb\xb5\xe9 SHA256 hash constants, little endian binwalk-2.1.1/src/binwalk/magic/images000066400000000000000000000264301263655036500176500ustar00rootroot00000000000000# Tag Image File Format, from Daniel Quinlan (quinlan@yggdrasil.com) # The second word of TIFF files is the TIFF version number, 42, which has # never changed. The TIFF specification recommends testing for it. 0 string MM\x00\x2a TIFF image data, big-endian, >4 belong 0 {invalid} >4 belong <0 {invalid} # First image directory must begin on an even byte boundary >4 belong &1 {invalid} >4 belong >10000000 {invalid} >4 belong x offset of first image directory: %d 0 string II\x2a\x00 TIFF image data, little-endian >4 lelong 0 {invalid} >4 lelong <0 {invalid} >4 lelong &1 {invalid} >4 lelong >10000000 {invalid} >4 lelong x offset of first image directory: %d # PNG [Portable Network Graphics, or "PNG's Not GIF"] images # (Greg Roelofs, newt@uchicago.edu) # (Albert Cahalan, acahalan@cs.uml.edu) # # 137 P N G \r \n ^Z \n [4-byte length] H E A D [HEAD data] [HEAD crc] ... # 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image >16 belong <1 {invalid} >16 belong >10000 {invalid} >20 belong <1 {invalid} >20 belong >10000 {invalid} >16 belong x \b, %d x >20 belong x %d, >24 byte x %d-bit >25 byte 0 grayscale, >25 byte 2 \b/color RGB, >25 byte 3 colormap, >25 byte 4 gray+alpha, >25 byte 6 \b/color RGBA, >28 byte 0 non-interlaced >28 byte 1 interlaced # GIF 0 string GIF8 GIF image data >4 string 7a \b, version "8%s", >4 string 9a \b, version "8%s", >6 leshort >0 %d x >8 leshort >0 %d #>10 byte &0x80 color mapped, #>10 byte&0x07 =0x00 2 colors #>10 byte&0x07 =0x01 4 colors #>10 byte&0x07 =0x02 8 colors #>10 byte&0x07 =0x03 16 colors #>10 byte&0x07 =0x04 32 colors #>10 byte&0x07 =0x05 64 colors #>10 byte&0x07 =0x06 128 colors #>10 byte&0x07 =0x07 256 colors # PC bitmaps (OS/2, Windows BMP files) (Greg Roelofs, newt@uchicago.edu) 0 string BM PC bitmap, >14 leshort !12 >>14 leshort !64 >>>14 leshort !40 >>>>14 leshort !128 {invalid} >14 leshort 12 OS/2 1.x format, >>18 lelong <1 {invalid} >>18 lelong >1000000 {invalid} >>18 leshort x \b, %d x >>20 lelong <1 {invalid} >>20 lelong >1000000 {invalid} >>20 leshort x %d >14 leshort 64 OS/2 2.x format, >>18 lelong <1 {invalid} >>18 lelong >1000000 {invalid} >>18 leshort x \b, %d x >>20 lelong <1 {invalid} >>20 lelong >1000000 {invalid} >>20 leshort x %d >14 leshort 40 Windows 3.x format, >>18 lelong <1 {invalid} >>18 lelong >1000000 {invalid} >>18 lelong x \b, %d x >>22 lelong <1 {invalid} >>22 lelong >1000000 {invalid} >>22 lelong x %d x >>28 lelong <1 {invalid} >>28 lelong >1000000 {invalid} >>28 leshort x %d >14 leshort 128 Windows NT/2000 format, >>18 lelong >1000000 {invalid} >>18 lelong <1 {invalid} >>18 lelong x \b, %d x >>22 lelong <1 {invalid} >>22 lelong >1000000 {invalid} >>22 lelong x %d x >>28 lelong <1 {invalid} >>28 lelong >1000000 {invalid} >>28 leshort x %d #------------------------------------------------------------------------------ # JPEG images # SunOS 5.5.1 had # # 0 string \377\330\377\340 JPEG file # 0 string \377\330\377\356 JPG file # # both of which turn into "JPEG image data" here. # 0 ubelong 0xffd8ffe0 JPEG image data, JFIF standard >6 string !JFIF {invalid} # The following added by Erik Rossen 1999-09-06 # in a vain attempt to add image size reporting for JFIF. Note that these # tests are not fool-proof since some perfectly valid JPEGs are currently # impossible to specify in magic(4) format. # First, a little JFIF version info: >11 byte x \b %d. >12 byte x \b%02d # Next, the resolution or aspect ratio of the image: #>>13 byte 0 \b, aspect ratio #>>13 byte 1 \b, resolution (DPI) #>>13 byte 2 \b, resolution (DPCM) #>>4 beshort x \b, segment length %d # Next, show thumbnail info, if it exists: >18 byte !0 \b, thumbnail %dx >>19 byte x \b%d 0 ubelong 0xffd8ffe1 JPEG image data, EXIF standard # EXIF moved down here to avoid reporting a bogus version number, # and EXIF version number printing added. # - Patrik R=E5dman >6 string !Exif {invalid} # Look for EXIF IFD offset in IFD 0, and then look for EXIF version tag in EXIF IFD. # All possible combinations of entries have to be enumerated, since no looping # is possible. And both endians are possible... # The combinations included below are from real-world JPEGs. # Little-endian >12 string II # IFD 0 Entry #5: >>70 leshort 0x8769 # EXIF IFD Entry #1: >>>(78.l+14) leshort 0x9000 >>>>(78.l+23) byte x %c >>>>(78.l+24) byte x \b.%c >>>>(78.l+25) byte !0x30 \b%c # IFD 0 Entry #9: >>118 leshort 0x8769 # EXIF IFD Entry #3: >>>(126.l+38) leshort 0x9000 >>>>(126.l+47) byte x %c >>>>(126.l+48) byte x \b.%c >>>>(126.l+49) byte !0x30 \b%c # IFD 0 Entry #10 >>130 leshort 0x8769 # EXIF IFD Entry #3: >>>(138.l+38) leshort 0x9000 >>>>(138.l+47) byte x %c >>>>(138.l+48) byte x \b.%c >>>>(138.l+49) byte !0x30 \b%c # EXIF IFD Entry #4: >>>(138.l+50) leshort 0x9000 >>>>(138.l+59) byte x %c >>>>(138.l+60) byte x \b.%c >>>>(138.l+61) byte !0x30 \b%c # EXIF IFD Entry #5: >>>(138.l+62) leshort 0x9000 >>>>(138.l+71) byte x %c >>>>(138.l+72) byte x \b.%c >>>>(138.l+73) byte !0x30 \b%c # IFD 0 Entry #11 >>142 leshort 0x8769 # EXIF IFD Entry #3: >>>(150.l+38) leshort 0x9000 >>>>(150.l+47) byte x %c >>>>(150.l+48) byte x \b.%c >>>>(150.l+49) byte !0x30 \b%c # EXIF IFD Entry #4: >>>(150.l+50) leshort 0x9000 >>>>(150.l+59) byte x %c >>>>(150.l+60) byte x \b.%c >>>>(150.l+61) byte !0x30 \b%c # EXIF IFD Entry #5: >>>(150.l+62) leshort 0x9000 >>>>(150.l+71) byte x %c >>>>(150.l+72) byte x \b.%c >>>>(150.l+73) byte !0x30 \b%c # Big-endian >12 string MM # IFD 0 Entry #9: >>118 beshort 0x8769 # EXIF IFD Entry #1: >>>(126.L+14) beshort 0x9000 >>>>(126.L+23) byte x %c >>>>(126.L+24) byte x \b.%c >>>>(126.L+25) byte !0x30 \b%c # EXIF IFD Entry #3: >>>(126.L+38) beshort 0x9000 >>>>(126.L+47) byte x %c >>>>(126.L+48) byte x \b.%c >>>>(126.L+49) byte !0x30 \b%c # IFD 0 Entry #10 >>130 beshort 0x8769 # EXIF IFD Entry #3: >>>(138.L+38) beshort 0x9000 >>>>(138.L+47) byte x %c >>>>(138.L+48) byte x \b.%c >>>>(138.L+49) byte !0x30 \b%c # EXIF IFD Entry #5: >>>(138.L+62) beshort 0x9000 >>>>(138.L+71) byte x %c >>>>(138.L+72) byte x \b.%c >>>>(138.L+73) byte !0x30 \b%c # IFD 0 Entry #11 >>142 beshort 0x8769 # EXIF IFD Entry #4: >>>(150.L+50) beshort 0x9000 >>>>(150.L+59) byte x %c >>>>(150.L+60) byte x \b.%c >>>>(150.L+61) byte !0x30 \b%c # Here things get sticky. We can do ONE MORE marker segment with # indirect addressing, and that's all. It would be great if we could # do pointer arithemetic like in an assembler language. Christos? # And if there was some sort of looping construct to do searches, plus a few # named accumulators, it would be even more effective... # At least we can show a comment if no other segments got inserted before: >(4.S+5) byte 0xFE >>(4.S+8) string >\0 \b, comment: "%s" # FIXME: When we can do non-byte counted strings, we can use that to get # the string's count, and fix Debian bug #283760 #>(4.S+5) byte 0xFE \b, comment #>>(4.S+6) beshort x \b length=%d #>>(4.S+8) string >\0 \b, "%s" # Or, we can show the encoding type (I've included only the three most common) # and image dimensions if we are lucky and the SOFn (image segment) is here: >(4.S+5) byte 0xC0 \b, baseline >>(4.S+6) byte x \b, precision %d >>(4.S+7) beshort x \b, %dx >>(4.S+9) beshort x \b%d >(4.S+5) byte 0xC1 \b, extended sequential >>(4.S+6) byte x \b, precision %d >>(4.S+7) beshort x \b, %dx >>(4.S+9) beshort x \b%d >(4.S+5) byte 0xC2 \b, progressive >>(4.S+6) byte x \b, precision %d >>(4.S+7) beshort x \b, %dx >>(4.S+9) beshort x \b%d binwalk-2.1.1/src/binwalk/magic/linux000066400000000000000000000025501263655036500175370ustar00rootroot00000000000000 #-------------------------Kernels------------------------------------- # Linux kernel boot images, from Albert Cahalan # and others such as Axel Kohlmeyer # and Nicolas Lichtmaier # All known start with: b8 c0 07 8e d8 b8 00 90 8e c0 b9 00 01 29 f6 29 0 string \xb8\xc0\x07\x8e\xd8\xb8\x00\x90\x8e\xc0\xb9\x00\x01\x29\xf6\x29 Linux kernel boot image >514 string !HdrS {invalid} # Finds and prints Linux kernel strings in raw Linux kernels (output like uname -a). # Commonly found in decompressed embedded kernel binaries. 0 string Linux\x20version\x20 Linux kernel version >14 byte 0 {invalid} >14 byte !0 >>14 string x "%s" # Linux ARM compressed kernel image # See arch/arm/boot/compressed/head.S and arch/arm/boot/compressed/vmlinux.lds.S 0 ulelong 0x016f2818 Linux kernel ARM boot executable zImage (little-endian), >4 ulelong x load address: "0x%.8X", >8 ulelong x end address: "0x%.8X" >12 ulelong !0x04030201 {invalid} 0 ubelong 0x016f2818 Linux kernel ARM boot executable zImage (big-endian), >4 ubelong x load address: "0x%.8X", >8 ubelong x end address: "0x%.8X" >12 ubelong !0x04030201 {invalid} binwalk-2.1.1/src/binwalk/magic/lzma000066400000000000000000003243151263655036500173510ustar00rootroot00000000000000# ## ------------------------------------------------------------------ ## Signature for LZMA compressed data with valid properties byte 0x40 ## ------------------------------------------------------------------ #0 string \x40\x00\x00 LZMA compressed data, properties: 0x40, # ## These are all the valid dictionary sizes supported by LZMA utils. #>1 lelong !65536 #>>1 lelong !131072 #>>>1 lelong !262144 #>>>>1 lelong !524288 #>>>>>1 lelong !1048576 #>>>>>>1 lelong !2097152 #>>>>>>>1 lelong !4194304 #>>>>>>>>1 lelong !8388608 #>>>>>>>>>1 lelong !16777216 #>>>>>>>>>>1 lelong !33554432 {invalid} #>1 lelong x dictionary size: %d bytes, # ## Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). ## This could technically be valid, but is unlikely. #>5 lequad !-1 #>>5 lequad <32 {invalid} #>>5 lequad >0x40000000 {invalid} # ## These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. ## Since most false positives are the result of repeating sequences of bytes (such as executable instructions), ## marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. #>1 lelong 65536 #>>5 lequad 65536 {invalid} #>1 lelong 131072 #>>5 lequad 131072 {invalid} #>1 lelong 262144 #>>5 lequad 262144 {invalid} #>1 lelong 524288 #>>5 lequad 524288 {invalid} #>1 lelong 1048576 #>>5 lequad 1048576 {invalid} #>1 lelong 2097152 #>>5 lequad 2097152 {invalid} #>1 lelong 4194304 #>>5 lequad 4194304 {invalid} #>1 lelong 8388608 #>>5 lequad 8388608 {invalid} #>1 lelong 16777216 #>>5 lequad 16777216 {invalid} #>1 lelong 33554432 #>>5 lequad 33554432 {invalid} #>5 lequad x uncompressed size: %lld bytes # # ## ------------------------------------------------------------------ ## Signature for LZMA compressed data with valid properties byte 0x41 ## ------------------------------------------------------------------ #0 string \x41\x00\x00 LZMA compressed data, properties: 0x41, # ## These are all the valid dictionary sizes supported by LZMA utils. #>1 lelong !65536 #>>1 lelong !131072 #>>>1 lelong !262144 #>>>>1 lelong !524288 #>>>>>1 lelong !1048576 #>>>>>>1 lelong !2097152 #>>>>>>>1 lelong !4194304 #>>>>>>>>1 lelong !8388608 #>>>>>>>>>1 lelong !16777216 #>>>>>>>>>>1 lelong !33554432 {invalid} #>1 lelong x dictionary size: %d bytes, # ## Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). ## This could technically be valid, but is unlikely. #>5 lequad !-1 #>>5 lequad <32 {invalid} #>>5 lequad >0x40000000 {invalid} # ## These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. ## Since most false positives are the result of repeating sequences of bytes (such as executable instructions), ## marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. #>1 lelong 65536 #>>5 lequad 65536 {invalid} #>1 lelong 131072 #>>5 lequad 131072 {invalid} #>1 lelong 262144 #>>5 lequad 262144 {invalid} #>1 lelong 524288 #>>5 lequad 524288 {invalid} #>1 lelong 1048576 #>>5 lequad 1048576 {invalid} #>1 lelong 2097152 #>>5 lequad 2097152 {invalid} #>1 lelong 4194304 #>>5 lequad 4194304 {invalid} #>1 lelong 8388608 #>>5 lequad 8388608 {invalid} #>1 lelong 16777216 #>>5 lequad 16777216 {invalid} #>1 lelong 33554432 #>>5 lequad 33554432 {invalid} #>5 lequad x uncompressed size: %lld bytes # # ## ------------------------------------------------------------------ ## Signature for LZMA compressed data with valid properties byte 0x48 ## ------------------------------------------------------------------ #0 string \x48\x00\x00 LZMA compressed data, properties: 0x48, # ## These are all the valid dictionary sizes supported by LZMA utils. #>1 lelong !65536 #>>1 lelong !131072 #>>>1 lelong !262144 #>>>>1 lelong !524288 #>>>>>1 lelong !1048576 #>>>>>>1 lelong !2097152 #>>>>>>>1 lelong !4194304 #>>>>>>>>1 lelong !8388608 #>>>>>>>>>1 lelong !16777216 #>>>>>>>>>>1 lelong !33554432 {invalid} #>1 lelong x dictionary size: %d bytes, # ## Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). ## This could technically be valid, but is unlikely. #>5 lequad !-1 #>>5 lequad <32 {invalid} #>>5 lequad >0x40000000 {invalid} # ## These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. ## Since most false positives are the result of repeating sequences of bytes (such as executable instructions), ## marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. #>1 lelong 65536 #>>5 lequad 65536 {invalid} #>1 lelong 131072 #>>5 lequad 131072 {invalid} #>1 lelong 262144 #>>5 lequad 262144 {invalid} #>1 lelong 524288 #>>5 lequad 524288 {invalid} #>1 lelong 1048576 #>>5 lequad 1048576 {invalid} #>1 lelong 2097152 #>>5 lequad 2097152 {invalid} #>1 lelong 4194304 #>>5 lequad 4194304 {invalid} #>1 lelong 8388608 #>>5 lequad 8388608 {invalid} #>1 lelong 16777216 #>>5 lequad 16777216 {invalid} #>1 lelong 33554432 #>>5 lequad 33554432 {invalid} #>5 lequad x uncompressed size: %lld bytes # # ## ------------------------------------------------------------------ ## Signature for LZMA compressed data with valid properties byte 0x49 ## ------------------------------------------------------------------ #0 string \x49\x00\x00 LZMA compressed data, properties: 0x49, # ## These are all the valid dictionary sizes supported by LZMA utils. #>1 lelong !65536 #>>1 lelong !131072 #>>>1 lelong !262144 #>>>>1 lelong !524288 #>>>>>1 lelong !1048576 #>>>>>>1 lelong !2097152 #>>>>>>>1 lelong !4194304 #>>>>>>>>1 lelong !8388608 #>>>>>>>>>1 lelong !16777216 #>>>>>>>>>>1 lelong !33554432 {invalid} #>1 lelong x dictionary size: %d bytes, # ## Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). ## This could technically be valid, but is unlikely. #>5 lequad !-1 #>>5 lequad <32 {invalid} #>>5 lequad >0x40000000 {invalid} # ## These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. ## Since most false positives are the result of repeating sequences of bytes (such as executable instructions), ## marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. #>1 lelong 65536 #>>5 lequad 65536 {invalid} #>1 lelong 131072 #>>5 lequad 131072 {invalid} #>1 lelong 262144 #>>5 lequad 262144 {invalid} #>1 lelong 524288 #>>5 lequad 524288 {invalid} #>1 lelong 1048576 #>>5 lequad 1048576 {invalid} #>1 lelong 2097152 #>>5 lequad 2097152 {invalid} #>1 lelong 4194304 #>>5 lequad 4194304 {invalid} #>1 lelong 8388608 #>>5 lequad 8388608 {invalid} #>1 lelong 16777216 #>>5 lequad 16777216 {invalid} #>1 lelong 33554432 #>>5 lequad 33554432 {invalid} #>5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x51 # ------------------------------------------------------------------ 0 string \x51\x00\x00 LZMA compressed data, properties: 0x51, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x5A # ------------------------------------------------------------------ 0 string \x5A\x00\x00 LZMA compressed data, properties: 0x5A, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x5B # ------------------------------------------------------------------ 0 string \x5B\x00\x00 LZMA compressed data, properties: 0x5B, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x5C # ------------------------------------------------------------------ 0 string \x5C\x00\x00 LZMA compressed data, properties: 0x5C, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x5D # ------------------------------------------------------------------ 0 string \x5D\x00\x00 LZMA compressed data, properties: 0x5D, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x5E # ------------------------------------------------------------------ 0 string \x5E\x00\x00 LZMA compressed data, properties: 0x5E, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x63 # ------------------------------------------------------------------ 0 string \x63\x00\x00 LZMA compressed data, properties: 0x63, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x64 # ------------------------------------------------------------------ 0 string \x64\x00\x00 LZMA compressed data, properties: 0x64, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x65 # ------------------------------------------------------------------ 0 string \x65\x00\x00 LZMA compressed data, properties: 0x65, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x66 # ------------------------------------------------------------------ 0 string \x66\x00\x00 LZMA compressed data, properties: 0x66, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x6C # ------------------------------------------------------------------ 0 string \x6C\x00\x00 LZMA compressed data, properties: 0x6C, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x6D # ------------------------------------------------------------------ 0 string \x6D\x00\x00 LZMA compressed data, properties: 0x6D, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x6E # ------------------------------------------------------------------ 0 string \x6E\x00\x00 LZMA compressed data, properties: 0x6E, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x75 # ------------------------------------------------------------------ 0 string \x75\x00\x00 LZMA compressed data, properties: 0x75, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x76 # ------------------------------------------------------------------ 0 string \x76\x00\x00 LZMA compressed data, properties: 0x76, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x7E # ------------------------------------------------------------------ 0 string \x7E\x00\x00 LZMA compressed data, properties: 0x7E, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x87 # ------------------------------------------------------------------ 0 string \x87\x00\x00 LZMA compressed data, properties: 0x87, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x88 # ------------------------------------------------------------------ 0 string \x88\x00\x00 LZMA compressed data, properties: 0x88, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x89 # ------------------------------------------------------------------ 0 string \x89\x00\x00 LZMA compressed data, properties: 0x89, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x8A # ------------------------------------------------------------------ 0 string \x8A\x00\x00 LZMA compressed data, properties: 0x8A, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x8B # ------------------------------------------------------------------ 0 string \x8B\x00\x00 LZMA compressed data, properties: 0x8B, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x90 # ------------------------------------------------------------------ 0 string \x90\x00\x00 LZMA compressed data, properties: 0x90, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x91 # ------------------------------------------------------------------ 0 string \x91\x00\x00 LZMA compressed data, properties: 0x91, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x92 # ------------------------------------------------------------------ 0 string \x92\x00\x00 LZMA compressed data, properties: 0x92, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x93 # ------------------------------------------------------------------ 0 string \x93\x00\x00 LZMA compressed data, properties: 0x93, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x99 # ------------------------------------------------------------------ 0 string \x99\x00\x00 LZMA compressed data, properties: 0x99, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x9A # ------------------------------------------------------------------ 0 string \x9A\x00\x00 LZMA compressed data, properties: 0x9A, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0x9B # ------------------------------------------------------------------ 0 string \x9B\x00\x00 LZMA compressed data, properties: 0x9B, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xA2 # ------------------------------------------------------------------ 0 string \xA2\x00\x00 LZMA compressed data, properties: 0xA2, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xA3 # ------------------------------------------------------------------ 0 string \xA3\x00\x00 LZMA compressed data, properties: 0xA3, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xAB # ------------------------------------------------------------------ 0 string \xAB\x00\x00 LZMA compressed data, properties: 0xAB, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xB4 # ------------------------------------------------------------------ 0 string \xB4\x00\x00 LZMA compressed data, properties: 0xB4, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xB5 # ------------------------------------------------------------------ 0 string \xB5\x00\x00 LZMA compressed data, properties: 0xB5, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xB6 # ------------------------------------------------------------------ 0 string \xB6\x00\x00 LZMA compressed data, properties: 0xB6, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xB7 # ------------------------------------------------------------------ 0 string \xB7\x00\x00 LZMA compressed data, properties: 0xB7, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xB8 # ------------------------------------------------------------------ 0 string \xB8\x00\x00 LZMA compressed data, properties: 0xB8, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xBD # ------------------------------------------------------------------ 0 string \xBD\x00\x00 LZMA compressed data, properties: 0xBD, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xBE # ------------------------------------------------------------------ 0 string \xBE\x00\x00 LZMA compressed data, properties: 0xBE, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xBF # ------------------------------------------------------------------ 0 string \xBF\x00\x00 LZMA compressed data, properties: 0xBF, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xC0 # ------------------------------------------------------------------ 0 string \xC0\x00\x00 LZMA compressed data, properties: 0xC0, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xC6 # ------------------------------------------------------------------ 0 string \xC6\x00\x00 LZMA compressed data, properties: 0xC6, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xC7 # ------------------------------------------------------------------ 0 string \xC7\x00\x00 LZMA compressed data, properties: 0xC7, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xC8 # ------------------------------------------------------------------ 0 string \xC8\x00\x00 LZMA compressed data, properties: 0xC8, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xCF # ------------------------------------------------------------------ 0 string \xCF\x00\x00 LZMA compressed data, properties: 0xCF, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xD0 # ------------------------------------------------------------------ 0 string \xD0\x00\x00 LZMA compressed data, properties: 0xD0, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes # ------------------------------------------------------------------ # Signature for LZMA compressed data with valid properties byte 0xD8 # ------------------------------------------------------------------ 0 string \xD8\x00\x00 LZMA compressed data, properties: 0xD8, # These are all the valid dictionary sizes supported by LZMA utils. >1 lelong !65536 >>1 lelong !131072 >>>1 lelong !262144 >>>>1 lelong !524288 >>>>>1 lelong !1048576 >>>>>>1 lelong !2097152 >>>>>>>1 lelong !4194304 >>>>>>>>1 lelong !8388608 >>>>>>>>>1 lelong !16777216 >>>>>>>>>>1 lelong !33554432 {invalid} >1 lelong x dictionary size: %d bytes, # Assume that a valid size will be greater than 32 bytes and less than 1GB (a value of -1 IS valid). # This could technically be valid, but is unlikely. >5 lequad !-1 >>5 lequad <32 {invalid} >>5 lequad >0x40000000 {invalid} # These are not 100%. The uncompressed size could be exactly the same as the dicionary size, but it is unlikely. # Since most false positives are the result of repeating sequences of bytes (such as executable instructions), # marking matches with the same uncompressed and dictionary sizes as {invalid} eliminates much of these false positives. >1 lelong 65536 >>5 lequad 65536 {invalid} >1 lelong 131072 >>5 lequad 131072 {invalid} >1 lelong 262144 >>5 lequad 262144 {invalid} >1 lelong 524288 >>5 lequad 524288 {invalid} >1 lelong 1048576 >>5 lequad 1048576 {invalid} >1 lelong 2097152 >>5 lequad 2097152 {invalid} >1 lelong 4194304 >>5 lequad 4194304 {invalid} >1 lelong 8388608 >>5 lequad 8388608 {invalid} >1 lelong 16777216 >>5 lequad 16777216 {invalid} >1 lelong 33554432 >>5 lequad 33554432 {invalid} >5 lequad x uncompressed size: %lld bytes binwalk-2.1.1/src/binwalk/magic/misc000066400000000000000000000060571263655036500173410ustar00rootroot00000000000000#------------------------------------------------------------------------------ # $File: pdf,v 1.6 2009/09/19 16:28:11 christos Exp $ # pdf: file(1) magic for Portable Document Format # 0 string %PDF- PDF document, >6 byte !0x2e {invalid} >5 string x version: "%3s" #------------------------------------------------------------------------------ # $File: zyxel,v 1.6 2009/09/19 16:28:13 christos Exp $ # zyxel: file(1) magic for ZyXEL modems # # From # These are the /etc/magic entries to decode datafiles as used for the # ZyXEL U-1496E DATA/FAX/VOICE modems. (This header conforms to a # ZyXEL-defined standard) 0 string ZyXEL\002 ZyXEL voice data >10 byte 0 \b, CELP encoding >10 byte&0x0B 1 \b, ADPCM2 encoding >10 byte&0x0B 2 \b, ADPCM3 encoding >10 byte&0x0B 3 \b, ADPCM4 encoding >10 byte&0x0B 8 \b, New ADPCM3 encoding >10 byte&0x04 4 \b,with resync 0 string LinuxGuestRecord Xen saved domain file 0 string \x3chtml HTML document header >5 byte !0x20 >>5 byte !0x3e {invalid} 0 string \x3cHTML HTML document header >5 byte !0x20 >>5 byte !0x3e {invalid} 0 string \x3c/html\x3e HTML document footer 0 string \x3c/HTML\x3e HTML document footer 0 string \x3c?xml\x20version XML document, >15 string x version: "%.3s" # CodeGate 2011 http://nopsrus.blogspot.com/2013/05/codegate-ctf-2011-binary-100-points.html 0 string \x23\x40\x7e\x5e Windows Script Encoded Data (screnc.exe) 0 regex /[a-zA-Z0-9\.\-_]{1,25}/[a-zA-Z0-9\.\-_]{1,25}/[a-zA-Z0-9\.\-_]{1,25}/[a-zA-Z0-9\.\-_/].* Unix path: >0 string x %s 0 string neighbor Neighborly text, >0 string x "%s >63 string x \b%s" 0 string Neighbor Neighborly text, >0 string x "%s >63 string x \b%s" 0 string neighborly Neighborly text, best guess: Goodspeed, >0 string x "%s >63 string x \b%s" 0 string begin\x20 uuencoded data, >9 byte !0x20 {invalid}invalid format, >6 byte <0x30 {invalid}invalid permissions, >6 byte >0x39 {invalid}invalid permissions, >7 byte <0x30 {invalid}invalid permissions, >7 byte >0x39 {invalid}invalid permissions, >8 byte <0x30 {invalid}invalid permissions, >8 byte >0x39 {invalid}invalid permissions, >10 string x file name: "%s", >6 string x file permissions: "%.3s" binwalk-2.1.1/src/binwalk/magic/network000066400000000000000000000214711263655036500200740ustar00rootroot00000000000000# # "pcap-ng" capture files. # http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html # Pcap-ng files can contain multiple sections. Printing the endianness, # snaplen, or other information from the first SHB may be misleading. # 0 string \x0a\x0d\x0d\x0a\x1a\x2b\x3c\x4d Pcap-ng capture file, big-endian, >12 beshort x version %d >14 beshort x \b.%d 0 string \x0a\x0d\x0d\x0a\x4d\x3c\x2b\x1a Pcap-ng capture file, little-endian, >12 leshort x version %d >14 leshort x \b.%d # # "libpcap" capture files. # 0 string \xa1\xb2\xc3\xd4\x00 Libpcap capture file, big-endian, >4 beshort >2 {invalid} >4 beshort x version %d >6 beshort x \b.%d, >20 belong 0 (No link-layer encapsulation >20 belong 1 (Ethernet >20 belong 2 (3Mb Ethernet >20 belong 3 (AX.25 >20 belong 4 (ProNET >20 belong 5 (CHAOS >20 belong 6 (Token Ring >20 belong 7 (BSD ARCNET >20 belong 8 (SLIP >20 belong 9 (PPP >20 belong 10 (FDDI >20 belong 11 (RFC 1483 ATM >20 belong 12 (raw IP >20 belong 13 (BSD/OS SLIP >20 belong 14 (BSD/OS PPP >20 belong 19 (Linux ATM Classical IP >20 belong 50 (PPP or Cisco HDLC >20 belong 51 (PPP-over-Ethernet >20 belong 99 (Symantec Enterprise Firewall >20 belong 100 (RFC 1483 ATM >20 belong 101 (raw IP >20 belong 102 (BSD/OS SLIP >20 belong 103 (BSD/OS PPP >20 belong 104 (BSD/OS Cisco HDLC >20 belong 105 (802.11 >20 belong 106 (Linux Classical IP over ATM >20 belong 107 (Frame Relay >20 belong 108 (OpenBSD loopback >20 belong 109 (OpenBSD IPsec encrypted >20 belong 112 (Cisco HDLC >20 belong 113 (Linux "cooked" >20 belong 114 (LocalTalk >20 belong 117 (OpenBSD PFLOG >20 belong 119 (802.11 with Prism header >20 belong 122 (RFC 2625 IP over Fibre Channel >20 belong 123 (SunATM >20 belong 127 (802.11 with radiotap header >20 belong 129 (Linux ARCNET >20 belong 138 (Apple IP over IEEE 1394 >20 belong 140 (MTP2 >20 belong 141 (MTP3 >20 belong 143 (DOCSIS >20 belong 144 (IrDA >20 belong 147 (Private use 0 >20 belong 148 (Private use 1 >20 belong 149 (Private use 2 >20 belong 150 (Private use 3 >20 belong 151 (Private use 4 >20 belong 152 (Private use 5 >20 belong 153 (Private use 6 >20 belong 154 (Private use 7 >20 belong 155 (Private use 8 >20 belong 156 (Private use 9 >20 belong 157 (Private use 10 >20 belong 158 (Private use 11 >20 belong 159 (Private use 12 >20 belong 160 (Private use 13 >20 belong 161 (Private use 14 >20 belong 162 (Private use 15 >20 belong 163 (802.11 with AVS header >20 belong >163 {invalid}(invalid link layer >20 belong <0 {invalid}(invalid link layer >16 belong x \b, snaplen: %d) 0 lelong 0xa1b2c3d4 Libpcap capture file, little-endian, >4 leshort >2 {invalid} >4 leshort <0 {invalid} >4 leshort x version %d >6 leshort x \b.%d, >20 lelong 0 (No link-layer encapsulation >20 lelong 1 (Ethernet >20 lelong 2 (3Mb Ethernet >20 lelong 3 (AX.25 >20 lelong 4 (ProNET >20 lelong 5 (CHAOS >20 lelong 6 (Token Ring >20 lelong 7 (ARCNET >20 lelong 8 (SLIP >20 lelong 9 (PPP >20 lelong 10 (FDDI >20 lelong 11 (RFC 1483 ATM >20 lelong 12 (raw IP >20 lelong 13 (BSD/OS SLIP >20 lelong 14 (BSD/OS PPP >20 lelong 19 (Linux ATM Classical IP >20 lelong 50 (PPP or Cisco HDLC >20 lelong 51 (PPP-over-Ethernet >20 lelong 99 (Symantec Enterprise Firewall >20 lelong 100 (RFC 1483 ATM >20 lelong 101 (raw IP >20 lelong 102 (BSD/OS SLIP >20 lelong 103 (BSD/OS PPP >20 lelong 104 (BSD/OS Cisco HDLC >20 lelong 105 (802.11 >20 lelong 106 (Linux Classical IP over ATM >20 lelong 107 (Frame Relay >20 lelong 108 (OpenBSD loopback >20 lelong 109 (OpenBSD IPsec encrypted >20 lelong 112 (Cisco HDLC >20 lelong 113 (Linux "cooked" >20 lelong 114 (LocalTalk >20 lelong 117 (OpenBSD PFLOG >20 lelong 119 (802.11 with Prism header >20 lelong 122 (RFC 2625 IP over Fibre Channel >20 lelong 123 (SunATM >20 lelong 127 (802.11 with radiotap header >20 lelong 129 (Linux ARCNET >20 lelong 138 (Apple IP over IEEE 1394 >20 lelong 140 (MTP2 >20 lelong 141 (MTP3 >20 lelong 143 (DOCSIS >20 lelong 144 (IrDA >20 lelong 147 (Private use 0 >20 lelong 148 (Private use 1 >20 lelong 149 (Private use 2 >20 lelong 150 (Private use 3 >20 lelong 151 (Private use 4 >20 lelong 152 (Private use 5 >20 lelong 153 (Private use 6 >20 lelong 154 (Private use 7 >20 lelong 155 (Private use 8 >20 lelong 156 (Private use 9 >20 lelong 157 (Private use 10 >20 lelong 158 (Private use 11 >20 lelong 159 (Private use 12 >20 lelong 160 (Private use 13 >20 lelong 161 (Private use 14 >20 lelong 162 (Private use 15 >20 lelong 163 (802.11 with AVS header >20 lelong >163 {invalid}(invalid link layer >20 lelong <0 {invalid}(invalid link layer >16 lelong x \b, snaplen: %d) binwalk-2.1.1/src/binwalk/magic/sql000066400000000000000000000045351263655036500172040ustar00rootroot00000000000000#------------------------------------------------------------------------------ # $File: sql,v 1.6 2009/09/19 16:28:12 christos Exp $ # sql: file(1) magic for SQL files # # From: "Marty Leisner" # Recognize some MySQL files. # 0 beshort 0xfe01 MySQL table definition file >2 ubyte <1 {invalid} >2 ubyte >11 {invalid} >2 byte x Version %d 0 string \xfe\xfe\x03 MySQL MISAM index file >3 ubyte <1 {invalid} >3 ubyte >11 {invalid} >3 byte x Version %d 0 string \xfe\xfe\x07 MySQL MISAM compressed data file >3 ubyte <1 {invalid} >3 ubyte >11 {invalid} >3 byte x Version %d 0 string \xfe\xfe\x05 MySQL ISAM index file >3 ubyte <1 {invalid} >3 ubyte >11 {invalid} >3 byte x Version %d 0 string \xfe\xfe\x06 MySQL ISAM compressed data file >3 ubyte <1 {invalid} >3 ubyte >11 {invalid} >3 byte x Version %d #0 string \376bin MySQL replication log #------------------------------------------------------------------------------ # iRiver H Series database file # From Ken Guest # As observed from iRivNavi.iDB and unencoded firmware # 0 string iRivDB iRiver Database file >11 byte !0 >>11 string x Version "%s" #>39 string iHP-100 [H Series] #------------------------------------------------------------------------------ # SQLite database files # Ken Guest , Ty Sarna, Zack Weinberg # # Version 1 used GDBM internally; its files cannot be distinguished # from other GDBM files. # # Version 2 used this format: 0 string \x2A\x2A\x20This\x20file\x20contains\x20an\x20SQLite SQLite 2.x database # Version 3 of SQLite allows applications to embed their own "user version" # number in the database. Detect this and distinguish those files. 0 string SQLite\x20format\x203 SQLite 3.x database, >60 string _MTN monotone source repository >60 ubelong !0 \b, user version %u binwalk-2.1.1/src/binwalk/magic/vxworks000066400000000000000000000244361263655036500201320ustar00rootroot00000000000000 # Signatures to identify the start of a VxWorks symbol table # The jump is necessary to ensure that we don't accidentally # report a big endian symbol table followed immediately by a # little endian symbol table. We only want to show the beginning # of the symbol table, so use the once keyword too. 8 string \x00\x00\x05 VxWorks symbol table, big endian,{overlap}{once}{jump:156} >11 string !\x00\x00\x00\x00\x00 {invalid} >4 belong 0 {invalid} >4 ubelong x first entry: [type: function, code address: 0x%X, >0 belong 0 {invalid} >0 ubelong x symbol address: 0x%X] >24 belong !0x500 >>24 belong !0x700 >>>24 belong !0x900 \b, {invalid} >40 belong !0x500 >>40 belong !0x700 >>>40 belong !0x900 \b, {invalid} >56 belong !0x500 >>56 belong !0x700 >>>56 belong !0x900 \b, {invalid} >72 belong !0x500 >>72 belong !0x700 >>>72 belong !0x900 \b, {invalid} >88 belong !0x500 >>88 belong !0x700 >>>88 belong !0x900 \b, {invalid} >104 belong !0x500 >>104 belong !0x700 >>>104 belong !0x900 \b, {invalid} >120 belong !0x500 >>120 belong !0x700 >>>120 belong !0x900 \b, {invalid} >136 belong !0x500 >>136 belong !0x700 >>>136 belong !0x900 \b, {invalid} >152 belong !0x500 >>152 belong !0x700 >>>152 belong !0x900 \b, {invalid} 8 string \x00\x00\x07 VxWorks symbol table, big endian,{once}{jump:156} >11 string !\x00\x00\x00\x00\x00 {invalid} >4 belong 0 {invalid} >4 ubelong x first entry: [type: initialized data, code address: 0x%X, >0 belong 0 {invalid} >0 ubelong x symbol address: 0x%X] >24 belong !0x500 >>24 belong !0x700 >>>24 belong !0x900 \b, {invalid} >40 belong !0x500 >>40 belong !0x700 >>>40 belong !0x900 \b, {invalid} >56 belong !0x500 >>56 belong !0x700 >>>56 belong !0x900 \b, {invalid} >72 belong !0x500 >>72 belong !0x700 >>>72 belong !0x900 \b, {invalid} >88 belong !0x500 >>88 belong !0x700 >>>88 belong !0x900 \b, {invalid} >104 belong !0x500 >>104 belong !0x700 >>>104 belong !0x900 \b, {invalid} >120 belong !0x500 >>120 belong !0x700 >>>120 belong !0x900 \b, {invalid} >136 belong !0x500 >>136 belong !0x700 >>>136 belong !0x900 \b, {invalid} >152 belong !0x500 >>152 belong !0x700 >>>152 belong !0x900 \b, {invalid} 8 string \x00\x00\x09 VxWorks symbol table, big endian,{once}{jump:156} >11 string !\x00\x00\x00\x00\x00 {invalid} >4 belong 0 {invalid} >4 ubelong x first entry: [type: uninitialized data, code address: 0x%X, >0 belong 0 {invalid} >0 ubelong x symbol address: 0x%X] >24 belong !0x500 >>24 belong !0x700 >>>24 belong !0x900 \b, {invalid} >40 belong !0x500 >>40 belong !0x700 >>>40 belong !0x900 \b, {invalid} >56 belong !0x500 >>56 belong !0x700 >>>56 belong !0x900 \b, {invalid} >72 belong !0x500 >>72 belong !0x700 >>>72 belong !0x900 \b, {invalid} >88 belong !0x500 >>88 belong !0x700 >>>88 belong !0x900 \b, {invalid} >104 belong !0x500 >>104 belong !0x700 >>>104 belong !0x900 \b, {invalid} >120 belong !0x500 >>120 belong !0x700 >>>120 belong !0x900 \b, {invalid} >136 belong !0x500 >>136 belong !0x700 >>>136 belong !0x900 \b, {invalid} >152 belong !0x500 >>152 belong !0x700 >>>152 belong !0x900 \b, {invalid} 8 string \x00\x05 VxWorks symbol table, little endian,{once}{jump:156} >11 string !\x00\x00\x00\x00\x00\x00 {invalid} >4 lelong 0 {invalid} >4 ulelong x first entry: [type: function, code address: 0x%X, >0 lelong 0 {invalid} >0 ulelong x symbol address: 0x%X] >24 lelong !0x500 >>24 lelong !0x700 >>>24 lelong !0x900 \b, {invalid} >40 lelong !0x500 >>40 lelong !0x700 >>>40 lelong !0x900 \b, {invalid} >56 lelong !0x500 >>56 lelong !0x700 >>>56 lelong !0x900 \b, {invalid} >72 lelong !0x500 >>72 lelong !0x700 >>>72 lelong !0x900 \b, {invalid} >88 lelong !0x500 >>88 lelong !0x700 >>>88 lelong !0x900 \b, {invalid} >104 lelong !0x500 >>104 lelong !0x700 >>>104 lelong !0x900 \b, {invalid} >120 lelong !0x500 >>120 lelong !0x700 >>>120 lelong !0x900 \b, {invalid} >136 lelong !0x500 >>136 lelong !0x700 >>>136 lelong !0x900 \b, {invalid} >152 lelong !0x500 >>152 lelong !0x700 >>>152 lelong !0x900 \b, {invalid} 8 string \x00\x07 VxWorks symbol table, little endian,{once}{jump:156} >11 string !\x00\x00\x00\x00\x00\x00 {invalid} >4 lelong 0 {invalid} >4 ulelong x first entry: [type: initialized data, code address: 0x%X, >0 lelong 0 {invalid} >0 ulelong x symbol address: 0x%X] >24 lelong !0x500 >>24 lelong !0x700 >>>24 lelong !0x900 \b, {invalid} >40 lelong !0x500 >>40 lelong !0x700 >>>40 lelong !0x900 \b, {invalid} >56 lelong !0x500 >>56 lelong !0x700 >>>56 lelong !0x900 \b, {invalid} >72 lelong !0x500 >>72 lelong !0x700 >>>72 lelong !0x900 \b, {invalid} >88 lelong !0x500 >>88 lelong !0x700 >>>88 lelong !0x900 \b, {invalid} >104 lelong !0x500 >>104 lelong !0x700 >>>104 lelong !0x900 \b, {invalid} >120 lelong !0x500 >>120 lelong !0x700 >>>120 lelong !0x900 \b, {invalid} >136 lelong !0x500 >>136 lelong !0x700 >>>136 lelong !0x900 \b, {invalid} >152 lelong !0x500 >>152 lelong !0x700 >>>152 lelong !0x900 \b, {invalid} 8 string \x00\x09 VxWorks symbol table, little endian,{once}{jump:156} >11 string !\x00\x00\x00\x00\x00\x00 {invalid} >4 lelong 0 {invalid} >4 ulelong x first entry: [type: uninitialized data, code address: 0x%X, >0 lelong 0 {invalid} >0 ulelong x symbol address: 0x%X] >24 lelong !0x500 >>24 lelong !0x700 >>>24 lelong !0x900 \b, {invalid} >40 lelong !0x500 >>40 lelong !0x700 >>>40 lelong !0x900 \b, {invalid} >56 lelong !0x500 >>56 lelong !0x700 >>>56 lelong !0x900 \b, {invalid} >72 lelong !0x500 >>72 lelong !0x700 >>>72 lelong !0x900 \b, {invalid} >88 lelong !0x500 >>88 lelong !0x700 >>>88 lelong !0x900 \b, {invalid} >104 lelong !0x500 >>104 lelong !0x700 >>>104 lelong !0x900 \b, {invalid} >120 lelong !0x500 >>120 lelong !0x700 >>>120 lelong !0x900 \b, {invalid} >136 lelong !0x500 >>136 lelong !0x700 >>>136 lelong !0x900 \b, {invalid} >152 lelong !0x500 >>152 lelong !0x700 >>>152 lelong !0x900 \b, {invalid} 0 string WIND\x20version\x20 VxWorks WIND kernel version >13 byte 0 {invalid} >13 byte !0 >>13 string x "%s" 0 string VxWorks\00 VxWorks operating system version >8 byte 0 {invalid} >8 byte !0 >>8 string x "%s" >16 string !VxWorks {invalid} >32 byte !0 >>32 string x , compiled: "%s" binwalk-2.1.1/src/binwalk/modules/000077500000000000000000000000001263655036500170435ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/modules/__init__.py000066400000000000000000000013321263655036500211530ustar00rootroot00000000000000# Don't load the disasm module if the capstone module can't be found try: from binwalk.modules.disasm import Disasm except ImportError: pass # Don't load the compression module if the lzma module can't be found try: from binwalk.modules.compression import RawCompression except ImportError: pass from binwalk.modules.signature import Signature from binwalk.modules.hexdiff import HexDiff from binwalk.modules.general import General from binwalk.modules.extractor import Extractor from binwalk.modules.entropy import Entropy # These are depreciated. #from binwalk.modules.binvis import Plotter #from binwalk.modules.hashmatch import HashMatch #from binwalk.modules.heuristics import HeuristicCompressionAnalyzer binwalk-2.1.1/src/binwalk/modules/binvis.py000066400000000000000000000241751263655036500207200ustar00rootroot00000000000000# Generates 3D visualizations of input files. import os from binwalk.core.compat import * from binwalk.core.common import BlockFile from binwalk.core.module import Module, Option, Kwarg class Plotter(Module): ''' Base class for visualizing binaries in Qt. Other plotter classes are derived from this. ''' VIEW_DISTANCE = 1024 MAX_2D_PLOT_POINTS = 12500 MAX_3D_PLOT_POINTS = 25000 TITLE = "Binary Visualization" CLI = [ Option(short='3', long='3D', kwargs={'axis' : 3, 'enabled' : True}, description='Generate a 3D binary visualization'), Option(short='2', long='2D', kwargs={'axis' : 2, 'enabled' : True}, description='Project data points onto 3D cube walls only'), Option(short='Z', long='points', type=int, kwargs={'max_points' : 0}, description='Set the maximum number of plotted data points'), # Option(short='V', # long='grids', # kwargs={'show_grids' : True}, # description='Display the x-y-z grids in the resulting plot'), ] KWARGS = [ Kwarg(name='axis', default=3), Kwarg(name='max_points', default=0), Kwarg(name='show_grids', default=False), Kwarg(name='enabled', default=False), ] # There isn't really any useful data to print to console. Disable header and result output. HEADER = None RESULT = None def init(self): import pyqtgraph.opengl as gl from pyqtgraph.Qt import QtGui self.verbose = self.config.verbose self.offset = self.config.offset self.length = self.config.length self.plane_count = -1 self.plot_points = None if self.axis == 2: self.MAX_PLOT_POINTS = self.MAX_2D_PLOT_POINTS self._generate_data_point = self._generate_2d_data_point elif self.axis == 3: self.MAX_PLOT_POINTS = self.MAX_3D_PLOT_POINTS self._generate_data_point = self._generate_3d_data_point else: raise Exception("Invalid Plotter axis specified: %d. Must be one of: [2,3]" % self.axis) if not self.max_points: self.max_points = self.MAX_PLOT_POINTS self.app = QtGui.QApplication([]) self.window = gl.GLViewWidget() self.window.opts['distance'] = self.VIEW_DISTANCE if len(self.config.target_files) == 1: self.window.setWindowTitle(self.config.target_files[0]) def _print(self, message): ''' Print console messages. For internal use only. ''' if self.verbose: print(message) def _generate_plot_points(self, data_points): ''' Generates plot points from a list of data points. @data_points - A dictionary containing each unique point and its frequency of occurance. Returns a set of plot points. ''' total = 0 min_weight = 0 weightings = {} plot_points = {} # If the number of data points exceeds the maximum number of allowed data points, use a # weighting system to eliminate data points that occur less freqently. if sum(data_points.values()) > self.max_points: # First, generate a set of weight values 1 - 10 for i in range(1, 11): weightings[i] = 0 # Go through every data point and how many times that point occurs for (point, count) in iterator(data_points): # For each data point, compare it to each remaining weight value for w in get_keys(weightings): # If the number of times this data point occurred is >= the weight value, # then increment the weight value. Since weight values are ordered lowest # to highest, this means that more frequent data points also increment lower # weight values. Thus, the more high-frequency data points there are, the # more lower-frequency data points are eliminated. if count >= w: weightings[w] += 1 else: break # Throw out weight values that exceed the maximum number of data points if weightings[w] > self.max_points: del weightings[w] # If there's only one weight value left, no sense in continuing the loop... if len(weightings) == 1: break # The least weighted value is our minimum weight min_weight = min(weightings) # Get rid of all data points that occur less frequently than our minimum weight for point in get_keys(data_points): if data_points[point] < min_weight: del data_points[point] for point in sorted(data_points, key=data_points.get, reverse=True): plot_points[point] = data_points[point] # Register this as a result in case future modules need access to the raw point information, # but mark plot as False to prevent the entropy module from attempting to overlay this data on its graph. self.result(point=point, plot=False) total += 1 if total >= self.max_points: break return plot_points def _generate_data_point(self, data): ''' Subclasses must override this to return the appropriate data point. @data - A string of data self.axis in length. Returns a data point tuple. ''' return (0,0,0) def _generate_data_points(self, fp): ''' Generates a dictionary of data points and their frequency of occurrance. @fp - The BlockFile object to generate data points from. Returns a dictionary. ''' i = 0 data_points = {} self._print("Generating data points for %s" % fp.name) # We don't need any extra data from BlockFile fp.set_block_size(peek=0) while True: (data, dlen) = fp.read_block() if not data or not dlen: break i = 0 while (i+(self.axis-1)) < dlen: point = self._generate_data_point(data[i:i+self.axis]) if has_key(data_points, point): data_points[point] += 1 else: data_points[point] = 1 i += 3 return data_points def _generate_plot(self, plot_points): import numpy as np import pyqtgraph.opengl as gl nitems = float(len(plot_points)) pos = np.empty((nitems, 3)) size = np.empty((nitems)) color = np.empty((nitems, 4)) i = 0 for (point, weight) in iterator(plot_points): r = 0.0 g = 0.0 b = 0.0 pos[i] = point frequency_percentage = (weight / nitems) # Give points that occur more frequently a brighter color and larger point size. # Frequency is determined as a percentage of total unique data points. if frequency_percentage > .010: size[i] = .20 r = 1.0 elif frequency_percentage > .005: size[i] = .15 b = 1.0 elif frequency_percentage > .002: size[i] = .10 g = 1.0 r = 1.0 else: size[i] = .05 g = 1.0 color[i] = (r, g, b, 1.0) i += 1 scatter_plot = gl.GLScatterPlotItem(pos=pos, size=size, color=color, pxMode=False) scatter_plot.translate(-127.5, -127.5, -127.5) return scatter_plot def plot(self, wait=True): import pyqtgraph.opengl as gl self.window.show() if self.show_grids: xgrid = gl.GLGridItem() ygrid = gl.GLGridItem() zgrid = gl.GLGridItem() self.window.addItem(xgrid) self.window.addItem(ygrid) self.window.addItem(zgrid) # Rotate x and y grids to face the correct direction xgrid.rotate(90, 0, 1, 0) ygrid.rotate(90, 1, 0, 0) # Scale grids to the appropriate dimensions xgrid.scale(12.8, 12.8, 12.8) ygrid.scale(12.8, 12.8, 12.8) zgrid.scale(12.8, 12.8, 12.8) for fd in iter(self.next_file, None): data_points = self._generate_data_points(fd) self._print("Generating plot points from %d data points" % len(data_points)) self.plot_points = self._generate_plot_points(data_points) del data_points self._print("Generating graph from %d plot points" % len(self.plot_points)) self.window.addItem(self._generate_plot(self.plot_points)) if wait: self.wait() def wait(self): from pyqtgraph.Qt import QtCore, QtGui t = QtCore.QTimer() t.start(50) QtGui.QApplication.instance().exec_() def _generate_3d_data_point(self, data): ''' Plot data points within a 3D cube. ''' return (ord(data[0]), ord(data[1]), ord(data[2])) def _generate_2d_data_point(self, data): ''' Plot data points projected on each cube face. ''' self.plane_count += 1 if self.plane_count > 5: self.plane_count = 0 if self.plane_count == 0: return (0, ord(data[0]), ord(data[1])) elif self.plane_count == 1: return (ord(data[0]), 0, ord(data[1])) elif self.plane_count == 2: return (ord(data[0]), ord(data[1]), 0) elif self.plane_count == 3: return (255, ord(data[0]), ord(data[1])) elif self.plane_count == 4: return (ord(data[0]), 255, ord(data[1])) elif self.plane_count == 5: return (ord(data[0]), ord(data[1]), 255) def run(self): self.plot() return True binwalk-2.1.1/src/binwalk/modules/compression.py000066400000000000000000000234251263655036500217640ustar00rootroot00000000000000# Performs raw decompression of various compression algorithms (currently, only deflate). import os import zlib import lzma import struct import binwalk.core.compat import binwalk.core.common from binwalk.core.module import Option, Kwarg, Module class LZMAHeader(object): def __init__(self, **kwargs): for (k,v) in binwalk.core.compat.iterator(kwargs): setattr(self, k, v) class LZMA(object): DESCRIPTION = "Raw LZMA compression stream" COMMON_PROPERTIES = [0x5D, 0x6E] MAX_PROP = ((4 * 5 + 4) * 9 + 8) BLOCK_SIZE = 32*1024 def __init__(self, module): self.module = module self.properties = None self.build_properties() self.build_dictionaries() self.build_headers() # Add an extraction rule if self.module.extractor.enabled: self.module.extractor.add_rule(regex='^%s' % self.DESCRIPTION.lower(), extension="7z", cmd=self.extractor) def extractor(self, file_name): # Open and read the file containing the raw compressed data. # This is not terribly efficient, especially for large files... compressed_data = binwalk.core.common.BlockFile(file_name).read() # Re-run self.decompress to detect the properties for this compressed data (stored in self.properties) if self.decompress(compressed_data[:self.BLOCK_SIZE]): # Build an LZMA header on top of the raw compressed data and write it back to disk. # Header consists of the detected properties values, the largest possible dictionary size, # and a fake output file size field. header = chr(self.properties) + self.dictionaries[-1] + ("\xFF" * 8) binwalk.core.common.BlockFile(file_name, "wb").write(header + compressed_data) # Try to extract it with all the normal lzma extractors until one works for exrule in self.module.extractor.match("lzma compressed data"): if self.module.extractor.execute(exrule['cmd'], file_name) == True: break def build_property(self, pb, lp, lc): prop = (((pb * 5) + lp) * 9) + lc if prop > self.MAX_PROP: return None return int(prop) def parse_property(self, prop): prop = int(ord(prop)) if prop > self.MAX_PROP: return None pb = prop / (9 * 5); prop -= pb * 9 * 5; lp = prop / 9; lc = prop - lp * 9; return (pb, lp, lc) def parse_header(self, header): (pb, lp, lc) = self.parse_property(header[0]) dictionary = struct.unpack("= 2: block_offset = 0 # Loop through the entire block, or until we're pretty sure we've found some valid code in this block while (block_offset < dlen) and (result is None or result.count < self.THRESHOLD): # Don't pass the entire data block into disasm_lite, it's horribly inefficient # to pass large strings around in Python. Break it up into smaller code blocks instead. code_block = binwalk.core.compat.str2bytes(data[block_offset:block_offset+self.disasm_data_size]) # If this code block doesn't contain at least two different bytes, skip it # to prevent false positives (e.g., "\x00\x00\x00\x00" is a nop in MIPS). if len(set(code_block)) >= 2: for (md, description) in self.disassemblers: insns = [insn for insn in md.disasm_lite(code_block, (total_read+block_offset))] binwalk.core.common.debug("0x%.8X %s, at least %d valid instructions" % ((total_read+block_offset), description, len(insns))) # Did we disassemble at least self.min_insn_count instructions? if len(insns) >= self.min_insn_count: # If we've already found the same type of code in this block, simply update the result counter if result and result.description == description: result.count += 1 if result.count >= self.THRESHOLD: break else: result = ArchResult(offset=total_read+block_offset+fp.offset, description=description, insns=insns, count=1) block_offset += 1 self.status.completed += 1 if result is not None: r = self.result(offset=result.offset, file=fp, description=(result.description + ", at least %d valid instructions" % len(result.insns))) if r.valid and r.display: if self.config.verbose: for (position, size, mnem, opnds) in result.insns: self.result(offset=position, file=fp, description="%s %s" % (mnem, opnds)) if not self.keep_going: return total_read += dlen self.status.completed = total_read def run(self): for fp in iter(self.next_file, None): self.header() self.scan_file(fp) self.footer() binwalk-2.1.1/src/binwalk/modules/entropy.py000066400000000000000000000263361263655036500211270ustar00rootroot00000000000000# Calculates and optionally plots the entropy of input files. import os import math import zlib import binwalk.core.common from binwalk.core.compat import * from binwalk.core.module import Module, Option, Kwarg class Entropy(Module): XLABEL = 'Offset' YLABEL = 'Entropy' XUNITS = 'B' YUNITS = 'E' FILE_WIDTH = 1024 FILE_FORMAT = 'png' COLORS = ['r', 'g', 'c', 'b', 'm'] DEFAULT_BLOCK_SIZE = 1024 DEFAULT_DATA_POINTS = 2048 DEFAULT_TRIGGER_HIGH = .95 DEFAULT_TRIGGER_LOW = .85 TITLE = "Entropy Analysis" ORDER = 8 # TODO: Add --dpoints option to set the number of data points? CLI = [ Option(short='E', long='entropy', kwargs={'enabled' : True}, description='Calculate file entropy'), Option(short='F', long='fast', kwargs={'use_zlib' : True}, description='Use faster, but less detailed, entropy analysis'), Option(short='J', long='save', kwargs={'save_plot' : True}, description='Save plot as a PNG'), Option(short='Q', long='nlegend', kwargs={'show_legend' : False}, description='Omit the legend from the entropy plot graph'), Option(short='N', long='nplot', kwargs={'do_plot' : False}, description='Do not generate an entropy plot graph'), Option(short='H', long='high', type=float, kwargs={'trigger_high' : DEFAULT_TRIGGER_HIGH}, description='Set the rising edge entropy trigger threshold (default: %.2f)' % DEFAULT_TRIGGER_HIGH), Option(short='L', long='low', type=float, kwargs={'trigger_low' : DEFAULT_TRIGGER_LOW}, description='Set the falling edge entropy trigger threshold (default: %.2f)' % DEFAULT_TRIGGER_LOW), ] KWARGS = [ Kwarg(name='enabled', default=False), Kwarg(name='save_plot', default=False), Kwarg(name='trigger_high', default=DEFAULT_TRIGGER_HIGH), Kwarg(name='trigger_low', default=DEFAULT_TRIGGER_LOW), Kwarg(name='use_zlib', default=False), Kwarg(name='display_results', default=True), Kwarg(name='do_plot', default=True), Kwarg(name='show_legend', default=True), Kwarg(name='block_size', default=0), ] # Run this module last so that it can process all other module's results and overlay them on the entropy graph PRIORITY = 0 def init(self): self.HEADER[-1] = "ENTROPY" self.max_description_length = 0 self.file_markers = {} if self.use_zlib: self.algorithm = self.gzip else: self.algorithm = self.shannon # Get a list of all other module's results to mark on the entropy graph for (module, obj) in iterator(self.modules): for result in obj.results: if result.plot and result.file and result.description: description = result.description.split(',')[0] if not has_key(self.file_markers, result.file.name): self.file_markers[result.file.name] = [] if len(description) > self.max_description_length: self.max_description_length = len(description) self.file_markers[result.file.name].append((result.offset, description)) # If other modules have been run and they produced results, don't spam the terminal with entropy results if self.file_markers: self.display_results = False if not self.block_size: if self.config.block: self.block_size = self.config.block else: self.block_size = None def _entropy_sigterm_handler(self, *args): print ("FUck it all.") def run(self): # If generating a graphical plot, this function will never return, as it invokes # pg.exit. Calling pg.exit is pretty much required, but pg.exit calls os._exit in # order to work around QT cleanup issues. self._run() def _run(self): # Sanity check and warning if pyqtgraph isn't found if self.do_plot: try: import pyqtgraph as pg except ImportError as e: binwalk.core.common.warning("Failed to import pyqtgraph module, visual entropy graphing will be disabled") self.do_plot = False for fp in iter(self.next_file, None): if self.display_results: self.header() self.calculate_file_entropy(fp) if self.display_results: self.footer() if self.do_plot: if not self.save_plot: from pyqtgraph.Qt import QtGui QtGui.QApplication.instance().exec_() pg.exit() def calculate_file_entropy(self, fp): # Tracks the last displayed rising/falling edge (0 for falling, 1 for rising, None if nothing has been printed yet) last_edge = None # Auto-reset the trigger; if True, an entropy above/below self.trigger_high/self.trigger_low will be printed trigger_reset = True # Clear results from any previously analyzed files self.clear(results=True) # If -K was not specified, calculate the block size to create DEFAULT_DATA_POINTS data points if self.block_size is None: block_size = fp.size / self.DEFAULT_DATA_POINTS # Round up to the nearest DEFAULT_BLOCK_SIZE (1024) block_size = int(block_size + ((self.DEFAULT_BLOCK_SIZE - block_size) % self.DEFAULT_BLOCK_SIZE)) else: block_size = self.block_size # Make sure block size is greater than 0 if block_size <= 0: block_size = self.DEFAULT_BLOCK_SIZE binwalk.core.common.debug("Entropy block size (%d data points): %d" % (self.DEFAULT_DATA_POINTS, block_size)) while True: file_offset = fp.tell() (data, dlen) = fp.read_block() if not data: break i = 0 while i < dlen: entropy = self.algorithm(data[i:i+block_size]) display = self.display_results description = "%f" % entropy if not self.config.verbose: if last_edge in [None, 0] and entropy > self.trigger_low: trigger_reset = True elif last_edge in [None, 1] and entropy < self.trigger_high: trigger_reset = True if trigger_reset and entropy >= self.trigger_high: description = "Rising entropy edge (%f)" % entropy display = self.display_results last_edge = 1 trigger_reset = False elif trigger_reset and entropy <= self.trigger_low: description = "Falling entropy edge (%f)" % entropy display = self.display_results last_edge = 0 trigger_reset = False else: display = False description = "%f" % entropy r = self.result(offset=(file_offset + i), file=fp, entropy=entropy, description=description, display=display) i += block_size if self.do_plot: self.plot_entropy(fp.name) def shannon(self, data): ''' Performs a Shannon entropy analysis on a given block of data. ''' entropy = 0 if data: length = len(data) seen = dict(((chr(x), 0) for x in range(0, 256))) for byte in data: seen[byte] += 1 for x in range(0, 256): p_x = float(seen[chr(x)]) / length if p_x > 0: entropy -= p_x * math.log(p_x, 2) return (entropy / 8) def gzip(self, data, truncate=True): ''' Performs an entropy analysis based on zlib compression ratio. This is faster than the shannon entropy analysis, but not as accurate. ''' # Entropy is a simple ratio of: / e = float(float(len(zlib.compress(str2bytes(data), 9))) / float(len(data))) if truncate and e > 1.0: e = 1.0 return e def plot_entropy(self, fname): try: import numpy as np import pyqtgraph as pg import pyqtgraph.exporters as exporters except ImportError as e: return i = 0 x = [] y = [] plotted_colors = {} for r in self.results: x.append(r.offset) y.append(r.entropy) plt = pg.plot(title=fname, clear=True) # Disable auto-ranging of the Y (entropy) axis, as it # can cause some very un-intuitive graphs, particularly #for files with only high-entropy data. plt.setYRange(0, 1) if self.show_legend and has_key(self.file_markers, fname): plt.addLegend(size=(self.max_description_length*10, 0)) for (offset, description) in self.file_markers[fname]: # If this description has already been plotted at a different offset, we need to # use the same color for the marker, but set the description to None to prevent # duplicate entries in the graph legend. # # Else, get the next color and use it to mark descriptions of this type. if has_key(plotted_colors, description): color = plotted_colors[description] description = None else: color = self.COLORS[i] plotted_colors[description] = color i += 1 if i >= len(self.COLORS): i = 0 plt.plot(x=[offset,offset], y=[0,1.1], name=description, pen=pg.mkPen(color, width=2.5)) # Plot data points plt.plot(x, y, pen='y') # TODO: legend is not displayed properly when saving plots to disk if self.save_plot: # Save graph to CWD out_file = os.path.join(os.getcwd(), os.path.basename(fname)) # exporters.ImageExporter is different in different versions of pyqtgraph try: exporter = exporters.ImageExporter(plt.plotItem) except TypeError: exporter = exporters.ImageExporter.ImageExporter(plt.plotItem) exporter.parameters()['width'] = self.FILE_WIDTH exporter.export(binwalk.core.common.unique_file_name(out_file, self.FILE_FORMAT)) else: plt.setLabel('left', self.YLABEL, units=self.YUNITS) plt.setLabel('bottom', self.XLABEL, units=self.XUNITS) binwalk-2.1.1/src/binwalk/modules/extractor.py000066400000000000000000000710101263655036500214270ustar00rootroot00000000000000# Performs extraction of data that matches extraction rules. # This is automatically invoked by core.module code if extraction has been # enabled by the user; other modules need not reference this module directly. import os import re import sys import stat import shlex import tempfile import subprocess import binwalk.core.common from binwalk.core.compat import * from binwalk.core.module import Module, Option, Kwarg from binwalk.core.common import file_size, file_md5, unique_file_name, BlockFile class ExtractInfo(object): def __init__(self): self.carved = {} self.extracted = {} self.directory = None class Extractor(Module): ''' Extractor class, responsible for extracting files from the target file and executing external applications, if requested. ''' # Extract rules are delimited with a colon. # :[:] RULE_DELIM = ':' # Comments in the extract.conf files start with a pound COMMENT_DELIM ='#' # Place holder for the extracted file name in the command FILE_NAME_PLACEHOLDER = '%e' # Unique path delimiter, used for generating unique output file/directory names. # Useful when, for example, extracting two squashfs images (squashfs-root, squashfs-root-0). UNIQUE_PATH_DELIMITER = '%%' TITLE = 'Extraction' ORDER = 9 PRIMARY = False CLI = [ Option(short='e', long='extract', kwargs={'load_default_rules' : True, 'enabled' : True}, description='Automatically extract known file types'), Option(short='D', long='dd', type=list, dtype='type:ext:cmd', kwargs={'manual_rules' : [], 'enabled' : True}, description='Extract signatures, give the files an extension of , and execute '), Option(short='M', long='matryoshka', kwargs={'matryoshka' : 8}, description='Recursively scan extracted files'), Option(short='d', long='depth', type=int, kwargs={'matryoshka' : 0}, description='Limit matryoshka recursion depth (default: 8 levels deep)'), Option(short='C', long='directory', type=str, kwargs={'base_directory' : 0}, description='Extract files/folders to a custom directory (default: current working directory)'), Option(short='j', long='size', type=int, kwargs={'max_size' : 0}, description='Limit the size of each extracted file'), Option(short='n', long='count', type=int, kwargs={'max_count' : 0}, description='Limit the number of extracted files'), Option(short='r', long='rm', kwargs={'remove_after_execute' : True}, description='Delete carved files after extraction'), Option(short='z', long='carve', kwargs={'run_extractors' : False}, description="Carve data from files, but don't execute extraction utilities"), ] KWARGS = [ Kwarg(name='max_size', default=None), Kwarg(name='max_count', default=None), Kwarg(name='base_directory', default=None), Kwarg(name='remove_after_execute', default=False), Kwarg(name='load_default_rules', default=False), Kwarg(name='run_extractors', default=True), Kwarg(name='manual_rules', default=[]), Kwarg(name='matryoshka', default=0), Kwarg(name='enabled', default=False), ] def load(self): # Holds a list of extraction rules loaded either from a file or when manually specified. self.extract_rules = [] # The input file specific output directory path (to be determined at runtime) self.directory = None # Key value pairs of input file path and output extraction path self.output = {} # Number of extracted files self.extraction_count = 0 if self.load_default_rules: self.load_defaults() for manual_rule in self.manual_rules: self.add_rule(manual_rule) if self.matryoshka: self.config.verbose = True def add_pending(self, f): # Ignore symlinks if os.path.islink(f): return # Get the file mode to check and see if it's a block/char device try: file_mode = os.stat(f).st_mode except OSError as e: return # Only add this to the pending list of files to scan # if the file is a regular file or a block/character device. if (stat.S_ISREG(file_mode) or stat.S_ISBLK(file_mode) or stat.S_ISCHR(file_mode)): self.pending.append(f) def reset(self): # Holds a list of pending files that should be scanned; only populated if self.matryoshka == True self.pending = [] # Holds a dictionary of extraction directories created for each scanned file. self.extraction_directories = {} # Holds a dictionary of the last directory listing for a given directory; used for identifying # newly created/extracted files that need to be appended to self.pending. self.last_directory_listing = {} def callback(self, r): # Make sure the file attribute is set to a compatible instance of binwalk.core.common.BlockFile try: r.file.size except KeyboardInterrupt as e: pass except Exception as e: return if not r.size: size = r.file.size - r.offset else: size = r.size # Only extract valid results that have been marked for extraction and displayed to the user. # Note that r.display is still True even if --quiet has been specified; it is False if the result has been # explicitly excluded via the -y/-x options. if r.valid and r.extract and r.display and (not self.max_count or self.extraction_count < self.max_count): # Create some extract output for this file, it it doesn't already exist if not binwalk.core.common.has_key(self.output, r.file.path): self.output[r.file.path] = ExtractInfo() # Attempt extraction binwalk.core.common.debug("Extractor callback for %s @%d [%s]" % (r.file.name, r.offset, r.description)) (extraction_directory, dd_file, scan_extracted_files) = self.extract(r.offset, r.description, r.file.path, size, r.name) # If the extraction was successful, self.extract will have returned the output directory and name of the dd'd file if extraction_directory and dd_file: # Track the number of extracted files self.extraction_count += 1 # Get the full path to the dd'd file and save it in the output info for this file dd_file_path = os.path.join(extraction_directory, dd_file) self.output[r.file.path].carved[r.offset] = dd_file_path self.output[r.file.path].extracted[r.offset] = [] # Do a directory listing of the output directory directory_listing = set(os.listdir(extraction_directory)) # If this is a newly created output directory, self.last_directory_listing won't have a record of it. # If we've extracted other files to this directory before, it will. if not has_key(self.last_directory_listing, extraction_directory): self.last_directory_listing[extraction_directory] = set() # Loop through a list of newly created files (i.e., files that weren't listed in the last directory listing) for f in directory_listing.difference(self.last_directory_listing[extraction_directory]): # Build the full file path and add it to the extractor results file_path = os.path.join(extraction_directory, f) real_file_path = os.path.realpath(file_path) self.result(description=file_path, display=False) # Also keep a list of files created by the extraction utility if real_file_path != dd_file_path: self.output[r.file.path].extracted[r.offset].append(real_file_path) # If recursion was specified, and the file is not the same one we just dd'd if (self.matryoshka and file_path != dd_file_path and scan_extracted_files and self.directory in real_file_path): # If the recursion level of this file is less than or equal to our desired recursion level if len(real_file_path.split(self.directory)[1].split(os.path.sep)) <= self.matryoshka: # If this is a directory and we are supposed to process directories for this extractor, # then add all files under that directory to the list of pending files. if os.path.isdir(file_path): for root, dirs, files in os.walk(file_path): for f in files: full_path = os.path.join(root, f) self.add_pending(full_path) # If it's just a file, it to the list of pending files else: self.add_pending(file_path) # Update the last directory listing for the next time we extract a file to this same output directory self.last_directory_listing[extraction_directory] = directory_listing def append_rule(self, r): self.extract_rules.append(r.copy()) def add_rule(self, txtrule=None, regex=None, extension=None, cmd=None, codes=[0, None], recurse=True): ''' Adds a set of rules to the extraction rule list. @txtrule - Rule string, or list of rule strings, in the format :[:] @regex - If rule string is not specified, this is the regular expression string to use. @extension - If rule string is not specified, this is the file extension to use. @cmd - If rule string is not specified, this is the command to run. Alternatively a callable object may be specified, which will be passed one argument: the path to the file to extract. @codes - A list of valid return codes for the extractor. @recurse - If False, extracted directories will not be recursed into when the matryoshka option is enabled. Returns None. ''' rules = [] match = False r = { 'extension' : '', 'cmd' : '', 'regex' : None, 'codes' : codes, 'recurse' : recurse, } # Process single explicitly specified rule if not txtrule and regex and extension: r['extension'] = extension r['regex'] = re.compile(regex) if cmd: r['cmd'] = cmd self.append_rule(r) return # Process rule string, or list of rule strings if not isinstance(txtrule, type([])): rules = [txtrule] else: rules = txtrule for rule in rules: r['cmd'] = '' r['extension'] = '' try: values = self._parse_rule(rule) match = values[0] r['regex'] = re.compile(values[0]) r['extension'] = values[1] r['cmd'] = values[2] r['codes'] = values[3] r['recurse'] = values[4] except KeyboardInterrupt as e: raise e except Exception: pass # Verify that the match string was retrieved. if match: self.append_rule(r) def remove_rule(self, text): ''' Remove all rules that match a specified text. @text - The text to match against. Returns the number of rules removed. ''' rm = [] for i in range(0, len(self.extract_rules)): if self.extract_rules[i]['regex'].match(text): rm.append(i) for i in rm: self.extract_rules.pop(i) return len(rm) def clear_rules(self): ''' Deletes all extraction rules. Returns None. ''' self.extract_rules = [] def get_rules(self): ''' Returns a list of all extraction rules. ''' return self.extract_rules def load_from_file(self, fname): ''' Loads extraction rules from the specified file. @fname - Path to the extraction rule file. Returns None. ''' try: # Process each line from the extract file, ignoring comments with open(fname, 'r') as f: for rule in f.readlines(): self.add_rule(rule.split(self.COMMENT_DELIM, 1)[0]) except KeyboardInterrupt as e: raise e except Exception as e: raise Exception("Extractor.load_from_file failed to load file '%s': %s" % (fname, str(e))) def load_defaults(self): ''' Loads default extraction rules from the user and system extract.conf files. Returns None. ''' # Load the user extract file first to ensure its rules take precedence. extract_files = [ self.config.settings.user.extract, self.config.settings.system.extract, ] for extract_file in extract_files: if extract_file: try: self.load_from_file(extract_file) except KeyboardInterrupt as e: raise e except Exception as e: if binwalk.core.common.DEBUG: raise Exception("Extractor.load_defaults failed to load file '%s': %s" % (extract_file, str(e))) def build_output_directory(self, path): ''' Set the output directory for extracted files. @path - The path to the file that data will be extracted from. Returns None. ''' # If we have not already created an output directory for this target file, create one now if not has_key(self.extraction_directories, path): basedir = os.path.dirname(path) basename = os.path.basename(path) # Make sure we put the initial extracted file in the CWD if self.directory is None: if self.base_directory is None: basedir = os.getcwd() else: basedir = self.base_directory if not os.path.exists(basedir): os.mkdir(basedir) outdir = os.path.join(basedir, '_' + basename) output_directory = unique_file_name(outdir, extension='extracted') if not os.path.exists(output_directory): os.mkdir(output_directory) self.extraction_directories[path] = output_directory # Else, just use the already created directory else: output_directory = self.extraction_directories[path] # Set the initial base extraction directory for later determining the level of recusion if self.directory is None: self.directory = os.path.realpath(output_directory) + os.path.sep self.output[path].directory = self.directory return output_directory def cleanup_extracted_files(self, tf=None): ''' Set the action to take after a file is extracted. @tf - If set to True, extracted files will be cleaned up after running a command against them. If set to False, extracted files will not be cleaned up after running a command against them. If set to None or not specified, the current setting will not be changed. Returns the current cleanup status (True/False). ''' if tf is not None: self.remove_after_execute = tf return self.remove_after_execute def extract(self, offset, description, file_name, size, name=None): ''' Extract an embedded file from the target file, if it matches an extract rule. Called automatically by Binwalk.scan(). @offset - Offset inside the target file to begin the extraction. @description - Description of the embedded file to extract, as returned by libmagic. @file_name - Path to the target file. @size - Number of bytes to extract. @name - Name to save the file as. Returns the name of the extracted file (blank string if nothing was extracted). ''' fname = '' original_dir = os.getcwd() rules = self.match(description) file_path = os.path.realpath(file_name) # No extraction rules for this file if not rules: return (None, None, False) else: binwalk.core.common.debug("Found %d matching extraction rules" % len(rules)) output_directory = self.build_output_directory(file_name) # Extract to end of file if no size was specified if not size: size = file_size(file_path) - offset if os.path.isfile(file_path): os.chdir(output_directory) # Loop through each extraction rule until one succeeds for i in range(0, len(rules)): rule = rules[i] # Make sure we don't recurse into any extracted directories if instructed not to if rule['recurse'] in [True, False]: recurse = rule['recurse'] else: recurse = True # Copy out the data to disk, if we haven't already fname = self._dd(file_path, offset, size, rule['extension'], output_file_name=name) # If there was a command specified for this rule, try to execute it. # If execution fails, the next rule will be attempted. if rule['cmd']: # Note the hash of the original file; if --rm is specified and the # extraction utility modifies the original file rather than creating # a new one (AFAIK none currently do, but could happen in the future), # we don't want to remove this file. if self.remove_after_execute: fname_md5 = file_md5(fname) # Execute the specified command against the extracted file if self.run_extractors: extract_ok = self.execute(rule['cmd'], fname, rule['codes']) else: extract_ok = True # Only clean up files if remove_after_execute was specified if extract_ok == True and self.remove_after_execute: # Remove the original file that we extracted, # if it has not been modified by the extractor. try: if file_md5(fname) == fname_md5: os.unlink(fname) except KeyboardInterrupt as e: raise e except Exception as e: pass # If the command executed OK, don't try any more rules if extract_ok == True: break # Else, remove the extracted file if this isn't the last rule in the list. # If it is the last rule, leave the file on disk for the user to examine. elif i != (len(rules)-1): try: os.unlink(fname) except KeyboardInterrupt as e: raise e except Exception as e: pass # If there was no command to execute, just use the first rule else: break os.chdir(original_dir) return (output_directory, fname, recurse) def _entry_offset(self, index, entries, description): ''' Gets the offset of the first entry that matches the description. @index - Index into the entries list to begin searching. @entries - Dictionary of result entries. @description - Case insensitive description. Returns the offset, if a matching description is found. Returns -1 if a matching description is not found. ''' description = description.lower() for (offset, infos) in entries[index:]: for info in infos: if info['description'].lower().startswith(description): return offset return -1 def match(self, description): ''' Check to see if the provided description string matches an extract rule. Called internally by self.extract(). @description - Description string to check. Returns the associated rule dictionary if a match is found. Returns None if no match is found. ''' rules = [] description = description.lower() for rule in self.extract_rules: if rule['regex'].search(description): rules.append(rule) return rules def _parse_rule(self, rule): ''' Parses an extraction rule. @rule - Rule string. Returns an array of ['', '', '', '', ]. ''' values = rule.strip().split(self.RULE_DELIM, 4) if len(values) >= 4: codes = values[3].split(',') for i in range(0, len(codes)): try: codes[i] = int(codes[i], 0) except ValueError as e: binwalk.core.common.warning("The specified return code '%s' for extractor '%s' is not a valid number!" % (codes[i], values[0])) values[3] = codes if len(values) >= 5: values[4] = (values[4].lower() == 'true') return values def _dd(self, file_name, offset, size, extension, output_file_name=None): ''' Extracts a file embedded inside the target file. @file_name - Path to the target file. @offset - Offset inside the target file where the embedded file begins. @size - Number of bytes to extract. @extension - The file exension to assign to the extracted file on disk. @output_file_name - The requested name of the output file. Returns the extracted file name. ''' total_size = 0 # Default extracted file name is . default_bname = "%X" % (offset + self.config.base) if self.max_size and size > self.max_size: size = self.max_size if not output_file_name or output_file_name is None: bname = default_bname else: # Strip the output file name of invalid/dangerous characters (like file paths) bname = os.path.basename(output_file_name) fname = unique_file_name(bname, extension) try: # If byte swapping is enabled, we need to start reading at a swap-size # aligned offset, then index in to the read data appropriately. if self.config.swap_size: adjust = offset % self.config.swap_size else: adjust = 0 offset -= adjust # Open the target file and seek to the offset fdin = self.config.open_file(file_name) fdin.seek(offset) # Open the output file try: fdout = BlockFile(fname, 'w') except KeyboardInterrupt as e: raise e except Exception as e: # Fall back to the default name if the requested name fails fname = unique_file_name(default_bname, extension) fdout = BlockFile(fname, 'w') while total_size < size: (data, dlen) = fdin.read_block() if not data: break else: fdout.write(str2bytes(data[adjust:dlen])) total_size += (dlen-adjust) adjust = 0 # Cleanup fdout.close() fdin.close() except KeyboardInterrupt as e: raise e except Exception as e: raise Exception("Extractor.dd failed to extract data from '%s' to '%s': %s" % (file_name, fname, str(e))) binwalk.core.common.debug("Carved data block 0x%X - 0x%X from '%s' to '%s'" % (offset, offset+size, file_name, fname)) return fname def execute(self, cmd, fname, codes=[0, None]): ''' Execute a command against the specified file. @cmd - Command to execute. @fname - File to run command against. @codes - List of return codes indicating cmd success. Returns True on success, False on failure, or None if the external extraction utility could not be found. ''' tmp = None rval = 0 retval = True binwalk.core.common.debug("Running extractor '%s'" % str(cmd)) try: if callable(cmd): try: retval = cmd(fname) except KeyboardInterrupt as e: raise e except Exception as e: binwalk.core.common.warning("Internal extractor '%s' failed with exception: '%s'" % (str(cmd), str(e))) elif cmd: # If not in debug mode, create a temporary file to redirect stdout and stderr to if not binwalk.core.common.DEBUG: tmp = tempfile.TemporaryFile() # Execute. for command in cmd.split("&&"): # Generate unique file paths for all paths in the current command that are surrounded by UNIQUE_PATH_DELIMITER while self.UNIQUE_PATH_DELIMITER in command: need_unique_path = command.split(self.UNIQUE_PATH_DELIMITER)[1].split(self.UNIQUE_PATH_DELIMITER)[0] unique_path = binwalk.core.common.unique_file_name(need_unique_path) command = command.replace(self.UNIQUE_PATH_DELIMITER + need_unique_path + self.UNIQUE_PATH_DELIMITER, unique_path) # Replace all instances of FILE_NAME_PLACEHOLDER in the command with fname command = command.strip().replace(self.FILE_NAME_PLACEHOLDER, fname) binwalk.core.common.debug("subprocess.call(%s, stdout=%s, stderr=%s)" % (command, str(tmp), str(tmp))) rval = subprocess.call(shlex.split(command), stdout=tmp, stderr=tmp) if rval in codes: retval = True else: retval = False binwalk.core.common.debug('External extractor command "%s" completed with return code %d (success: %s)' % (cmd, rval, str(retval))) # TODO: Should errors from all commands in a command string be checked? Currently we only support # specifying one set of error codes, so at the moment, this is not done; it is up to the # final command to return success or failure (which presumably it will if previous necessary # commands were not successful, but this is an assumption). #if retval == False: # break except KeyboardInterrupt as e: raise e except Exception as e: binwalk.core.common.warning("Extractor.execute failed to run external extractor '%s': %s" % (str(cmd), str(e))) retval = None if tmp is not None: tmp.close() return retval binwalk-2.1.1/src/binwalk/modules/general.py000066400000000000000000000177361263655036500210500ustar00rootroot00000000000000# Module to process general user input options (scan length, starting offset, etc). import io import os import re import sys import argparse import binwalk.core.idb import binwalk.core.common import binwalk.core.display import binwalk.core.settings from binwalk.core.compat import * from binwalk.core.module import Module, Option, Kwarg, show_help class General(Module): TITLE = "General" ORDER = 0 DEFAULT_DEPENDS = [] CLI = [ Option(long='length', short='l', type=int, kwargs={'length' : 0}, description='Number of bytes to scan'), Option(long='offset', short='o', type=int, kwargs={'offset' : 0}, description='Start scan at this file offset'), Option(long='base', short='O', type=int, kwargs={'base' : 0}, description='Add a base address to all printed offsets'), Option(long='block', short='K', type=int, kwargs={'block' : 0}, description='Set file block size'), Option(long='swap', short='g', type=int, kwargs={'swap_size' : 0}, description='Reverse every n bytes before scanning'), Option(long='log', short='f', type=argparse.FileType, kwargs={'log_file' : None}, description='Log results to file'), Option(long='csv', short='c', kwargs={'csv' : True}, description='Log results to file in CSV format'), Option(long='term', short='t', kwargs={'format_to_terminal' : True}, description='Format output to fit the terminal window'), Option(long='quiet', short='q', kwargs={'quiet' : True}, description='Suppress output to stdout'), Option(long='verbose', short='v', kwargs={'verbose' : True}, description='Enable verbose output'), Option(short='h', long='help', kwargs={'show_help' : True}, description='Show help output'), Option(short='a', long='finclude', type=str, kwargs={'file_name_include_regex' : ""}, description='Only scan files whose names match this regex'), Option(short='p', long='fexclude', type=str, kwargs={'file_name_exclude_regex' : ""}, description='Do not scan files whose names match this regex'), Option(short='s', long='status', type=int, kwargs={'status_server_port' : 0}, description='Enable the status server on the specified port'), Option(long=None, short=None, type=binwalk.core.common.BlockFile, kwargs={'files' : []}), # Hidden, API-only arguments Option(long="string", hidden=True, kwargs={'subclass' : binwalk.core.common.StringFile}), ] KWARGS = [ Kwarg(name='length', default=0), Kwarg(name='offset', default=0), Kwarg(name='base', default=0), Kwarg(name='block', default=0), Kwarg(name='status_server_port', default=0), Kwarg(name='swap_size', default=0), Kwarg(name='log_file', default=None), Kwarg(name='csv', default=False), Kwarg(name='format_to_terminal', default=False), Kwarg(name='quiet', default=False), Kwarg(name='verbose', default=False), Kwarg(name='files', default=[]), Kwarg(name='show_help', default=False), Kwarg(name='keep_going', default=False), Kwarg(name='subclass', default=io.FileIO), Kwarg(name='file_name_include_regex', default=None), Kwarg(name='file_name_exclude_regex', default=None), ] PRIMARY = False def load(self): self.threads_active = False self.target_files = [] # A special case for when we're loaded into IDA if self.subclass == io.FileIO and binwalk.core.idb.LOADED_IN_IDA: self.subclass = binwalk.core.idb.IDBFileIO # Order is important with these two methods self._open_target_files() self._set_verbosity() # Build file name filter regex rules if self.file_name_include_regex: self.file_name_include_regex = re.compile(self.file_name_include_regex) if self.file_name_exclude_regex: self.file_name_exclude_regex = re.compile(self.file_name_exclude_regex) self.settings = binwalk.core.settings.Settings() self.display = binwalk.core.display.Display(log=self.log_file, csv=self.csv, quiet=self.quiet, verbose=self.verbose, fit_to_screen=self.format_to_terminal) if self.show_help: show_help() if not binwalk.core.idb.LOADED_IN_IDA: sys.exit(0) if self.status_server_port > 0: self.parent.status_server(self.status_server_port) def reset(self): pass def _set_verbosity(self): ''' Sets the appropriate verbosity. Must be called after self._test_target_files so that self.target_files is properly set. ''' # If more than one target file was specified, enable verbose mode; else, there is # nothing in some outputs to indicate which scan corresponds to which file. if len(self.target_files) > 1 and not self.verbose: self.verbose = True def file_name_filter(self, fp): ''' Checks to see if a file should be scanned based on file name include/exclude filters. Most useful for matryoshka scans where only certian files are desired. @fp - An instances of binwalk.common.BlockFile Returns True if the file should be scanned, False if not. ''' if self.file_name_include_regex and not self.file_name_include_regex.search(fp.name): return False if self.file_name_exclude_regex and self.file_name_exclude_regex.search(fp.name): return False return True def open_file(self, fname, length=None, offset=None, swap=None, block=None, peek=None): ''' Opens the specified file with all pertinent configuration settings. ''' if length is None: length = self.length if offset is None: offset = self.offset if swap is None: swap = self.swap_size return binwalk.core.common.BlockFile(fname, subclass=self.subclass, length=length, offset=offset, swap=swap, block=block, peek=peek) def _open_target_files(self): ''' Checks if the target files can be opened. Any files that cannot be opened are removed from the self.target_files list. ''' # Validate the target files listed in target_files for tfile in self.files: # Ignore directories. if not self.subclass == io.FileIO or not os.path.isdir(tfile): # Make sure we can open the target files try: fp = self.open_file(tfile) fp.close() self.target_files.append(tfile) except KeyboardInterrupt as e: raise e except Exception as e: self.error(description="Cannot open file : %s" % str(e)) binwalk-2.1.1/src/binwalk/modules/hashmatch.py000066400000000000000000000267121263655036500213650ustar00rootroot00000000000000# Performs fuzzy hashing against files/directories. # Unlike other scans, this doesn't produce any file offsets, so its results are not applicable to # some other scans, such as the entropy scan. # Additionally, this module currently doesn't support certian general options (length, offset, swap, etc), # as the libfuzzy C library is responsible for opening and scanning the specified files. import os import re import ctypes import fnmatch import binwalk.core.C import binwalk.core.common from binwalk.core.compat import * from binwalk.core.module import Module, Option, Kwarg class HashResult(object): ''' Class for storing libfuzzy hash results. For internal use only. ''' def __init__(self, name, hash=None, strings=None): self.name = name self.hash = hash self.strings = strings class HashMatch(Module): ''' Class for fuzzy hash matching of files and directories. ''' DEFAULT_CUTOFF = 0 CONSERVATIVE_CUTOFF = 90 TITLE = "Fuzzy Hash" CLI = [ Option(short='F', long='fuzzy', kwargs={'enabled' : True}, description='Perform fuzzy hash matching on files/directories'), Option(short='u', long='cutoff', priority=100, type=int, kwargs={'cutoff' : DEFAULT_CUTOFF}, description='Set the cutoff percentage'), Option(short='S', long='strings', kwargs={'strings' : True}, description='Diff strings inside files instead of the entire file'), Option(short='s', long='same', kwargs={'same' : True, 'cutoff' : CONSERVATIVE_CUTOFF}, description='Only show files that are the same'), Option(short='p', long='diff', kwargs={'same' : False, 'cutoff' : CONSERVATIVE_CUTOFF}, description='Only show files that are different'), Option(short='n', long='name', kwargs={'filter_by_name' : True}, description='Only compare files whose base names are the same'), Option(short='L', long='symlinks', kwargs={'symlinks' : True}, description="Don't ignore symlinks"), ] KWARGS = [ Kwarg(name='cutoff', default=DEFAULT_CUTOFF), Kwarg(name='strings', default=False), Kwarg(name='same', default=True), Kwarg(name='symlinks', default=False), Kwarg(name='max_results', default=None), Kwarg(name='abspath', default=False), Kwarg(name='filter_by_name', default=False), Kwarg(name='symlinks', default=False), Kwarg(name='enabled', default=False), ] LIBRARY_NAME = "fuzzy" LIBRARY_FUNCTIONS = [ binwalk.core.C.Function(name="fuzzy_hash_buf", type=int), binwalk.core.C.Function(name="fuzzy_hash_filename", type=int), binwalk.core.C.Function(name="fuzzy_compare", type=int), ] # Max result is 148 (http://ssdeep.sourceforge.net/api/html/fuzzy_8h.html) FUZZY_MAX_RESULT = 150 # Files smaller than this won't produce meaningful fuzzy results (from ssdeep.h) FUZZY_MIN_FILE_SIZE = 4096 HEADER_FORMAT = "\n%s" + " " * 11 + "%s\n" RESULT_FORMAT = "%d%%" + " " * 17 + "%s\n" HEADER = ["SIMILARITY", "FILE NAME"] RESULT = ["percentage", "description"] def init(self): self.total = 0 self.last_file1 = HashResult(None) self.last_file2 = HashResult(None) self.lib = binwalk.core.C.Library(self.LIBRARY_NAME, self.LIBRARY_FUNCTIONS) def _get_strings(self, fname): return ''.join(list(binwalk.core.common.strings(fname, minimum=10))) def _show_result(self, match, fname): if self.abspath: fname = os.path.abspath(fname) # Add description string padding for alignment if match < 100: fname = ' ' + fname if match < 10: fname = ' ' + fname self.result(percentage=match, description=fname, plot=False) def _compare_files(self, file1, file2): ''' Fuzzy diff two files. @file1 - The first file to diff. @file2 - The second file to diff. Returns the match percentage. Returns None on error. ''' status = 0 file1_dup = False file2_dup = False if not self.filter_by_name or os.path.basename(file1) == os.path.basename(file2): if os.path.exists(file1) and os.path.exists(file2): hash1 = ctypes.create_string_buffer(self.FUZZY_MAX_RESULT) hash2 = ctypes.create_string_buffer(self.FUZZY_MAX_RESULT) # Check if the last file1 or file2 matches this file1 or file2; no need to re-hash if they match. if file1 == self.last_file1.name and self.last_file1.hash: file1_dup = True else: self.last_file1.name = file1 if file2 == self.last_file2.name and self.last_file2.hash: file2_dup = True else: self.last_file2.name = file2 try: if self.strings: if file1_dup: file1_strings = self.last_file1.strings else: self.last_file1.strings = file1_strings = self._get_strings(file1) if file2_dup: file2_strings = self.last_file2.strings else: self.last_file2.strings = file2_strings = self._get_strings(file2) if file1_strings == file2_strings: return 100 else: if file1_dup: hash1 = self.last_file1.hash else: status |= self.lib.fuzzy_hash_buf(file1_strings, len(file1_strings), hash1) if file2_dup: hash2 = self.last_file2.hash else: status |= self.lib.fuzzy_hash_buf(file2_strings, len(file2_strings), hash2) else: if file1_dup: hash1 = self.last_file1.hash else: status |= self.lib.fuzzy_hash_filename(file1, hash1) if file2_dup: hash2 = self.last_file2.hash else: status |= self.lib.fuzzy_hash_filename(file2, hash2) if status == 0: if not file1_dup: self.last_file1.hash = hash1 if not file2_dup: self.last_file2.hash = hash2 if hash1.raw == hash2.raw: return 100 else: return self.lib.fuzzy_compare(hash1, hash2) except Exception as e: binwalk.core.common.warning("Exception while doing fuzzy hash: %s" % str(e)) return None def is_match(self, match): ''' Returns True if this is a good match. Returns False if his is not a good match. ''' return (match is not None and ((match >= self.cutoff and self.same) or (match < self.cutoff and not self.same))) def _get_file_list(self, directory): ''' Generates a directory tree. @directory - The root directory to start from. Returns a set of file paths, excluding the root directory. ''' file_list = [] # Normalize directory path so that we can exclude it from each individual file path directory = os.path.abspath(directory) + os.path.sep for (root, dirs, files) in os.walk(directory): # Don't include the root directory in the file paths root = ''.join(root.split(directory, 1)[1:]) # Get a list of files, with or without symlinks as specified during __init__ files = [os.path.join(root, f) for f in files if self.symlinks or not os.path.islink(f)] file_list += files return set(file_list) def hash_files(self, needle, haystack): ''' Compare one file against a list of other files. Returns a list of tuple results. ''' self.total = 0 for f in haystack: m = self._compare_files(needle, f) if m is not None and self.is_match(m): self._show_result(m, f) self.total += 1 if self.max_results and self.total >= self.max_results: break def hash_file(self, needle, haystack): ''' Search for one file inside one or more directories. Returns a list of tuple results. ''' matching_files = [] self.total = 0 done = False for directory in haystack: for f in self._get_file_list(directory): f = os.path.join(directory, f) m = self._compare_files(needle, f) if m is not None and self.is_match(m): self._show_result(m, f) matching_files.append((m, f)) self.total += 1 if self.max_results and self.total >= self.max_results: done = True break if done: break return matching_files def hash_directories(self, needle, haystack): ''' Compare the contents of one directory with the contents of other directories. Returns a list of tuple results. ''' done = False self.total = 0 source_files = self._get_file_list(needle) for directory in haystack: dir_files = self._get_file_list(directory) for source_file in source_files: for dir_file in dir_files: file1 = os.path.join(needle, source_file) file2 = os.path.join(directory, dir_file) m = self._compare_files(file1, file2) if m is not None and self.is_match(m): self._show_result(m, "%s => %s" % (file1, file2)) self.total += 1 if self.max_results and self.total >= self.max_results: done = True break if done: break def run(self): ''' Main module method. ''' # Access the raw self.config.files list directly here, since we accept both # files and directories and self.next_file only works for files. needle = self.config.files[0] haystack = self.config.files[1:] self.header() if os.path.isfile(needle): if os.path.isfile(haystack[0]): self.hash_files(needle, haystack) else: self.hash_file(needle, haystack) else: self.hash_directories(needle, haystack) self.footer() return True binwalk-2.1.1/src/binwalk/modules/heuristics.py000066400000000000000000000133001263655036500215740ustar00rootroot00000000000000# Routines to perform Chi Squared tests. # Used for fingerprinting unknown areas of high entropy (e.g., is this block of high entropy data compressed or encrypted?). # Inspired by people who actually know what they're doing: http://www.fourmilab.ch/random/ import math from binwalk.core.compat import * from binwalk.core.module import Module, Kwarg, Option, Dependency class ChiSquare(object): ''' Performs a Chi Squared test against the provided data. ''' IDEAL = 256.0 def __init__(self): ''' Class constructor. Returns None. ''' self.bytes = {} self.freedom = self.IDEAL - 1 # Initialize the self.bytes dictionary with keys for all possible byte values (0 - 255) for i in range(0, int(self.IDEAL)): self.bytes[chr(i)] = 0 self.reset() def reset(self): self.xc2 = 0.0 self.byte_count = 0 for key in self.bytes.keys(): self.bytes[key] = 0 def update(self, data): ''' Updates the current byte counts with new data. @data - String of bytes to update. Returns None. ''' # Count the number of occurances of each byte value for i in data: self.bytes[i] += 1 self.byte_count += len(data) def chisq(self): ''' Calculate the Chi Square critical value. Returns the critical value. ''' expected = self.byte_count / self.IDEAL if expected: for byte in self.bytes.values(): self.xc2 += ((byte - expected) ** 2 ) / expected return self.xc2 class EntropyBlock(object): def __init__(self, **kwargs): self.start = None self.end = None self.length = None for (k,v) in iterator(kwargs): setattr(self, k, v) class HeuristicCompressionAnalyzer(Module): ''' Performs analysis and attempts to interpret the results. ''' BLOCK_SIZE = 32 CHI_CUTOFF = 512 ENTROPY_TRIGGER = .90 MIN_BLOCK_SIZE = 4096 BLOCK_OFFSET = 1024 ENTROPY_BLOCK_SIZE = 1024 TITLE = "Heuristic Compression" DEPENDS = [ Dependency(name='Entropy', attribute='entropy', kwargs={'enabled' : True, 'do_plot' : False, 'display_results' : False, 'block_size' : ENTROPY_BLOCK_SIZE}), ] CLI = [ Option(short='H', long='heuristic', kwargs={'enabled' : True}, description='Heuristically classify high entropy data'), Option(short='a', long='trigger', kwargs={'trigger_level' : 0}, type=float, description='Set the entropy trigger level (0.0 - 1.0, default: %.2f)' % ENTROPY_TRIGGER), ] KWARGS = [ Kwarg(name='enabled', default=False), Kwarg(name='trigger_level', default=ENTROPY_TRIGGER), ] def init(self): self.blocks = {} self.HEADER[-1] = "HEURISTIC ENTROPY ANALYSIS" # Trigger level sanity check if self.trigger_level > 1.0: self.trigger_level = 1.0 elif self.trigger_level < 0.0: self.trigger_level = 0.0 if self.config.block: self.block_size = self.config.block else: self.block_size = self.BLOCK_SIZE for result in self.entropy.results: if not has_key(self.blocks, result.file.name): self.blocks[result.file.name] = [] if result.entropy >= self.trigger_level and (not self.blocks[result.file.name] or self.blocks[result.file.name][-1].end is not None): self.blocks[result.file.name].append(EntropyBlock(start=result.offset + self.BLOCK_OFFSET)) elif result.entropy < self.trigger_level and self.blocks[result.file.name] and self.blocks[result.file.name][-1].end is None: self.blocks[result.file.name][-1].end = result.offset - self.BLOCK_OFFSET def run(self): for fp in iter(self.next_file, None): if has_key(self.blocks, fp.name): self.header() for block in self.blocks[fp.name]: if block.end is None: block.length = fp.offset + fp.length - block.start else: block.length = block.end - block.start if block.length >= self.MIN_BLOCK_SIZE: self.analyze(fp, block) self.footer() def analyze(self, fp, block): ''' Perform analysis and interpretation. ''' i = 0 num_error = 0 analyzer_results = [] chi = ChiSquare() fp.seek(block.start) while i < block.length: j = 0 (d, dlen) = fp.read_block() if not d: break while j < dlen: chi.reset() data = d[j:j+self.block_size] if len(data) < self.block_size: break chi.update(data) if chi.chisq() >= self.CHI_CUTOFF: num_error += 1 j += self.block_size if (j + i) > block.length: break i += dlen if num_error > 0: verdict = 'Moderate entropy data, best guess: compressed' else: verdict = 'High entropy data, best guess: encrypted' desc = '%s, size: %d, %d low entropy blocks' % (verdict, block.length, num_error) self.result(offset=block.start, description=desc, file=fp) binwalk-2.1.1/src/binwalk/modules/hexdiff.py000066400000000000000000000161551263655036500210420ustar00rootroot00000000000000import os import sys import string import binwalk.core.common as common from binwalk.core.compat import * from binwalk.core.module import Module, Option, Kwarg class HexDiff(Module): COLORS = { 'red' : '31', 'green' : '32', 'blue' : '34', } SEPERATORS = ['\\', '/'] DEFAULT_BLOCK_SIZE = 16 SKIPPED_LINE = "*" CUSTOM_DISPLAY_FORMAT = "0x%.8X %s" TITLE = "Binary Diffing" CLI = [ Option(short='W', long='hexdump', kwargs={'enabled' : True}, description='Perform a hexdump / diff of a file or files'), Option(short='G', long='green', kwargs={'show_green' : True}, description='Only show lines containing bytes that are the same among all files'), Option(short='i', long='red', kwargs={'show_red' : True}, description='Only show lines containing bytes that are different among all files'), Option(short='U', long='blue', kwargs={'show_blue' : True}, description='Only show lines containing bytes that are different among some files'), Option(short='w', long='terse', kwargs={'terse' : True}, description='Diff all files, but only display a hex dump of the first file'), ] KWARGS = [ Kwarg(name='show_red', default=False), Kwarg(name='show_blue', default=False), Kwarg(name='show_green', default=False), Kwarg(name='terse', default=False), Kwarg(name='enabled', default=False), ] RESULT_FORMAT = "%s\n" RESULT = ['display'] def _no_colorize(self, c, color="red", bold=True): return c def _colorize(self, c, color="red", bold=True): attr = [] attr.append(self.COLORS[color]) if bold: attr.append('1') return "\x1b[%sm%s\x1b[0m" % (';'.join(attr), c) def _color_filter(self, data): red = '\x1b[' + self.COLORS['red'] + ';' green = '\x1b[' + self.COLORS['green'] + ';' blue = '\x1b[' + self.COLORS['blue'] + ';' if self.show_blue and blue in data: return True elif self.show_green and green in data: return True elif self.show_red and red in data: return True return False def hexascii(self, target_data, byte, offset): color = "green" for (fp_i, data_i) in iterator(target_data): diff_count = 0 for (fp_j, data_j) in iterator(target_data): if fp_i == fp_j: continue try: if data_i[offset] != data_j[offset]: diff_count += 1 except IndexError as e: diff_count += 1 if diff_count == len(target_data)-1: color = "red" elif diff_count > 0: color = "blue" break hexbyte = self.colorize("%.2X" % ord(byte), color) if byte not in string.printable or byte in string.whitespace: byte = "." asciibyte = self.colorize(byte, color) return (hexbyte, asciibyte) def diff_files(self, target_files): last_line = None loop_count = 0 sep_count = 0 # Figure out the maximum diff size (largest file size) self.status.total = 0 for i in range(0, len(target_files)): if target_files[i].size > self.status.total: self.status.total = target_files[i].size self.status.fp = target_files[i] while True: line = "" done_files = 0 block_data = {} seperator = self.SEPERATORS[sep_count % 2] for fp in target_files: block_data[fp] = fp.read(self.block) if not block_data[fp]: done_files += 1 # No more data from any of the target files? Done. if done_files == len(target_files): break for fp in target_files: hexline = "" asciiline = "" for i in range(0, self.block): if i >= len(block_data[fp]): hexbyte = "XX" asciibyte = "." else: (hexbyte, asciibyte) = self.hexascii(block_data, block_data[fp][i], i) hexline += "%s " % hexbyte asciiline += "%s" % asciibyte line += "%s |%s|" % (hexline, asciiline) if self.terse: break if fp != target_files[-1]: line += " %s " % seperator offset = fp.offset + (self.block * loop_count) if not self._color_filter(line): display = line = self.SKIPPED_LINE else: display = self.CUSTOM_DISPLAY_FORMAT % (offset, line) sep_count += 1 if line != self.SKIPPED_LINE or last_line != line: self.result(offset=offset, description=line, display=display) last_line = line loop_count += 1 self.status.completed += self.block def init(self): # To mimic expected behavior, if all options are False, we show everything if not any([self.show_red, self.show_green, self.show_blue]): self.show_red = self.show_green = self.show_blue = True # Always disable terminal formatting, as it won't work properly with colorized output self.config.display.fit_to_screen = False # Set the block size (aka, hexdump line size) self.block = self.config.block if not self.block: self.block = self.DEFAULT_BLOCK_SIZE # Build a list of files to hexdiff self.hex_target_files = [] while True: f = self.next_file(close_previous=False) if not f: break else: self.hex_target_files.append(f) # Build the header format string header_width = (self.block * 4) + 2 if self.terse: file_count = 1 else: file_count = len(self.hex_target_files) self.HEADER_FORMAT = "OFFSET " + (("%%-%ds " % header_width) * file_count) + "\n" # Build the header argument list self.HEADER = [fp.name for fp in self.hex_target_files] if self.terse and len(self.HEADER) > 1: self.HEADER = self.HEADER[0] # Set up the tty for colorization, if it is supported if hasattr(sys.stderr, 'isatty') and sys.stderr.isatty() and not common.MSWindows(): import curses curses.setupterm() self.colorize = self._colorize else: self.colorize = self._no_colorize def run(self): if self.hex_target_files: self.header() self.diff_files(self.hex_target_files) self.footer() binwalk-2.1.1/src/binwalk/modules/signature.py000066400000000000000000000163301263655036500214210ustar00rootroot00000000000000# Basic signature scan module. This is the default (and primary) feature of binwalk. import binwalk.core.magic from binwalk.core.module import Module, Option, Kwarg class Signature(Module): TITLE = "Signature Scan" ORDER = 10 CLI = [ Option(short='B', long='signature', kwargs={'enabled' : True, 'explicit_signature_scan' : True}, description='Scan target file(s) for common file signatures'), Option(short='R', long='raw', kwargs={'enabled' : True, 'raw_bytes' : []}, type=list, dtype=str.__name__, description='Scan target file(s) for the specified sequence of bytes'), Option(short='A', long='opcodes', kwargs={'enabled' : True, 'search_for_opcodes' : True}, description='Scan target file(s) for common executable opcode signatures'), Option(short='m', long='magic', kwargs={'enabled' : True, 'magic_files' : []}, type=list, dtype='file', description='Specify a custom magic file to use'), Option(short='b', long='dumb', kwargs={'dumb_scan' : True}, description='Disable smart signature keywords'), Option(short='I', long='invalid', kwargs={'show_invalid' : True}, description='Show results marked as invalid'), Option(short='x', long='exclude', kwargs={'exclude_filters' : []}, type=list, dtype=str.__name__, description='Exclude results that match '), Option(short='y', long='include', kwargs={'include_filters' : []}, type=list, dtype=str.__name__, description='Only show results that match '), ] KWARGS = [ Kwarg(name='enabled', default=False), Kwarg(name='show_invalid', default=False), Kwarg(name='include_filters', default=[]), Kwarg(name='exclude_filters', default=[]), Kwarg(name='raw_bytes', default=[]), Kwarg(name='search_for_opcodes', default=False), Kwarg(name='explicit_signature_scan', default=False), Kwarg(name='dumb_scan', default=False), Kwarg(name='magic_files', default=[]), ] VERBOSE_FORMAT = "%s %d" def init(self): self.one_of_many = None # Append the user's magic file first so that those signatures take precedence if self.search_for_opcodes: self.magic_files = [ self.config.settings.user.binarch, self.config.settings.system.binarch, ] # Use the system default magic file if no other was specified, or if -B was explicitly specified if (not self.magic_files and not self.raw_bytes) or self.explicit_signature_scan: self.magic_files += self.config.settings.user.magic + self.config.settings.system.magic # Initialize libmagic self.magic = binwalk.core.magic.Magic(include=self.include_filters, exclude=self.exclude_filters, invalid=self.show_invalid) # Create a signature from the raw bytes, if any if self.raw_bytes: raw_signatures = [] for raw_bytes in self.raw_bytes: raw_signatures.append("0 string %s %s" % (raw_bytes, raw_bytes)) binwalk.core.common.debug("Parsing raw signatures: %s" % str(raw_signatures)) self.magic.parse(raw_signatures) # Parse the magic file(s) if self.magic_files: binwalk.core.common.debug("Loading magic files: %s" % str(self.magic_files)) for f in self.magic_files: self.magic.load(f) self.VERBOSE = ["Signatures:", len(self.magic.signatures)] def validate(self, r): ''' Called automatically by self.result. ''' if self.show_invalid: r.valid = True elif r.valid: if not r.description: r.valid = False if r.size and (r.size + r.offset) > r.file.size: r.valid = False if r.jump and (r.jump + r.offset) > r.file.size: r.valid = False if r.valid: # Don't keep displaying signatures that repeat a bunch of times (e.g., JFFS2 nodes) if r.id == self.one_of_many: r.display = False elif r.many: self.one_of_many = r.id else: self.one_of_many = None def scan_file(self, fp): current_file_offset = 0 while True: (data, dlen) = fp.read_block() if not data: break current_block_offset = 0 block_start = fp.tell() - dlen self.status.completed = block_start - fp.offset # Scan this data block for magic signatures for r in self.magic.scan(data, dlen): # current_block_offset is set when a jump-to-offset keyword is encountered while # processing signatures. This points to an offset inside the current data block # that scanning should jump to, so ignore any subsequent candidate signatures that # occur before this offset inside the current data block. if r.offset < current_block_offset: continue # Keep a record of the relative offset of this signature inside the current data block # (used later for setting current_block_offset). relative_offset = r.offset + r.adjust # Set the absolute offset inside the target file r.offset = block_start + relative_offset # Provide an instance of the current file object r.file = fp # Register the result for futher processing/display # self.result automatically calls self.validate for result validation self.result(r=r) # Is this a valid result and did it specify a jump-to-offset keyword, and are we doing a "smart" scan? if r.valid and r.jump > 0 and not self.dumb_scan: absolute_jump_offset = r.offset + r.jump current_block_offset = relative_offset + r.jump #print ("Jumping to: 0x%X (0x%X)..." % (absolute_jump_offset, current_block_offset)) # If the jump-to-offset is beyond the confines of the current block, seek the file to # that offset and quit processing this block of data. if absolute_jump_offset >= fp.tell(): fp.seek(r.offset + r.jump) break def run(self): for fp in iter(self.next_file, None): self.header() self.scan_file(fp) self.footer() binwalk-2.1.1/src/binwalk/plugins/000077500000000000000000000000001263655036500170545ustar00rootroot00000000000000binwalk-2.1.1/src/binwalk/plugins/arcadyan.py000066400000000000000000000052631263655036500212160ustar00rootroot00000000000000import os import binwalk.core.common import binwalk.core.plugin class ArcadyanDeobfuscator(binwalk.core.plugin.Plugin): ''' Deobfuscator for known Arcadyan firmware obfuscation(s). ''' MODULES = ['Signature'] OBFUSCATION_MAGIC_SIZE = 4 MAX_IMAGE_SIZE = 0x1B0000 BLOCK_SIZE = 32 BLOCK1_OFFSET = 4 BLOCK2_OFFSET = 0x68 MIN_FILE_SIZE = (OBFUSCATION_MAGIC_SIZE + BLOCK2_OFFSET + BLOCK_SIZE) BLOCK1_START = BLOCK1_OFFSET BLOCK1_END = BLOCK1_START + BLOCK_SIZE BLOCK2_START = BLOCK2_OFFSET BLOCK2_END = BLOCK2_OFFSET + BLOCK_SIZE P1_START = 0 P1_END = BLOCK1_OFFSET P2_START = BLOCK1_END P2_END = BLOCK2_START P3_START = BLOCK2_END def init(self): if self.module.extractor.enabled: self.module.extractor.add_rule(regex="^obfuscated arcadyan firmware", extension="obfuscated", cmd=self.extractor) def extractor(self, fname): deobfuscated = None fname = os.path.abspath(fname) infile = binwalk.core.common.BlockFile(fname, "rb") obfuscated = infile.read() infile.close() if len(obfuscated) >= self.MIN_FILE_SIZE: # Swap blocks 1 and 2 p1 = obfuscated[self.P1_START:self.P1_END] b1 = obfuscated[self.BLOCK1_START:self.BLOCK1_END] p2 = obfuscated[self.P2_START:self.P2_END] b2 = obfuscated[self.BLOCK2_START:self.BLOCK2_END] p3 = obfuscated[self.P3_START:] deobfuscated = p1 + b2 + p2 + b1 + p3 # Nibble-swap each byte in block 1 nswap = '' for i in range(self.BLOCK1_START, self.BLOCK1_END): nswap += chr(((ord(deobfuscated[i]) & 0x0F) << 4) + ((ord(deobfuscated[i]) & 0xF0) >> 4)); deobfuscated = deobfuscated[self.P1_START:self.P1_END] + nswap + deobfuscated[self.BLOCK1_END:] # Byte-swap each byte pair in block 1 bswap = '' i = self.BLOCK1_START while i < self.BLOCK1_END: bswap += deobfuscated[i+1] + deobfuscated[i] i += 2 deobfuscated = deobfuscated[self.P1_START:self.P1_END] + bswap + deobfuscated[self.BLOCK1_END:] if deobfuscated: out = binwalk.core.common.BlockFile((os.path.splitext(fname)[0] + '.deobfuscated'), "wb") out.write(deobfuscated) out.close() return True else: return False binwalk-2.1.1/src/binwalk/plugins/compressd.py000066400000000000000000000026151263655036500214310ustar00rootroot00000000000000#import binwalk.core.C import binwalk.core.plugin #from binwalk.core.common import * class CompressdPlugin(binwalk.core.plugin.Plugin): # ''' # Searches for and validates compress'd data. # ''' MODULES = ['Signature'] #READ_SIZE = 64 #COMPRESS42 = "compress42" #COMPRESS42_FUNCTIONS = [ # binwalk.core.C.Function(name="is_compressed", type=bool), #] #comp = None #def init(self): #self.comp = binwalk.core.C.Library(self.COMPRESS42, self.COMPRESS42_FUNCTIONS) # This plugin is currently disabled due to the need to move away from supporting C # libraries and into a pure Python project, for cross-platform support and ease of # installation / package maintenance. A Python implementation will likely need to # be custom developed in the future, but for now, since this compression format is # not very common, especially in firmware, simply disable it. #self.comp = None #def scan(self, result): # if self.comp and result.file and result.description.lower().startswith("compress'd data"): # fd = self.module.config.open_file(result.file.name, offset=result.offset, length=self.READ_SIZE) # compressed_data = fd.read(self.READ_SIZE) # fd.close() # if not self.comp.is_compressed(compressed_data, len(compressed_data)): # result.valid = False binwalk-2.1.1/src/binwalk/plugins/cpio.py000066400000000000000000000075121263655036500203650ustar00rootroot00000000000000import os import subprocess import binwalk.core.plugin class CPIOPlugin(binwalk.core.plugin.Plugin): ''' Ensures that ASCII CPIO archive entries only get extracted once. Also provides an internal CPIO extraction wrapper around the Unix cpio utility since no output directory can be provided to it directly. ''' CPIO_OUT_DIR = "cpio-root" MODULES = ['Signature'] def init(self): self.consecutive_hits = 0 if self.module.extractor.enabled: self.module.extractor.add_rule(regex="^ascii cpio archive", extension="cpio", cmd=self.extractor, recurse=False) # Most CPIO archives are file systems, so don't recurse into the extracted contents def extractor(self, fname): result = None fname = os.path.abspath(fname) out_dir = os.path.join(os.path.dirname(fname), self.CPIO_OUT_DIR) try: fpin = open(fname, "rb") fperr = open(os.devnull, "rb") os.mkdir(out_dir) except OSError: return try: curdir = os.getcwd() os.chdir(out_dir) except OSError: return try: result = subprocess.call(['cpio', '-d', '-i', '--no-absolute-filenames'], stdin=fpin, stderr=fperr, stdout=fperr) except OSError: result = -1 os.chdir(curdir) fpin.close() fperr.close() if result in [0, 2]: return True else: return False def pre_scan(self): # Be sure to re-set this at the beginning of every scan self.found_archive = False self.found_archive_in_file = None def scan(self, result): if result.valid: # ASCII CPIO archives consist of multiple entries, ending with an entry named 'TRAILER!!!'. # Displaying each entry is useful, as it shows what files are contained in the archive, # but we only want to extract the archive when the first entry is found. if result.description.startswith('ASCII cpio archive'): self.consecutive_hits += 1 if not self.found_archive or self.found_archive_in_file != result.file.name: # This is the first entry. Set found_archive and allow the scan to continue normally. self.found_archive_in_file = result.file.name self.found_archive = True result.extract = True elif 'TRAILER!!!' in result.description: # This is the last entry, un-set found_archive. self.found_archive = False result.extract = False self.consecutive_hits = 0 else: # The first entry has already been found and this is not the last entry, or the last entry # has not yet been found. Don't extract. result.extract = False elif self.consecutive_hits < 4: # If this was a valid non-CPIO archive result, reset these values; else, a previous # false positive CPIO result could leave these set, causing a subsequent valid CPIO # result to not be extracted. self.found_archive = False self.found_archive_in_file = None self.consecutive_hits = 0 elif self.consecutive_hits >= 4: # Ignore other stuff until the end of CPIO is found # TODO: It would be better to jump to the end of this CPIO entry rather than make this assumption... result.valid = False binwalk-2.1.1/src/binwalk/plugins/gzipextract.py000066400000000000000000000026741263655036500220030ustar00rootroot00000000000000import os import gzip import binwalk.core.plugin class GzipExtractPlugin(binwalk.core.plugin.Plugin): ''' Gzip extractor plugin. ''' MODULES = ['Signature'] BLOCK_SIZE = 10 * 1024 def init(self): # If the extractor is enabled for the module we're currently loaded # into, and if a rule that matches gzip signature results already exists # (e.g., the default rules were loaded or a gzip rule was specified manually), # then register self.extractor as a gzip extraction rule. if self.module.extractor.enabled and self.module.extractor.match("gzip compressed data"): self.module.extractor.add_rule(txtrule=None, regex="^gzip compressed data", extension="gz", cmd=self.extractor) def extractor(self, fname): fname = os.path.abspath(fname) outfile = os.path.splitext(fname)[0] try: fpout = open(outfile, "wb") gz = gzip.GzipFile(fname, "rb") while True: data = gz.read(self.BLOCK_SIZE) if data: fpout.write(data) else: break gz.close() fpout.close() except KeyboardInterrupt as e: raise e except Exception as e: return False return True binwalk-2.1.1/src/binwalk/plugins/gzipvalid.py000066400000000000000000000034351263655036500214240ustar00rootroot00000000000000import zlib import binwalk.core.compat import binwalk.core.plugin from binwalk.core.common import BlockFile class GzipValidPlugin(binwalk.core.plugin.Plugin): ''' Validates gzip compressed data. Almost identical to zlibvalid.py. ''' MODULES = ['Signature'] MAX_DATA_SIZE = 33 * 1024 def scan(self, result): # If this result is a gzip signature match, try to decompress the data if result.file and result.description.lower().startswith('gzip'): # Seek to and read the suspected gzip data fd = self.module.config.open_file(result.file.name, offset=result.offset, length=self.MAX_DATA_SIZE) data = fd.read(self.MAX_DATA_SIZE) fd.close() # Grab the flags and initialize the default offset of the start of # compressed data. flags = int(ord(data[3])) offset = 10 # If there is a comment or the original file name, find the end of that # string and start decompression from there. if (flags & 0x0C) or (flags & 0x10): while data[offset] != "\x00": offset += 1 offset += 1 # Append basic zlib header to the beginning of the compressed data data = "\x78\x9C" + data[offset:] # Check if this is valid deflate data (no zlib header) try: zlib.decompress(binwalk.core.compat.str2bytes(data)) except zlib.error as e: error = str(e) # Truncated input data results in error -5. # gzip uses different checksums than zlib, which results in error -3. if not error.startswith("Error -5") and not error.startswith("Error -3"): result.valid = False binwalk-2.1.1/src/binwalk/plugins/jffs2valid.py000066400000000000000000000033501263655036500214610ustar00rootroot00000000000000import struct import binascii import binwalk.core.plugin class JFFS2ValidPlugin(binwalk.core.plugin.Plugin): ''' Helps validate JFFS2 signature results. The JFFS2 signature rules catch obvious cases, but inadvertently mark some valid JFFS2 nodes as invalid due to padding (0xFF's or 0x00's) in between nodes. ''' MODULES = ['Signature'] def _check_crc(self, node_header): # struct and binascii want a bytes object in Python3 node_header = binwalk.core.compat.str2bytes(node_header) # Get the header's reported CRC value if node_header[0:2] == b"\x19\x85": header_crc = struct.unpack(">I", node_header[8:12])[0] else: header_crc = struct.unpack("= result.file.size: # we hit the end of the file is_tar = False else: fd.seek(file_offset) else: is_tar = False result.jump = file_offset binwalk-2.1.1/src/binwalk/plugins/ubivalid.py000077500000000000000000000041311263655036500212270ustar00rootroot00000000000000import struct import binascii import binwalk.core.plugin import binwalk.core.compat class UBIValidPlugin(binwalk.core.plugin.Plugin): ''' Helps validate UBI erase count signature results. Checks header CRC and calculates jump value ''' MODULES = ['Signature'] current_file=None last_ec_hdr_offset = None peb_size = None def _check_crc(self, ec_header): # Get the header's reported CRC value header_crc = struct.unpack(">I", ec_header[60:64])[0] # Calculate the actual CRC calculated_header_crc = ~binascii.crc32(ec_header[0:60]) & 0xffffffff # Make sure they match return header_crc == calculated_header_crc def _process_result(self, result): if self.current_file == result.file.name: result.display=False else: # Reset everything in case new file is encountered self.peb_size=None self.last_ec_hdr_offset=None self.peb_size=None # Display result and trigger extraction result.display=True self.current_file = result.file.name if not self.peb_size and self.last_ec_hdr_offset: # Calculate PEB size by subtracting last EC block offset self.peb_size = result.offset - self.last_ec_hdr_offset else: # First time plugin is called on file, save EC block offset self.last_ec_hdr_offset = result.offset if self.peb_size: # If PEB size has been determined jump PEB size result.jump = self.peb_size else: result.jump = 0 def scan(self, result): if result.file and result.description.lower().startswith('ubi erase count header'): # Seek to and read the suspected UBI erase count header fd = self.module.config.open_file(result.file.name, offset=result.offset) ec_header = binwalk.core.compat.str2bytes(fd.read(1024)) fd.close() result.valid = self._check_crc(ec_header[0:64]) if result.valid: self._process_result(result) binwalk-2.1.1/src/binwalk/plugins/unjffs2.py000066400000000000000000000004131263655036500210010ustar00rootroot00000000000000# This file has been depreciated and is no longer in use. # This is merely a placeholder to ensure future installations # don't leave the old, depreciated file in tact. import binwalk.core.plugin class Unjffs2DepreciatedPlugin(binwalk.core.plugin.Plugin): pass binwalk-2.1.1/src/binwalk/plugins/ziphelper.py000066400000000000000000000014361263655036500214340ustar00rootroot00000000000000import binwalk.core.plugin class ZipHelperPlugin(binwalk.core.plugin.Plugin): ''' A helper plugin for Zip files to ensure that the Zip archive extraction rule is only executed once when the first Zip archive entry is encountered. This resets once and end of zip archive is found. ''' MODULES = ['Signature'] extraction_active = False def scan(self, result): if result.valid and result.display: if result.description.lower().startswith('zip archive data'): if self.extraction_active: result.extract = False else: self.extraction_active = True elif result.description.lower().startswith('end of zip archive'): self.extraction_active = False binwalk-2.1.1/src/binwalk/plugins/zlibextract.py000066400000000000000000000022771263655036500217710ustar00rootroot00000000000000import os import zlib import binwalk.core.compat import binwalk.core.common import binwalk.core.plugin class ZLIBExtractPlugin(binwalk.core.plugin.Plugin): ''' Zlib extractor plugin. ''' MODULES = ['Signature'] def init(self): # If the extractor is enabled for the module we're currently loaded # into, then register self.extractor as a zlib extraction rule. if self.module.extractor.enabled: self.module.extractor.add_rule(txtrule=None, regex="^zlib compressed data", extension="zlib", cmd=self.extractor) def extractor(self, fname): outfile = os.path.splitext(fname)[0] try: fpin = binwalk.core.common.BlockFile(fname) fpout = binwalk.core.common.BlockFile(outfile, 'w') plaintext = zlib.decompress(binwalk.core.compat.str2bytes(fpin.read())) fpout.write(plaintext) fpin.close() fpout.close() except KeyboardInterrupt as e: raise e except Exception as e: return False return True binwalk-2.1.1/src/binwalk/plugins/zlibvalid.py000066400000000000000000000027651263655036500214200ustar00rootroot00000000000000import zlib import binwalk.core.compat import binwalk.core.plugin from binwalk.core.common import BlockFile class ZlibValidPlugin(binwalk.core.plugin.Plugin): ''' Validates zlib compressed data. ''' MODULES = ['Signature'] MAX_DATA_SIZE = 33 * 1024 def scan(self, result): # If this result is a zlib signature match, try to decompress the data if result.file and result.description.lower().startswith('zlib'): # If byte swapping is enabled, we need to start reading at a swap-size # aligned offset, then index in to the read data appropriately. if self.module.config.swap_size: adjust = result.offset % self.module.config.swap_size else: adjust = 0 offset = result.offset - adjust # Seek to and read the suspected zlib data fd = self.module.config.open_file(result.file.name) fd.seek(offset) data = fd.read(self.MAX_DATA_SIZE)[adjust:] fd.close() # Check if this is valid zlib data. It is valid if: # # 1. It decompresses without error # 2. Decompression fails only because of truncated input try: zlib.decompress(binwalk.core.compat.str2bytes(data)) except zlib.error as e: # Error -5, incomplete or truncated data input if not str(e).startswith("Error -5"): result.valid = False binwalk-2.1.1/src/scripts/000077500000000000000000000000001263655036500154335ustar00rootroot00000000000000binwalk-2.1.1/src/scripts/binida.py000077500000000000000000000016301263655036500172360ustar00rootroot00000000000000import idc import idaapi import binwalk class binwalk_t(idaapi.plugin_t): flags = 0 comment = "Scan the current IDB for file signatures" help = "" wanted_name = "Binwalk IDA Plugin" wanted_hotkey = "" def init(self): self.menu_context_1 = idaapi.add_menu_item("Search/", "binwalk opcodes", "", 0, self.opcode_scan, (None,)) self.menu_context_2 = idaapi.add_menu_item("Search/", "binwalk signatures", "", 0, self.signature_scan, (None,)) return idaapi.PLUGIN_KEEP def term(self): idaapi.del_menu_item(self.menu_context_1) idaapi.del_menu_item(self.menu_context_2) return None def run(self, arg): return None def signature_scan(self, arg): binwalk.scan(idc.GetIdbPath(), signature=True) def opcode_scan(self, arg): binwalk.scan(idc.GetIdbPath(), opcode=True) def PLUGIN_ENTRY(): return binwalk_t() binwalk-2.1.1/src/scripts/binwalk000077500000000000000000000044361263655036500170170ustar00rootroot00000000000000#!/usr/bin/env python import os import sys # If installed to a custom prefix directory, binwalk may not be in # the default module search path(s). Try to resolve the prefix module # path and make it the first entry in sys.path. # Ensure that 'src/binwalk' becomes '.' instead of an empty string _parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) for _module_path in [ # from repo: src/scripts/ -> src/ _parent_dir, # from build dir: build/scripts-3.4/ -> build/lib/ os.path.join(_parent_dir, "lib"), # installed in non-default path: bin/ -> lib/python3.4/site-packages/ os.path.join(_parent_dir, "lib", "python%d.%d" % (sys.version_info[0], sys.version_info[1]), "site-packages") ]: if os.path.exists(_module_path) and _module_path not in sys.path: sys.path = [_module_path] + sys.path import binwalk import binwalk.modules def main(): with binwalk.Modules() as modules: try: if len(sys.argv) == 1: sys.stderr.write(modules.help()) sys.exit(1) # If no explicit module was enabled in the command line arguments, # run again with the default signature scan explicitly enabled. elif not modules.execute(): # Make sure the Signature module is loaded before attempting # an implicit signature scan; else, the error message received # by the end user is not very helpful. if hasattr(binwalk.modules, "Signature"): modules.execute(*sys.argv[1:], signature=True) else: sys.stderr.write("Error: Signature scans not supported; ") sys.stderr.write("make sure you have python-lzma installed and try again.\n") sys.exit(2) except binwalk.ModuleException as e: sys.exit(3) if __name__ == '__main__': try: # Special options for profiling the code. For debug use only. if '--profile' in sys.argv: import cProfile sys.argv.pop(sys.argv.index('--profile')) cProfile.run('main()') else: main() except IOError: pass except KeyboardInterrupt: sys.stdout.write("\n") binwalk-2.1.1/src/scripts/examples/000077500000000000000000000000001263655036500172515ustar00rootroot00000000000000binwalk-2.1.1/src/scripts/examples/binwalk_simple.py000077500000000000000000000003201263655036500226210ustar00rootroot00000000000000#!/usr/bin/env python import binwalk # Since no options are specified, they are by default taken from sys.argv. # Effecitvely, this duplicates the functionality of the normal binwalk script. binwalk.scan() binwalk-2.1.1/src/scripts/examples/extract_data.py000077500000000000000000000014731263655036500222760ustar00rootroot00000000000000#!/usr/bin/env python import sys import binwalk # Extracts and logs for module in binwalk.scan(*sys.argv[1:], signature=True, quiet=True, extract=True): print ("%s Results:" % module.name) for result in module.results: if module.extractor.output.has_key(result.file.path): if module.extractor.output[result.file.path].extracted.has_key(result.offset): print ("Extracted '%s' at offset 0x%X from '%s' to '%s'" % (result.description.split(',')[0], result.offset, result.file.path, str(module.extractor.output[result.file.path].extracted[result.offset]))) binwalk-2.1.1/src/scripts/examples/signature_scan.py000077500000000000000000000007631263655036500226410ustar00rootroot00000000000000#!/usr/bin/env python import sys import binwalk try: # Perform a signature scan against the files specified on the command line and suppress the usual binwalk output. for module in binwalk.scan(*sys.argv[1:], signature=True, quiet=True): print ("%s Results:" % module.name) for result in module.results: print ("\t%s 0x%.8X %s [%s]" % (result.file.name, result.offset, result.description, str(result.valid))) except binwalk.ModuleException as e: pass