def create_temp_file(temp_path, temp_data): try: with tempfile.NamedTemporaryFile(dir=temp_path, delete=True) as tmpfile: temp_file_name = tmpfile.name if writefile(temp_file_name, temp_data): return temp_file_name else: raise FileIOError( "Failed Create Temp File. ({})".format(temp_file_name)) except FileIOError as e: Log.error(e.msg) return "" except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def __queuing__(self): """[summary] 큐잉용 모니터링시 사용할 폴더를 생성한다. Returns: {bool} -- [desciption] 큐잉용 폴더 생성 여부 """ try: if config.queue_monitor: for name, dirPath in config.dir_q_monitor.items(): dirName = os.path.join(config.temp_path, dirPath) if not utils.makedirectory(dirName): raise WorkError( "Failed Create {} for Monitoring.".format(name)) return True except WorkError as e: Log.error(e.msg) return False except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def readData(endian, data, data_type, unit, offset, size): # b signed char integer 1 (1),(3) # B unsigned char integer 1 (3) # H unsigned short integer 2 (3) # i int integer 4 (3) # I unsigned int integer 4 (3) # L unsigned long integer 4 (3) # Q unsigned long long integer 8 (2), (3) # s char[] bytes try: result = struct.unpack("{}{}{}".format(endian, unit, data_type), data[offset:offset + size])[0] if data_type in ["s"]: return convert_ToUTF8(result) else: return result except FileIOError as e: Log.error(e.msg) return None except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return None finally: pass
def get_jobs(args): try: jobs = [] # Job을 가져온다. # - 폴더/파일에 대한 Job if args.file or args.folder: jobs += utils.get_file_list(args.file, args.folder) # - MWS에 대한 Job if config.mws_flag: json_list = mws.get_tag_search(args.mws_tag, args.mws_start_time, args.mws_end_time, args.mws_limit) if not json_list == []: jobs += mws.get_file_list_download(json_list) return jobs except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return [] finally: pass
def get_streams(self, scanObject): """[summary] 분석 대상 파일에서 Stream을 추출하고 이를 <temp_path>\\<"Embedded"> 에 저장한다. * 반환 결과 [ { "fileName" : "", "internal_path" : [] }, ..... ] Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 Returns: {list} -- [description] 추출된 Stream 목록 """ try: # 분석 대상 파일명을 가져온다. fileName = scanObject.get_file_name() # 분석 대상 파일에서 Stream을 추출한다. return kernel.extract(fileName) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return [] finally: pass
def __clear__(self): """[summary] 임시 폴더를 정리한다. * clear_temp : 임시 폴더 정리 여부 * clear_q_monitor : 임시 폴더 내 분석 모니터링용 폴더 정리 여부 """ try: if config.clear_temp: fileList = utils.get_dir_list(config.temp_path) for fileName in fileList: if not config.clear_q_monitor: if os.path.basename( fileName) in config.dir_q_monitor.keys(): continue if utils.is_file(fileName): utils.deletefile(fileName) else: utils.deletedirectory(fileName) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def _yara_group(yaraList): """[summary] Yara 룰 파일 목록을 List 타입에서 dict 타입으로 변환한다. 변환된 dict 형식으로 컴파일한다. Arguments: yaraList {list} -- [description] Yara 룰 파일 목록 Returns: {dict} -- [description] dict 로 변환된 Yara 룰 파일 목록 """ try: return { os.path.basename(yara_file).split(".")[0]: yara_file for yara_file in yaraList } except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return {} finally: pass
def get_file_path(rootPath="", fileName=""): """[summary] 파일의 절대 경로를 생성한다. Keyword Arguments: rootPath {str} -- [description] (optional) Root 폴더 경로 (default: {""}) fileName {str} -- [description] 파일명 (default: {""}) Returns: {str} -- [description] 파일 절대 경로 """ try: abs_file_path = get_abs_file_path(rootPath, fileName) if not is_exists(abs_file_path): raise UtilityError("Not Exists: {}".format(abs_file_path)) if not is_file(abs_file_path): raise UtilityError("Not File: {}".format(abs_file_path)) return abs_file_path except UtilityError as e: Log.error(e.msg) return "" except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def get_file_path_in_folder(dirName): """[summary] 폴더 내 파일목록을 가져온다. Arguments: dirName {[type]} -- [description] 폴더 경로 Returns: {list} -- [description] 폴더내 파일 목록 """ try: fileList = [] # 폴더내 전체 목록을 가져온다. for root, dirs, files in os.walk(dirName): # 파일 목록 중 ".", ".." 를 제외한다. files = [f for f in files if not f[0] == "."] # 파일 목록을 생성한다. for fileName in files: abs_file_path = get_file_path(rootPath=root, fileName=fileName) if abs_file_path: fileList.append(abs_file_path) return fileList except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return [] finally: pass
def __is_filtered_by_format__(fformat): """[summary] 파일 포멧 기반으로 필터링한다. Arguments: fformat {dict} -- [description] 파일 포멧 Returns: {bool} -- [description] 필터링 여부 (True : 필터 대상, False : 분석 대상) """ try: filtered_format = config.filter_format if fformat.file_type in filtered_format: raise FilterError("exception type by file_type") if fformat.name in filtered_format: raise FilterError("exception type by yara_name") return False except FilterError as e: Log.error(e.msg) return True except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return True finally: pass
def _get_properties(self, data, dict_value): try: value = self.__format__(data, dict_value) return { "value" : value, "is_arch" : (value & 0x00000001), "is_password" : (value & 0x00000002), "is_distributed" : (value & 0x00000004), "is_save_script" : (value & 0x00000008), "is_drm" : (value & 0x00000010), "is_template" : (value & 0x00000020), "is_history" : (value & 0x00000040), "is_signature" : (value & 0x00000080), "is_certificate" : (value & 0x00000100), "is_reserve_sign" : (value & 0x00000200), "is_cert_drm" : (value & 0x00000400), "is_ccl" : (value & 0x00000800), "reserved" : (value & 0xFFFFF000) } except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return None finally: pass
def __parse__(self, obj): try: self.name = obj.name self.namelength = obj.namelength self.createTime = obj.createTime self.modifyTime = obj.modifyTime self.color = obj.color self.entry_type = obj.entry_type self.sid = obj.sid self.sid_left = obj.sid_left self.sid_right = obj.sid_right self.sid_child = obj.sid_child self.size = obj.size except AttributeError: # obj.name 이 NoneType으로 나오는 경우 # Directory Padding을 위해 추가된 Null 데이터. pass except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def deletefile(fileName): """[summary] 전체 경로의 파일을 삭제한다. Arguments: fileName {str} -- [description] 파일 전체 경로 """ try: if is_file(fileName) and is_exists(fileName): os.remove(fileName) return True else: raise FileIOError("{} is not file or not exists.".format(fileName)) except FileIOError as e: Log.error(e.msg) return False except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def movefile(src, dst): """[summary] 파일을 이동시킨다. Arguments: src {str} -- [description] 원본 파일 경로 dst {str} -- [description] 이동할 파일 경로 Returns: {bool} -- [description] 파일 이동 여부 """ try: dirName = os.path.dirname(dst) if not os.path.exists(dirName): if not makedirectory(dirName): raise FileIOError("Failed MakeDirectory ({})".format(dirName)) shutil.move(src, dst) return True except FileIOError as e: Log.error(e.msg) return False except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def deletedirectory(dirName): """[summary] 전체 경로의 폴더를 삭제한다. Arguments: dirName {str} -- [description] 폴더 전체 경로 """ try: if is_dir(dirName) and is_exists(dirName): shutil.rmtree(dirName) return True else: raise FileIOError( "{} is not directory or not exists.".format(dirName)) except FileIOError as e: Log.error(e.msg) return False except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def __run__(self, tasks_queue, stop_flag, queue_wait): """[summary] 사용자가 작성할 run() 함수의 Wrapper Arguments: tasks_queue {instnace} -- [description] 작업 큐 stop_flag {instance} -- [description] 프로세스 종료여부 Flag queue_wait {int} -- [description] 다음 Queue 확인까지 대기 시간 """ try: # 전처리 # Log 초기화 Log.init(log_name="work", log_level=config.log_level, log_path=config.log_path, log_cmd=config.log_cmd) # 처리 self.run(tasks_queue, stop_flag, queue_wait) # 후처리 except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def get_abs_file_path(rootPath, fileName): """[summary] 파일의 절대 경로를 생성한다. Arguments: rootPath {[str]]} -- [description] (optional) Root 폴더 경로 fileName {[str]} -- [description] 파일명 Returns: {str} -- [description] 파일 절대 경로 """ try: if rootPath: return os.path.join(rootPath, fileName) else: return os.path.abspath(fileName) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def __parse_root__(self, scanObject): """[summary] ScanObject 대상(Root 파일)을 분석한다. 분석 결과는 ScanObject.Children 에 저장된다. * 저장형태 : Children = { <uuid> : { "child_name" : "", "internal_path" : [], "priority" : <int> }, ..... } Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 """ error = False err_msg = "" try: # 분석 대상의 Directory 정보를 저장한다. if not self.get_directories_info(scanObject): raise KeiEngineError("Failed get directory info.") # 구조를 분석해 Stream을 파일로 저장한다. streams = self.get_streams(scanObject) # 생성한 Stream을 Children에 업데이트 한다. for i, stream in enumerate(streams): if stream.get("internal_path", [])[-1] in config.ole_reference: priority = config.PRIORITY_LEVEL.get("LEVEL_HIGH", 0) else: priority = config.PRIORITY_LEVEL.get("LEVEL_NORMAL", 0) scanObject.updateChildren(stream.get("fileName", ""), stream.get("internal_path", []), priority) except KeiEngineError as e: error = True err_msg = e.msg except: # 에러 로그를 저장한다. _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) # 에러 결과를 ScanObject에 업데이트할 수 있도록 상태값을 변경한다. error = True err_msg = msg finally: scanObject.updateResult(error, err_msg)
def get_directories_info(self, scanObject): """[summary] 분석 대상 파일에서 Directory 정보를 추출해 Struct 에 저장한다. * 저장 경로 : scanObject.fformat.struct[<directory name>] = {"header" : instance} Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 Returns: {bool} -- [description] 정상 처리 여부 """ try: # 분석 대상 파일명을 가져온다. fileName = scanObject.get_file_name() # Directory 목록을 가져온다. directories = kernel.get_directories(fileName) for directory in directories: # 개별 Directory 정보를 추출한다. instance = oledirectory.OleDirectoryObject(directory) # 추출된 정보를 업데이트한다. if instance: try: new_name = kernel.convert_entryname([instance.name]) # 함수 : scanObject.updateStructure() # 위치 : scanObject.fformat.struct[new_name] = instance scanObject.updateStructure(new_name[0], instance) except AttributeError: # instance는 생성되고 instance.name 이 없는 경우 # - Padding을 위해 채워진 Directory 의 경우 pass else: raise KeiEngineError( "{} file is not ole structure.".format( os.path.basename(fileName))) return True except KeiEngineError as e: Log.error(e.msg) return False except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def __waiting__(scanObject): """[summary] 분석 대상 파일의 상태를 변경한다. * 변경 시점 : dispatch.Dispatch() 내 _get_metadata() 완료 시점 * 변경 위치 : 원본 파일 경로 -> <temp_path>\\<waiting>\\<uid>\\<fileName> Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 """ try: # 분석 대상 파일의 현재 경로를 가져온다. # src : 원본 파일 경로 src = scanObject.get_file_name() # 분석 대상 파일의 변경할 경로를 가져온다. # 위치 : <temp_path>\\<waiting>\\<uid>\\<fileName> dstDir1 = os.path.join(config.temp_path, config.dir_q_monitor.get("waiting")) if not utils.is_exists(dstDir1): utils.makedirectory(dstDir1) dstDir2 = os.path.join(dstDir1, scanObject.get_uid()) if not os.path.exists(dstDir2): utils.makedirectory(dstDir2) dst = os.path.join(dstDir2, os.path.basename(src)) # 테스트 # - 파일명 내에 한글이고 띄어쓰기가 있는 경우 dst = dst.replace(" ", "_") dst = dst.replace("\\", "\\\\") # 파일을 복사한다. if not utils.copyfile(src, dst): raise QueueMonitorError( "Failed change status to wait. ({})".format( scanObject.get_uid())) Log.debug("[ORI -> WAIT] {}".format(os.path.basename(src))) # 복사에 성공하면 분석 대상 파일의 경로를 변경한다. scanObject.update_file_name(dst) except QueueMonitorError as e: Log.error(e.msg) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def unzip(data): try: return zlib.decompress(data, -15) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def is_ole(fileName): try: return olefile.isOleFile(fileName) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def __load_module__(self, scanObject): """[summary] ScanObject 대상에 맞는 엔진을 로드하고 엔진 내 정의된 클래스 (config.engine_class) 명을 반환한다. Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 Returns: {str} -- [description] 모듈 내 클래스 명 """ error = False err_msg = "" try: Log.info(scanObject.get_internal_path()) engine_name = scanObject.get_internal_path()[0] mod = utils.load_module("{}.{}".format(config.ole_eng_path, engine_name)) if not mod: # 경로내 <엔진명>.py 가 없거나 로드 실패한 경우 raise KeiEngineError("Failed Load Module") # 엔진내 정의된 클래스명을 가져온다. clsName = getattr(mod, config.engine_class) # 있는 경우 if clsName: return clsName else: raise KeiEngineError("Not Found {} Engine Class.".format( config.engine_class)) except KeiEngineError as e: # 에러 로그를 저장한다. Log.error(e.msg) # 에러 결과를 ScanObject에 업데이트할 수 있도록 상태값을 변경한다. error = True err_msg = e.msg except: # 에러 로그를 저장한다. _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) # 에러 결과를 ScanObject에 업데이트할 수 있도록 상태값을 변경한다. error = True err_msg = msg finally: scanObject.updateResult(error, err_msg)
def save_json(result_json): try: print(result_json) return True except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass
def convert_str2utf8_for_py3(thing): try: return thing.decode("utf-8", "slashescape") except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def __analyzing__(scanObject): """[summary] 분석 대상 파일의 상태를 변경한다. * 변경 시점 : _run_module() 실행시 Skeleton.EngineProcess.__run__() 시점 * 변경 위치 : <waiting> -> <temp_path>\\<analyzing>\\<uid>\\<fileName> Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 KeyArgument: fileName {str} -- [description] 분석 대상 파일 (optional), 주로 분석 엔진에서 선분석시 요청됨. """ try: # 분석 대상 파일의 현재 경로를 가져온다. ori_fileName = scanObject.get_file_name() srcDir, fileName = os.path.split(ori_fileName) # 분석 대상 파일의 변경할 경로를 가져온다. dstDir = os.path.join(config.temp_path, config.dir_q_monitor.get("analyzing")) if not utils.is_exists(dstDir): utils.makedirectory(dstDir) # 원본 파일을 이동 시킨다. # srcDir : <temp_path>\\<waiting>\\<uid> 또는 <temp_path>\\<embedding> # dstDir : <temp_path>\\<analyzing> if not utils.movefile(srcDir, dstDir): raise QueueMonitorError( "Failed change status to analyzing. ({})".format( scanObject.get_uid())) Log.debug("[WAIT -> ANLZ] {}".format(fileName)) # 복사에 성공하면 분석 대상 파일의 경로를 변경한다. dst_fileName1 = os.path.join(dstDir, scanObject.get_uid()) dst_fileName = os.path.join(dst_fileName1, fileName) scanObject.update_file_name(dst_fileName) except QueueMonitorError as e: Log.error(e.msg) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def get_file_download(file_hash): try: params = {"api_key": config.mws_key, "hash": file_hash} response = requests.get("https://{유료 URL}/v3/file/download", params=params) return response.content except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return "" finally: pass
def __parse_child__(self, scanResult, scanObject): """[summary] ScanObject 대상(Embedded or Children) 을 분석하고 그 결과를 저장한다. * 저장 경로 : ScanObject.fformat.struct[<name>] = {"body" : instance} Arguments: scanResult {instance} -- [description] ScanResult 인스턴스 scanObject {instance} -- [description] ScanObject 인스턴스 """ error = False err_msg = "" try: # 엔진을 로드한다. clsName = self.__load_module__(scanObject) if not clsName: raise KeiEngineError("Failed Load Module") # 분석 클래스를 초기화한다. instance = clsName() # 분석한다. instance.__run__(scanResult, scanObject) # 분석 결과를 저장한다. # print(instance.to_dict()) scanObject.updateStructure(instance.__engine__, instance) except KeiEngineError as e: # 에러 로그를 저장한다. Log.error(e.msg) # 에러 결과를 ScanObject에 업데이트할 수 있도록 상태값을 변경한다. error = True err_msg = e.msg except: # 에러 로그를 저장한다. _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) # 에러 결과를 ScanObject에 업데이트할 수 있도록 상태값을 변경한다. error = True err_msg = msg finally: scanObject.updateResult(error, err_msg)
def __complete__(scanObject): """[summary] 분석 대상 파일의 상태를 변경한다. * 변경 시점 : dispatch.Dispatch() 내 분석 완료 시점 * 변경 위치 : <analyzing> -> <temp_path>\\<complete>\\<uid>\\<fileName> Arguments: scanObject {instance} -- [description] ScanObject 인스턴스 """ try: # 분석 대상 파일의 현재 경로를 가져온다. ori_fileName = scanObject.get_file_name() srcDir, fileName = os.path.split(ori_fileName) # 분석 대상 파일의 변경할 경로를 가져온다. dstDir = os.path.join(config.temp_path, config.dir_q_monitor.get("complete")) if not utils.is_exists(dstDir): utils.makedirectory(dstDir) # 원본 파일을 이동 시킨다. # srcDir : <temp_path>\\<waiting>\\<uid> # dstDir : <temp_path>\\<analyzing> if not utils.movefile(srcDir, dstDir): raise QueueMonitorError( "Failed change status to complete. ({})".format( scanObject.get_uid())) Log.debug("[ANLZ -> CMPT] {}".format(fileName)) # 복사에 성공하면 분석 대상 파일의 경로를 변경한다. dst_fileName1 = os.path.join(dstDir, scanObject.get_uid()) dst_fileName = os.path.join(dst_fileName1, fileName) scanObject.update_file_name(dst_fileName) except QueueMonitorError as e: Log.error(e.msg) except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) finally: pass
def makedirectory(dirName): try: if not os.path.exists(dirName): os.mkdir(dirName) return True except: _, msg, obj = sys.exc_info() msg = "{} ({}::{})".format(msg, obj.tb_lineno, obj.tb_frame.f_globals.get("__file__")) Log.error(msg) return False finally: pass