def read(self, filename): if filename in self.nsis_header.files: data = None (foff, ftime, extract_type) = self.nsis_header.files[filename] if self.case_type == 1: # case 1: 설치 파일 전부를 압축한 경우 # print '#Case 1' # print hex(foff) # print hex(kavutil.get_uint32(self.body_data, foff) & 0x7fffffff) fsize = kavutil.get_uint32(self.body_data, foff) & 0x7fffffff return self.body_data[foff+4:foff+4+fsize] elif self.case_type == 2: # case 2: 개별로 압축한 경우 # print '#Case 2' # print hex(foff) # print hex(kavutil.get_uint32(self.body_data, foff) & 0x7fffffff) fsize = kavutil.get_uint32(self.body_data, foff) & 0x7fffffff fdata = self.body_data[foff+4:foff+4+fsize] comp_type = self.__get_comp_type(kavutil.get_uint32(fdata, 0)) # print comp_type if comp_type == self.TYPE_LZMA: try: # 전체 압축한 경우인지 확인해 본다. data = pylzma.decompress(fdata) except TypeError: pass elif comp_type == self.TYPE_ZLIB: try: data = zlib.decompress(fdata, -15) except zlib.error: pass return data else: return None
def parse(self): try: self.fp = open(self.fname, 'rb') self.fsize = os.fstat(self.fp.fileno()).st_size cur_pos = 0 # Magic 체크 if self.fp.read(0xe) != 'InstallShield\x00': raise ValueError cur_pos += 0xe # InstallShield에 첨부된 파일 수 data = self.fp.read(0x20) num_file = kavutil.get_uint32(data, 0) cur_pos += 0x20 for i in range(num_file): data = self.fp.read(0x138) fname = data[:0x10b].replace('\x00', '') fsize = kavutil.get_uint32(data, 0x10c) foff = cur_pos + 0x138 self.install_name.append((foff, fsize, fname)) cur_pos += 0x138 + fsize self.fp.seek(cur_pos) return True except (IOError, OSError, ValueError) as e: pass return False
def read(self, filename): if filename in self.nsis_header.files: data = None (foff, ftime, extract_type) = self.nsis_header.files[filename] if self.case_type == 1: # case 1: 설치 파일 전부를 압축한 경우 # print '#Case 1' # print hex(foff) # print hex(kavutil.get_uint32(self.body_data, foff) & 0x7fffffff) fsize = kavutil.get_uint32(self.body_data, foff) & 0x7fffffff return self.body_data[foff + 4:foff + 4 + fsize] elif self.case_type == 2: # case 2: 개별로 압축한 경우 # print '#Case 2' # print hex(foff) # print hex(kavutil.get_uint32(self.body_data, foff) & 0x7fffffff) fsize = kavutil.get_uint32(self.body_data, foff) & 0x7fffffff fdata = self.body_data[foff + 4:foff + 4 + fsize] comp_type = self.__get_comp_type(kavutil.get_uint32(fdata, 0)) # print comp_type if comp_type == self.TYPE_LZMA: try: # 전체 압축한 경우인지 확인해 본다. data = pylzma.decompress(fdata) except TypeError: pass elif comp_type == self.TYPE_ZLIB: try: data = zlib.decompress(fdata, -15) except zlib.error: pass return data else: return None
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 __ReadBlockData__(self): egg_pos = self.egg_pos mm = self.mm data_size = self.data_size try: while egg_pos < data_size: # magic = struct.unpack('<L', mm[egg_pos:egg_pos+4])[0] magic = kavutil.get_uint32(mm, egg_pos) if magic == 0x02B50C13: # Block Header # print 'Block Header' size = self.__EGG_Block_Header_Size__(mm[egg_pos:]) if size == -1: raise SystemError compress__method__m = ord(mm[egg_pos + 4]) # Compress_Method_H = ord(mm[egg_pos+5]) # Uncompress_Size = struct.unpack('<L', mm[egg_pos+6:egg_pos+10])[0] # compress__size = struct.unpack('<L', mm[egg_pos+10:egg_pos+14])[0] compress__size = kavutil.get_uint32(mm, egg_pos + 10) # CRC = struct.unpack('<L', mm[egg_pos+14:egg_pos+18])[0] compressed__data = mm[egg_pos + 22:egg_pos + 22 + compress__size] egg_pos += size return compressed__data, compress__method__m, egg_pos else: egg_pos = self.__DefaultMagicIDProc__(magic, egg_pos) if egg_pos == -1: raise SystemError except SystemError: pass return None, -1, -1
def scan_hwp_recoard(buf, lenbuf): pos = 0 tagid = 0 while pos < lenbuf: extra_size = 4 val = kavutil.get_uint32(buf, pos) tagid, level, size = get_hwp_recoard(val) if size == 0xfff: extra_size = 8 size = kavutil.get_uint32(buf, pos + 4) if tagid == 0x43 and size > 4000: # PARA_TEXT t_buf = buf[pos:pos+size+extra_size] d_buf = zlib.compress(t_buf) if len(d_buf) / float(len(t_buf)) < 0.02: return False, 0x43 pos += (size + extra_size) if pos == lenbuf: return True, -1 return False, tagid
def __ReadBlockData__(self): egg_pos = self.egg_pos mm = self.mm data_size = self.data_size try: while egg_pos < data_size: # magic = struct.unpack('<L', mm[egg_pos:egg_pos+4])[0] magic = kavutil.get_uint32(mm, egg_pos) if magic == 0x02B50C13: # Block Header # print 'Block Header' size = self.__EGG_Block_Header_Size__(mm[egg_pos:]) if size == -1: raise SystemError compress__method__m = ord(mm[egg_pos+4]) # Compress_Method_H = ord(mm[egg_pos+5]) # Uncompress_Size = struct.unpack('<L', mm[egg_pos+6:egg_pos+10])[0] # compress__size = struct.unpack('<L', mm[egg_pos+10:egg_pos+14])[0] compress__size = kavutil.get_uint32(mm, egg_pos+10) # CRC = struct.unpack('<L', mm[egg_pos+14:egg_pos+18])[0] compressed__data = mm[egg_pos+22:egg_pos+22+compress__size] egg_pos += size return compressed__data, compress__method__m, egg_pos else: egg_pos = self.__DefaultMagicIDProc__(magic, egg_pos) if egg_pos == -1: raise SystemError except SystemError: pass return None, -1, -1
def analysis_ole10native(mm, verbose=False): fileformat = {} try: size = kavutil.get_uint32(mm, 0) if mm[4:6] == '\x02\x00': if len(mm) == size + 4: fileformat['size'] = len(mm) # 포맷 주요 정보 저장 label = mm[6:6 + MAX_PATH].split('\x00', 1)[0] fileformat['label'] = label off = 6 + len(label) + 1 fname = mm[off:off + MAX_PATH].split('\x00', 1)[0] off += len(fname) + 1 off += 2 # flag unknown_size = ord(mm[off]) off += 1 + unknown_size + 2 command = mm[off:off + MAX_PATH].split('\x00', 1)[0] off += len(command) + 1 data_size = kavutil.get_uint32(mm, off) fileformat['data_off'] = off + 4 fileformat['data_size'] = data_size if len(mm) < off + data_size: # 오류 raise ValueError if verbose: print kavutil.vprint('Ole10Native Stream') kavutil.vprint(None, 'Size', '0x%08X' % size) kavutil.vprint(None, 'Label', label) kavutil.vprint(None, 'File Name', fname) kavutil.vprint(None, 'Command Line', command) kavutil.vprint(None, 'Data Offset', '0x%08X' % (off + 4)) kavutil.vprint(None, 'Data Size', '0x%08X' % data_size) print kavutil.vprint('Data Dump') print kavutil.HexDump().Buffer(mm[:], off + 4, 0x80) print return fileformat except ValueError: pass except struct.error: pass return None
def parse(self): self.temp_name = tempfile.mktemp(prefix='knsf') # NSIS 위치 읽기 fp = open(self.filename, 'rb') fp.seek(self.start_offset) data = fp.read() fp.close() open(self.temp_name, 'wb').write(data) self.fp = open(self.temp_name, 'rb') fsize = os.path.getsize(self.temp_name) if fsize == 0: return False self.mm = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ) flag = kavutil.get_uint32(self.mm, 0) head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x18) data, case_type = self.get_data() # NSIS의 모든 데이터를 가진다. self.body_data = data self.case_type = case_type if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'nsis.kmd') kavutil.vprint(None, 'File name', os.path.split(self.filename)[-1]) print kavutil.vprint('NSIS') kavutil.vprint(None, 'Flag', '%d' % flag) kavutil.vprint(None, 'Uncompress Case', '%d' % case_type) print kavutil.vprint('Uncompress Data') print kavutil.HexDump().Buffer(data, 0, 0x80) s = self.nsis_header.namelist_ex() if len(s): print kavutil.vprint('File Extract') print for t in s: (foff, fname, ftime, extract_type) = t print "%08X | %-45s | %s" % (foff, fname, ftime if ftime != '' else 'N/A') return True
def __get_string(self, str_off): if str_off < 0: off = self.nh.langtables + 2 + 4 + -str_off * 4 str_off = kavutil.get_uint32(self.header_data, off) if (str_off * self.is_unicode) in range(0, self.strings_max): str_data = '' off = self.nh.strings + (str_off * self.is_unicode) char = self.header_data[off:off + (1 * self.is_unicode)] off += (1 * self.is_unicode) while char != '\x00' * self.is_unicode and len(char) != 0: if self.is_unicode == 2: ch = struct.unpack('<H', char)[0] else: ch = ord(char) if (ch >= self.ns_codes_start) if self.ver3 else (ch < self.ns_codes_start): str_data += char elif ch == self.ns_var_code: n_data = self.__decode_short(off) off += 2 str_data += self.__binary_str_to_unicode(self.__get_user_var_name(n_data)) char = self.header_data[off:off + (1 * self.is_unicode)] off += (1 * self.is_unicode) # end while str_data = self.__binary_unicode_to_str(str_data) return str_data else: return ''
def __GetFileName__(self, alz_pos): try: mm = self.mm data_size = len(mm) except TypeError: return None, -1 try: while alz_pos < data_size: magic = kavutil.get_uint32(mm, alz_pos) if magic == 0x015A4C42: # Local File Header _, _, size, fname = self.__Alz_LocalFileHeader__(mm[alz_pos:]) if size == -1: raise ValueError alz_pos += size return fname, alz_pos else: alz_pos = self.__DefaultMagicIDProc__(magic, alz_pos) if alz_pos == -1: raise ValueError except ValueError: pass return None, -1
def __GetFileName__(self, alz_pos): try: mm = self.mm data_size = len(mm) except TypeError: return None, -1 try: while alz_pos < data_size: magic = kavutil.get_uint32(mm, alz_pos) if magic == 0x015A4C42: # Local File Header _, _, size, fname = self.__Alz_LocalFileHeader__( mm[alz_pos:]) if size == -1: raise ValueError alz_pos += size return fname, alz_pos else: alz_pos = self.__DefaultMagicIDProc__(magic, alz_pos) if alz_pos == -1: raise ValueError except ValueError: pass return None, -1
def __scan_virus_win32_small_a(self, mm, fileformat): if 'ff_pe' in fileformat: ff = fileformat['ff_pe']['pe'] ep_off = ff['EntryPointRaw'] if cryptolib.crc32(mm[ep_off:ep_off + 12]) == '4d49a25f': v_rva = kavutil.get_uint32(mm, ep_off + 12) + 1 # 악성코드 RVA v_rva -= ff['ImageBase'] # v_rva가 마지막 섹션에 속하는 값인지 확인한다. sec = ff['Sections'][-1] if sec['RVA'] <= v_rva <= sec['RVA'] + sec['VirtualSize']: pe_file_align = ff['FileAlignment'] if pe_file_align: foff = (sec['PointerRawData'] / pe_file_align) * pe_file_align else: foff = sec['PointerRawData'] v_off = v_rva - sec['RVA'] + foff x = cryptolib.crc32(mm[v_off:v_off + 0x30]) if x == '8d964738': return True, 'Virus.Win32.Small.a' elif x == '00000000' or x == 'f288b395': # 파일이 깨진 경우이거나 모든 값이 0인 경우이다. return True, 'Virus.Win32.SuperThreat.b' return False, None
def get_data(self): # NSIS가 두가지 종류가 있는것으로 보여짐 # 설치 파일 전부를 압축한 경우와 개별로 압축한 경우 # case 1: 설치 파일 전부를 압축한 경우 try: head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x18) comp_type = self.__get_comp_type(kavutil.get_uint32(self.mm, 0x1C)) uncmp_data = self.do_decompress(comp_type, 0x1C, comp_size) if uncmp_data: if head_size == kavutil.get_uint32(uncmp_data, 0): self.nsis_header = NSISHeader(uncmp_data[4:head_size + 4]) if self.nsis_header.parse(): return uncmp_data[head_size + 4:], 1 except struct.error: pass # case 2: 개별로 압축한 경우 try: head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x1C) & 0x7fffffff comp_type = self.__get_comp_type(kavutil.get_uint32(self.mm, 0x20)) uncmp_data = self.do_decompress(comp_type, 0x20, comp_size) if uncmp_data: if head_size == len(uncmp_data): self.nsis_header = NSISHeader(uncmp_data) if self.nsis_header.parse(): return self.mm[0x20+comp_size:], 2 except struct.error: pass return None, 0
def get_data(self): # NSIS가 두가지 종류가 있는것으로 보여짐 # 설치 파일 전부를 압축한 경우와 개별로 압축한 경우 # case 1: 설치 파일 전부를 압축한 경우 try: head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x18) comp_type = self.__get_comp_type(kavutil.get_uint32(self.mm, 0x1C)) uncmp_data = self.do_decompress(comp_type, 0x1C, comp_size) if uncmp_data: if head_size == kavutil.get_uint32(uncmp_data, 0): self.nsis_header = NSISHeader(uncmp_data[4:head_size + 4]) if self.nsis_header.parse(): return uncmp_data[head_size + 4:], 1 except struct.error: pass # case 2: 개별로 압축한 경우 try: head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x1C) & 0x7fffffff comp_type = self.__get_comp_type(kavutil.get_uint32(self.mm, 0x20)) uncmp_data = self.do_decompress(comp_type, 0x20, comp_size) if uncmp_data: if head_size == len(uncmp_data): self.nsis_header = NSISHeader(uncmp_data) if self.nsis_header.parse(): return self.mm[0x20 + comp_size:], 2 except struct.error: pass return None, 0
def scan_hwp_recoard(buf, lenbuf): pos = 0 while pos < lenbuf: extra_size = 4 val = kavutil.get_uint32(buf, pos) tagid, level, size = get_hwp_recoard(val) if size == 0xfff: extra_size = 8 size = kavutil.get_uint32(buf, pos + 4) pos += (size + extra_size) if pos == lenbuf: return True return False
def parse(self): self.fp = open(self.filename, 'rb') fsize = os.path.getsize(self.filename) if fsize == 0: return False self.mm = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ) flag = kavutil.get_uint32(self.mm, 0) head_size = kavutil.get_uint32(self.mm, 0x14) comp_size = kavutil.get_uint32(self.mm, 0x18) data, case_type = self.get_data() # NSIS의 모든 데이터를 가진다. self.body_data = data self.case_type = case_type if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'nsis.kmd') kavutil.vprint(None, 'File name', os.path.split(self.filename)[-1]) print kavutil.vprint('NSIS') kavutil.vprint(None, 'Flag', '%d' % flag) kavutil.vprint(None, 'Uncompress Case', '%d' % case_type) print kavutil.vprint('Uncompress Data') print kavutil.HexDump().Buffer(data, 0, 0x80) s = self.nsis_header.namelist_ex() if len(s): print kavutil.vprint('File Extract') print for t in s: (foff, fname, ftime, extract_type) = t print "%08X | %-45s | %s" % (foff, fname, ftime if ftime != '' else 'N/A') return True
def __EGG_Block_Header_Size__(self, data): size = -1 try: block__size = (18 + 4) # compress__size = struct.unpack('<L', data[10:14])[0] compress__size = kavutil.get_uint32(data, 10) size = block__size + compress__size except: pass return size
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 __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 doubleebx(src, myebx, scur, ssize): oldebx = myebx try: myebx = (myebx * 2) & 0xFFFFFFFF if not (oldebx & 0x7fffffff): if not ISCONTAINED(0, ssize, scur, 4): return -1, myebx, scur oldebx = kavutil.get_uint32(src, scur) myebx = uint32(oldebx * 2 + 1) scur += 4 return (oldebx >> 31), myebx, scur except: return -1, myebx, scur
def checkpe(dst, dsize, pehdr): try: if kavutil.get_uint32(dst, pehdr) != 0x4550: raise ValueError valign = kavutil.get_uint32(dst, pehdr + 0x38) if not valign: raise ValueError sectcnt = kavutil.get_uint32(dst, pehdr + 6) if not sectcnt: raise ValueError sections_pos = pehdr + 0xF8 if (sections_pos + (sectcnt * 0x28)) > dsize: raise ValueError except ValueError: sections_pos = 0 valign = 0 sectcnt = 0 return sections_pos, valign, sectcnt
def checkpe(dst, dsize, pehdr): try: if kavutil.get_uint32(dst, pehdr) != 0x4550: raise ValueError valign = kavutil.get_uint32(dst, pehdr+0x38) if not valign: raise ValueError sectcnt = kavutil.get_uint32(dst, pehdr+6) if not sectcnt: raise ValueError sections_pos = pehdr + 0xF8 if (sections_pos + (sectcnt * 0x28)) > dsize: raise ValueError except ValueError: sections_pos = 0 valign = 0 sectcnt = 0 return sections_pos, valign, sectcnt
def scan(self, filehandle, filename, fileformat, filename_ex): # 악성코드 검사 mm = filehandle if filename_ex.lower().find('bodytext/section') >= 0 or filename_ex.lower().find('docinfo') >= 0: val = kavutil.get_uint32(mm, 0) tagid, level, size = get_hwp_recoard(val) # 문서의 첫번째 tag가 문서 헤더(0x42), 문서 속성(0x10) 일때만 추적 진행 if tagid == 0x42 or tagid == 0x10: ret, tagid = scan_hwp_recoard(mm, len(mm)) if ret is False: # 레코드 추적 실패 return True, 'Exploit.HWP.Generic.%02X' % tagid, 0, kernel.INFECTED # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND
def scan(self, filehandle, filename, fileformat, filename_ex): # 악성코드 검사 mm = filehandle if filename_ex.lower().find('bodytext/section') >= 0 or filename_ex.lower().find('docinfo') >= 0: val = kavutil.get_uint32(mm, 0) tagid, level, size = get_hwp_recoard(val) # 문서의 첫번째 tag가 문서 헤더(0x42), 문서 속성(0x10) 일때만 추적 진행 if tagid == 0x42 or tagid == 0x10: ret, tagid = scan_hwp_recoard(mm, len(mm)) if ret is False: # 레코드 추적 실패 return True, 'Exploit.HWP.Generic.%02X' % tagid, 0, kernel.INFECTED elif filename_ex.lower().find('scripts/defaultjscript') >= 0: if self.hwp_js.search(mm): return True, 'Exploit.JS.Agent.gen', 0, kernel.INFECTED # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND
def scan(self, filehandle, filename, fileformat, filename_ex): # 악성코드 검사 try: # 미리 분석된 파일 포맷중에 PE 포맷이 있는가? if 'ff_pyc' in fileformat: if self.verbose: print '-' * 79 kavutil.vprint('Engine') kavutil.vprint(None, 'Engine', 'pyz.kmd') mm = filehandle # String 추출 if len(mm): if self.verbose: print kavutil.vprint('String') for match in self.p_string.finditer(mm): find_str = match.group() find_str_off = match.start() # 중요 문자열 시작전에 해당 문자열의 길이가 존재함 x = kavutil.get_uint32(mm, find_str_off - 4) if len(find_str) < x: continue buf = find_str[:x] fsize = len(buf) if self.verbose: fmd5 = cryptolib.md5(buf) kavutil.vprint(None, fmd5, '%3d : %s' % (fsize, buf)) if fsize and kavutil.handle_pattern_md5.match_size('emalware', fsize): fmd5 = cryptolib.md5(buf) # print fsize, fmd5 vname = kavutil.handle_pattern_md5.scan('emalware', fsize, fmd5) if vname: return True, vname, 0, kernel.INFECTED except IOError: pass # 악성코드를 발견하지 못했음을 리턴한다. return False, '', -1, kernel.NOT_FOUND
def format(self, filehandle, filename, filename_ex): fileformat = {} # 포맷 정보를 담을 공간 mm = filehandle try: zlib.decompress(mm, -15) ret = {'ff_zlib': 'ZLIB'} return ret except zlib.error: pass try: if kavutil.get_uint32(mm, 0) == len(mm) - 4: ret = {'ff_embed_ole': 'EMBED_OLE'} return ret except struct.error: pass return None
def init(self, plugins_path, verbose=False): # 플러그인 엔진 초기화 self.verbose = verbose self.sig_num_yara = 0 # Yara 모듈이 없을 경우 엔질 로딩 실패 처리 if not LOAD_YARA: return -1 # Adware Yara 룰 로딩 try: b = open(os.path.join(plugins_path, 'adware.y01'), 'rb').read() self.sig_num_yara = kavutil.get_uint32(b, 4) if b[:4] == 'KAVS': t = zlib.decompress(b[12:]) buff = StringIO.StringIO(t) self.adware_gen = yara.load(file=buff) except: self.adware_gen = None return 0 # 플러그인 엔진 초기화 성공
def unarc(self, arc_engine_id, arc_name, fname_in_arc): data = None if arc_engine_id == 'arc_hwp': o = ole.OleFile(arc_name) fp = o.openstream(fname_in_arc) data = fp.read() o.close() elif arc_engine_id == 'arc_hwp_ole': with open(arc_name, 'rb') as fp: buf = fp.read() try: buf = zlib.decompress(buf, -15) except zlib.error: pass if kavutil.get_uint32(buf, 0) == len(buf[4:]): data = buf[4:] return data
def format(self, filehandle, filename, filename_ex): ret = {} # 포맷 정보를 담을 공간 mm = filehandle try: d = zlib.decompress(mm, -15) if len(d) > 1: ret['ff_zlib'] = 'ZLIB' except zlib.error: pass try: if kavutil.get_uint32(mm, 0) == len(mm) - 4: ret['ff_embed_ole'] = 'EMBED_OLE' except struct.error: pass if len(ret): return ret return None
def __GetFileName__(self, egg_pos): mm = self.mm data_size = self.data_size try: while egg_pos < data_size: # magic = struct.unpack('<L', mm[egg_pos:egg_pos+4])[0] magic = kavutil.get_uint32(mm, egg_pos) if magic == 0x0A8591AC: # Filename Header # print 'Filename Header' size, fname = self.__EGG_Filename_Header__(mm[egg_pos:]) if size == -1: raise SystemError egg_pos += size return fname, egg_pos else: egg_pos = self.__DefaultMagicIDProc__(magic, egg_pos) if egg_pos == -1: raise SystemError except SystemError: pass return None, -1
def RebuildPE(src, ssize, dst, dsize, ep, upx0, upx1, magic, dend): try: foffset = uint32(0xD0 + 0xF8) if len(src) == 0 or len(dst) == 0: raise SystemError for valign in magic: if (ep - upx1 + valign <= ssize - 5) and \ ord(src[ep - upx1 + valign - 2]) == 0x8D and \ ord(src[ep - upx1 + valign - 1]) == 0xBE: break if not valign and (ep - upx1 + len(HEADERS) < ssize - 8): i = 0 while True: b1 = ord(src[ep - upx1 + len(HEADERS) + 0 + i]) b2 = ord(src[ep - upx1 + len(HEADERS) + 1 + i]) if b1 == 0x8D and b2 == 0xBE: b3 = ord(src[ep - upx1 + len(HEADERS) + 6 + i]) b4 = ord(src[ep - upx1 + len(HEADERS) + 7 + i]) if b3 == 0x8B and b4 == 0x07: valign = len(HEADERS) + i + 2 break i += 1 if valign and ISCONTAINED(0, ssize, ep - upx1 + valign, 4): dst_imports = kavutil.get_uint32(src, ep - upx1 + valign) # dst_imports = struct.unpack('<l', src[ep - upx1 + valign:ep - upx1 + valign + 4])[0] realstuffsz = dst_imports if realstuffsz >= dsize: raise SystemError else: pehdr = dst_imports while (pehdr + 8 < dsize) and kavutil.get_uint32(dst, pehdr): # while (pehdr + 8 < dsize) and (struct.unpack('<l', dst[pehdr:pehdr + 4])[0]): pehdr += 8 while (pehdr + 2 < dsize) and ord(dst[pehdr]): pehdr += 1 while (pehdr + 2 < dsize) and ord(dst[pehdr]): pehdr += 1 pehdr += 1 pehdr += 1 pehdr += 4 sections, valign, sectcnt = checkpe(dst, dsize, pehdr) if not sections: pehdr = 0 if not pehdr and dend > (0xF8 + 0x28): pehdr = dend - 0xF8 - 0x28 if int32(pehdr) < 0: raise SystemError while True: sections, valign, sectcnt = checkpe(dst, dsize, pehdr) if sections: break pehdr -= 1 realstuffsz = pehdr if not realstuffsz: raise SystemError if not pehdr: rebsz = uint32(PESALIGN(dend, 0x1000)) # print hex(rebsz) # To Do foffset = PESALIGN(foffset + 0x28 * sectcnt, valign) for upd in range(sectcnt): t = kavutil.get_uint32(dst, sections + 8) vsize = uint32(PESALIGN(t, valign)) t = kavutil.get_uint32(dst, sections + 12) urva = uint32(PESALIGN(t, valign)) if not (upx0 + realstuffsz >= urva + vsize): raise SystemError t = struct.pack('<LLLL', vsize, urva, vsize, foffset) dest = dst[:sections + 8] dest += t dest += dst[sections + 24:] dst = dest foffset += vsize sections += 0x28 t = struct.pack('<L', valign) dest = dst[:pehdr + 0x3C] dest += t dest += dst[pehdr + 0x3C + 4:] dst = dest newbuf = ['\x00'] * foffset for i in range(len(HEADERS)): newbuf[i] = HEADERS[i] for i in range(0xf8 + 0x28 * sectcnt): newbuf[0xD0 + i] = dst[pehdr + i] sections = pehdr + 0xF8 for upd in range(sectcnt): t1 = kavutil.get_uint32(dst, sections + 20) t2 = kavutil.get_uint32(dst, sections + 12) t3 = kavutil.get_uint32(dst, sections + 16) for i in range(t3): newbuf[t1 + i] = dst[t2 - upx0 + i] sections += 0x28 if foffset > dsize + 8192: raise SystemError dsize = foffset upx_d = '' for ch in newbuf: upx_d += ch except: return None return upx_d
def unarc(self, arc_engine_id, arc_name, fname_in_arc): fp = None mm = None data = None if arc_engine_id.find('arc_upx') != -1: filename = fname_in_arc try: # UPX로 압축된 파일 열기 fp = open(arc_name, 'rb') mm = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) p = pe.PE(mm, False, arc_name) pe_format = p.parse() # PE 파일 분석 if pe_format is None: return ValueError pe_img = pe_format['ImageBase'] pe_ep = pe_format['EntryPoint'] sections = pe_format['Sections'] ep_raw = pe_format['EntryPointRaw'] # EP의 Raw 위치 ep_nsec = pe_format[ 'EntryPoint_in_Section'] # EP는 몇번째 섹션에 있는가? foff = 0 ssize = 0 dsize = 0 for section in sections: ssize = section['VirtualSize'] rva = section['RVA'] if rva <= pe_ep < rva + ssize: foff = section['PointerRawData'] i = sections.index(section) if i != 0: upx0 = sections[i - 1]['RVA'] upx1 = sections[i]['RVA'] dsize = sections[i - 1]['VirtualSize'] + ssize break if ssize == 0 or dsize == 0: raise ValueError upx_data_rva = kavutil.get_uint32(mm, ep_raw + 2) sec_rva = sections[ep_nsec]['RVA'] skew = upx_data_rva - sec_rva - pe_img if mm[ep_raw + 1] != '\xBE' or skew <= 0 or skew > 0xFFF: skew = 0 elif skew > ssize: skew = 0 else: raise ValueError data = mm[foff + skew:foff + ssize - skew] unpack_data = '' # UPX 해제된 이미지 if arc_engine_id[8:] == 'nrv2b': # UPX 알고리즘 중 nrv2b 압축인가? ret_val, unpack_data = upx_inflate2b( data, dsize, pe_ep, upx0, upx1, pe_img) if unpack_data == '': # 압축 해제 실패 raise ValueError data = unpack_data except IOError: pass except ValueError: pass if mm: mm.close() if fp: fp.close() return data return None
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 RebuildPE(src, ssize, dst, dsize, ep, upx0, upx1, magic, dend): try: foffset = uint32(0xD0 + 0xF8) if len(src) == 0 or len(dst) == 0: raise SystemError for valign in magic: if (ep - upx1 + valign <= ssize - 5) and \ ord(src[ep - upx1 + valign - 2]) == 0x8D and \ ord(src[ep - upx1 + valign - 1]) == 0xBE: break if not valign and (ep - upx1 + 0x80 < ssize - 8): i = 0 while True: b1 = ord(src[ep - upx1 + 0x80 + 0 + i]) b2 = ord(src[ep - upx1 + 0x80 + 1 + i]) if b1 == 0x8D and b2 == 0xBE: b3 = ord(src[ep - upx1 + 0x80 + 6 + i]) b4 = ord(src[ep - upx1 + 0x80 + 7 + i]) if b3 == 0x8B and b4 == 0x07: valign = 0x80 + i + 2 break i += 1 if valign and ISCONTAINED(0, ssize, ep - upx1 + valign, 4): dst_imports = struct.unpack('<l', src[ep - upx1 + valign:ep - upx1 + valign + 4])[0] realstuffsz = dst_imports if realstuffsz >= dsize: raise SystemError else: pehdr = dst_imports while (pehdr + 8 < dsize) and (struct.unpack('<l', dst[pehdr:pehdr + 4])[0]): pehdr += 8 while (pehdr + 2 < dsize) and ord(dst[pehdr]): pehdr += 1 while (pehdr + 2 < dsize) and ord(dst[pehdr]): pehdr += 1 pehdr += 1 pehdr += 1 pehdr += 4 sections, valign, sectcnt = checkpe(dst, dsize, pehdr) if not sections: pehdr = 0 if not pehdr and dend > (0xF8 + 0x28): pehdr = dend - 0xF8 - 0x28 while int32(pehdr) > 0: sections, valign, sectcnt = checkpe(dst, dsize, pehdr) if sections: break pehdr -= 1 realstuffsz = pehdr if not realstuffsz: raise SystemError if not pehdr: rebsz = uint32(PESALIGN(dend, 0x1000)) # print hex(rebsz) # To Do foffset = PESALIGN(foffset + 0x28 * sectcnt, valign) for upd in range(sectcnt): t = kavutil.get_uint32(dst, sections + 8) vsize = uint32(PESALIGN(t, valign)) t = kavutil.get_uint32(dst, sections + 12) urva = uint32(PESALIGN(t, valign)) if not (upx0 + realstuffsz >= urva + vsize): raise SystemError t = struct.pack('<LLLL', vsize, urva, vsize, foffset) dest = dst[:sections + 8] dest += t dest += dst[sections + 24:] dst = dest foffset += vsize sections += 0x28 t = struct.pack('<L', valign) dest = dst[:pehdr + 0x3C] dest += t dest += dst[pehdr + 0x3C + 4:] dst = dest newbuf = ['\x00'] * foffset for i in range(len(HEADERS)): newbuf[i] = HEADERS[i] for i in range(0xf8 + 0x28 * sectcnt): newbuf[0xD0 + i] = dst[pehdr + i] sections = pehdr + 0xF8 for upd in range(sectcnt): t1 = kavutil.get_uint32(dst, sections + 20) t2 = kavutil.get_uint32(dst, sections + 12) t3 = kavutil.get_uint32(dst, sections + 16) for i in range(t3): newbuf[t1 + i] = dst[t2 - upx0 + i] sections += 0x28 if foffset > dsize + 8192: raise SystemError dsize = foffset upx_d = '' for ch in newbuf: upx_d += ch except: return '' return upx_d
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 ModifyCallAddr(src, dest, ep, upx0, upx1, baseaddr): try: # Call 개수 확인 ssize = len(src) pos = ep - upx1 call_count = -1 while True: if pos > ssize - 13: break if ord(src[pos]) == 0xE9 and \ ord(src[pos + 3]) == 0xFF and \ ord(src[pos + 4]) == 0xFF and \ ord(src[pos + 5]) == 0x5E and \ ord(src[pos + 6]) == 0x89 and \ ord(src[pos + 7]) == 0xF7 and \ ord(src[pos + 8]) == 0xB9: call_count = kavutil.get_uint32(src, pos + 9) break else: pos += 1 if call_count == -1: raise SystemError # Call Address 수정 dst = '' pos = 0 dcur = 0 dsize = len(dest) while call_count: if pos > dsize - 5: break if (ord(dest[pos]) == 0xE9 or ord(dest[pos]) == 0xE8) and \ ord(dest[pos + 1]) == 0x01: eax = kavutil.get_uint32(dest, pos + 1) ax = eax & 0xFFFF ax >>= 8 eax &= 0xFFFF0000 eax |= ax eax = ROL(eax, 0x10) ah = (eax & 0xFF00) >> 8 al = (eax & 0x00FF) << 8 ax = ah | al eax &= 0xFFFF0000 eax |= ax eax = uint32(eax - (baseaddr + upx0 + pos + 1)) eax = uint32(eax + (baseaddr + upx0)) dst += dest[dcur:pos + 1] dst += struct.pack('<L', eax) pos += 5 dcur = pos call_count -= 1 else: pos += 1 dst += dest[dcur:] except: return None return dst
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 ModifyCallAddr(src, dest, ep, upx0, upx1, baseaddr): try: # Call 개수 확인 ssize = len(src) pos = ep - upx1 call_count = -1 while True: if pos > ssize - 13: break if ord(src[pos]) == 0xE9 and \ ord(src[pos + 3]) == 0xFF and ord(src[pos + 4]) == 0xFF and \ ord(src[pos + 5]) == 0x5E and ord(src[pos + 6]) == 0x89 and \ ord(src[pos + 7]) == 0xF7 and ord(src[pos + 8]) == 0xB9: call_count = kavutil.get_uint32(src, pos + 9) break else: pos += 1 if call_count == -1: raise SystemError # Call Address 수정 dst = '' pos = 0 dcur = 0 dsize = len(dest) while call_count: if pos > dsize - 5: break if (ord(dest[pos]) == 0xE9 or ord(dest[pos]) == 0xE8) and \ ord(dest[pos + 1]) == 0x01: eax = kavutil.get_uint32(dest, pos + 1) ax = eax & 0xFFFF ax >>= 8 eax &= 0xFFFF0000 eax |= ax eax = ROL(eax, 0x10) ah = (eax & 0xFF00) >> 8 al = (eax & 0x00FF) << 8 ax = ah | al eax &= 0xFFFF0000 eax |= ax eax = uint32(eax - (baseaddr + upx0 + pos + 1)) eax = uint32(eax + (baseaddr + upx0)) dst += dest[dcur:pos + 1] dst += struct.pack('<L', eax) pos += 5 dcur = pos call_count -= 1 else: pos += 1 dst += dest[dcur:] ''' fp = open('call.dmp', 'wb') fp.write(dst) fp.close() ''' except: return None return dst
def unarc(self, arc_engine_id, arc_name, fname_in_arc): fp = None mm = None data = None if arc_engine_id.find('arc_upx') != -1: filename = fname_in_arc try: # UPX로 압축된 파일 열기 fp = open(arc_name, 'rb') mm = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) p = pe.PE(mm, False, arc_name) pe_format = p.parse() # PE 파일 분석 if pe_format is None: return ValueError pe_img = pe_format['ImageBase'] pe_ep = pe_format['EntryPoint'] sections = pe_format['Sections'] ep_raw = pe_format['EntryPointRaw'] # EP의 Raw 위치 ep_nsec = pe_format['EntryPoint_in_Section'] # EP는 몇번째 섹션에 있는가? foff = 0 ssize = 0 dsize = 0 for section in sections: ssize = section['VirtualSize'] rva = section['RVA'] if rva <= pe_ep < rva+ssize: foff = section['PointerRawData'] i = sections.index(section) if i != 0: upx0 = sections[i-1]['RVA'] upx1 = sections[i]['RVA'] dsize = sections[i-1]['VirtualSize'] + ssize break if ssize == 0 or dsize == 0: raise ValueError upx_data_rva = kavutil.get_uint32(mm, ep_raw+2) sec_rva = sections[ep_nsec]['RVA'] skew = upx_data_rva - sec_rva - pe_img if mm[ep_raw+1] != '\xBE' or skew <= 0 or skew > 0xFFF: skew = 0 elif skew > ssize: skew = 0 else: raise ValueError data = mm[foff+skew:foff+ssize-skew] unpack_data = '' # UPX 해제된 이미지 if arc_engine_id[8:] == 'nrv2b': # UPX 알고리즘 중 nrv2b 압축인가? try: ret_val, unpack_data = upx_inflate2b(data, dsize, pe_ep, upx0, upx1, pe_img) except OverflowError: raise ValueError if self.verbose: kavutil.vprint('Decompress') kavutil.vprint(None, 'Compressed Size', '%d' % len(data)) if unpack_data == '': # 압축 해제 실패 kavutil.vprint(None, 'Decompress Size', 'Error') else: kavutil.vprint(None, 'Decompress Size', '%d' % len(unpack_data)) print if unpack_data == '': # 압축 해제 실패 raise ValueError data = unpack_data except IOError: pass except ValueError: pass if mm: mm.close() if fp: fp.close() return data return None