Skip to content

Instantly share code, notes, and snippets.

@jessy-lua
Created October 15, 2021 06:27
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jessy-lua/d080b7bf499d72d2911f9bb15d9f2fc5 to your computer and use it in GitHub Desktop.
Save jessy-lua/d080b7bf499d72d2911f9bb15d9f2fc5 to your computer and use it in GitHub Desktop.
New World oodle preset for QuickBMS
# ZIP files example 0.4.11
# more info: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
# note that with some archives like those created by Stuff-it on MacOSX is
# not possible to use this script because they are wrongly built, practically
# they set the comp_size and uncomp_size fields of the "Local file header" at
# 0 and they set them only in the relative "Central directory structure" which
# means that it's necesary to read this one first for extracting the files
# contained in the local header... senseless and stupid
# script for QuickBMS http://quickbms.aluigi.org
# put the password here, it supports both ZipCrypto and AES
set ZIP_PASSWORD string ""
quickbmsver "0.7.4"
get EXE_SIGN long
goto 0
if EXE_SIGN == 0x00905a4d
get EXT extension
if EXT == "exe" || EXT == "dll"
findloc OFFSET string "PK\x03\x04"
goto OFFSET
endif
elif EXE_SIGN == 0x02014b50
findloc OFFSET binary "\x50\x4b\x03\x04"
goto OFFSET
endif
savepos OFFSET
set ZIP_SIGN short 0x0403
goto OFFSET
getdstring ZIP_CENTRAL_SEARCH 6 # PK_sign + sign + ver
goto OFFSET
get DUMMY short
get ZIP_SIGN short
math ALTERNATIVE_MODE = 0 # in reality this is the real correct mode to read the ZIP archives
math FIRST_FILE = 1
goto OFFSET
get zip_filesize asize
for offset = offset < zip_filesize
#idstring "PK\x03\x04"
get PK_sign short # so it works also with modified ZIP files!
get sign short
if sign == ZIP_SIGN # Local file header
get ver short
get flag short
get method short
get modtime short
get moddate short
get zip_crc long
get comp_size long
get uncomp_size long
get name_len short
get extra_len short
getdstring name name_len
getdstring extra extra_len
savepos offset
if FIRST_FILE != 0
math FIRST_FILE = 0
if flag & 8
if zip_crc == 0
if comp_size == 0
#if uncomp_size == 0 # needs to be commented out
goto -0x16
get PK_sign short
idstring "\x05\x06"
get disk_num short
get disk_start short
get central_entries short
get central_entries short
get central_size long
get central_offset long
get comm_len short # let's think it's zero
getdstring comment comm_len
math ALTERNATIVE_MODE = 1
math ALTERNATIVE_OFFSET = central_offset
math ALTERNATIVE_comp_size = 0
math ALTERNATIVE_uncomp_size = 0
math ALTERNATIVE_zip_crc = 0
set NAME string "/" # skip this file
math uncomp_size = 0 # skip this file
#endif
endif
endif
endif
endif
if ALTERNATIVE_MODE != 0
math comp_size = ALTERNATIVE_comp_size
math uncomp_size = ALTERNATIVE_uncomp_size
math zip_crc = ALTERNATIVE_zip_crc
endif
# zip64
if extra_len >= 20
getvarchr extra_id extra 0 short
if extra_id == 0x0001
if comp_size == 0xffffffff
getvarchr uncomp_size 4 longlong
getvarchr comp_size 12 longlong
endif
endif
endif
# possible lame tricks used by games
if comp_size & 0x80000000 # < 0
if comp_size u> zip_filesize
math comp_size ^= 0xffffffff
math uncomp_size ^= 0xffffffff
endif
endif
if name_len & 0x80000000 # < 0
math name_len ^= 0xffffffff
endif
if extra_len & 0x80000000 # < 0
math extra_len ^= 0xffffffff
endif
if flag & 1
if ZIP_PASSWORD == ""
print "the file is encrypted, you must set ZIP_PASSWORD in the script!"
#cleanexit
endif
math method_backup = method
if method == 99
getvarchr AES_EXTRA1 extra 0 short # Extra field header ID (0x9901)
getvarchr AES_EXTRA2 extra 2 short # Data size (currently 7, but subject to possible increase in the future)
getvarchr AES_EXTRA3 extra 4 short # Integer version number specific to the zip vendor
getvarchr AES_EXTRA4 extra 6 short # 2-character vendor ID
getvarchr AES_EXTRA5 extra 8 byte # Integer mode value indicating AES encryption strength
getvarchr AES_EXTRA6 extra 9 short # The actual compression method used to compress the file
math method = AES_EXTRA6
if AES_EXTRA5 == 0x01
math AES_KEY_SIZE = 8
set AES_ALGO string "ZIP_AES128"
elif AES_EXTRA5 == 0x02
math AES_KEY_SIZE = 12
set AES_ALGO string "ZIP_AES192"
elif AES_EXTRA5 == 0x03
math AES_KEY_SIZE = 16
set AES_ALGO string "ZIP_AES256"
else
print "Error: invalid AES_EXTRA5 %AES_EXTRA5%"
cleanexit
endif
getdstring AES_SALT AES_KEY_SIZE
xmath offset "offset + (AES_KEY_SIZE + 2)"
xmath comp_size "comp_size - (AES_KEY_SIZE + 2 + 10)"
strlen ZIP_PASSWORD_LEN ZIP_PASSWORD
encryption AES_ALGO ZIP_PASSWORD AES_SALT 0 ZIP_PASSWORD_LEN # long story short, set the password length or use "Set binary"
else
encryption zipcrypto ZIP_PASSWORD 1
endif
endif
if method == 0
Log name offset comp_size # was uncomp_size before AES
else
if method == 8
ComType deflate
elif method == 1
ComType shrink
elif method == 2
ComType reduce1
elif method == 3
ComType reduce2
elif method == 4
ComType reduce3
elif method == 5
ComType reduce4
elif method == 6
ComType pkware # ???
elif method == 9
ComType deflate64
elif method == 10
ComType pkware # ???
elif method == 12
ComType bzip2
elif method == 13
ComType XMemDecompress
elif method == 14
ComType lzmaefs
elif method == 15
ComType oodle
elif method == 18
ComType terse # ???
elif method == 21
ComType XMemDecompress
elif method == 28
ComType lz4f
elif method == 34
ComType brotli
elif method == 64
ComType darksector
elif method == 95
comtype LZMA2_EFS0
getdstring XZ_MAGIC 6
get XZ_FLAGS0 byte
get XZ_FLAGS byte
get XZ_CRC32 long
xmath DUMMY "1 << ((((XZ_FLAGS & 0xf) - 1) / 3) + 2)"
getdstring DUMMY DUMMY
savepos tmp
xmath comp_size "comp_size - (tmp - offset)"
math offset = tmp
elif method == 96 # compressed jpeg
ComType copy
math uncomp_size = comp_size
string name + ".jpg"
elif method == 97 # wavpack
ComType copy
math uncomp_size = comp_size
string name + ".wv"
elif method == 98
ComType ppmd
elif method == 99
ComType lzfse
else
print "unsupported compression method %method%"
cleanexit
endif
CLog name offset comp_size uncomp_size
endif
if flag & 1
encryption "" ""
if method_backup == 99
math offset += 10
endif
endif
math offset += comp_size
goto offset
if ALTERNATIVE_MODE != 0
goto ALTERNATIVE_OFFSET
endif
elif sign == 0x0806 # Archive extra data record
get extra_len long
getdstring extra extra_len
elif sign == 0x0201 # Central directory structure
get ver_made short
get ver_need short
get flag short
get method short
get modtime short
get moddate short
get zip_crc long
get comp_size long
get uncomp_size long
get name_len short
get extra_len short
get comm_len short
get disknum short
get int_attr short
get ext_attr long
get rel_offset long
getdstring name name_len
getdstring extra extra_len
getdstring comment comm_len
if ALTERNATIVE_MODE != 0
math ALTERNATIVE_comp_size = comp_size
math ALTERNATIVE_uncomp_size = uncomp_size
math ALTERNATIVE_zip_crc = zip_crc
savepos ALTERNATIVE_OFFSET
goto rel_offset
endif
elif sign == 0x0505 # Digital Signature
get sign_len long
getdstring sign sign_len
elif sign == 0x0606 # Zip64 end of central directory record
get dir_record longlong
get ver_made short
get ver_need short
get num_disk long
get num_disk2 long
get tot_entries longlong
get tot_entries2 longlong
get central_size longlong
get central_offset longlong
print "Error: zip64 extensible data sector not implemented, contact me"
cleanexit
elif sign == 0x0706 # Zip64 end of central directory locator
get start_central long
get end_central longlong
get disks long
elif sign == 0x0605 # End of central directory record
get disk_num short
get disk_start short
get central_entries short
get central_entries short
get central_size long
get central_offset long
get comm_len short
getdstring comment comm_len
elif sign == 0x0807 # Data Descriptor
get zip_crc long
get comp_size long
get uncomp_size long
elif sign == 0x3030 # disk spanning
# nothing?
else
# A ZIP archive should be read from the end and not sequentially because in some rare cases
# we may have some "gaps" between the various directories, this is a basic way to guess
# the beginning of the next directory
# ZIP_CENTRAL_SEARCH contains zeroes that will not be considered, not a problem
print "...search ZIP signature..."
findloc NEW_OFFSET binary ZIP_CENTRAL_SEARCH ""
if NEW_OFFSET == ""
xmath COVERAGE "offset / (zip_filesize / 100)" # "(offset*100)/zip_filesize" may go in overflow on 32bit
set COVERAGE_OK string "not fully covered, lot of data remaining"
if COVERAGE >= 90
set COVERAGE_OK string "fully covered, probably no remaining data"
endif
print "\nError: unknown ZIP signature %sign|x% at offset %offset|x%\n if the other files have been extracted correctly it's all ok,\n maybe this is just the end of file:\n\n OFFSET %offset|x%\n ZIP SIZE %zip_filesize|x%\n COVERAGE %COVERAGE% / 100 (%COVERAGE_OK%)"
cleanexit
endif
goto NEW_OFFSET
endif
savepos offset
next
@pmqs
Copy link

pmqs commented Nov 9, 2022

@jessy-lua do you happen to remember the source of the compression methods that aren't in either APNOTE or the infozip equivalent.

In particular,

  • 13 ComType XMemDecompress
  • 14 lzmaefs
  • 15 oodle
  • 21 XMemDecompress
  • 28 lz4f
  • 34 brolti
  • 64 darksector

@jessy-lua
Copy link
Author

@jessy-lua do you happen to remember the source of the compression methods that aren't in either APNOTE or the infozip equivalent.

In particular,

  • 13 ComType XMemDecompress
  • 14 lzmaefs
  • 15 oodle
  • 21 XMemDecompress
  • 28 lz4f
  • 34 brolti
  • 64 darksector

I even don't know what this means, sorry 🤷‍♂️

@pmqs
Copy link

pmqs commented Nov 10, 2022

No worries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment