def __EGG_Header__(self, data): try: # magic = struct.unpack('<L', data[0:4])[0] magic = kavutil.get_uint32(data, 0) if magic != 0x41474745: raise SystemError # version = struct.unpack('<H', data[4:6])[0] version = kavutil.get_uint16(data, 4) if version != 0x0100: raise SystemError # header_id = struct.unpack('<L', data[6:10])[0] header_id = kavutil.get_uint32(data, 6) if header_id == 0: raise SystemError # reserved = struct.unpack('<L', data[10:14])[0] reserved = kavutil.get_uint32(data, 10) if reserved != 0: raise SystemError return 0 except SystemError: pass return -1
def unarc(self, arc_engine_id, arc_name, fname_in_arc): if arc_engine_id == 'arc_icon': mm = self.__get_handle(arc_name) num = kavutil.get_uint16(mm, 4) p = p_name.search(fname_in_arc) if p: fw = int(p.groups()[0]) fh = int(p.groups()[1]) # fc = int(p.groups()[2]) for i in range(num): off = 6 + (16 * i) w = ord(mm[off]) h = ord(mm[off + 1]) # c = kavutil.get_uint16(mm, off+6) if w == fw and h == fh: # and c == fc: img_size = kavutil.get_uint32(mm, off + 8) img_off = kavutil.get_uint32(mm, off + 12) data = mm[img_off:img_off + img_size] return data return None
def __EGG_Dummy_Header_Size__(self, data): try: # dummy__size = struct.unpack('<H', data[5:7])[0] dummy__size = kavutil.get_uint16(data, 5) return 7 + dummy__size # (5 + 2 + dummy__size) except: pass return -1
def format(self, filehandle, filename, filename_ex): ret = {} mm = filehandle if mm[0:4] == '\x00\x00\x01\x00': # 헤더 체크 ret['ff_icon'] = kavutil.get_uint16(mm, 4) return ret return None
def __Alz_LocalFileHeader__(self, data): try: fname_size = kavutil.get_uint16(data, 4) file_desc = ord(data[11]) compress__method__m = ord(data[13]) size = 19 if file_desc & 0x10: compress__size = ord(data[size]) uncompress__size = ord(data[size + 1]) size += (1 * 2) # 파일 크기가 2개 옴(압축전, 압축 후) elif file_desc & 0x20: compress__size = kavutil.get_uint16(data, size) uncompress__size = kavutil.get_uint16(data, size + 2) size += (2 * 2) elif file_desc & 0x40: compress__size = kavutil.get_uint32(data, size) uncompress__size = kavutil.get_uint32(data, size + 4) size += (4 * 2) elif file_desc & 0x80: compress__size = kavutil.get_uint64(data, size) uncompress__size = kavutil.get_uint64(data, size + 8) size += (8 * 2) else: raise SystemError fname = data[size:size + fname_size] size += fname_size # 파일 이름 if file_desc & 1: size += 12 # Encrypt Block compressed__data = data[size:size + compress__size] return compressed__data, compress__method__m, size + compress__size, fname except IndexError: pass return None, -1
def __EGG_Filename_Header__(self, data): size = -1 fname = None try: # fname_size = struct.unpack('<H', data[5:7])[0] fname_size = kavutil.get_uint16(data, 5) fname = data[7:7 + fname_size] size = 7 + fname_size except: pass return size, fname
def __Alz_LocalFileHeader__(self, data): try: fname_size = kavutil.get_uint16(data, 4) file_desc = ord(data[11]) compress__method__m = ord(data[13]) size = 19 if file_desc & 0x10: compress__size = ord(data[size]) uncompress__size = ord(data[size + 1]) size += (1 * 2) # 파일 크기가 2개 옴(압축전, 압축 후) elif file_desc & 0x20: compress__size = kavutil.get_uint16(data, size) uncompress__size = kavutil.get_uint16(data, size+2) size += (2 * 2) elif file_desc & 0x40: compress__size = kavutil.get_uint32(data, size) uncompress__size = kavutil.get_uint32(data, size+4) size += (4 * 2) elif file_desc & 0x80: compress__size = kavutil.get_uint64(data, size) uncompress__size = kavutil.get_uint64(data, size+8) size += (8 * 2) else: raise SystemError fname = data[size:size + fname_size] size += fname_size # 파일 이름 if file_desc & 1: size += 12 # Encrypt Block compressed__data = data[size:size + compress__size] return compressed__data, compress__method__m, size+compress__size, fname except IndexError: pass return None, -1
def __EGG_Filename_Header__(self, data): size = -1 fname = None try: # fname_size = struct.unpack('<H', data[5:7])[0] fname_size = kavutil.get_uint16(data, 5) fname = data[7:7+fname_size] size = 7 + fname_size except: pass fsencoding = sys.getfilesystemencoding() or sys.getdefaultencoding() return size, fname.decode('utf-8').encode(fsencoding)
def __EGG_Filename_Header__(self, data): size = -1 fname = None try: # fname_size = struct.unpack('<H', data[5:7])[0] fname_size = kavutil.get_uint16(data, 5) fname = data[7:7 + fname_size] size = 7 + fname_size except: pass fsencoding = sys.getfilesystemencoding() or sys.getdefaultencoding() return size, fname.decode('utf-8').encode(fsencoding)
def arclist(self, filename, fileformat): file_scan_list = [] # 검사 대상 정보를 모두 가짐 # 미리 분석된 파일 포맷중에 ICON 포맷이 있는가? if 'ff_icon' in fileformat: num = fileformat['ff_icon'] mm = self.__get_handle(filename) for i in range(num): off = 6 + (16 * i) w = ord(mm[off]) h = ord(mm[off + 1]) c = kavutil.get_uint16(mm, off + 6) # name = '%dx%d %d bit' % (w, h, c) name = '%dx%d' % (w, h) file_scan_list.append(['arc_icon', name]) return file_scan_list
def checkpe(dst, dsize, pehdr): try: if kavutil.get_uint32(dst, pehdr) != 0x4550: raise SystemError valign = kavutil.get_uint32(dst, pehdr + 0x38) if not valign: raise SystemError sectcnt = kavutil.get_uint16(dst, pehdr + 6) if not sectcnt: raise SystemError sections_pos = pehdr + 0xF8 if (sections_pos + (sectcnt * 0x28)) > dsize: raise SystemError except: sections_pos = 0 valign = 0 sectcnt = 0 pass return sections_pos, valign, sectcnt
def parse(self): mm = self.mm pe_format = { 'PE_Position': 0, 'EntryPoint': 0, 'SectionNumber': 0, 'Sections': None, 'EntryPointRaw': 0, 'FileAlignment': 0 } try: if mm[0:2] != 'MZ': # MZ로 시작하나? raise ValueError # PE 표식자 위치 알아내기 pe_pos = kavutil.get_uint32(mm, 0x3C) # PE 인가? if mm[pe_pos:pe_pos + 4] != 'PE\x00\x00': raise ValueError pe_format['PE_Position'] = pe_pos # Optional Header의 Magic ID? if mm[pe_pos + 0x18:pe_pos + 0x18 + 2] != '\x0B\x01': raise ValueError # Entry Point 구하기 pe_ep = kavutil.get_uint32(mm, pe_pos + 0x28) pe_format['EntryPoint'] = pe_ep # Image Base 구하기 pe_img = kavutil.get_uint32(mm, pe_pos + 0x34) pe_format['ImageBase'] = pe_img # File Alignment 구하기 self.pe_file_align = kavutil.get_uint32(mm, pe_pos + 0x3C) pe_format['FileAlignment'] = self.pe_file_align # Section 개수 구하기 section_num = kavutil.get_uint16(mm, pe_pos + 0x6) pe_format['SectionNumber'] = section_num # Optional Header 크기 구하기 opthdr_size = kavutil.get_uint16(mm, pe_pos + 0x14) pe_format['OptionalHederSize'] = opthdr_size # t섹션 시작 위치 section_pos = pe_pos + 0x18 + opthdr_size # 모든 섹션 정보 추출 for i in range(section_num): section = {} s = section_pos + (0x28 * i) section['Name'] = mm[s:s + 8].replace('\x00', '') section['VirtualSize'] = kavutil.get_uint32(mm, s + 8) section['RVA'] = kavutil.get_uint32(mm, s + 12) section['SizeRawData'] = kavutil.get_uint32(mm, s + 16) section['PointerRawData'] = kavutil.get_uint32(mm, s + 20) section['Characteristics'] = kavutil.get_uint32(mm, s + 36) self.sections.append(section) pe_format['Sections'] = self.sections # EntryPoint의 파일에서의 위치 구하기 ep_raw, sec_idx = self.rva_to_off(pe_ep) pe_format['EntryPointRaw'] = ep_raw # EP의 Raw 위치 pe_format['EntryPoint_in_Section'] = sec_idx # EP가 포함된 섹션 # 리소스 분석 rsrc_rva = kavutil.get_uint32(mm, pe_pos + 0x88) # 리소스 위치(RVA) rsrc_size = kavutil.get_uint32(mm, pe_pos + 0x8C) # 리소스 크기 if rsrc_rva: # 리소스가 존재한가? try: rsrc_off, _ = self.rva_to_off(rsrc_rva) # 리소스 위치 변환 # Type 체크 num_type_name = kavutil.get_uint16(mm, rsrc_off + 0xC) num_type_id = kavutil.get_uint16(mm, rsrc_off + 0xE) for i in range(num_type_name + num_type_id): type_id = kavutil.get_uint32(mm, rsrc_off + 0x10 + (i * 8)) name_id_off = kavutil.get_uint32( mm, rsrc_off + 0x14 + (i * 8)) # Type이 사용자가 정의한 이름 or RCDATA? if type_id & 0x80000000 == 0x80000000 or type_id == 0xA: if type_id & 0x80000000 == 0x80000000: # 사용자가 정의한 이름 추출 string_off = (type_id & 0x7FFFFFFF) + rsrc_off len_name = kavutil.get_uint16(mm, string_off) rsrc_type_name = mm[string_off + 2:string_off + 2 + (len_name * 2):2] else: rsrc_type_name = 'RCDATA' # Name ID name_id_off = (name_id_off & 0x7FFFFFFF) + rsrc_off num_name_id_name = kavutil.get_uint16( mm, name_id_off + 0xC) num_name_id_id = kavutil.get_uint16( mm, name_id_off + 0xE) for j in range(num_name_id_name + num_name_id_id): name_id_id = kavutil.get_uint32( mm, name_id_off + 0x10 + (j * 8)) language_off = kavutil.get_uint32( mm, name_id_off + 0x14 + (j * 8)) # 리소스 영역의 최종 이름 생성 if name_id_id & 0x80000000 == 0x80000000: string_off = (name_id_id & 0x7FFFFFFF) + rsrc_off len_name = kavutil.get_uint16( mm, string_off) rsrc_name_id_name = mm[string_off + 2:string_off + 2 + (len_name * 2):2] string_name = rsrc_type_name + '/' + rsrc_name_id_name else: string_name = rsrc_type_name + '/' + hex( name_id_id).upper()[2:] # Language language_off = (language_off & 0x7FFFFFFF) + rsrc_off num_language_name = kavutil.get_uint16( mm, language_off + 0xC) num_language_id = kavutil.get_uint16( mm, language_off + 0xE) for k in range(num_language_name + num_language_id): # language_id = kavutil.get_uint32(mm, language_off + 0x10 + (k * 8)) data_entry_off = kavutil.get_uint32( mm, language_off + 0x14 + (k * 8)) data_entry_off = (data_entry_off & 0x7FFFFFFF) + rsrc_off data_rva = kavutil.get_uint32( mm, data_entry_off) data_off, _ = self.rva_to_off(data_rva) data_size = kavutil.get_uint32( mm, data_entry_off + 4) if data_size > 8192: # 최소 8K 이상인 리소스만 데이터로 추출 if 'Resource_UserData' in pe_format: pe_format['Resource_UserData'][ string_name] = (data_off, data_size) else: pe_format['Resource_UserData'] = { string_name: (data_off, data_size) } except struct.error: pass # if 'Resource_UserData' in pe_format: # print pe_format['Resource_UserData'] # Import API 분석 imp_rva = kavutil.get_uint32(mm, pe_pos + 0x80) # Import API 위치(RVA) imp_size = kavutil.get_uint32(mm, pe_pos + 0x84) # Import API 크기 if imp_rva: # Import API 존재 imp_api = {} # print 'IMP : %08X' % imp_rva imp_off = self.rva_to_off(imp_rva)[0] # print hex(imp_off), imp_size imp_data = mm[imp_off:imp_off + imp_size] for i in range(imp_size / 0x14): # DLL 정보 크기가 0x14 try: dll_rva = kavutil.get_uint32(imp_data, (i * 0x14) + 0xC) api_rva = kavutil.get_uint32(imp_data, (i * 0x14)) bo = 2 if api_rva == 0: api_rva = kavutil.get_uint32( imp_data, (i * 0x14) + 0x10) bo = 0 # print hex(api_rva) if dll_rva == 0: # DLL 정보가 없음 break t_off = self.rva_to_off(dll_rva)[0] dll_name = p_str.search(mm[t_off:t_off + 0x20]).group() # print '[+]', dll_name imp_api[dll_name] = [] t_off = self.rva_to_off(api_rva)[0] while True: try: api_name_rva = kavutil.get_uint32(mm, t_off) except struct.error: break if api_name_rva & 0x80000000 == 0x80000000: # Odinal API t_off += 4 continue if api_name_rva == 0: break t = self.rva_to_off(api_name_rva)[0] # print hex(t_off), hex(t) api_name = p_str.search(mm[t + bo:t + bo + 0x20]).group() # print ' ', api_name imp_api[dll_name].append(api_name) t_off += 4 except struct.error: pass pe_format['Import_API'] = imp_api # 디지털 인증서 분석 cert_off = kavutil.get_uint32(mm, pe_pos + 0x98) # 디지털 인증서 위치(유일하게 RVA가 아닌 오프셋) cert_size = kavutil.get_uint32(mm, pe_pos + 0x9C) # 디지털 인증서 크기 if cert_off: # 디지털 인증서 존재 if cert_off + cert_size <= len(mm[:]): # UPack의 경우 이상한 값이 셋팅 됨 pe_format['CERTIFICATE_Offset'] = cert_off pe_format['CERTIFICATE_Size'] = cert_size if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'pe.kmd') kavutil.vprint(None, 'File name', os.path.split(self.filename)[-1]) kavutil.vprint(None, 'MD5', cryptolib.md5(mm[:])) print kavutil.vprint('PE') kavutil.vprint(None, 'EntryPoint', '%08X' % pe_format['EntryPoint']) kavutil.vprint(None, 'EntryPoint (Section)', '%d' % pe_format['EntryPoint_in_Section']) # 섹션 보기 if section_num: print kavutil.vprint('Section Header') print ' %-8s %-8s %-8s %-8s %-8s %-8s' % ( 'Name', 'VOFF', 'VSIZE', 'FOFF', 'FSIZE', 'EXEC') print ' ' + ('-' * (9 * 6 - 1)) for s in self.sections: print ' %-8s %08X %08X %08X %08X %-05s' % ( s['Name'], s['RVA'], s['VirtualSize'], s['PointerRawData'], s['SizeRawData'], s['Characteristics'] & 0x20000000 == 0x20000000) if section_num: print kavutil.vprint('Section MD5') print ' %-8s %-8s %-32s' % ('Name', 'FSIZE', 'MD5') print ' ' + ('-' * ((9 * 2 - 1) + 32)) for s in self.sections: if s['Characteristics'] & 0x20000000 == 0x20000000: off = s['PointerRawData'] size = s['SizeRawData'] fmd5 = cryptolib.md5(mm[off:off + size]) print ' %-8s %8d %s' % (s['Name'], size, fmd5) print kavutil.vprint('Entry Point (Raw)') print kavutil.HexDump().Buffer(mm[:], pe_format['EntryPointRaw'], 0x80) print except ValueError: return None return pe_format
def __scan_asn1(self, filehandle, filename, fileformat, filename_ex): mm = filehandle ff = fileformat['ff_pe'] cert_off = ff['pe'].get('CERTIFICATE_Offset', 0) cert_size = ff['pe'].get('CERTIFICATE_Size', 0) if cert_off != 0 and cert_size != 0: if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'adware.kmd') # 인증서 추출 cert_data = mm[cert_off:cert_off + cert_size] asn1 = ASN1() asn1.set_data(cert_data[8:]) try: r = asn1.parse() # Signed Data 이면서 버전 정보가 1인가? if r[0][0] == '2A 86 48 86 F7 0D 01 07 02' and r[0][1][0][ 0] == '01': signeddata = r[0][1][0] certificates = signeddata[3] signerinfo = r[0][1][0][-1] issuer_and_serialnumber = signerinfo[0][1] issuer_serial = issuer_and_serialnumber[1] for cert in certificates: if cert[0][1] == issuer_serial: # 동일한 일련번호 찾기 for x in cert[0][5]: if x[0][0] == '55 04 03': # Common Name signer_name = x[0][1] break else: continue # no break encountered break else: raise IndexError # 일련번호의 길이가 제각각이라 md5 고정길이로 만듬 fmd5 = cryptolib.md5(issuer_serial) fsize = kavutil.get_uint16(fmd5.decode('hex'), 0) if self.verbose: kavutil.vprint('Signer') kavutil.vprint(None, 'Name', signer_name) kavutil.vprint(None, 'Serial Number', issuer_serial) msg = '%d:%s: # %s, %s\n' % (fsize, fmd5, signer_name, cryptolib.sha256(mm)) open('adware.mdb', 'at').write(msg) if fsize and kavutil.handle_pattern_md5.match_size( 'adware', fsize): vname = kavutil.handle_pattern_md5.scan( 'adware', fsize, fmd5) if vname: pos = ff['pe'].get('EntryPointRaw', 0) if mm[pos:pos + 4] == '\xff\x25\x00\x20': pf = 'MSIL' else: pf = 'Win32' vname = kavutil.normal_vname(vname, pf) return True, vname, 0, kernel.INFECTED except IndexError: pass # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND
def parse(self): mm = self.mm pe_format = {'PE_Position': 0, 'EntryPoint': 0, 'SectionNumber': 0, 'Sections': None, 'EntryPointRaw': 0, 'FileAlignment': 0} try: if mm[0:2] != 'MZ': # MZ로 시작하나? raise ValueError dos_header = DOS_HEADER() ctypes.memmove(ctypes.addressof(dos_header), mm[0:], ctypes.sizeof(dos_header)) # PE 표식자 위치 알아내기 pe_pos = dos_header.e_lfanew # PE 인가? if mm[pe_pos:pe_pos + 4] != 'PE\x00\x00': raise ValueError pe_format['PE_Position'] = pe_pos # File Header 읽기 file_header = FILE_HEADER() file_header_size = ctypes.sizeof(file_header) # file_header_size : 0x14 ctypes.memmove(ctypes.addressof(file_header), mm[pe_pos + 4:], file_header_size) # Optional Header 읽기 optional_header = OPTIONAL_HEADER() optional_header_size = ctypes.sizeof(optional_header) ctypes.memmove(ctypes.addressof(optional_header), mm[pe_pos + 4 + file_header_size:], optional_header_size) # Optional Header의 Magic ID? if optional_header.Magic != 0x10b: raise ValueError # Entry Point 구하기 pe_ep = optional_header.AddressOfEntryPoint pe_format['EntryPoint'] = pe_ep # Image Base 구하기 pe_img = optional_header.ImageBase pe_format['ImageBase'] = pe_img # File Alignment 구하기 self.pe_file_align = optional_header.FileAlignment pe_format['FileAlignment'] = self.pe_file_align # Section 개수 구하기 section_num = file_header.NumberOfSections pe_format['SectionNumber'] = section_num # Optional Header 크기 구하기 opthdr_size = file_header.SizeOfOptionalHeader pe_format['OptionalHederSize'] = opthdr_size # Data Directory 읽기 data_directory_size = ctypes.sizeof(DATA_DIRECTORY()) # data_directory_size : 8 num_data_directory = (opthdr_size - optional_header_size) / data_directory_size off_data_directory = pe_pos + 4 + file_header_size + optional_header_size for i in range(num_data_directory): dx = DATA_DIRECTORY() ctypes.memmove(ctypes.addressof(dx), mm[off_data_directory + (i * data_directory_size):], data_directory_size) self.data_directories.append(dx) # 섹션 시작 위치 section_pos = pe_pos + 4 + file_header_size + opthdr_size # 모든 섹션 정보 추출 for i in range(section_num): section = {} section_header = SECTION_HEADER() section_header_size = ctypes.sizeof(section_header) # section_header_size : 0x28 s = section_pos + (section_header_size * i) ctypes.memmove(ctypes.addressof(section_header), mm[s:], section_header_size) sec_name = ctypes.cast(section_header.Name, ctypes.c_char_p) section['Name'] = sec_name.value.replace('\x00', '') section['VirtualSize'] = section_header.Misc_VirtualSize section['RVA'] = section_header.VirtualAddress section['SizeRawData'] = section_header.SizeOfRawData section['PointerRawData'] = section_header.PointerToRawData section['Characteristics'] = section_header.Characteristics self.sections.append(section) pe_format['Sections'] = self.sections # EntryPoint의 파일에서의 위치 구하기 ep_raw, sec_idx = self.rva_to_off(pe_ep) pe_format['EntryPointRaw'] = ep_raw # EP의 Raw 위치 pe_format['EntryPoint_in_Section'] = sec_idx # EP가 포함된 섹션 # 리소스 분석 try: rsrc_rva = self.data_directories[image_directory_entry.RESOURCE].VirtualAddress # 리소스 위치(RVA) rsrc_size = self.data_directories[image_directory_entry.RESOURCE].Size # 리소스 크기 except IndexError: rsrc_rva = 0 rsrc_size = 0 if rsrc_rva: # 리소스가 존재한가? try: rsrc_off, rsrc_idx = self.rva_to_off(rsrc_rva) # 리소스 위치 변환 if rsrc_off > self.filesize: raise ValueError t_size = self.sections[rsrc_idx]['SizeRawData'] if not (len(mm[rsrc_off:rsrc_off + rsrc_size]) == rsrc_size or \ len(mm[rsrc_off:rsrc_off + t_size]) == t_size): # 충분한 리소스가 존재하지 않음 raise ValueError # Type 체크 num_type_name = kavutil.get_uint16(mm, rsrc_off+0xC) num_type_id = kavutil.get_uint16(mm, rsrc_off + 0xE) for i in range(num_type_name + num_type_id): type_id = kavutil.get_uint32(mm, rsrc_off + 0x10 + (i*8)) name_id_off = kavutil.get_uint32(mm, rsrc_off + 0x14 + (i * 8)) # Type이 사용자가 정의한 이름 or RCDATA? if type_id & 0x80000000 == 0x80000000 or type_id == 0xA or type_id == 0: if type_id & 0x80000000 == 0x80000000: # 사용자가 정의한 이름 추출 string_off = (type_id & 0x7FFFFFFF) + rsrc_off len_name = kavutil.get_uint16(mm, string_off) rsrc_type_name = mm[string_off + 2:string_off + 2 + (len_name * 2):2] elif type_id == 0xA: rsrc_type_name = 'RCDATA' else: rsrc_type_name = '%d' % type_id # Name ID name_id_off = (name_id_off & 0x7FFFFFFF) + rsrc_off if name_id_off > self.filesize: raise ValueError num_name_id_name = kavutil.get_uint16(mm, name_id_off + 0xC) num_name_id_id = kavutil.get_uint16(mm, name_id_off + 0xE) for j in range(num_name_id_name + num_name_id_id): name_id_id = kavutil.get_uint32(mm, name_id_off + 0x10 + (j * 8)) language_off = kavutil.get_uint32(mm, name_id_off + 0x14 + (j * 8)) # 리소스 영역의 최종 이름 생성 if name_id_id & 0x80000000 == 0x80000000: string_off = (name_id_id & 0x7FFFFFFF) + rsrc_off if string_off > self.filesize: raise ValueError len_name = kavutil.get_uint16(mm, string_off) rsrc_name_id_name = mm[string_off + 2:string_off + 2 + (len_name * 2):2] string_name = rsrc_type_name + '/' + rsrc_name_id_name else: string_name = rsrc_type_name + '/' + hex(name_id_id).upper()[2:] # Language language_off = (language_off & 0x7FFFFFFF) + rsrc_off if language_off > self.filesize: raise ValueError num_language_name = kavutil.get_uint16(mm, language_off + 0xC) num_language_id = kavutil.get_uint16(mm, language_off + 0xE) for k in range(num_language_name + num_language_id): # language_id = kavutil.get_uint32(mm, language_off + 0x10 + (k * 8)) data_entry_off = kavutil.get_uint32(mm, language_off + 0x14 + (k * 8)) data_entry_off = (data_entry_off & 0x7FFFFFFF) + rsrc_off data_rva = kavutil.get_uint32(mm, data_entry_off) data_off, _ = self.rva_to_off(data_rva) if data_off > self.filesize: continue data_size = kavutil.get_uint32(mm, data_entry_off + 4) if data_size > self.filesize: continue if data_size > 8192: # 최소 8K 이상인 리소스만 데이터로 추출 if 'Resource_UserData' in pe_format: pe_format['Resource_UserData'][string_name] = (data_off, data_size) else: pe_format['Resource_UserData'] = {string_name: (data_off, data_size)} except (struct.error, ValueError) as e: pass # if 'Resource_UserData' in pe_format: # print pe_format['Resource_UserData'] # Import API 분석 try: imp_rva = self.data_directories[image_directory_entry.IMPORT].VirtualAddress # Import API 위치(RVA) imp_size = self.data_directories[image_directory_entry.IMPORT].Size # Import API 크기 except IndexError: imp_rva = 0 imp_size = 0 if imp_rva: # Import API 존재 imp_api = {} # print 'IMP : %08X' % imp_rva imp_off = self.rva_to_off(imp_rva)[0] # print hex(imp_off), imp_size imp_data = mm[imp_off:imp_off+imp_size] if len(imp_data) == imp_size: for i in range(imp_size / 0x14): # DLL 정보 크기가 0x14 try: dll_rva = kavutil.get_uint32(imp_data, (i*0x14)+0xC) api_rva = kavutil.get_uint32(imp_data, (i * 0x14)) bo = 2 if api_rva == 0: api_rva = kavutil.get_uint32(imp_data, (i*0x14)+0x10) bo = 0 # print hex(api_rva) if dll_rva == 0: # DLL 정보가 없음 break t_off = self.rva_to_off(dll_rva)[0] dll_name = p_str.search(mm[t_off:t_off+0x20]).group() # print '[+]', dll_name imp_api[dll_name] = [] t_off = self.rva_to_off(api_rva)[0] while True: try: api_name_rva = kavutil.get_uint32(mm, t_off) except struct.error: break if api_name_rva & 0x80000000 == 0x80000000: # Odinal API t_off += 4 continue if api_name_rva == 0: break t = self.rva_to_off(api_name_rva)[0] # print hex(t_off), hex(t) api_name = p_str.search(mm[t+bo:t+bo+0x20]).group() # print ' ', api_name imp_api[dll_name].append(api_name) t_off += 4 except struct.error: pass # end if pe_format['Import_API'] = imp_api # 디지털 인증서 분석 try: cert_off = self.data_directories[image_directory_entry.SECURITY].VirtualAddress # 유일하게 RVA가 아닌 오프셋 cert_size = self.data_directories[image_directory_entry.SECURITY].Size # 디지털 인증서 크기 except IndexError: cert_off = 0 cert_size = 0 if cert_off: # 디지털 인증서 존재 if cert_off + cert_size <= len(mm[:]): # UPack의 경우 이상한 값이 셋팅 됨 pe_format['CERTIFICATE_Offset'] = cert_off pe_format['CERTIFICATE_Size'] = cert_size # Debug 정보 분석 try: debug_rva = self.data_directories[image_directory_entry.DEBUG].VirtualAddress # RVA debug_size = self.data_directories[image_directory_entry.DEBUG].Size # 크기 if debug_size < 0x1C: raise ValueError except (IndexError, ValueError) as e: debug_rva = 0 debug_size = 0 if debug_rva: # Debug 정보 존재 t = self.rva_to_off(debug_rva)[0] debug_off = kavutil.get_uint32(mm, t + 0x18) debug_size = kavutil.get_uint32(mm, t + 0x10) debug_data = mm[debug_off:debug_off + debug_size] if debug_data[:4] == 'RSDS': pe_format['PDB_Name'] = debug_data[0x18:] else: pe_format['PDB_Name'] = 'Not support Type : %s' % debug_data[:4] if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'pe.kmd') kavutil.vprint(None, 'File name', os.path.split(self.filename)[-1]) kavutil.vprint(None, 'MD5', cryptolib.md5(mm[:])) print kavutil.vprint('PE') kavutil.vprint(None, 'EntryPoint', '%08X' % pe_format['EntryPoint']) kavutil.vprint(None, 'EntryPoint (Section)', '%d' % pe_format['EntryPoint_in_Section']) # 섹션 보기 if section_num: print kavutil.vprint('Section Header') print ' %-8s %-8s %-8s %-8s %-8s %-8s' % ('Name', 'VOFF', 'VSIZE', 'FOFF', 'FSIZE', 'EXEC') print ' ' + ('-' * (9*6 - 1)) for s in self.sections: print ' %-8s %08X %08X %08X %08X %-05s' % (s['Name'], s['RVA'], s['VirtualSize'], s['PointerRawData'], s['SizeRawData'], s['Characteristics'] & 0x20000000 == 0x20000000) if section_num: print kavutil.vprint('Section MD5') print ' %-8s %-8s %-32s' % ('Name', 'FSIZE', 'MD5') print ' ' + ('-' * ((9 * 2 - 1)+32)) for s in self.sections: # if s['Characteristics'] & 0x20000000 == 0x20000000: off = s['PointerRawData'] size = s['SizeRawData'] fmd5 = cryptolib.md5(mm[off:off+size]) if size else '-' print ' %-8s %8d %s' % (s['Name'], size, fmd5) print kavutil.vprint('Entry Point (Raw)') print kavutil.HexDump().Buffer(mm[:], pe_format['EntryPointRaw'], 0x80) print if 'PDB_Name' in pe_format: kavutil.vprint('PDB Information') kavutil.vprint(None, 'Name', '%s' % repr(pe_format['PDB_Name'])) print repr(pe_format['PDB_Name']) print except (ValueError, struct.error) as e: return None return pe_format
def scan(self, filehandle, filename, fileformat, filename_ex): # 악성코드 검사 try: mm = filehandle if mm[0:8] != '\x4c\x00\x00\x00\x01\x14\x02\x00': # LNK 헤더 체크 raise ValueError flag = kavutil.get_uint32(mm, 0x14) off = 0x4c if flag & 0x0001 == 0x0001: # HasLinkTargetIDList clid_mycom = '14001F50E04FD020EA3A6910A2D808002B30309D'.decode( 'hex') if mm[off + 2:off + 2 + 0x14] != clid_mycom: # MyComputer raise ValueError off += 2 while True: size = kavutil.get_uint16(mm, off) if size == 0: off += 2 break if ord(mm[off + 2]) == 0x32: if mm[off + 0xe:off + 0xe + 7].lower() != 'cmd.exe': raise ValueError off += size if flag & 0x0002 == 0x0002: # HasLinkInfo off += kavutil.get_uint16(mm, off) if flag & 0x0004 == 0x0004: # HasName size = kavutil.get_uint16(mm, off) off += (2 + (size * 2)) if flag & 0x0008 == 0x0008: # HasRelativePath size = kavutil.get_uint16(mm, off) cmd_path = mm[off + 2:off + 2 + (size * 2):2].lower() # print cmd_path if cmd_path.find('cmd.exe') == -1: raise ValueError off += (2 + (size * 2)) if flag & 0x0010 == 0x0010: # HasWorkingDir size = kavutil.get_uint16(mm, off) off += (2 + (size * 2)) if flag & 0x0020 == 0x0020: # HasArguments size = kavutil.get_uint16(mm, off) cmd_arg = mm[off + 2:off + 2 + (size * 2):2].lower() cmd_arg = cmd_arg.replace('^', '') # print cmd_arg # 악성코드 패턴을 비교한다. if self.p_http.search(cmd_arg): return True, 'Trojan.LNK.Agent.gen', 0, kernel.INFECTED except (IOError, ValueError): pass # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND
def __scan_asn1(self, filehandle, filename, fileformat, filename_ex): mm = filehandle ff = fileformat['ff_pe'] cert_off = ff['pe'].get('CERTIFICATE_Offset', 0) cert_size = ff['pe'].get('CERTIFICATE_Size', 0) if cert_off != 0 and cert_size != 0: if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'adware.kmd') # 인증서 추출 cert_data = mm[cert_off:cert_off + cert_size] asn1 = ASN1() asn1.set_data(cert_data[8:]) try: r = asn1.parse() # Signed Data 이면서 버전 정보가 1인가? if r[0][0] == '2A 86 48 86 F7 0D 01 07 02' and r[0][1][0][0] == '01': signeddata = r[0][1][0] certificates = signeddata[3] signerinfo = r[0][1][0][-1] issuer_and_serialnumber = signerinfo[0][1] issuer_serial = issuer_and_serialnumber[1] for cert in certificates: if cert[0][1] == issuer_serial: # 동일한 일련번호 찾기 for x in cert[0][5]: if x[0][0] == '55 04 03': # Common Name signer_name = x[0][1] break else: continue # no break encountered break else: raise IndexError # 일련번호의 길이가 제각각이라 md5 고정길이로 만듬 fmd5 = cryptolib.md5(issuer_serial) fsize = kavutil.get_uint16(fmd5.decode('hex'), 0) if self.verbose: kavutil.vprint('Signer') kavutil.vprint(None, 'Name', signer_name) kavutil.vprint(None, 'Serial Number', issuer_serial) msg = '%d:%s: # %s, %s\n' % (fsize, fmd5, signer_name, cryptolib.sha256(mm)) open('adware.mdb', 'at').write(msg) if fsize and kavutil.handle_pattern_md5.match_size('adware', fsize): vname = kavutil.handle_pattern_md5.scan('adware', fsize, fmd5) if vname: pos = ff['pe'].get('EntryPointRaw', 0) if mm[pos:pos + 4] == '\xff\x25\x00\x20': pf = 'MSIL' else: pf = 'Win32' vname = kavutil.normal_vname(vname, pf) return True, vname, 0, kernel.INFECTED except IndexError: pass # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND