def dbupdate(cls, src, pdestdir): ''' when given a directory of updates (CABs/MSUs/ZIPs) and no extraction, only set up update files to be added to dbc. Destination is where patched files are. ''' extlogger = logging.getLogger("BAM.Pools.ExWkr") logmsg = "[EXMGR][DBUP] starting on " + str(src) extlogger.log(logging.DEBUG, logmsg) # initialize deliverables deliverables = None newpath = '' # indicates that this cab is one of the new update version that MS started using for v1809 and forward # can't handle this type of update yet, so skip it. if "PSFX" in src or "psfx" in src: return deliverables hashes = getfilehashes(src) if hashes is None: return hashes if not (validatecab(str(src)) or ispe(str(src)) or validatezip(str(src))): logmsg = "[EXMGR][DBUP] invalid cab/pe/zip" extlogger.log(logging.DEBUG, logmsg) return deliverables newname = src.split("\\")[-1].lstrip() newpath = pdestdir + "\\" + newname if ".exe" in newname: newpath = newpath.split(".exe")[0] elif ".cab" in newname: newpath = newpath.split(".cab")[0] elif ".zip" in newname: newpath = newpath.split(".zip")[0] deliverables = ((newpath, []), hashes[0], hashes[1]) # No need to locate nested CABs/MSUs as long the parent update file # is found. Revisit if needed logmsg = "[EXMGR][DBUP] Extraction (DB update only) task completed for " + src extlogger.log(logging.DEBUG, logmsg) # Send the job to the next manager (DB will be updated eventually) return deliverables
def cleantask(cls, jobfile, updateid): ''' task to clean up folder before submitting jobs for symbol search if item is removed in cleaning, None is returned, else return item ''' clnlogger = logging.getLogger("BAM.Pools.ClnWkr") results = None logmsg = "[CLNMGR] Starting on " + str(jobfile) clnlogger.log(logging.DEBUG, logmsg) if ispe(jobfile): # check db to see if job already exists: hashes = getfilehashes(jobfile) if hashes is None: return hashes if wsuse_db.dbentryexistwithsymbols(globs.DBCONN.cursor(), \ globs.PATCHEDFILESDBNAME, hashes[0], hashes[1]): # if PE is already in db with symbols obtained, # do not retask job to symbol manager, return None instead return results else: pass logmsg = "[CLNMGR] continuing forward with " + str(jobfile) clnlogger.log(logging.DEBUG, logmsg) # getting to this point means item is not in db, may need to come up # with case where db needs to update item though infolist = { 'OriginalFilename': '', 'FileDescription': '', 'ProductName': '', 'Comments': '', 'CompanyName': '', 'FileVersion': '', 'ProductVersion': '', 'IsDebug': '', 'IsPatched': '', 'IsPreReleased': '', 'IsPrivateBuild': '', 'IsSpecialBuild': '', 'Language': '', 'PrivateBuild': '', 'SpecialBuild': '' } try: unpefile = pefile.PE(jobfile, fast_load=True) except pefile.PEFormatError as peerror: logmsg = "[WSUS_DB] skipping " + str(jobfile) + " due to exception: " + peerror.value clnlogger.log(logging.ERROR, logmsg) return results infolist['fileext'], infolist['stype'] = pebinarytype(unpefile) infolist['arch'] = getpearch(unpefile) infolist['age'] = getpeage(unpefile) infolist['strippedpe'] = ispedbgstripped(unpefile) infolist['builtwithdbginfo'] = ispebuiltwithdebug(unpefile) direntires=[ pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'], \ pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'] ] unpefile.parse_data_directories(directories=direntires) infolist['pdbfilename'] = getpepdbfilename(unpefile) infolist['signature'] = getpesigwoage(unpefile) # a PE only have 1 VERSIONINFO, but multiple language strings # More information on different properites can be found at # https://msdn.microsoft.com/en-us/library/windows/desktop/aa381058 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa381049 if getattr(unpefile, "VS_VERSIONINFO", None) is not None and \ getattr(unpefile, "FileInfo", None) is not None: for fileinfoentries in unpefile.FileInfo: for fileinfoentry in fileinfoentries: if getattr(fileinfoentry, "StringTable", None) is not None: for strtable in fileinfoentry.StringTable: # Currently only handling unicode en-us if strtable.LangID[:4] == b'0409' or \ (strtable.LangID[:4] == b'0000' and (strtable.LangID[4:] == b'04b0' or strtable.LangID[4:] == b'04B0')): infolist["Language"] \ = strtable.LangID.decode("utf-8") for field, value in strtable.entries.items(): dfield = field.decode('utf-8') dvalue = value.decode('utf-8') if dfield == "OriginalFilename": infolist["OriginalFilename"] \ = dvalue if dfield == "FileDescription": infolist["FileDescription"] \ = dvalue if dfield == "ProductName": infolist["ProductName"] \ = dvalue if dfield == "Comments": infolist["Comments"] \ = dvalue if dfield == "CompanyName": infolist["CompanyName"] \ = dvalue if dfield == "FileVersion": infolist["FileVersion"] \ = dvalue if dfield == "ProductVersion": infolist["ProductVersion"] \ = dvalue if dfield == "IsDebug": infolist["IsDebug"] \ = dvalue if dfield == "IsPatched": infolist["IsPatched"] \ = dvalue if dfield == "IsPreReleased": infolist["IsPreReleased"] \ = dvalue if dfield == "IsPrivateBuild": infolist["IsPrivateBuild"] \ = dvalue if dfield == "IsSpecialBuild": infolist["IsSpecialBuild"] \ = dvalue if dfield == "PrivateBuild": infolist["PrivateBuild"] \ = dvalue if dfield == "SpecialBuild": infolist["SpecialBuild"] \ = dvalue # Get the OS this PE is designed towards. # Microsoft PE files distributed via Microsoft's Updates typically # use the ProductVersion file properties to indicate the OS a specific # PE file is built towards. # If this is a Microsoft binary, the Product version is typically # the OS version it was built towards, but other products this is not # necessarily true if infolist['ProductName'].find("Operating System") != -1: infolist['osver'] = "NT" + infolist['ProductVersion'] else: infolist['osver'] = "UNKNOWN" unpefile.close() results = ((str(jobfile), updateid), hashes[0], hashes[1], infolist) else: # if jobfile is not a PE, then check if it's a cab. If not a cab, remove it. if not validatecab(str(jobfile)): logmsg = "[CLNMGR] cleantask: Removing " + str(jobfile) clnlogger.log(logging.DEBUG, logmsg) rmfile(jobfile) logmsg = "[CLNMGR] " + str(jobfile) + " removed, not PE or cab file" clnlogger.log(logging.DEBUG, logmsg) else: pass logmsg = "[CLNMGR] " + str(jobfile) + " is nested cab, skipping" clnlogger.log(logging.DEBUG, logmsg) return results logmsg = "[CLNMGR] completed one cleantask for " + str(jobfile) clnlogger.log(logging.DEBUG, logmsg) return results
def extracttask(cls, src, pdir, dst): ''' task for workers to extract contents of .cab file and return directory of result to for use by cleaner ''' extlogger = logging.getLogger("BAM.Pools.ExWkr") hashes = getfilehashes(src) if hashes is None: return hashes entryexists = False if cls.verifyentry(src, hashes[0], hashes[1], extlogger): entryexists = True logmsg = "[EXMGR] started on " + str(src) + " extracting files to " + str(dst) extlogger.log(logging.DEBUG, logmsg) # initialize deliverables deliverables = None # indicates that this cab is one of the new update version that MS started using for v1809 and forward # can't handle this type of update yet, so skip it. if "PSFX" in src or "psfx" in src: return deliverables newname = src.split("\\")[-1].lstrip() # If the files being worked on is a PE file # see if it can be opened with 7z.exe and that # it has PE files. Otherwise, skip to other # update files. if ispe(src): logmsg = "[EXMGR] extracting PE file (" + src + ")..." extlogger.log(logging.DEBUG, logmsg) newdir = (dst + "\\" + newname).split(".exe")[0] try: os.mkdir(newdir) except FileExistsError: pass except OSError as oserror: logmsg = "[EXMGR] OSError creating new directory... skipping extraction for (" + \ src + "). Error: " + str(oserror) extlogger.log(logging.ERROR, logmsg) return deliverables if not entryexists and cls.perform7zextract(src, newdir, extlogger) is None: return deliverables deliverables = ((newdir, []), hashes[0], hashes[1]) # if nothing was extracted, remove the directory to clean up try: os.rmdir(newdir) except OSError: pass else: if not validatecab(str(src)): logmsg = "[EXMGR] {-} invalid file: " + src extlogger.log(logging.ERROR, logmsg) return None # make new directory to hold extracted files newdir = "" # if this is true, this must be a nested cab file if dst in src: newdir = str(os.path.dirname(src)) # otherwise the cab is brand new and should create a newdir in dst else: if ".cab" in newname: newdir = (dst + "\\" + newname).split(".cab")[0] elif ".msu" in newname: newdir = (dst + "\\" + newname).split(".msu")[0] try: os.mkdir(newdir) except FileExistsError: pass except OSError as oserror: logmsg = "[EXMGR] OSError creating new directory... skipping extraction for (" + \ src + "). Error: " + str(oserror) extlogger.log(logging.ERROR, logmsg) return deliverables if not entryexists: # extract .dll, .exe and .sys first cls.performcabextract("*.dll", src, newdir, extlogger) cls.performcabextract("*.exe", src, newdir, extlogger) cls.performcabextract("*.sys", src, newdir, extlogger) deliverables = ((newdir, []), hashes[0], hashes[1]) # search through rest of .cab for nested cabs or msus to extract # again if not entryexists: listing = cls.performcablisting(src, extlogger) if listing is None: return deliverables stroutput = listing.decode("ascii").split("\r\n") # indicates that this cab is one of the new update version that MS started using for v1809 and forward # can't handle this type of update yet, so skip it. if "psfx" in stroutput[3] or "PSFX" in stroutput[4]: return deliverables for line in stroutput: if line.endswith(".cab") or line.endswith(".msu"): # expand that line only to start another thread on it potentialfile = line.split(":")[-1].lstrip() # make a new directory to store the nested cab # nested cabs with the same name may exists, keep contents # under the newly created extracted directory for update parentdir = src.split("\\")[-1][0:-4] ncabdir = str(dst) + "\\" + str(parentdir) + "\\" + str(potentialfile)[0:-4] if not os.path.exists(ncabdir): try: os.mkdir(ncabdir) ncabdir = Path(ncabdir).resolve() except OSError as error: logmsg = "[EXMGR] {-} unable to make nested cab directory: " + str(error) extlogger.log(logging.ERROR, logmsg) break logmsg = "[EXMGR] beginning extraction of nestedcab: " + str(src) extlogger.log(logging.DEBUG, logmsg) extractstdout = cls.performcabextract(potentialfile, src, str(ncabdir), extlogger) if extractstdout is not None: # Case where there exists nested cabs with a .manifest file newpath = None for root, dummy, cabs in os.walk(ncabdir): for cab in cabs: if str(cab) == potentialfile: newpath = Path(os.path.join(root, cab)).resolve() break if newpath is None: continue # if file is not a cab/msu, remove it since that's all we're interested # in at this point if not validatecab(str(newpath)): logmsg = "[EXMGR] {-} extracttask: " + str(newpath) + " extracted from " + str(src) + " is not a validate cab" extlogger.log(logging.ERROR, logmsg) logmsg = "[EXMGR] extracttask: Removing " + str(newpath) extlogger.log(logging.ERROR, logmsg) rmfile(newpath) continue logmsg = "[EXMGR] Creating " + str(newpath) + " for new thread..." extlogger.log(logging.DEBUG, logmsg) # return new location of extracted cab for addition to job queue deliverables[0][1].append(str(newpath)) logmsg = "[EXMGR] Extraction task completed for " + src extlogger.log(logging.DEBUG, logmsg) return deliverables