├── LICENSE ├── README.md ├── impfuzzy_for_Neo4j ├── README.md ├── images │ └── sample.png ├── impfuzzy_for_neo4j.py └── test │ ├── Emdivi.csv │ └── README.md ├── impfuzzy_for_Volatility ├── README.md └── impfuzzy.py ├── impfuzzy_for_Volatility3 ├── README.md └── pehash.py ├── pyimpfuzzy-windows ├── README.md ├── bin │ ├── fuzzy.dll │ └── fuzzy64.dll ├── pyimpfuzzy.py └── setup.py └── pyimpfuzzy ├── README.md ├── impfuzzy_util.c ├── pyimpfuzzy.py └── setup.py /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # impfuzzy 2 | Impfuzzy is Fuzzy Hash calculated from import API of PE files 3 | 4 | ## pyimpfuzzy 5 | Python module for comparing the impfuzzy 6 | 7 | More details are described in the following documents: 8 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy.html (Japanese) 9 | http://blog.jpcert.or.jp/2016/05/classifying-mal-a988.html (English) 10 | 11 | ## pyimpfuzzy-windows 12 | Python module comparing the impfuzzy for Windows 13 | 14 | ## impfuzzy for Volatility 15 | Volatility plugin for comparing the impfuzzy and imphash 16 | 17 | More details are described in the following documents: 18 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy_volatility.html (Japanese) 19 | http://blog.jpcert.or.jp/2016/12/a-new-tool-to-d-d6bc.html (English) 20 | 21 | ## impfuzzy for Volatility3 22 | Volatility plugin for comparing the impfuzzy / imphash / ssdeep 23 | 24 | ## impfuzzy for Neo4j 25 | Python script for clustering malware based on fuzzy hash and importing/visualizing the result using Neo4j 26 | 27 | More details are described in the following documents: 28 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy_neo4.html (Japanese) 29 | http://blog.jpcert.or.jp/2017/03/malware-clustering-using-impfuzzy-and-network-analysis---impfuzzy-for-neo4j-.html (English) 30 | 31 | ## Other Tools or Frameworks 32 | [MISP](http://www.misp-project.org): Malware Information Sharing Platform and Threat Sharing 33 | [CRITs](https://crits.github.io): Collaborative Research Into Threats 34 | [MultiScanner](http://multiscanner.readthedocs.io/en/latest/): File Analysis Framework 35 | [ViruSign](https://www.virusign.com): Malware Research & Data Center, Virus Free Downloads 36 | -------------------------------------------------------------------------------- /impfuzzy_for_Neo4j/README.md: -------------------------------------------------------------------------------- 1 | # impfuzzy for Neo4j 2 | Python script for clustering malware based on fuzzy hash and importing/visualizing the result using Neo4j. 3 | 4 | The following tools and a methods are used: 5 | 6 | [impfuzzy](https://github.com/JPCERTCC/impfuzzy) for fuzzy hash calculation. 7 | [Neo4j](https://neo4j.com) for a graph database. 8 | [The Louvain Method](http://perso.uclouvain.be/vincent.blondel/research/louvain.html) to detect community from large networks. 9 | 10 | More details are described in the following documents: 11 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy_neo4.html (Japanese) 12 | http://blog.jpcert.or.jp/2017/03/malware-clustering-using-impfuzzy-and-network-analysis---impfuzzy-for-neo4j-.html (English) 13 | 14 | ![impfuzzy for Neo4j sample](images/sample.png) 15 | 16 | ## Requirements 17 | This script requires the following modules: 18 | 19 | * pyimpfuzzy https://github.com/JPCERTCC/impfuzzy/tree/master/pyimpfuzzy 20 | * py2neo v3 http://py2neo.org/v3/ 21 | * pylouvain.py https://github.com/patapizza/pylouvain 22 | 23 | ## Usage 24 | 1. Download and install [Neo4j community edition](https://neo4j.com/download/). 25 | 2. Start the Neo4j server. 26 | 3. Enter your Neo4j password to impfuzzy_for_neo4j.py 27 | ``` 28 | NEO4J_PASSWORD = "{password}" 29 | ``` 30 | 4. Import the result of clustering by impfuzzy using impfuzzy_for_neo4j.py 31 | Use -h to see help message. 32 | 33 | * -f - import from file (one EXE or DLL file) 34 | * -d - import from directory (multiple EXE and/or DLL files) 35 | * -l - import from csv file (File Name, impfuzzy hash, md5, sha1, sha256) 36 | 37 | 5. Access http://localhost:7474 via Web browser. 38 | 6. Use Cypher query to see the graph. 39 | 40 | ### Cypher Query Examples 41 | #### Visualizing all clusters 42 | ``` 43 | $ MATCH (m:Malware) RETURN m 44 | ``` 45 | #### Visualizing clusters that match a specific MD5 hash 46 | ``` 47 | MATCH (m1:Malware) WHERE m1.md5 = "149925d0d9ab9bf37af1f38dfe9c2af5" 48 | MATCH (m2:Malware) WHERE m2.cluster = m1.cluster 49 | 50 | RETURN m2 51 | ``` 52 | #### Visualizing clusters that match the impfuzzy threshold greater than 90 53 | ``` 54 | $ MATCH (m:Malware)-[s:same]-() WHERE s.value > 90 RETURN m,s 55 | ``` 56 | -------------------------------------------------------------------------------- /impfuzzy_for_Neo4j/images/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPCERTCC/impfuzzy/b30548d005c9d980b3e3630648b39830597293fc/impfuzzy_for_Neo4j/images/sample.png -------------------------------------------------------------------------------- /impfuzzy_for_Neo4j/impfuzzy_for_neo4j.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # LICENSE 5 | # Please refer to the LICENSE.txt in the https://github.com/JPCERTCC/aa-tools/ 6 | # 7 | 8 | import os 9 | import sys 10 | import csv 11 | import re 12 | import json 13 | import argparse 14 | import hashlib 15 | 16 | # password = {Your neo4j password} 17 | NEO4J_PASSWORD = "{password}" 18 | # neo4j user name 19 | NEO4J_USER = "neo4j" 20 | # neo4j listen port 21 | NEO4J_PORT = "7474" 22 | 23 | try: 24 | from pylouvain import PyLouvain 25 | has_pylouvain = True 26 | except ImportError: 27 | has_pylouvain = False 28 | 29 | try: 30 | import pyimpfuzzy 31 | has_pyimpfuzzy = True 32 | except ImportError: 33 | has_pyimpfuzzy = False 34 | 35 | try: 36 | from py2neo import Graph 37 | has_py2neo = True 38 | except ImportError: 39 | has_py2neo = False 40 | 41 | statement_c = """ 42 | MERGE (m:Malware{ id:{id} }) set m.name={name}, m.impfuzzy={impfuzzy}, m.md5={md5}, 43 | m.sha1={sha1}, m.sha256={sha256}, m.cluster={cluster} 44 | 45 | RETURN m 46 | """ 47 | 48 | statement_r = """ 49 | MATCH (m1:Malware { id:{id1} }) 50 | MATCH (m2:Malware { id:{id2} }) 51 | CREATE (m1)-[s1:same]->(m2) set s1.value={value} 52 | 53 | RETURN m1, m2 54 | """ 55 | 56 | parser = argparse.ArgumentParser(description="impfuzzy for neo4j") 57 | parser.add_argument("-l", "--list", dest="listname", action="store", metavar="LIST", 58 | help="Hash List (File Name, impfuzzy hash, md5, sha1, sha256)") 59 | parser.add_argument("-d", "--directory", dest="directory", action="store", metavar="DIRECTORY", 60 | help="Malware Directory") 61 | parser.add_argument("-f", "--file", dest="file", action="store", metavar="FILE", 62 | help="Windows Executable File (EXE, DLL)") 63 | parser.add_argument("-t", "--threshold", dest="threshold", action="store", type=int, metavar="THRESHOLD", 64 | help="Impfuzzy hashing threshold (Default 30)") 65 | parser.add_argument("--delete", action="store_true", default=False, 66 | help="Delete all nodes and relationships from this Neo4j database. (default: False)") 67 | parser.add_argument("--nocluster", action="store_true", default=False, 68 | help="Not clustering the malware. (default: False)") 69 | args = parser.parse_args() 70 | 71 | 72 | # Calculate hash 73 | def get_digest(file): 74 | md5 = hashlib.md5() 75 | sha1 = hashlib.sha1() 76 | sha256 = hashlib.sha256() 77 | 78 | try: 79 | impfuzzy = pyimpfuzzy.get_impfuzzy(file) 80 | except: 81 | impfuzzy = "" 82 | 83 | with open(file, "rb") as f: 84 | while True: 85 | buf = f.read(2047) 86 | if not buf: 87 | break 88 | md5.update(buf) 89 | sha1.update(buf) 90 | sha256.update(buf) 91 | 92 | return impfuzzy, md5.hexdigest(), sha1.hexdigest(), sha256.hexdigest() 93 | 94 | 95 | # Compare impfuzzy 96 | def impfuzzy_comp(list, list_new): 97 | ssdeep = re.compile("^[0-9]{1,5}:[0-9a-zA-Z\/\+]+:[0-9a-zA-Z\/\+]+$", re.DOTALL) 98 | complist = [] 99 | list_len = len(list_new) 100 | i = 0 101 | for item_new in list_new: 102 | i += 1 103 | if re.search(ssdeep, item_new[2]) and len(item_new[2]) < 150: 104 | for j in range(i, list_len): 105 | if re.search(ssdeep, list_new[j][2]) and len(list_new[j][2]) < 150: 106 | complist.append([item_new[0], list_new[j][0], pyimpfuzzy.hash_compare(item_new[2], list_new[j][2])]) 107 | else: 108 | ("[!] This impfuzzy hash is not ssdeep format: %s" % item_new[2]) 109 | 110 | if list: 111 | for item_new in list_new: 112 | if re.search(ssdeep, item_new[2]) and len(item_new[2]) < 150: 113 | for item in list: 114 | if re.search(ssdeep, item[2]) and len(item[2]) < 150: 115 | complist.append([item_new[0], item[0], pyimpfuzzy.hash_compare(item_new[2], item[2])]) 116 | else: 117 | ("[!] This impfuzzy hash is not ssdeep format: %s" % item[2]) 118 | else: 119 | ("[!] This impfuzzy hash is not ssdeep format: %s" % item_new[2]) 120 | 121 | return complist 122 | 123 | 124 | def main(): 125 | if not has_pyimpfuzzy: 126 | sys.exit("[!] pyimpfuzzy must be installed for this script.") 127 | 128 | if not has_py2neo: 129 | sys.exit("[!] py2neo must be installed for this script.") 130 | 131 | if not has_pylouvain and not args.nocluster: 132 | sys.exit("[!] Please download the pylouvain from https://github.com/patapizza/pylouvain.") 133 | 134 | try: 135 | graph_http = "http://" + NEO4J_USER + ":" + NEO4J_PASSWORD +"@:" + NEO4J_PORT + "/db/data/" 136 | GRAPH = Graph(graph_http) 137 | except: 138 | sys.exit("[!] Can't connect Neo4j Database.") 139 | 140 | if len(sys.argv) == 1: 141 | parser.print_help() 142 | sys.exit() 143 | 144 | i= 0 145 | hashlist = [] 146 | hashlist_new = [] 147 | nodes = [] 148 | edges = [] 149 | relationships = [] 150 | 151 | # This is a impfuzzys threshold 152 | if args.threshold: 153 | ss_threshold = args.threshold 154 | else: 155 | ss_threshold = 30 156 | print("[*] Impfuzzy threshold is %i." % ss_threshold) 157 | 158 | # Delete database data 159 | if args.delete: 160 | GRAPH.delete_all() 161 | print("[*] Delete all nodes and relationships from this Neo4j database.") 162 | 163 | # Load database data 164 | database = GRAPH.data("MATCH (m:Malware) RETURN m.id, m.name, m.impfuzzy, m.md5, m.sha1, m.sha256") 165 | 166 | if database: 167 | print("[*] Database nodes %d." % len(database)) 168 | for d in database: 169 | hashlist.append([d["m.id"], d["m.name"], d["m.impfuzzy"], d["m.md5"], d["m.sha1"], d["m.sha256"]]) 170 | 171 | nodes_count = len(database) 172 | # Load relationships 173 | relation_data = GRAPH.data("MATCH (m1:Malware)-[s:same]-(m2:Malware) RETURN m1.id,m2.id,s.value") 174 | if relation_data: 175 | print("[*] Database relationships %d." % len(relation_data)) 176 | for r in relation_data: 177 | relationships.append([r["m1.id"], r["m2.id"], r["s.value"]]) 178 | 179 | for x in range(nodes_count): 180 | nodes.append(x) 181 | 182 | print("[*] Creating a graph data.") 183 | 184 | # Import data from EXE or DLL 185 | if args.file: 186 | if os.path.isfile(args.file): 187 | i = nodes_count 188 | impfuzzy, md5, sha1, sha256 = get_digest(args.file) 189 | query = "MATCH (m:Malware) WHERE m.sha256=\"%s\" RETURN m" % sha256 190 | if impfuzzy: 191 | if not GRAPH.data(query): 192 | nodes.append(i) 193 | hashlist_new.append([i, args.file, impfuzzy, md5, sha1, sha256]) 194 | else: 195 | print("[!] This malware is registered already. sha256: %s" % sha256) 196 | else: 197 | print("[!] Can't calculate the impfuzzy hash. sha256: %s" % sha256) 198 | else: 199 | sys.exit("[!] Can't open file {0}.".format(args.file)) 200 | 201 | # Import data from directory 202 | if args.directory: 203 | try: 204 | files = os.listdir(args.directory) 205 | except OSError: 206 | sys.exit("[!] Can't open directory {0}.".format(args.directory)) 207 | 208 | outf = args.directory + "_hash.csv" 209 | fl = open(outf, "w") 210 | i = nodes_count 211 | for file in files: 212 | filename = args.directory + "/" + file 213 | impfuzzy, md5, sha1, sha256 = get_digest(filename) 214 | fl.write("%s,%s,%s,%s,%s\n" % (file, impfuzzy, md5, sha1, sha256)) 215 | query = "MATCH (m:Malware) WHERE m.sha256=\"%s\" RETURN m" % sha256 216 | if impfuzzy: 217 | if not GRAPH.data(query) and sha256 not in [x[5] for x in hashlist_new]: 218 | nodes.append(i) 219 | hashlist_new.append([i, file, impfuzzy, md5, sha1, sha256]) 220 | i += 1 221 | else: 222 | print("[!] This malware is registered already. sha256: %s" % sha256) 223 | else: 224 | print("[!] Can't calculate the impfuzzy hash. sha256: %s" % sha256) 225 | print("[*] Created hash list %s." % outf) 226 | fl.close() 227 | 228 | # Import data from csv file 229 | if args.listname: 230 | print("[*] Parse file %s." % args.listname) 231 | try: 232 | csvfile = csv.reader(open(args.listname), delimiter=",") 233 | except IOError: 234 | sys.exit("[!] Can't open file {0}.".format(args.listname)) 235 | 236 | i = nodes_count 237 | for array in csvfile: 238 | query = "MATCH (m:Malware) WHERE m.sha256=\"%s\" RETURN m" % array[4] 239 | if array[1]: 240 | if not GRAPH.data(query): 241 | nodes.append(i) 242 | array.insert(0, i) 243 | hashlist_new.append(array) 244 | i += 1 245 | else: 246 | print("[!] This malware is registered already. sha256: %s" % array[4]) 247 | else: 248 | print("[!] Impfuzzy hash is blank. sha256: %s" % array[4]) 249 | 250 | # Compare impfuzzy 251 | print("[*] The total number of malware is %i." % i) 252 | result_list = impfuzzy_comp(hashlist, hashlist_new) 253 | 254 | if len(database) != len(nodes): 255 | # Clustering 256 | if not args.nocluster: 257 | for edge in result_list + relationships: 258 | if edge[2] > ss_threshold: 259 | edges.append([[edge[0], edge[1]], edge[2]]) 260 | else: 261 | edges.append([[edge[0], edge[1]], 0]) 262 | pyl = PyLouvain(nodes, edges) 263 | partition, modularity = pyl.apply_method() 264 | print("[*] The number of clusters is %i." % (len(partition) - 1)) 265 | else: 266 | print("[*] No clustering option.") 267 | 268 | # Create node 269 | tx = GRAPH.begin() 270 | if args.nocluster: 271 | for hash in hashlist_new: 272 | tx.append(statement_c, {"id": hash[0], "name": hash[1], "impfuzzy": hash[2], 273 | "md5": hash[3], "sha1": hash[4], "sha256": hash[5], 274 | "cluster": "NULL"}) 275 | else: 276 | for hash in hashlist_new + hashlist: 277 | i=0 278 | for a in partition: 279 | i=i+1 280 | if hash[0] in a: 281 | tx.append(statement_c, {"id": hash[0], "name": hash[1], "impfuzzy": hash[2], 282 | "md5": hash[3], "sha1": hash[4], "sha256": hash[5], 283 | "cluster": i}) 284 | 285 | # Create relationship 286 | for result in result_list: 287 | if result[2] > ss_threshold: 288 | tx.append(statement_r, {"id1": result[0], "id2": result[1], "value": result[2]}) 289 | 290 | tx.process() 291 | tx.commit() 292 | print("[*] Created a graph data.\n") 293 | else: 294 | print("[*] Not find a new malware.\n") 295 | 296 | print(" Access to http://localhost:7474 via Web browser.") 297 | print(" Use Cypher query. You can see the graph.\n") 298 | print(" == Cypher Query Examples ==") 299 | print(" [Visualizing the all clusters]") 300 | print(" $ MATCH (m:Malware) RETURN m\n") 301 | print(" [Visualizing the clusters that matches the MD5 hash]") 302 | print(" $ MATCH (m1:Malware)-[s]-() WHERE m1.md5 = \"[MD5]\"") 303 | print(" MATCH (m2:Malware) WHERE m2.cluster = m1.cluster") 304 | print(" RETURN m2\n") 305 | print(" [Visualizing the clusters that matches the threshold more than 90]") 306 | print(" $ MATCH (m:Malware)-[s:same]-() WHERE s.value > 90 RETURN m,s") 307 | print(" ===========================\n") 308 | 309 | 310 | if __name__ == "__main__": 311 | main() 312 | -------------------------------------------------------------------------------- /impfuzzy_for_Neo4j/test/Emdivi.csv: -------------------------------------------------------------------------------- 1 | t100.08.21.fuck0,48:KX1zaVOAX+epCxDDpytX0TGVcU9aGfECES/8t/6imq7L62o1:KX1zYFpCxDDpytX0GVcCaGknPPs,f753b044b7f16ff203b2552df92d79fa,e217b278a9225362629f092247fec985e5a8c97f,439e7959e87d11b6bb1ad8b4d8b3641ff22a65caec3c29c807480c4c7bebe8af 2 | t15.07,48:bZToOG3l2s/pgdZ+fcfthqGqpXJZpQkKQ3/JG4EkEnc/ZtZk54l:buB3l2AYZ+fcfthqGqlTpS7Y3Zke,1c462660b33130f5e9c2ad664eeedb40,4adcb6e32249eb11e57101dfeffc6e102aa132b3,76d9de0755f87b58aaeecaa42abb5565063376eeb5ca970112e7248f16deb6bf 3 | t16.19,96:8Fv7R9nJRWVda6m6aFfkCFuVaSfaCMfaCPYfaCHHb8Ewb8EOuxQXOhQ6paMBtCKU:YQzTq2OLSu+bQM0OwjWh/1,402406d85bcec25b14e2baa1c7b9584c,c6c644537669dad8ef57d50885be95027b74b0fb,39dbafb7c93dfda23f1743d8409ecad198b7c0f3050228f0c9c8b35e8201b8d7 4 | t17.08.10,48:JQpx+XX1IOOFjwuzntdcpsS9vgAEkEU3KQkE8VKl/ZGr4:YUXX1I9FcantdcpsovwXVKJAs,82e623bef94961906a0f7b922b9da566,8859d33a569d6afe4c01d4610ae492e451eaabf2,b97ab11d1154fae07d2cfab055cdc6b745a5117fc1d8e557f6a244040ba7cce3 5 | t17.08.10,48:JQpx+XX1IOOFjwuzntdcpsS9vgAEkEU3KQkE8VKl/ZGr4:YUXX1I9FcantdcpsovwXVKJAs,f1c8131bc391b4441f1b072a22d80534,d0bdc6a874ff8d5d0c5894862b531a95c10fbbf1,04b9a6ef5ef6cdaf42e90431039bd56b68082c5056889cf4b9ababc6e0834b56 6 | t17.08.16,48:QCVmX1IOYxJF+upntdcpsSpv7T/8tKQECE8i+AZcGzmX:rgX1ITxJoMntdcpsGv753+1X,2b5e441eec4ed461dc16110c41e7e74c,55e46c22111b5e0e9336dc1b74a879251a38af8d,c805af2204c1d8612cd929b93fc5c38a448a03561d410d7a198c313553e47e39 7 | t17.08.16,48:QCVmX1IOYxJF+upntdcpsSpv7T/8tKQECE8i+AZcGzmX:rgX1ITxJoMntdcpsGv753+1X,dd8b4434fea3c91a95fa124b0a2ea8e0,fd4ed04a48d052ad894d6681d39b8769145776a6,3243925baa06dc69731da91da49242fd73aea38afe46e171708de4ecd4e53b80 8 | t17.08.16,48:QCVmX1IOYxJF+upntdcpsSpv7T/8tKQECE8i+AZcGzmX:rgX1ITxJoMntdcpsGv753+1X,5c53e0de724ad4c9e5f10d5c7aeedff6,dc94816d25083cb2e7a60adbaf330c22d9bb2fa5,df97dd9607f0fdcc10f9ba99e6c3d01eb8453ceeeab840ef6b965458e24485bc 9 | t17.08.16,48:QCVmX1IOYxJF+upntdcpsSpv7T/8tKQECE8i+AZcGzmX:rgX1ITxJoMntdcpsGv753+1X,67724ba79d92832b8dc132ebafe4f53a,5bd337b2b29b6f80ce6014efe60db0d9f07d0f99,d26eb51e2787353b18c8f290f0710510423e3925a796697ff15aafd14fea6f2d 10 | t17.08.16,48:qP2HJpX1IOYxJEep+gttAGVcU9cec/8tKQECEa0mPIMzN:ssJpX1ITxJP+gtmGVcCce2d9Y,713c28766673eaab5c73bd1de527be7c,0e18b5ffe79ee98fad1d57b561a25e46a24be4d9,9fc76d0fb4f01819c0d9af09a0357dab6c33a4d5f6e41cafebeb9ef7ae35c99e 11 | t17.08.16,48:QCVmX1IOYxJF+upntdcpsSpv7T/8tKQECE8i+AZcGzmX:rgX1ITxJoMntdcpsGv753+1X,bccd0d36e58a4e5a3214efdad0eaf971,848d3c54eaaa2770617664e1ba7fb362a93d5344,37f43f9c4298dc41f6b1ed03396cc1f7da664ed25e97c4263e6c360f59f3a51b 12 | t17.08.18,48:2xX17F+epJOYxXZpytX0TGVcU9aGfECES/8t/60+Aq75kao1:GX173JTxXZpytX0GVcCaGkH+hK1,fee4459ef67451a07a13a9e40a8fb441,0a65aadce08484be717b807776b22ac21785be0f,f017218f05d225cdb62f3081c4dac4b09a3fb2b93c01096bd4141b67d3eb3bbf 13 | t17.08.18,48:2xX17F+epJOYxXZpytX0TGVcU9aGfECES/8t/60+Aq75kao1:GX173JTxXZpytX0GVcCaGkH+hK1,6c6a2feaed2c33640b1b76c60e7e1f2b,2c35e572fca2599caf4565cb7d569f37ff3fb5ba,139e22abe7aaec635e2b570935636c4894a19a7b284516b77f190b78a369c4d6 14 | t17.08.18,48:2xX17F+epJOYxXZpytX0TGVcU9aGfECES/8t/60+Aq75kao1:GX173JTxXZpytX0GVcCaGkH+hK1,83637b6710e76720849909eecc1cd7bd,f0b8f1b71f82c41b111dfe9572203bef91b65048,a00e37d1d3fe990ebac26a4805a7ab42bd1dcf7ef65f151906204eee7b0c71fd 15 | t17.08.18,48:2xX17F+epJOYxXZpytX0TGVcU9aGfECES/8t/60+Aq75kao1:GX173JTxXZpytX0GVcCaGkH+hK1,cf8b4d2fbd7622881b13b96d6467cdab,9f449b5d156b1d69ef23fbe9d3480058db480b1d,196364b3e78add557b6f0471fb32061468bb2b20e16acd1a7686122234c984a7 16 | t17.08.18,48:N6X13VyJmOYxeYep3gttAGVcU9ceq8tECES//60mqefq4B:kX13IJmTxer3gtmGVcCcerHPIB,1babb4fd07612955acad779d5028803f,f8069747f82f56b2ab7cdab7676a095a76947cbb,92860e0a9e7dc49c43a0db87d4fb345294000ac3191af1dc6d702b89628c97eb 17 | t17.08.18,48:2xX17F+epJOYxXZpytX0TGVcU9aGfECES/8t/60+Aq75kao1:GX173JTxXZpytX0GVcCaGkH+hK1,39279516e711416208e74b39a2247ba9,8e607b851f331a0866316d01204f7c84fa3d0209,3d084155e6f79b45acba165cd4a17a3bed42daba478c14a795dc2c2809f302b6 18 | t17.08.21.3,48:9OfX1zahLI+epQxDZpKtX0TGVcU9aGlESEC/8t/6imf/Eomt:9sX1zmLQQxDZpKtX0GVcCaGOniEt,7525f3072168f87d4ada8d04c4b635ad,5351e577ca547bd5c2aa5f04fdb79a87d9989090,c89823eba2bdcdfcae33b33fb358154debe3fd88c75c684aa6b510e2d4b3ca53 19 | t17.08.21,48:KX1za3OsLO+epCxJZpytX0TGVcU9aGfECES/8t/6imq75kao1:KX1zaRLeCxJZpytX0GVcCaGknPK1,ae345f9833ac621cf497141b08ad34c2,e9c38b66cd13ff4a8f331389c2da172fd44b66ca,1209d8b3c83c72df781b805a2c17a0939c841384aadc32e4e9005536a3bba53f 20 | t17.08.21,48:KX1za1OCLi+epCxhZpytX0TGVcU9aGfECES/8t/6imq7hgao1:KX1zULLCCxhZpytX0GVcCaGknPm1,b4d519b36431b119150aba7b1e5d265d,02c3cd13dbca918f54a07d32598df5251535b437,884cbc1f0e70efae4815127bda7bab50883a707581d9d4061d268249c154ff2d 21 | t17.08.21,48:tX1zaeLO+epuOYxJZpytX0TGVcU9aGfECES/8t/60mq75kao1:tX1zzLeuTxJZpytX0GVcCaGkHPK1,8bf944283987de847851d3d2279b8cf8,b1b4c62e96cae7950e0bba95f89cc3fd1cbcff4d,b99f08be6a476d359820c48345ddf4f2f0fcc1ca041f3630680635c675a1d7be 22 | t17.08.21,48:KX1za1OC1+epCxhZpytX0TGVcU9aGfECES/8t/6imq7hgao1:KX1zULHCxhZpytX0GVcCaGknPm1,53246946ab835b0e87cafcbb0adc6195,e8e0b344ee6e5a7098b0503f4a48b9924b33fc82,682b6c9d468e8d0ab8b5d4080cecf52a9dd66b59b99936a4941b8190c5f3fff9 23 | t17.08.21,48:tX1zaeLO+epuOYxJZpytX0TGVcU9aGfECES/8t/60mq75kao1:tX1zzLeuTxJZpytX0GVcCaGkHPK1,e4fc0ce4d1fd8c91eed4748721f279a8,299e191984b2bc6550e7bf06eaabff8ca7a4effa,878937da134339ccd8c6bbc5ac020472c20a42fb1f07b56152cfcc1656077d62 24 | t17.08.21,48:tX1zaeLO+epuOYxJZpytX0TGVcU9aGfECES/8t/60mq75kao1:tX1zzLeuTxJZpytX0GVcCaGkHPK1,dccc63cd649b439d31afd0674bcab1a1,828e4d1591d2826b501a867698bf1ad7ddeba3ff,8c3666940afd65835e4251fbd14942d210323d46adf57c5e8f29b61d552fd386 25 | t17.08.2,48:wPZ8X1IO2x4vXpntdcpsSp2iP/T0xmAFrmg:GCX1IFx4ZntdcpsG2iXYxm3g,02646e77c0f38fa94e906f343984f2fe,03e87a5d96f1006d368622c5a03686e9fa9ca567,c162df8761e09c95160e9d432d310a4673d53615c2ff837a1a6f322e45038180 26 | t17.08.2,48:wPZ8X1IO2x4vXpntdcpsSp2iP/T0xmAFrmg:GCX1IFx4ZntdcpsG2iXYxm3g,2cd8e88ca082b6dd7b96f66a279d9c3f,59fc06fc0f9078011564fee9ca72e911bd8f2fa6,bf8cba80f4d80e13f11c8231477f0b96c3a9e9abc8da798e6cede052f6801aa8 27 | t17.08.2,48:wPZ8X1IO2x4vXpntdcpsSp2iP/T0xmAFrmg:GCX1IFx4ZntdcpsG2iXYxm3g,9966d2add4e0b4fcba2ba217a1ad384a,8f3fc0e5c9382a2403a9a5cb51ea5bf00f31a318,742c70238ea0b2b0a1d66660913b18deaff2af35c6dc5b19e9d2158249cae433 28 | t17.08.2,48:KWLEX1IOOFdbrTXzntdcpsS925EkEU3KQkVapKl/Jdc+bilBRz:ZEX1I9FJ/Dntdcpso2MEpKJJG+bQRz,b8d9aa534bec288115ec7cab14f52fe2,8190b54f160769f4fa1358f5ff4b129571e21e23,bf9229b342c144970358308ccb017802cb2ff5c2086bf0367d9d72f34556b7c1 29 | t17.08.2,48:nP2BX1IO2x80VXpntdcpsSp2jEC8tKQ/ES0xmAK9A4PilBRz:PeX1IFxtVZntdcpsG2XVxmLm4PQRz,31d67411a63cf8dc0485ffc474259825,116eaf0af542ddd4d52a0b17f3422fe1484e54c6,08a542fe7f8450d2c66b5e428872860d584bc5be714a50293a10aef415310fe8 30 | t17.08.2,48:KWLEX1IOOFdbrTXzntdcpsS925EkEU3KQkVapKl/Jdc+bilBRz:ZEX1I9FJ/Dntdcpso2MEpKJJG+bQRz,2f316d834dba64ebebd69e47b1cdabe5,3016c6a1305b7f1101a43dfd2dbaa4e7b4b707aa,2bf87ec696356a685b081b9e0aec88c3ac3e3353927f712e978db0d2f5a9476b 31 | t17.08.23.Elec,24:dj0DBp5IfKOCX109vyVTJ+uQlARw1ipOovGntdcpluiRvpxjMDkGySHO9giNwxr+:6ICOCX1GBlAWppntdcpsSptz6iGa,6df5d36d8f066137b136a685aeaa2dde,f8bdc1f39b0e8e3ecbdac27afb5345f97fe14955,6c4c3bc7b0dfe531790bfb023b141c23f3c17a9971fed704d1b46e43f97d41c1 32 | t17.08.23.jmu,48:R/COfX1nbjiWppntdcpsSptzTEC/KYZM06:R/CsX1n/iWTntdcpsGBBKYX6,638732a9f10fe0765e3e161e18c2227b,6e5fcfb5f789cca403a82d1de4bf05b095ca89a5,21a51f69d08aaf0aaaeb5b8413bb710c1727d9d08a9a1f46883f6f93691e0870 33 | t17.08.23.jtdndf,24:dj0DBp5IfKOCX109vyVTJ+uQlypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmog1:6ICOCX1GBlypbtXAPTGVcUD/mAtyiGz,f9fca01b38f1fb5e04b0c09d0e0730b3,c471179455a6de44bb873bcdd9da661fa7997981,9801caaf44ce9a6be3f497e706f5b71dcc7c50351374c33dc2c9fcbb55f55e05 34 | t17.08.23.nenme,48:UOCOCX1JHep0btXAPTGVcUD/mAtsEC/scI:UOCnX1J40btXALGVci/mI0Q,fc6f9b6c7402d1018f69f3f665f81c28,e246baefea2244cef2927c6c543da712373d5c5a,b19a233b07a1342f867aef1b3fb3e473b875bd788832bb9422cacb5df1bda04e 35 | t17.08.2,48:wPZ8X1IO2x4vXpntdcpsSp2iP/T0xmAFrmg:GCX1IFx4ZntdcpsG2iXYxm3g,052e29f6be0319644b03e15313bccb42,fe9e9a1476ffa127fdc8ffc1c2089160f8d940dd,18dfb3ff38c802f54c66c7d06380e7aff4834ac7a0c9ea35e50f46cf40266c3a 36 | t17.08.23,48:JTCOCX18U+ep/DtXAPTGVcU9/mrdAKQ8trCz:JTCnX18c/DtXALGVcC/mrdPRs,365f6b4ef127bc2adf445f3b19615cc2,492e3f0a02de03faf70951aa453bb1ce70dad894,23449109f0d4b07fd8010bb36b3b1084b48d5ac515725b68bf32322b4902397e 37 | t17.08.23,48:6COCX1QHUyep4WtXAPTGVcUD/mAtjAKQ8tabo:6CnX1QHUV4WtXALGVci/mIbp,c248bd02cf6468cb97a34b149701ec94,cd3b0910c99246d2a75c8936fc2332a3a77c6e3e,a79cfba79489d45a928ef3794d361898a2da4e1af4b33786d1e0d2759f4924c3 38 | t17.08.2,48:wPZ8X1IO2x4vXpntdcpsSp2iP/T0xmAFrmg:GCX1IFx4ZntdcpsG2iXYxm3g,1ec4a27d33fd86739abd9f8d4d30dff4,0bda82f59605fd65711041b86b487f3f9a6501a0,6b0192ec4f0290c0c00517eeb75648e340dacc58189d9d6adee844283cda4a5f 39 | t17.08.25.IE,24:B5NvfKOCX17DT9vyVTJ+uQZjqypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmogu:bFCOCX1JBIypbtXAPTGVcUD/mAty/dQ,5724bf44ec5ab245aff8c614678af8d5,dd114f1324b1c1423d25f08db8b6033a87446ec7,29a480579353c85e48b996ebc38cad9313ad6b9e495a3a69bf1519837acab04f 40 | t17.08.25.K150113,24:V0Da5NvfKOCX1wKu+9vyVTJ+uQqypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmn:hFCOCX11BqypbtXAPTGVcUD/mAty/+O,65b51e624c79ff5e69e4459de86c1fcd,bb347814165836682433a2c1c1de5f6b2d30525d,9e74825e251a4f4cef9bc98273082f3b58695a224b1ed16ba6dedaa4c154cb21 41 | t17.08.25.Meti,48:VFCOCX1LB+ypbtXAPTGVcUD/mAty/MSJQ:VFCnX1LAsbtXALGVci/mIUDK,9d437f6e47039ae40bf69c3b4982abce,2e6f79cd75498d76a52a4e29c24babc5e646fba0,28a774235865924a7fec405aaf6463164a03f6e646c9fd964c3191304e59d35b 42 | t17.08.25.Mki1208,48:bFCOCX18PeepTrtXAPTGVcUD/mAtyKQE0kB0:bFCnX18P5TrtXALGVci/mIdgT,3e384130131ee3a5bf5aae19f6017fd7,d6eb379bd003a7b199ee694df3b43673d9672685,34bc147423f565bf38100913d25f85057e252755eef622abc1b788d511caf605 43 | t17.08.25.Nict1212,24:mDa5NvfKOZjlX1JA9vyVTJ+uQhNypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOm/:VFCOfX1UBPypbtXAPTGVcUD/mAty/+S,3f4c0b73cf13ffc0544085639745a9d2,9dc28dc5be21f36f4293a649d2184ae029659e3c,a188b87e495e4b0aad0d0595987677f9758479b120fb2ed3a04fba308a66830a 44 | t17.08.25.Nict1217,48:XCOCX1/ZO+epUSWtXAPTGVcUD/mrdAKQ8tEfco:XCnX1/ZeUSWtXALGVci/mrdPM,72ffb562c6a0e59d3d5a04172362838b,6ad0efcbedf3b6d134935231d6b08c51391c7860,e39b1b36a5da4ad0f9c103478ab469b13a0528540ddbd1679eb24349a6726dbf 45 | t17.08.25.Nict122,24:V0Da5NvfKOCX1wS9vyVTJ+uQqypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmogc:hFCOCX1jBqypbtXAPTGVcUD/mAty/+T,05edc5d5bd9bda9ac8a75392b4231146,94191f06219531205cf05e7ca05cb350b5131c90,037b0dbfc2643a4a4779f6e3a8e5c8c41cbcd64533d2245c9a26dfd1d4f55dd8 46 | t17.08.26.JADI0120,48:hFCOCX1wBepcwtXAPTGVcUD/mAtsKQEfb:hFCnX1wicwtXALGVci/mIDf,6701efb6306fb3919cde58b82d42712d,c7870e1b93c98d1d8177160f8b5eeddcf8aab0bf,635b43f7c0508f5e2cbf26f81daf0a730a0f0b06303c54c747b780f91430bb7f 47 | t17.08.26.KENPO012201,48:bFCOCX1JBep0btXAPTGVcUD/mAts8t/MSJBHG:bFCnX1Ji0btXALGVci/mIxDe,3bdb9ab7caa2a9285b4ed04fe1c4753b,16ff2ea534d90b90dfd67470f0300b6d79a993db,efa57d43145de9a1e3c7541f94837a9c7b76d604b779d9847637d4a55b1ee723 48 | t17.08.26.KENPO0123,24:QNvfKOCX1sl9vEVTJ+uQyDojqypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmog3:QFCOCX14b2ypbtXAPTGVcUD/mAty/2Q,b582d899d519aaa8bb5a5c8b13bc6f76,67805443e94f25608d396ad4e014a4fca8c7aba2,9ace48ecef568bb9f5ccd462ca3efb4c2fbc15f0316323f1729e88cbe184158d 49 | t17.08.26.kenpo0203,24:V0Da5NvfKOCX1xo9vyVTJ+uQ7ypaR+tsipOovdGVcQRvDxjMmkOmgmqFQHOmm/Zw:hFCOCX10B7ypagtsp+GVcUDlmgDN/hQ,8cc0f235189efcf3fe1c4ccc7527fcfc,cdbbcd70452fd84fe4612a7fe2208077fb8fa8ee,6aed51b108d9f9f197842e17b0f58d4dec3709ca1eae4d42146d0bba0c145eaf 50 | t17.08.26.Ntago0126,24:QNf9fKOCX11d2VTJ+uQyDojAp2vb3mtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmoX:QnCOCX1RUp2TWtXAPTGVcUD/mAty/vo,a64bb1ed1f8210ef13fe686621161699,29a6ef1c5cb140e44efe96d5ba9974c5c2ee147b,9ebef65f00fc6ad70f591f7fb1f39f0f6b1766ff3fd9f47693ce669e70f84abb 51 | t17.08.26.SAJN0123,48:QgCOCX1hji+ep+ltXAPTGVcU9/mrb8t/cb:QgCnX1hjC+ltXALGVcC/mrAq,fcc4820790d8bf2c0cd654b594b791e1,4efdea74934de75acd111b958397b84ccd80f010,42e6b7afe4da672ab9bf647e73201135b3faf2121b629612b35307dc0d8698e4 52 | t17.08.26.SOF0120,48:xCOfX1AYepLTWtXAPTGVcUD/mAtyKQE0dd:xCsX1ArLTWtXALGVci/mIdgz,b19d9aa5bcede2aa8648b85308ede71c,ddd32e62227928c709648329b92447072a41efa7,5e221bd0eef231b7a948d8f6a2f660f8d6685cf2711fe50311485227ebcf9e51 53 | t17.08.27.CCI0304,48:bnCOCX1Vtyep/MtXAPTGVcU9/mAtsES/KQE1bUN:bnCnX1VtV/MtXALGVcC/mIfK,fb66a2d53d7e20056a80a0f1cf5471f4,ab6be1a0e4ab25f45d0536028765a3a7db46bb7c,fca765c535d1870d71ee152e5b004e73515ade1ee1c9a512a0858a508380465d 54 | t17.08.27.CCI030501,24:V0Da5NvfKOCX1xo9vyVTJ+uQ7ypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmog9:hFCOCX10B7ypbtXAPTGVcUD/mAty/EQ,d97c74719ba0caf6c3e8b8e17427f3ec,8eefca90b4167e4a1061a31fc6d4479c1fa8cbed,eac8441227077edb28adf096c5493710e2ca1978f4e4c4b2b93d481cd482d890 55 | t17.08.27.DUM0324,48:VFCOCX1Bfepr8tXAPTGVcU9/mAthEC//saUDt:VFCnX1Bgr8tXALGVcC/mI/sl,f7382f17a387cbed5f5bce00deb78e5d,8e65bd96945835f4d70e793396bb46655f071304,400a08b4a067b1e2fb3bee509bf933a746cf3ef2d000bb3181c7176344641a01 56 | t17.08.27.ELE0317,48:QFCOCX1fyO+FypbtXAPTGVcUD/mry/sSQ:QFCnX1fmsbtXALGVci/mrUsT,db9648a79689ecf615fc8da750a938ef,f633d7d0993084fb11ed1baa0b8835b050fcf022,9f66ad282373b8b0df45dd32723dcdfcd4821e22cba4912678c3c8632e722730 57 | t17.08.27.FSA0302,48:bFCOCX18PyepTftXAPTGVcUD/mAtdAKQENau:bFCnX18PVTftXALGVci/mIdPBf,1261b41025f53278f7efcfbf462e9b5c,641dd27166e47b54e189aba61d5881a4cae894a5,f6fce0464f1ad8044092e6812bdfb8545e1df5ee23aba828b4dcb86fb6d0e62b 58 | t17.08.27.sb0319,48:XCOCX1r1epot9tXAPTGVcUD/mAtjAES/hUDz:XCnX1r+ot9tXALGVci/mIMm,ed60b19b2d1e5fc8ca1187dec08e2e1a,cbda27014154635d9f393b4fa3fb1fc8a34aef8e,77fa012060884d17eea1e54d97176a7a88c499f03315dfd602c1e1e17e556ede 59 | t17.08.27.sb0320,48:bnCOfX1dPeep/DtXAPTGVcUD/mAtdAKQ8tE3YZl:bnCsX1dP5/DtXALGVci/mIdPiYz,acb6fba02239de95d9d826b25d1e5e29,8f499d0d7b03af52e3744f1fc494ef03d7f0e4a1,3cade660e227faadad0060d793b69cb778842a514ac6996bc6aaddb6a055f445 60 | t17.08.27.SBSB0320,24:mDa5NvfKOCX1aq9vyVTJ+uQhgypKmtXRegOovIGVcQRvDxjMmkWmlKmWFQHOmogQ:VFCOCX1LB+ypbtXAPTGVcUD/mAty/vQ,a88c30b25e2a70fb531be8b0a76630df,273f68352c1f3afd3c85c17246fd547c7b4912c1,6c3b955ad677ff26428d95a35b3a22ca3d523265674f08b6a0b59df270e6bf19 61 | t17.08.29.JMU0424,48:xCOCX1wmZ9q+1zbWppntdcpsSp6KQECESecF:xCnX1wmZ5zbWTntdcpsG1I,78e383b5c8ae525bc5703817881cda26,ed878f11021204eb39519eef5a78561beebe9600,782b3bed336eab77a49df51e697bc64d830f7f11a32ff49abc599fe5b074e0b9 62 | t17.08.29,48:bnKOCX17y9mepaRtXAPTGVcUD/mAtjAKQESE1b0:bnKnX17yLaRtXALGVci/mIbs,3e88e2f55f1d6db8a734c62a832ba062,2d03a8fdcdc8d9f140777d0de55cf49d289ec770,e3a2d62a997d4e9ee581fd86d312ac34caddd3165c07ca30c6741b4c21088d08 63 | t17.08.30.Ghi522,48:3xCOYX1J2m9meprytrntdcMSDl0Ar2lKQE9/L:3xChX1ImHrytrntdcMEl09Ux,dba397405916869fdbfc66fa57f553ae,42171cab3d11b03a4bdc32a547130df9ee5120ef,cb3976965f2105492193889f3f58f2ef2ccfeb8604e2b9448055ec6608d4aa85 64 | t17.08.30.Ghi522,48:3xCOYX1J2m9meprytrntdcMSDl0Ar2lKQE9/L:3xChX1ImHrytrntdcMEl09Ux,59be12be455b9115b37e028c62ec1216,8b5d90eac162ba1877cf85170d8015f352166a66,de8759fe34eb2f395574be79479832402aa4d113e102d6945df493abee3d8b34 65 | t17.08.30.Ghi522,48:3xCOYX1J2m9meprytrntdcMSDl0Ar2lKQE9/L:3xChX1ImHrytrntdcMEl09Ux,aa1af951f3f16eb106cc96c747e3f530,8bab56e5263e29194dfc497817ac14673176482b,09178fa9c4be32982619a183b8b76bfc2ff57486aac04c8fed654a4d9fe91436 66 | t17.08.30.KENPO0520,48:VFCOCX17y9msypRAPTntdcMSDlsAtjA/vJ:VFCnX17yBsRALntdcMElsIWx,f60cdde57bd9ca9412c32a08ef068abc,3573a9d03211e3935a48a947d1152d7611539f68,e03e6f7d98b214b5051b7484e4099ce5bd8c46e49faf44002c8ba146977127ef 67 | t17.08.30.pathology0528,48:RxCOYX1Kqa9meprhtrntdcMSDl0Ar2vA/U/Z:RxChX1taHrhtrntdcMEl09yW,84055f2bfec110090a9e2426ca8b69aa,cab9660c7bb70da2b26cd4997bfdc9815484560e,05ef4e0de8d57e6cd10d1673fcfca9c03b6e9a271d54028781e96235c4530e15 68 | t17.08.30.tokyo0602,48:3xCOYX1Kja9mepYHtrntdcMSDl0Ar2+/sKt/Q:3xChX1QaHYHtrntdcMEl094sK6,d78ec13e14cec4d6a7ed0998e1c69cc2,c8af03d73c61e487990190bae43e8ab912483e41,07b7041016c16341ea1f35a8c5fb5312d15f089ed5e925f78ffdd2568a8cf17c 69 | t17.08.30,48:WCOCX1pt79mFypXAPTntdcMS9lsAty/hCj:WCnX1pt72sXALntdcMolsIUhs,4be4ebe1db4ea1be2f293037eb7f8b0f,23c6a696f28a9123651a334ba9a0ba6c06abae14,28426751f30de4091dee898c70f49ec2ece607b6b642b45f5dcd9ae73ac38739 70 | t17.08.31.DGflash0714,48:VFCOCX199mqp2TcAPTntdcMSDlsAtyis7:VFCnX19R2TcALntdcMElsIM,2a11d0f22b413d990437892ec6fb28a9,5d36d17528bd5c1378b603442a840f46d00eb86c,bfcd987ca3e79bd7ba8dde95a392dbba02ffa30242954a0cfa35ec81182f0cc8 71 | t17.08.31.DGflash0716,48:bnCOfX1sw9mepT7APTntdcMSDlsAtdAKQEgk:bnCsX1swLT7ALntdcMElsIdPf,40dbf138d50e784851bbf0d25e85dc3a,5ebc7f6f951aa19a4e020157dfdf1b3139d33eb8,3caf60dd3bb551d4da244dffaeb68fe01b59cd19bd0f0509611b706048b3382f 72 | t17.08.31.FaSPDJ0804,48:VgCOCX1arF9mepvXAPTntdcMSDlsAtsEC/hy/:VgCnX1arFLvXALntdcMElsI0hQ,9194e0c1b045153fbeae6dab49a88337,483a6ca733650fe703f4048e21381708b2b18487,3cebf71221af741ea0b0883b45c092f900b513de3a004f81d3c595648311b7e9 73 | t17.08.31.FC0706,48:hxCOFX1KBa9mep4HtrntdcMSDl0Ar2+iK/Q:hxCeX1yaH4HtrntdcMEl09v,b9dc761f06a61a4bcb7d6b4b2ff61b05,e8272b4a49c85a2f99aaf72cd34a98cdb3dd2b58,c59ebe1fa6abe52c85f5f56a7da810a35e44c4772746bc829fa7d9e4e6a59477 74 | t17.08.31.flash0709,48:hFCOfX17w9mYypXAPTntdcMS9lsAtzuFd:hFCsX17wRsXALntdcMolsIq,a37198ac3ba35a83c87e22450e1219f6,43b4ea2f34dea0b21245d7a8c9e3f6986abd5cc5,3e850306025c231f09fa1922d1bb8e1a40bd8acc142d92219d9e9c8f8911b77d 75 | t17.08.31.Fmhi728,48:hFCOCX1/y9mgypZAPTntdcMSDlsAtb/wJ:hFCnX1/ynsZALntdcMElsI7O,7af68ddba01ba2d69a8ef7c17430e5d0,04746a061ebbba8c551862f27d76cb85225851cf,280371475442917b782f6a834003313f3aa0e5bb65f0acac5aab673d04336ba4 76 | t17.08.31.Gflash0709,48:VFCOCX1Fy9mlypAAPTntdcMSDlsAtR/Va:VFCnX1FyAsAALntdcMElsIG,302fbe13736403921ad7f9d310d7beb2,d87315166be5e3aa2d0962563e0b2edaf371d959,008f4f14cf64dc9d323b6cb5942da4a99979c4c7d750ec1228d8c8285883771e 77 | t17.08.31.Gflash071301,48:VFCOCX11y9m4ypXAPTntdcMSDlsAtyisQ:VFCnX11yXsXALntdcMElsIN,e3b2f18f8073e8df371dac855b260d14,df7ca8df9891ac4b20804ca42c29cd4e5f562ed6,567fa6bf28862ce7d14a2f3cf5b718780213fa3ee73f59557c29525f8daa200c 78 | t17.08.31.Mflash0714,48:xCOfX1aZ9mepETDAPTntdcMSDlsAtsKQESdca:xCsX1aZLETDALntdcMElsIDM,2345ae36972f9fe842e9ea6da66f52a8,c2d276d0a732532211b7aa0de91d39c113fbbde9,5a30f9010a316cc74ed271e732741c6d5d38f0e1c6f3b547176adcd40cb547ae 79 | t17.08.31.Wflash0714,48:VFCOCX1rE9msypXAPTntdcMS9lsAty/jQ:VFCnX1rEBsXALntdcMolsIUE,07aa0340ec0bfbb2e59f1cc50382c055,f0a73f20bc6c986d5e09a11f5606cf0aff271b2f,a94bf485cebeda8e4b74bbe2c0a0567903a13c36b9bf60fab484a9b55207fe0d 80 | t17.08.31,48:BFKOCX1ay9mepvHAPTntdcMSDlsAtyES/GQ:BFKnX1ayLvHALntdcMElsIav,bb3f0ad472aac26ae6dc8c0e7969cc30,7e8c4127902dbb0fd3f714d2e6b50acc57d4fcc1,e919ae6a3bdc6abe6b695215a53b74072a39b86757e049f930866b3f69000957 81 | t17.08.34.Fkohokyo1013,48:3bgX1KJAZ9mepjOWbKintdcMSDlmgK32b/hC/7:3bgX16eHjNbKintdcMElmC7hK,55fad6d72d9ac988b12ef3dff6df4ac6,2e5d0818bf7268ee40fa31f554bdeb9b2d3dc52b,e68e835904aaef2da5b38e9532036117996d58d3fba05cbe454f9d418be60ef4 82 | t17.08.34.Fshinetsu,48:h4dX1uMV9mepVOoMKintdcMSDlmgK32PiK/b:h4dX1RVHVhMKintdcMElmCL,62cef94f307b1d2409c7836d75a96b4c,5b65e97c148fc7af1835ac06a2e78e991877c9d1,90d07ea2bb80ed52b007f57d0d9a79430cd50174825c43d5746a16ee4f94ea86 83 | t17.08.34.Fxair0819,48:h4gX1KM69mepVOotKintdcMSDlmgK32W/c/xM:h4gX1l6HVhtKintdcMElmCgl,337efc3851244c93fc0d812fb4ae66f9,4bd916b88b8bf54322100a6900b54b55ba66201a,3553c136b4eba70eec5d80abe44bd7c7c33ab1b65de617dbb7be5025c9cf01f1 84 | t17.08.34.Fxair0819,48:h4gX1KM69mepVOotKintdcMSDlmgK32W/c/xM:h4gX1l6HVhtKintdcMElmCgl,36dffe3a45f376d28af6ec51730e0f9d,fd42eaee2f4692e2676367fb5183dc47fda6c618,22957429e8ab527ff8bb45fbc50aa8400ea643a68de8d43da3fee3239e2159d4 85 | t17.08.34.Fxshinetsu0813,48:t4gX1KhT9mepvOtOKintdcMSDlmgK32EAiK/UG:t4gX1mTHvOOKintdcMElmCL,b8d7fec363ac1d303717ba0732c7eb40,018c5dedc73c6b7d9c56dc829e9b14ae65b275da,6a331c4e654dd8ddaa2c69d260aa5f4f76f243df8b5019d62d4db5ae5c965662 86 | t17.08.34.FxZT0813,48:hagX1KCa9yepVOoOKintdcMSDlmgK32EAiK/5:hagX1ha7VhOKintdcMElmCr,bf7bc4c288df36bdc4f01e3d97cffc10,aa6bc5a2bcc325f1d418ba719d61c0be15719b95,17e646ca2558a65ffe7aa185ba75d5c3a573c041b897355c2721e9a8ca5fee24 87 | t17.08.5,48:OzLJLX1IO2xY7depntdcpsSpvaEC8tKQ/E90deI1nD:ALhX1IFxYh8ntdcpsGvUQdz1,149925d0d9ab9bf37af1f38dfe9c2af5,7768a989f4c0d90299fc45d0274eea05e9a7a315,396c7766eb8873227c270eae2b13357dbcd68fa7f07053dd280375418eeee614 88 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,0b35de255869ac3b4a62c29f34a3c113,a09bce13f58358b72b84b08408ce3bd939a6e202,83620f29a19a4d372e256d98ebfd2d3e5cb4b8db97b385c2942914298b8d2870 89 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,9a3af7262ab2bc3345c448db7660d070,76d89832a5a8dda8ad351f0548bb233e1db4a728,4422d1568f729c316e8d02a35fe147c4c36c91d650989e9ac3caa6fbbc086b37 90 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,2a3bf0b80b71e5df66a618a7011a9082,8cda408545cd25dadba92f55f424d6317f9fdd9b,7875c21473cf5f8d936f1335c049ae6df9e0b0574b263060d7a526f3d53cbf07 91 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,fb2724a29056c5f58a296aba8f00de1c,82f67a3cb946186c3179a525ef9ee30b8c29914f,4df19e155cac0735500cffae49007b3d971979cccca779a5af685db489b4b042 92 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,0a20da7d0f01d75df53c20942e734e72,75a21d324f65465a39643abaea6576f723de6286,e7ae0995e3d4dd9c3fed51d5bca73ea9fa3edd90e2e87fc0cfac58165afdf4e8 93 | t17.08.9,48:JAly+XX1IOObjwuzntdcpsS9vgAEkEU3KQkE8VKl/Z9r4:cdXX1I9bcantdcpsovwXVKJzs,7c9da8eab40d699535bb87a2c781f8ad,5faa69d3a86235b4f366283fc15170970fb94c5b,138e7c2e5cf0caba02d005752686a66482df23f4b4b648f446f2afada32a5750 94 | t19.10,96:RZcLsKp0X1rd45lCW3wMMtfyOSuUXNPTFJuP0:esu0FmWpaOwX9TFJuP0,469501e1342b704f2c1814eb1cc0a54b,b4dfec5a7bbc6dba47b0bfd77363b841cc836eb8,47fe9ca3fb03cf70a111293d9e877c1d3d771bfe55fcae5b3a5d3af130c27639 95 | t19.10,96:RZcLsKp0X1rd45lCW3wMMtfyOSuUXNPTFJuP0:esu0FmWpaOwX9TFJuP0,dbcc03ccd3129141dd3bd23fd1b5f1f6,56fa84f4c4eab1f061d0d53a815e0f0cebf81eb7,ceb8624f405f0c892f74f6b51d6cc05d6ff64631983defd924a7443fdea3d3bb 96 | t19.14,96:Ampn8pfpbX13d4IRke6Rt6WGRze3m0ugmXIsjCIM:ASWFaIC4hPjCIM,ae619b8ade2c591ba8e07ea334eaf638,70d5918339035230004ab371da061aaa893c2125,a2b65352f31b5f70a12e5510e932042ff76a5ad0db9ae97abefe3e4aee4042e3 97 | t19.18,96:1p198CfprX1FLIFrRke6gthWGRNe3m0ugmXIyjCIM:X1nFGFrCEhPjCIM,269cec2efb0acc21da5f955e68c7c7a4,d9bdf708dadcaf8a2e335a83a1eb384d46826495,65de47062b0a7090bd0db4cbf1a8578f0f8e7e0f68e71d21e052e6501dba8594 98 | t19.20,96:tX1aQp15RIFQAWntdcpsGnne3m0ugmXjHs0:tFaU1AP9hs0,2709d199171aebd9fd665f14341a3c48,114b72367738ffc4471faba6b696c1681480253e,c7a044bd2a762f192b5d4c1352430188893909a845c67f288973abc6755f5854 99 | t19.22.1,96:1X1diZcEGN60MD+xTfwM3tft85x8Ye3m0JmXIYkU3Cz:1FQGE0Yu3+5xTfCz,fa2f00d888944dd860ed92fa521fb22d,19ef193641e730b86847adc00f2cb33f073bd2f8,b2bdcd0a014f8bc6767e9d90ad5b031b9e766c3d49f87da99fd0e6740d4f6ddd 100 | t19.23.01.,96:1X1diZcEb/60u+KxTfwM3tft85x8Ye3m0JmXIYkU3Cz:1FQby0dK3+5xTfCz,cdaa982b80f2c7a84442a60f6b1e130d,07d48a5948eeba7922576ed8cc28f9452889f17c,13aee447ee29101f68c80d6fc267459e5962e7e9707baa6db94ad0f52667a086 101 | t20.03,96:1X1di2cN60EHmfDmDuaIr+AZtq4M2Le3m0ugmXIZ5uw:1Fv02+Dcm02i5uw,a042015759c18761e8b4494fe45e577d,89a40cf2c35bae79be13b9fcafbe425ff12e7b01,ab6e3dc06c6c37a4505c591d2ca6e8cddc5aa392a4e4e246b75211c67608fb09 102 | t20.03,48:5O7FBX1dB59f+83EW6ItdcpsSpi8t/ESEelL/onT:5QFBX1dLH3EWftdcpsGZNIT,54e4fb7a3235cb11385cf3410e9927b3,35ba854b584db03f3faaf0abc97612165e9dc243,59fb6c637abfe8ad6601229d44c9e8317a70c295ab4ba00c9368c4328401b93d 103 | t20.06,96:IX1RM2cC40AHaCfHu6xzDwMVRt6e+2PmfeAugmXIwrUuut:IFY0SnHDm/2xnut,44fe2541cd5a140f78b7dac8e572a58c,f46f5088fd24db5aa2bc91aa6c6f4b06e76e1b4d,1df4678d7210a339acf5eb786b4f7f1b31c079365bb99ab8028018fa0e849f2e 104 | t20.07,96:tX18M2cI4024CT9mJ4xy+Msc+5tx9le3m0JmXloqqfUuufZ:tFN0299WUNWzufZ,6721ae55c42a1ca977abfd1bc668302c,38615b02de06806b610ff5ced55b0969d02d26a0,d155570d4616343939e7ab816c93b3bd67bc33c09d60c26e9e5577ce0a9a348b 105 | t20.08,96:gKeX18aL0gT9mQMp+fc44GJtjBi27le3m0JmXloqYM0:uFh089Ze2SM0,af3cda1a880eb3fb6be354710a2c4fc1,953a918a84d6e59b71a91520d372f1d163521f63,9b8c1830a3b278c2eccb536b5abd39d4033badca2138721d420ab41bb60d8fd2 106 | t20.09.Ko12F,48:QNcXaX14ciRrN9XsepX9OHQauAYTntqS9aKQ/jx4hmqu:QNcKX14cwlHX9MQauAontqoVCxv,06ae3fe6336b84e035394c7a5541fc99,56315e3ddc5dbf2fbc51706a710bfaf7bc360879,6173c5494eefad440584bb7a430866bba6356a40286e0a951905d308c559dee1 107 | t20.09.Koitochu,48:pOVCNmcX1Vc19sepOhvaSAYTntqS9sEC8J4mqrh:p+CNmcX1VcPHONaSAontqoL2,b7c6a57b7a7413f7910c6a7d20a6c848,69af1c22148ca1ec9ea94f972ce4ac7c27f6f630,d62b7448457e3d150d75590c8a21d1f9bf97bb43853e0caff02edd0474655a81 108 | t20.10.1,48:5OVxKIX1+ccWks4pShvaGAYTntqSDs8tES4/7Em044:5+xKIX1+ccWk7SNaGAontqERm0R,d89b0983b0002157064ba1e7d7d086cf,0f21dc3f24cb082d881bcadfb5136825325e94aa,5d7ecadc82db1aa7112affc3924a931436e27b708980629add427a5fbfc2d5b5 109 | t20.12,96:+X1VGcTgkH0e712FdC2UJqYntqOkie3m0pMKOMX9uR:+FcWd12QMSkTOMX9uR,438a3b6783fb290197d3023ce441229c,dfd74765a126a0fff4122d9b101720e148c179cb,85a5b524a07d2a37e56876495c1a3a67a1217998a45283fe87f4ab1f97f6a973 110 | t20.12,96:UFK9X17i2cRvHJzFANaQWntqod+DNNafF9:UwF8/JjQC+D+F9,569ef496a8ec060863d5f7f0fce1fe9d,405ebbf4e54eda11ff945e327c434ce31812bf54,aedcea10bea5becd8ba9ac70df500c6b7402c65fa9af7245bd113dcf5a4a9450 111 | t20.12,96:UFK9X17i2cRvHJzFANaQWntqod+DNNafF9:UwF8/JjQC+D+F9,569ef496a8ec060863d5f7f0fce1fe9d,405ebbf4e54eda11ff945e327c434ce31812bf54,aedcea10bea5becd8ba9ac70df500c6b7402c65fa9af7245bd113dcf5a4a9450 112 | t20.14,96:9KVgX1m3Io9G7NP2ntdcMoUEaNeAOoIY0H:ZF2Io9YQEajA,38808ead68a02f6ff705da2e5912fe96,8af5b9a173b3935ad7d3c06228a68337ff9e651c,732ddb1f5c7d134849938680d63c322fa7d70df087123f6970570d4535062ab2 113 | t20.19,48:KN/demdX1VcBi9Xsep49OgvFa8UTntqS9oECESKQ8tlnex3Z:KNVeUX1Vc4lH49FvFa8Untqo7jna3Z,2d5637c5019017d122c029a98aa9ad02,3afdc1df3997dfa80c450aee620ab5d79cd218c9,6fa9702039adbdf4338b28c3b711cae100e60801328190d40a8354993e4f916e 114 | t20.19,96:9KVFX1iUI09b4uaYqntdcMoZCmnaHAugmFr5X:SFXI09CY5lazFX,4dafabf9a06297bfd40daa95ea65a536,80c7caedc047302eff865fa74304026b528b904d,967955b4741af54f62e66232ee3c98058b998f9a33643db6077972a430401919 115 | t20.19,48:SdRmdX1kcf/9XsepUn9O0UuaeUTntqS9+AKQ1lsmVH:uRUX1kcf/lHUn9bUuaeUntqo+POTH,23f23e1345f6bc70af34604246d6300d,26fc5977b2d235e36b084e2f5b2c1cb23ea834be,a1fa7c5216737e96359452dcbf121afc251b225abd00f6a464392591caaf52e1 116 | t20.19,96:Cs+8KVoX1H5auOqntdcMovmXXPe3m5oDIu:z+iFZIPmXX9u,56c28e33b1a04de9b285a0c9bdb206ed,0a82ed73cb2994bcae63c4dbb6ea5f10f1f22258,e935cb190fe198ed6388bf6225deba48a131b23a423b84019147de35a5285f6b 117 | t20.19,96:t+8KVGWoX1sXIlu2qntdcMEn0X1m5gmXaj:t+4FmIzt0XT,9455f3a25233c52317501219ba393a82,3101d4be4d60155ed501fb322459bf926b496318,1d3e13257e0541ca2dc450140ebf9dabc4c03b6f13d9010c91faae8290ec297c 118 | t20.20,96:pKVN4X1/hII9b1urvqntdcMoIz7ne3mho8Bt:5FZII9cvoz7nj,0ae0511416d08bf447f60179c47282b8,d43f07021ec62c8b3cec326ea9dd5a03f9e98c72,ad607ece1005c450039a600c0eb4f1057a82b3dc832ad6606cacfbb911b1a495 119 | t20.20,96:BVoX1BRc8lHhIZbJNaPHwtUntZ4ROm5ol9f:MFn5mZmGw4U9,0d9be54a980f2df875d70f5f3e7bc03f,2b64d6198059d2ac6239da03e581fb29e6e925c9,d842154543f97a7749abc9412923d0a9e5fa16a7f304a97e1e33c8dbacaebc90 120 | t20.20,96:pKVNX109cUL0ZEeFJdXLP+ithCGD3UHe3mWdPu4:IFa0ZEe/H3UIu4,5aaaa1e35b0f10fcf9b6169706a11d67,7fba1909e73353774205fb9343fecbbef2de820f,8b482a80ee03d0f14eaf5b4b6d4ac033d9fac3bd69ebb839a75f29bae7dffacd 121 | t20.20,96:BV2X1BEcY6U8I/btN6X+oUGTtZPMNuouO/8:6Ffy/SvVuU,da8cc9bddd12034ed964039403b64478,6cb1d6e5347ef5bc0d39e20fc551a617336b5f9c,430e33e9199c55bf23a53eceba7e48863c1589646491835e4f41932844d79e3e 122 | t20.22.1,48:k8K/dLJdX1UBcp+HpCPTMHefO+7bb2D3SQqKQ/ESjAWAi4zb:k8KVLXX1UBcp+JyMHef97bb2TSYI1Ahb,85510bd4054986e77c4d352a495ea70a,7511bea05743cac08cb48b1d7ced0ac43f44e8ca,3ea3cdabd7eb7f76be1632e69e42516d51413d85e42d3744155dddcd4e2e7e91 123 | t20.22.1,96:0KVoX1Bez0jHRbzPyrgGVcCyCY+oPa4m0u0m:KFYI9utXwad,bd201f3d21dacb02f6585fa536d62d88,56614db4baf76ea58cc09c54ff52e4a3d5a9dbfc,33e4045cb93b14d834cecde64f36e36eb760b98f6bbd660db4514e34e12662c8 124 | t20.23.1,96:U8KV5gX1Ic5mrNbK8xTPftmGEpH+emuD1FUIuF:UcFRoNjPaH+I7uF,e427ee78902ad672e72b00a5651e107f,8f496004248854bc40e2808bb0b38e223a39da6d,a5be7cb1f37030c9f9211c71e0fbe01dae19ff0e6560c5aab393621f18a7d012 125 | t20.23.1,96:pKV0rX1IcqTu0TTFQiyTfTftmGh2+ekeAZxRVuN:hFi60TTqrP2+ekuN,076d27e43ad7f3c7b44c479f29ea98b9,960fee9e3aacde8240b6bc37d4bc2a85eb37432e,9a351885bf5f6fec466f30021088504d96e9db10309622ed198184294717add1 126 | t20.25.1,96:uuX1eAcZTziemRr+uLrPLJftJGL2/3mQD0rIouT:dFqmeErdVw290rIouT,a0ab2d5b144d4ae2de9ef8d835afd652,6546334b21484108a71592dad0a68c86fa55decd,9183abb9b639699cd2ad28d375febe1f34c14679b7638d1a79edb49d920524a4 127 | t20.26,96:B7QX1jmcBdeUEp/JH5PrftJGEv+0ROm0MYmXf/Duh:yF1eDpLrN+0H/Duh,823050f6a22fe8be69f2c542b40b45f2,f376f2bedfb0100be2a1ab64d6c320eb1a277b19,8f79088418a7cceb73c07feffe1ca7e01fd9e7d0f9daa0cf30ec15fc958cf573 128 | t20.26,96:ykN7BPX1t3cyikHmB0BsltsUGEC1tihXyu6:y8FtliWXvZ1shXyu6,6dc405c7f7410681ef2fcbffc506f6da,d3c3feb912fb89ec6464b3b3d344a1e8ed720e10,dbda8ff81a459e2872daf2027f501dcf656d5023ae5c1270aaaa9e9e56882017 129 | -------------------------------------------------------------------------------- /impfuzzy_for_Neo4j/test/README.md: -------------------------------------------------------------------------------- 1 | # Sample Dataset 2 | Emdivi.csv is malware list based Emdivi. 3 | 4 | ## How To Use 5 | ``` 6 | $ python impfuzzy_for_neo4j.py -l Emdivi.csv 7 | ``` 8 | -------------------------------------------------------------------------------- /impfuzzy_for_Volatility/README.md: -------------------------------------------------------------------------------- 1 | # impfuzzy for Volatility 2 | Volatility plugin for comparing the impfuzzy and imphash. 3 | This plugin can be used to scan malware in memory image. 4 | Imphash see [FireEye Blog](https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html) 5 | 6 | More details are described in the following documents: 7 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy_volatility.html (Japanese) 8 | http://blog.jpcert.or.jp/2016/12/a-new-tool-to-d-d6bc.html (English) 9 | 10 | ## Requirements 11 | This plugin requires the following modules: 12 | 13 | * pyimpfuzzy https://github.com/JPCERTCC/impfuzzy/tree/master/pyimpfuzzy 14 | 15 | ## Usage 16 | Use -h to see help message. 17 | * impfuzzy - compare or print the impfuzzy 18 | * imphashlist - print the imphash 19 | * imphashsearch - search the imphash 20 | 21 | ### Example Usage 22 | #### Printing The Impfuzzy Hash of Process and Dll Module 23 | ``` 24 | $ python vol.py -f [image] --profile=[profile] impfuzzy -p [PID] -a 25 | ``` 26 | #### Searching The Impfuzzy Hash from PE Files 27 | ``` 28 | $ python vol.py -f [image] --profile=[profile] impfuzzy -e [PE File or Folder] 29 | ``` 30 | #### Searching The Impfuzzy Hash from Hash List 31 | ``` 32 | $ python vol.py -f [image] --profile=[profile] impfuzzy -i [Hash List File] 33 | ``` 34 | #### Printing The Imphash 35 | ``` 36 | $ python vol.py -f [image] --profile=[profile] imphashlist -p [PID] 37 | ``` 38 | #### Searching The Imphash 39 | ``` 40 | $ python vol.py -f [image] --profile=[profile] imphashsearch -i [Hash List] 41 | ``` 42 | -------------------------------------------------------------------------------- /impfuzzy_for_Volatility/impfuzzy.py: -------------------------------------------------------------------------------- 1 | # Searching the ImpFuzzy and Imphash for Volatility 2 | # 3 | # LICENSE 4 | # Please refer to the LICENSE.txt in the https://github.com/JPCERTCC/aa-tools/ 5 | # 6 | # How to use: 7 | # 1. cd "Volatility Folder" 8 | # 2. mv impfuzzy.py volatility/plugins 9 | # 3. python vol.py [ imphashlist | imphashsearch | impfuzzy ] -f 10 | # images.mem --profile=Win7SP1x64 11 | 12 | import os 13 | import pefile 14 | import hashlib 15 | import volatility.obj as obj 16 | import volatility.debug as debug 17 | import volatility.utils as utils 18 | import volatility.cache as cache 19 | import volatility.win32.tasks as tasks 20 | import volatility.win32.modules as modules 21 | import volatility.plugins.procdump as procdump 22 | import volatility.plugins.malware.impscan as impscan 23 | import volatility.plugins.malware.malfind as malfind 24 | 25 | try: 26 | import pyimpfuzzy 27 | import impfuzzyutil 28 | has_pyimpfuzzy = True 29 | except ImportError: 30 | has_pyimpfuzzy = False 31 | 32 | 33 | class SearchImp(impscan.ImpScan): 34 | 35 | def get_apilist(self, pid, addr_space, base, task, task_space): 36 | 37 | if not task_space: 38 | return None 39 | 40 | all_mods = list(task.get_load_modules()) 41 | 42 | base_address = base 43 | for vad in task.VadRoot.traverse(): 44 | if base_address >= vad.Start and base_address <= vad.End: 45 | size_to_read = vad.Length 46 | 47 | if not task_space.is_valid_address(base_address): 48 | return None 49 | 50 | data = task_space.zread(base_address, size_to_read) 51 | apis = self.enum_apis(all_mods) 52 | addr_space = task_space 53 | 54 | # This is a dictionary of confirmed API calls. 55 | calls_imported = dict( 56 | (iat, call) 57 | for (_, iat, call) in self.call_scan(addr_space, base_address, data) 58 | if call in apis 59 | ) 60 | 61 | impstrs = [] 62 | for iat, call in sorted(calls_imported.items()): 63 | mod_name, func_name = self._original_import( 64 | str(apis[call][0].BaseDllName or ""), 65 | apis[call][1]) 66 | impstrs.append("%s.%s" % 67 | (mod_name.lower().split(".")[0], func_name.lower())) 68 | 69 | if len(impstrs) != 0: 70 | return ",".join(impstrs) 71 | else: 72 | return None 73 | 74 | 75 | class ImpHashList(procdump.ProcDump, malfind.Malfind): 76 | """Listing the Import Hash(imphash)""" 77 | 78 | def __init__(self, config, *args, **kwargs): 79 | procdump.ProcDump.__init__(self, config, *args, **kwargs) 80 | config.add_option("FASTMODE", default=False, action="store_true", 81 | help="Use Fast scan mode (Not use impscan)") 82 | 83 | def detect_injection_proc(self, proc, space): 84 | detects = [] 85 | for vad, address_space in proc.get_vads(vad_filter=proc._injection_filter): 86 | if self._is_vad_empty(vad, address_space): 87 | continue 88 | if obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=address_space).e_magic != 0x5A4D: 89 | continue 90 | detects.append([vad.Start, address_space]) 91 | 92 | return detects 93 | 94 | def calc_hash(self, pe_data, addr_space, base, proc, space): 95 | try: 96 | pe = pefile.PE(data=pe_data) 97 | hash_result = pe.get_imphash() 98 | except: 99 | hash_result = "Error: This file is not PE file imphash" 100 | 101 | try: 102 | fuzzy_result = pyimpfuzzy.get_impfuzzy_data(pe_data) 103 | except: 104 | fuzzy_result = "Error: This file is not PE file impfuzzy" 105 | 106 | if not hash_result and not self._config.FASTMODE: 107 | pid = proc.UniqueProcessId 108 | simp = SearchImp(self._config) 109 | apilists = simp.get_apilist(pid, addr_space, base, proc, space) 110 | if apilists is not None: 111 | hash_result = hashlib.md5(apilists).hexdigest() 112 | fuzzy_result = impfuzzyutil.hash_data(apilists) 113 | else: 114 | hash_result = "" 115 | fuzzy_result = "" 116 | 117 | return hash_result, fuzzy_result 118 | 119 | def calculate(self): 120 | addr_space = utils.load_as(self._config) 121 | 122 | data = self.filter_tasks(tasks.pslist(addr_space)) 123 | 124 | for proc in data: 125 | space = proc.get_process_address_space() 126 | if space == None: 127 | continue 128 | 129 | mods = dict((mod.DllBase.v(), mod) 130 | for mod in proc.get_load_modules()) 131 | 132 | for start, size in self.detect_injection_proc(proc, space): 133 | pe_file = obj.Object("_IMAGE_DOS_HEADER", 134 | offset=start, vm=size) 135 | dataset = [] 136 | for offset, code in pe_file.get_image(unsafe=self._config.UNSAFE, 137 | memory=self._config.MEMORY, 138 | fix=self._config.FIX): 139 | dataset.append(code) 140 | data = "".join(dataset) 141 | 142 | hash_result, fuzzy_result = self.calc_hash( 143 | data, addr_space, start, proc, space) 144 | 145 | yield proc.obj_offset, proc.ImageFileName, start, "INJECTED CODE", hash_result, fuzzy_result 146 | 147 | for mod in mods.values(): 148 | base = mod.DllBase.v() 149 | mod_name = mod.BaseDllName 150 | 151 | if not space.is_valid_address(base): 152 | result = "Error: DllBase is unavailable (possibly due to paging)" 153 | else: 154 | process_offset = space.vtop(proc.obj_offset) 155 | pe_file = obj.Object( 156 | "_IMAGE_DOS_HEADER", offset=base, vm=space) 157 | dataset = [] 158 | for offset, code in pe_file.get_image(unsafe=self._config.UNSAFE, 159 | memory=self._config.MEMORY, 160 | fix=self._config.FIX): 161 | dataset.append(code) 162 | data = "".join(dataset) 163 | 164 | hash_result, fuzzy_result = self.calc_hash( 165 | data, addr_space, base, proc, space) 166 | 167 | yield proc.obj_offset, proc.ImageFileName, base, str(mod_name or ""), hash_result, fuzzy_result 168 | 169 | def render_text(self, outfd, data): 170 | self.table_header(outfd, 171 | [("Process", "[addrpad]"), 172 | ("Name", "20"), 173 | ("Module Base", "[addrpad]"), 174 | ("Module Name", "20"), 175 | ("imphash", "32")]) 176 | 177 | for offset, FileName, base, ModName, result, fuzzy_result in data: 178 | self.table_row(outfd, offset, FileName, base, ModName, result) 179 | 180 | 181 | class ImpHashSearch(ImpHashList): 182 | """Searching the Import Hash(imphash)""" 183 | 184 | def __init__(self, config, *args, **kwargs): 185 | ImpHashList.__init__(self, config, *args, **kwargs) 186 | config.add_option("IMPHASH", short_option="s", type="string", 187 | help="Search single imphash value") 188 | config.add_option("IMPHASHLIST", short_option="i", type="string", 189 | help="Search imphash list file") 190 | 191 | def render_text(self, outfd, data): 192 | self.table_header(outfd, 193 | [("Process", "[addrpad]"), 194 | ("Name", "20"), 195 | ("Module Base", "[addrpad]"), 196 | ("Module Name", "20"), 197 | ("imphash", "32")]) 198 | 199 | if self._config.IMPHASHLIST is not None: 200 | hashlist = [] 201 | of = open(self._config.IMPHASHLIST, "r") 202 | lines = of.readlines() 203 | for line in lines: 204 | hashlist.append(line.rstrip()) 205 | 206 | for offset, FileName, base, ModName, result, fuzzy_result in data: 207 | if self._config.IMPHASH is not None: 208 | if result in self._config.IMPHASH and len(result) == 32: 209 | self.table_row(outfd, offset, FileName, 210 | base, ModName, result) 211 | elif self._config.IMPHASHLIST is not None: 212 | if result in hashlist and len(result) == 32: 213 | self.table_row(outfd, offset, FileName, 214 | base, ModName, result) 215 | else: 216 | debug.error( 217 | "Please set option -s(single imphash) or -i(imphash list file)\n") 218 | 219 | 220 | class ImpFuzzy(ImpHashList): 221 | """Comparing or listing the Import Fuzzy Hashing(impfuzzy)""" 222 | 223 | def __init__(self, config, *args, **kwargs): 224 | ImpHashList.__init__(self, config, *args, **kwargs) 225 | config.add_option("EXEFILE", short_option="e", type="string", 226 | help="Comparing the PE file or direcroty") 227 | config.add_option("THRESHOLD", short_option="t", type="int", 228 | help="Import fuzzy hashing threshold (Default 30)") 229 | config.add_option("COMPIMPFUZZY", short_option="i", type="string", 230 | help="Comparing the list file of impfuzzy") 231 | config.add_option("LISTIMPFUZZY", short_option="a", default=False, action="store_true", 232 | help="Listing the impfuzzy") 233 | 234 | def render_text(self, outfd, data): 235 | # This is a impfuzzys threshold 236 | ss_threshold = 30 237 | 238 | if not has_pyimpfuzzy: 239 | debug.error("pyimpfuzzy must be installed for this plugin") 240 | 241 | files = [] 242 | impfuzzys = [] 243 | impfuzzy = "" 244 | if self._config.EXEFILE is not None: 245 | mode = "search" 246 | if os.path.isdir(self._config.EXEFILE): 247 | for root, dirs, filenames in os.walk(self._config.EXEFILE): 248 | for name in filenames: 249 | files.append(os.path.join(root, name)) 250 | elif os.path.isfile(self._config.EXEFILE): 251 | files.append(self._config.EXEFILE) 252 | 253 | for file in files: 254 | impfuzzys.append(pyimpfuzzy.get_impfuzzy(file)) 255 | # outfd.write("%s Impfuzzy : %s\n" % (file, pyimpfuzzy.get_impfuzzy(file))) 256 | elif self._config.COMPIMPFUZZY is not None: 257 | mode = "search" 258 | of = open(self._config.COMPIMPFUZZY, "r") 259 | lines = of.readlines() 260 | for line in lines: 261 | impfuzzys.append(line.rstrip()) 262 | elif self._config.LISTIMPFUZZY: 263 | mode = "list" 264 | else: 265 | debug.error( 266 | "Please set option -e (PE file or directory) or -i (impfuzzy hash list file) or -a (Listing the impfuzzy)") 267 | 268 | if self._config.THRESHOLD is not None: 269 | ss_threshold = self._config.THRESHOLD 270 | 271 | if "search" in mode: 272 | self.table_header(outfd, 273 | [("Process", "[addrpad]"), 274 | ("Name", "20"), 275 | ("Module Base", "[addrpad]"), 276 | ("Module Name", "20"), 277 | ("impfuzzy", "20"), 278 | ("Compare", "7")]) 279 | 280 | for offset, FileName, base, ModName, hash_result, fuzzy_result in data: 281 | for impfuzzy in impfuzzys: 282 | if not "Error" in fuzzy_result: 283 | if pyimpfuzzy.hash_compare(impfuzzy, fuzzy_result) >= ss_threshold: 284 | self.table_row(outfd, offset, FileName, base, ModName, fuzzy_result, 285 | pyimpfuzzy.hash_compare(impfuzzy, fuzzy_result)) 286 | 287 | if "list" in mode: 288 | self.table_header(outfd, 289 | [("Process", "[addrpad]"), 290 | ("Name", "20"), 291 | ("Module Base", "[addrpad]"), 292 | ("Module Name", "20"), 293 | ("impfuzzy", "110")]) 294 | 295 | for offset, FileName, base, ModName, hash_result, fuzzy_result in data: 296 | if not "Error" in fuzzy_result: 297 | self.table_row(outfd, offset, FileName, 298 | base, ModName, fuzzy_result) 299 | -------------------------------------------------------------------------------- /impfuzzy_for_Volatility3/README.md: -------------------------------------------------------------------------------- 1 | # impfuzzy for Volatility3 2 | Volatility3 plugin for comparing the impfuzzy, imphash and ssdeep. 3 | This plugin can be used to scan malware in memory image. 4 | Imphash see [FireEye Blog](https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html) 5 | 6 | More details are described in the following documents: 7 | * https://blogs.jpcert.or.jp/ja/2016/11/impfuzzy_volatility.html (Japanese) 8 | * https://blogs.jpcert.or.jp/en/2016/12/a-new-tool-to-d-d6bc.html (English) 9 | 10 | ## Functions 11 | 12 | * pehash.ImpFuzzy - compare or print the impfuzzy 13 | * pehash.ImpHash - search or print the imphash 14 | * pehash.Ssdeep - compare or print the ssdeep 15 | 16 | ## Requirements 17 | This plugin requires the following modules: 18 | 19 | * pefile https://github.com/erocarrera/pefile 20 | * pyimpfuzzy https://github.com/JPCERTCC/impfuzzy/tree/master/pyimpfuzzy 21 | * python-ssdeep https://github.com/DinoTools/python-ssdeep 22 | 23 | ## How to Use 24 | 25 | ### Download Volatility3 and impfuzzy for Volatility3 26 | ```shell 27 | $ git clone https://github.com/volatilityfoundation/volatility3.git 28 | $ git clone https://github.com/JPCERTCC/impfuzzy.git 29 | ``` 30 | 31 | ### Install requirements 32 | ```shell 33 | $ pip3 install pefile yara-python capstone pyimpfuzzy ssdeep 34 | ``` 35 | 36 | ### Run 37 | ```shell 38 | $ cd volatility3 39 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 [ pehash.ImpHash | pehash.ImpFuzzy | pehash.Ssdeep ] 40 | ``` 41 | 42 | Use -h to see help message. 43 | 44 | ### Example Usage 45 | #### Printing The Impfuzzy Hash of Process and Dll Module 46 | ``` 47 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.ImpFuzzy --pid [PID] 48 | ``` 49 | #### Searching The Impfuzzy Hash from PE Files 50 | ``` 51 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.ImpFuzzy --exefile [PE file] 52 | ``` 53 | #### Searching The Impfuzzy Hash from Hash List 54 | ``` 55 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.ImpFuzzy --impfuzzylist [Hash List File] 56 | ``` 57 | #### Printing The Imphash 58 | ``` 59 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.ImpHash --pid [PID] 60 | ``` 61 | #### Searching The Imphash from Hash List 62 | ``` 63 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.ImpHash --imphashlist [Hash List] 64 | ``` 65 | #### Printing The ssdeep 66 | ``` 67 | $ python3 vol.py -f [memorydata] --plugin-dirs ../impfuzzy/impfuzzy_for_Volatility3 pehash.Ssdeep --pid [PID] 68 | ``` 69 | -------------------------------------------------------------------------------- /impfuzzy_for_Volatility3/pehash.py: -------------------------------------------------------------------------------- 1 | # Search PE File with ImpFuzzy / Imphash / Ssdeep for Volatility3 2 | # 3 | # LICENSE 4 | # Please refer to the LICENSE.txt in the https://github.com/JPCERTCC/aa-tools/ 5 | # 6 | # How to use: 7 | # 1. git clone https://github.com/JPCERTCC/impfuzzy.git 8 | # 2. python3 vol.py -f memdata --plugin-dirs impfuzzy/impfuzzy_for_Volatility3 [ pehash.ImpHash | pehash.ImpFuzzy | pehash.Ssdeep ] 9 | # 10 | 11 | import logging, os, io 12 | 13 | from volatility3.framework import interfaces, constants, exceptions, renderers 14 | from volatility3.framework.objects import utility 15 | from volatility3.framework.layers import resources 16 | from volatility3.framework.renderers import format_hints 17 | from volatility3.framework.configuration import requirements 18 | from volatility3.framework.symbols import intermed 19 | from volatility3.framework.symbols.windows import extensions 20 | from volatility3.framework.symbols.windows.extensions import pe 21 | from volatility3.plugins.windows import pslist, vadinfo 22 | 23 | vollog = logging.getLogger(__name__) 24 | 25 | try: 26 | import pefile 27 | has_pefile = True 28 | except ImportError: 29 | has_pefile = False 30 | 31 | try: 32 | import pyimpfuzzy 33 | import impfuzzyutil 34 | has_pyimpfuzzy = True 35 | except ImportError: 36 | has_pyimpfuzzy = False 37 | 38 | try: 39 | import ssdeep 40 | has_ssdeep = True 41 | except ImportError: 42 | has_ssdeep = False 43 | 44 | vollog = logging.getLogger(__name__) 45 | 46 | 47 | class ImpHash(interfaces.plugins.PluginInterface): 48 | """Listing the Import Hash (imphash)""" 49 | 50 | _required_framework_version = (2, 0, 0) 51 | 52 | @classmethod 53 | def get_requirements(cls): 54 | # Since we're calling the plugin, make sure we have the plugin's requirements 55 | return [requirements.TranslationLayerRequirement(name='primary', 56 | description='Memory layer for the kernel', 57 | architectures=["Intel32", "Intel64"]), 58 | requirements.SymbolTableRequirement(name="nt_symbols", description="Windows kernel symbols"), 59 | requirements.IntRequirement(name='pid', 60 | description="Process ID to include (all other processes are excluded)", 61 | optional=True), 62 | requirements.StringRequirement(name="imphash", 63 | description="Search single imphash value", 64 | optional=True), 65 | requirements.StringRequirement(name="imphashlist", 66 | description="Search imphash list file", 67 | optional=True), 68 | requirements.PluginRequirement(name='pslist', plugin=pslist.PsList, version=(2, 0, 0)), 69 | requirements.PluginRequirement(name='vadinfo', plugin=vadinfo.VadInfo, version=(2, 0, 0)), 70 | ] 71 | 72 | @classmethod 73 | def calc_hash(cls, pe_data): 74 | try: 75 | pe = pefile.PE(data=pe_data) 76 | hash_result = pe.get_imphash() 77 | except: 78 | hash_result = "Unable to calc imphash" 79 | 80 | return hash_result 81 | 82 | def _generator(self, procs, hashlist): 83 | pe_table_name = intermed.IntermediateSymbolTable.create(self.context, 84 | self.config_path, 85 | "windows", 86 | "pe", 87 | class_types=pe.class_types) 88 | 89 | filter_func = lambda _: False 90 | if self.config.get('address', None) is not None: 91 | filter_func = lambda x: x.get_start() not in [self.config['address']] 92 | 93 | for proc in procs: 94 | 95 | proc_id = "Unknown" 96 | try: 97 | proc_id = proc.UniqueProcessId 98 | proc_layer_name = proc.add_process_layer() 99 | except exceptions.InvalidAddressException as excp: 100 | vollog.debug("Process {}: invalid address {} in layer {}".format(proc_id, excp.invalid_address, 101 | excp.layer_name)) 102 | continue 103 | 104 | for vad in vadinfo.VadInfo.list_vads(proc, filter_func=filter_func): 105 | 106 | # this parameter is inherited from the VadInfo plugin. if a user specifies 107 | # an address, then it bypasses the DLL identification heuristics 108 | if self.config.get("address", None) is None: 109 | 110 | # rather than relying on the PEB for DLLs, which can be swapped, 111 | # it requires special handling on wow64 processes, and its 112 | # unreliable from an integrity standpoint, let's use the VADs instead 113 | protection_string = vad.get_protection( 114 | vadinfo.VadInfo.protect_values(self.context, self.config['primary'], self.config['nt_symbols']), 115 | vadinfo.winnt_protections) 116 | 117 | # DLLs are write copy... 118 | if protection_string != "PAGE_EXECUTE_WRITECOPY": 119 | continue 120 | 121 | # DLLs have mapped files... 122 | if isinstance(vad.get_file_name(), interfaces.renderers.BaseAbsentValue): 123 | continue 124 | 125 | try: 126 | dos_header = self.context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", 127 | offset=vad.get_start(), 128 | layer_name=proc_layer_name) 129 | 130 | pe_data = io.BytesIO() 131 | 132 | for offset, data in dos_header.reconstruct(): 133 | pe_data.seek(offset) 134 | pe_data.write(data) 135 | 136 | pe_data_raw = pe_data.getvalue() 137 | 138 | pe_data.close() 139 | 140 | result_text = self.calc_hash(pe_data_raw) 141 | except Exception: 142 | result_text = "Unable to dump PE at {0:#x}".format(vad.get_start()) 143 | 144 | if (result_text in hashlist and len(result_text) == 32) or len(hashlist) == 0: 145 | yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(vad.get_start()), vad.get_file_name(), result_text)) 146 | 147 | def run(self): 148 | if not has_pefile: 149 | vollog.info("Python pefile module not found, plugin (and dependent plugins) not available") 150 | raise 151 | 152 | filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) 153 | 154 | hashlist = [] 155 | if self.config.get('imphash', None) is not None: 156 | hashlist.append(self.config['imphash']) 157 | elif self.config.get('imphashlist', None) is not None: 158 | rf = open(self.config['imphashlist'], "r") 159 | lines = rf.readlines() 160 | for line in lines: 161 | hashlist.append(line.rstrip()) 162 | 163 | return renderers.TreeGrid([("PID", int), ("ImageFileName", str), ("Module Base", format_hints.Hex), ("Module Name", str), ("imphash", str)], 164 | self._generator( 165 | pslist.PsList.list_processes(context=self.context, 166 | layer_name=self.config['primary'], 167 | symbol_table=self.config['nt_symbols'], 168 | filter_func=filter_func), hashlist)) 169 | 170 | 171 | class ImpFuzzy(interfaces.plugins.PluginInterface): 172 | """Listing the Import Fuzzy Hashing (impfuzzy)""" 173 | 174 | _required_framework_version = (2, 0, 0) 175 | 176 | @classmethod 177 | def get_requirements(cls): 178 | # Since we're calling the plugin, make sure we have the plugin's requirements 179 | return [requirements.TranslationLayerRequirement(name='primary', 180 | description='Memory layer for the kernel', 181 | architectures=["Intel32", "Intel64"]), 182 | requirements.SymbolTableRequirement(name="nt_symbols", description="Windows kernel symbols"), 183 | requirements.IntRequirement(name='pid', 184 | description="Process ID to include (all other processes are excluded)", 185 | optional=True), 186 | requirements.StringRequirement(name="impfuzzy", 187 | description="Search single impfuzzy value", 188 | optional=True), 189 | requirements.StringRequirement(name="impfuzzylist", 190 | description="Search impfuzzy list file", 191 | optional=True), 192 | requirements.StringRequirement(name="exefile", 193 | description="Comparing the PE file or direcroty", 194 | optional=True), 195 | requirements.IntRequirement(name='threshold', 196 | description="Import fuzzy hashing threshold (Default 30)", 197 | optional=True), 198 | requirements.PluginRequirement(name='pslist', plugin=pslist.PsList, version=(2, 0, 0)), 199 | requirements.PluginRequirement(name='vadinfo', plugin=vadinfo.VadInfo, version=(2, 0, 0)), 200 | ] 201 | 202 | @classmethod 203 | def calc_hash(cls, pe_data): 204 | try: 205 | fuzzy_result = pyimpfuzzy.get_impfuzzy_data(pe_data) 206 | except: 207 | fuzzy_result = "Unable to calc impfuzzy" 208 | 209 | return fuzzy_result 210 | 211 | def _generator(self, procs, hashlist, threshold): 212 | pe_table_name = intermed.IntermediateSymbolTable.create(self.context, 213 | self.config_path, 214 | "windows", 215 | "pe", 216 | class_types=pe.class_types) 217 | 218 | filter_func = lambda _: False 219 | if self.config.get('address', None) is not None: 220 | filter_func = lambda x: x.get_start() not in [self.config['address']] 221 | 222 | for proc in procs: 223 | 224 | proc_id = "Unknown" 225 | try: 226 | proc_id = proc.UniqueProcessId 227 | proc_layer_name = proc.add_process_layer() 228 | except exceptions.InvalidAddressException as excp: 229 | vollog.debug("Process {}: invalid address {} in layer {}".format(proc_id, excp.invalid_address, 230 | excp.layer_name)) 231 | continue 232 | 233 | for vad in vadinfo.VadInfo.list_vads(proc, filter_func=filter_func): 234 | 235 | # this parameter is inherited from the VadInfo plugin. if a user specifies 236 | # an address, then it bypasses the DLL identification heuristics 237 | if self.config.get("address", None) is None: 238 | 239 | # rather than relying on the PEB for DLLs, which can be swapped, 240 | # it requires special handling on wow64 processes, and its 241 | # unreliable from an integrity standpoint, let's use the VADs instead 242 | protection_string = vad.get_protection( 243 | vadinfo.VadInfo.protect_values(self.context, self.config['primary'], self.config['nt_symbols']), 244 | vadinfo.winnt_protections) 245 | 246 | # DLLs are write copy... 247 | if protection_string != "PAGE_EXECUTE_WRITECOPY": 248 | continue 249 | 250 | # DLLs have mapped files... 251 | if isinstance(vad.get_file_name(), interfaces.renderers.BaseAbsentValue): 252 | continue 253 | 254 | try: 255 | dos_header = self.context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", 256 | offset=vad.get_start(), 257 | layer_name=proc_layer_name) 258 | 259 | pe_data = io.BytesIO() 260 | 261 | for offset, data in dos_header.reconstruct(): 262 | pe_data.seek(offset) 263 | pe_data.write(data) 264 | 265 | pe_data_raw = pe_data.getvalue() 266 | 267 | pe_data.close() 268 | 269 | result_text = self.calc_hash(pe_data_raw) 270 | except Exception: 271 | result_text = "Unable to dump PE at {0:#x}".format(vad.get_start()) 272 | 273 | if len(hashlist) == 0: 274 | yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(vad.get_start()), vad.get_file_name(), result_text)) 275 | elif not "Unable" in result_text: 276 | for hash in hashlist: 277 | if pyimpfuzzy.hash_compare(result_text, hash) >= threshold: 278 | yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(vad.get_start()), vad.get_file_name(), result_text)) 279 | 280 | def run(self): 281 | if not has_pyimpfuzzy: 282 | vollog.info("Python pyimpfuzzy module not found, plugin (and dependent plugins) not available") 283 | raise 284 | 285 | # This is a impfuzzys threshold 286 | if self.config.get('threshold', None) is not None: 287 | threshold = self.config['threshold'] 288 | else: 289 | threshold = 30 290 | 291 | filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) 292 | 293 | files = [] 294 | hashlist = [] 295 | if self.config.get('impfuzzy', None) is not None: 296 | hashlist.append(self.config['impfuzzy']) 297 | elif self.config.get('impfuzzylist', None) is not None: 298 | rf = open(self.config['impfuzzylist'], "r") 299 | lines = rf.readlines() 300 | for line in lines: 301 | hashlist.append(line.rstrip()) 302 | elif self.config.get('exefile', None) is not None: 303 | if os.path.isdir(self.config['exefile']): 304 | for root, dirs, filenames in os.walk(self.config['exefile']): 305 | for name in filenames: 306 | files.append(os.path.join(root, name)) 307 | elif os.path.isfile(self.config['exefile']): 308 | files.append(self.config['exefile']) 309 | 310 | for file in files: 311 | hashlist.append(pyimpfuzzy.get_impfuzzy(file)) 312 | 313 | return renderers.TreeGrid([("PID", int), ("ImageFileName", str), ("Module Base", format_hints.Hex), ("Module Name", str), ("impfuzzy", str)], 314 | self._generator( 315 | pslist.PsList.list_processes(context=self.context, 316 | layer_name=self.config['primary'], 317 | symbol_table=self.config['nt_symbols'], 318 | filter_func=filter_func), hashlist, threshold)) 319 | 320 | 321 | class Ssdeep(interfaces.plugins.PluginInterface): 322 | """Listing the File ssdeep""" 323 | 324 | _required_framework_version = (2, 0, 0) 325 | 326 | @classmethod 327 | def get_requirements(cls): 328 | # Since we're calling the plugin, make sure we have the plugin's requirements 329 | return [requirements.TranslationLayerRequirement(name='primary', 330 | description='Memory layer for the kernel', 331 | architectures=["Intel32", "Intel64"]), 332 | requirements.SymbolTableRequirement(name="nt_symbols", description="Windows kernel symbols"), 333 | requirements.IntRequirement(name='pid', 334 | description="Process ID to include (all other processes are excluded)", 335 | optional=True), 336 | requirements.StringRequirement(name="ssdeep", 337 | description="Search single ssdeep value", 338 | optional=True), 339 | requirements.StringRequirement(name="ssdeeplist", 340 | description="Search ssdeep list file", 341 | optional=True), 342 | requirements.StringRequirement(name="exefile", 343 | description="Comparing the PE file or direcroty", 344 | optional=True), 345 | requirements.IntRequirement(name='threshold', 346 | description="Ssdeep threshold (Default 30)", 347 | optional=True), 348 | requirements.PluginRequirement(name='pslist', plugin=pslist.PsList, version=(2, 0, 0)), 349 | requirements.PluginRequirement(name='vadinfo', plugin=vadinfo.VadInfo, version=(2, 0, 0)), 350 | ] 351 | 352 | @classmethod 353 | def calc_hash(cls, pe_data): 354 | try: 355 | fuzzy_result = ssdeep.hash(pe_data) 356 | except: 357 | fuzzy_result = "Unable to calc ssdeep" 358 | 359 | return fuzzy_result 360 | 361 | def _generator(self, procs, hashlist, threshold): 362 | pe_table_name = intermed.IntermediateSymbolTable.create(self.context, 363 | self.config_path, 364 | "windows", 365 | "pe", 366 | class_types=pe.class_types) 367 | 368 | filter_func = lambda _: False 369 | if self.config.get('address', None) is not None: 370 | filter_func = lambda x: x.get_start() not in [self.config['address']] 371 | 372 | for proc in procs: 373 | 374 | proc_id = "Unknown" 375 | try: 376 | proc_id = proc.UniqueProcessId 377 | proc_layer_name = proc.add_process_layer() 378 | except exceptions.InvalidAddressException as excp: 379 | vollog.debug("Process {}: invalid address {} in layer {}".format(proc_id, excp.invalid_address, 380 | excp.layer_name)) 381 | continue 382 | 383 | for vad in vadinfo.VadInfo.list_vads(proc, filter_func=filter_func): 384 | 385 | # this parameter is inherited from the VadInfo plugin. if a user specifies 386 | # an address, then it bypasses the DLL identification heuristics 387 | if self.config.get("address", None) is None: 388 | 389 | # rather than relying on the PEB for DLLs, which can be swapped, 390 | # it requires special handling on wow64 processes, and its 391 | # unreliable from an integrity standpoint, let's use the VADs instead 392 | protection_string = vad.get_protection( 393 | vadinfo.VadInfo.protect_values(self.context, self.config['primary'], self.config['nt_symbols']), 394 | vadinfo.winnt_protections) 395 | 396 | # DLLs are write copy... 397 | if protection_string != "PAGE_EXECUTE_WRITECOPY": 398 | continue 399 | 400 | # DLLs have mapped files... 401 | if isinstance(vad.get_file_name(), interfaces.renderers.BaseAbsentValue): 402 | continue 403 | 404 | try: 405 | dos_header = self.context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", 406 | offset=vad.get_start(), 407 | layer_name=proc_layer_name) 408 | 409 | pe_data = io.BytesIO() 410 | 411 | for offset, data in dos_header.reconstruct(): 412 | pe_data.seek(offset) 413 | pe_data.write(data) 414 | 415 | pe_data_raw = pe_data.getvalue() 416 | 417 | pe_data.close() 418 | 419 | result_text = self.calc_hash(pe_data_raw) 420 | except Exception: 421 | result_text = "Unable to dump PE at {0:#x}".format(vad.get_start()) 422 | 423 | if len(hashlist) == 0: 424 | yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(vad.get_start()), vad.get_file_name(), result_text)) 425 | elif not "Unable" in result_text: 426 | for hash in hashlist: 427 | if ssdeep.compare(result_text, hash) >= threshold: 428 | yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast("string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(vad.get_start()), vad.get_file_name(), result_text)) 429 | 430 | def run(self): 431 | if not has_ssdeep: 432 | vollog.info("Python ssdeep module not found, plugin (and dependent plugins) not available") 433 | raise 434 | 435 | # This is a ssdeep threshold 436 | if self.config.get('threshold', None) is not None: 437 | threshold = self.config['threshold'] 438 | else: 439 | threshold = 30 440 | 441 | filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) 442 | 443 | hashlist = [] 444 | if self.config.get('ssdeep', None) is not None: 445 | hashlist.append(self.config['ssdeep']) 446 | elif self.config.get('ssdeeplist', None) is not None: 447 | rf = open(self.config['ssdeeplist'], "r") 448 | lines = rf.readlines() 449 | for line in lines: 450 | hashlist.append(line.rstrip()) 451 | elif self.config.get('exefile', None) is not None: 452 | if os.path.isdir(self.config['exefile']): 453 | for root, dirs, filenames in os.walk(self.config['exefile']): 454 | for name in filenames: 455 | files.append(os.path.join(root, name)) 456 | elif os.path.isfile(self.config['exefile']): 457 | files.append(self.config['exefile']) 458 | 459 | for file in files: 460 | hashlist.append(ssdeep.hash(file)) 461 | 462 | return renderers.TreeGrid([("PID", int), ("ImageFileName", str), ("Module Base", format_hints.Hex), ("Module Name", str), ("ssdeep", str)], 463 | self._generator( 464 | pslist.PsList.list_processes(context=self.context, 465 | layer_name=self.config['primary'], 466 | symbol_table=self.config['nt_symbols'], 467 | filter_func=filter_func), hashlist, threshold)) 468 | -------------------------------------------------------------------------------- /pyimpfuzzy-windows/README.md: -------------------------------------------------------------------------------- 1 | # pyimpfuzzy-windows 2 | Python module comparing the impfuzzy for Windows 3 | pyimpfuzzy-windows is python module which calculate and compare the impfuzzy(import fuzzy hashing) 4 | This module is pyimpfuzzy for Windows version. For Linux and Mac OS users, please use [pyimpfuzzy](https://github.com/JPCERTCC/impfuzzy/tree/master/pyimpfuzzy). 5 | 6 | More details are described in the following documents: 7 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy.html (Japanese) 8 | http://blog.jpcert.or.jp/2016/05/classifying-mal-a988.html (English) 9 | 10 | ## Requirements 11 | pyimpfuzzy-windows requires the following modules: 12 | 13 | * pefile 1.2.10-139 or later 14 | 15 | ## Installation 16 | 17 | ```bash 18 | $ pip install pyimpfuzzy-windows 19 | ``` 20 | or 21 | ```bash 22 | $ python setup.py install 23 | ``` 24 | 25 | ## Usage 26 | * get_impfuzzy - return the impfuzzy hash for a given file 27 | * get_impfuzzy_data - return the impfuzzy hash for a buffer 28 | * hash_compare - return the match between 2 hashes 29 | 30 | ### Example Usage 31 | 32 | ```python 33 | import pyimpfuzzy 34 | import sys 35 | 36 | hash1 = pyimpfuzzy.get_impfuzzy(sys.argv[1]) 37 | hash2 = pyimpfuzzy.get_impfuzzy(sys.argv[2]) 38 | print("ImpFuzzy1: %s" % hash1) 39 | print("ImpFuzzy2: %s" % hash2) 40 | print("Compare: %i" % pyimpfuzzy.hash_compare(hash1, hash2)) 41 | ``` 42 | 43 | ## Notes 44 | This module includes [fuzzy.dll version 2.14.1](https://github.com/ssdeep-project/ssdeep/releases/tag/release-2.14.1). 45 | -------------------------------------------------------------------------------- /pyimpfuzzy-windows/bin/fuzzy.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPCERTCC/impfuzzy/b30548d005c9d980b3e3630648b39830597293fc/pyimpfuzzy-windows/bin/fuzzy.dll -------------------------------------------------------------------------------- /pyimpfuzzy-windows/bin/fuzzy64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPCERTCC/impfuzzy/b30548d005c9d980b3e3630648b39830597293fc/pyimpfuzzy-windows/bin/fuzzy64.dll -------------------------------------------------------------------------------- /pyimpfuzzy-windows/pyimpfuzzy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import ctypes 5 | import pefile 6 | import ordlookup 7 | from os.path import join 8 | from os.path import split 9 | from pip._vendor import six 10 | 11 | # Length of an individual fuzzy hash signature component 12 | SPAMSUM_LENGTH = 64 13 | 14 | # The longest possible length for a fuzzy hash signature 15 | FUZZY_MAX_RESULT = (2 * SPAMSUM_LENGTH + 20) 16 | 17 | is_64bits = sys.maxsize > 2**32 18 | _package_path = split(__file__)[0] 19 | _lib_path = join(_package_path, r'bin\fuzzy64.dll' if is_64bits else r'bin\fuzzy.dll') 20 | fuzzy_lib = ctypes.cdll.LoadLibrary(_lib_path) 21 | 22 | def _get_hash(pe): 23 | apilist = pe.calc_impfuzzy() 24 | if isinstance(apilist, six.text_type): 25 | apilist = apilist.encode("ascii") 26 | 27 | result_buffer = ctypes.create_string_buffer(FUZZY_MAX_RESULT) 28 | file_buffer = ctypes.create_string_buffer(apilist) 29 | hash_result = fuzzy_lib.fuzzy_hash_buf(file_buffer, len(file_buffer) - 1, result_buffer) 30 | hash_value = result_buffer.value.decode("ascii") 31 | 32 | return hash_value 33 | 34 | def get_impfuzzy(file): 35 | pe = pefileEx(file) 36 | 37 | return _get_hash(pe) 38 | 39 | 40 | def get_impfuzzy_data(file): 41 | pe = pefileEx(data=file) 42 | 43 | return _get_hash(pe) 44 | 45 | 46 | def hash_compare(hash1, hash2): 47 | if isinstance(hash1, six.text_type): 48 | hash1 = hash1.encode("ascii") 49 | 50 | if isinstance(hash2, six.text_type): 51 | hash2 = hash2.encode("ascii") 52 | 53 | hash_1_buffer = ctypes.create_string_buffer(hash1) 54 | hash_2_buffer = ctypes.create_string_buffer(hash2) 55 | 56 | return fuzzy_lib.fuzzy_compare(hash_1_buffer, hash_2_buffer) 57 | 58 | 59 | class pefileEx(pefile.PE): 60 | 61 | def __init__(self, *args, **kwargs): 62 | pefile.PE.__init__(self, *args, **kwargs) 63 | 64 | def calc_impfuzzy(self): 65 | impstrs = [] 66 | exts = ["ocx", "sys", "dll"] 67 | if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"): 68 | return "" 69 | for entry in self.DIRECTORY_ENTRY_IMPORT: 70 | no_iat_flag = False 71 | if isinstance(entry.dll, bytes): 72 | libname = entry.dll.decode().lower() 73 | else: 74 | libname = entry.dll.lower() 75 | parts = libname.rsplit(".", 1) 76 | if len(parts) > 1 and parts[1] in exts: 77 | libname = parts[0] 78 | 79 | if not entry.imports[0].struct_iat: 80 | no_iat_flag = True 81 | 82 | for imp in entry.imports: 83 | funcname = None 84 | if imp.struct_iat or no_iat_flag: 85 | if not imp.name: 86 | funcname = ordlookup.ordLookup( 87 | entry.dll.lower(), imp.ordinal, make_name=True) 88 | if not funcname: 89 | raise Exception("Unable to look up ordinal %s:%04x" % ( 90 | entry.dll, imp.ordinal)) 91 | else: 92 | funcname = imp.name 93 | 94 | if not funcname: 95 | continue 96 | 97 | if isinstance(funcname, bytes): 98 | funcname = funcname.decode() 99 | impstrs.append("%s.%s" % (libname.lower(), funcname.lower())) 100 | 101 | apilist = ",".join(impstrs) 102 | return apilist 103 | -------------------------------------------------------------------------------- /pyimpfuzzy-windows/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup, Extension 4 | 5 | 6 | setup( 7 | name="pyimpfuzzy-windows", 8 | version="0.1", 9 | author="JPCERT/CC Analysis Center", 10 | author_email="aa-info@jpcert.or.jp", 11 | license="the GNU General Public License version 2", 12 | description="impfuzzy python modules for Windows", 13 | long_description=open("README.md").read(), 14 | long_description_content_type="text/markdown", 15 | packages = ["bin"], 16 | py_modules=["pyimpfuzzy"], 17 | package_data={"bin": ["*.dll"]}, 18 | url="https://github.com/JPCERTCC/impfuzzy/", 19 | install_requires=['pefile'] 20 | ) 21 | -------------------------------------------------------------------------------- /pyimpfuzzy/README.md: -------------------------------------------------------------------------------- 1 | # pyimpfuzzy 2 | Python module for comparing the impfuzzy 3 | pyimpfuzzy is python module which calculate and compare the impfuzzy(import fuzzy hashing) 4 | 5 | More details are described in the following documents: 6 | https://www.jpcert.or.jp/magazine/acreport-impfuzzy.html (Japanese) 7 | http://blog.jpcert.or.jp/2016/05/classifying-mal-a988.html (English) 8 | 9 | ## Requirements 10 | pyimpfuzzy requires the following modules: 11 | 12 | * pefile 1.2.10-139 or later 13 | * ssdeep http://ssdeep.sourceforge.net 14 | 15 | ## Installation 16 | 17 | ```bash 18 | $ pip install pyimpfuzzy 19 | ``` 20 | or 21 | ```bash 22 | $ sudo python setup.py install 23 | ``` 24 | 25 | ## Usage 26 | * get_impfuzzy - return the impfuzzy hash for a given file 27 | * get_impfuzzy_data - return the impfuzzy hash for a buffer 28 | * hash_compare - return the match between 2 hashes 29 | 30 | ### Example Usage 31 | 32 | ```python 33 | import pyimpfuzzy 34 | import sys 35 | 36 | hash1 = pyimpfuzzy.get_impfuzzy(sys.argv[1]) 37 | hash2 = pyimpfuzzy.get_impfuzzy(sys.argv[2]) 38 | print("ImpFuzzy1: %s" % hash1) 39 | print("ImpFuzzy2: %s" % hash2) 40 | print("Compare: %i" % pyimpfuzzy.hash_compare(hash1, hash2)) 41 | ``` 42 | ## Archive 43 | [pyimpfuzzy-0.1.tar.gz](https://pypi.python.org/packages/9b/f9/3abdd7e0e2cbfe3328260c06e38e693d86d54b95e9954a7ca6b953005513/pyimpfuzzy-0.1.tar.gz) sha256 09c997df16c822d88f0aac21e21cdfb7195716e2b24dc6c4554eaa99b7de81da 44 | [pyimpfuzzy-0.2.tar.gz](https://pypi.python.org/packages/41/46/f01a1730da6b0a7e91a861b69ce1f79f244487ff1e4c05c30dba5cb22eea/pyimpfuzzy-0.2.tar.gz) sha256 06cfe1588055ceeef839446cea7bac9c08cce8aa2eb1621bc591a97af2729622 45 | [pyimpfuzzy-0.5.tar.gz](https://files.pythonhosted.org/packages/87/bd/552292946148a8300fcbca597f2acaf060a8e76d75219a3f4d0201178c1c/pyimpfuzzy-0.5.tar.gz) sha256 da9796df302db4b04a197128637f84988f1882f1e08fdd69bbf9fdc6cfbaf349 46 | -------------------------------------------------------------------------------- /pyimpfuzzy/impfuzzy_util.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static PyObject *impfuzzyError; 8 | 9 | static PyObject * fuzzy_hash_data(PyObject *self, PyObject *args){ 10 | PyObject *Hash = NULL; 11 | Py_ssize_t inputsize = 0; 12 | char *input = NULL; 13 | char *hashbuff; 14 | int i; 15 | 16 | if (!PyArg_ParseTuple(args, "s#", &input, &inputsize)) 17 | return NULL; 18 | 19 | hashbuff = malloc(FUZZY_MAX_RESULT); 20 | if (hashbuff == NULL) { 21 | PyErr_SetString(impfuzzyError, "Error cannot allocate buffer"); 22 | return NULL; 23 | } 24 | 25 | i = fuzzy_hash_buf((unsigned char*)input, (uint32_t)inputsize, (char *)hashbuff); 26 | if (i == 0) { 27 | Hash = PyUnicode_FromString(hashbuff); 28 | free(hashbuff); 29 | return Hash; 30 | } else { 31 | PyErr_SetString(impfuzzyError, "Error cannot compute hash"); 32 | free(hashbuff); 33 | return NULL; 34 | } 35 | } 36 | 37 | static PyObject * hash_compare(PyObject *self, PyObject *args){ 38 | char *Hash1 = NULL; 39 | char *Hash2 = NULL; 40 | int i; 41 | if (!PyArg_ParseTuple(args, "ss", &Hash1, &Hash2)) { 42 | return NULL; 43 | } 44 | i = fuzzy_compare(Hash1, Hash2); 45 | if (i >= 0) { 46 | return Py_BuildValue("i", i); 47 | } else { 48 | PyErr_SetString(impfuzzyError, "Error cannot compare fuzzy hash"); 49 | return NULL; 50 | } 51 | } 52 | 53 | static PyMethodDef impfuzzyMethods[] = { 54 | {"hash_data", fuzzy_hash_data, METH_VARARGS, "calculate the fuzzy hashing for a strings"}, 55 | {"compare", hash_compare, METH_VARARGS, "compare two ssdeep hashes"}, 56 | {NULL, NULL} 57 | }; 58 | 59 | static char doc[] = "Fuzzy hashing module"; 60 | 61 | #if PY_MAJOR_VERSION >= 3 62 | static struct PyModuleDef moduledef = { 63 | PyModuleDef_HEAD_INIT, 64 | "impfuzzyutil", /* m_name */ 65 | doc, /* m_doc */ 66 | -1, /* m_size */ 67 | impfuzzyMethods, /* m_methods */ 68 | NULL, /* m_reload */ 69 | NULL, /* m_traverse */ 70 | NULL, /* m_clear */ 71 | NULL, /* m_free */ 72 | }; 73 | #endif 74 | 75 | static PyObject * 76 | impfuzzyutilinit(void) 77 | { 78 | PyObject* m; 79 | #if PY_MAJOR_VERSION < 3 80 | m = Py_InitModule3("impfuzzyutil", impfuzzyMethods, doc); 81 | #else 82 | m = PyModule_Create(&moduledef); 83 | #endif 84 | impfuzzyError = PyErr_NewException("impfuzzy.Error", NULL, NULL); 85 | Py_INCREF(impfuzzyError); 86 | PyModule_AddObject(m, "error", impfuzzyError); 87 | return m; 88 | } 89 | 90 | #if PY_MAJOR_VERSION < 3 91 | PyMODINIT_FUNC initimpfuzzyutil(void) 92 | { 93 | impfuzzyutilinit(); 94 | } 95 | #else 96 | PyMODINIT_FUNC PyInit_impfuzzyutil(void) 97 | { 98 | return impfuzzyutilinit(); 99 | } 100 | #endif 101 | -------------------------------------------------------------------------------- /pyimpfuzzy/pyimpfuzzy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import pefile 4 | import impfuzzyutil 5 | import ordlookup 6 | 7 | 8 | def get_impfuzzy(file): 9 | pe = pefileEx(file) 10 | apilist = pe.calc_impfuzzy() 11 | 12 | return impfuzzyutil.hash_data(apilist) 13 | 14 | 15 | def get_impfuzzy_data(file): 16 | pe = pefileEx(data=file) 17 | apilist = pe.calc_impfuzzy() 18 | 19 | return impfuzzyutil.hash_data(apilist) 20 | 21 | 22 | def hash_compare(hash1, hash2): 23 | return impfuzzyutil.compare(hash1, hash2) 24 | 25 | 26 | class pefileEx(pefile.PE): 27 | 28 | def __init__(self, *args, **kwargs): 29 | pefile.PE.__init__(self, *args, **kwargs) 30 | 31 | def calc_impfuzzy(self): 32 | impstrs = [] 33 | exts = ["ocx", "sys", "dll"] 34 | if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"): 35 | return "" 36 | for entry in self.DIRECTORY_ENTRY_IMPORT: 37 | no_iat_flag = False 38 | if isinstance(entry.dll, bytes): 39 | libname = entry.dll.decode().lower() 40 | else: 41 | libname = entry.dll.lower() 42 | parts = libname.rsplit(".", 1) 43 | if len(parts) > 1 and parts[1] in exts: 44 | libname = parts[0] 45 | 46 | if not entry.imports[0].struct_iat: 47 | no_iat_flag = True 48 | 49 | for imp in entry.imports: 50 | funcname = None 51 | if imp.struct_iat or no_iat_flag: 52 | if not imp.name: 53 | funcname = ordlookup.ordLookup( 54 | entry.dll.lower(), imp.ordinal, make_name=True) 55 | if not funcname: 56 | raise Exception("Unable to look up ordinal %s:%04x" % ( 57 | entry.dll, imp.ordinal)) 58 | else: 59 | funcname = imp.name 60 | 61 | if not funcname: 62 | continue 63 | 64 | if isinstance(funcname, bytes): 65 | funcname = funcname.decode() 66 | impstrs.append("%s.%s" % (libname.lower(), funcname.lower())) 67 | 68 | apilist = ",".join(impstrs) 69 | return apilist 70 | -------------------------------------------------------------------------------- /pyimpfuzzy/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup, Extension 4 | 5 | 6 | setup( 7 | name="pyimpfuzzy", 8 | version="0.5", 9 | author="JPCERT/CC Analysis Center", 10 | author_email="aa-info@jpcert.or.jp", 11 | license="the GNU General Public License version 2", 12 | description="Python modules for impfuzzy", 13 | long_description=open("README.md").read(), 14 | long_description_content_type="text/markdown", 15 | ext_modules=[Extension( 16 | "impfuzzyutil", 17 | sources=["impfuzzy_util.c"], 18 | libraries=["fuzzy"], 19 | library_dirs=["/usr/local/lib/", ], 20 | include_dirs=["/usr/local/include/", ], 21 | )], 22 | py_modules=["pyimpfuzzy"], 23 | url="https://github.com/JPCERTCC/impfuzzy/", 24 | install_requires=['pefile'] 25 | ) 26 | --------------------------------------------------------------------------------