├── LICENSE ├── README.md ├── UserDB.txt ├── malscan.py ├── ncat.exe.log ├── pefile.py ├── peutils.py └── usage_example.png /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Nizamul Rana 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MalScan 2 | 3 | MalScan is a simple PE File Heuristics Scanners written in python that you can use to quickly analyze a PE file and find out whether anything suspicious exists. It is a simple tool so doesn't offers much fancy features. You are free to extend it or do whatever you want with it. 4 | 5 | ## Things Supported 6 | 7 | - Information About file such as MD5, SHA1, Timestamp 8 | - PEiD Signature Check 9 | - Custom Yara Rules Integration 10 | - Section, Imports, Exports, Resources and TLS Callbacks Overview 11 | - Provides some custom heuristics :-) 12 | 13 | ## Installing 14 | 15 | You need to have Python 2.7 installed on your machine. The additional requirement is yara-python. 16 | 17 | ```sh 18 | git clone https://github.com/Ice3man543/MalScan.git . 19 | cd MalScan 20 | python malscan.py 21 | ``` 22 | 23 | ## Usage 24 | 25 | Simply run with the name of file you want to check. 26 | 27 | ![tool_in_action](https://raw.githubusercontent.com/Ice3man543/MalScan/master/usage_example.png) 28 | 29 | ### Development 30 | 31 | Want to contribute? Great! 32 | 33 | You can add more featrues or recommend any changes to the existing ones. Any kind of help is appreciated. 34 | 35 | License 36 | ---- 37 | 38 | BSD 2-Clause "Simplified" License 39 | 40 | 41 | ## Contact 42 | 43 | Meet me on Twitter: [@Ice3man543](https://twitter.com/ice3man543) 44 | 45 | ## Credits 46 | - The Awesome PEiD project 47 | - Malware Analysts Cookbook 48 | - Any other malware resource that this tool contains code from :-) 49 | -------------------------------------------------------------------------------- /UserDB.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ice3man543/MalScan/773505adc32d8f55801b1964beeb37e04d9406a7/UserDB.txt -------------------------------------------------------------------------------- /malscan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 4 | # MalScan, A Simple PE File Heuristics Scanner 5 | # Written By : @Ice3man (Nizamul Rana) 6 | # 7 | # MalScan is a simple PE file heuristics scanner in python that I wrote 8 | # as a simple learning experiment for Windows Malware Analysis. 9 | # It is not complete by any means, so do whatever you want with the code. 10 | # 11 | # PEID Rules are from awesome PEiD project. 12 | # 13 | # (C) Ice3man, 2018-19 All Rights Reserved 14 | # 15 | # Redistribution and use in source and binary forms, with or without 16 | # modification, are permitted provided that the following conditions 17 | # are met: 18 | # 19 | # 1. Redistributions of source code must retain the above copyright 20 | # notice, this list of conditions and the following disclaimer. 21 | # 2. Redistributions in binary form must reproduce the above copyright 22 | # notice, this list of conditions and the following disclaimer in the 23 | # documentation and/or other materials provided with the distribution. 24 | # 25 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 28 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 29 | # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 32 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 33 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 34 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 35 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | # 37 | 38 | 39 | __author__ = "@Ice3man" 40 | __version__ = "1.0" 41 | __contact__ = "nizamulrock@gmail.com" 42 | 43 | yarasig = "Yara-Rules.yara" # TODO : Change this to your own custom rules 44 | 45 | peid = "UserDB.txt" # peid rules 46 | 47 | import os 48 | import sys 49 | import hashlib 50 | import time 51 | import peutils 52 | 53 | try: 54 | import pefile 55 | except ImportError: 56 | print 'pefile not installed, see http://code.google.com/p/pefile/' 57 | sys.exit() 58 | 59 | try: 60 | import yara 61 | except ImportError: 62 | print 'yara-python is not installed, see http://code.google.com/p/yara-project/' 63 | 64 | has_res = 0 # For Suspecious Resources 65 | high_entropy = 0 # checking for high entropy sections 66 | has_ep_out = 0 # cheks Entry point for bad entries 67 | has_antivm = 0 # antivm checks 68 | has_kernelmode_imports = 0 # ntoskernel imports 69 | has_antidbg = 0 # anti_dbg_imp 70 | 71 | good_ep_sections = ['.text', '.code', 'CODE', 'INIT', 'PAGE'] 72 | anti_dbg_imp = ['NtQueryInformationProcess', 'NtSetInformationThread', 73 | 'IsDebuggerPresent', 'CheckRemoteDebugger'] 74 | 75 | kernel_loads = [] # for holding kernel32 imports 76 | 77 | sigs = peutils.SignatureDatabase(peid) 78 | 79 | 80 | def check_antidbg(pe): 81 | for entry in pe.DIRECTORY_ENTRY_IMPORT: 82 | if (entry.dll == "ntdll.dll"): 83 | for imp in entry.imports: 84 | for i in anti_dbg_imp: 85 | if(imp.name == i): 86 | has_antidbg = 1 87 | 88 | 89 | def check_kernel_mode(pe): 90 | for entry in pe.DIRECTORY_ENTRY_IMPORT: 91 | if (entry.dll == "ntoskrnl.exe"): 92 | has_kernelmode_imports = 1 93 | 94 | 95 | def check_yara(file): 96 | rule = yara.compile(yarasig) 97 | result = rule.match(file) 98 | return result 99 | 100 | # taken From OpenSource AnalyzePE Program 101 | 102 | 103 | def antivm(file): 104 | tricks = { 105 | "Red Pill": "\x0f\x01\x0d\x00\x00\x00\x00\xc3", 106 | "VirtualPc trick": "\x0f\x3f\x07\x0b", 107 | "VMware trick": "VMXh", 108 | "VMCheck.dll": "\x45\xC7\x00\x01", 109 | "VMCheck.dll for VirtualPC": "\x0f\x3f\x07\x0b\xc7\x45\xfc\xff\xff\xff\xff", 110 | "Xen": "XenVMM", 111 | "Bochs & QEmu CPUID Trick": "\x44\x4d\x41\x63", 112 | "Torpig VMM Trick": "\xE8\xED\xFF\xFF\xFF\x25\x00\x00\x00\xFF\x33\xC9\x3D\x00\x00\x00\x80\x0F\x95\xC1\x8B\xC1\xC3", 113 | "Torpig (UPX) VMM Trick": "\x51\x51\x0F\x01\x27\x00\xC1\xFB\xB5\xD5\x35\x02\xE2\xC3\xD1\x66\x25\x32\xBD\x83\x7F\xB7\x4E\x3D\x06\x80\x0F\x95\xC1\x8B\xC1\xC3" 114 | } 115 | ret = [] 116 | with open(file, "rb") as f: 117 | buf = f.read() 118 | for trick in tricks: 119 | pos = buf.find(tricks[trick]) 120 | if pos > -1: 121 | ret.append("\t[+] 0x%x %s" % (pos, trick)) 122 | has_antivm = 1 123 | 124 | if len(ret): 125 | resp = "Yes" 126 | if verb == True: 127 | antis = '\n'.join(ret) 128 | resp = resp + '\n' + antis 129 | return resp 130 | 131 | 132 | def check_dynamic_loaders(pe): 133 | i = 0 134 | for entry in pe.DIRECTORY_ENTRY_IMPORT: 135 | if (entry.dll == "KERNEL32.DLL"): 136 | for imp in entry.imports: 137 | i += 1 138 | if (i <= 7): 139 | dynamic = 1 140 | elif (i == 0): 141 | dynamic = 1 142 | return dynamic 143 | 144 | # uses peutils module to perform checking for PEiD signs 145 | 146 | 147 | def check_packers(pe): 148 | packers = [] 149 | if sigs: 150 | matches = sigs.match(pe, ep_only=True) 151 | if matches != None: 152 | for match in matches: 153 | packers.append(match) 154 | return packers 155 | 156 | # thanks pescanner 157 | 158 | 159 | def check_entry_point(pe): 160 | name = '' 161 | ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint 162 | pos = 0 163 | for sec in pe.sections: 164 | if (ep >= sec.VirtualAddress) and \ 165 | (ep < (sec.VirtualAddress + sec.Misc_VirtualSize)): 166 | name = sec.Name.replace('\x00', '') 167 | break 168 | else: 169 | pos += 1 170 | return (ep, name, pos) 171 | 172 | 173 | def main(): 174 | print "\n[*] MalScan : A PE File Heuristics Scanner" 175 | print "[*] Written By : @Ice3man (Nizamul Rana)" 176 | print "[*] Github : https://github.com/ice3man543" 177 | 178 | if len(sys.argv) != 2: 179 | print "\nUsage: %s \n" % (sys.argv[0]) 180 | sys.exit() 181 | 182 | file = sys.argv[1] 183 | 184 | pe = pefile.PE(file) # Loading the PE file 185 | 186 | fp = open(file, 'rb') 187 | data = fp.read() 188 | 189 | print "\n\n[*] File Name : %s" % file 190 | 191 | print"[*] Size : %d bytes" % len(data) 192 | if pe.FILE_HEADER.Machine == 0x14C: # IMAGE_FILE_MACHINE_I386 193 | print "[*] Architecture : 32 Bits Binary" 194 | elif pe.FILE_HEADER.Machine == 0x8664: # IMAGE_FILE_MACHINE_AMD64 195 | print "[*] Architecture : 64 Bits Binary" 196 | 197 | print "[*] MD5 : %s" % hashlib.md5(data).hexdigest() 198 | print "[*] SHA1 : %s" % hashlib.sha1(data).hexdigest() 199 | print "[*] SHA256 : %s" % hashlib.sha256(data).hexdigest() 200 | print "[*] CRC Hash : 0x%x" % pe.OPTIONAL_HEADER.CheckSum 201 | val = pe.FILE_HEADER.TimeDateStamp 202 | print "[*] Timestamp : [%s UTC]" % time.asctime(time.gmtime(val)) 203 | 204 | # packer = check_yara(file) 205 | print "\n[*] PEiD Signatures Check ==> %s" % check_packers(pe) 206 | # print "\n[*] Yara Scan ==> %s" % packer 207 | print "[*] Anit-VM ==> %s" % antivm(file) 208 | 209 | (ep, name, pos) = check_entry_point(pe) 210 | ep_ava = ep + pe.OPTIONAL_HEADER.ImageBase 211 | if (name not in good_ep_sections) or pos == len(pe.sections): 212 | print "\n[*] Entry-Point Check ==> %s %s %d/%d [SUSPECIOUS]" % (hex(ep_ava), name, pos, len(pe.sections)) 213 | has_ep_out = 1 214 | else: 215 | print "\n[*] Entry-Point Check ==> %s %s %d/%d" % (hex(ep_ava), name, pos, len(pe.sections)) 216 | has_ep_out = 0 217 | 218 | high_entropy = 0 219 | print "\n\n[Section Overview]" 220 | j = 1 221 | print "\nNo.| Section Name | VirtualAddress | VirtualSize | SizeOfRawData | Entropy \n" 222 | for section in pe.sections: 223 | # check if sections has high entropy 224 | if section.get_entropy() >= 7.4: 225 | print "[%d]| %s | 0x%x | 0x%x | 0x%x | %d [SUSPECIOUS]" % (j, section.Name, section.VirtualAddress, section.Misc_VirtualSize, section.SizeOfRawData, section.get_entropy()) 226 | j += 1 227 | high_entropy = 1 228 | else: 229 | print "[%d]| %s | 0x%x | 0x%x | 0x%x | %d " % (j, section.Name, section.VirtualAddress, section.Misc_VirtualSize, section.SizeOfRawData, section.get_entropy()) 230 | j += 1 231 | 232 | print "\n\n[Imports Overview]" 233 | for entry in pe.DIRECTORY_ENTRY_IMPORT: 234 | print "\n", entry.dll 235 | for imp in entry.imports: 236 | print '\t', hex(imp.address), imp.name 237 | 238 | print "\n\n[Exports Overview]" 239 | try: 240 | for export in pe.DIRECTORY_ENTRY_EXPORT.symbols: 241 | print "\t", hex(export.address), export.name 242 | except AttributeError: 243 | print "\n[*] No Exports Found In File . . .\n" 244 | 245 | k = 1 246 | print "\n\n[Resources Overview]" 247 | try: 248 | print "\nNo.| Resource Name | OffsetToData | Resource Size | Language\n" 249 | for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries: 250 | if resource_type.name is not None: 251 | name = "%s" % resource_type.name 252 | else: 253 | name = "%s" % pefile.RESOURCE_TYPE.get(resource_type.struct.Id) 254 | if name == None: 255 | name = "%d" % resource_type.struct.Id 256 | if hasattr(resource_type, 'directory'): 257 | for resource_id in resource_type.directory.entries: 258 | if hasattr(resource_id, 'directory'): 259 | for resource_lang in resource_id.directory.entries: 260 | # filetype = get_filetype(data) 261 | lang = pefile.LANG.get( 262 | resource_lang.data.lang, '*unknown*') 263 | if (name == "BINARY"): 264 | print "[%d]| %s | 0x%x | %d | %s [SUSPECIOUS]" % (k, name, resource_lang.data.struct.OffsetToData, resource_lang.data.struct.Size, lang) 265 | k += 1 266 | has_res = 1 267 | elif (name == "RT_RCDATA"): 268 | print "[%d]| %s | 0x%x | %d | %s [SUSPECIOUS]" % (k, name, resource_lang.data.struct.OffsetToData, resource_lang.data.struct.Size, lang) 269 | k += 1 270 | has_res = 1 271 | else: 272 | print "[%d]| %s | 0x%x | %d | %s" % (k, name, resource_lang.data.struct.OffsetToData, resource_lang.data.struct.Size, lang) 273 | k += 1 274 | has_res = 0 275 | except AttributeError: 276 | print "\n[*] No Resources Found In File\n" 277 | 278 | print "\n[TLS Overview]" 279 | if hasattr(pe, "DIRECTORY_ENTRY_TLS"): 280 | print "[*] TLS callback functions array detected at 0x%x" % pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks 281 | callback_rva = pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - \ 282 | pe.OPTIONAL_HEADER.ImageBase 283 | print("\t[*] Callback Array RVA 0x%x" % callback_rva) 284 | else: 285 | print "\n[*] No TLS Callbacks Detected ..." 286 | 287 | # final report 288 | check_antidbg(pe) 289 | check_kernel_mode(pe) 290 | antivm(file) 291 | dynamic = check_dynamic_loaders(pe) 292 | 293 | print "\n[MISC. Information]\n" 294 | 295 | if has_res == 1: 296 | print "[*] Alert -> Has Some Suspecious Resources" 297 | if has_antivm == 1: 298 | print "[*] Alert -> Has Anti-VirtualMachine Tricks" 299 | if has_ep_out == 1: 300 | print "[*] Alert -> Has Entry Point Outside Known Good Sections" 301 | if high_entropy == 1: 302 | print "[*] Alert -> Has High Entropy Values. Probably Packed Or Compressed" 303 | if dynamic == 1: 304 | print "[*] Alert -> Most Likely, Executable Uses Dynamic Loading or is Packed [Very Suspecious]" 305 | if has_kernelmode_imports == 1: 306 | print "[*] Alert -> Uses Kernel Mode. Probably a system driver" 307 | if has_antidbg == 1: 308 | print "[*] Alert -> Uses Anti-Debugging Tricks" 309 | 310 | if has_antidbg != 1 & high_entropy != 1 & has_ep_out != 1 & has_antivm != 1 & has_res != 1: 311 | print "\n[*] No Known Anomalies Detected In PE File ..." 312 | 313 | print "\n[*] Exiting MalScan Engine ..." 314 | sys.exit() 315 | 316 | main() 317 | -------------------------------------------------------------------------------- /ncat.exe.log: -------------------------------------------------------------------------------- 1 | 2 | [*] MalScan : A PE File Heuristics Scanner 3 | [*] Written By : @Ice3man (Nizamul Rana) 4 | [*] Github : https://github.com/ice3man543 5 | 6 | 7 | [*] File Name : ncat.exe 8 | [*] Size : 386048 bytes 9 | [*] Architecture : 32 Bits Binary 10 | [*] MD5 : 933dbd446a88bf507c4af5f14e2c211b 11 | [*] SHA1 : 4b1336b27b80f7c0c7788e1ed2e7052c51f247a5 12 | [*] SHA256 : 5c494dfd88e74487544194980cf3ad111f893950e34f065cb4d26f2fc7d82733 13 | [*] CRC Hash : 0x0 14 | [*] Timestamp : [Wed Mar 16 15:47:40 2016 UTC] 15 | 16 | [*] PEiD Signatures Check ==> [] 17 | [*] Anit-VM ==> None 18 | 19 | [*] Entry-Point Check ==> 0x43ea9a .text 0/7 20 | 21 | 22 | [Section Overview] 23 | 24 | No.| Section Name | VirtualAddress | VirtualSize | SizeOfRawData | Entropy 25 | 26 | [1]| .text | 0x1000 | 0x4ab04 | 0x4ac00 | 5 27 | [2]| .rdata | 0x4c000 | 0xc7ba | 0xc800 | 4 28 | [3]| .data | 0x59000 | 0x2910 | 0x800 | 5 29 | [4]| .idata | 0x5c000 | 0x1c4b | 0x1e00 | 4 30 | [5]| .didat | 0x5e000 | 0x3e0 | 0x400 | 1 31 | [6]| .rsrc | 0x5f000 | 0x43c | 0x600 | 2 32 | [7]| .reloc | 0x60000 | 0x3a07 | 0x3c00 | 6 33 | 34 | 35 | [Imports Overview] 36 | 37 | WS2_32.dll 38 | 0x45c630 WSASocketA 39 | 0x45c634 None 40 | 0x45c638 None 41 | 0x45c63c None 42 | 0x45c640 None 43 | 0x45c644 None 44 | 0x45c648 None 45 | 0x45c64c None 46 | 0x45c650 None 47 | 0x45c654 None 48 | 0x45c658 None 49 | 0x45c65c None 50 | 0x45c660 None 51 | 0x45c664 None 52 | 0x45c668 WSAEventSelect 53 | 0x45c66c WSACreateEvent 54 | 0x45c670 WSACloseEvent 55 | 0x45c674 None 56 | 0x45c678 None 57 | 0x45c67c None 58 | 0x45c680 None 59 | 0x45c684 None 60 | 0x45c688 None 61 | 0x45c68c None 62 | 0x45c690 None 63 | 0x45c694 None 64 | 0x45c698 None 65 | 0x45c69c None 66 | 0x45c6a0 None 67 | 0x45c6a4 None 68 | 0x45c6a8 None 69 | 0x45c6ac None 70 | 0x45c6b0 None 71 | 0x45c6b4 None 72 | 0x45c6b8 None 73 | 0x45c6bc None 74 | 75 | ADVAPI32.dll 76 | 0x45c000 CryptReleaseContext 77 | 0x45c004 CryptAcquireContextA 78 | 0x45c008 CryptGenRandom 79 | 80 | LIBEAY32.dll 81 | 0x45c12c None 82 | 0x45c130 None 83 | 0x45c134 None 84 | 0x45c138 None 85 | 0x45c13c None 86 | 0x45c140 None 87 | 0x45c144 None 88 | 0x45c148 None 89 | 0x45c14c None 90 | 0x45c150 None 91 | 0x45c154 None 92 | 0x45c158 None 93 | 0x45c15c None 94 | 0x45c160 None 95 | 0x45c164 None 96 | 0x45c168 None 97 | 0x45c16c None 98 | 0x45c170 None 99 | 0x45c174 None 100 | 0x45c178 None 101 | 0x45c17c None 102 | 0x45c180 None 103 | 0x45c184 None 104 | 0x45c188 None 105 | 0x45c18c None 106 | 0x45c190 None 107 | 0x45c194 None 108 | 0x45c198 None 109 | 0x45c19c None 110 | 0x45c1a0 None 111 | 0x45c1a4 None 112 | 0x45c1a8 None 113 | 0x45c1ac None 114 | 0x45c1b0 None 115 | 0x45c1b4 None 116 | 0x45c1b8 None 117 | 0x45c1bc None 118 | 0x45c1c0 None 119 | 0x45c1c4 None 120 | 0x45c1c8 None 121 | 0x45c1cc None 122 | 0x45c1d0 None 123 | 0x45c1d4 None 124 | 0x45c1d8 None 125 | 0x45c1dc None 126 | 0x45c1e0 None 127 | 0x45c1e4 None 128 | 0x45c1e8 None 129 | 0x45c1ec None 130 | 0x45c1f0 None 131 | 132 | SSLEAY32.dll 133 | 0x45c568 None 134 | 0x45c56c None 135 | 0x45c570 None 136 | 0x45c574 None 137 | 0x45c578 None 138 | 0x45c57c None 139 | 0x45c580 None 140 | 0x45c584 None 141 | 0x45c588 None 142 | 0x45c58c None 143 | 0x45c590 None 144 | 0x45c594 None 145 | 0x45c598 None 146 | 0x45c59c None 147 | 0x45c5a0 None 148 | 0x45c5a4 None 149 | 0x45c5a8 None 150 | 0x45c5ac None 151 | 0x45c5b0 None 152 | 0x45c5b4 None 153 | 0x45c5b8 None 154 | 0x45c5bc None 155 | 0x45c5c0 None 156 | 0x45c5c4 None 157 | 0x45c5c8 None 158 | 0x45c5cc None 159 | 0x45c5d0 None 160 | 0x45c5d4 None 161 | 0x45c5d8 None 162 | 0x45c5dc None 163 | 0x45c5e0 None 164 | 0x45c5e4 None 165 | 0x45c5e8 None 166 | 167 | MSVCR120.dll 168 | 0x45c248 fgets 169 | 0x45c24c _controlfp_s 170 | 0x45c250 _invoke_watson 171 | 0x45c254 __crtSetUnhandledExceptionFilter 172 | 0x45c258 ?terminate@@YAXXZ 173 | 0x45c25c _except_handler4_common 174 | 0x45c260 _except1 175 | 0x45c264 _vsnprintf 176 | 0x45c268 __crtTerminateProcess 177 | 0x45c26c __crtUnhandledException 178 | 0x45c270 _crt_debugger_hook 179 | 0x45c274 _commode 180 | 0x45c278 _fmode 181 | 0x45c27c __initenv 182 | 0x45c280 _initterm 183 | 0x45c284 _initterm_e 184 | 0x45c288 __setusermatherr 185 | 0x45c28c _configthreadlocale 186 | 0x45c290 _cexit 187 | 0x45c294 _exit 188 | 0x45c298 __set_app_type 189 | 0x45c29c __getmainargs 190 | 0x45c2a0 _amsg_exit 191 | 0x45c2a4 _XcptFilter 192 | 0x45c2a8 _onexit 193 | 0x45c2ac __dllonexit 194 | 0x45c2b0 _calloc_crt 195 | 0x45c2b4 memchr 196 | 0x45c2b8 strchr 197 | 0x45c2bc strrchr 198 | 0x45c2c0 _errno 199 | 0x45c2c4 iscntrl 200 | 0x45c2c8 tolower 201 | 0x45c2cc free 202 | 0x45c2d0 memcpy 203 | 0x45c2d4 sscanf 204 | 0x45c2d8 __iob_func 205 | 0x45c2dc strtok 206 | 0x45c2e0 exit 207 | 0x45c2e4 isprint 208 | 0x45c2e8 strcpy_s 209 | 0x45c2ec strcat_s 210 | 0x45c2f0 strncat 211 | 0x45c2f4 strncpy_s 212 | 0x45c2f8 sprintf_s 213 | 0x45c2fc strtoul 214 | 0x45c300 calloc 215 | 0x45c304 memset 216 | 0x45c308 getenv 217 | 0x45c30c _putenv 218 | 0x45c310 signal 219 | 0x45c314 strerror 220 | 0x45c318 fclose 221 | 0x45c31c _fileno 222 | 0x45c320 fopen 223 | 0x45c324 perror 224 | 0x45c328 printf 225 | 0x45c32c strspn 226 | 0x45c330 atoi 227 | 0x45c334 strtol 228 | 0x45c338 _setmode 229 | 0x45c33c clearerr 230 | 0x45c340 feof 231 | 0x45c344 ferror 232 | 0x45c348 fflush 233 | 0x45c34c _isatty 234 | 0x45c350 fprintf 235 | 0x45c354 fread 236 | 0x45c358 fseek 237 | 0x45c35c ftell 238 | 0x45c360 fwrite 239 | 0x45c364 _close 240 | 0x45c368 _lseek 241 | 0x45c36c _read 242 | 0x45c370 _write 243 | 0x45c374 ?_open@@YAHPBDHH@Z 244 | 0x45c378 _strdup 245 | 0x45c37c _dup2 246 | 0x45c380 vfprintf 247 | 0x45c384 _wassert 248 | 0x45c388 sprintf 249 | 0x45c38c isalpha 250 | 0x45c390 isdigit 251 | 0x45c394 _strnicmp 252 | 0x45c398 strstr 253 | 0x45c39c malloc 254 | 0x45c3a0 realloc 255 | 0x45c3a4 strncpy 256 | 0x45c3a8 ispunct 257 | 0x45c3ac isalnum 258 | 0x45c3b0 _stricmp 259 | 0x45c3b4 _snprintf 260 | 0x45c3b8 strtod 261 | 0x45c3bc _access 262 | 0x45c3c0 _stat64i32 263 | 0x45c3c4 _ftime64 264 | 0x45c3c8 isspace 265 | 0x45c3cc getc 266 | 0x45c3d0 ungetc 267 | 0x45c3d4 strncmp 268 | 0x45c3d8 _open_osfhandle 269 | 0x45c3dc _time64 270 | 0x45c3e0 freopen 271 | 0x45c3e4 longjmp 272 | 0x45c3e8 abort 273 | 0x45c3ec _setjmp3 274 | 0x45c3f0 localeconv 275 | 0x45c3f4 frexp 276 | 0x45c3f8 strpbrk 277 | 0x45c3fc ldexp 278 | 0x45c400 _libm_sse2_pow_precise 279 | 0x45c404 floor 280 | 0x45c408 strcoll 281 | 0x45c40c toupper 282 | 0x45c410 fscanf 283 | 0x45c414 _fseeki64 284 | 0x45c418 _ftelli64 285 | 0x45c41c _pclose 286 | 0x45c420 _popen 287 | 0x45c424 setvbuf 288 | 0x45c428 tmpfile 289 | 0x45c42c setlocale 290 | 0x45c430 system 291 | 0x45c434 clock 292 | 0x45c438 strftime 293 | 0x45c43c _difftime64 294 | 0x45c440 _gmtime64 295 | 0x45c444 _localtime64 296 | 0x45c448 _mktime64 297 | 0x45c44c remove 298 | 0x45c450 rename 299 | 0x45c454 tmpnam 300 | 0x45c458 isupper 301 | 0x45c45c islower 302 | 0x45c460 isxdigit 303 | 0x45c464 isgraph 304 | 0x45c468 rand 305 | 0x45c46c srand 306 | 0x45c470 modf 307 | 0x45c474 _CIatan2 308 | 0x45c478 _CIcosh 309 | 0x45c47c _CIfmod 310 | 0x45c480 _CIsinh 311 | 0x45c484 _CItanh 312 | 0x45c488 _libm_sse2_acos_precise 313 | 0x45c48c _libm_sse2_asin_precise 314 | 0x45c490 _libm_sse2_atan_precise 315 | 0x45c494 _libm_sse2_cos_precise 316 | 0x45c498 _libm_sse2_exp_precise 317 | 0x45c49c _libm_sse2_log10_precise 318 | 0x45c4a0 _libm_sse2_log_precise 319 | 0x45c4a4 _libm_sse2_sin_precise 320 | 0x45c4a8 _libm_sse2_sqrt_precise 321 | 0x45c4ac _libm_sse2_tan_precise 322 | 0x45c4b0 ceil 323 | 0x45c4b4 _HUGE 324 | 0x45c4b8 _lock 325 | 0x45c4bc _unlock 326 | 327 | KERNEL32.dll 328 | 0x45c038 QueryPerformanceCounter 329 | 0x45c03c IsProcessorFeaturePresent 330 | 0x45c040 IsDebuggerPresent 331 | 0x45c044 DecodePointer 332 | 0x45c048 EncodePointer 333 | 0x45c04c LoadLibraryExA 334 | 0x45c050 RaiseException 335 | 0x45c054 PeekNamedPipe 336 | 0x45c058 SetStdHandle 337 | 0x45c05c Sleep 338 | 0x45c060 DuplicateHandle 339 | 0x45c064 GetCurrentProcess 340 | 0x45c068 FormatMessageA 341 | 0x45c06c CreateNamedPipeA 342 | 0x45c070 CreateFileA 343 | 0x45c074 CreateProcessA 344 | 0x45c078 GetCurrentThreadId 345 | 0x45c07c GetModuleFileNameA 346 | 0x45c080 CreateMutexA 347 | 0x45c084 CreatePipe 348 | 0x45c088 SetHandleInformation 349 | 0x45c08c CloseHandle 350 | 0x45c090 ReadFile 351 | 0x45c094 WriteFile 352 | 0x45c098 GetStdHandle 353 | 0x45c09c WaitForMultipleObjects 354 | 0x45c0a0 WaitForSingleObject 355 | 0x45c0a4 ReleaseMutex 356 | 0x45c0a8 ResetEvent 357 | 0x45c0ac GetOverlappedResult 358 | 0x45c0b0 GetLastError 359 | 0x45c0b4 CreateThread 360 | 0x45c0b8 GetExitCodeProcess 361 | 0x45c0bc GetSystemTimeAsFileTime 362 | 0x45c0c0 GetModuleHandleA 363 | 0x45c0c4 FreeLibrary 364 | 0x45c0c8 LoadLibraryA 365 | 0x45c0cc GetSystemDirectoryA 366 | 0x45c0d0 GetCurrentProcessId 367 | 0x45c0d4 ExitProcess 368 | 0x45c0d8 TerminateProcess 369 | 0x45c0dc GetProcAddress 370 | 371 | 372 | [Exports Overview] 373 | 0x100f OPENSSL_Applink 374 | 375 | 376 | [Resources Overview] 377 | 378 | No.| Resource Name | OffsetToData | Resource Size | Language 379 | 380 | [1]| RT_MANIFEST | 0x5f170 | 381 | LANG_ENGLISH 381 | 382 | [TLS Overview] 383 | 384 | [*] No TLS Callbacks Detected ... 385 | 386 | [MISC. Information] 387 | 388 | [*] Alert -> Most Likely, Executable Uses Dynamic Loading or is Packed [Very Suspecious] 389 | 390 | [*] Exiting MalScan Engine ... 391 | -------------------------------------------------------------------------------- /peutils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: Latin-1 -*- 2 | """peutils, Portable Executable utilities module 3 | 4 | 5 | Copyright (c) 2005-2011 Ero Carrera 6 | 7 | All rights reserved. 8 | 9 | For detailed copyright information see the file COPYING in 10 | the root of the distribution archive. 11 | """ 12 | 13 | import os 14 | import re 15 | import string 16 | import urllib 17 | import pefile 18 | 19 | __author__ = 'Ero Carrera' 20 | __version__ = pefile.__version__ 21 | __contact__ = 'ero.carrera@gmail.com' 22 | 23 | 24 | 25 | 26 | class SignatureDatabase: 27 | """This class loads and keeps a parsed PEiD signature database. 28 | 29 | Usage: 30 | 31 | sig_db = SignatureDatabase('/path/to/signature/file') 32 | 33 | and/or 34 | 35 | sig_db = SignatureDatabase() 36 | sig_db.load('/path/to/signature/file') 37 | 38 | Signature databases can be combined by performing multiple loads. 39 | 40 | The filename parameter can be a URL too. In that case the 41 | signature database will be downloaded from that location. 42 | """ 43 | 44 | def __init__(self, filename=None, data=None): 45 | 46 | # RegExp to match a signature block 47 | # 48 | self.parse_sig = re.compile( 49 | '\[(.*?)\]\s+?signature\s*=\s*(.*?)(\s+\?\?)*\s*ep_only\s*=\s*(\w+)(?:\s*section_start_only\s*=\s*(\w+)|)', re.S) 50 | 51 | # Signature information 52 | # 53 | # Signatures are stored as trees using dictionaries 54 | # The keys are the byte values while the values for 55 | # each key are either: 56 | # 57 | # - Other dictionaries of the same form for further 58 | # bytes in the signature 59 | # 60 | # - A dictionary with a string as a key (packer name) 61 | # and None as value to indicate a full signature 62 | # 63 | self.signature_tree_eponly_true = dict () 64 | self.signature_count_eponly_true = 0 65 | self.signature_tree_eponly_false = dict () 66 | self.signature_count_eponly_false = 0 67 | self.signature_tree_section_start = dict () 68 | self.signature_count_section_start = 0 69 | 70 | # The depth (length) of the longest signature 71 | # 72 | self.max_depth = 0 73 | 74 | self.__load(filename=filename, data=data) 75 | 76 | def generate_section_signatures(self, pe, name, sig_length=512): 77 | """Generates signatures for all the sections in a PE file. 78 | 79 | If the section contains any data a signature will be created 80 | for it. The signature name will be a combination of the 81 | parameter 'name' and the section number and its name. 82 | """ 83 | 84 | section_signatures = list() 85 | 86 | for idx, section in enumerate(pe.sections): 87 | 88 | if section.SizeOfRawData < sig_length: 89 | continue 90 | 91 | #offset = pe.get_offset_from_rva(section.VirtualAddress) 92 | offset = section.PointerToRawData 93 | 94 | sig_name = '%s Section(%d/%d,%s)' % ( 95 | name, idx + 1, len(pe.sections), 96 | ''.join([c for c in section.Name if c in string.printable])) 97 | 98 | section_signatures.append( 99 | self.__generate_signature( 100 | pe, offset, sig_name, ep_only=False, 101 | section_start_only=True, 102 | sig_length=sig_length) ) 103 | 104 | return '\n'.join(section_signatures)+'\n' 105 | 106 | 107 | 108 | def generate_ep_signature(self, pe, name, sig_length=512): 109 | """Generate signatures for the entry point of a PE file. 110 | 111 | Creates a signature whose name will be the parameter 'name' 112 | and the section number and its name. 113 | """ 114 | 115 | offset = pe.get_offset_from_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) 116 | 117 | return self.__generate_signature( 118 | pe, offset, name, ep_only=True, sig_length=sig_length) 119 | 120 | 121 | 122 | def __generate_signature(self, pe, offset, name, ep_only=False, 123 | section_start_only=False, sig_length=512): 124 | 125 | data = pe.__data__[offset:offset+sig_length] 126 | 127 | signature_bytes = ' '.join(['%02x' % ord(c) for c in data]) 128 | 129 | if ep_only == True: 130 | ep_only = 'true' 131 | else: 132 | ep_only = 'false' 133 | 134 | if section_start_only == True: 135 | section_start_only = 'true' 136 | else: 137 | section_start_only = 'false' 138 | 139 | signature = '[%s]\nsignature = %s\nep_only = %s\nsection_start_only = %s\n' % ( 140 | name, signature_bytes, ep_only, section_start_only) 141 | 142 | return signature 143 | 144 | def match(self, pe, ep_only=True, section_start_only=False): 145 | """Matches and returns the exact match(es). 146 | 147 | If ep_only is True the result will be a string with 148 | the packer name. Otherwise it will be a list of the 149 | form (file_ofsset, packer_name). Specifying where 150 | in the file the signature was found. 151 | """ 152 | 153 | matches = self.__match(pe, ep_only, section_start_only) 154 | 155 | # The last match (the most precise) from the 156 | # list of matches (if any) is returned 157 | # 158 | if matches: 159 | if ep_only == False: 160 | # Get the most exact match for each list of matches 161 | # at a given offset 162 | # 163 | return [(match[0], match[1][-1]) for match in matches] 164 | 165 | return matches[1][-1] 166 | 167 | return None 168 | 169 | def match_all(self, pe, ep_only=True, section_start_only=False): 170 | """Matches and returns all the likely matches.""" 171 | 172 | matches = self.__match(pe, ep_only, section_start_only) 173 | 174 | if matches: 175 | if ep_only == False: 176 | # Get the most exact match for each list of matches 177 | # at a given offset 178 | # 179 | return matches 180 | 181 | return matches[1] 182 | 183 | return None 184 | 185 | def __match(self, pe, ep_only, section_start_only): 186 | 187 | # Load the corresponding set of signatures 188 | # Either the one for ep_only equal to True or 189 | # to False 190 | # 191 | if section_start_only is True: 192 | 193 | # Fetch the data of the executable as it'd 194 | # look once loaded in memory 195 | # 196 | try : 197 | data = pe.__data__ 198 | except Exception, excp : 199 | raise 200 | 201 | # Load the corresponding tree of signatures 202 | # 203 | signatures = self.signature_tree_section_start 204 | 205 | # Set the starting address to start scanning from 206 | # 207 | scan_addresses = [section.PointerToRawData for section in pe.sections] 208 | 209 | elif ep_only is True: 210 | 211 | # Fetch the data of the executable as it'd 212 | # look once loaded in memory 213 | # 214 | try : 215 | data = pe.get_memory_mapped_image() 216 | except Exception, excp : 217 | raise 218 | 219 | # Load the corresponding tree of signatures 220 | # 221 | signatures = self.signature_tree_eponly_true 222 | 223 | # Fetch the entry point of the PE file and the data 224 | # at the entry point 225 | # 226 | ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint 227 | 228 | # Set the starting address to start scanning from 229 | # 230 | scan_addresses = [ep] 231 | 232 | else: 233 | 234 | data = pe.__data__ 235 | 236 | signatures = self.signature_tree_eponly_false 237 | 238 | scan_addresses = xrange( len(data) ) 239 | 240 | # For each start address, check if any signature matches 241 | # 242 | matches = [] 243 | for idx in scan_addresses: 244 | result = self.__match_signature_tree( 245 | signatures, 246 | data[idx:idx+self.max_depth]) 247 | if result: 248 | matches.append( (idx, result) ) 249 | 250 | # Return only the matched items found at the entry point if 251 | # ep_only is True (matches will have only one element in that 252 | # case) 253 | # 254 | if ep_only is True: 255 | if matches: 256 | return matches[0] 257 | 258 | return matches 259 | 260 | 261 | def match_data(self, code_data, ep_only=True, section_start_only=False): 262 | 263 | data = code_data 264 | scan_addresses = [ 0 ] 265 | 266 | # Load the corresponding set of signatures 267 | # Either the one for ep_only equal to True or 268 | # to False 269 | # 270 | if section_start_only is True: 271 | 272 | # Load the corresponding tree of signatures 273 | # 274 | signatures = self.signature_tree_section_start 275 | 276 | # Set the starting address to start scanning from 277 | # 278 | 279 | elif ep_only is True: 280 | 281 | # Load the corresponding tree of signatures 282 | # 283 | signatures = self.signature_tree_eponly_true 284 | 285 | 286 | # For each start address, check if any signature matches 287 | # 288 | matches = [] 289 | for idx in scan_addresses: 290 | result = self.__match_signature_tree( 291 | signatures, 292 | data[idx:idx+self.max_depth]) 293 | if result: 294 | matches.append( (idx, result) ) 295 | 296 | # Return only the matched items found at the entry point if 297 | # ep_only is True (matches will have only one element in that 298 | # case) 299 | # 300 | if ep_only is True: 301 | if matches: 302 | return matches[0] 303 | 304 | return matches 305 | 306 | 307 | def __match_signature_tree(self, signature_tree, data, depth = 0): 308 | """Recursive function to find matches along the signature tree. 309 | 310 | signature_tree is the part of the tree left to walk 311 | data is the data being checked against the signature tree 312 | depth keeps track of how far we have gone down the tree 313 | """ 314 | 315 | 316 | matched_names = list () 317 | match = signature_tree 318 | 319 | # Walk the bytes in the data and match them 320 | # against the signature 321 | # 322 | for idx, byte in enumerate ( [ord (b) for b in data] ): 323 | 324 | # If the tree is exhausted... 325 | # 326 | if match is None : 327 | break 328 | 329 | # Get the next byte in the tree 330 | # 331 | match_next = match.get(byte, None) 332 | 333 | 334 | # If None is among the values for the key 335 | # it means that a signature in the database 336 | # ends here and that there's an exact match. 337 | # 338 | if None in match.values(): 339 | # idx represent how deep we are in the tree 340 | # 341 | #names = [idx+depth] 342 | names = list() 343 | 344 | # For each of the item pairs we check 345 | # if it has an element other than None, 346 | # if not then we have an exact signature 347 | # 348 | for item in match.items(): 349 | if item[1] is None : 350 | names.append (item[0]) 351 | matched_names.append(names) 352 | 353 | # If a wildcard is found keep scanning the signature 354 | # ignoring the byte. 355 | # 356 | if match.has_key ('??') : 357 | match_tree_alternate = match.get ('??', None) 358 | data_remaining = data[idx + 1 :] 359 | if data_remaining: 360 | matched_names.extend( 361 | self.__match_signature_tree( 362 | match_tree_alternate, data_remaining, idx+depth+1)) 363 | 364 | match = match_next 365 | 366 | # If we have any more packer name in the end of the signature tree 367 | # add them to the matches 368 | # 369 | if match is not None and None in match.values(): 370 | #names = [idx + depth + 1] 371 | names = list() 372 | for item in match.items() : 373 | if item[1] is None: 374 | names.append(item[0]) 375 | matched_names.append(names) 376 | 377 | return matched_names 378 | 379 | def load(self , filename=None, data=None): 380 | """Load a PEiD signature file. 381 | 382 | Invoking this method on different files combines the signatures. 383 | """ 384 | 385 | self.__load(filename=filename, data=data) 386 | 387 | def __load(self, filename=None, data=None): 388 | 389 | 390 | if filename is not None: 391 | # If the path does not exist, attempt to open a URL 392 | # 393 | if not os.path.exists(filename): 394 | try: 395 | sig_f = urllib.urlopen(filename) 396 | sig_data = sig_f.read() 397 | sig_f.close() 398 | except IOError: 399 | # Let this be raised back to the user... 400 | raise 401 | else: 402 | # Get the data for a file 403 | # 404 | try: 405 | sig_f = file( filename, 'rt' ) 406 | sig_data = sig_f.read() 407 | sig_f.close() 408 | except IOError: 409 | # Let this be raised back to the user... 410 | raise 411 | else: 412 | sig_data = data 413 | 414 | # If the file/URL could not be read or no "raw" data 415 | # was provided there's nothing else to do 416 | # 417 | if not sig_data: 418 | return 419 | 420 | # Helper function to parse the signature bytes 421 | # 422 | def to_byte(value) : 423 | if value == '??' or value == '?0' : 424 | return value 425 | return int (value, 16) 426 | 427 | 428 | # Parse all the signatures in the file 429 | # 430 | matches = self.parse_sig.findall(sig_data) 431 | 432 | # For each signature, get the details and load it into the 433 | # signature tree 434 | # 435 | for packer_name, signature, superfluous_wildcards, ep_only, section_start_only in matches: 436 | 437 | ep_only = ep_only.strip().lower() 438 | 439 | signature = signature.replace('\\n', '').strip() 440 | 441 | signature_bytes = [to_byte(b) for b in signature.split()] 442 | 443 | if ep_only == 'true': 444 | ep_only = True 445 | else: 446 | ep_only = False 447 | 448 | if section_start_only == 'true': 449 | section_start_only = True 450 | else: 451 | section_start_only = False 452 | 453 | 454 | depth = 0 455 | 456 | if section_start_only is True: 457 | 458 | tree = self.signature_tree_section_start 459 | self.signature_count_section_start += 1 460 | 461 | else: 462 | if ep_only is True : 463 | tree = self.signature_tree_eponly_true 464 | self.signature_count_eponly_true += 1 465 | else : 466 | tree = self.signature_tree_eponly_false 467 | self.signature_count_eponly_false += 1 468 | 469 | for idx, byte in enumerate (signature_bytes) : 470 | 471 | if idx+1 == len(signature_bytes): 472 | 473 | tree[byte] = tree.get( byte, dict() ) 474 | tree[byte][packer_name] = None 475 | 476 | else : 477 | 478 | tree[byte] = tree.get ( byte, dict() ) 479 | 480 | tree = tree[byte] 481 | depth += 1 482 | 483 | if depth > self.max_depth: 484 | self.max_depth = depth 485 | -------------------------------------------------------------------------------- /usage_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ice3man543/MalScan/773505adc32d8f55801b1964beeb37e04d9406a7/usage_example.png --------------------------------------------------------------------------------