polyfile-weave 0.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of polyfile-weave might be problematic. Click here for more details.
- polyfile/__init__.py +15 -0
- polyfile/__main__.py +394 -0
- polyfile/arithmetic.py +27 -0
- polyfile/ast.py +114 -0
- polyfile/debugger.py +1039 -0
- polyfile/expressions.py +346 -0
- polyfile/fileutils.py +343 -0
- polyfile/html.py +135 -0
- polyfile/http/__init__.py +1 -0
- polyfile/http/defacto.py +37 -0
- polyfile/http/deprecated.py +51 -0
- polyfile/http/experimental.py +67 -0
- polyfile/http/http_11.py +548 -0
- polyfile/http/matcher.py +37 -0
- polyfile/http/structured_headers.py +48 -0
- polyfile/iterators.py +72 -0
- polyfile/jpeg.py +24 -0
- polyfile/kaitai/__init__.py +0 -0
- polyfile/kaitai/compiler.py +156 -0
- polyfile/kaitai/parser.py +312 -0
- polyfile/kaitai/parsers/__init__.py +0 -0
- polyfile/kaitai/parsers/aix_utmp.py +116 -0
- polyfile/kaitai/parsers/allegro_dat.py +367 -0
- polyfile/kaitai/parsers/andes_firmware.py +64 -0
- polyfile/kaitai/parsers/android_bootldr_asus.py +105 -0
- polyfile/kaitai/parsers/android_bootldr_huawei.py +181 -0
- polyfile/kaitai/parsers/android_bootldr_qcom.py +217 -0
- polyfile/kaitai/parsers/android_dto.py +138 -0
- polyfile/kaitai/parsers/android_img.py +319 -0
- polyfile/kaitai/parsers/android_nanoapp_header.py +83 -0
- polyfile/kaitai/parsers/android_opengl_shaders_cache.py +151 -0
- polyfile/kaitai/parsers/android_sparse.py +237 -0
- polyfile/kaitai/parsers/android_super.py +401 -0
- polyfile/kaitai/parsers/apm_partition_table.py +196 -0
- polyfile/kaitai/parsers/apple_single_double.py +180 -0
- polyfile/kaitai/parsers/asn1_der.py +235 -0
- polyfile/kaitai/parsers/au.py +138 -0
- polyfile/kaitai/parsers/avantes_roh60.py +112 -0
- polyfile/kaitai/parsers/avi.py +296 -0
- polyfile/kaitai/parsers/bcd.py +111 -0
- polyfile/kaitai/parsers/bitcoin_transaction.py +210 -0
- polyfile/kaitai/parsers/blender_blend.py +334 -0
- polyfile/kaitai/parsers/bmp.py +780 -0
- polyfile/kaitai/parsers/bson.py +411 -0
- polyfile/kaitai/parsers/btrfs_stream.py +318 -0
- polyfile/kaitai/parsers/bytes_with_io.py +27 -0
- polyfile/kaitai/parsers/chrome_pak.py +194 -0
- polyfile/kaitai/parsers/code_6502.py +456 -0
- polyfile/kaitai/parsers/compressed_resource.py +217 -0
- polyfile/kaitai/parsers/cpio_old_le.py +154 -0
- polyfile/kaitai/parsers/cramfs.py +344 -0
- polyfile/kaitai/parsers/creative_voice_file.py +342 -0
- polyfile/kaitai/parsers/dbf.py +274 -0
- polyfile/kaitai/parsers/dcmp_0.py +664 -0
- polyfile/kaitai/parsers/dcmp_1.py +422 -0
- polyfile/kaitai/parsers/dcmp_2.py +312 -0
- polyfile/kaitai/parsers/dcmp_variable_length_integer.py +66 -0
- polyfile/kaitai/parsers/dex.py +1086 -0
- polyfile/kaitai/parsers/dicom.py +4370 -0
- polyfile/kaitai/parsers/dime_message.py +201 -0
- polyfile/kaitai/parsers/dns_packet.py +569 -0
- polyfile/kaitai/parsers/doom_wad.py +654 -0
- polyfile/kaitai/parsers/dos_datetime.py +191 -0
- polyfile/kaitai/parsers/dos_mz.py +172 -0
- polyfile/kaitai/parsers/ds_store.py +513 -0
- polyfile/kaitai/parsers/dtb.py +310 -0
- polyfile/kaitai/parsers/dune_2_pak.py +126 -0
- polyfile/kaitai/parsers/edid.py +472 -0
- polyfile/kaitai/parsers/efivar_signature_list.py +331 -0
- polyfile/kaitai/parsers/elf.py +2482 -0
- polyfile/kaitai/parsers/ethernet_frame.py +114 -0
- polyfile/kaitai/parsers/exif.py +723 -0
- polyfile/kaitai/parsers/ext2.py +537 -0
- polyfile/kaitai/parsers/fallout2_dat.py +187 -0
- polyfile/kaitai/parsers/fallout_dat.py +156 -0
- polyfile/kaitai/parsers/fasttracker_xm_module.py +558 -0
- polyfile/kaitai/parsers/ftl_dat.py +90 -0
- polyfile/kaitai/parsers/genmidi_op2.py +161 -0
- polyfile/kaitai/parsers/gettext_mo.py +541 -0
- polyfile/kaitai/parsers/gif.py +492 -0
- polyfile/kaitai/parsers/gimp_brush.py +244 -0
- polyfile/kaitai/parsers/glibc_utmp.py +114 -0
- polyfile/kaitai/parsers/gltf_binary.py +132 -0
- polyfile/kaitai/parsers/google_protobuf.py +151 -0
- polyfile/kaitai/parsers/gpt_partition_table.py +175 -0
- polyfile/kaitai/parsers/gran_turismo_vol.py +140 -0
- polyfile/kaitai/parsers/grub2_font.py +337 -0
- polyfile/kaitai/parsers/gzip.py +232 -0
- polyfile/kaitai/parsers/hashcat_restore.py +60 -0
- polyfile/kaitai/parsers/hccap.py +111 -0
- polyfile/kaitai/parsers/hccapx.py +103 -0
- polyfile/kaitai/parsers/heaps_pak.py +177 -0
- polyfile/kaitai/parsers/heroes_of_might_and_magic_agg.py +116 -0
- polyfile/kaitai/parsers/heroes_of_might_and_magic_bmp.py +34 -0
- polyfile/kaitai/parsers/icmp_packet.py +136 -0
- polyfile/kaitai/parsers/ico.py +129 -0
- polyfile/kaitai/parsers/id3v1_1.py +220 -0
- polyfile/kaitai/parsers/id3v2_3.py +324 -0
- polyfile/kaitai/parsers/id3v2_4.py +423 -0
- polyfile/kaitai/parsers/ines.py +282 -0
- polyfile/kaitai/parsers/ipv4_packet.py +158 -0
- polyfile/kaitai/parsers/ipv6_packet.py +55 -0
- polyfile/kaitai/parsers/iso9660.py +544 -0
- polyfile/kaitai/parsers/java_class.py +1113 -0
- polyfile/kaitai/parsers/jpeg.py +361 -0
- polyfile/kaitai/parsers/luks.py +149 -0
- polyfile/kaitai/parsers/lzh.py +165 -0
- polyfile/kaitai/parsers/mac_os_resource_snd.py +493 -0
- polyfile/kaitai/parsers/mach_o.py +3033 -0
- polyfile/kaitai/parsers/mach_o_fat.py +92 -0
- polyfile/kaitai/parsers/magicavoxel_vox.py +391 -0
- polyfile/kaitai/parsers/manifest.json +1 -0
- polyfile/kaitai/parsers/mbr_partition_table.py +119 -0
- polyfile/kaitai/parsers/mcap.py +1015 -0
- polyfile/kaitai/parsers/microsoft_cfb.py +293 -0
- polyfile/kaitai/parsers/microsoft_network_monitor_v2.py +309 -0
- polyfile/kaitai/parsers/microsoft_pe.py +765 -0
- polyfile/kaitai/parsers/mifare_classic.py +706 -0
- polyfile/kaitai/parsers/minecraft_nbt.py +449 -0
- polyfile/kaitai/parsers/monomakh_sapr_chg.py +69 -0
- polyfile/kaitai/parsers/mozilla_mar.py +239 -0
- polyfile/kaitai/parsers/mp4.py +333 -0
- polyfile/kaitai/parsers/msgpack.py +467 -0
- polyfile/kaitai/parsers/nitf.py +1189 -0
- polyfile/kaitai/parsers/nt_mdt_pal.py +155 -0
- polyfile/kaitai/parsers/ogg.py +118 -0
- polyfile/kaitai/parsers/openpgp_message.py +993 -0
- polyfile/kaitai/parsers/packet_ppi.py +515 -0
- polyfile/kaitai/parsers/pcap.py +344 -0
- polyfile/kaitai/parsers/pcf_font.py +506 -0
- polyfile/kaitai/parsers/pcx.py +195 -0
- polyfile/kaitai/parsers/pcx_dcx.py +79 -0
- polyfile/kaitai/parsers/phar_without_stub.py +399 -0
- polyfile/kaitai/parsers/php_serialized_value.py +505 -0
- polyfile/kaitai/parsers/png.py +721 -0
- polyfile/kaitai/parsers/protocol_body.py +260 -0
- polyfile/kaitai/parsers/psx_tim.py +104 -0
- polyfile/kaitai/parsers/python_pickle.py +718 -0
- polyfile/kaitai/parsers/python_pyc_27.py +510 -0
- polyfile/kaitai/parsers/quake_mdl.py +441 -0
- polyfile/kaitai/parsers/quake_pak.py +112 -0
- polyfile/kaitai/parsers/quicktime_mov.py +634 -0
- polyfile/kaitai/parsers/rar.py +265 -0
- polyfile/kaitai/parsers/regf.py +569 -0
- polyfile/kaitai/parsers/renderware_binary_stream.py +877 -0
- polyfile/kaitai/parsers/resource_fork.py +611 -0
- polyfile/kaitai/parsers/respack.py +57 -0
- polyfile/kaitai/parsers/riff.py +409 -0
- polyfile/kaitai/parsers/rpm.py +964 -0
- polyfile/kaitai/parsers/rtcp_payload.py +579 -0
- polyfile/kaitai/parsers/rtp_packet.py +150 -0
- polyfile/kaitai/parsers/rtpdump.py +115 -0
- polyfile/kaitai/parsers/ruby_marshal.py +423 -0
- polyfile/kaitai/parsers/s3m.py +493 -0
- polyfile/kaitai/parsers/saints_row_2_vpp_pc.py +254 -0
- polyfile/kaitai/parsers/shapefile_index.py +174 -0
- polyfile/kaitai/parsers/shapefile_main.py +893 -0
- polyfile/kaitai/parsers/some_ip.py +209 -0
- polyfile/kaitai/parsers/some_ip_container.py +37 -0
- polyfile/kaitai/parsers/some_ip_sd.py +86 -0
- polyfile/kaitai/parsers/some_ip_sd_entries.py +160 -0
- polyfile/kaitai/parsers/some_ip_sd_options.py +374 -0
- polyfile/kaitai/parsers/specpr.py +404 -0
- polyfile/kaitai/parsers/sqlite3.py +472 -0
- polyfile/kaitai/parsers/ssh_public_key.py +252 -0
- polyfile/kaitai/parsers/standard_midi_file.py +390 -0
- polyfile/kaitai/parsers/stl.py +111 -0
- polyfile/kaitai/parsers/sudoers_ts.py +201 -0
- polyfile/kaitai/parsers/swf.py +406 -0
- polyfile/kaitai/parsers/systemd_journal.py +361 -0
- polyfile/kaitai/parsers/tcp_segment.py +57 -0
- polyfile/kaitai/parsers/tga.py +213 -0
- polyfile/kaitai/parsers/tls_client_hello.py +293 -0
- polyfile/kaitai/parsers/tr_dos_image.py +322 -0
- polyfile/kaitai/parsers/tsm.py +198 -0
- polyfile/kaitai/parsers/ttf.py +1847 -0
- polyfile/kaitai/parsers/udp_datagram.py +42 -0
- polyfile/kaitai/parsers/uefi_te.py +236 -0
- polyfile/kaitai/parsers/uimage.py +198 -0
- polyfile/kaitai/parsers/utf8_string.py +137 -0
- polyfile/kaitai/parsers/vfat.py +410 -0
- polyfile/kaitai/parsers/vlq_base128_be.py +104 -0
- polyfile/kaitai/parsers/vlq_base128_le.py +129 -0
- polyfile/kaitai/parsers/vmware_vmdk.py +167 -0
- polyfile/kaitai/parsers/vp8_ivf.py +112 -0
- polyfile/kaitai/parsers/warcraft_2_pud.py +423 -0
- polyfile/kaitai/parsers/wav.py +1014 -0
- polyfile/kaitai/parsers/websocket.py +167 -0
- polyfile/kaitai/parsers/windows_evt_log.py +304 -0
- polyfile/kaitai/parsers/windows_lnk_file.py +467 -0
- polyfile/kaitai/parsers/windows_minidump.py +575 -0
- polyfile/kaitai/parsers/windows_resource_file.py +243 -0
- polyfile/kaitai/parsers/windows_shell_items.py +190 -0
- polyfile/kaitai/parsers/windows_systemtime.py +52 -0
- polyfile/kaitai/parsers/wmf.py +502 -0
- polyfile/kaitai/parsers/xar.py +181 -0
- polyfile/kaitai/parsers/xwd.py +189 -0
- polyfile/kaitai/parsers/zip.py +685 -0
- polyfile/kaitai/parsers/zisofs.py +158 -0
- polyfile/kaitai/parsers/zx_spectrum_tap.py +184 -0
- polyfile/kaitaimatcher.py +113 -0
- polyfile/languagematcher.py +217 -0
- polyfile/logger.py +135 -0
- polyfile/magic.py +2983 -0
- polyfile/magic_defs/COPYING +29 -0
- polyfile/magic_defs/__init__.py +0 -0
- polyfile/magic_defs/acorn +102 -0
- polyfile/magic_defs/adi +13 -0
- polyfile/magic_defs/adventure +122 -0
- polyfile/magic_defs/aes +29 -0
- polyfile/magic_defs/algol68 +35 -0
- polyfile/magic_defs/allegro +9 -0
- polyfile/magic_defs/alliant +18 -0
- polyfile/magic_defs/alpha +32 -0
- polyfile/magic_defs/amanda +12 -0
- polyfile/magic_defs/amigaos +218 -0
- polyfile/magic_defs/android +259 -0
- polyfile/magic_defs/animation +1197 -0
- polyfile/magic_defs/aout +46 -0
- polyfile/magic_defs/apache +28 -0
- polyfile/magic_defs/apl +7 -0
- polyfile/magic_defs/apple +773 -0
- polyfile/magic_defs/application +7 -0
- polyfile/magic_defs/applix +13 -0
- polyfile/magic_defs/apt +52 -0
- polyfile/magic_defs/archive +2586 -0
- polyfile/magic_defs/aria +38 -0
- polyfile/magic_defs/arm +50 -0
- polyfile/magic_defs/asf +132 -0
- polyfile/magic_defs/assembler +18 -0
- polyfile/magic_defs/asterix +18 -0
- polyfile/magic_defs/att3b +41 -0
- polyfile/magic_defs/audio +1291 -0
- polyfile/magic_defs/avm +33 -0
- polyfile/magic_defs/basis +18 -0
- polyfile/magic_defs/beetle +7 -0
- polyfile/magic_defs/ber +65 -0
- polyfile/magic_defs/bflt +14 -0
- polyfile/magic_defs/bhl +10 -0
- polyfile/magic_defs/bioinformatics +178 -0
- polyfile/magic_defs/biosig +154 -0
- polyfile/magic_defs/blackberry +8 -0
- polyfile/magic_defs/blcr +25 -0
- polyfile/magic_defs/blender +50 -0
- polyfile/magic_defs/blit +24 -0
- polyfile/magic_defs/bm +10 -0
- polyfile/magic_defs/bout +11 -0
- polyfile/magic_defs/bsdi +33 -0
- polyfile/magic_defs/bsi +10 -0
- polyfile/magic_defs/btsnoop +13 -0
- polyfile/magic_defs/burp +7 -0
- polyfile/magic_defs/bytecode +41 -0
- polyfile/magic_defs/c-lang +110 -0
- polyfile/magic_defs/c64 +531 -0
- polyfile/magic_defs/cad +437 -0
- polyfile/magic_defs/cafebabe +107 -0
- polyfile/magic_defs/cbor +21 -0
- polyfile/magic_defs/ccf +14 -0
- polyfile/magic_defs/cddb +12 -0
- polyfile/magic_defs/chord +15 -0
- polyfile/magic_defs/cisco +12 -0
- polyfile/magic_defs/citrus +12 -0
- polyfile/magic_defs/clarion +27 -0
- polyfile/magic_defs/claris +48 -0
- polyfile/magic_defs/clipper +65 -0
- polyfile/magic_defs/clojure +30 -0
- polyfile/magic_defs/coff +98 -0
- polyfile/magic_defs/commands +201 -0
- polyfile/magic_defs/communications +22 -0
- polyfile/magic_defs/compress +461 -0
- polyfile/magic_defs/console +1213 -0
- polyfile/magic_defs/convex +69 -0
- polyfile/magic_defs/coverage +91 -0
- polyfile/magic_defs/cracklib +14 -0
- polyfile/magic_defs/crypto +31 -0
- polyfile/magic_defs/csv +8 -0
- polyfile/magic_defs/ctags +6 -0
- polyfile/magic_defs/ctf +23 -0
- polyfile/magic_defs/cubemap +8 -0
- polyfile/magic_defs/cups +56 -0
- polyfile/magic_defs/dact +11 -0
- polyfile/magic_defs/database +886 -0
- polyfile/magic_defs/dataone +47 -0
- polyfile/magic_defs/dbpf +15 -0
- polyfile/magic_defs/der +146 -0
- polyfile/magic_defs/diamond +12 -0
- polyfile/magic_defs/dif +33 -0
- polyfile/magic_defs/diff +41 -0
- polyfile/magic_defs/digital +59 -0
- polyfile/magic_defs/dolby +69 -0
- polyfile/magic_defs/dsf +25 -0
- polyfile/magic_defs/dump +96 -0
- polyfile/magic_defs/dwarfs +45 -0
- polyfile/magic_defs/dyadic +61 -0
- polyfile/magic_defs/ebml +8 -0
- polyfile/magic_defs/edid +11 -0
- polyfile/magic_defs/editors +43 -0
- polyfile/magic_defs/efi +15 -0
- polyfile/magic_defs/elf +379 -0
- polyfile/magic_defs/encore +22 -0
- polyfile/magic_defs/epoc +62 -0
- polyfile/magic_defs/erlang +21 -0
- polyfile/magic_defs/espressif +57 -0
- polyfile/magic_defs/esri +28 -0
- polyfile/magic_defs/etf +33 -0
- polyfile/magic_defs/fcs +9 -0
- polyfile/magic_defs/filesystems +2694 -0
- polyfile/magic_defs/finger +16 -0
- polyfile/magic_defs/firmware +133 -0
- polyfile/magic_defs/flash +62 -0
- polyfile/magic_defs/flif +36 -0
- polyfile/magic_defs/fonts +449 -0
- polyfile/magic_defs/forth +82 -0
- polyfile/magic_defs/fortran +9 -0
- polyfile/magic_defs/frame +62 -0
- polyfile/magic_defs/freebsd +164 -0
- polyfile/magic_defs/fsav +128 -0
- polyfile/magic_defs/fusecompress +12 -0
- polyfile/magic_defs/games +696 -0
- polyfile/magic_defs/gcc +17 -0
- polyfile/magic_defs/gconv +10 -0
- polyfile/magic_defs/gentoo +85 -0
- polyfile/magic_defs/geo +166 -0
- polyfile/magic_defs/geos +20 -0
- polyfile/magic_defs/gimp +77 -0
- polyfile/magic_defs/git +13 -0
- polyfile/magic_defs/glibc +21 -0
- polyfile/magic_defs/gnome +59 -0
- polyfile/magic_defs/gnu +173 -0
- polyfile/magic_defs/gnumeric +8 -0
- polyfile/magic_defs/gpt +240 -0
- polyfile/magic_defs/gpu +28 -0
- polyfile/magic_defs/grace +21 -0
- polyfile/magic_defs/graphviz +12 -0
- polyfile/magic_defs/gringotts +48 -0
- polyfile/magic_defs/guile +13 -0
- polyfile/magic_defs/hardware +12 -0
- polyfile/magic_defs/hitachi-sh +30 -0
- polyfile/magic_defs/hp +433 -0
- polyfile/magic_defs/human68k +26 -0
- polyfile/magic_defs/ibm370 +52 -0
- polyfile/magic_defs/ibm6000 +35 -0
- polyfile/magic_defs/icc +214 -0
- polyfile/magic_defs/iff +80 -0
- polyfile/magic_defs/images +4210 -0
- polyfile/magic_defs/inform +9 -0
- polyfile/magic_defs/intel +310 -0
- polyfile/magic_defs/interleaf +9 -0
- polyfile/magic_defs/island +10 -0
- polyfile/magic_defs/ispell +63 -0
- polyfile/magic_defs/isz +15 -0
- polyfile/magic_defs/java +52 -0
- polyfile/magic_defs/javascript +171 -0
- polyfile/magic_defs/jpeg +252 -0
- polyfile/magic_defs/json +8 -0
- polyfile/magic_defs/karma +9 -0
- polyfile/magic_defs/kde +11 -0
- polyfile/magic_defs/keepass +20 -0
- polyfile/magic_defs/kerberos +45 -0
- polyfile/magic_defs/kicad +85 -0
- polyfile/magic_defs/kml +34 -0
- polyfile/magic_defs/lammps +64 -0
- polyfile/magic_defs/lecter +6 -0
- polyfile/magic_defs/lex +12 -0
- polyfile/magic_defs/lif +50 -0
- polyfile/magic_defs/linux +557 -0
- polyfile/magic_defs/lisp +78 -0
- polyfile/magic_defs/llvm +22 -0
- polyfile/magic_defs/locoscript +12 -0
- polyfile/magic_defs/lua +31 -0
- polyfile/magic_defs/luks +126 -0
- polyfile/magic_defs/m4 +11 -0
- polyfile/magic_defs/mach +303 -0
- polyfile/magic_defs/macintosh +505 -0
- polyfile/magic_defs/macos +7 -0
- polyfile/magic_defs/magic +10 -0
- polyfile/magic_defs/magic.mgc +0 -0
- polyfile/magic_defs/mail.news +132 -0
- polyfile/magic_defs/make +21 -0
- polyfile/magic_defs/map +413 -0
- polyfile/magic_defs/maple +109 -0
- polyfile/magic_defs/marc21 +30 -0
- polyfile/magic_defs/mathcad +8 -0
- polyfile/magic_defs/mathematica +188 -0
- polyfile/magic_defs/matroska +17 -0
- polyfile/magic_defs/mcrypt +52 -0
- polyfile/magic_defs/measure +44 -0
- polyfile/magic_defs/mercurial +13 -0
- polyfile/magic_defs/metastore +8 -0
- polyfile/magic_defs/meteorological +53 -0
- polyfile/magic_defs/microfocus +21 -0
- polyfile/magic_defs/mime +9 -0
- polyfile/magic_defs/mips +120 -0
- polyfile/magic_defs/mirage +8 -0
- polyfile/magic_defs/misctools +140 -0
- polyfile/magic_defs/mkid +11 -0
- polyfile/magic_defs/mlssa +8 -0
- polyfile/magic_defs/mmdf +6 -0
- polyfile/magic_defs/modem +92 -0
- polyfile/magic_defs/modulefile +9 -0
- polyfile/magic_defs/motorola +71 -0
- polyfile/magic_defs/mozilla +37 -0
- polyfile/magic_defs/msdos +2304 -0
- polyfile/magic_defs/msooxml +68 -0
- polyfile/magic_defs/msvc +222 -0
- polyfile/magic_defs/msx +309 -0
- polyfile/magic_defs/mup +24 -0
- polyfile/magic_defs/music +17 -0
- polyfile/magic_defs/nasa +7 -0
- polyfile/magic_defs/natinst +24 -0
- polyfile/magic_defs/ncr +49 -0
- polyfile/magic_defs/neko +12 -0
- polyfile/magic_defs/netbsd +251 -0
- polyfile/magic_defs/netscape +26 -0
- polyfile/magic_defs/netware +11 -0
- polyfile/magic_defs/news +13 -0
- polyfile/magic_defs/nifty +202 -0
- polyfile/magic_defs/nim-lang +29 -0
- polyfile/magic_defs/nitpicker +14 -0
- polyfile/magic_defs/numpy +9 -0
- polyfile/magic_defs/oasis +12 -0
- polyfile/magic_defs/ocaml +14 -0
- polyfile/magic_defs/octave +6 -0
- polyfile/magic_defs/ole2compounddocs +760 -0
- polyfile/magic_defs/olf +98 -0
- polyfile/magic_defs/openfst +17 -0
- polyfile/magic_defs/opentimestamps +16 -0
- polyfile/magic_defs/oric +16 -0
- polyfile/magic_defs/os2 +186 -0
- polyfile/magic_defs/os400 +39 -0
- polyfile/magic_defs/os9 +80 -0
- polyfile/magic_defs/osf1 +10 -0
- polyfile/magic_defs/palm +156 -0
- polyfile/magic_defs/parix +13 -0
- polyfile/magic_defs/parrot +22 -0
- polyfile/magic_defs/pascal +39 -0
- polyfile/magic_defs/pbf +11 -0
- polyfile/magic_defs/pbm +8 -0
- polyfile/magic_defs/pc88 +24 -0
- polyfile/magic_defs/pc98 +77 -0
- polyfile/magic_defs/pci_ids +116 -0
- polyfile/magic_defs/pcjr +8 -0
- polyfile/magic_defs/pdf +51 -0
- polyfile/magic_defs/pdp +42 -0
- polyfile/magic_defs/perl +100 -0
- polyfile/magic_defs/pgf +52 -0
- polyfile/magic_defs/pgp +581 -0
- polyfile/magic_defs/pgp-binary-keys +388 -0
- polyfile/magic_defs/pkgadd +7 -0
- polyfile/magic_defs/plan9 +25 -0
- polyfile/magic_defs/playdate +57 -0
- polyfile/magic_defs/plus5 +18 -0
- polyfile/magic_defs/pmem +46 -0
- polyfile/magic_defs/polyfile_zip +5 -0
- polyfile/magic_defs/polyml +23 -0
- polyfile/magic_defs/printer +269 -0
- polyfile/magic_defs/project +10 -0
- polyfile/magic_defs/psdbms +14 -0
- polyfile/magic_defs/psl +14 -0
- polyfile/magic_defs/pulsar +13 -0
- polyfile/magic_defs/puzzle +17 -0
- polyfile/magic_defs/pwsafe +14 -0
- polyfile/magic_defs/pyramid +12 -0
- polyfile/magic_defs/python +305 -0
- polyfile/magic_defs/qt +30 -0
- polyfile/magic_defs/revision +66 -0
- polyfile/magic_defs/riff +840 -0
- polyfile/magic_defs/rinex +44 -0
- polyfile/magic_defs/ringdove +45 -0
- polyfile/magic_defs/rpi +52 -0
- polyfile/magic_defs/rpm +45 -0
- polyfile/magic_defs/rpmsg +7 -0
- polyfile/magic_defs/rst +11 -0
- polyfile/magic_defs/rtf +94 -0
- polyfile/magic_defs/ruby +55 -0
- polyfile/magic_defs/rust +21 -0
- polyfile/magic_defs/sc +7 -0
- polyfile/magic_defs/sccs +24 -0
- polyfile/magic_defs/scientific +144 -0
- polyfile/magic_defs/securitycerts +6 -0
- polyfile/magic_defs/selinux +24 -0
- polyfile/magic_defs/sendmail +37 -0
- polyfile/magic_defs/sequent +42 -0
- polyfile/magic_defs/sereal +35 -0
- polyfile/magic_defs/sgi +144 -0
- polyfile/magic_defs/sgml +161 -0
- polyfile/magic_defs/sharc +23 -0
- polyfile/magic_defs/sinclair +40 -0
- polyfile/magic_defs/sisu +18 -0
- polyfile/magic_defs/sketch +6 -0
- polyfile/magic_defs/smalltalk +25 -0
- polyfile/magic_defs/smile +34 -0
- polyfile/magic_defs/sniffer +482 -0
- polyfile/magic_defs/softquad +40 -0
- polyfile/magic_defs/sosi +40 -0
- polyfile/magic_defs/spec +21 -0
- polyfile/magic_defs/spectrum +184 -0
- polyfile/magic_defs/sql +288 -0
- polyfile/magic_defs/ssh +39 -0
- polyfile/magic_defs/ssl +20 -0
- polyfile/magic_defs/statistics +45 -0
- polyfile/magic_defs/subtitle +38 -0
- polyfile/magic_defs/sun +141 -0
- polyfile/magic_defs/svf +5 -0
- polyfile/magic_defs/sylk +36 -0
- polyfile/magic_defs/symbos +42 -0
- polyfile/magic_defs/sysex +429 -0
- polyfile/magic_defs/tcl +29 -0
- polyfile/magic_defs/teapot +6 -0
- polyfile/magic_defs/terminfo +63 -0
- polyfile/magic_defs/tex +141 -0
- polyfile/magic_defs/tgif +7 -0
- polyfile/magic_defs/ti-8x +239 -0
- polyfile/magic_defs/timezone +42 -0
- polyfile/magic_defs/tplink +95 -0
- polyfile/magic_defs/troff +38 -0
- polyfile/magic_defs/tuxedo +8 -0
- polyfile/magic_defs/typeset +8 -0
- polyfile/magic_defs/uf2 +72 -0
- polyfile/magic_defs/unicode +15 -0
- polyfile/magic_defs/unisig +12 -0
- polyfile/magic_defs/unknown +34 -0
- polyfile/magic_defs/usd +21 -0
- polyfile/magic_defs/uterus +16 -0
- polyfile/magic_defs/uuencode +28 -0
- polyfile/magic_defs/vacuum-cleaner +54 -0
- polyfile/magic_defs/varied.out +46 -0
- polyfile/magic_defs/varied.script +21 -0
- polyfile/magic_defs/vax +32 -0
- polyfile/magic_defs/vicar +17 -0
- polyfile/magic_defs/virtual +307 -0
- polyfile/magic_defs/virtutech +12 -0
- polyfile/magic_defs/visx +32 -0
- polyfile/magic_defs/vms +30 -0
- polyfile/magic_defs/vmware +6 -0
- polyfile/magic_defs/vorbis +155 -0
- polyfile/magic_defs/vxl +14 -0
- polyfile/magic_defs/warc +16 -0
- polyfile/magic_defs/weak +16 -0
- polyfile/magic_defs/web +18 -0
- polyfile/magic_defs/webassembly +17 -0
- polyfile/magic_defs/windows +1811 -0
- polyfile/magic_defs/wireless +7 -0
- polyfile/magic_defs/wordprocessors +630 -0
- polyfile/magic_defs/wsdl +23 -0
- polyfile/magic_defs/x68000 +25 -0
- polyfile/magic_defs/xdelta +13 -0
- polyfile/magic_defs/xenix +106 -0
- polyfile/magic_defs/xilinx +58 -0
- polyfile/magic_defs/xo65 +37 -0
- polyfile/magic_defs/xwindows +43 -0
- polyfile/magic_defs/yara +17 -0
- polyfile/magic_defs/zfs +96 -0
- polyfile/magic_defs/zilog +12 -0
- polyfile/magic_defs/zip +126 -0
- polyfile/magic_defs/zyxel +17 -0
- polyfile/nes.py +144 -0
- polyfile/nitf.py +15 -0
- polyfile/pdf.py +1264 -0
- polyfile/pickles.py +45 -0
- polyfile/polyfile.py +409 -0
- polyfile/profiling.py +115 -0
- polyfile/repl.py +624 -0
- polyfile/search.py +310 -0
- polyfile/serialization.py +323 -0
- polyfile/structmatcher.py +46 -0
- polyfile/structs.py +281 -0
- polyfile/templates/download.js +162 -0
- polyfile/templates/hexdump.css +268 -0
- polyfile/templates/hexdump.js +756 -0
- polyfile/templates/jquery-3.4.1.min.js +2 -0
- polyfile/templates/template.html +119 -0
- polyfile/wildcards.py +62 -0
- polyfile/zipmatcher.py +183 -0
- polyfile_weave-0.5.5.dist-info/METADATA +173 -0
- polyfile_weave-0.5.5.dist-info/RECORD +585 -0
- polyfile_weave-0.5.5.dist-info/WHEEL +5 -0
- polyfile_weave-0.5.5.dist-info/entry_points.txt +2 -0
- polyfile_weave-0.5.5.dist-info/licenses/LICENSE +202 -0
- polyfile_weave-0.5.5.dist-info/top_level.txt +2 -0
- polymerge/__init__.py +1 -0
- polymerge/__main__.py +296 -0
- polymerge/cfg.py +127 -0
- polymerge/polymerge.py +227 -0
- polymerge/polytracker.py +190 -0
polyfile/pickles.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from fickling.analysis import Analyzer, Severity
|
|
4
|
+
from fickling.fickle import Pickled, PickleDecodeError
|
|
5
|
+
|
|
6
|
+
from .magic import AbsoluteOffset, DynamicMagicTest, FailedTest, MagicMatcher, MatchedTest, TestResult, TestType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PickleMatcher(DynamicMagicTest):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__(
|
|
12
|
+
offset=AbsoluteOffset(0),
|
|
13
|
+
mime="application/x-python-pickle",
|
|
14
|
+
extensions=("pickle", "pkl"),
|
|
15
|
+
default_message="Python Pickle Serialization"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def subtest_type(self) -> TestType:
|
|
19
|
+
return TestType.BINARY
|
|
20
|
+
|
|
21
|
+
def test(self, data: bytes, absolute_offset: int, parent_match: Optional[TestResult]) -> TestResult:
|
|
22
|
+
prev = -1
|
|
23
|
+
for i, c in enumerate(data):
|
|
24
|
+
if i >= 128:
|
|
25
|
+
break
|
|
26
|
+
elif prev == 0x80 and c in (2, 3, 4):
|
|
27
|
+
try:
|
|
28
|
+
pickled = Pickled.load(data)
|
|
29
|
+
results = Analyzer.default_instance.analyze(pickled)
|
|
30
|
+
if results.severity <= Severity.LIKELY_SAFE:
|
|
31
|
+
message = self.message
|
|
32
|
+
else:
|
|
33
|
+
buffer_data = results.to_string(verbosity=Severity.LIKELY_UNSAFE)
|
|
34
|
+
if buffer_data:
|
|
35
|
+
buffer_data = f"\n{buffer_data}".replace("%", "%%")
|
|
36
|
+
message = f"Likely Unsafe {self.default_message}{buffer_data}"
|
|
37
|
+
return MatchedTest(self.bind(message), value=str(message), offset=0, length=len(data))
|
|
38
|
+
except PickleDecodeError as e:
|
|
39
|
+
return FailedTest(self, offset=0, message=f"data was not decodable as a pickle file: {e!s}")
|
|
40
|
+
prev = c
|
|
41
|
+
return FailedTest(self, offset=0, message="data did not start with b\"\x80[\x02\x03\x04]\"")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
DEFAULT_PICKLE_MATCHER = PickleMatcher()
|
|
45
|
+
MagicMatcher.DEFAULT_INSTANCE.add(DEFAULT_PICKLE_MATCHER)
|
polyfile/polyfile.py
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
import base64
|
|
4
|
+
import hashlib
|
|
5
|
+
from json import dumps
|
|
6
|
+
from mimetypes import guess_extension
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import sys
|
|
9
|
+
from time import localtime
|
|
10
|
+
import traceback
|
|
11
|
+
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Optional, Set, Tuple, Union
|
|
12
|
+
|
|
13
|
+
from .fileutils import FileStream
|
|
14
|
+
from . import logger
|
|
15
|
+
from .magic import MagicMatcher, Match as MagicMatch, MatchContext, TestResult
|
|
16
|
+
|
|
17
|
+
if sys.version_info >= (3, 10):
|
|
18
|
+
from importlib.metadata import version
|
|
19
|
+
__version__: str = version("polyfile")
|
|
20
|
+
del version
|
|
21
|
+
else:
|
|
22
|
+
import pkg_resources
|
|
23
|
+
__version__ = pkg_resources.require("polyfile")[0].version
|
|
24
|
+
del pkg_resources
|
|
25
|
+
mod_year = localtime(Path(__file__).stat().st_mtime).tm_year
|
|
26
|
+
__copyright__: str = f"Copyright ©{mod_year} Trail of Bits"
|
|
27
|
+
__license__: str = "Apache License Version 2.0 https://www.apache.org/licenses/"
|
|
28
|
+
|
|
29
|
+
ParserFunction = Callable[[FileStream, "Match"], Iterator["Submatch"]]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Parser(ABC, ParserFunction):
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def parse(self, stream: FileStream, match: "Match") -> Iterator["Submatch"]:
|
|
35
|
+
raise NotImplementedError()
|
|
36
|
+
|
|
37
|
+
def __call__(self, stream: FileStream, match: "Match") -> Iterator["Submatch"]:
|
|
38
|
+
yield from self.parse(stream, match)
|
|
39
|
+
|
|
40
|
+
def __hash__(self):
|
|
41
|
+
return id(self)
|
|
42
|
+
|
|
43
|
+
def __str__(self):
|
|
44
|
+
return self.__class__.__name__
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ParserFunctionWrapper(Parser):
|
|
48
|
+
def __init__(self, parser: ParserFunction):
|
|
49
|
+
self.parser: ParserFunction = parser
|
|
50
|
+
|
|
51
|
+
def __hash__(self):
|
|
52
|
+
return hash(self.parser)
|
|
53
|
+
|
|
54
|
+
def parse(self, stream: FileStream, match: "Match") -> Iterator["Submatch"]:
|
|
55
|
+
yield from self.parser(stream, match)
|
|
56
|
+
|
|
57
|
+
def __str__(self):
|
|
58
|
+
if hasattr(self.parser, "__qualname__"):
|
|
59
|
+
return self.parser.__qualname__
|
|
60
|
+
elif hasattr(self.parser, "__name__"):
|
|
61
|
+
return self.parser.__name__
|
|
62
|
+
else:
|
|
63
|
+
return self.parser.__class__.__name__
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
PARSERS: Dict[str, Set[Parser]] = defaultdict(set)
|
|
67
|
+
|
|
68
|
+
log = logger.getStatusLogger("polyfile")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InvalidMatch(ValueError):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Match:
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
name: str,
|
|
79
|
+
match_obj: Any,
|
|
80
|
+
relative_offset: int = 0,
|
|
81
|
+
length: Optional[int] = None,
|
|
82
|
+
parent: Optional["Match"] = None,
|
|
83
|
+
matcher: Optional["Matcher"] = None,
|
|
84
|
+
display_name: Optional[str] = None,
|
|
85
|
+
img_data: Optional[str] = None,
|
|
86
|
+
decoded: Optional[bytes] = None,
|
|
87
|
+
extension: Optional[str] = None
|
|
88
|
+
):
|
|
89
|
+
self._children: List[Match] = []
|
|
90
|
+
self.name: str = name
|
|
91
|
+
self.matcher: Optional[Matcher] = None
|
|
92
|
+
self.match = match_obj
|
|
93
|
+
self.img_data: Optional[str] = img_data
|
|
94
|
+
self.decoded: Optional[bytes] = decoded
|
|
95
|
+
self._offset: int = relative_offset
|
|
96
|
+
self._length: Optional[int] = length
|
|
97
|
+
self._parent: Optional[Match] = parent
|
|
98
|
+
if parent is not None:
|
|
99
|
+
if not isinstance(parent, Match):
|
|
100
|
+
raise ValueError("The parent must be an instance of a Match")
|
|
101
|
+
parent._children.append(self)
|
|
102
|
+
if matcher is None:
|
|
103
|
+
matcher = parent.matcher
|
|
104
|
+
if matcher is None:
|
|
105
|
+
raise(ValueError("A Match must be initialized with `parent` and/or `matcher` not being None"))
|
|
106
|
+
self.matcher = matcher
|
|
107
|
+
if display_name is None:
|
|
108
|
+
self.display_name: str = name
|
|
109
|
+
else:
|
|
110
|
+
self.display_name = display_name
|
|
111
|
+
self.extension: Optional[str] = extension
|
|
112
|
+
if extension is None:
|
|
113
|
+
self.extension = guess_extension(self.name)
|
|
114
|
+
if self.extension is not None and self.extension.startswith("."):
|
|
115
|
+
# guess_extension adds a leading dot
|
|
116
|
+
self.extension = self.extension[1:]
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def children(self) -> Tuple["Match", ...]:
|
|
120
|
+
return tuple(self._children)
|
|
121
|
+
|
|
122
|
+
def __len__(self):
|
|
123
|
+
return len(self._children)
|
|
124
|
+
|
|
125
|
+
def __iter__(self) -> Iterator["Match"]:
|
|
126
|
+
return iter(self._children)
|
|
127
|
+
|
|
128
|
+
def __getitem__(self, index: int) -> "Match":
|
|
129
|
+
return self._children[index]
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def parent(self) -> Optional["Match"]:
|
|
133
|
+
return self._parent
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def offset(self) -> int:
|
|
137
|
+
"""The global offset of this match with respect to the original file"""
|
|
138
|
+
if self.parent is not None:
|
|
139
|
+
return self.parent.offset + self.relative_offset
|
|
140
|
+
else:
|
|
141
|
+
return self.relative_offset
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def root(self) -> "Match":
|
|
145
|
+
if self.parent is None:
|
|
146
|
+
return self
|
|
147
|
+
else:
|
|
148
|
+
return self.parent.root
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def root_offset(self) -> int:
|
|
152
|
+
return self.offset - self.root.offset
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def relative_offset(self) -> int:
|
|
156
|
+
"""The offset of this match relative to its parent"""
|
|
157
|
+
return self._offset
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def length(self) -> int:
|
|
161
|
+
"""The number of bytes in the match"""
|
|
162
|
+
if self._length is None:
|
|
163
|
+
if self._children:
|
|
164
|
+
return max(c.offset + c.length for c in self._children) - self.offset
|
|
165
|
+
else:
|
|
166
|
+
return 0
|
|
167
|
+
return self._length
|
|
168
|
+
|
|
169
|
+
def to_obj(self):
|
|
170
|
+
ret = {
|
|
171
|
+
'relative_offset': self.relative_offset,
|
|
172
|
+
'offset': self.offset,
|
|
173
|
+
'size': self.length,
|
|
174
|
+
'type': self.name,
|
|
175
|
+
'name': self.display_name,
|
|
176
|
+
'value': str(self.match),
|
|
177
|
+
'subEls': [c.to_obj() for c in self]
|
|
178
|
+
}
|
|
179
|
+
if self.img_data is not None:
|
|
180
|
+
ret['img_data'] = self.img_data
|
|
181
|
+
if self.decoded is not None:
|
|
182
|
+
ret['decoded'] = base64.b64encode(self.decoded).decode('utf-8')
|
|
183
|
+
if self.extension is not None:
|
|
184
|
+
ret['extension'] = self.extension
|
|
185
|
+
return ret
|
|
186
|
+
|
|
187
|
+
def json(self) -> str:
|
|
188
|
+
return dumps(self.to_obj())
|
|
189
|
+
|
|
190
|
+
def __repr__(self):
|
|
191
|
+
return f"{self.__class__.__name__}(match={self.match!r}, relative_offset={self._offset}, parent={self._parent!r})"
|
|
192
|
+
|
|
193
|
+
def __str__(self):
|
|
194
|
+
return f"Match<{self.match}>@{self._offset}"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class Submatch(Match):
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def register_parser(*filetypes: str) -> Callable[[Union[Parser, ParserFunction]], Parser]:
|
|
202
|
+
def wrapper(parser: Union[Parser, ParserFunction]) -> Parser:
|
|
203
|
+
if not isinstance(parser, Parser):
|
|
204
|
+
parser = ParserFunctionWrapper(parser)
|
|
205
|
+
for ft in filetypes:
|
|
206
|
+
PARSERS[ft].add(parser)
|
|
207
|
+
return parser
|
|
208
|
+
return wrapper
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class Matcher:
|
|
212
|
+
def __init__(self, try_all_offsets: bool = False, parse: bool = True, matcher: Optional[MagicMatcher] = None):
|
|
213
|
+
if matcher is None:
|
|
214
|
+
self.magic_matcher: MagicMatcher = MagicMatcher.DEFAULT_INSTANCE
|
|
215
|
+
else:
|
|
216
|
+
self.magic_matcher = matcher
|
|
217
|
+
self.try_all_offsets: bool = try_all_offsets
|
|
218
|
+
self.parse: bool = parse
|
|
219
|
+
|
|
220
|
+
def handle_mimetype(
|
|
221
|
+
self, mimetype: str,
|
|
222
|
+
match_obj: TestResult,
|
|
223
|
+
data: bytes,
|
|
224
|
+
file_stream: Union[str, Path, IO, FileStream],
|
|
225
|
+
parent: Optional[Match] = None,
|
|
226
|
+
offset: int = 0,
|
|
227
|
+
length: Optional[int] = None
|
|
228
|
+
) -> Iterator[Match]:
|
|
229
|
+
if length is None:
|
|
230
|
+
length = len(data) - offset
|
|
231
|
+
extension: Optional[str] = None
|
|
232
|
+
try:
|
|
233
|
+
extension = next(iter(match_obj.test.all_extensions()))
|
|
234
|
+
except StopIteration:
|
|
235
|
+
pass
|
|
236
|
+
m = Match(
|
|
237
|
+
mimetype,
|
|
238
|
+
match_obj,
|
|
239
|
+
offset,
|
|
240
|
+
length=length,
|
|
241
|
+
parent=parent,
|
|
242
|
+
matcher=self,
|
|
243
|
+
extension=extension
|
|
244
|
+
)
|
|
245
|
+
yield m
|
|
246
|
+
if self.parse:
|
|
247
|
+
for parser in PARSERS[mimetype]:
|
|
248
|
+
# Don't yield this custom match until we've tried its submatch function
|
|
249
|
+
# (which may throw an InvalidMatch, meaning that this match is invalid)
|
|
250
|
+
try:
|
|
251
|
+
with FileStream(file_stream, start=offset, length=length) as fs:
|
|
252
|
+
submatch_iter = parser(fs, m)
|
|
253
|
+
try:
|
|
254
|
+
first_submatch = next(submatch_iter)
|
|
255
|
+
has_first = True
|
|
256
|
+
except StopIteration:
|
|
257
|
+
has_first = False
|
|
258
|
+
if has_first:
|
|
259
|
+
yield first_submatch
|
|
260
|
+
yield from submatch_iter
|
|
261
|
+
except InvalidMatch:
|
|
262
|
+
pass
|
|
263
|
+
except Exception as e:
|
|
264
|
+
log.warning(f"Parser {parser!s} for MIME type {mimetype} raised an exception while "
|
|
265
|
+
f"parsing {match_obj!s} in {file_stream!s}: {e!s}")
|
|
266
|
+
if log.isEnabledFor(logger.logging.DEBUG):
|
|
267
|
+
traceback.print_exc()
|
|
268
|
+
|
|
269
|
+
def identify(
|
|
270
|
+
self, file_stream: Union[str, Path, IO, FileStream]
|
|
271
|
+
) -> Iterator[MagicMatch]:
|
|
272
|
+
with FileStream(file_stream) as f:
|
|
273
|
+
context = MatchContext.load(f, only_match_mime=False)
|
|
274
|
+
yield from self.magic_matcher.match(context)
|
|
275
|
+
|
|
276
|
+
def match(self, file_stream: Union[str, Path, IO, FileStream], parent: Optional[Match] = None) -> Iterator[Match]:
|
|
277
|
+
with FileStream(file_stream) as f:
|
|
278
|
+
matched_mimetypes: Set[str] = set()
|
|
279
|
+
context = MatchContext.load(f, only_match_mime=True)
|
|
280
|
+
for magic_match in self.magic_matcher.match(context):
|
|
281
|
+
for result in magic_match:
|
|
282
|
+
if result.test.mime is None:
|
|
283
|
+
continue
|
|
284
|
+
mimetype = result.test.mime.resolve(context)
|
|
285
|
+
if mimetype in matched_mimetypes:
|
|
286
|
+
continue
|
|
287
|
+
matched_mimetypes.add(mimetype)
|
|
288
|
+
yield from self.handle_mimetype(mimetype, result, context.data, file_stream, parent)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class Analyzer:
|
|
292
|
+
def __init__(self, path: Union[str, Path], try_all_offsets: bool = False, parse: bool = True,
|
|
293
|
+
magic_matcher: Optional[MagicMatcher] = None):
|
|
294
|
+
self.path: Union[str, Path] = path
|
|
295
|
+
self.try_all_offsets: bool = try_all_offsets
|
|
296
|
+
self.parse: bool = parse
|
|
297
|
+
self._magic_matcher: Optional[MagicMatcher] = magic_matcher
|
|
298
|
+
self._matcher: Optional[Matcher] = None
|
|
299
|
+
self._matches: Optional[List[Match]] = None
|
|
300
|
+
self._match_iterator: Optional[Iterator[Match]] = None
|
|
301
|
+
self._magic_matches: Optional[List[MagicMatch]] = None
|
|
302
|
+
self._magic_match_iterator: Optional[Iterator[MagicMatch]] = None
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def magic_matcher(self) -> MagicMatcher:
|
|
306
|
+
if self._magic_matcher is None:
|
|
307
|
+
return MagicMatcher.DEFAULT_INSTANCE
|
|
308
|
+
else:
|
|
309
|
+
return self._magic_matcher
|
|
310
|
+
|
|
311
|
+
def mime_types(self) -> Iterator[Tuple[str, MagicMatch]]:
|
|
312
|
+
mimetypes: Dict[str, Set[str]] = {}
|
|
313
|
+
with open(self.path, "rb") as f:
|
|
314
|
+
for match in self.magic_matcher.match(MatchContext.load(f, only_match_mime=True)):
|
|
315
|
+
for mimetype in match.mimetypes:
|
|
316
|
+
match_text = str(match)
|
|
317
|
+
if mimetype not in mimetypes:
|
|
318
|
+
mimetypes[mimetype] = set()
|
|
319
|
+
if match_text not in mimetypes[mimetype]:
|
|
320
|
+
yield mimetype, match
|
|
321
|
+
mimetypes[mimetype].add(match_text)
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def matcher(self) -> Matcher:
|
|
325
|
+
if self._matcher is None:
|
|
326
|
+
self._matcher = Matcher(parse=self.parse, matcher=self.magic_matcher)
|
|
327
|
+
return self._matcher
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def matches_so_far(self) -> List[Match]:
|
|
331
|
+
return self._matches
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def magic_matches_so_far(self) -> List[MagicMatch]:
|
|
335
|
+
return self._magic_matches
|
|
336
|
+
|
|
337
|
+
def matches(self) -> Iterator[Match]:
|
|
338
|
+
if self._matches is None or self._match_iterator is not None:
|
|
339
|
+
if self._matches is None:
|
|
340
|
+
self._matches = []
|
|
341
|
+
self._match_iterator = iter(self.matcher.match(self.path))
|
|
342
|
+
else:
|
|
343
|
+
yield from self._matches
|
|
344
|
+
while True:
|
|
345
|
+
try:
|
|
346
|
+
match = next(self._match_iterator)
|
|
347
|
+
except StopIteration:
|
|
348
|
+
self._match_iterator = None
|
|
349
|
+
break
|
|
350
|
+
if hasattr(match.match, "filetype"):
|
|
351
|
+
filetype = match.match.filetype
|
|
352
|
+
else:
|
|
353
|
+
filetype = match.name
|
|
354
|
+
if match.parent is None:
|
|
355
|
+
log.info(f"Found a file of type {filetype} at byte offset {match.offset}")
|
|
356
|
+
self._matches.append(match)
|
|
357
|
+
yield match
|
|
358
|
+
elif isinstance(match, Submatch):
|
|
359
|
+
log.debug(f"Found a subregion of type {filetype} at byte offset {match.offset}")
|
|
360
|
+
else:
|
|
361
|
+
log.info(f"Found an embedded file of type {filetype} at byte offset {match.offset}")
|
|
362
|
+
else:
|
|
363
|
+
yield from self._matches
|
|
364
|
+
|
|
365
|
+
def magic_matches(self) -> Iterator[Match]:
|
|
366
|
+
if self._magic_matches is None or self._magic_match_iterator is not None:
|
|
367
|
+
if self._magic_matches is None:
|
|
368
|
+
self._magic_matches = []
|
|
369
|
+
self._magic_match_iterator = iter(self.matcher.identify(self.path))
|
|
370
|
+
else:
|
|
371
|
+
yield from self._magic_matches
|
|
372
|
+
while True:
|
|
373
|
+
try:
|
|
374
|
+
match = next(self._magic_match_iterator)
|
|
375
|
+
yield match
|
|
376
|
+
except StopIteration:
|
|
377
|
+
self._magic_match_iterator = None
|
|
378
|
+
break
|
|
379
|
+
else:
|
|
380
|
+
yield from self._magic_matches
|
|
381
|
+
|
|
382
|
+
def sbud(self, matches: Optional[Iterable[Match]] = None) -> Dict[str, Any]:
|
|
383
|
+
if matches is None:
|
|
384
|
+
matches = self.matches()
|
|
385
|
+
md5 = hashlib.md5()
|
|
386
|
+
sha1 = hashlib.sha1()
|
|
387
|
+
sha256 = hashlib.sha256()
|
|
388
|
+
with open(self.path, 'rb') as hash_file:
|
|
389
|
+
data = hash_file.read()
|
|
390
|
+
md5.update(data)
|
|
391
|
+
sha1.update(data)
|
|
392
|
+
sha256.update(data)
|
|
393
|
+
b64contents = base64.b64encode(data)
|
|
394
|
+
file_length = len(data)
|
|
395
|
+
del data
|
|
396
|
+
return {
|
|
397
|
+
'MD5': md5.hexdigest(),
|
|
398
|
+
'SHA1': sha1.hexdigest(),
|
|
399
|
+
'SHA256': sha256.hexdigest(),
|
|
400
|
+
'b64contents': b64contents.decode('utf-8'),
|
|
401
|
+
'fileName': self.path,
|
|
402
|
+
'length': file_length,
|
|
403
|
+
'versions': {
|
|
404
|
+
'polyfile': __version__
|
|
405
|
+
},
|
|
406
|
+
'struc': [
|
|
407
|
+
match.to_obj() for match in matches
|
|
408
|
+
]
|
|
409
|
+
}
|
polyfile/profiling.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
import time
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from . import logger
|
|
5
|
+
|
|
6
|
+
log = logger.getStatusLogger(__file__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def current_time_ms():
|
|
10
|
+
return time.process_time_ns() / 1000000.0
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Note: This assumes single-threading, but that's basically the case as long as the GIL exists
|
|
14
|
+
_PROFILER_STACK: ["Profiler"] = []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Profiler:
|
|
18
|
+
def __init__(self, parent: Optional["Profiler"] = None):
|
|
19
|
+
self.start_time_ms: Optional[float] = None
|
|
20
|
+
self.end_time_ms: Optional[float] = None
|
|
21
|
+
self.parent: Optional[Profiler] = parent
|
|
22
|
+
self._paused_start_time_ms: [float] = []
|
|
23
|
+
self._paused_ms: float = 0.0
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def complete(self) -> bool:
|
|
27
|
+
return self.end_time_ms is not None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def is_paused(self) -> bool:
|
|
31
|
+
return bool(self._paused_start_time_ms)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def paused_ms(self) -> float:
|
|
35
|
+
if self.complete or not self.is_paused:
|
|
36
|
+
return self._paused_ms
|
|
37
|
+
return self._paused_ms + current_time_ms() - self._paused_start_time_ms[0]
|
|
38
|
+
|
|
39
|
+
def pause(self):
|
|
40
|
+
profiler = self
|
|
41
|
+
while profiler is not None:
|
|
42
|
+
if profiler.complete:
|
|
43
|
+
raise ValueError("You cannot pause a completed profiler")
|
|
44
|
+
profiler._paused_start_time_ms.append(current_time_ms())
|
|
45
|
+
|
|
46
|
+
def unpause(self):
|
|
47
|
+
profiler = self
|
|
48
|
+
while profiler is not None:
|
|
49
|
+
if profiler.complete:
|
|
50
|
+
raise ValueError("You cannot unpause a completed profiler")
|
|
51
|
+
elif not profiler.is_paused:
|
|
52
|
+
raise ValueError("The profiler is not currently paused")
|
|
53
|
+
profiler._paused_ms += current_time_ms() - profiler._paused_start_time_ms[-1]
|
|
54
|
+
profiler._paused_start_time_ms.pop()
|
|
55
|
+
profiler = profiler.parent
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def elapsed_ms(self) -> float:
|
|
59
|
+
if self.start_time_ms is None:
|
|
60
|
+
raise ValueError("the profiler has not been started yet")
|
|
61
|
+
if self.end_time_ms is None:
|
|
62
|
+
end_time_ms = current_time_ms()
|
|
63
|
+
else:
|
|
64
|
+
end_time_ms = self.end_time_ms
|
|
65
|
+
return end_time_ms - self.start_time_ms - self.paused_ms
|
|
66
|
+
|
|
67
|
+
def start(self):
|
|
68
|
+
self.start_time_ms = current_time_ms()
|
|
69
|
+
self.end_time_ms = None
|
|
70
|
+
|
|
71
|
+
def stop(self):
|
|
72
|
+
if self._paused_start_time_ms:
|
|
73
|
+
raise ValueError("A profiler cannot be stopped while it is paused")
|
|
74
|
+
self.end_time_ms = current_time_ms()
|
|
75
|
+
|
|
76
|
+
def __enter__(self) -> "Profiler":
|
|
77
|
+
self.start()
|
|
78
|
+
if self.parent is None and _PROFILER_STACK:
|
|
79
|
+
self.parent = _PROFILER_STACK[-1]
|
|
80
|
+
_PROFILER_STACK.append(self)
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
84
|
+
self.stop()
|
|
85
|
+
if _PROFILER_STACK[-1] is not self:
|
|
86
|
+
log.warning(f"Profiler {self!r} stopped before its children stopped!")
|
|
87
|
+
_PROFILER_STACK.remove(self)
|
|
88
|
+
else:
|
|
89
|
+
_PROFILER_STACK.pop()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Unprofiled:
|
|
93
|
+
def __init__(self, profiler: Optional[Profiler] = None):
|
|
94
|
+
self.profiler: Optional[Profiler] = profiler
|
|
95
|
+
self._had_profiler = profiler is not None
|
|
96
|
+
|
|
97
|
+
def __enter__(self):
|
|
98
|
+
if not self._had_profiler and _PROFILER_STACK:
|
|
99
|
+
self.profiler = _PROFILER_STACK[-1]
|
|
100
|
+
if self.profiler is not None:
|
|
101
|
+
self.profiler.pause()
|
|
102
|
+
|
|
103
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
104
|
+
if self.profiler is not None:
|
|
105
|
+
self.profiler.unpause()
|
|
106
|
+
if not self._had_profiler:
|
|
107
|
+
self.profiler = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def unprofiled(func):
|
|
111
|
+
@wraps(func)
|
|
112
|
+
def wrapped(*args, **kwargs):
|
|
113
|
+
with Unprofiled():
|
|
114
|
+
return func(*args, **kwargs)
|
|
115
|
+
return wrapped
|