├── .gitmodules ├── LICENSE ├── MANIFEST.in ├── README.rst ├── config.h ├── setup.cfg ├── setup.py ├── tests.py └── yara-python.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "yara"] 2 | path = yara 3 | url = https://git@github.com/rednaga/yara.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.c *.h 2 | include yara/libyara/modules/module_list 3 | recursive-include yara *.c *.h 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | yara-python 2 | =========== 3 | 4 | With this library you can use `YARA `_ from 5 | your Python programs. It covers all YARA's features, from compiling, saving 6 | and loading rules to scanning files, strings and processes. 7 | 8 | Here it goes a little example: 9 | 10 | .. code-block:: python 11 | 12 | >>> import yara 13 | >>> rule = yara.compile(source='rule foo: bar {strings: $a = "lmn" condition: $a}') 14 | >>> matches = rule.match(data='abcdefgjiklmnoprstuvwxyz') 15 | >>> print(matches) 16 | [foo] 17 | >>> print(matches[0].rule) 18 | foo 19 | >>> print(matches[0].tags) 20 | ['bar'] 21 | >>> print(matches[0].strings) 22 | [(10L, '$a', 'lmn')] 23 | 24 | 25 | Installation 26 | ------------ 27 | 28 | The easiest way of installing YARA is by using ``pip``: 29 | 30 | .. code-block:: bash 31 | 32 | $ pip install yara-python 33 | 34 | But you can also get the source from GitHub and compile it yourself: 35 | 36 | .. code-block:: bash 37 | 38 | $ git clone --recursive https://github.com/VirusTotal/yara-python 39 | $ cd yara-python 40 | $ python setup.py build 41 | $ sudo python setup.py install 42 | 43 | Notice the ``--recursive`` option used with ``git``. This is important because 44 | we need to download the ``yara`` subproject containing the source code for 45 | ``libyara`` (the core YARA library). It's also important to note that the two 46 | methods above link ``libyara`` statically into yara-python. If you want to link 47 | dynamically against a shared ``libyara`` library use: 48 | 49 | .. code-block:: bash 50 | 51 | $ sudo python setup.py install --dynamic-linking 52 | 53 | For this option to work you must build and install 54 | `YARA `_ separately before installing 55 | ``yara-python``. 56 | 57 | 58 | Documentation 59 | ------------- 60 | 61 | Find more information about how to use yara-python at 62 | http://yara.readthedocs.org/en/latest/yarapython.html. 63 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | This a dummy file to satisfy some #include "config.h" directives while 3 | building libyara. 4 | */ 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | from distutils.command.build import build 18 | from distutils.command.install import install 19 | from setuptools import setup, Extension 20 | from codecs import open 21 | 22 | import distutils.errors 23 | import distutils.ccompiler 24 | import distutils.sysconfig 25 | 26 | import contextlib 27 | import os 28 | import sys 29 | import tempfile 30 | import shutil 31 | import subprocess 32 | 33 | @contextlib.contextmanager 34 | def muted(*streams): 35 | """A context manager to redirect stdout and/or stderr to /dev/null. 36 | 37 | Examples: 38 | with muted(sys.stdout): 39 | ... 40 | 41 | with muted(sys.stderr): 42 | ... 43 | 44 | with muted(sys.stdout, sys.stderr): 45 | ... 46 | """ 47 | devnull = open(os.devnull, 'w') 48 | try: 49 | old_streams = [os.dup(s.fileno()) for s in streams] 50 | for s in streams: 51 | os.dup2(devnull.fileno(), s.fileno()) 52 | yield 53 | finally: 54 | for o,n in zip(old_streams, streams): 55 | os.dup2(o, n.fileno()) 56 | devnull.close() 57 | 58 | 59 | def has_function(function_name, libraries=None): 60 | """Checks if a given functions exists in the current platform.""" 61 | compiler = distutils.ccompiler.new_compiler() 62 | with muted(sys.stdout, sys.stderr): 63 | result = compiler.has_function( 64 | function_name, libraries=libraries) 65 | if os.path.exists('a.out'): 66 | os.remove('a.out') 67 | return result 68 | 69 | class CustomInstall(install): 70 | def run(self): 71 | self.run_command('build') 72 | install.run(self) 73 | 74 | class BuildCommand(build): 75 | 76 | user_options = build.user_options + [ 77 | ('dynamic-linking', None,'link dynamically against libyara'), 78 | ('enable-cuckoo', None,'enable "cuckoo" module'), 79 | ('enable-magic', None,'enable "magic" module'), 80 | ('enable-profiling', None,'enable profiling features')] 81 | 82 | boolean_options = build.boolean_options + [ 83 | 'dynamic-linking', 'enable-cuckoo', 'enable-magic', 'enable-profiling'] 84 | 85 | def initialize_options(self): 86 | build.initialize_options(self) 87 | self.dynamic_linking = None 88 | self.enable_magic = None 89 | self.enable_cuckoo = None 90 | self.enable_profiling = None 91 | 92 | def finalize_options(self): 93 | build.finalize_options(self) 94 | if self.enable_magic and self.dynamic_linking: 95 | raise distutils.errors.DistutilsOptionError( 96 | '--enable-magic can''t be used with --dynamic-linking') 97 | if self.enable_cuckoo and self.dynamic_linking: 98 | raise distutils.errors.DistutilsOptionError( 99 | '--enable-cuckoo can''t be used with --dynamic-linking') 100 | 101 | def run(self): 102 | """Execute the build command.""" 103 | if os.path.exists('.git'): 104 | from subprocess import check_call 105 | check_call(['git', 'submodule', 'update', '--init', '--recursive']) 106 | 107 | module = self.distribution.ext_modules[0] 108 | base_dir = os.path.dirname(__file__) 109 | 110 | if base_dir: 111 | os.chdir(base_dir) 112 | 113 | #exclusions = ['yara/libyara/modules/pe_utils.c'] 114 | exclusions = [] 115 | 116 | if self.plat_name in ('win32','win-amd64'): 117 | building_for_windows = True 118 | bits = '64' if self.plat_name == 'win-amd64' else '32' 119 | module.define_macros.append(('_CRT_SECURE_NO_WARNINGS','1')) 120 | module.include_dirs.append('yara/windows/include') 121 | module.libraries.append('advapi32') 122 | module.libraries.append('user32') 123 | else: 124 | building_for_windows = False 125 | 126 | if 'macosx' in self.plat_name: 127 | building_for_osx = True 128 | module.include_dirs.append('/opt/local/include') 129 | module.library_dirs.append('/opt/local/lib') 130 | else: 131 | building_for_osx = False 132 | 133 | if has_function('memmem'): 134 | module.define_macros.append(('HAVE_MEMMEM', '1')) 135 | if has_function('strlcpy'): 136 | module.define_macros.append(('HAVE_STRLCPY', '1')) 137 | if has_function('strlcat'): 138 | module.define_macros.append(('HAVE_STRLCAT', '1')) 139 | 140 | if self.enable_profiling: 141 | module.define_macros.append(('PROFILING_ENABLED', '1')) 142 | 143 | if self.dynamic_linking: 144 | module.libraries.append('yara') 145 | else: 146 | if building_for_windows: 147 | module.library_dirs.append('yara/windows/lib') 148 | 149 | if building_for_windows: 150 | module.define_macros.append(('HASH_MODULE', '1')) 151 | module.libraries.append('libeay%s' % bits) 152 | elif (has_function('MD5_Init', libraries=['crypto']) and 153 | has_function('SHA256_Init', libraries=['crypto'])): 154 | module.define_macros.append(('HASH_MODULE', '1')) 155 | module.libraries.append('crypto') 156 | else: 157 | exclusions.append('yara/libyara/modules/hash.c') 158 | 159 | if self.enable_magic: 160 | module.define_macros.append(('MAGIC_MODULE', '1')) 161 | else: 162 | exclusions.append('yara/libyara/modules/magic.c') 163 | 164 | if self.enable_cuckoo: 165 | module.define_macros.append(('CUCKOO_MODULE', '1')) 166 | if building_for_windows: 167 | module.libraries.append('jansson%s' % bits) 168 | else: 169 | module.libraries.append('jansson') 170 | else: 171 | exclusions.append('yara/libyara/modules/cuckoo.c') 172 | 173 | exclusions = [os.path.normpath(x) for x in exclusions] 174 | 175 | for directory, _, files in os.walk('yara/libyara/'): 176 | for x in files: 177 | x = os.path.normpath(os.path.join(directory, x)) 178 | if x.endswith('.c') and x not in exclusions: 179 | module.sources.append(x) 180 | 181 | build.run(self) 182 | 183 | 184 | with open('README.rst', 'r', 'utf-8') as f: 185 | readme = f.read() 186 | 187 | setup( 188 | name='yara-python', 189 | version='3.5.0.999', 190 | description='Python interface for YARA', 191 | long_description=readme, 192 | license='Apache 2.0', 193 | author='Victor M. Alvarez', 194 | author_email='plusvic@gmail.com;vmalvarez@virustotal.com', 195 | url='https://github.com/VirusTotal/yara-python', 196 | zip_safe=False, 197 | cmdclass={ 198 | 'build': BuildCommand, 199 | 'install': CustomInstall, 200 | }, 201 | ext_modules=[Extension( 202 | name='yara', 203 | include_dirs=['yara/libyara/include', 'yara/libyara/', '.'], 204 | sources=['yara-python.c'])]) 205 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2007-2014. The YARA Authors. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | import tempfile 18 | import binascii 19 | import os 20 | import sys 21 | import unittest 22 | import yara 23 | # Python 2/3 24 | try: 25 | import StringIO 26 | except: 27 | import io 28 | 29 | PE32_FILE = binascii.unhexlify('\ 30 | 4d5a000000000000000000000000000000000000000000000000000000000000\ 31 | 0000000000000000000000000000000000000000000000000000000040000000\ 32 | 504500004c0101005dbe45450000000000000000e00003010b01080004000000\ 33 | 0000000000000000600100006001000064010000000040000100000001000000\ 34 | 0400000000000000040000000000000064010000600100000000000002000004\ 35 | 0000100000100000000010000010000000000000100000000000000000000000\ 36 | 0000000000000000000000000000000000000000000000000000000000000000\ 37 | 0000000000000000000000000000000000000000000000000000000000000000\ 38 | 0000000000000000000000000000000000000000000000000000000000000000\ 39 | 0000000000000000000000000000000000000000000000002e74657874000000\ 40 | 0400000060010000040000006001000000000000000000000000000020000060\ 41 | 6a2a58c3') 42 | 43 | ELF32_FILE = binascii.unhexlify('\ 44 | 7f454c4601010100000000000000000002000300010000006080040834000000\ 45 | a800000000000000340020000100280004000300010000000000000000800408\ 46 | 008004086c0000006c0000000500000000100000000000000000000000000000\ 47 | b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\ 48 | 657220322e30352e303100002e7368737472746162002e74657874002e636f6d\ 49 | 6d656e7400000000000000000000000000000000000000000000000000000000\ 50 | 000000000000000000000000000000000b000000010000000600000060800408\ 51 | 600000000c000000000000000000000010000000000000001100000001000000\ 52 | 00000000000000006c0000001f00000000000000000000000100000000000000\ 53 | 010000000300000000000000000000008b0000001a0000000000000000000000\ 54 | 0100000000000000') 55 | 56 | ELF64_FILE = binascii.unhexlify('\ 57 | 7f454c4602010100000000000000000002003e00010000008000400000000000\ 58 | 4000000000000000c80000000000000000000000400038000100400004000300\ 59 | 0100000005000000000000000000000000004000000000000000400000000000\ 60 | 8c000000000000008c0000000000000000002000000000000000000000000000\ 61 | b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\ 62 | 657220322e30352e303100002e7368737472746162002e74657874002e636f6d\ 63 | 6d656e7400000000000000000000000000000000000000000000000000000000\ 64 | 0000000000000000000000000000000000000000000000000000000000000000\ 65 | 00000000000000000b0000000100000006000000000000008000400000000000\ 66 | 80000000000000000c0000000000000000000000000000001000000000000000\ 67 | 0000000000000000110000000100000000000000000000000000000000000000\ 68 | 8c000000000000001f0000000000000000000000000000000100000000000000\ 69 | 0000000000000000010000000300000000000000000000000000000000000000\ 70 | ab000000000000001a0000000000000000000000000000000100000000000000\ 71 | 0000000000000000') 72 | 73 | # The 3 possible outcomes for each pattern 74 | [SUCCEED, FAIL, SYNTAX_ERROR] = range(3) 75 | 76 | 77 | RE_TESTS = [ 78 | 79 | # RE, string, expected result, expected matching 80 | 81 | (')', '', SYNTAX_ERROR), 82 | ('abc', 'abc', SUCCEED, 'abc'), 83 | ('abc', 'xbc', FAIL), 84 | ('abc', 'axc', FAIL), 85 | ('abc', 'abx', FAIL), 86 | ('abc', 'xabcx', SUCCEED, 'abc'), 87 | ('abc', 'ababc', SUCCEED, 'abc'), 88 | ('a.c', 'abc', SUCCEED, 'abc'), 89 | ('a.b', 'a\nb', FAIL), 90 | ('a.*b', 'acc\nccb', FAIL), 91 | ('a.{4,5}b', 'acc\nccb', FAIL), 92 | ('a.b', 'a\rb', SUCCEED, 'a\rb'), 93 | ('ab*c', 'abc', SUCCEED, 'abc'), 94 | ('ab*c', 'ac', SUCCEED, 'ac'), 95 | ('ab*bc', 'abc', SUCCEED, 'abc'), 96 | ('ab*bc', 'abbc', SUCCEED, 'abbc'), 97 | ('a.*bb', 'abbbb', SUCCEED, 'abbbb'), 98 | ('a.*?bbb', 'abbbbbb', SUCCEED, 'abbb'), 99 | ('a.*c', 'ac', SUCCEED, 'ac'), 100 | ('a.*c', 'axyzc', SUCCEED, 'axyzc'), 101 | ('ab+c', 'abbc', SUCCEED, 'abbc'), 102 | ('ab+c', 'ac', FAIL), 103 | ('ab+', 'abbbb', SUCCEED, 'abbbb'), 104 | ('ab+?', 'abbbb', SUCCEED, 'ab'), 105 | ('ab+bc', 'abc', FAIL), 106 | ('ab+bc', 'abq', FAIL), 107 | ('a+b+c', 'aabbabc', SUCCEED, 'abc'), 108 | ('ab?bc', 'abbbbc', FAIL), 109 | ('ab?c', 'abc', SUCCEED, 'abc'), 110 | ('ab*?', 'abbb', SUCCEED, 'a'), 111 | ('ab?c', 'abc', SUCCEED, 'abc'), 112 | ('ab??', 'ab', SUCCEED, 'a'), 113 | ('a(b|x)c', 'abc', SUCCEED, 'abc'), 114 | ('a(b|x)c', 'axc', SUCCEED, 'axc'), 115 | ('a(b|.)c', 'axc', SUCCEED, 'axc'), 116 | ('a(b|x|y)c', 'ayc', SUCCEED, 'ayc'), 117 | ('(a+|b)*', 'ab', SUCCEED, 'ab'), 118 | ('a|b|c|d|e', 'e', SUCCEED, 'e'), 119 | ('(a|b|c|d|e)f', 'ef', SUCCEED, 'ef'), 120 | ('.b{2}', 'abb', SUCCEED, 'abb'), 121 | ('ab{1}c', 'abc', SUCCEED, 'abc'), 122 | ('ab{1,2}c', 'abbc', SUCCEED, 'abbc'), 123 | ('ab{1,}c', 'abbbc', SUCCEED, 'abbbc'), 124 | ('ab{1,}b', 'ab', FAIL), 125 | ('ab{1}c', 'abbc', FAIL), 126 | ('ab{0,}c', 'ac', SUCCEED, 'ac'), 127 | ('ab{0,}c', 'abbbc', SUCCEED, 'abbbc'), 128 | ('ab{,3}c', 'abbbc', SUCCEED, 'abbbc'), 129 | ('ab{,2}c', 'abbbc', FAIL), 130 | ('ab{4,5}bc', 'abbbbc', FAIL), 131 | ('ab{2,3}?', 'abbbbb', SUCCEED, 'abb'), 132 | ('ab{.*}', 'ab{c}', SUCCEED, 'ab{c}'), 133 | ('.(aa){1,2}', 'aaaaaaaaaa', SUCCEED, 'aaaaa'), 134 | ('a.(bc.){2}', 'aabcabca', SUCCEED, 'aabcabca'), 135 | ('(ab{1,2}c){1,3}', 'abbcabc', SUCCEED, 'abbcabc'), 136 | ('ab(c|cc){1,3}d', 'abccccccd', SUCCEED, 'abccccccd'), 137 | ('a[bx]c', 'abc', SUCCEED, 'abc'), 138 | ('a[bx]c', 'axc', SUCCEED, 'axc'), 139 | ('a[0-9]*b', 'ab', SUCCEED, 'ab'), 140 | ('a[0-9]*b', 'a0123456789b', SUCCEED, 'a0123456789b'), 141 | ('[0-9a-f]+', '0123456789abcdef', SUCCEED, '0123456789abcdef'), 142 | ('[0-9a-f]+', 'xyz0123456789xyz', SUCCEED, '0123456789'), 143 | ('a[\s\S]b', 'a b', SUCCEED, 'a b'), 144 | ('a[\d\D]b', 'a1b', SUCCEED, 'a1b'), 145 | ('[x-z]+', 'abc', FAIL), 146 | ('a[-]?c', 'ac', SUCCEED, 'ac'), 147 | ('a[-b]', 'a-', SUCCEED, 'a-'), 148 | ('a[-b]', 'ab', SUCCEED, 'ab'), 149 | ('a[b-]', 'a-', SUCCEED, 'a-'), 150 | ('a[b-]', 'ab', SUCCEED, 'ab'), 151 | ('[a-c-e]', 'b', SUCCEED, 'b'), 152 | ('[a-c-e]', '-', SUCCEED, '-'), 153 | ('[a-c-e]', 'd', FAIL), 154 | ('[b-a]', '', SYNTAX_ERROR), 155 | ('(abc', '', SYNTAX_ERROR), 156 | ('abc)', '', SYNTAX_ERROR), 157 | ('a[]b', '', SYNTAX_ERROR), 158 | ('a\\', '', SYNTAX_ERROR), 159 | ('a[\\-b]', 'a-', SUCCEED, 'a-'), 160 | ('a[\\-b]', 'ab', SUCCEED, 'ab'), 161 | ('a[\\', '', SYNTAX_ERROR), 162 | ('a]', 'a]', SUCCEED, 'a]'), 163 | ('a[]]b', 'a]b', SUCCEED, 'a]b'), 164 | ('a[\]]b', 'a]b', SUCCEED, 'a]b'), 165 | ('a[^bc]d', 'aed', SUCCEED, 'aed'), 166 | ('a[^bc]d', 'abd', FAIL), 167 | ('a[^-b]c', 'adc', SUCCEED, 'adc'), 168 | ('a[^-b]c', 'a-c', FAIL), 169 | ('a[^]b]c', 'a]c', FAIL), 170 | ('a[^]b]c', 'adc', SUCCEED, 'adc'), 171 | ('[^ab]*', 'cde', SUCCEED, 'cde'), 172 | (')(', '', SYNTAX_ERROR), 173 | (r'a\sb', 'a b', SUCCEED, 'a b'), 174 | (r'a\sb', 'a\tb', SUCCEED, 'a\tb'), 175 | (r'a\sb', 'a\rb', SUCCEED, 'a\rb'), 176 | (r'a\sb', 'a\nb', SUCCEED, 'a\nb'), 177 | (r'a\sb', 'a\vb', SUCCEED, 'a\vb'), 178 | (r'a\sb', 'a\fb', SUCCEED, 'a\fb'), 179 | (r'a\Sb', 'a b', FAIL), 180 | (r'a\Sb', 'a\tb', FAIL), 181 | (r'a\Sb', 'a\rb', FAIL), 182 | (r'a\Sb', 'a\nb', FAIL), 183 | (r'a\Sb', 'a\vb', FAIL), 184 | (r'a\Sb', 'a\fb', FAIL), 185 | (r'\n\r\t\f\a', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'), 186 | (r'[\n][\r][\t][\f][\a]', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'), 187 | (r'\x00\x01\x02', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'), 188 | (r'[\x00-\x02]+', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'), 189 | (r'[\x00-\x02]+', '\x03\x04\x05', FAIL), 190 | (r'[\x5D]', ']', SUCCEED, ']'), 191 | (r'[\0x5A-\x5D]', '\x5B', SUCCEED, '\x5B'), 192 | (r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'), 193 | (r'[\x5C-\x5F]', '\x5E', SUCCEED, '\x5E'), 194 | (r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'), 195 | ('a\wc', 'abc', SUCCEED, 'abc'), 196 | ('a\wc', 'a_c', SUCCEED, 'a_c'), 197 | ('a\wc', 'a0c', SUCCEED, 'a0c'), 198 | ('a\wc', 'a*c', FAIL), 199 | ('\w+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), 200 | ('[\w]+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), 201 | ('\D+', '1234abc5678', SUCCEED, 'abc'), 202 | ('[\d]+', '0123456789', SUCCEED, '0123456789'), 203 | ('[\D]+', '1234abc5678', SUCCEED, 'abc'), 204 | ('[\da-fA-F]+', '123abc', SUCCEED, '123abc'), 205 | ('^(ab|cd)e', 'abcde', FAIL), 206 | ('(abc|)ef', 'abcdef', SUCCEED, 'ef'), 207 | ('(abc|)ef', 'abcef', SUCCEED, 'abcef'), 208 | (r'\babc', 'abc', SUCCEED, 'abc'), 209 | (r'abc\b', 'abc', SUCCEED, 'abc'), 210 | (r'\babc', '1abc', FAIL), 211 | (r'abc\b', 'abc1', FAIL), 212 | (r'abc\s\b', 'abc x', SUCCEED, 'abc '), 213 | (r'abc\s\b', 'abc ', FAIL), 214 | (r'\babc\b', ' abc ', SUCCEED, 'abc'), 215 | (r'\b\w\w\w\b', ' abc ', SUCCEED, 'abc'), 216 | (r'\w\w\w\b', 'abcd', SUCCEED, 'bcd'), 217 | (r'\b\w\w\w', 'abcd', SUCCEED, 'abc'), 218 | (r'\b\w\w\w\b', 'abcd', FAIL), 219 | (r'\Babc', 'abc', FAIL), 220 | (r'abc\B', 'abc', FAIL), 221 | (r'\Babc', '1abc', SUCCEED, 'abc'), 222 | (r'abc\B', 'abc1', SUCCEED, 'abc'), 223 | (r'abc\s\B', 'abc x', FAIL), 224 | (r'abc\s\B', 'abc ', SUCCEED, 'abc '), 225 | (r'\w\w\w\B', 'abcd', SUCCEED, 'abc'), 226 | (r'\B\w\w\w', 'abcd', SUCCEED, 'bcd'), 227 | (r'\B\w\w\w\B', 'abcd', FAIL), 228 | 229 | # This is allowed in most regexp engines but in order to keep the 230 | # grammar free of shift/reduce conflicts I've decided not supporting 231 | # it. Users can use the (abc|) form instead. 232 | 233 | ('(|abc)ef', '', SYNTAX_ERROR), 234 | 235 | ('((a)(b)c)(d)', 'abcd', SUCCEED, 'abcd'), 236 | ('(a|b)c*d', 'abcd', SUCCEED, 'bcd'), 237 | ('(ab|ab*)bc', 'abc', SUCCEED, 'abc'), 238 | ('a([bc]*)c*', 'abc', SUCCEED, 'abc'), 239 | ('a([bc]*)c*', 'ac', SUCCEED, 'ac'), 240 | ('a([bc]*)c*', 'a', SUCCEED, 'a'), 241 | ('a([bc]*)(c*d)', 'abcd', SUCCEED, 'abcd'), 242 | ('a([bc]+)(c*d)', 'abcd', SUCCEED, 'abcd'), 243 | ('a([bc]*)(c+d)', 'abcd', SUCCEED, 'abcd'), 244 | ('a[bcd]*dcdcde', 'adcdcde', SUCCEED, 'adcdcde'), 245 | ('a[bcd]+dcdcde', 'adcdcde', FAIL), 246 | (r'\((.*), (.*)\)', '(a, b)', SUCCEED, '(a, b)'), 247 | ('abc|123$', 'abcx', SUCCEED, 'abc'), 248 | ('abc|123$', '123x', FAIL), 249 | ('abc|^123', '123', SUCCEED, '123'), 250 | ('abc|^123', 'x123', FAIL), 251 | ('^abc$', 'abc', SUCCEED, 'abc'), 252 | ('^abc$', 'abcc', FAIL), 253 | ('^abc', 'abcc', SUCCEED, 'abc'), 254 | ('^abc$', 'aabc', FAIL), 255 | ('abc$', 'aabc', SUCCEED, 'abc'), 256 | ('^a(bc+|b[eh])g|.h$', 'abhg', SUCCEED, 'abhg'), 257 | ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', SUCCEED, 'effgz'), 258 | ('(bc+d$|ef*g.|h?i(j|k))', 'ij', SUCCEED, 'ij'), 259 | ('(bc+d$|ef*g.|h?i(j|k))', 'effg', FAIL), 260 | ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', FAIL), 261 | ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', SUCCEED, 'effgz'), 262 | 263 | # Test case for issue #324 264 | ('whatever| x. x', ' xy x', SUCCEED, ' xy x'), 265 | ] 266 | 267 | 268 | class TestYara(unittest.TestCase): 269 | 270 | def assertTrueRules(self, rules, data='dummy'): 271 | 272 | for r in rules: 273 | r = yara.compile(source=r) 274 | self.assertTrue(r.match(data=data)) 275 | 276 | def assertFalseRules(self, rules, data='dummy'): 277 | 278 | for r in rules: 279 | r = yara.compile(source=r) 280 | self.assertFalse(r.match(data=data)) 281 | 282 | def assertSyntaxError(self, rules): 283 | 284 | for r in rules: 285 | self.assertRaises(yara.SyntaxError, yara.compile, source=r) 286 | 287 | def runReTest(self, test): 288 | 289 | regexp = test[0] 290 | string = test[1] 291 | expected_result = test[2] 292 | 293 | source = 'rule test { strings: $a = /%s/ condition: $a }' % regexp 294 | 295 | if expected_result == SYNTAX_ERROR: 296 | self.assertRaises(yara.SyntaxError, yara.compile, source=source) 297 | else: 298 | rule = yara.compile(source=source) 299 | matches = rule.match(data=string) 300 | if expected_result == SUCCEED: 301 | self.assertTrue(matches) 302 | _, _, matching_string = matches[0].strings[0] 303 | if sys.version_info[0] >= 3: 304 | self.assertTrue(matching_string == bytes(test[3], 'utf-8')) 305 | else: 306 | self.assertTrue(matching_string == test[3]) 307 | else: 308 | self.assertFalse(matches) 309 | 310 | def testBooleanOperators(self): 311 | 312 | self.assertTrueRules([ 313 | 'rule test { condition: true }', 314 | 'rule test { condition: true or false }', 315 | 'rule test { condition: true and true }', 316 | 'rule test { condition: 0x1 and 0x2}', 317 | ]) 318 | 319 | self.assertFalseRules([ 320 | 'rule test { condition: false }', 321 | 'rule test { condition: true and false }', 322 | 'rule test { condition: false or false }' 323 | ]) 324 | 325 | def testComparisonOperators(self): 326 | 327 | self.assertTrueRules([ 328 | 'rule test { condition: 2 > 1 }', 329 | 'rule test { condition: 1 < 2 }', 330 | 'rule test { condition: 2 >= 1 }', 331 | 'rule test { condition: 1 <= 1 }', 332 | 'rule test { condition: 1 == 1 }', 333 | 'rule test { condition: 1.5 == 1.5}', 334 | 'rule test { condition: 1.0 == 1}', 335 | 'rule test { condition: 1.5 >= 1.0}', 336 | 'rule test { condition: 1.5 >= 1}', 337 | 'rule test { condition: 1.0 >= 1}', 338 | 'rule test { condition: 0.5 < 1}', 339 | 'rule test { condition: 0.5 <= 1}', 340 | 'rule rest { condition: 1.0 <= 1}', 341 | 'rule rest { condition: "abc" == "abc"}', 342 | 'rule rest { condition: "abc" <= "abc"}', 343 | 'rule rest { condition: "abc" >= "abc"}', 344 | 'rule rest { condition: "ab" < "abc"}', 345 | 'rule rest { condition: "abc" > "ab"}', 346 | 'rule rest { condition: "abc" < "abd"}', 347 | 'rule rest { condition: "abd" > "abc"}', 348 | ]) 349 | 350 | self.assertFalseRules([ 351 | 'rule test { condition: 1 != 1}', 352 | 'rule test { condition: 1 != 1.0}', 353 | 'rule test { condition: 2 > 3}', 354 | 'rule test { condition: 2.1 < 2}', 355 | 'rule test { condition: "abc" != "abc"}', 356 | 'rule test { condition: "abc" > "abc"}', 357 | 'rule test { condition: "abc" < "abc"}', 358 | ]) 359 | 360 | def testArithmeticOperators(self): 361 | 362 | self.assertTrueRules([ 363 | 'rule test { condition: (1 + 1) * 2 == (9 - 1) \ 2 }', 364 | 'rule test { condition: 5 % 2 == 1 }', 365 | 'rule test { condition: 1.5 + 1.5 == 3}', 366 | 'rule test { condition: 3 \ 2 == 1}', 367 | 'rule test { condition: 3.0 \ 2 == 1.5}', 368 | 'rule test { condition: 1 + -1 == 0}', 369 | 'rule test { condition: -1 + -1 == -2}', 370 | 'rule test { condition: 4 --2 * 2 == 8}', 371 | 'rule test { condition: -1.0 * 1 == -1.0}', 372 | 'rule test { condition: 1-1 == 0}', 373 | 'rule test { condition: -2.0-3.0 == -5}', 374 | 'rule test { condition: --1 == 1}', 375 | 'rule test { condition: 1--1 == 2}', 376 | 'rule test { condition: -0x01 == -1}', 377 | ]) 378 | 379 | def testBitwiseOperators(self): 380 | 381 | self.assertTrueRules([ 382 | 'rule test { condition: 0x55 | 0xAA == 0xFF }', 383 | 'rule test { condition: ~0xAA ^ 0x5A & 0xFF == (~0xAA) ^ (0x5A & 0xFF) }', 384 | 'rule test { condition: ~0x55 & 0xFF == 0xAA }', 385 | 'rule test { condition: 8 >> 2 == 2 }', 386 | 'rule test { condition: 1 << 3 == 8 }', 387 | 'rule test { condition: 1 | 3 ^ 3 == 1 | (3 ^ 3) }' 388 | ]) 389 | 390 | self.assertFalseRules([ 391 | 'rule test { condition: ~0xAA ^ 0x5A & 0xFF == 0x0F }', 392 | 'rule test { condition: 1 | 3 ^ 3 == (1 | 3) ^ 3}' 393 | ]) 394 | 395 | def testSyntax(self): 396 | 397 | self.assertSyntaxError([ 398 | 'rule test { strings: $a = "a" $a = "a" condition: all of them }' 399 | ]) 400 | 401 | def testAnonymousStrings(self): 402 | 403 | self.assertTrueRules([ 404 | 'rule test { strings: $ = "a" $ = "b" condition: all of them }', 405 | ], "ab") 406 | 407 | def testStrings(self): 408 | 409 | self.assertTrueRules([ 410 | 'rule test { strings: $a = "a" condition: $a }', 411 | 'rule test { strings: $a = "ab" condition: $a }', 412 | 'rule test { strings: $a = "abc" condition: $a }', 413 | 'rule test { strings: $a = "xyz" condition: $a }', 414 | 'rule test { strings: $a = "abc" nocase fullword condition: $a }', 415 | 'rule test { strings: $a = "aBc" nocase condition: $a }', 416 | 'rule test { strings: $a = "abc" fullword condition: $a }', 417 | ], "---- abc ---- xyz") 418 | 419 | self.assertFalseRules([ 420 | 'rule test { strings: $a = "a" fullword condition: $a }', 421 | 'rule test { strings: $a = "ab" fullword condition: $a }', 422 | 'rule test { strings: $a = "abc" wide fullword condition: $a }', 423 | ], "---- abc ---- xyz") 424 | 425 | self.assertTrueRules([ 426 | 'rule test { strings: $a = "a" wide condition: $a }', 427 | 'rule test { strings: $a = "a" wide ascii condition: $a }', 428 | 'rule test { strings: $a = "ab" wide condition: $a }', 429 | 'rule test { strings: $a = "ab" wide ascii condition: $a }', 430 | 'rule test { strings: $a = "abc" wide condition: $a }', 431 | 'rule test { strings: $a = "abc" wide nocase fullword condition: $a }', 432 | 'rule test { strings: $a = "aBc" wide nocase condition: $a }', 433 | 'rule test { strings: $a = "aBc" wide ascii nocase condition: $a }', 434 | 'rule test { strings: $a = "---xyz" wide nocase condition: $a }' 435 | ], "---- a\x00b\x00c\x00 -\x00-\x00-\x00-\x00x\x00y\x00z\x00") 436 | 437 | self.assertTrueRules([ 438 | 'rule test { strings: $a = "abc" fullword condition: $a }', 439 | ], "abc") 440 | 441 | self.assertFalseRules([ 442 | 'rule test { strings: $a = "abc" fullword condition: $a }', 443 | ], "xabcx") 444 | 445 | self.assertFalseRules([ 446 | 'rule test { strings: $a = "abc" fullword condition: $a }', 447 | ], "xabc") 448 | 449 | self.assertFalseRules([ 450 | 'rule test { strings: $a = "abc" fullword condition: $a }', 451 | ], "abcx") 452 | 453 | self.assertFalseRules([ 454 | 'rule test { strings: $a = "abc" ascii wide fullword condition: $a }', 455 | ], "abcx") 456 | 457 | self.assertTrueRules([ 458 | 'rule test { strings: $a = "abc" ascii wide fullword condition: $a }', 459 | ], "a\x00abc") 460 | 461 | self.assertTrueRules([ 462 | 'rule test { strings: $a = "abc" wide fullword condition: $a }', 463 | ], "a\x00b\x00c\x00") 464 | 465 | self.assertFalseRules([ 466 | 'rule test { strings: $a = "abc" wide fullword condition: $a }', 467 | ], "x\x00a\x00b\x00c\x00x\x00") 468 | 469 | self.assertFalseRules([ 470 | 'rule test { strings: $a = "ab" wide fullword condition: $a }', 471 | ], "x\x00a\x00b\x00") 472 | 473 | self.assertFalseRules([ 474 | 'rule test { strings: $a = "abc" wide fullword condition: $a }', 475 | ], "x\x00a\x00b\x00c\x00") 476 | 477 | self.assertTrueRules([ 478 | 'rule test { strings: $a = "abc" wide fullword condition: $a }', 479 | ], "x\x01a\x00b\x00c\x00") 480 | 481 | self.assertTrueRules([ 482 | 'rule test {\ 483 | strings:\ 484 | $a = "abcdef"\ 485 | $b = "cdef"\ 486 | $c = "ef"\ 487 | condition:\ 488 | all of them\ 489 | }' 490 | ], 'abcdef') 491 | 492 | def testWildcardStrings(self): 493 | 494 | self.assertTrueRules([ 495 | 'rule test {\ 496 | strings:\ 497 | $s1 = "abc"\ 498 | $s2 = "xyz"\ 499 | condition:\ 500 | for all of ($*) : ($)\ 501 | }' 502 | ], "---- abc ---- A\x00B\x00C\x00 ---- xyz") 503 | 504 | def testHexStrings(self): 505 | 506 | self.assertTrueRules([ 507 | 'rule test { strings: $a = { 64 01 00 00 60 01 } condition: $a }', 508 | 'rule test { strings: $a = { 64 0? 00 00 ?0 01 } condition: $a }', 509 | 'rule test { strings: $a = { 6? 01 00 00 60 0? } condition: $a }', 510 | 'rule test { strings: $a = { 64 01 [1-3] 60 01 } condition: $a }', 511 | 'rule test { strings: $a = { 64 01 [1-3] (60|61) 01 } condition: $a }', 512 | 'rule test { strings: $a = { 4D 5A [-] 6A 2A [-] 58 C3} condition: $a }', 513 | 'rule test { strings: $a = { 4D 5A [300-] 6A 2A [-] 58 C3} condition: $a }', 514 | 'rule test { strings: $a = { 2e 7? (65 | ??) 78 } condition: $a }' 515 | ], PE32_FILE) 516 | 517 | self.assertFalseRules([ 518 | 'rule test { strings: $a = { 4D 5A [0-300] 6A 2A } condition: $a }', 519 | 'rule test { strings: $a = { 4D 5A [0-128] 45 [0-128] 01 [0-128] C3 } condition: $a }', 520 | ], PE32_FILE) 521 | 522 | self.assertTrueRules([ 523 | 'rule test { strings: $a = { 31 32 [-] 38 39 } condition: $a }', 524 | 'rule test { strings: $a = { 31 32 [-] 33 34 [-] 38 39 } condition: $a }', 525 | 'rule test { strings: $a = { 31 32 [1] 34 35 [2] 38 39 } condition: $a }', 526 | 'rule test { strings: $a = { 31 32 [1-] 34 35 [1-] 38 39 } condition: $a }', 527 | 'rule test { strings: $a = { 31 32 [0-3] 34 35 [1-] 38 39 } condition: $a }', 528 | 'rule test { strings: $a = { 31 32 [0-2] 35 [1-] 37 38 39 } condition: $a }', 529 | ], '123456789') 530 | 531 | self.assertTrueRules([ 532 | 'rule test { strings: $a = { 31 32 [-] 38 39 } condition: all of them }', 533 | ], '123456789') 534 | 535 | self.assertFalseRules([ 536 | 'rule test { strings: $a = { 31 32 [-] 32 33 } condition: $a }', 537 | 'rule test { strings: $a = { 35 36 [-] 31 32 } condition: $a }', 538 | 'rule test { strings: $a = { 31 32 [2-] 34 35 } condition: $a }', 539 | 'rule test { strings: $a = { 31 32 [0-3] 37 38 } condition: $a }', 540 | ], '123456789') 541 | 542 | self.assertSyntaxError([ 543 | 'rule test { strings: $a = { 01 [0] 02 } condition: $a }', 544 | 'rule test { strings: $a = { [-] 01 02 } condition: $a }', 545 | 'rule test { strings: $a = { 01 02 [-] } condition: $a }', 546 | 'rule test { strings: $a = { 01 02 ([-] 03 | 04) } condition: $a }', 547 | 'rule test { strings: $a = { 01 02 (03 [-] | 04) } condition: $a }', 548 | 'rule test { strings: $a = { 01 02 (03 | 04 [-]) } condition: $a }' 549 | ]) 550 | 551 | rules = yara.compile(source='rule test { strings: $a = { 61 [0-3] (62|63) } condition: $a }') 552 | matches = rules.match(data='abbb') 553 | 554 | if sys.version_info[0] >= 3: 555 | self.assertTrue(matches[0].strings == [(0, '$a', bytes('ab', 'utf-8'))]) 556 | else: 557 | self.assertTrue(matches[0].strings == [(0, '$a', 'ab')]) 558 | 559 | def testCount(self): 560 | 561 | self.assertTrueRules([ 562 | 'rule test { strings: $a = "ssi" condition: #a == 2 }', 563 | ], 'mississippi') 564 | 565 | def testAt(self): 566 | 567 | self.assertTrueRules([ 568 | 'rule test { strings: $a = "ssi" condition: $a at 2 and $a at 5 }', 569 | 'rule test { strings: $a = "mis" condition: $a at ~0xFF & 0xFF }' 570 | ], 'mississippi') 571 | 572 | self.assertTrueRules([ 573 | 'rule test { strings: $a = { 00 00 00 00 ?? 74 65 78 74 } condition: $a at 308}', 574 | ], PE32_FILE) 575 | 576 | def testIn(self): 577 | 578 | self.assertTrueRules([ 579 | 'import "pe" rule test { strings: $a = { 6a 2a 58 c3 } condition: $a in (pe.entry_point .. pe.entry_point + 1) }', 580 | ], PE32_FILE) 581 | 582 | def testOffset(self): 583 | 584 | self.assertTrueRules([ 585 | 'rule test { strings: $a = "ssi" condition: @a == 2 }', 586 | 'rule test { strings: $a = "ssi" condition: @a == @a[1] }', 587 | 'rule test { strings: $a = "ssi" condition: @a[2] == 5 }' 588 | ], 'mississippi') 589 | 590 | def testLength(self): 591 | 592 | self.assertTrueRules([ 593 | 'rule test { strings: $a = /m.*?ssi/ condition: !a == 5 }', 594 | 'rule test { strings: $a = /m.*?ssi/ condition: !a[1] == 5 }', 595 | 'rule test { strings: $a = /m.*ssi/ condition: !a == 8 }', 596 | 'rule test { strings: $a = /m.*ssi/ condition: !a[1] == 8 }', 597 | 'rule test { strings: $a = /ssi.*ppi/ condition: !a[1] == 9 }', 598 | 'rule test { strings: $a = /ssi.*ppi/ condition: !a[2] == 6 }', 599 | 'rule test { strings: $a = { 6D [1-3] 73 73 69 } condition: !a == 5}', 600 | 'rule test { strings: $a = { 6D [-] 73 73 69 } condition: !a == 5}', 601 | 'rule test { strings: $a = { 6D [-] 70 70 69 } condition: !a == 11}', 602 | 'rule test { strings: $a = { 6D 69 73 73 [-] 70 69 } condition: !a == 11}', 603 | ], 'mississippi') 604 | 605 | def testOf(self): 606 | 607 | self.assertTrueRules([ 608 | 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: any of them }', 609 | 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 1 of them }', 610 | 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 2 of them }', 611 | 'rule test { strings: $a1 = "dummy1" $b1 = "dummy1" $b2 = "ssi" condition: any of ($a*, $b*) }', 612 | ], 'mississipi') 613 | 614 | self.assertTrueRules([""" 615 | rule test 616 | { 617 | strings: 618 | $ = /abc/ 619 | $ = /def/ 620 | $ = /ghi/ 621 | condition: 622 | for any of ($*) : ( for any i in (1..#): (uint8(@[i] - 1) == 0x00) ) 623 | }""" 624 | ], 'abc\x00def\x00ghi') 625 | 626 | self.assertFalseRules([ 627 | 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: all of them }' 628 | ], 'mississipi') 629 | 630 | self.assertSyntaxError([ 631 | 'rule test { condition: all of ($a*) }', 632 | 'rule test { condition: all of them }' 633 | ]) 634 | 635 | def testFor(self): 636 | 637 | self.assertTrueRules([ 638 | 'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] >= 2 and @a[i] <= 5) }', 639 | 'rule test { strings: $a = "ssi" $b = "mi" condition: for all i in (1..#a) : ( for all j in (1..#b) : (@a[i] >= @b[j])) }' 640 | ], 'mississipi') 641 | 642 | self.assertFalseRules([ 643 | 'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] == 5) }', 644 | ], 'mississipi') 645 | 646 | def testRE(self): 647 | 648 | self.assertTrueRules([ 649 | 'rule test { strings: $a = /ssi/ condition: $a }', 650 | 'rule test { strings: $a = /ssi(s|p)/ condition: $a }', 651 | 'rule test { strings: $a = /ssim*/ condition: $a }', 652 | 'rule test { strings: $a = /ssa?/ condition: $a }', 653 | 'rule test { strings: $a = /Miss/ nocase condition: $a }', 654 | 'rule test { strings: $a = /(M|N)iss/ nocase condition: $a }', 655 | 'rule test { strings: $a = /[M-N]iss/ nocase condition: $a }', 656 | 'rule test { strings: $a = /(Mi|ssi)ssippi/ nocase condition: $a }', 657 | 'rule test { strings: $a = /ppi\tmi/ condition: $a }', 658 | 'rule test { strings: $a = /ppi\.mi/ condition: $a }', 659 | 'rule test { strings: $a = /^mississippi/ fullword condition: $a }', 660 | 'rule test { strings: $a = /mississippi.*mississippi$/s condition: $a }', 661 | ], 'mississippi\tmississippi.mississippi\nmississippi') 662 | 663 | self.assertFalseRules([ 664 | 'rule test { strings: $a = /^ssi/ condition: $a }', 665 | 'rule test { strings: $a = /ssi$/ condition: $a }', 666 | 'rule test { strings: $a = /ssissi/ fullword condition: $a }', 667 | 'rule test { strings: $a = /^[isp]+/ condition: $a }' 668 | ], 'mississippi') 669 | 670 | for test in RE_TESTS: 671 | try: 672 | self.runReTest(test) 673 | except Exception as e: 674 | print('\nFailed test: %s\n' % str(test)) 675 | raise e 676 | 677 | def testEntrypoint(self): 678 | 679 | self.assertTrueRules([ 680 | 'rule test { strings: $a = { 6a 2a 58 c3 } condition: $a at entrypoint }', 681 | ], PE32_FILE) 682 | 683 | self.assertTrueRules([ 684 | 'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }', 685 | ], ELF32_FILE) 686 | 687 | self.assertTrueRules([ 688 | 'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }', 689 | ], ELF64_FILE) 690 | 691 | self.assertFalseRules([ 692 | 'rule test { condition: entrypoint >= 0 }', 693 | ]) 694 | 695 | def testFilesize(self): 696 | 697 | self.assertTrueRules([ 698 | 'rule test { condition: filesize == %d }' % len(PE32_FILE), 699 | ], PE32_FILE) 700 | 701 | def testCompileFile(self): 702 | 703 | f = tempfile.TemporaryFile('wt') 704 | 705 | f.write('rule test { condition: true }') 706 | f.flush() 707 | f.seek(0) 708 | 709 | r = yara.compile(file=f) 710 | f.close() 711 | self.assertTrue(r.match(data=PE32_FILE)) 712 | 713 | def testCompileFiles(self): 714 | 715 | tmpdir = tempfile.gettempdir() 716 | 717 | p1 = os.path.join(tmpdir, 'test1') 718 | f1 = open(p1, 'wt') 719 | f1.write('rule test1 { condition: true }') 720 | f1.close() 721 | 722 | p2 = os.path.join(tmpdir, 'test2') 723 | t2 = open(p2, 'wt') 724 | t2.write('rule test2 { condition: true }') 725 | t2.close() 726 | 727 | r = yara.compile(filepaths={ 728 | 'test1': p1, 729 | 'test2': p2 730 | }) 731 | 732 | self.assertTrue(len(r.match(data='dummy')) == 2) 733 | 734 | for m in r.match(data='dummy'): 735 | self.assertTrue(m.rule in ('test1', 'test2')) 736 | self.assertTrue(m.namespace == m.rule) 737 | 738 | os.remove(p1) 739 | os.remove(p2) 740 | 741 | def testIncludeFiles(self): 742 | 743 | tmpdir = tempfile.gettempdir() 744 | 745 | p1 = os.path.join(tmpdir, 'test1') 746 | f1 = open(p1, 'wt') 747 | f1.write('rule test1 { condition: true }') 748 | f1.close() 749 | 750 | p2 = os.path.join(tmpdir, 'test2') 751 | f2 = open(p2, 'wt') 752 | f2.write('include "%s" rule test2 { condition: test1 }' % p1) 753 | f2.close() 754 | 755 | r = yara.compile(p2) 756 | self.assertTrue(len(r.match(data='dummy')) == 2) 757 | 758 | def testExternals(self): 759 | 760 | r = yara.compile(source='rule test { condition: ext_int == 15 }', externals={'ext_int': 15}) 761 | self.assertTrue(r.match(data='dummy')) 762 | 763 | r = yara.compile(source='rule test { condition: ext_int == -15}', externals={'ext_int': -15}) 764 | self.assertTrue(r.match(data='dummy')) 765 | 766 | r = yara.compile(source='rule test { condition: ext_float == 3.14 }', externals={'ext_float': 3.14}) 767 | self.assertTrue(r.match(data='dummy')) 768 | 769 | r = yara.compile(source='rule test { condition: ext_float == -0.5 }', externals={'ext_float': -0.5}) 770 | self.assertTrue(r.match(data='dummy')) 771 | 772 | r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': True}) 773 | self.assertTrue(r.match(data='dummy')) 774 | 775 | r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': ''}) 776 | self.assertFalse(r.match(data='dummy')) 777 | 778 | r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': 'foo'}) 779 | self.assertTrue(r.match(data='dummy')) 780 | 781 | r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': False}) 782 | self.assertFalse(r.match(data='dummy')) 783 | 784 | r = yara.compile(source='rule test { condition: ext_str contains "ssi" }', externals={'ext_str': 'mississippi'}) 785 | self.assertTrue(r.match(data='dummy')) 786 | 787 | r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': ''}) 788 | self.assertFalse(r.match(data='dummy')) 789 | 790 | r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': 'FOO'}) 791 | self.assertFalse(r.match(data='dummy')) 792 | 793 | r = yara.compile(source='rule test { condition: ext_str matches /foo/i }', externals={'ext_str': 'FOO'}) 794 | self.assertTrue(r.match(data='dummy')) 795 | 796 | r = yara.compile(source='rule test { condition: ext_str matches /ssi(s|p)/ }', externals={'ext_str': 'mississippi'}) 797 | self.assertTrue(r.match(data='dummy')) 798 | 799 | r = yara.compile(source='rule test { condition: ext_str matches /ppi$/ }', externals={'ext_str': 'mississippi'}) 800 | self.assertTrue(r.match(data='dummy')) 801 | 802 | r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'}) 803 | self.assertFalse(r.match(data='dummy')) 804 | 805 | r = yara.compile(source='rule test { condition: ext_str matches /^miss/ }', externals={'ext_str': 'mississippi'}) 806 | self.assertTrue(r.match(data='dummy')) 807 | 808 | r = yara.compile(source='rule test { condition: ext_str matches /^iss/ }', externals={'ext_str': 'mississippi'}) 809 | self.assertFalse(r.match(data='dummy')) 810 | 811 | r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'}) 812 | self.assertFalse(r.match(data='dummy')) 813 | 814 | def testCallback(self): 815 | 816 | global rule_data 817 | rule_data = None 818 | 819 | def callback(data): 820 | global rule_data 821 | rule_data = data 822 | return yara.CALLBACK_CONTINUE 823 | 824 | r = yara.compile(source='rule test { strings: $a = { 50 45 00 00 4c 01 } condition: $a }') 825 | r.match(data=PE32_FILE, callback=callback) 826 | 827 | self.assertTrue(rule_data['matches']) 828 | self.assertTrue(rule_data['rule'] == 'test') 829 | 830 | def testCompare(self): 831 | 832 | r = yara.compile(sources={ 833 | 'test1': 'rule test { condition: true}', 834 | 'test2': 'rule test { condition: true}' 835 | }) 836 | 837 | m = r.match(data="dummy") 838 | 839 | self.assertTrue(len(m) == 2) 840 | self.assertTrue(m[0] < m[1]) 841 | self.assertTrue(m[0] != m[1]) 842 | self.assertFalse(m[0] > m[1]) 843 | self.assertFalse(m[0] == m[1]) 844 | 845 | def testComments(self): 846 | 847 | self.assertTrueRules([ 848 | """ 849 | rule test { 850 | condition: 851 | // this is a comment 852 | /*** this is a comment ***/ 853 | /* /* /* 854 | this is a comment 855 | */ 856 | true 857 | } 858 | """, 859 | ]) 860 | 861 | def testModules(self): 862 | 863 | self.assertTrueRules([ 864 | 'import "tests" rule test { condition: tests.constants.one + 1 == tests.constants.two }', 865 | 'import "tests" rule test { condition: tests.constants.foo == "foo" }', 866 | 'import "tests" rule test { condition: tests.constants.empty == "" }', 867 | 'import "tests" rule test { condition: tests.empty() == "" }', 868 | 'import "tests" rule test { condition: tests.struct_array[1].i == 1 }', 869 | 'import "tests" rule test { condition: tests.struct_array[0].i == 1 or true}', 870 | 'import "tests" rule test { condition: tests.integer_array[0] == 0}', 871 | 'import "tests" rule test { condition: tests.integer_array[1] == 1}', 872 | 'import "tests" rule test { condition: tests.string_array[0] == "foo"}', 873 | 'import "tests" rule test { condition: tests.string_array[2] == "baz"}', 874 | 'import "tests" rule test { condition: tests.string_dict["foo"] == "foo"}', 875 | 'import "tests" rule test { condition: tests.string_dict["bar"] == "bar"}', 876 | 'import "tests" rule test { condition: tests.isum(1,2) == 3}', 877 | 'import "tests" rule test { condition: tests.isum(1,2,3) == 6}', 878 | 'import "tests" rule test { condition: tests.fsum(1.0,2.0) == 3.0}', 879 | 'import "tests" rule test { condition: tests.fsum(1.0,2.0,3.0) == 6.0}', 880 | 'import "tests" rule test { condition: tests.length("dummy") == 5}', 881 | ]) 882 | 883 | self.assertFalseRules([ 884 | 'import "tests" rule test { condition: tests.struct_array[0].i == 1 }', 885 | 'import "tests" rule test { condition: tests.isum(1,1) == 3}', 886 | 'import "tests" rule test { condition: tests.fsum(1.0,1.0) == 3.0}', 887 | ]) 888 | 889 | def testIntegerFunctions(self): 890 | 891 | self.assertTrueRules([ 892 | 'rule test { condition: uint8(0) == 0xAA}', 893 | 'rule test { condition: uint16(0) == 0xBBAA}', 894 | 'rule test { condition: uint32(0) == 0xDDCCBBAA}', 895 | 'rule test { condition: uint8be(0) == 0xAA}', 896 | 'rule test { condition: uint16be(0) == 0xAABB}', 897 | 'rule test { condition: uint32be(0) == 0xAABBCCDD}', 898 | ], b'\xAA\xBB\xCC\xDD') 899 | 900 | def testStringIO(self): 901 | 902 | # Python 2/3 903 | try: 904 | stream = StringIO.StringIO() 905 | except: 906 | stream = io.BytesIO() 907 | 908 | r1 = yara.compile(source='rule test { condition: true }') 909 | r1.save(file=stream) 910 | 911 | stream.seek(0) 912 | 913 | r2 = yara.load(file=stream) 914 | m = r2.match(data="dummy") 915 | 916 | self.assertTrue(len(m) == 1) 917 | 918 | def testModuleData(self): 919 | 920 | data = {} 921 | 922 | def callback(module_data): 923 | data['constants'] = module_data.get('constants') 924 | 925 | r1 = yara.compile( 926 | source='import "tests" rule test { condition: false }') 927 | 928 | r1.match(data='', modules_callback=callback) 929 | 930 | self.assertTrue(data['constants']['foo'] == 'foo') 931 | self.assertTrue(data['constants']['empty'] == '') 932 | self.assertTrue(data['constants']['one'] == 1) 933 | self.assertTrue(data['constants']['two'] == 2) 934 | 935 | 936 | 937 | if __name__ == "__main__": 938 | unittest.main() 939 | -------------------------------------------------------------------------------- /yara-python.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* headers */ 17 | 18 | #include 19 | #include "structmember.h" 20 | 21 | #if PY_VERSION_HEX >= 0x02060000 22 | #include "bytesobject.h" 23 | #elif PY_VERSION_HEX < 0x02060000 24 | #define PyBytes_AsString PyString_AsString 25 | #define PyBytes_Check PyString_Check 26 | #define PyBytes_FromStringAndSize PyString_FromStringAndSize 27 | #endif 28 | 29 | #include 30 | #include 31 | 32 | #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) 33 | typedef int Py_ssize_t; 34 | #define PY_SSIZE_T_MAX INT_MAX 35 | #define PY_SSIZE_T_MIN INT_MIN 36 | #endif 37 | 38 | #ifndef PyVarObject_HEAD_INIT 39 | #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, 40 | #endif 41 | 42 | #if PY_VERSION_HEX < 0x03020000 43 | typedef long Py_hash_t; 44 | #endif 45 | 46 | #if PY_MAJOR_VERSION >= 3 47 | #define PY_STRING(x) PyUnicode_FromString(x) 48 | #define PY_STRING_TO_C(x) PyBytes_AsString(\ 49 | PyUnicode_AsEncodedString(x, "utf-8", "strict")) 50 | #define PY_STRING_CHECK(x) PyUnicode_Check(x) 51 | #else 52 | #define PY_STRING(x) PyString_FromString(x) 53 | #define PY_STRING_TO_C(x) PyString_AsString(x) 54 | #define PY_STRING_CHECK(x) (PyString_Check(x) || PyUnicode_Check(x)) 55 | #endif 56 | 57 | /* Module globals */ 58 | 59 | static PyObject* YaraError = NULL; 60 | static PyObject* YaraSyntaxError = NULL; 61 | static PyObject* YaraTimeoutError = NULL; 62 | static PyObject* YaraWarningError = NULL; 63 | 64 | 65 | #define YARA_DOC "\ 66 | This module allows you to apply YARA rules to files or strings.\n\ 67 | \n\ 68 | For complete documentation please visit:\n\ 69 | https://plusvic.github.io/yara\n" 70 | 71 | 72 | // Match object 73 | 74 | typedef struct 75 | { 76 | PyObject_HEAD 77 | PyObject* rule; 78 | PyObject* ns; 79 | PyObject* tags; 80 | PyObject* meta; 81 | PyObject* strings; 82 | 83 | } Match; 84 | 85 | static PyMemberDef Match_members[] = { 86 | { 87 | "rule", 88 | T_OBJECT_EX, 89 | offsetof(Match, rule), 90 | READONLY, 91 | "Name of the matching rule" 92 | }, 93 | { 94 | "namespace", 95 | T_OBJECT_EX, 96 | offsetof(Match, ns), 97 | READONLY, 98 | "Namespace of the matching rule" 99 | }, 100 | { 101 | "tags", 102 | T_OBJECT_EX, 103 | offsetof(Match, tags), 104 | READONLY, 105 | "List of tags associated to the rule" 106 | }, 107 | { 108 | "meta", 109 | T_OBJECT_EX, 110 | offsetof(Match, meta), 111 | READONLY, 112 | "Dictionary with metadata associated to the rule" 113 | }, 114 | { 115 | "strings", 116 | T_OBJECT_EX, 117 | offsetof(Match, strings), 118 | READONLY, 119 | "Dictionary with offsets and strings that matched the file" 120 | }, 121 | { NULL } // End marker 122 | }; 123 | 124 | static PyObject* Match_NEW( 125 | const char* rule, 126 | const char* ns, 127 | PyObject* tags, 128 | PyObject* meta, 129 | PyObject* strings); 130 | 131 | static void Match_dealloc( 132 | PyObject* self); 133 | 134 | static PyObject* Match_repr( 135 | PyObject* self); 136 | 137 | static PyObject* Match_getattro( 138 | PyObject* self, 139 | PyObject* name); 140 | 141 | static PyObject* Match_richcompare( 142 | PyObject* self, 143 | PyObject* other, 144 | int op); 145 | 146 | static Py_hash_t Match_hash( 147 | PyObject* self); 148 | 149 | 150 | static PyMethodDef Match_methods[] = 151 | { 152 | { NULL }, 153 | }; 154 | 155 | static PyTypeObject Match_Type = { 156 | PyVarObject_HEAD_INIT(NULL, 0) 157 | "yara.Match", /*tp_name*/ 158 | sizeof(Match), /*tp_basicsize*/ 159 | 0, /*tp_itemsize*/ 160 | (destructor)Match_dealloc, /*tp_dealloc*/ 161 | 0, /*tp_print*/ 162 | 0, /*tp_getattr*/ 163 | 0, /*tp_setattr*/ 164 | 0, /*tp_compare*/ 165 | Match_repr, /*tp_repr*/ 166 | 0, /*tp_as_number*/ 167 | 0, /*tp_as_sequence*/ 168 | 0, /*tp_as_mapping*/ 169 | Match_hash, /*tp_hash */ 170 | 0, /*tp_call*/ 171 | 0, /*tp_str*/ 172 | Match_getattro, /*tp_getattro*/ 173 | 0, /*tp_setattro*/ 174 | 0, /*tp_as_buffer*/ 175 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 176 | "Match class", /* tp_doc */ 177 | 0, /* tp_traverse */ 178 | 0, /* tp_clear */ 179 | Match_richcompare, /* tp_richcompare */ 180 | 0, /* tp_weaklistoffset */ 181 | 0, /* tp_iter */ 182 | 0, /* tp_iternext */ 183 | Match_methods, /* tp_methods */ 184 | Match_members, /* tp_members */ 185 | 0, /* tp_getset */ 186 | 0, /* tp_base */ 187 | 0, /* tp_dict */ 188 | 0, /* tp_descr_get */ 189 | 0, /* tp_descr_set */ 190 | 0, /* tp_dictoffset */ 191 | 0, /* tp_init */ 192 | 0, /* tp_alloc */ 193 | 0, /* tp_new */ 194 | }; 195 | 196 | // Rule object 197 | 198 | typedef struct 199 | { 200 | PyObject_HEAD 201 | PyObject* identifier; 202 | PyObject* tags; 203 | PyObject* meta; 204 | } Rule; 205 | 206 | static void Rule_dealloc( 207 | PyObject* self); 208 | 209 | static PyObject* Rule_getattro( 210 | PyObject* self, 211 | PyObject* name); 212 | 213 | static PyMemberDef Rule_members[] = { 214 | { 215 | "identifier", 216 | T_OBJECT_EX, 217 | offsetof(Rule, identifier), 218 | READONLY, 219 | "Name of the rule" 220 | }, 221 | { 222 | "tags", 223 | T_OBJECT_EX, 224 | offsetof(Rule, tags), 225 | READONLY, 226 | "Tags for the rule" 227 | }, 228 | { 229 | "meta", 230 | T_OBJECT_EX, 231 | offsetof(Rule, meta), 232 | READONLY, 233 | "Meta for the rule" 234 | }, 235 | { NULL } // End marker 236 | }; 237 | 238 | static PyMethodDef Rule_methods[] = 239 | { 240 | { NULL, NULL } 241 | }; 242 | 243 | static PyTypeObject Rule_Type = { 244 | PyVarObject_HEAD_INIT(NULL, 0) 245 | "yara.Rule", /*tp_name*/ 246 | sizeof(Rule), /*tp_basicsize*/ 247 | 0, /*tp_itemsize*/ 248 | (destructor) Rule_dealloc, /*tp_dealloc*/ 249 | 0, /*tp_print*/ 250 | 0, /*tp_getattr*/ 251 | 0, /*tp_setattr*/ 252 | 0, /*tp_compare*/ 253 | 0, /*tp_repr*/ 254 | 0, /*tp_as_number*/ 255 | 0, /*tp_as_sequence*/ 256 | 0, /*tp_as_mapping*/ 257 | 0, /*tp_hash */ 258 | 0, /*tp_call*/ 259 | 0, /*tp_str*/ 260 | Rule_getattro, /*tp_getattro*/ 261 | 0, /*tp_setattro*/ 262 | 0, /*tp_as_buffer*/ 263 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 264 | "Rule class", /* tp_doc */ 265 | 0, /* tp_traverse */ 266 | 0, /* tp_clear */ 267 | 0, /* tp_richcompare */ 268 | 0, /* tp_weaklistoffset */ 269 | 0, /* tp_iter */ 270 | 0, /* tp_iternext */ 271 | Rule_methods, /* tp_methods */ 272 | Rule_members, /* tp_members */ 273 | 0, /* tp_getset */ 274 | 0, /* tp_base */ 275 | 0, /* tp_dict */ 276 | 0, /* tp_descr_get */ 277 | 0, /* tp_descr_set */ 278 | 0, /* tp_dictoffset */ 279 | 0, /* tp_init */ 280 | 0, /* tp_alloc */ 281 | 0, /* tp_new */ 282 | }; 283 | 284 | 285 | // Rules object 286 | 287 | typedef struct 288 | { 289 | PyObject_HEAD 290 | PyObject* externals; 291 | YR_RULES* rules; 292 | YR_RULE* iter_current_rule; 293 | } Rules; 294 | 295 | 296 | static Rules* Rules_NEW(void); 297 | 298 | static void Rules_dealloc( 299 | PyObject* self); 300 | 301 | static PyObject* Rules_match( 302 | PyObject* self, 303 | PyObject* args, 304 | PyObject* keywords); 305 | 306 | static PyObject* Rules_save( 307 | PyObject* self, 308 | PyObject* args, 309 | PyObject* keywords); 310 | 311 | static PyObject* Rules_profiling_info( 312 | PyObject* self, 313 | PyObject* args); 314 | 315 | static PyObject* Rules_getattro( 316 | PyObject* self, 317 | PyObject* name); 318 | 319 | static PyObject* Rules_next( 320 | PyObject* self); 321 | 322 | static PyMethodDef Rules_methods[] = 323 | { 324 | { 325 | "match", 326 | (PyCFunction) Rules_match, 327 | METH_VARARGS | METH_KEYWORDS 328 | }, 329 | { 330 | "save", 331 | (PyCFunction) Rules_save, 332 | METH_VARARGS | METH_KEYWORDS 333 | }, 334 | { 335 | "profiling_info", 336 | (PyCFunction) Rules_profiling_info, 337 | METH_NOARGS 338 | }, 339 | { 340 | NULL, 341 | NULL 342 | } 343 | }; 344 | 345 | static PyTypeObject Rules_Type = { 346 | PyVarObject_HEAD_INIT(NULL, 0) 347 | "yara.Rules", /*tp_name*/ 348 | sizeof(Rules), /*tp_basicsize*/ 349 | 0, /*tp_itemsize*/ 350 | (destructor) Rules_dealloc, /*tp_dealloc*/ 351 | 0, /*tp_print*/ 352 | 0, /*tp_getattr*/ 353 | 0, /*tp_setattr*/ 354 | 0, /*tp_compare*/ 355 | 0, /*tp_repr*/ 356 | 0, /*tp_as_number*/ 357 | 0, /*tp_as_sequence*/ 358 | 0, /*tp_as_mapping*/ 359 | 0, /*tp_hash */ 360 | 0, /*tp_call*/ 361 | 0, /*tp_str*/ 362 | Rules_getattro, /*tp_getattro*/ 363 | 0, /*tp_setattro*/ 364 | 0, /*tp_as_buffer*/ 365 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 366 | "Rules class", /* tp_doc */ 367 | 0, /* tp_traverse */ 368 | 0, /* tp_clear */ 369 | 0, /* tp_richcompare */ 370 | 0, /* tp_weaklistoffset */ 371 | PyObject_SelfIter, /* tp_iter */ 372 | (iternextfunc) Rules_next, /* tp_iternext */ 373 | Rules_methods, /* tp_methods */ 374 | 0, /* tp_members */ 375 | 0, /* tp_getset */ 376 | 0, /* tp_base */ 377 | 0, /* tp_dict */ 378 | 0, /* tp_descr_get */ 379 | 0, /* tp_descr_set */ 380 | 0, /* tp_dictoffset */ 381 | 0, /* tp_init */ 382 | 0, /* tp_alloc */ 383 | 0, /* tp_new */ 384 | }; 385 | 386 | typedef struct _CALLBACK_DATA 387 | { 388 | PyObject* matches; 389 | PyObject* callback; 390 | PyObject* modules_data; 391 | PyObject* modules_callback; 392 | 393 | } CALLBACK_DATA; 394 | 395 | 396 | // Forward declarations for handling module data. 397 | PyObject* convert_structure_to_python( 398 | YR_OBJECT_STRUCTURE* structure); 399 | 400 | 401 | PyObject* convert_array_to_python( 402 | YR_OBJECT_ARRAY* array); 403 | 404 | 405 | PyObject* convert_dictionary_to_python( 406 | YR_OBJECT_DICTIONARY* dictionary); 407 | 408 | 409 | PyObject* convert_object_to_python( 410 | YR_OBJECT* object) 411 | { 412 | SIZED_STRING* sized_string; 413 | PyObject* result = NULL; 414 | 415 | if (object == NULL) 416 | return NULL; 417 | 418 | switch(object->type) 419 | { 420 | case OBJECT_TYPE_INTEGER: 421 | if (((YR_OBJECT_INTEGER*) object)->value != UNDEFINED) 422 | result = Py_BuildValue( 423 | "i", ((YR_OBJECT_INTEGER*) object)->value); 424 | break; 425 | 426 | case OBJECT_TYPE_STRING: 427 | sized_string = ((YR_OBJECT_STRING*) object)->value; 428 | if (sized_string != NULL) 429 | result = PyBytes_FromStringAndSize( 430 | sized_string->c_string, sized_string->length); 431 | break; 432 | 433 | case OBJECT_TYPE_STRUCTURE: 434 | result = convert_structure_to_python((YR_OBJECT_STRUCTURE*) object); 435 | break; 436 | 437 | case OBJECT_TYPE_ARRAY: 438 | result = convert_array_to_python((YR_OBJECT_ARRAY*) object); 439 | break; 440 | 441 | case OBJECT_TYPE_FUNCTION: 442 | // Do nothing with functions... 443 | break; 444 | 445 | case OBJECT_TYPE_REGEXP: 446 | // Fairly certain you can't have these. :) 447 | break; 448 | 449 | case OBJECT_TYPE_DICTIONARY: 450 | result = convert_dictionary_to_python((YR_OBJECT_DICTIONARY*) object); 451 | break; 452 | 453 | case OBJECT_TYPE_FLOAT: 454 | if (!isnan(((YR_OBJECT_DOUBLE*) object)->value)) 455 | result = Py_BuildValue("d", ((YR_OBJECT_DOUBLE*) object)->value); 456 | break; 457 | 458 | default: 459 | break; 460 | } 461 | 462 | return result; 463 | } 464 | 465 | 466 | PyObject* convert_structure_to_python( 467 | YR_OBJECT_STRUCTURE* structure) 468 | { 469 | YR_STRUCTURE_MEMBER* member; 470 | 471 | PyObject* py_object; 472 | PyObject* py_dict = PyDict_New(); 473 | 474 | if (py_dict == NULL) 475 | return py_dict; 476 | 477 | member = structure->members; 478 | 479 | while (member != NULL) 480 | { 481 | py_object = convert_object_to_python(member->object); 482 | 483 | if (py_object != NULL) 484 | { 485 | PyDict_SetItemString(py_dict, member->object->identifier, py_object); 486 | Py_DECREF(py_object); 487 | } 488 | 489 | member =member->next; 490 | } 491 | 492 | return py_dict; 493 | } 494 | 495 | 496 | PyObject* convert_array_to_python( 497 | YR_OBJECT_ARRAY* array) 498 | { 499 | int i; 500 | 501 | PyObject* py_object; 502 | PyObject* py_list = PyList_New(0); 503 | 504 | if (py_list == NULL) 505 | return py_list; 506 | 507 | // If there is nothing in the list, return an empty Python list 508 | if (array->items == NULL) 509 | return py_list; 510 | 511 | for (i = 0; i < array->items->count; i++) 512 | { 513 | py_object = convert_object_to_python(array->items->objects[i]); 514 | 515 | if (py_object != NULL) 516 | { 517 | PyList_Append(py_list, py_object); 518 | Py_DECREF(py_object); 519 | } 520 | } 521 | 522 | return py_list; 523 | } 524 | 525 | 526 | PyObject* convert_dictionary_to_python( 527 | YR_OBJECT_DICTIONARY* dictionary) 528 | { 529 | int i; 530 | 531 | PyObject* py_object; 532 | PyObject* py_dict = PyDict_New(); 533 | 534 | if (py_dict == NULL) 535 | return py_dict; 536 | 537 | // If there is nothing in the YARA dictionary, return an empty Python dict 538 | if (dictionary->items == NULL) 539 | return py_dict; 540 | 541 | for (i = 0; i < dictionary->items->used; i++) 542 | { 543 | py_object = convert_object_to_python(dictionary->items->objects[i].obj); 544 | 545 | if (py_object != NULL) 546 | { 547 | PyDict_SetItemString( 548 | py_dict, 549 | dictionary->items->objects[i].key, 550 | py_object); 551 | 552 | Py_DECREF(py_object); 553 | } 554 | } 555 | 556 | return py_dict; 557 | } 558 | 559 | 560 | int yara_callback( 561 | int message, 562 | void* message_data, 563 | void* user_data) 564 | { 565 | YR_STRING* string; 566 | YR_MATCH* m; 567 | YR_META* meta; 568 | YR_RULE* rule; 569 | YR_MODULE_IMPORT* module_import; 570 | 571 | const char* tag; 572 | 573 | PyObject* tag_list = NULL; 574 | PyObject* string_list = NULL; 575 | PyObject* meta_list = NULL; 576 | PyObject* match; 577 | PyObject* callback_dict; 578 | PyObject* object; 579 | PyObject* tuple; 580 | PyObject* matches = ((CALLBACK_DATA*) user_data)->matches; 581 | PyObject* callback = ((CALLBACK_DATA*) user_data)->callback; 582 | PyObject* modules_data = ((CALLBACK_DATA*) user_data)->modules_data; 583 | PyObject* modules_callback = ((CALLBACK_DATA*) user_data)->modules_callback; 584 | PyObject* module_data; 585 | PyObject* callback_result; 586 | PyObject* module_info_dict; 587 | 588 | Py_ssize_t data_size; 589 | PyGILState_STATE gil_state; 590 | 591 | int result = CALLBACK_CONTINUE; 592 | 593 | if (message == CALLBACK_MSG_SCAN_FINISHED) 594 | return CALLBACK_CONTINUE; 595 | 596 | if (message == CALLBACK_MSG_RULE_NOT_MATCHING && callback == NULL) 597 | return CALLBACK_CONTINUE; 598 | 599 | if (message == CALLBACK_MSG_IMPORT_MODULE && modules_data == NULL) 600 | return CALLBACK_CONTINUE; 601 | 602 | if (message == CALLBACK_MSG_MODULE_IMPORTED && modules_callback == NULL) 603 | return CALLBACK_CONTINUE; 604 | 605 | if (message == CALLBACK_MSG_IMPORT_MODULE) 606 | { 607 | gil_state = PyGILState_Ensure(); 608 | module_import = (YR_MODULE_IMPORT*) message_data; 609 | 610 | module_data = PyDict_GetItemString( 611 | modules_data, 612 | module_import->module_name); 613 | 614 | #if PY_MAJOR_VERSION >= 3 615 | if (module_data != NULL && PyBytes_Check(module_data)) 616 | #else 617 | if (module_data != NULL && PyString_Check(module_data)) 618 | #endif 619 | { 620 | #if PY_MAJOR_VERSION >= 3 621 | PyBytes_AsStringAndSize( 622 | module_data, 623 | (char**) &module_import->module_data, 624 | &data_size); 625 | #else 626 | PyString_AsStringAndSize( 627 | module_data, 628 | (char**) &module_import->module_data, 629 | &data_size); 630 | #endif 631 | 632 | module_import->module_data_size = data_size; 633 | } 634 | 635 | PyGILState_Release(gil_state); 636 | return CALLBACK_CONTINUE; 637 | } 638 | 639 | if (message == CALLBACK_MSG_MODULE_IMPORTED) 640 | { 641 | gil_state = PyGILState_Ensure(); 642 | 643 | module_info_dict = convert_structure_to_python( 644 | (YR_OBJECT_STRUCTURE*) message_data); 645 | 646 | if (module_info_dict == NULL) 647 | return CALLBACK_CONTINUE; 648 | 649 | object = PY_STRING(((YR_OBJECT_STRUCTURE*) message_data)->identifier); 650 | PyDict_SetItemString(module_info_dict, "module", object); 651 | Py_DECREF(object); 652 | 653 | Py_INCREF(modules_callback); 654 | 655 | callback_result = PyObject_CallFunctionObjArgs( 656 | modules_callback, 657 | module_info_dict, 658 | NULL); 659 | 660 | if (callback_result != NULL) 661 | { 662 | #if PY_MAJOR_VERSION >= 3 663 | if (PyLong_Check(callback_result)) 664 | #else 665 | if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) 666 | #endif 667 | { 668 | result = (int) PyLong_AsLong(callback_result); 669 | } 670 | 671 | Py_DECREF(callback_result); 672 | } 673 | else 674 | { 675 | result = CALLBACK_ERROR; 676 | } 677 | 678 | Py_DECREF(module_info_dict); 679 | Py_DECREF(modules_callback); 680 | PyGILState_Release(gil_state); 681 | 682 | return result; 683 | } 684 | 685 | rule = (YR_RULE*) message_data; 686 | 687 | gil_state = PyGILState_Ensure(); 688 | 689 | tag_list = PyList_New(0); 690 | string_list = PyList_New(0); 691 | meta_list = PyDict_New(); 692 | 693 | if (tag_list == NULL || string_list == NULL || meta_list == NULL) 694 | { 695 | Py_XDECREF(tag_list); 696 | Py_XDECREF(string_list); 697 | Py_XDECREF(meta_list); 698 | PyGILState_Release(gil_state); 699 | 700 | return CALLBACK_ERROR; 701 | } 702 | 703 | yr_rule_tags_foreach(rule, tag) 704 | { 705 | object = PY_STRING(tag); 706 | PyList_Append(tag_list, object); 707 | Py_DECREF(object); 708 | } 709 | 710 | yr_rule_metas_foreach(rule, meta) 711 | { 712 | if (meta->type == META_TYPE_INTEGER) 713 | object = Py_BuildValue("i", meta->integer); 714 | else if (meta->type == META_TYPE_BOOLEAN) 715 | object = PyBool_FromLong((long) meta->integer); 716 | else 717 | object = PY_STRING(meta->string); 718 | 719 | PyDict_SetItemString(meta_list, meta->identifier, object); 720 | Py_DECREF(object); 721 | } 722 | 723 | yr_rule_strings_foreach(rule, string) 724 | { 725 | yr_string_matches_foreach(string, m) 726 | { 727 | object = PyBytes_FromStringAndSize((char*) m->data, m->data_length); 728 | 729 | tuple = Py_BuildValue( 730 | "(L,s,O)", 731 | m->base + m->offset, 732 | string->identifier, 733 | object); 734 | 735 | PyList_Append(string_list, tuple); 736 | 737 | Py_DECREF(object); 738 | Py_DECREF(tuple); 739 | } 740 | } 741 | 742 | if (message == CALLBACK_MSG_RULE_MATCHING) 743 | { 744 | match = Match_NEW( 745 | rule->identifier, 746 | rule->ns->name, 747 | tag_list, 748 | meta_list, 749 | string_list); 750 | 751 | if (match != NULL) 752 | { 753 | PyList_Append(matches, match); 754 | Py_DECREF(match); 755 | } 756 | else 757 | { 758 | Py_DECREF(tag_list); 759 | Py_DECREF(string_list); 760 | Py_DECREF(meta_list); 761 | PyGILState_Release(gil_state); 762 | 763 | return CALLBACK_ERROR; 764 | } 765 | } 766 | 767 | if (callback != NULL) 768 | { 769 | Py_INCREF(callback); 770 | 771 | callback_dict = PyDict_New(); 772 | 773 | object = PyBool_FromLong(message == CALLBACK_MSG_RULE_MATCHING); 774 | PyDict_SetItemString(callback_dict, "matches", object); 775 | Py_DECREF(object); 776 | 777 | object = PY_STRING(rule->identifier); 778 | PyDict_SetItemString(callback_dict, "rule", object); 779 | Py_DECREF(object); 780 | 781 | object = PY_STRING(rule->ns->name); 782 | PyDict_SetItemString(callback_dict, "namespace", object); 783 | Py_DECREF(object); 784 | 785 | PyDict_SetItemString(callback_dict, "tags", tag_list); 786 | PyDict_SetItemString(callback_dict, "meta", meta_list); 787 | PyDict_SetItemString(callback_dict, "strings", string_list); 788 | 789 | callback_result = PyObject_CallFunctionObjArgs( 790 | callback, 791 | callback_dict, 792 | NULL); 793 | 794 | if (callback_result != NULL) 795 | { 796 | #if PY_MAJOR_VERSION >= 3 797 | if (PyLong_Check(callback_result)) 798 | #else 799 | if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) 800 | #endif 801 | { 802 | result = (int) PyLong_AsLong(callback_result); 803 | } 804 | 805 | Py_DECREF(callback_result); 806 | } 807 | else 808 | { 809 | result = CALLBACK_ERROR; 810 | } 811 | 812 | Py_DECREF(callback_dict); 813 | Py_DECREF(callback); 814 | } 815 | 816 | Py_DECREF(tag_list); 817 | Py_DECREF(string_list); 818 | Py_DECREF(meta_list); 819 | PyGILState_Release(gil_state); 820 | 821 | return result; 822 | } 823 | 824 | 825 | /* YR_STREAM read method for "file-like objects" */ 826 | 827 | static size_t flo_read( 828 | void* ptr, 829 | size_t size, 830 | size_t count, 831 | void* user_data) 832 | { 833 | size_t i; 834 | 835 | for (i = 0; i < count; i++) 836 | { 837 | PyGILState_STATE gil_state = PyGILState_Ensure(); 838 | 839 | PyObject* bytes = PyObject_CallMethod( 840 | (PyObject*) user_data, "read", "n", (Py_ssize_t) size); 841 | 842 | PyGILState_Release(gil_state); 843 | 844 | if (bytes != NULL) 845 | { 846 | Py_ssize_t len; 847 | char* buffer; 848 | 849 | int result = PyBytes_AsStringAndSize(bytes, &buffer, &len); 850 | 851 | if (result == -1 || (size_t) len < size) 852 | { 853 | Py_DECREF(bytes); 854 | return i; 855 | } 856 | 857 | memcpy((char*) ptr + i * size, buffer, size); 858 | 859 | Py_DECREF(bytes); 860 | } 861 | else 862 | { 863 | return i; 864 | } 865 | } 866 | 867 | return count; 868 | } 869 | 870 | 871 | /* YR_STREAM write method for "file-like objects" */ 872 | 873 | static size_t flo_write( 874 | const void* ptr, 875 | size_t size, 876 | size_t count, 877 | void* user_data) 878 | { 879 | size_t i; 880 | 881 | for (i = 0; i < count; i++) 882 | { 883 | PyGILState_STATE gil_state = PyGILState_Ensure(); 884 | 885 | PyObject* result = PyObject_CallMethod( 886 | #if PY_MAJOR_VERSION >= 3 887 | (PyObject*) user_data, "write", "y#", (char*) ptr + i * size, size); 888 | #else 889 | (PyObject*) user_data, "write", "s#", (char*) ptr + i * size, size); 890 | #endif 891 | 892 | PyGILState_Release(gil_state); 893 | 894 | if (result == NULL) 895 | return i; 896 | 897 | Py_DECREF(result); 898 | } 899 | 900 | return count; 901 | } 902 | 903 | 904 | PyObject* handle_error( 905 | int error, 906 | char* extra) 907 | { 908 | switch(error) 909 | { 910 | case ERROR_COULD_NOT_ATTACH_TO_PROCESS: 911 | return PyErr_Format( 912 | YaraError, 913 | "access denied"); 914 | case ERROR_INSUFFICIENT_MEMORY: 915 | return PyErr_NoMemory(); 916 | case ERROR_COULD_NOT_OPEN_FILE: 917 | return PyErr_Format( 918 | YaraError, 919 | "could not open file \"%s\"", 920 | extra); 921 | case ERROR_COULD_NOT_MAP_FILE: 922 | return PyErr_Format( 923 | YaraError, 924 | "could not map file \"%s\" into memory", 925 | extra); 926 | case ERROR_INVALID_FILE: 927 | return PyErr_Format( 928 | YaraError, 929 | "invalid rules file \"%s\"", 930 | extra); 931 | case ERROR_CORRUPT_FILE: 932 | return PyErr_Format( 933 | YaraError, 934 | "corrupt rules file \"%s\"", 935 | extra); 936 | case ERROR_SCAN_TIMEOUT: 937 | return PyErr_Format( 938 | YaraTimeoutError, 939 | "scanning timed out"); 940 | case ERROR_INVALID_EXTERNAL_VARIABLE_TYPE: 941 | return PyErr_Format( 942 | YaraError, 943 | "external variable \"%s\" was already defined with a different type", 944 | extra); 945 | default: 946 | return PyErr_Format( 947 | YaraError, 948 | "internal error: %d", 949 | error); 950 | } 951 | } 952 | 953 | 954 | int process_compile_externals( 955 | PyObject* externals, 956 | YR_COMPILER* compiler) 957 | { 958 | PyObject* key; 959 | PyObject* value; 960 | Py_ssize_t pos = 0; 961 | 962 | char* identifier = NULL; 963 | int result; 964 | 965 | while (PyDict_Next(externals, &pos, &key, &value)) 966 | { 967 | identifier = PY_STRING_TO_C(key); 968 | 969 | if (PyBool_Check(value)) 970 | { 971 | result = yr_compiler_define_boolean_variable( 972 | compiler, 973 | identifier, 974 | PyObject_IsTrue(value)); 975 | } 976 | #if PY_MAJOR_VERSION >= 3 977 | else if (PyLong_Check(value)) 978 | #else 979 | else if (PyLong_Check(value) || PyInt_Check(value)) 980 | #endif 981 | { 982 | result = yr_compiler_define_integer_variable( 983 | compiler, 984 | identifier, 985 | PyLong_AsLong(value)); 986 | } 987 | else if (PyFloat_Check(value)) 988 | { 989 | result = yr_compiler_define_float_variable( 990 | compiler, 991 | identifier, 992 | PyFloat_AsDouble(value)); 993 | } 994 | else if (PY_STRING_CHECK(value)) 995 | { 996 | result = yr_compiler_define_string_variable( 997 | compiler, 998 | identifier, 999 | PY_STRING_TO_C(value)); 1000 | } 1001 | else 1002 | { 1003 | PyErr_Format( 1004 | PyExc_TypeError, 1005 | "external values must be of type integer, float, boolean or string"); 1006 | 1007 | return ERROR_INVALID_ARGUMENT; 1008 | } 1009 | 1010 | if (result != ERROR_SUCCESS) 1011 | { 1012 | handle_error(result, identifier); 1013 | return result; 1014 | } 1015 | } 1016 | 1017 | return ERROR_SUCCESS; 1018 | } 1019 | 1020 | 1021 | int process_match_externals( 1022 | PyObject* externals, 1023 | YR_RULES* rules) 1024 | { 1025 | PyObject* key; 1026 | PyObject* value; 1027 | Py_ssize_t pos = 0; 1028 | 1029 | char* identifier = NULL; 1030 | int result; 1031 | 1032 | while (PyDict_Next(externals, &pos, &key, &value)) 1033 | { 1034 | identifier = PY_STRING_TO_C(key); 1035 | 1036 | if (PyBool_Check(value)) 1037 | { 1038 | result = yr_rules_define_boolean_variable( 1039 | rules, 1040 | identifier, 1041 | PyObject_IsTrue(value)); 1042 | } 1043 | #if PY_MAJOR_VERSION >= 3 1044 | else if (PyLong_Check(value)) 1045 | #else 1046 | else if (PyLong_Check(value) || PyInt_Check(value)) 1047 | #endif 1048 | { 1049 | result = yr_rules_define_integer_variable( 1050 | rules, 1051 | identifier, 1052 | PyLong_AsLong(value)); 1053 | } 1054 | else if (PyFloat_Check(value)) 1055 | { 1056 | result = yr_rules_define_float_variable( 1057 | rules, 1058 | identifier, 1059 | PyFloat_AsDouble(value)); 1060 | } 1061 | else if (PY_STRING_CHECK(value)) 1062 | { 1063 | result = yr_rules_define_string_variable( 1064 | rules, 1065 | identifier, 1066 | PY_STRING_TO_C(value)); 1067 | } 1068 | else 1069 | { 1070 | PyErr_Format( 1071 | PyExc_TypeError, 1072 | "external values must be of type integer, float, boolean or string"); 1073 | 1074 | return ERROR_INVALID_ARGUMENT; 1075 | } 1076 | 1077 | // yr_rules_define_xxx_variable returns ERROR_INVALID_ARGUMENT if the 1078 | // variable wasn't previously defined in the compilation phase. Ignore 1079 | // those errors because we don't want the "scan" method being aborted 1080 | // because of the "externals" dictionary having more keys than those used 1081 | // during compilation. 1082 | 1083 | if (result != ERROR_SUCCESS && 1084 | result != ERROR_INVALID_ARGUMENT) 1085 | { 1086 | handle_error(result, identifier); 1087 | return result; 1088 | } 1089 | } 1090 | 1091 | return ERROR_SUCCESS; 1092 | } 1093 | 1094 | 1095 | static PyObject* Match_NEW( 1096 | const char* rule, 1097 | const char* ns, 1098 | PyObject* tags, 1099 | PyObject* meta, 1100 | PyObject* strings) 1101 | { 1102 | Match* object = PyObject_NEW(Match, &Match_Type); 1103 | 1104 | if (object != NULL) 1105 | { 1106 | object->rule = PY_STRING(rule); 1107 | object->ns = PY_STRING(ns); 1108 | object->tags = tags; 1109 | object->meta = meta; 1110 | object->strings = strings; 1111 | 1112 | Py_INCREF(tags); 1113 | Py_INCREF(meta); 1114 | Py_INCREF(strings); 1115 | } 1116 | 1117 | return (PyObject*) object; 1118 | } 1119 | 1120 | 1121 | static void Match_dealloc( 1122 | PyObject* self) 1123 | { 1124 | Match* object = (Match*) self; 1125 | 1126 | Py_DECREF(object->rule); 1127 | Py_DECREF(object->ns); 1128 | Py_DECREF(object->tags); 1129 | Py_DECREF(object->meta); 1130 | Py_DECREF(object->strings); 1131 | 1132 | PyObject_Del(self); 1133 | } 1134 | 1135 | 1136 | static PyObject* Match_repr( 1137 | PyObject* self) 1138 | { 1139 | Match* object = (Match*) self; 1140 | Py_INCREF(object->rule); 1141 | return object->rule; 1142 | } 1143 | 1144 | 1145 | static PyObject* Match_getattro( 1146 | PyObject* self, 1147 | PyObject* name) 1148 | { 1149 | return PyObject_GenericGetAttr(self, name); 1150 | } 1151 | 1152 | 1153 | static PyObject* Match_richcompare( 1154 | PyObject* self, 1155 | PyObject* other, 1156 | int op) 1157 | { 1158 | PyObject* result = NULL; 1159 | 1160 | Match* a = (Match*) self; 1161 | Match* b = (Match*) other; 1162 | 1163 | if(PyObject_TypeCheck(other, &Match_Type)) 1164 | { 1165 | switch(op) 1166 | { 1167 | case Py_EQ: 1168 | if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ) && 1169 | PyObject_RichCompareBool(a->ns, b->ns, Py_EQ)) 1170 | result = Py_True; 1171 | else 1172 | result = Py_False; 1173 | 1174 | Py_INCREF(result); 1175 | break; 1176 | 1177 | case Py_NE: 1178 | if (PyObject_RichCompareBool(a->rule, b->rule, Py_NE) || 1179 | PyObject_RichCompareBool(a->ns, b->ns, Py_NE)) 1180 | result = Py_True; 1181 | else 1182 | result = Py_False; 1183 | 1184 | Py_INCREF(result); 1185 | break; 1186 | 1187 | case Py_LT: 1188 | case Py_LE: 1189 | case Py_GT: 1190 | case Py_GE: 1191 | if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ)) 1192 | result = PyObject_RichCompare(a->ns, b->ns, op); 1193 | else 1194 | result = PyObject_RichCompare(a->rule, b->rule, op); 1195 | 1196 | break; 1197 | } 1198 | } 1199 | else 1200 | { 1201 | result = PyErr_Format( 1202 | PyExc_TypeError, 1203 | "'Match' objects must be compared with objects of the same class"); 1204 | } 1205 | 1206 | return result; 1207 | } 1208 | 1209 | 1210 | static Py_hash_t Match_hash( 1211 | PyObject* self) 1212 | { 1213 | Match* match = (Match*) self; 1214 | return PyObject_Hash(match->rule) + PyObject_Hash(match->ns); 1215 | } 1216 | 1217 | //////////////////////////////////////////////////////////////////////////////// 1218 | 1219 | 1220 | static void Rule_dealloc( 1221 | PyObject* self) 1222 | { 1223 | Rule* object = (Rule*) self; 1224 | Py_XDECREF(object->identifier); 1225 | Py_XDECREF(object->tags); 1226 | Py_XDECREF(object->meta); 1227 | PyObject_Del(self); 1228 | } 1229 | 1230 | static PyObject* Rule_getattro( 1231 | PyObject* self, 1232 | PyObject* name) 1233 | { 1234 | return PyObject_GenericGetAttr(self, name); 1235 | } 1236 | 1237 | 1238 | static Rules* Rules_NEW(void) 1239 | { 1240 | Rules* rules = PyObject_NEW(Rules, &Rules_Type); 1241 | 1242 | if (rules != NULL) 1243 | { 1244 | rules->rules = NULL; 1245 | rules->externals = NULL; 1246 | } 1247 | 1248 | return rules; 1249 | } 1250 | 1251 | static void Rules_dealloc( 1252 | PyObject* self) 1253 | { 1254 | Rules* object = (Rules*) self; 1255 | 1256 | Py_XDECREF(object->externals); 1257 | 1258 | if (object->rules != NULL) 1259 | yr_rules_destroy(object->rules); 1260 | 1261 | PyObject_Del(self); 1262 | } 1263 | 1264 | static PyObject* Rules_next( 1265 | PyObject* self) 1266 | { 1267 | PyObject* tag_list; 1268 | PyObject* object; 1269 | PyObject* meta_list; 1270 | 1271 | YR_META* meta; 1272 | const char* tag; 1273 | 1274 | Rule* rule; 1275 | Rules* rules = (Rules *) self; 1276 | 1277 | // Generate new Rule object based upon iter_current_rule and increment 1278 | // iter_current_rule. 1279 | 1280 | if (RULE_IS_NULL(rules->iter_current_rule)) 1281 | { 1282 | PyErr_SetNone(PyExc_StopIteration); 1283 | return NULL; 1284 | } 1285 | 1286 | rule = PyObject_NEW(Rule, &Rule_Type); 1287 | tag_list = PyList_New(0); 1288 | meta_list = PyDict_New(); 1289 | 1290 | if (rule != NULL && tag_list != NULL && meta_list != NULL) 1291 | { 1292 | yr_rule_tags_foreach(rules->iter_current_rule, tag) 1293 | { 1294 | object = PY_STRING(tag); 1295 | PyList_Append(tag_list, object); 1296 | Py_DECREF(object); 1297 | } 1298 | 1299 | yr_rule_metas_foreach(rules->iter_current_rule, meta) 1300 | { 1301 | if (meta->type == META_TYPE_INTEGER) 1302 | object = Py_BuildValue("i", meta->integer); 1303 | else if (meta->type == META_TYPE_BOOLEAN) 1304 | object = PyBool_FromLong((long) meta->integer); 1305 | else 1306 | object = PY_STRING(meta->string); 1307 | 1308 | PyDict_SetItemString(meta_list, meta->identifier, object); 1309 | Py_DECREF(object); 1310 | } 1311 | 1312 | rule->identifier = PY_STRING(rules->iter_current_rule->identifier); 1313 | rule->tags = tag_list; 1314 | rule->meta = meta_list; 1315 | rules->iter_current_rule++; 1316 | return (PyObject*) rule; 1317 | } 1318 | else 1319 | { 1320 | Py_XDECREF(tag_list); 1321 | Py_XDECREF(meta_list); 1322 | return PyErr_Format(PyExc_TypeError, "Out of memory"); 1323 | } 1324 | } 1325 | 1326 | static PyObject* Rules_match( 1327 | PyObject* self, 1328 | PyObject* args, 1329 | PyObject* keywords) 1330 | { 1331 | static char* kwlist[] = { 1332 | "filepath", "pid", "data", "externals", 1333 | "callback", "fast", "timeout", "modules_data", 1334 | "modules_callback", NULL 1335 | }; 1336 | 1337 | char* filepath = NULL; 1338 | char* data = NULL; 1339 | 1340 | int pid = 0; 1341 | int timeout = 0; 1342 | int length; 1343 | int error = ERROR_SUCCESS; 1344 | int fast_mode = FALSE; 1345 | 1346 | PyObject* externals = NULL; 1347 | PyObject* fast = NULL; 1348 | 1349 | Rules* object = (Rules*) self; 1350 | 1351 | CALLBACK_DATA callback_data; 1352 | 1353 | callback_data.matches = NULL; 1354 | callback_data.callback = NULL; 1355 | callback_data.modules_data = NULL; 1356 | callback_data.modules_callback = NULL; 1357 | 1358 | if (PyArg_ParseTupleAndKeywords( 1359 | args, 1360 | keywords, 1361 | "|sis#OOOiOO", 1362 | kwlist, 1363 | &filepath, 1364 | &pid, 1365 | &data, 1366 | &length, 1367 | &externals, 1368 | &callback_data.callback, 1369 | &fast, 1370 | &timeout, 1371 | &callback_data.modules_data, 1372 | &callback_data.modules_callback)) 1373 | { 1374 | if (filepath == NULL && data == NULL && pid == 0) 1375 | { 1376 | return PyErr_Format( 1377 | PyExc_TypeError, 1378 | "match() takes at least one argument"); 1379 | } 1380 | 1381 | if (callback_data.callback != NULL) 1382 | { 1383 | if (!PyCallable_Check(callback_data.callback)) 1384 | { 1385 | return PyErr_Format( 1386 | PyExc_TypeError, 1387 | "'callback' must be callable"); 1388 | } 1389 | } 1390 | 1391 | if (callback_data.modules_callback != NULL) 1392 | { 1393 | if (!PyCallable_Check(callback_data.modules_callback)) 1394 | { 1395 | return PyErr_Format( 1396 | PyExc_TypeError, 1397 | "'modules_callback' must be callable"); 1398 | } 1399 | } 1400 | 1401 | if (callback_data.modules_data != NULL) 1402 | { 1403 | if (!PyDict_Check(callback_data.modules_data)) 1404 | { 1405 | return PyErr_Format( 1406 | PyExc_TypeError, 1407 | "'modules_data' must be a dictionary"); 1408 | } 1409 | } 1410 | 1411 | if (externals != NULL && externals != Py_None) 1412 | { 1413 | if (PyDict_Check(externals)) 1414 | { 1415 | if (process_match_externals(externals, object->rules) != ERROR_SUCCESS) 1416 | { 1417 | // Restore original externals provided during compiling. 1418 | process_match_externals(object->externals, object->rules); 1419 | return NULL; 1420 | } 1421 | } 1422 | else 1423 | { 1424 | return PyErr_Format( 1425 | PyExc_TypeError, 1426 | "'externals' must be a dictionary"); 1427 | } 1428 | } 1429 | 1430 | if (fast != NULL) 1431 | { 1432 | fast_mode = (PyObject_IsTrue(fast) == 1); 1433 | } 1434 | 1435 | if (filepath != NULL) 1436 | { 1437 | callback_data.matches = PyList_New(0); 1438 | 1439 | Py_BEGIN_ALLOW_THREADS 1440 | 1441 | error = yr_rules_scan_file( 1442 | object->rules, 1443 | filepath, 1444 | fast_mode ? SCAN_FLAGS_FAST_MODE : 0, 1445 | yara_callback, 1446 | &callback_data, 1447 | timeout); 1448 | 1449 | Py_END_ALLOW_THREADS 1450 | } 1451 | else if (data != NULL) 1452 | { 1453 | callback_data.matches = PyList_New(0); 1454 | 1455 | Py_BEGIN_ALLOW_THREADS 1456 | 1457 | error = yr_rules_scan_mem( 1458 | object->rules, 1459 | (unsigned char*) data, 1460 | (unsigned int) length, 1461 | fast_mode ? SCAN_FLAGS_FAST_MODE : 0, 1462 | yara_callback, 1463 | &callback_data, 1464 | timeout); 1465 | 1466 | Py_END_ALLOW_THREADS 1467 | } 1468 | else if (pid != 0) 1469 | { 1470 | callback_data.matches = PyList_New(0); 1471 | 1472 | Py_BEGIN_ALLOW_THREADS 1473 | 1474 | error = yr_rules_scan_proc( 1475 | object->rules, 1476 | pid, 1477 | fast_mode ? SCAN_FLAGS_FAST_MODE : 0, 1478 | yara_callback, 1479 | &callback_data, 1480 | timeout); 1481 | 1482 | Py_END_ALLOW_THREADS 1483 | } 1484 | 1485 | // Restore original externals provided during compiling. 1486 | if (object->externals != NULL) 1487 | { 1488 | if (process_match_externals( 1489 | object->externals, object->rules) != ERROR_SUCCESS) 1490 | { 1491 | Py_DECREF(callback_data.matches); 1492 | return NULL; 1493 | } 1494 | } 1495 | 1496 | if (error != ERROR_SUCCESS) 1497 | { 1498 | Py_DECREF(callback_data.matches); 1499 | 1500 | if (error != ERROR_CALLBACK_ERROR) 1501 | { 1502 | handle_error(error, filepath); 1503 | 1504 | #ifdef PROFILING_ENABLED 1505 | PyObject* exception = PyErr_Occurred(); 1506 | 1507 | if (exception != NULL && error == ERROR_SCAN_TIMEOUT) 1508 | { 1509 | PyObject_SetAttrString( 1510 | exception, 1511 | "profiling_info", 1512 | Rules_profiling_info(self, NULL)); 1513 | } 1514 | #endif 1515 | } 1516 | 1517 | return NULL; 1518 | } 1519 | } 1520 | 1521 | return callback_data.matches; 1522 | } 1523 | 1524 | 1525 | static PyObject* Rules_save( 1526 | PyObject* self, 1527 | PyObject* args, 1528 | PyObject* keywords) 1529 | { 1530 | static char* kwlist[] = { 1531 | "filepath", "file", NULL 1532 | }; 1533 | 1534 | char* filepath = NULL; 1535 | PyObject* file = NULL; 1536 | Rules* rules = (Rules*) self; 1537 | 1538 | int error; 1539 | 1540 | if (!PyArg_ParseTupleAndKeywords( 1541 | args, 1542 | keywords, 1543 | "|sO", 1544 | kwlist, 1545 | &filepath, 1546 | &file)) 1547 | { 1548 | return NULL; 1549 | } 1550 | 1551 | if (filepath != NULL) 1552 | { 1553 | Py_BEGIN_ALLOW_THREADS 1554 | error = yr_rules_save(rules->rules, filepath); 1555 | Py_END_ALLOW_THREADS 1556 | 1557 | if (error != ERROR_SUCCESS) 1558 | return handle_error(error, filepath); 1559 | } 1560 | else if (file != NULL && PyObject_HasAttrString(file, "write")) 1561 | { 1562 | YR_STREAM stream; 1563 | 1564 | stream.user_data = file; 1565 | stream.write = flo_write; 1566 | 1567 | Py_BEGIN_ALLOW_THREADS; 1568 | error = yr_rules_save_stream(rules->rules, &stream); 1569 | Py_END_ALLOW_THREADS; 1570 | 1571 | if (error != ERROR_SUCCESS) 1572 | return handle_error(error, ""); 1573 | } 1574 | else 1575 | { 1576 | return PyErr_Format( 1577 | PyExc_TypeError, 1578 | "load() expects either a file path or a file-like object"); 1579 | } 1580 | 1581 | Py_RETURN_NONE; 1582 | } 1583 | 1584 | 1585 | static PyObject* Rules_profiling_info( 1586 | PyObject* self, 1587 | PyObject* args) 1588 | { 1589 | 1590 | #ifdef PROFILING_ENABLED 1591 | PyObject* object; 1592 | PyObject* result; 1593 | 1594 | YR_RULES* rules = ((Rules*) self)->rules; 1595 | YR_RULE* rule; 1596 | YR_STRING* string; 1597 | 1598 | char key[512]; 1599 | uint64_t clock_ticks; 1600 | 1601 | result = PyDict_New(); 1602 | 1603 | yr_rules_foreach(rules, rule) 1604 | { 1605 | clock_ticks = rule->clock_ticks; 1606 | 1607 | yr_rule_strings_foreach(rule, string) 1608 | { 1609 | clock_ticks += string->clock_ticks; 1610 | } 1611 | 1612 | snprintf(key, sizeof(key), "%s:%s", rule->ns->name, rule->identifier); 1613 | 1614 | object = PyLong_FromLongLong(clock_ticks); 1615 | PyDict_SetItemString(result, key, object); 1616 | Py_DECREF(object); 1617 | } 1618 | 1619 | return result; 1620 | #else 1621 | return PyErr_Format(YaraError, "libyara compiled without profiling support"); 1622 | #endif 1623 | } 1624 | 1625 | 1626 | static PyObject* Rules_getattro( 1627 | PyObject* self, 1628 | PyObject* name) 1629 | { 1630 | return PyObject_GenericGetAttr(self, name); 1631 | } 1632 | 1633 | 1634 | void raise_exception_on_error( 1635 | int error_level, 1636 | const char* file_name, 1637 | int line_number, 1638 | const char* message, 1639 | void* user_data) 1640 | { 1641 | if (error_level == YARA_ERROR_LEVEL_ERROR) 1642 | { 1643 | if (file_name != NULL) 1644 | PyErr_Format( 1645 | YaraSyntaxError, 1646 | "%s(%d): %s", 1647 | file_name, 1648 | line_number, 1649 | message); 1650 | else 1651 | PyErr_Format( 1652 | YaraSyntaxError, 1653 | "%s", 1654 | message); 1655 | } 1656 | } 1657 | 1658 | 1659 | void raise_exception_on_error_or_warning( 1660 | int error_level, 1661 | const char* file_name, 1662 | int line_number, 1663 | const char* message, 1664 | void* user_data) 1665 | { 1666 | if (error_level == YARA_ERROR_LEVEL_ERROR) 1667 | { 1668 | if (file_name != NULL) 1669 | PyErr_Format( 1670 | YaraSyntaxError, 1671 | "%s(%d): %s", 1672 | file_name, 1673 | line_number, 1674 | message); 1675 | else 1676 | PyErr_Format( 1677 | YaraSyntaxError, 1678 | "%s", 1679 | message); 1680 | } 1681 | else 1682 | { 1683 | if (file_name != NULL) 1684 | PyErr_Format( 1685 | YaraWarningError, 1686 | "%s(%d): %s", 1687 | file_name, 1688 | line_number, 1689 | message); 1690 | else 1691 | PyErr_Format( 1692 | YaraWarningError, 1693 | "%s", 1694 | message); 1695 | } 1696 | } 1697 | 1698 | //////////////////////////////////////////////////////////////////////////////// 1699 | 1700 | static PyObject* yara_compile( 1701 | PyObject* self, 1702 | PyObject* args, 1703 | PyObject* keywords) 1704 | { 1705 | static char *kwlist[] = { 1706 | "filepath", "source", "file", "filepaths", "sources", 1707 | "includes", "externals", "error_on_warning", NULL}; 1708 | 1709 | YR_COMPILER* compiler; 1710 | YR_RULES* yara_rules; 1711 | FILE* fh; 1712 | 1713 | Rules* rules; 1714 | 1715 | PyObject* key; 1716 | PyObject* value; 1717 | PyObject* result = NULL; 1718 | PyObject* file = NULL; 1719 | PyObject* sources_dict = NULL; 1720 | PyObject* filepaths_dict = NULL; 1721 | PyObject* includes = NULL; 1722 | PyObject* externals = NULL; 1723 | PyObject* error_on_warning = NULL; 1724 | 1725 | Py_ssize_t pos = 0; 1726 | 1727 | int fd; 1728 | int error = 0; 1729 | 1730 | char* filepath = NULL; 1731 | char* source = NULL; 1732 | char* ns = NULL; 1733 | 1734 | if (PyArg_ParseTupleAndKeywords( 1735 | args, 1736 | keywords, 1737 | "|ssOOOOOO", 1738 | kwlist, 1739 | &filepath, 1740 | &source, 1741 | &file, 1742 | &filepaths_dict, 1743 | &sources_dict, 1744 | &includes, 1745 | &externals, 1746 | &error_on_warning)) 1747 | { 1748 | error = yr_compiler_create(&compiler); 1749 | 1750 | if (error != ERROR_SUCCESS) 1751 | return handle_error(error, NULL); 1752 | 1753 | yr_compiler_set_callback(compiler, raise_exception_on_error, NULL); 1754 | 1755 | if (error_on_warning != NULL) 1756 | { 1757 | if (PyBool_Check(error_on_warning)) 1758 | { 1759 | if (PyObject_IsTrue(error_on_warning) == 1) 1760 | { 1761 | yr_compiler_set_callback( 1762 | compiler, 1763 | raise_exception_on_error_or_warning, 1764 | NULL); 1765 | } 1766 | } 1767 | else 1768 | { 1769 | yr_compiler_destroy(compiler); 1770 | return PyErr_Format( 1771 | PyExc_TypeError, 1772 | "'error_on_warning' param must be of boolean type"); 1773 | } 1774 | } 1775 | 1776 | if (includes != NULL) 1777 | { 1778 | if (PyBool_Check(includes)) 1779 | { 1780 | // PyObject_IsTrue can return -1 in case of error 1781 | compiler->allow_includes = (PyObject_IsTrue(includes) == 1); 1782 | } 1783 | else 1784 | { 1785 | yr_compiler_destroy(compiler); 1786 | return PyErr_Format( 1787 | PyExc_TypeError, 1788 | "'includes' param must be of boolean type"); 1789 | } 1790 | } 1791 | 1792 | if (externals != NULL && externals != Py_None) 1793 | { 1794 | if (PyDict_Check(externals)) 1795 | { 1796 | if (process_compile_externals(externals, compiler) != ERROR_SUCCESS) 1797 | { 1798 | yr_compiler_destroy(compiler); 1799 | return NULL; 1800 | } 1801 | } 1802 | else 1803 | { 1804 | yr_compiler_destroy(compiler); 1805 | return PyErr_Format( 1806 | PyExc_TypeError, 1807 | "'externals' must be a dictionary"); 1808 | } 1809 | } 1810 | 1811 | if (filepath != NULL) 1812 | { 1813 | fh = fopen(filepath, "r"); 1814 | 1815 | if (fh != NULL) 1816 | { 1817 | error = yr_compiler_add_file(compiler, fh, NULL, filepath); 1818 | fclose(fh); 1819 | } 1820 | else 1821 | { 1822 | result = PyErr_SetFromErrno(YaraError); 1823 | } 1824 | } 1825 | else if (source != NULL) 1826 | { 1827 | error = yr_compiler_add_string(compiler, source, NULL); 1828 | } 1829 | else if (file != NULL) 1830 | { 1831 | fd = dup(PyObject_AsFileDescriptor(file)); 1832 | fh = fdopen(fd, "r"); 1833 | error = yr_compiler_add_file(compiler, fh, NULL, NULL); 1834 | fclose(fh); 1835 | } 1836 | else if (sources_dict != NULL) 1837 | { 1838 | if (PyDict_Check(sources_dict)) 1839 | { 1840 | while (PyDict_Next(sources_dict, &pos, &key, &value)) 1841 | { 1842 | source = PY_STRING_TO_C(value); 1843 | ns = PY_STRING_TO_C(key); 1844 | 1845 | if (source != NULL && ns != NULL) 1846 | { 1847 | error = yr_compiler_add_string(compiler, source, ns); 1848 | 1849 | if (error > 0) 1850 | break; 1851 | } 1852 | else 1853 | { 1854 | result = PyErr_Format( 1855 | PyExc_TypeError, 1856 | "keys and values of the 'sources' dictionary must be " 1857 | "of string type"); 1858 | break; 1859 | } 1860 | } 1861 | } 1862 | else 1863 | { 1864 | result = PyErr_Format( 1865 | PyExc_TypeError, 1866 | "'sources' must be a dictionary"); 1867 | } 1868 | } 1869 | else if (filepaths_dict != NULL) 1870 | { 1871 | if (PyDict_Check(filepaths_dict)) 1872 | { 1873 | while (PyDict_Next(filepaths_dict, &pos, &key, &value)) 1874 | { 1875 | filepath = PY_STRING_TO_C(value); 1876 | ns = PY_STRING_TO_C(key); 1877 | 1878 | if (filepath != NULL && ns != NULL) 1879 | { 1880 | fh = fopen(filepath, "r"); 1881 | 1882 | if (fh != NULL) 1883 | { 1884 | error = yr_compiler_add_file(compiler, fh, ns, filepath); 1885 | fclose(fh); 1886 | 1887 | if (error > 0) 1888 | break; 1889 | } 1890 | else 1891 | { 1892 | result = PyErr_SetFromErrno(YaraError); 1893 | break; 1894 | } 1895 | } 1896 | else 1897 | { 1898 | result = PyErr_Format( 1899 | PyExc_TypeError, 1900 | "keys and values of the filepaths dictionary must be of " 1901 | "string type"); 1902 | break; 1903 | } 1904 | } 1905 | } 1906 | else 1907 | { 1908 | result = PyErr_Format( 1909 | PyExc_TypeError, 1910 | "filepaths must be a dictionary"); 1911 | } 1912 | } 1913 | else 1914 | { 1915 | result = PyErr_Format( 1916 | PyExc_TypeError, 1917 | "compile() takes 1 argument"); 1918 | } 1919 | 1920 | if (PyErr_Occurred() == NULL) 1921 | { 1922 | rules = Rules_NEW(); 1923 | 1924 | if (rules != NULL) 1925 | { 1926 | Py_BEGIN_ALLOW_THREADS 1927 | error = yr_compiler_get_rules(compiler, &yara_rules); 1928 | Py_END_ALLOW_THREADS 1929 | 1930 | if (error == ERROR_SUCCESS) 1931 | { 1932 | rules->rules = yara_rules; 1933 | rules->iter_current_rule = rules->rules->rules_list_head; 1934 | 1935 | if (externals != NULL && externals != Py_None) 1936 | rules->externals = PyDict_Copy(externals); 1937 | 1938 | result = (PyObject*) rules; 1939 | } 1940 | else 1941 | { 1942 | Py_DECREF(rules); 1943 | result = handle_error(error, NULL); 1944 | } 1945 | } 1946 | else 1947 | { 1948 | result = handle_error(ERROR_INSUFFICIENT_MEMORY, NULL); 1949 | } 1950 | } 1951 | 1952 | yr_compiler_destroy(compiler); 1953 | } 1954 | 1955 | return result; 1956 | } 1957 | 1958 | 1959 | static PyObject* yara_load( 1960 | PyObject* self, 1961 | PyObject* args, 1962 | PyObject* keywords) 1963 | { 1964 | static char* kwlist[] = { 1965 | "filepath", "file", NULL 1966 | }; 1967 | 1968 | YR_EXTERNAL_VARIABLE* external; 1969 | 1970 | Rules* rules = NULL; 1971 | PyObject* file = NULL; 1972 | char* filepath = NULL; 1973 | 1974 | int error; 1975 | 1976 | if (!PyArg_ParseTupleAndKeywords( 1977 | args, 1978 | keywords, 1979 | "|sO", 1980 | kwlist, 1981 | &filepath, 1982 | &file)) 1983 | { 1984 | return NULL; 1985 | } 1986 | 1987 | if (filepath != NULL) 1988 | { 1989 | rules = Rules_NEW(); 1990 | 1991 | if (rules == NULL) 1992 | return PyErr_NoMemory(); 1993 | 1994 | Py_BEGIN_ALLOW_THREADS; 1995 | error = yr_rules_load(filepath, &rules->rules); 1996 | Py_END_ALLOW_THREADS; 1997 | 1998 | if (error != ERROR_SUCCESS) 1999 | { 2000 | Py_DECREF(rules); 2001 | return handle_error(error, filepath); 2002 | } 2003 | } 2004 | else if (file != NULL && PyObject_HasAttrString(file, "read")) 2005 | { 2006 | YR_STREAM stream; 2007 | 2008 | stream.user_data = file; 2009 | stream.read = flo_read; 2010 | 2011 | rules = Rules_NEW(); 2012 | 2013 | if (rules == NULL) 2014 | return PyErr_NoMemory(); 2015 | 2016 | Py_BEGIN_ALLOW_THREADS; 2017 | error = yr_rules_load_stream(&stream, &rules->rules); 2018 | Py_END_ALLOW_THREADS; 2019 | 2020 | if (error != ERROR_SUCCESS) 2021 | { 2022 | Py_DECREF(rules); 2023 | return handle_error(error, ""); 2024 | } 2025 | } 2026 | else 2027 | { 2028 | return PyErr_Format( 2029 | PyExc_TypeError, 2030 | "load() expects either a file path or a file-like object"); 2031 | } 2032 | 2033 | external = rules->rules->externals_list_head; 2034 | rules->iter_current_rule = rules->rules->rules_list_head; 2035 | 2036 | if (!EXTERNAL_VARIABLE_IS_NULL(external)) 2037 | rules->externals = PyDict_New(); 2038 | 2039 | while (!EXTERNAL_VARIABLE_IS_NULL(external)) 2040 | { 2041 | switch(external->type) 2042 | { 2043 | case EXTERNAL_VARIABLE_TYPE_BOOLEAN: 2044 | PyDict_SetItemString( 2045 | rules->externals, 2046 | external->identifier, 2047 | PyBool_FromLong((long) external->value.i)); 2048 | break; 2049 | case EXTERNAL_VARIABLE_TYPE_INTEGER: 2050 | PyDict_SetItemString( 2051 | rules->externals, 2052 | external->identifier, 2053 | PyLong_FromLong((long) external->value.i)); 2054 | break; 2055 | case EXTERNAL_VARIABLE_TYPE_FLOAT: 2056 | PyDict_SetItemString( 2057 | rules->externals, 2058 | external->identifier, 2059 | PyFloat_FromDouble(external->value.f)); 2060 | break; 2061 | case EXTERNAL_VARIABLE_TYPE_STRING: 2062 | PyDict_SetItemString( 2063 | rules->externals, 2064 | external->identifier, 2065 | PY_STRING(external->value.s)); 2066 | break; 2067 | } 2068 | 2069 | external++; 2070 | } 2071 | 2072 | return (PyObject*) rules; 2073 | } 2074 | 2075 | 2076 | void finalize(void) 2077 | { 2078 | yr_finalize(); 2079 | } 2080 | 2081 | 2082 | static PyMethodDef yara_methods[] = { 2083 | { 2084 | "compile", 2085 | (PyCFunction) yara_compile, 2086 | METH_VARARGS | METH_KEYWORDS, 2087 | "Compiles a YARA rules file and returns an instance of class Rules" 2088 | }, 2089 | { 2090 | "load", 2091 | (PyCFunction) yara_load, 2092 | METH_VARARGS | METH_KEYWORDS, 2093 | "Loads a previously saved YARA rules file and returns an instance of class Rules" 2094 | }, 2095 | { NULL, NULL } 2096 | }; 2097 | 2098 | #if PY_MAJOR_VERSION >= 3 2099 | #define MOD_ERROR_VAL NULL 2100 | #define MOD_SUCCESS_VAL(val) val 2101 | #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) 2102 | #define MOD_DEF(ob, name, doc, methods) \ 2103 | static struct PyModuleDef moduledef = { \ 2104 | PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ 2105 | ob = PyModule_Create(&moduledef); 2106 | #else 2107 | #define MOD_ERROR_VAL 2108 | #define MOD_SUCCESS_VAL(val) 2109 | #define MOD_INIT(name) void init##name(void) 2110 | #define MOD_DEF(ob, name, doc, methods) \ 2111 | ob = Py_InitModule3(name, methods, doc); 2112 | #endif 2113 | 2114 | 2115 | MOD_INIT(yara) 2116 | { 2117 | PyObject* m; 2118 | 2119 | MOD_DEF(m, "yara", YARA_DOC, yara_methods) 2120 | 2121 | if (m == NULL) 2122 | return MOD_ERROR_VAL; 2123 | 2124 | /* initialize module variables/constants */ 2125 | 2126 | PyModule_AddIntConstant(m, "CALLBACK_CONTINUE", 0); 2127 | PyModule_AddIntConstant(m, "CALLBACK_ABORT", 1); 2128 | PyModule_AddStringConstant(m, "__version__", YR_VERSION); 2129 | PyModule_AddStringConstant(m, "YARA_VERSION", YR_VERSION); 2130 | PyModule_AddIntConstant(m, "YARA_VERSION_HEX", YR_VERSION_HEX); 2131 | 2132 | #if PYTHON_API_VERSION >= 1007 2133 | YaraError = PyErr_NewException("yara.Error", PyExc_Exception, NULL); 2134 | YaraSyntaxError = PyErr_NewException("yara.SyntaxError", YaraError, NULL); 2135 | YaraTimeoutError = PyErr_NewException("yara.TimeoutError", YaraError, NULL); 2136 | YaraWarningError = PyErr_NewException("yara.WarningError", YaraError, NULL); 2137 | #else 2138 | YaraError = Py_BuildValue("s", "yara.Error"); 2139 | YaraSyntaxError = Py_BuildValue("s", "yara.SyntaxError"); 2140 | YaraTimeoutError = Py_BuildValue("s", "yara.TimeoutError"); 2141 | YaraWarningError = Py_BuildValue("s", "yara.WarningError"); 2142 | #endif 2143 | 2144 | if (PyType_Ready(&Rule_Type) < 0) 2145 | return MOD_ERROR_VAL; 2146 | 2147 | if (PyType_Ready(&Rules_Type) < 0) 2148 | return MOD_ERROR_VAL; 2149 | 2150 | if (PyType_Ready(&Match_Type) < 0) 2151 | return MOD_ERROR_VAL; 2152 | 2153 | PyModule_AddObject(m, "Error", YaraError); 2154 | PyModule_AddObject(m, "SyntaxError", YaraSyntaxError); 2155 | PyModule_AddObject(m, "TimeoutError", YaraTimeoutError); 2156 | PyModule_AddObject(m, "WarningError", YaraWarningError); 2157 | 2158 | if (yr_initialize() != ERROR_SUCCESS) 2159 | { 2160 | PyErr_SetString(YaraError, "initialization error"); 2161 | return MOD_ERROR_VAL; 2162 | } 2163 | 2164 | Py_AtExit(finalize); 2165 | 2166 | return MOD_SUCCESS_VAL(m); 2167 | } 2168 | --------------------------------------------------------------------------------