1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#!/usr/bin/env python3
"""
__author__ = "Axelle Apvrille"
__status__ = "In progress"
__license__ = "MIT License"
"""
import zipfile
import rarfile
import re
import droidutil
import struct
import subprocess
import logging
logging.basicConfig(format='%(levelname)s:%(filename)s:%(message)s', level=logging.INFO)
class droidziprar:
def __init__(self, archive, zipmode=True, verbose=False):
"""Returns the archive file handle - don't forget to close it once you've finished with the file"""
if verbose:
logging.getLogger().setLevel(logging.DEBUG)
self.zipmode = zipmode # True for a zip, False for a Rar
self.password = 'infected'
self.handle = None
self.archive_name = archive
# File type is not fully certain until get_type() has been called
if self.zipmode:
self.filetype = droidutil.ZIP
else:
self.filetype = droidutil.RAR
self.open(archive)
def open(self, filename):
try:
if self.zipmode:
logging.debug("Opening Zip archive "+filename)
self.handle = zipfile.ZipFile(filename, 'r')
else:
logging.debug("Opening Rar archive "+filename)
self.handle = rarfile.RarFile(filename, 'r')
except (struct.error, zipfile.BadZipfile, zipfile.LargeZipFile, IOError) as e:
logging.debug("Exception caught in ZipFile: %s" % (repr(e)))
self.handle = None
return self.handle
def get_type(self):
"""A ZIP file can be really an APK, a JAR, or a real ZIP
Calling this function reallys sets the appropriate type for the archive.
Returns:
- the file type: APK, CLASS or ZIP
- a list of inner zips/rars if any
"""
assert self.handle is not None, "zip/rar file handle has been closed"
innerzips = [] # default value
files_in_zip = self.handle.namelist()
# guess file type based on contents
if [x for x in files_in_zip if re.search("classes\.dex|AndroidManifest\.xml|resources\.arsc|assets/|res/", x)]:
self.filetype = droidutil.APK
else:
innerzips = [x for x in files_in_zip if re.search("\.apk|\.zip|\.rar", x)]
if innerzips:
if self.zipmode:
self.filetype = droidutil.ZIP
else:
self.filetype = droidutil.RAR
else:
if [x for x in files_in_zip if re.search("\.class", x)]:
self.filetype = droidutil.CLASS
return self.filetype, innerzips
def extract_one_file(self, filename, outdir):
"""Extracts from a ZIP or a RAR
- file: file to extract
- outdir: directory to extract to
Will use a password if necessary
"""
if self.zipmode:
# the unzip command works better... (manages to unzip more)
subprocess.call(["/usr/bin/unzip", "-o", "-qq", "-P",
self.password, "-d", outdir, self.archive_name, filename])
else:
assert self.handle is not None, "zip/rar file handle has been closed"
# beware this may raise errors KeyError, RuntimeError...
self.handle.extract(filename, outdir, pwd=self.password)
def extract_all(self, outdir):
if self.zipmode:
print('Launching unzip process on %s with output dir=%s' % (self.archive_name, outdir))
subprocess.call(["/usr/bin/unzip", "-o", "-qq", "-P", self.password, self.archive_name, "-d", outdir])
else:
assert self.handle is not None, "zip/rar file handle has been closed"
self.handle.extractall(path=outdir, pwd=self.password)
def extract_pattern(self, outdir, pattern):
"""Unzips (or unrars) files matching a given regexp to a given output directory.
Returns a list of what has been unzipped.
"""
assert self.handle is not None, "zip/rar file handle has been closed"
all_files = self.handle.namelist()
list = [x for x in all_files if re.search(pattern, x)]
for file in list:
self.extract_one_file(file, outdir)
return list
def get_date(self, filename):
"""Gets the time at which filename was created"""
assert self.handle is not None, "zip/rar file handle has been closed"
try:
metainfo = self.handle.getinfo(filename)
return metainfo.date_time
except (KeyError, rarfile.NoRarEntry) as e:
logging.debug("%s does not exist in %s" % (filename, self.archive_name))
return None
def close(self):
if self.handle is not None:
pass
else:
self.handle.close()
logging.debug("Closing archive: " + self.archive_name)
|