def symtask(cls, jobitem, symserver, symdest, symlocal): ''' perform symbol search for symbols of jobfile. If there are no symbols or symbols found are already in db, discard results. Else, return found symbols ''' symlogger = logging.getLogger("BAM.Pools.SymWkr") jobfile = jobitem[0] hashes = (jobfile[1], jobfile[2]) result = None logmsg = "[SYMMGR].. Getting SYM for (" + str(jobfile) + ")" symlogger.log(logging.DEBUG, logmsg) servers = "" if symlocal: servers = " \""+ symdest + "\"" else: servers = "u \"srv*" + symdest + "*" + symserver +"\"" args = (".\\tools\\x64\\symchk.exe /v \"" + str(jobfile) + "\" /s" + servers + " /od") try: with subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) \ as psymchk: # communicate used as symchk's output is for one file and # is not "large or unlimited" pstdout, pstderr = psymchk.communicate() stdoutsplit = str(pstdout.decode("ascii")).split("\r\n") stderrsplit = str(pstderr.decode("ascii")).split("\r\n") logmsg = "[SYMMGR] Attempt to obtain symbols for " + str(jobfile) + " complete" symlogger.log(logging.DEBUG, logmsg) infolist = {} try: unpefile = pefile.PE(jobfile) except pefile.PEFormatError as peerror: logmsg = "[WSUS_DB] Caught: PE error " + str(peerror) + ". File: " + jobfile symlogger.log(logging.ERROR, logmsg) return result infolist['signature'] = getpesigwoage(unpefile) infolist['arch'] = getpearch(unpefile) unpefile.close() stderrsplit.append(symserver) result = ((str(jobfile), stderrsplit, stdoutsplit), hashes[0], hashes[1], infolist) except subprocess.CalledProcessError as error: logmsg = "[SYMMGR] {-} symchk failed with error: " + str(error) + ". File: " + jobfile symlogger.log(logging.ERROR, logmsg) result = None except FileNotFoundError as error: logmsg = ("[SYMMGR] {-} symchk.exe not found") symlogger.log(logging.ERROR, logmsg) result = None logmsg = "[SYMMGR] completed symtask for " + str(jobfile) symlogger.log(logging.DEBUG, logmsg) return result
def ispe(file): ''' checks for valid PE file ''' try: petemp = pefile.PE(file, fast_load=False) petemp.close() except (pefile.PEFormatError, IOError): return False return True
def ispebuiltwithdebug(pebinary): ''' check to see if PE was built with debug symbols in a pdb file ''' peitem = pefile.PE(pebinary) debugsize = peitem.dump_dict()['Directories'][6]['Size']['Value'] peitem.close() if debugsize == 0: return False return True
def ispedbgstripped(file): ''' checks to see if PE debug information was stripped and placed into dbg file. ''' unknownpefile = pefile.PE(file) filehdr = getattr(unknownpefile, "FILE_HEADER", None) if filehdr is not None: characteristics = getattr(unknownpefile, "Characteristics", None) unknownpefile.close() if characteristics is not None: # https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_file_header # IMAGE_FILE_DEBUG_STRIPPED if (characteristics & 0x0200) == 0x0200: return True return False
def findbannedapis(file): global _pbanlogger _pbanlogger.log( logging.DEBUG, "[PBAN] Working on " + file + " for Banned APIs verification") bannedapis = getbannedapis() if bannedapis is None: logmsg = ("[PBAN] {-} Skipping Banned Analysis.") _pbanlogger.log(logging.DEBUG, logmsg) return basename = os.path.basename(file) pe_file = None try: pe_file = pefile.PE(file) except pe_file.PEFormatError as peerror: logmsg = ( "[PBAN] {-} Skipping DB insertion. Issue with handling PE file" + str(peerror.value)) _pbanlogger.log(logging.DEBUG, logmsg) return hashes = getfilehashes(file) if hashes is None: _pbanlogger.log(logging.DEBUG, "[PBSK] Error getting hashes for " + file) return dbcursor = DBCONN2.cursor() dbcursor.execute("BEGIN TRANSACTION") if hasattr(pe_file, 'DIRECTORY_ENTRY_IMPORT'): for module in pe_file.DIRECTORY_ENTRY_IMPORT: for importm in module.imports: if importm.name is not None and importm.name.decode( 'ascii') in bannedapis: mname = module.dll.decode('ascii') fn = importm.name.decode('ascii') try: dbcursor.execute( "INSERT INTO " + "BannedApiFiles" + " VALUES (" + "?," * 5 + "?)", # FileName, SHA256, SHA1, ModuleName, BannedApiUsed ( basename, hashes[0], hashes[1], mname, fn, # timestamp str(time()))) except sqlite3.Error as error: _pbanlogger.log(logging.DEBUG, ( "[PBSK] INSERT ConfigurationNotifications error (incomplete): " + error.args[0])) dbcursor.execute("END TRANSACTION") _pbanlogger.log(logging.DEBUG, ("[PBAN] Completed " + file)) dbcursor.close()
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