Commit 4897fc4c by Victor M. Alvarez

Initial commit

parent 3e100f21
README
setup.py
yara-python.c
#
# Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys
from distutils.core import setup, Extension
args = sys.argv[1:]
macros = []
if '--with-profiling' in args:
macros.append(('PROFILING_ENABLED','1'))
args.remove('--with-profiling')
setup(script_args=args,
name='yara-python',
version='3.4.0',
author='Victor M. Alvarez',
author_email='plusvic@gmail.com;vmalvarez@virustotal.com',
ext_modules=[Extension(
name='yara',
sources=['yara-python.c'],
libraries=['yara'],
include_dirs=['../libyara/include'],
library_dirs=['../libyara/.libs'],
define_macros=macros,
extra_compile_args=['-std=gnu99']
)])
#
# Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from distutils.core import setup, Extension
setup(name='yara-python',
version='3.4.0',
author='Victor M. Alvarez',
author_email='plusvic@gmail.com;vmalvarez@virustotal.com',
ext_modules=[Extension(
name='yara',
sources=['yara-python.c'],
include_dirs=['../windows/include', '../libyara/include'],
extra_objects=[
'user32.lib', 'gdi32.lib', '../windows/libyara/Release/libyara32.lib']
)])
#
# Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from distutils.core import setup, Extension
setup(name='yara-python',
version='3.4.0',
author='Victor M. Alvarez',
author_email='plusvic@gmail.com;vmalvarez@virustotal.com',
ext_modules=[Extension(
name='yara',
sources=['yara-python.c'],
include_dirs=['../windows/include', '../libyara/include'],
extra_objects=[
'user32.lib', 'gdi32.lib', '../windows/libyara/Release/libyara64.lib']
)])
#
# Copyright (c) 2007-2014. The YARA Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import tempfile
import binascii
import os
import sys
import unittest
import yara
# Python 2/3
try:
import StringIO
except:
import io
PE32_FILE = binascii.unhexlify('\
4d5a000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000040000000\
504500004c0101005dbe45450000000000000000e00003010b01080004000000\
0000000000000000600100006001000064010000000040000100000001000000\
0400000000000000040000000000000064010000600100000000000002000004\
0000100000100000000010000010000000000000100000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000002e74657874000000\
0400000060010000040000006001000000000000000000000000000020000060\
6a2a58c3')
ELF32_FILE = binascii.unhexlify('\
7f454c4601010100000000000000000002000300010000006080040834000000\
a800000000000000340020000100280004000300010000000000000000800408\
008004086c0000006c0000000500000000100000000000000000000000000000\
b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\
657220322e30352e303100002e7368737472746162002e74657874002e636f6d\
6d656e7400000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000b000000010000000600000060800408\
600000000c000000000000000000000010000000000000001100000001000000\
00000000000000006c0000001f00000000000000000000000100000000000000\
010000000300000000000000000000008b0000001a0000000000000000000000\
0100000000000000')
ELF64_FILE = binascii.unhexlify('\
7f454c4602010100000000000000000002003e00010000008000400000000000\
4000000000000000c80000000000000000000000400038000100400004000300\
0100000005000000000000000000000000004000000000000000400000000000\
8c000000000000008c0000000000000000002000000000000000000000000000\
b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\
657220322e30352e303100002e7368737472746162002e74657874002e636f6d\
6d656e7400000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
00000000000000000b0000000100000006000000000000008000400000000000\
80000000000000000c0000000000000000000000000000001000000000000000\
0000000000000000110000000100000000000000000000000000000000000000\
8c000000000000001f0000000000000000000000000000000100000000000000\
0000000000000000010000000300000000000000000000000000000000000000\
ab000000000000001a0000000000000000000000000000000100000000000000\
0000000000000000')
# The 3 possible outcomes for each pattern
[SUCCEED, FAIL, SYNTAX_ERROR] = range(3)
RE_TESTS = [
# RE, string, expected result, expected matching
(')', '', SYNTAX_ERROR),
('abc', 'abc', SUCCEED, 'abc'),
('abc', 'xbc', FAIL),
('abc', 'axc', FAIL),
('abc', 'abx', FAIL),
('abc', 'xabcx', SUCCEED, 'abc'),
('abc', 'ababc', SUCCEED, 'abc'),
('a.c', 'abc', SUCCEED, 'abc'),
('a.b', 'a\nb', FAIL),
('a.*b', 'acc\nccb', FAIL),
('a.{4,5}b', 'acc\nccb', FAIL),
('a.b', 'a\rb', SUCCEED, 'a\rb'),
('ab*c', 'abc', SUCCEED, 'abc'),
('ab*c', 'ac', SUCCEED, 'ac'),
('ab*bc', 'abc', SUCCEED, 'abc'),
('ab*bc', 'abbc', SUCCEED, 'abbc'),
('a.*bb', 'abbbb', SUCCEED, 'abbbb'),
('a.*?bbb', 'abbbbbb', SUCCEED, 'abbb'),
('a.*c', 'ac', SUCCEED, 'ac'),
('a.*c', 'axyzc', SUCCEED, 'axyzc'),
('ab+c', 'abbc', SUCCEED, 'abbc'),
('ab+c', 'ac', FAIL),
('ab+', 'abbbb', SUCCEED, 'abbbb'),
('ab+?', 'abbbb', SUCCEED, 'ab'),
('ab+bc', 'abc', FAIL),
('ab+bc', 'abq', FAIL),
('a+b+c', 'aabbabc', SUCCEED, 'abc'),
('ab?bc', 'abbbbc', FAIL),
('ab?c', 'abc', SUCCEED, 'abc'),
('ab*?', 'abbb', SUCCEED, 'a'),
('ab?c', 'abc', SUCCEED, 'abc'),
('ab??', 'ab', SUCCEED, 'a'),
('a(b|x)c', 'abc', SUCCEED, 'abc'),
('a(b|x)c', 'axc', SUCCEED, 'axc'),
('a(b|.)c', 'axc', SUCCEED, 'axc'),
('a(b|x|y)c', 'ayc', SUCCEED, 'ayc'),
('(a+|b)*', 'ab', SUCCEED, 'ab'),
('a|b|c|d|e', 'e', SUCCEED, 'e'),
('(a|b|c|d|e)f', 'ef', SUCCEED, 'ef'),
('.b{2}', 'abb', SUCCEED, 'abb'),
('ab{1}c', 'abc', SUCCEED, 'abc'),
('ab{1,2}c', 'abbc', SUCCEED, 'abbc'),
('ab{1,}c', 'abbbc', SUCCEED, 'abbbc'),
('ab{1,}b', 'ab', FAIL),
('ab{1}c', 'abbc', FAIL),
('ab{0,}c', 'ac', SUCCEED, 'ac'),
('ab{0,}c', 'abbbc', SUCCEED, 'abbbc'),
('ab{,3}c', 'abbbc', SUCCEED, 'abbbc'),
('ab{,2}c', 'abbbc', FAIL),
('ab{4,5}bc', 'abbbbc', FAIL),
('ab{2,3}?', 'abbbbb', SUCCEED, 'abb'),
('ab{.*}', 'ab{c}', SUCCEED, 'ab{c}'),
('.(aa){1,2}', 'aaaaaaaaaa', SUCCEED, 'aaaaa'),
('a.(bc.){2}', 'aabcabca', SUCCEED, 'aabcabca'),
('(ab{1,2}c){1,3}', 'abbcabc', SUCCEED, 'abbcabc'),
('ab(c|cc){1,3}d', 'abccccccd', SUCCEED, 'abccccccd'),
('a[bx]c', 'abc', SUCCEED, 'abc'),
('a[bx]c', 'axc', SUCCEED, 'axc'),
('a[0-9]*b', 'ab', SUCCEED, 'ab'),
('a[0-9]*b', 'a0123456789b', SUCCEED, 'a0123456789b'),
('[0-9a-f]+', '0123456789abcdef', SUCCEED, '0123456789abcdef'),
('[0-9a-f]+', 'xyz0123456789xyz', SUCCEED, '0123456789'),
('a[\s\S]b', 'a b', SUCCEED, 'a b'),
('a[\d\D]b', 'a1b', SUCCEED, 'a1b'),
('[x-z]+', 'abc', FAIL),
('a[-]?c', 'ac', SUCCEED, 'ac'),
('a[-b]', 'a-', SUCCEED, 'a-'),
('a[-b]', 'ab', SUCCEED, 'ab'),
('a[b-]', 'a-', SUCCEED, 'a-'),
('a[b-]', 'ab', SUCCEED, 'ab'),
('[a-c-e]', 'b', SUCCEED, 'b'),
('[a-c-e]', '-', SUCCEED, '-'),
('[a-c-e]', 'd', FAIL),
('[b-a]', '', SYNTAX_ERROR),
('(abc', '', SYNTAX_ERROR),
('abc)', '', SYNTAX_ERROR),
('a[]b', '', SYNTAX_ERROR),
('a\\', '', SYNTAX_ERROR),
('a[\\-b]', 'a-', SUCCEED, 'a-'),
('a[\\-b]', 'ab', SUCCEED, 'ab'),
('a[\\', '', SYNTAX_ERROR),
('a]', 'a]', SUCCEED, 'a]'),
('a[]]b', 'a]b', SUCCEED, 'a]b'),
('a[\]]b', 'a]b', SUCCEED, 'a]b'),
('a[^bc]d', 'aed', SUCCEED, 'aed'),
('a[^bc]d', 'abd', FAIL),
('a[^-b]c', 'adc', SUCCEED, 'adc'),
('a[^-b]c', 'a-c', FAIL),
('a[^]b]c', 'a]c', FAIL),
('a[^]b]c', 'adc', SUCCEED, 'adc'),
('[^ab]*', 'cde', SUCCEED, 'cde'),
(')(', '', SYNTAX_ERROR),
(r'a\sb', 'a b', SUCCEED, 'a b'),
(r'a\sb', 'a\tb', SUCCEED, 'a\tb'),
(r'a\sb', 'a\rb', SUCCEED, 'a\rb'),
(r'a\sb', 'a\nb', SUCCEED, 'a\nb'),
(r'a\sb', 'a\vb', SUCCEED, 'a\vb'),
(r'a\sb', 'a\fb', SUCCEED, 'a\fb'),
(r'a\Sb', 'a b', FAIL),
(r'a\Sb', 'a\tb', FAIL),
(r'a\Sb', 'a\rb', FAIL),
(r'a\Sb', 'a\nb', FAIL),
(r'a\Sb', 'a\vb', FAIL),
(r'a\Sb', 'a\fb', FAIL),
(r'\n\r\t\f\a', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'),
(r'[\n][\r][\t][\f][\a]', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'),
(r'\x00\x01\x02', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'),
(r'[\x00-\x02]+', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'),
(r'[\x00-\x02]+', '\x03\x04\x05', FAIL),
(r'[\x5D]', ']', SUCCEED, ']'),
(r'[\0x5A-\x5D]', '\x5B', SUCCEED, '\x5B'),
(r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'),
(r'[\x5C-\x5F]', '\x5E', SUCCEED, '\x5E'),
(r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'),
('a\wc', 'abc', SUCCEED, 'abc'),
('a\wc', 'a_c', SUCCEED, 'a_c'),
('a\wc', 'a0c', SUCCEED, 'a0c'),
('a\wc', 'a*c', FAIL),
('\w+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'),
('[\w]+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'),
('\D+', '1234abc5678', SUCCEED, 'abc'),
('[\d]+', '0123456789', SUCCEED, '0123456789'),
('[\D]+', '1234abc5678', SUCCEED, 'abc'),
('[\da-fA-F]+', '123abc', SUCCEED, '123abc'),
('^(ab|cd)e', 'abcde', FAIL),
('(abc|)ef', 'abcdef', SUCCEED, 'ef'),
('(abc|)ef', 'abcef', SUCCEED, 'abcef'),
(r'\babc', 'abc', SUCCEED, 'abc'),
(r'abc\b', 'abc', SUCCEED, 'abc'),
(r'\babc', '1abc', FAIL),
(r'abc\b', 'abc1', FAIL),
(r'abc\s\b', 'abc x', SUCCEED, 'abc '),
(r'abc\s\b', 'abc ', FAIL),
(r'\babc\b', ' abc ', SUCCEED, 'abc'),
(r'\b\w\w\w\b', ' abc ', SUCCEED, 'abc'),
(r'\w\w\w\b', 'abcd', SUCCEED, 'bcd'),
(r'\b\w\w\w', 'abcd', SUCCEED, 'abc'),
(r'\b\w\w\w\b', 'abcd', FAIL),
(r'\Babc', 'abc', FAIL),
(r'abc\B', 'abc', FAIL),
(r'\Babc', '1abc', SUCCEED, 'abc'),
(r'abc\B', 'abc1', SUCCEED, 'abc'),
(r'abc\s\B', 'abc x', FAIL),
(r'abc\s\B', 'abc ', SUCCEED, 'abc '),
(r'\w\w\w\B', 'abcd', SUCCEED, 'abc'),
(r'\B\w\w\w', 'abcd', SUCCEED, 'bcd'),
(r'\B\w\w\w\B', 'abcd', FAIL),
# This is allowed in most regexp engines but in order to keep the
# grammar free of shift/reduce conflicts I've decided not supporting
# it. Users can use the (abc|) form instead.
('(|abc)ef', '', SYNTAX_ERROR),
('((a)(b)c)(d)', 'abcd', SUCCEED, 'abcd'),
('(a|b)c*d', 'abcd', SUCCEED, 'bcd'),
('(ab|ab*)bc', 'abc', SUCCEED, 'abc'),
('a([bc]*)c*', 'abc', SUCCEED, 'abc'),
('a([bc]*)c*', 'ac', SUCCEED, 'ac'),
('a([bc]*)c*', 'a', SUCCEED, 'a'),
('a([bc]*)(c*d)', 'abcd', SUCCEED, 'abcd'),
('a([bc]+)(c*d)', 'abcd', SUCCEED, 'abcd'),
('a([bc]*)(c+d)', 'abcd', SUCCEED, 'abcd'),
('a[bcd]*dcdcde', 'adcdcde', SUCCEED, 'adcdcde'),
('a[bcd]+dcdcde', 'adcdcde', FAIL),
(r'\((.*), (.*)\)', '(a, b)', SUCCEED, '(a, b)'),
('abc|123$', 'abcx', SUCCEED, 'abc'),
('abc|123$', '123x', FAIL),
('abc|^123', '123', SUCCEED, '123'),
('abc|^123', 'x123', FAIL),
('^abc$', 'abc', SUCCEED, 'abc'),
('^abc$', 'abcc', FAIL),
('^abc', 'abcc', SUCCEED, 'abc'),
('^abc$', 'aabc', FAIL),
('abc$', 'aabc', SUCCEED, 'abc'),
('^a(bc+|b[eh])g|.h$', 'abhg', SUCCEED, 'abhg'),
('(bc+d$|ef*g.|h?i(j|k))', 'effgz', SUCCEED, 'effgz'),
('(bc+d$|ef*g.|h?i(j|k))', 'ij', SUCCEED, 'ij'),
('(bc+d$|ef*g.|h?i(j|k))', 'effg', FAIL),
('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', FAIL),
('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', SUCCEED, 'effgz'),
# Test case for issue #324
('whatever| x. x', ' xy x', SUCCEED, ' xy x'),
]
class TestYara(unittest.TestCase):
def assertTrueRules(self, rules, data='dummy'):
for r in rules:
r = yara.compile(source=r)
self.assertTrue(r.match(data=data))
def assertFalseRules(self, rules, data='dummy'):
for r in rules:
r = yara.compile(source=r)
self.assertFalse(r.match(data=data))
def assertSyntaxError(self, rules):
for r in rules:
self.assertRaises(yara.SyntaxError, yara.compile, source=r)
def runReTest(self, test):
regexp = test[0]
string = test[1]
expected_result = test[2]
source = 'rule test { strings: $a = /%s/ condition: $a }' % regexp
if expected_result == SYNTAX_ERROR:
self.assertRaises(yara.SyntaxError, yara.compile, source=source)
else:
rule = yara.compile(source=source)
matches = rule.match(data=string)
if expected_result == SUCCEED:
self.assertTrue(matches)
_, _, matching_string = matches[0].strings[0]
if sys.version_info[0] >= 3:
self.assertTrue(matching_string == bytes(test[3], 'utf-8'))
else:
self.assertTrue(matching_string == test[3])
else:
self.assertFalse(matches)
def testBooleanOperators(self):
self.assertTrueRules([
'rule test { condition: true }',
'rule test { condition: true or false }',
'rule test { condition: true and true }',
'rule test { condition: 0x1 and 0x2}',
])
self.assertFalseRules([
'rule test { condition: false }',
'rule test { condition: true and false }',
'rule test { condition: false or false }'
])
def testComparisonOperators(self):
self.assertTrueRules([
'rule test { condition: 2 > 1 }',
'rule test { condition: 1 < 2 }',
'rule test { condition: 2 >= 1 }',
'rule test { condition: 1 <= 1 }',
'rule test { condition: 1 == 1 }',
'rule test { condition: 1.5 == 1.5}',
'rule test { condition: 1.0 == 1}',
'rule test { condition: 1.5 >= 1.0}',
'rule test { condition: 1.5 >= 1}',
'rule test { condition: 1.0 >= 1}',
'rule test { condition: 0.5 < 1}',
'rule test { condition: 0.5 <= 1}',
'rule rest { condition: 1.0 <= 1}',
'rule rest { condition: "abc" == "abc"}',
'rule rest { condition: "abc" <= "abc"}',
'rule rest { condition: "abc" >= "abc"}',
'rule rest { condition: "ab" < "abc"}',
'rule rest { condition: "abc" > "ab"}',
'rule rest { condition: "abc" < "abd"}',
'rule rest { condition: "abd" > "abc"}',
])
self.assertFalseRules([
'rule test { condition: 1 != 1}',
'rule test { condition: 1 != 1.0}',
'rule test { condition: 2 > 3}',
'rule test { condition: 2.1 < 2}',
'rule test { condition: "abc" != "abc"}',
'rule test { condition: "abc" > "abc"}',
'rule test { condition: "abc" < "abc"}',
])
def testArithmeticOperators(self):
self.assertTrueRules([
'rule test { condition: (1 + 1) * 2 == (9 - 1) \ 2 }',
'rule test { condition: 5 % 2 == 1 }',
'rule test { condition: 1.5 + 1.5 == 3}',
'rule test { condition: 3 \ 2 == 1}',
'rule test { condition: 3.0 \ 2 == 1.5}',
'rule test { condition: 1 + -1 == 0}',
'rule test { condition: -1 + -1 == -2}',
'rule test { condition: 4 --2 * 2 == 8}',
'rule test { condition: -1.0 * 1 == -1.0}',
'rule test { condition: 1-1 == 0}',
'rule test { condition: -2.0-3.0 == -5}',
'rule test { condition: --1 == 1}',
'rule test { condition: 1--1 == 2}',
'rule test { condition: -0x01 == -1}',
])
def testBitwiseOperators(self):
self.assertTrueRules([
'rule test { condition: 0x55 | 0xAA == 0xFF }',
'rule test { condition: ~0xAA ^ 0x5A & 0xFF == (~0xAA) ^ (0x5A & 0xFF) }',
'rule test { condition: ~0x55 & 0xFF == 0xAA }',
'rule test { condition: 8 >> 2 == 2 }',
'rule test { condition: 1 << 3 == 8 }',
'rule test { condition: 1 | 3 ^ 3 == 1 | (3 ^ 3) }'
])
self.assertFalseRules([
'rule test { condition: ~0xAA ^ 0x5A & 0xFF == 0x0F }',
'rule test { condition: 1 | 3 ^ 3 == (1 | 3) ^ 3}'
])
def testStrings(self):
self.assertTrueRules([
'rule test { strings: $a = "a" condition: $a }',
'rule test { strings: $a = "ab" condition: $a }',
'rule test { strings: $a = "abc" condition: $a }',
'rule test { strings: $a = "xyz" condition: $a }',
'rule test { strings: $a = "abc" nocase fullword condition: $a }',
'rule test { strings: $a = "aBc" nocase condition: $a }',
'rule test { strings: $a = "abc" fullword condition: $a }',
], "---- abc ---- xyz")
self.assertFalseRules([
'rule test { strings: $a = "a" fullword condition: $a }',
'rule test { strings: $a = "ab" fullword condition: $a }',
'rule test { strings: $a = "abc" wide fullword condition: $a }',
], "---- abc ---- xyz")
self.assertTrueRules([
'rule test { strings: $a = "a" wide condition: $a }',
'rule test { strings: $a = "a" wide ascii condition: $a }',
'rule test { strings: $a = "ab" wide condition: $a }',
'rule test { strings: $a = "ab" wide ascii condition: $a }',
'rule test { strings: $a = "abc" wide condition: $a }',
'rule test { strings: $a = "abc" wide nocase fullword condition: $a }',
'rule test { strings: $a = "aBc" wide nocase condition: $a }',
'rule test { strings: $a = "aBc" wide ascii nocase condition: $a }',
'rule test { strings: $a = "---xyz" wide nocase condition: $a }'
], "---- a\x00b\x00c\x00 -\x00-\x00-\x00-\x00x\x00y\x00z\x00")
self.assertTrueRules([
'rule test { strings: $a = "abc" fullword condition: $a }',
], "abc")
self.assertFalseRules([
'rule test { strings: $a = "abc" fullword condition: $a }',
], "xabcx")
self.assertFalseRules([
'rule test { strings: $a = "abc" fullword condition: $a }',
], "xabc")
self.assertFalseRules([
'rule test { strings: $a = "abc" fullword condition: $a }',
], "abcx")
self.assertFalseRules([
'rule test { strings: $a = "abc" ascii wide fullword condition: $a }',
], "abcx")
self.assertTrueRules([
'rule test { strings: $a = "abc" ascii wide fullword condition: $a }',
], "a\x00abc")
self.assertTrueRules([
'rule test { strings: $a = "abc" wide fullword condition: $a }',
], "a\x00b\x00c\x00")
self.assertFalseRules([
'rule test { strings: $a = "abc" wide fullword condition: $a }',
], "x\x00a\x00b\x00c\x00x\x00")
self.assertFalseRules([
'rule test { strings: $a = "ab" wide fullword condition: $a }',
], "x\x00a\x00b\x00")
self.assertFalseRules([
'rule test { strings: $a = "abc" wide fullword condition: $a }',
], "x\x00a\x00b\x00c\x00")
self.assertTrueRules([
'rule test { strings: $a = "abc" wide fullword condition: $a }',
], "x\x01a\x00b\x00c\x00")
self.assertTrueRules([
'rule test {\
strings:\
$a = "abcdef"\
$b = "cdef"\
$c = "ef"\
condition:\
all of them\
}'
], 'abcdef')
def testWildcardStrings(self):
self.assertTrueRules([
'rule test {\
strings:\
$s1 = "abc"\
$s2 = "xyz"\
condition:\
for all of ($*) : ($)\
}'
], "---- abc ---- A\x00B\x00C\x00 ---- xyz")
def testHexStrings(self):
self.assertTrueRules([
'rule test { strings: $a = { 64 01 00 00 60 01 } condition: $a }',
'rule test { strings: $a = { 64 0? 00 00 ?0 01 } condition: $a }',
'rule test { strings: $a = { 6? 01 00 00 60 0? } condition: $a }',
'rule test { strings: $a = { 64 01 [1-3] 60 01 } condition: $a }',
'rule test { strings: $a = { 64 01 [1-3] (60|61) 01 } condition: $a }',
'rule test { strings: $a = { 4D 5A [-] 6A 2A [-] 58 C3} condition: $a }',
'rule test { strings: $a = { 4D 5A [300-] 6A 2A [-] 58 C3} condition: $a }',
'rule test { strings: $a = { 2e 7? (65 | ??) 78 } condition: $a }'
], PE32_FILE)
self.assertFalseRules([
'rule test { strings: $a = { 4D 5A [0-300] 6A 2A } condition: $a }'
], PE32_FILE)
self.assertTrueRules([
'rule test { strings: $a = { 31 32 [-] 38 39 } condition: $a }',
'rule test { strings: $a = { 31 32 [-] 33 34 [-] 38 39 } condition: $a }',
'rule test { strings: $a = { 31 32 [1] 34 35 [2] 38 39 } condition: $a }',
'rule test { strings: $a = { 31 32 [1-] 34 35 [1-] 38 39 } condition: $a }',
'rule test { strings: $a = { 31 32 [0-3] 34 35 [1-] 38 39 } condition: $a }',
], '123456789')
self.assertTrueRules([
'rule test { strings: $a = { 31 32 [-] 38 39 } condition: all of them }',
], '123456789')
self.assertFalseRules([
'rule test { strings: $a = { 31 32 [-] 32 33 } condition: $a }',
'rule test { strings: $a = { 35 36 [-] 31 32 } condition: $a }',
'rule test { strings: $a = { 31 32 [2-] 34 35 } condition: $a }',
'rule test { strings: $a = { 31 32 [0-3] 37 38 } condition: $a }',
], '123456789')
self.assertSyntaxError([
'rule test { strings: $a = { 01 [0] 02 } condition: $a }',
'rule test { strings: $a = { [-] 01 02 } condition: $a }',
'rule test { strings: $a = { 01 02 [-] } condition: $a }',
'rule test { strings: $a = { 01 02 ([-] 03 | 04) } condition: $a }',
'rule test { strings: $a = { 01 02 (03 [-] | 04) } condition: $a }',
'rule test { strings: $a = { 01 02 (03 | 04 [-]) } condition: $a }'
])
rules = yara.compile(source='rule test { strings: $a = { 61 [0-3] (62|63) } condition: $a }')
matches = rules.match(data='abbb')
if sys.version_info[0] >= 3:
self.assertTrue(matches[0].strings == [(0, '$a', bytes('ab', 'utf-8'))])
else:
self.assertTrue(matches[0].strings == [(0, '$a', 'ab')])
def testCount(self):
self.assertTrueRules([
'rule test { strings: $a = "ssi" condition: #a == 2 }',
], 'mississippi')
def testAt(self):
self.assertTrueRules([
'rule test { strings: $a = "ssi" condition: $a at 2 and $a at 5 }',
'rule test { strings: $a = "mis" condition: $a at ~0xFF & 0xFF }'
], 'mississippi')
self.assertTrueRules([
'rule test { strings: $a = { 00 00 00 00 ?? 74 65 78 74 } condition: $a at 308}',
], PE32_FILE)
def testIn(self):
self.assertTrueRules([
'import "pe" rule test { strings: $a = { 6a 2a 58 c3 } condition: $a in (pe.entry_point .. pe.entry_point + 1) }',
], PE32_FILE)
def testOffset(self):
self.assertTrueRules([
'rule test { strings: $a = "ssi" condition: @a == 2 }',
'rule test { strings: $a = "ssi" condition: @a == @a[1] }',
'rule test { strings: $a = "ssi" condition: @a[2] == 5 }'
], 'mississippi')
def testLength(self):
self.assertTrueRules([
'rule test { strings: $a = /m.*?ssi/ condition: !a == 5 }',
'rule test { strings: $a = /m.*?ssi/ condition: !a[1] == 5 }',
'rule test { strings: $a = /m.*ssi/ condition: !a == 8 }',
'rule test { strings: $a = /m.*ssi/ condition: !a[1] == 8 }',
'rule test { strings: $a = /ssi.*ppi/ condition: !a[1] == 9 }',
'rule test { strings: $a = /ssi.*ppi/ condition: !a[2] == 6 }',
'rule test { strings: $a = { 6D [1-3] 73 73 69 } condition: !a == 5}',
'rule test { strings: $a = { 6D [-] 73 73 69 } condition: !a == 5}',
'rule test { strings: $a = { 6D [-] 70 70 69 } condition: !a == 11}',
'rule test { strings: $a = { 6D 69 73 73 [-] 70 69 } condition: !a == 11}',
], 'mississippi')
def testOf(self):
self.assertTrueRules([
'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: any of them }',
'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 1 of them }',
'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 2 of them }',
'rule test { strings: $a1 = "dummy1" $b1 = "dummy1" $b2 = "ssi" condition: any of ($a*, $b*) }',
], 'mississipi')
self.assertTrueRules(["""
rule test
{
strings:
$ = /abc/
$ = /def/
$ = /ghi/
condition:
for any of ($*) : ( for any i in (1..#): (uint8(@[i] - 1) == 0x00) )
}"""
], 'abc\x00def\x00ghi')
self.assertFalseRules([
'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: all of them }'
], 'mississipi')
def testFor(self):
self.assertTrueRules([
'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] >= 2 and @a[i] <= 5) }',
'rule test { strings: $a = "ssi" $b = "mi" condition: for all i in (1..#a) : ( for all j in (1..#b) : (@a[i] >= @b[j])) }'
], 'mississipi')
self.assertFalseRules([
'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] == 5) }',
], 'mississipi')
def testRE(self):
self.assertTrueRules([
'rule test { strings: $a = /ssi/ condition: $a }',
'rule test { strings: $a = /ssi(s|p)/ condition: $a }',
'rule test { strings: $a = /ssim*/ condition: $a }',
'rule test { strings: $a = /ssa?/ condition: $a }',
'rule test { strings: $a = /Miss/ nocase condition: $a }',
'rule test { strings: $a = /(M|N)iss/ nocase condition: $a }',
'rule test { strings: $a = /[M-N]iss/ nocase condition: $a }',
'rule test { strings: $a = /(Mi|ssi)ssippi/ nocase condition: $a }',
'rule test { strings: $a = /ppi\tmi/ condition: $a }',
'rule test { strings: $a = /ppi\.mi/ condition: $a }',
'rule test { strings: $a = /^mississippi/ fullword condition: $a }',
'rule test { strings: $a = /mississippi.*mississippi$/s condition: $a }',
], 'mississippi\tmississippi.mississippi\nmississippi')
self.assertFalseRules([
'rule test { strings: $a = /^ssi/ condition: $a }',
'rule test { strings: $a = /ssi$/ condition: $a }',
'rule test { strings: $a = /ssissi/ fullword condition: $a }',
'rule test { strings: $a = /^[isp]+/ condition: $a }'
], 'mississippi')
for test in RE_TESTS:
try:
self.runReTest(test)
except Exception as e:
print('\nFailed test: %s\n' % str(test))
raise e
def testEntrypoint(self):
self.assertTrueRules([
'rule test { strings: $a = { 6a 2a 58 c3 } condition: $a at entrypoint }',
], PE32_FILE)
self.assertTrueRules([
'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }',
], ELF32_FILE)
self.assertTrueRules([
'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }',
], ELF64_FILE)
self.assertFalseRules([
'rule test { condition: entrypoint >= 0 }',
])
def testFilesize(self):
self.assertTrueRules([
'rule test { condition: filesize == %d }' % len(PE32_FILE),
], PE32_FILE)
def testCompileFile(self):
f = tempfile.TemporaryFile('wt')
f.write('rule test { condition: true }')
f.flush()
f.seek(0)
r = yara.compile(file=f)
f.close()
self.assertTrue(r.match(data=PE32_FILE))
def testCompileFiles(self):
tmpdir = tempfile.gettempdir()
p1 = os.path.join(tmpdir, 'test1')
f1 = open(p1, 'wt')
f1.write('rule test1 { condition: true }')
f1.close()
p2 = os.path.join(tmpdir, 'test2')
t2 = open(p2, 'wt')
t2.write('rule test2 { condition: true }')
t2.close()
r = yara.compile(filepaths={
'test1': p1,
'test2': p2
})
self.assertTrue(len(r.match(data='dummy')) == 2)
for m in r.match(data='dummy'):
self.assertTrue(m.rule in ('test1', 'test2'))
self.assertTrue(m.namespace == m.rule)
os.remove(p1)
os.remove(p2)
def testIncludeFiles(self):
tmpdir = tempfile.gettempdir()
p1 = os.path.join(tmpdir, 'test1')
f1 = open(p1, 'wt')
f1.write('rule test1 { condition: true }')
f1.close()
p2 = os.path.join(tmpdir, 'test2')
f2 = open(p2, 'wt')
f2.write('include "%s" rule test2 { condition: test1 }' % p1)
f2.close()
r = yara.compile(p2)
self.assertTrue(len(r.match(data='dummy')) == 2)
def testExternals(self):
r = yara.compile(source='rule test { condition: ext_int == 15 }', externals={'ext_int': 15})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_int == -15}', externals={'ext_int': -15})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_float == 3.14 }', externals={'ext_float': 3.14})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_float == -0.5 }', externals={'ext_float': -0.5})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': True})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': ''})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': 'foo'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': False})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str contains "ssi" }', externals={'ext_str': 'mississippi'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': ''})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': 'FOO'})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /foo/i }', externals={'ext_str': 'FOO'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /ssi(s|p)/ }', externals={'ext_str': 'mississippi'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /ppi$/ }', externals={'ext_str': 'mississippi'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /^miss/ }', externals={'ext_str': 'mississippi'})
self.assertTrue(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /^iss/ }', externals={'ext_str': 'mississippi'})
self.assertFalse(r.match(data='dummy'))
r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'})
self.assertFalse(r.match(data='dummy'))
def testCallback(self):
global rule_data
rule_data = None
def callback(data):
global rule_data
rule_data = data
return yara.CALLBACK_CONTINUE
r = yara.compile(source='rule test { strings: $a = { 50 45 00 00 4c 01 } condition: $a }')
r.match(data=PE32_FILE, callback=callback)
self.assertTrue(rule_data['matches'])
self.assertTrue(rule_data['rule'] == 'test')
def testCompare(self):
r = yara.compile(sources={
'test1': 'rule test { condition: true}',
'test2': 'rule test { condition: true}'
})
m = r.match(data="dummy")
self.assertTrue(len(m) == 2)
self.assertTrue(m[0] < m[1])
self.assertTrue(m[0] != m[1])
self.assertFalse(m[0] > m[1])
self.assertFalse(m[0] == m[1])
def testComments(self):
self.assertTrueRules([
"""
rule test {
condition:
// this is a comment
/*** this is a comment ***/
/* /* /*
this is a comment
*/
true
}
""",
])
def testModules(self):
self.assertTrueRules([
'import "tests" rule test { condition: tests.constants.one + 1 == tests.constants.two }',
'import "tests" rule test { condition: tests.constants.foo == "foo" }',
'import "tests" rule test { condition: tests.constants.empty == "" }',
'import "tests" rule test { condition: tests.empty() == "" }',
'import "tests" rule test { condition: tests.struct_array[1].i == 1 }',
'import "tests" rule test { condition: tests.struct_array[0].i == 1 or true}',
'import "tests" rule test { condition: tests.integer_array[0] == 0}',
'import "tests" rule test { condition: tests.integer_array[1] == 1}',
'import "tests" rule test { condition: tests.string_array[0] == "foo"}',
'import "tests" rule test { condition: tests.string_array[2] == "baz"}',
'import "tests" rule test { condition: tests.string_dict["foo"] == "foo"}',
'import "tests" rule test { condition: tests.string_dict["bar"] == "bar"}',
'import "tests" rule test { condition: tests.isum(1,2) == 3}',
'import "tests" rule test { condition: tests.isum(1,2,3) == 6}',
'import "tests" rule test { condition: tests.fsum(1.0,2.0) == 3.0}',
'import "tests" rule test { condition: tests.fsum(1.0,2.0,3.0) == 6.0}',
'import "tests" rule test { condition: tests.length("dummy") == 5}',
])
self.assertFalseRules([
'import "tests" rule test { condition: tests.struct_array[0].i == 1 }',
'import "tests" rule test { condition: tests.isum(1,1) == 3}',
'import "tests" rule test { condition: tests.fsum(1.0,1.0) == 3.0}',
])
def testIntegerFunctions(self):
self.assertTrueRules([
'rule test { condition: uint8(0) == 0xAA}',
'rule test { condition: uint16(0) == 0xBBAA}',
'rule test { condition: uint32(0) == 0xDDCCBBAA}',
'rule test { condition: uint8be(0) == 0xAA}',
'rule test { condition: uint16be(0) == 0xAABB}',
'rule test { condition: uint32be(0) == 0xAABBCCDD}',
], b'\xAA\xBB\xCC\xDD')
def testStringIO(self):
# Python 2/3
try:
stream = StringIO.StringIO()
except:
stream = io.BytesIO()
r1 = yara.compile(source='rule test { condition: true }')
r1.save(file=stream)
stream.seek(0)
r2 = yara.load(file=stream)
m = r2.match(data="dummy")
self.assertTrue(len(m) == 1)
if __name__ == "__main__":
unittest.main()
/*
Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* headers */
#include <Python.h>
#include "structmember.h"
#if PY_VERSION_HEX >= 0x02060000
#include "bytesobject.h"
#elif PY_VERSION_HEX < 0x02060000
#define PyBytes_AsString PyString_AsString
#define PyBytes_Check PyString_Check
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
#endif
#include <time.h>
#include <yara.h>
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
#define PY_SSIZE_T_MAX INT_MAX
#define PY_SSIZE_T_MIN INT_MIN
#endif
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
#endif
#if PY_MAJOR_VERSION >= 3
#define PY_STRING(x) PyUnicode_FromString(x)
#define PY_STRING_TO_C(x) PyBytes_AsString(\
PyUnicode_AsEncodedString(x, "utf-8", "strict"))
#define PY_STRING_CHECK(x) PyUnicode_Check(x)
#else
#define PY_STRING(x) PyString_FromString(x)
#define PY_STRING_TO_C(x) PyString_AsString(x)
#define PY_STRING_CHECK(x) (PyString_Check(x) || PyUnicode_Check(x))
#endif
/* Module globals */
static PyObject* YaraError = NULL;
static PyObject* YaraSyntaxError = NULL;
static PyObject* YaraTimeoutError = NULL;
static PyObject* YaraWarningError = NULL;
#define YARA_DOC "\
This module allows you to apply YARA rules to files or strings.\n\
\n\
For complete documentation please visit:\n\
https://plusvic.github.io/yara\n"
// Match object
typedef struct
{
PyObject_HEAD
PyObject* rule;
PyObject* ns;
PyObject* tags;
PyObject* meta;
PyObject* strings;
} Match;
static PyMemberDef Match_members[] = {
{
"rule",
T_OBJECT_EX,
offsetof(Match, rule),
READONLY,
"Name of the matching rule"
},
{
"namespace",
T_OBJECT_EX,
offsetof(Match, ns),
READONLY,
"Namespace of the matching rule"
},
{
"tags",
T_OBJECT_EX,
offsetof(Match, tags),
READONLY,
"List of tags associated to the rule"
},
{
"meta",
T_OBJECT_EX,
offsetof(Match, meta),
READONLY,
"Dictionary with metadata associated to the rule"
},
{
"strings",
T_OBJECT_EX,
offsetof(Match, strings),
READONLY,
"Dictionary with offsets and strings that matched the file"
},
{ NULL } // End marker
};
static PyObject* Match_NEW(
const char* rule,
const char* ns,
PyObject* tags,
PyObject* meta,
PyObject* strings);
static void Match_dealloc(
PyObject* self);
static PyObject* Match_repr(
PyObject* self);
static PyObject* Match_getattro(
PyObject* self,
PyObject* name);
static PyObject* Match_richcompare(
PyObject* self,
PyObject* other,
int op);
static long Match_hash(
PyObject* self);
static PyMethodDef Match_methods[] =
{
{ NULL },
};
static PyTypeObject Match_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"yara.Match", /*tp_name*/
sizeof(Match), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Match_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
Match_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
Match_hash, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
Match_getattro, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Match class", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
Match_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Match_methods, /* tp_methods */
Match_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
// Rule object
typedef struct
{
PyObject_HEAD
PyObject* identifier;
PyObject* tags;
PyObject* meta;
} Rule;
static void Rule_dealloc(
PyObject* self);
static PyObject* Rule_getattro(
PyObject* self,
PyObject* name);
static PyMemberDef Rule_members[] = {
{
"identifier",
T_OBJECT_EX,
offsetof(Rule, identifier),
READONLY,
"Name of the rule"
},
{
"tags",
T_OBJECT_EX,
offsetof(Rule, tags),
READONLY,
"Tags for the rule"
},
{
"meta",
T_OBJECT_EX,
offsetof(Rule, meta),
READONLY,
"Meta for the rule"
},
{ NULL } // End marker
};
static PyMethodDef Rule_methods[] =
{
{ NULL, NULL }
};
static PyTypeObject Rule_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"yara.Rule", /*tp_name*/
sizeof(Rule), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) Rule_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
Rule_getattro, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Rule class", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Rule_methods, /* tp_methods */
Rule_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
// Rules object
typedef struct
{
PyObject_HEAD
PyObject* externals;
YR_RULES* rules;
YR_RULE* iter_current_rule;
} Rules;
static Rules* Rules_NEW(void);
static void Rules_dealloc(
PyObject* self);
static PyObject* Rules_match(
PyObject* self,
PyObject* args,
PyObject* keywords);
static PyObject* Rules_save(
PyObject* self,
PyObject* args,
PyObject* keywords);
static PyObject* Rules_profiling_info(
PyObject* self,
PyObject* args);
static PyObject* Rules_getattro(
PyObject* self,
PyObject* name);
static PyObject* Rules_next(
PyObject* self);
static PyMethodDef Rules_methods[] =
{
{
"match",
(PyCFunction) Rules_match,
METH_VARARGS | METH_KEYWORDS
},
{
"save",
(PyCFunction) Rules_save,
METH_VARARGS | METH_KEYWORDS
},
{
"profiling_info",
(PyCFunction) Rules_profiling_info,
METH_NOARGS
},
{
NULL,
NULL
}
};
static PyTypeObject Rules_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"yara.Rules", /*tp_name*/
sizeof(Rules), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) Rules_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
Rules_getattro, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Rules class", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc) Rules_next, /* tp_iternext */
Rules_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct _CALLBACK_DATA
{
PyObject* matches;
PyObject* callback;
PyObject* modules_data;
} CALLBACK_DATA;
int yara_callback(
int message,
void* message_data,
void* user_data)
{
YR_STRING* string;
YR_MATCH* m;
YR_META* meta;
YR_RULE* rule;
YR_MODULE_IMPORT* module_import;
const char* tag;
PyObject* tag_list = NULL;
PyObject* string_list = NULL;
PyObject* meta_list = NULL;
PyObject* match;
PyObject* callback_dict;
PyObject* object;
PyObject* tuple;
PyObject* matches = ((CALLBACK_DATA*) user_data)->matches;
PyObject* callback = ((CALLBACK_DATA*) user_data)->callback;
PyObject* modules_data = ((CALLBACK_DATA*) user_data)->modules_data;
PyObject* module_data;
PyObject* callback_result;
Py_ssize_t data_size;
PyGILState_STATE gil_state;
int result = CALLBACK_CONTINUE;
if (message == CALLBACK_MSG_SCAN_FINISHED)
return CALLBACK_CONTINUE;
if (message == CALLBACK_MSG_RULE_NOT_MATCHING && callback == NULL)
return CALLBACK_CONTINUE;
if (message == CALLBACK_MSG_IMPORT_MODULE)
{
if (modules_data == NULL)
return CALLBACK_CONTINUE;
module_import = (YR_MODULE_IMPORT*) message_data;
module_data = PyDict_GetItemString(
modules_data,
module_import->module_name);
#if PY_MAJOR_VERSION >= 3
if (module_data != NULL && PyBytes_Check(module_data))
#else
if (module_data != NULL && PyString_Check(module_data))
#endif
{
#if PY_MAJOR_VERSION >= 3
PyBytes_AsStringAndSize(
module_data,
(char**) &module_import->module_data,
&data_size);
#else
PyString_AsStringAndSize(
module_data,
(char**) &module_import->module_data,
&data_size);
#endif
module_import->module_data_size = data_size;
}
return CALLBACK_CONTINUE;
}
rule = (YR_RULE*) message_data;
gil_state = PyGILState_Ensure();
tag_list = PyList_New(0);
string_list = PyList_New(0);
meta_list = PyDict_New();
if (tag_list == NULL || string_list == NULL || meta_list == NULL)
{
Py_XDECREF(tag_list);
Py_XDECREF(string_list);
Py_XDECREF(meta_list);
PyGILState_Release(gil_state);
return CALLBACK_ERROR;
}
yr_rule_tags_foreach(rule, tag)
{
object = PY_STRING(tag);
PyList_Append(tag_list, object);
Py_DECREF(object);
}
yr_rule_metas_foreach(rule, meta)
{
if (meta->type == META_TYPE_INTEGER)
object = Py_BuildValue("i", meta->integer);
else if (meta->type == META_TYPE_BOOLEAN)
object = PyBool_FromLong(meta->integer);
else
object = PY_STRING(meta->string);
PyDict_SetItemString(meta_list, meta->identifier, object);
Py_DECREF(object);
}
yr_rule_strings_foreach(rule, string)
{
yr_string_matches_foreach(string, m)
{
object = PyBytes_FromStringAndSize((char*) m->data, m->length);
tuple = Py_BuildValue(
"(L,s,O)",
m->offset,
string->identifier,
object);
PyList_Append(string_list, tuple);
Py_DECREF(object);
Py_DECREF(tuple);
}
}
if (message == CALLBACK_MSG_RULE_MATCHING)
{
match = Match_NEW(
rule->identifier,
rule->ns->name,
tag_list,
meta_list,
string_list);
if (match != NULL)
{
PyList_Append(matches, match);
Py_DECREF(match);
}
else
{
Py_DECREF(tag_list);
Py_DECREF(string_list);
Py_DECREF(meta_list);
PyGILState_Release(gil_state);
return CALLBACK_ERROR;
}
}
if (callback != NULL)
{
Py_INCREF(callback);
callback_dict = PyDict_New();
object = PyBool_FromLong(message == CALLBACK_MSG_RULE_MATCHING);
PyDict_SetItemString(callback_dict, "matches", object);
Py_DECREF(object);
object = PY_STRING(rule->identifier);
PyDict_SetItemString(callback_dict, "rule", object);
Py_DECREF(object);
object = PY_STRING(rule->ns->name);
PyDict_SetItemString(callback_dict, "namespace", object);
Py_DECREF(object);
PyDict_SetItemString(callback_dict, "tags", tag_list);
PyDict_SetItemString(callback_dict, "meta", meta_list);
PyDict_SetItemString(callback_dict, "strings", string_list);
callback_result = PyObject_CallFunctionObjArgs(
callback,
callback_dict,
NULL);
if (callback_result != NULL)
{
#if PY_MAJOR_VERSION >= 3
if (PyLong_Check(callback_result))
#else
if (PyLong_Check(callback_result) || PyInt_Check(callback_result))
#endif
{
result = (int) PyLong_AsLong(callback_result);
}
Py_DECREF(callback_result);
}
else
{
result = CALLBACK_ERROR;
}
Py_DECREF(callback_dict);
Py_DECREF(callback);
}
Py_DECREF(tag_list);
Py_DECREF(string_list);
Py_DECREF(meta_list);
PyGILState_Release(gil_state);
return result;
}
/* YR_STREAM read method for "file-like objects" */
static size_t flo_read(
void* ptr,
size_t size,
size_t count,
void* user_data)
{
size_t i;
for (i = 0; i < count; i++)
{
PyGILState_STATE gil_state = PyGILState_Ensure();
PyObject* bytes = PyObject_CallMethod(
(PyObject*) user_data, "read", "n", (Py_ssize_t) size);
PyGILState_Release(gil_state);
if (bytes != NULL)
{
Py_ssize_t len;
char* buffer;
int result = PyBytes_AsStringAndSize(bytes, &buffer, &len);
if (result == -1 || (size_t) len < size)
{
Py_DECREF(bytes);
return i;
}
memcpy((char*) ptr + i * size, buffer, size);
Py_DECREF(bytes);
}
else
{
return i;
}
}
return count;
}
/* YR_STREAM write method for "file-like objects" */
static size_t flo_write(
const void* ptr,
size_t size,
size_t count,
void* user_data)
{
size_t i;
for (i = 0; i < count; i++)
{
PyGILState_STATE gil_state = PyGILState_Ensure();
PyObject* result = PyObject_CallMethod(
#if PY_MAJOR_VERSION >= 3
(PyObject*) user_data, "write", "y#", (char*) ptr + i * size, size);
#else
(PyObject*) user_data, "write", "s#", (char*) ptr + i * size, size);
#endif
PyGILState_Release(gil_state);
if (result == NULL)
return i;
Py_DECREF(result);
}
return count;
}
int process_compile_externals(
PyObject* externals,
YR_COMPILER* compiler)
{
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
char* identifier = NULL;
while (PyDict_Next(externals, &pos, &key, &value))
{
identifier = PY_STRING_TO_C(key);
if (PyBool_Check(value))
{
yr_compiler_define_boolean_variable(
compiler,
identifier,
PyObject_IsTrue(value));
}
#if PY_MAJOR_VERSION >= 3
else if (PyLong_Check(value))
#else
else if (PyLong_Check(value) || PyInt_Check(value))
#endif
{
yr_compiler_define_integer_variable(
compiler,
identifier,
PyLong_AsLong(value));
}
else if (PyFloat_Check(value))
{
yr_compiler_define_float_variable(
compiler,
identifier,
PyFloat_AsDouble(value));
}
else if (PY_STRING_CHECK(value))
{
yr_compiler_define_string_variable(
compiler,
identifier,
PY_STRING_TO_C(value));
}
else
{
return FALSE;
}
}
return TRUE;
}
int process_match_externals(
PyObject* externals,
YR_RULES* rules)
{
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
char* identifier = NULL;
while (PyDict_Next(externals, &pos, &key, &value))
{
identifier = PY_STRING_TO_C(key);
if (PyBool_Check(value))
{
yr_rules_define_boolean_variable(
rules,
identifier,
PyObject_IsTrue(value));
}
#if PY_MAJOR_VERSION >= 3
else if (PyLong_Check(value))
#else
else if (PyLong_Check(value) || PyInt_Check(value))
#endif
{
yr_rules_define_integer_variable(
rules,
identifier,
PyLong_AsLong(value));
}
else if (PyFloat_Check(value))
{
yr_rules_define_float_variable(
rules,
identifier,
PyFloat_AsDouble(value));
}
else if (PY_STRING_CHECK(value))
{
yr_rules_define_string_variable(
rules,
identifier,
PY_STRING_TO_C(value));
}
else
{
return FALSE;
}
}
return TRUE;
}
PyObject* handle_error(
int error,
char* extra)
{
switch(error)
{
case ERROR_COULD_NOT_ATTACH_TO_PROCESS:
return PyErr_Format(
YaraError,
"access denied");
case ERROR_INSUFICIENT_MEMORY:
return PyErr_NoMemory();
case ERROR_COULD_NOT_OPEN_FILE:
return PyErr_Format(
YaraError,
"could not open file \"%s\"",
extra);
case ERROR_COULD_NOT_MAP_FILE:
return PyErr_Format(
YaraError,
"could not map file \"%s\" into memory",
extra);
case ERROR_INVALID_FILE:
return PyErr_Format(
YaraError,
"invalid rules file \"%s\"",
extra);
case ERROR_CORRUPT_FILE:
return PyErr_Format(
YaraError,
"corrupt rules file \"%s\"",
extra);
case ERROR_SCAN_TIMEOUT:
return PyErr_Format(
YaraTimeoutError,
"scanning timed out");
default:
return PyErr_Format(
YaraError,
"internal error: %d",
error);
}
}
static PyObject* Match_NEW(
const char* rule,
const char* ns,
PyObject* tags,
PyObject* meta,
PyObject* strings)
{
Match* object = PyObject_NEW(Match, &Match_Type);
if (object != NULL)
{
object->rule = PY_STRING(rule);
object->ns = PY_STRING(ns);
object->tags = tags;
object->meta = meta;
object->strings = strings;
Py_INCREF(tags);
Py_INCREF(meta);
Py_INCREF(strings);
}
return (PyObject*) object;
}
static void Match_dealloc(
PyObject* self)
{
Match* object = (Match*) self;
Py_DECREF(object->rule);
Py_DECREF(object->ns);
Py_DECREF(object->tags);
Py_DECREF(object->meta);
Py_DECREF(object->strings);
PyObject_Del(self);
}
static PyObject* Match_repr(
PyObject* self)
{
Match* object = (Match*) self;
Py_INCREF(object->rule);
return object->rule;
}
static PyObject* Match_getattro(
PyObject* self,
PyObject* name)
{
return PyObject_GenericGetAttr(self, name);
}
static PyObject* Match_richcompare(
PyObject* self,
PyObject* other,
int op)
{
PyObject* result = NULL;
Match* a = (Match*) self;
Match* b = (Match*) other;
if(PyObject_TypeCheck(other, &Match_Type))
{
switch(op)
{
case Py_EQ:
if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ) &&
PyObject_RichCompareBool(a->ns, b->ns, Py_EQ))
result = Py_True;
else
result = Py_False;
Py_INCREF(result);
break;
case Py_NE:
if (PyObject_RichCompareBool(a->rule, b->rule, Py_NE) ||
PyObject_RichCompareBool(a->ns, b->ns, Py_NE))
result = Py_True;
else
result = Py_False;
Py_INCREF(result);
break;
case Py_LT:
case Py_LE:
case Py_GT:
case Py_GE:
if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ))
result = PyObject_RichCompare(a->ns, b->ns, op);
else
result = PyObject_RichCompare(a->rule, b->rule, op);
break;
}
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"'Match' objects must be compared with objects of the same class");
}
return result;
}
static long Match_hash(
PyObject* self)
{
Match* match = (Match*) self;
return PyObject_Hash(match->rule) + PyObject_Hash(match->ns);
}
////////////////////////////////////////////////////////////////////////////////
static void Rule_dealloc(
PyObject* self)
{
Rule* object = (Rule*) self;
Py_XDECREF(object->identifier);
Py_XDECREF(object->tags);
Py_XDECREF(object->meta);
PyObject_Del(self);
}
static PyObject* Rule_getattro(
PyObject* self,
PyObject* name)
{
return PyObject_GenericGetAttr(self, name);
}
static Rules* Rules_NEW(void)
{
Rules* rules = PyObject_NEW(Rules, &Rules_Type);
if (rules != NULL)
{
rules->rules = NULL;
rules->externals = NULL;
}
return rules;
}
static void Rules_dealloc(
PyObject* self)
{
Rules* object = (Rules*) self;
Py_XDECREF(object->externals);
if (object->rules != NULL)
yr_rules_destroy(object->rules);
PyObject_Del(self);
}
static PyObject* Rules_next(
PyObject* self)
{
PyObject* tag_list;
PyObject* object;
PyObject* meta_list;
YR_META* meta;
const char* tag;
Rule* rule;
Rules* rules = (Rules *) self;
// Generate new Rule object based upon iter_current_rule and increment
// iter_current_rule.
if (RULE_IS_NULL(rules->iter_current_rule))
{
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
rule = PyObject_NEW(Rule, &Rule_Type);
tag_list = PyList_New(0);
meta_list = PyDict_New();
if (rule != NULL && tag_list != NULL && meta_list != NULL)
{
yr_rule_tags_foreach(rules->iter_current_rule, tag)
{
object = PY_STRING(tag);
PyList_Append(tag_list, object);
Py_DECREF(object);
}
yr_rule_metas_foreach(rules->iter_current_rule, meta)
{
if (meta->type == META_TYPE_INTEGER)
object = Py_BuildValue("i", meta->integer);
else if (meta->type == META_TYPE_BOOLEAN)
object = PyBool_FromLong(meta->integer);
else
object = PY_STRING(meta->string);
PyDict_SetItemString(meta_list, meta->identifier, object);
Py_DECREF(object);
}
rule->identifier = PY_STRING(rules->iter_current_rule->identifier);
rule->tags = tag_list;
rule->meta = meta_list;
rules->iter_current_rule++;
return (PyObject*) rule;
}
else
{
Py_XDECREF(tag_list);
Py_XDECREF(meta_list);
return PyErr_Format(PyExc_TypeError, "Out of memory");
}
}
static PyObject* Rules_match(
PyObject* self,
PyObject* args,
PyObject* keywords)
{
static char* kwlist[] = {
"filepath", "pid", "data", "externals",
"callback", "fast", "timeout", "modules_data", NULL
};
char* filepath = NULL;
char* data = NULL;
int pid = 0;
int timeout = 0;
int length;
int error = ERROR_SUCCESS;
int fast_mode = FALSE;
PyObject* externals = NULL;
PyObject* fast = NULL;
Rules* object = (Rules*) self;
CALLBACK_DATA callback_data;
callback_data.matches = NULL;
callback_data.callback = NULL;
callback_data.modules_data = NULL;
if (PyArg_ParseTupleAndKeywords(
args,
keywords,
"|sis#OOOiO",
kwlist,
&filepath,
&pid,
&data,
&length,
&externals,
&callback_data.callback,
&fast,
&timeout,
&callback_data.modules_data))
{
if (filepath == NULL && data == NULL && pid == 0)
{
return PyErr_Format(
PyExc_TypeError,
"match() takes at least one argument");
}
if (callback_data.callback != NULL)
{
if (!PyCallable_Check(callback_data.callback))
{
return PyErr_Format(
PyExc_TypeError,
"'callback' must be callable");
}
}
if (callback_data.modules_data != NULL)
{
if (!PyDict_Check(callback_data.modules_data))
{
return PyErr_Format(
PyExc_TypeError,
"'modules_data' must be a dictionary");
}
}
if (externals != NULL && externals != Py_None)
{
if (PyDict_Check(externals))
{
if (!process_match_externals(externals, object->rules))
{
// Restore original externals provided during compiling.
process_match_externals(object->externals, object->rules);
return PyErr_Format(
PyExc_TypeError,
"external values must be of type integer, float, boolean or string");
}
}
else
{
return PyErr_Format(
PyExc_TypeError,
"'externals' must be a dictionary");
}
}
if (fast != NULL)
{
fast_mode = (PyObject_IsTrue(fast) == 1);
}
if (filepath != NULL)
{
callback_data.matches = PyList_New(0);
Py_BEGIN_ALLOW_THREADS
error = yr_rules_scan_file(
object->rules,
filepath,
fast_mode ? SCAN_FLAGS_FAST_MODE : 0,
yara_callback,
&callback_data,
timeout);
Py_END_ALLOW_THREADS
}
else if (data != NULL)
{
callback_data.matches = PyList_New(0);
Py_BEGIN_ALLOW_THREADS
error = yr_rules_scan_mem(
object->rules,
(unsigned char*) data,
(unsigned int) length,
fast_mode ? SCAN_FLAGS_FAST_MODE : 0,
yara_callback,
&callback_data,
timeout);
Py_END_ALLOW_THREADS
}
else if (pid != 0)
{
callback_data.matches = PyList_New(0);
Py_BEGIN_ALLOW_THREADS
error = yr_rules_scan_proc(
object->rules,
pid,
fast_mode ? SCAN_FLAGS_FAST_MODE : 0,
yara_callback,
&callback_data,
timeout);
Py_END_ALLOW_THREADS
}
// Restore original externals provided during compiling.
if (object->externals != NULL)
process_match_externals(object->externals, object->rules);
if (error != ERROR_SUCCESS)
{
Py_DECREF(callback_data.matches);
if (error == ERROR_CALLBACK_ERROR)
{
return NULL;
}
else
{
handle_error(error, filepath);
#ifdef PROFILING_ENABLED
PyObject* exception = PyErr_Occurred();
if (exception != NULL && error == ERROR_SCAN_TIMEOUT)
{
PyObject_SetAttrString(
exception,
"profiling_info",
Rules_profiling_info(self, NULL));
}
#endif
return NULL;
}
}
}
return callback_data.matches;
}
static PyObject* Rules_save(
PyObject* self,
PyObject* args,
PyObject* keywords)
{
static char* kwlist[] = {
"filepath", "file", NULL
};
char* filepath = NULL;
PyObject* file = NULL;
Rules* rules = (Rules*) self;
int error;
if (!PyArg_ParseTupleAndKeywords(
args,
keywords,
"|sO",
kwlist,
&filepath,
&file))
{
return NULL;
}
if (filepath != NULL)
{
Py_BEGIN_ALLOW_THREADS
error = yr_rules_save(rules->rules, filepath);
Py_END_ALLOW_THREADS
if (error != ERROR_SUCCESS)
return handle_error(error, filepath);
}
else if (file != NULL && PyObject_HasAttrString(file, "write"))
{
YR_STREAM stream;
stream.user_data = file;
stream.write = flo_write;
Py_BEGIN_ALLOW_THREADS;
error = yr_rules_save_stream(rules->rules, &stream);
Py_END_ALLOW_THREADS;
if (error != ERROR_SUCCESS)
return handle_error(error, "<file-like-object>");
}
else
{
return PyErr_Format(
PyExc_TypeError,
"load() expects either a file path or a file-like object");
}
Py_RETURN_NONE;
}
static PyObject* Rules_profiling_info(
PyObject* self,
PyObject* args)
{
#ifdef PROFILING_ENABLED
PyObject* object;
PyObject* result;
YR_RULES* rules = ((Rules*) self)->rules;
YR_RULE* rule;
YR_STRING* string;
char key[512];
uint64_t clock_ticks;
result = PyDict_New();
yr_rules_foreach(rules, rule)
{
clock_ticks = rule->clock_ticks;
yr_rule_strings_foreach(rule, string)
{
clock_ticks += string->clock_ticks;
}
snprintf(key, sizeof(key), "%s:%s", rule->ns->name, rule->identifier);
object = PyLong_FromLongLong(clock_ticks);
PyDict_SetItemString(result, key, object);
Py_DECREF(object);
}
return result;
#else
return PyErr_Format(YaraError, "libyara compiled without profiling support");
#endif
}
static PyObject* Rules_getattro(
PyObject* self,
PyObject* name)
{
return PyObject_GenericGetAttr(self, name);
}
void raise_exception_on_error(
int error_level,
const char* file_name,
int line_number,
const char* message,
void* user_data)
{
if (error_level == YARA_ERROR_LEVEL_ERROR)
{
if (file_name != NULL)
PyErr_Format(
YaraSyntaxError,
"%s(%d): %s",
file_name,
line_number,
message);
else
PyErr_Format(
YaraSyntaxError,
"%s",
message);
}
}
void raise_exception_on_error_or_warning(
int error_level,
const char* file_name,
int line_number,
const char* message,
void* user_data)
{
if (error_level == YARA_ERROR_LEVEL_ERROR)
{
if (file_name != NULL)
PyErr_Format(
YaraSyntaxError,
"%s(%d): %s",
file_name,
line_number,
message);
else
PyErr_Format(
YaraSyntaxError,
"%s",
message);
}
else
{
if (file_name != NULL)
PyErr_Format(
YaraWarningError,
"%s(%d): %s",
file_name,
line_number,
message);
else
PyErr_Format(
YaraWarningError,
"%s",
message);
}
}
////////////////////////////////////////////////////////////////////////////////
static PyObject* yara_compile(
PyObject* self,
PyObject* args,
PyObject* keywords)
{
static char *kwlist[] = {
"filepath", "source", "file", "filepaths", "sources",
"includes", "externals", "error_on_warning", NULL};
YR_COMPILER* compiler;
YR_RULES* yara_rules;
FILE* fh;
Rules* rules;
PyObject* key;
PyObject* value;
PyObject* result = NULL;
PyObject* file = NULL;
PyObject* sources_dict = NULL;
PyObject* filepaths_dict = NULL;
PyObject* includes = NULL;
PyObject* externals = NULL;
PyObject* error_on_warning = NULL;
Py_ssize_t pos = 0;
int fd;
int error = 0;
char* filepath = NULL;
char* source = NULL;
char* ns = NULL;
if (PyArg_ParseTupleAndKeywords(
args,
keywords,
"|ssOOOOOO",
kwlist,
&filepath,
&source,
&file,
&filepaths_dict,
&sources_dict,
&includes,
&externals,
&error_on_warning))
{
error = yr_compiler_create(&compiler);
if (error != ERROR_SUCCESS)
return handle_error(error, NULL);
yr_compiler_set_callback(compiler, raise_exception_on_error, NULL);
if (error_on_warning != NULL)
{
if (PyBool_Check(error_on_warning))
{
if (PyObject_IsTrue(error_on_warning) == 1)
{
yr_compiler_set_callback(
compiler,
raise_exception_on_error_or_warning,
NULL);
}
}
else
{
yr_compiler_destroy(compiler);
return PyErr_Format(
PyExc_TypeError,
"'error_on_warning' param must be of boolean type");
}
}
if (includes != NULL)
{
if (PyBool_Check(includes))
{
// PyObject_IsTrue can return -1 in case of error
compiler->allow_includes = (PyObject_IsTrue(includes) == 1);
}
else
{
yr_compiler_destroy(compiler);
return PyErr_Format(
PyExc_TypeError,
"'includes' param must be of boolean type");
}
}
if (externals != NULL && externals != Py_None)
{
if (PyDict_Check(externals))
{
if (!process_compile_externals(externals, compiler))
{
yr_compiler_destroy(compiler);
return PyErr_Format(
PyExc_TypeError,
"external values must be of type integer, float, boolean or string");
}
}
else
{
yr_compiler_destroy(compiler);
return PyErr_Format(
PyExc_TypeError,
"'externals' must be a dictionary");
}
}
if (filepath != NULL)
{
fh = fopen(filepath, "r");
if (fh != NULL)
{
error = yr_compiler_add_file(compiler, fh, NULL, filepath);
fclose(fh);
}
else
{
result = PyErr_SetFromErrno(YaraError);
}
}
else if (source != NULL)
{
error = yr_compiler_add_string(compiler, source, NULL);
}
else if (file != NULL)
{
fd = dup(PyObject_AsFileDescriptor(file));
fh = fdopen(fd, "r");
error = yr_compiler_add_file(compiler, fh, NULL, NULL);
fclose(fh);
}
else if (sources_dict != NULL)
{
if (PyDict_Check(sources_dict))
{
while (PyDict_Next(sources_dict, &pos, &key, &value))
{
source = PY_STRING_TO_C(value);
ns = PY_STRING_TO_C(key);
if (source != NULL && ns != NULL)
{
error = yr_compiler_add_string(compiler, source, ns);
if (error > 0)
break;
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"keys and values of the 'sources' dictionary must be "
"of string type");
break;
}
}
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"'sources' must be a dictionary");
}
}
else if (filepaths_dict != NULL)
{
if (PyDict_Check(filepaths_dict))
{
while (PyDict_Next(filepaths_dict, &pos, &key, &value))
{
filepath = PY_STRING_TO_C(value);
ns = PY_STRING_TO_C(key);
if (filepath != NULL && ns != NULL)
{
fh = fopen(filepath, "r");
if (fh != NULL)
{
error = yr_compiler_add_file(compiler, fh, ns, filepath);
fclose(fh);
if (error > 0)
break;
}
else
{
result = PyErr_SetFromErrno(YaraError);
break;
}
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"keys and values of the filepaths dictionary must be of "
"string type");
break;
}
}
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"filepaths must be a dictionary");
}
}
else
{
result = PyErr_Format(
PyExc_TypeError,
"compile() takes 1 argument");
}
if (PyErr_Occurred() == NULL)
{
rules = Rules_NEW();
if (rules != NULL)
{
Py_BEGIN_ALLOW_THREADS
error = yr_compiler_get_rules(compiler, &yara_rules);
Py_END_ALLOW_THREADS
if (error == ERROR_SUCCESS)
{
rules->rules = yara_rules;
rules->iter_current_rule = rules->rules->rules_list_head;
if (externals != NULL && externals != Py_None)
rules->externals = PyDict_Copy(externals);
result = (PyObject*) rules;
}
else
{
Py_DECREF(rules);
result = handle_error(error, NULL);
}
}
else
{
result = handle_error(ERROR_INSUFICIENT_MEMORY, NULL);
}
}
yr_compiler_destroy(compiler);
}
return result;
}
static PyObject* yara_load(
PyObject* self,
PyObject* args,
PyObject* keywords)
{
static char* kwlist[] = {
"filepath", "file", NULL
};
YR_EXTERNAL_VARIABLE* external;
Rules* rules = NULL;
PyObject* file = NULL;
char* filepath = NULL;
int error;
if (!PyArg_ParseTupleAndKeywords(
args,
keywords,
"|sO",
kwlist,
&filepath,
&file))
{
return NULL;
}
if (filepath != NULL)
{
rules = Rules_NEW();
if (rules == NULL)
return PyErr_NoMemory();
Py_BEGIN_ALLOW_THREADS;
error = yr_rules_load(filepath, &rules->rules);
Py_END_ALLOW_THREADS;
if (error != ERROR_SUCCESS)
{
Py_DECREF(rules);
return handle_error(error, filepath);
}
}
else if (file != NULL && PyObject_HasAttrString(file, "read"))
{
YR_STREAM stream;
stream.user_data = file;
stream.read = flo_read;
rules = Rules_NEW();
if (rules == NULL)
return PyErr_NoMemory();
Py_BEGIN_ALLOW_THREADS;
error = yr_rules_load_stream(&stream, &rules->rules);
Py_END_ALLOW_THREADS;
if (error != ERROR_SUCCESS)
{
Py_DECREF(rules);
return handle_error(error, "<file-like-object>");
}
}
else
{
return PyErr_Format(
PyExc_TypeError,
"load() expects either a file path or a file-like object");
}
external = rules->rules->externals_list_head;
rules->iter_current_rule = rules->rules->rules_list_head;
if (!EXTERNAL_VARIABLE_IS_NULL(external))
rules->externals = PyDict_New();
while (!EXTERNAL_VARIABLE_IS_NULL(external))
{
switch(external->type)
{
case EXTERNAL_VARIABLE_TYPE_BOOLEAN:
PyDict_SetItemString(
rules->externals,
external->identifier,
PyBool_FromLong((long) external->value.i));
break;
case EXTERNAL_VARIABLE_TYPE_INTEGER:
PyDict_SetItemString(
rules->externals,
external->identifier,
PyLong_FromLong((long) external->value.i));
break;
case EXTERNAL_VARIABLE_TYPE_FLOAT:
PyDict_SetItemString(
rules->externals,
external->identifier,
PyFloat_FromDouble(external->value.f));
break;
case EXTERNAL_VARIABLE_TYPE_STRING:
PyDict_SetItemString(
rules->externals,
external->identifier,
PY_STRING(external->value.s));
break;
}
external++;
}
return (PyObject*) rules;
}
void finalize(void)
{
yr_finalize();
}
static PyMethodDef yara_methods[] = {
{
"compile",
(PyCFunction) yara_compile,
METH_VARARGS | METH_KEYWORDS,
"Compiles a YARA rules file and returns an instance of class Rules"
},
{
"load",
(PyCFunction) yara_load,
METH_VARARGS | METH_KEYWORDS,
"Loads a previously saved YARA rules file and returns an instance of class Rules"
},
{ NULL, NULL }
};
#if PY_MAJOR_VERSION >= 3
#define MOD_ERROR_VAL NULL
#define MOD_SUCCESS_VAL(val) val
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#define MOD_DEF(ob, name, doc, methods) \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
ob = PyModule_Create(&moduledef);
#else
#define MOD_ERROR_VAL
#define MOD_SUCCESS_VAL(val)
#define MOD_INIT(name) void init##name(void)
#define MOD_DEF(ob, name, doc, methods) \
ob = Py_InitModule3(name, methods, doc);
#endif
MOD_INIT(yara)
{
PyObject* m;
MOD_DEF(m, "yara", YARA_DOC, yara_methods)
if (m == NULL)
return MOD_ERROR_VAL;
/* initialize module variables/constants */
PyModule_AddIntConstant(m, "CALLBACK_CONTINUE", 0);
PyModule_AddIntConstant(m, "CALLBACK_ABORT", 1);
PyModule_AddStringConstant(m, "__version__", YR_VERSION);
PyModule_AddStringConstant(m, "YARA_VERSION", YR_VERSION);
PyModule_AddIntConstant(m, "YARA_VERSION_HEX", YR_VERSION_HEX);
#if PYTHON_API_VERSION >= 1007
YaraError = PyErr_NewException("yara.Error", PyExc_Exception, NULL);
YaraSyntaxError = PyErr_NewException("yara.SyntaxError", YaraError, NULL);
YaraTimeoutError = PyErr_NewException("yara.TimeoutError", YaraError, NULL);
YaraWarningError = PyErr_NewException("yara.WarningError", YaraError, NULL);
#else
YaraError = Py_BuildValue("s", "yara.Error");
YaraSyntaxError = Py_BuildValue("s", "yara.SyntaxError");
YaraTimeoutError = Py_BuildValue("s", "yara.TimeoutError");
YaraWarningError = Py_BuildValue("s", "yara.WarningError");
#endif
if (PyType_Ready(&Rule_Type) < 0)
return MOD_ERROR_VAL;
if (PyType_Ready(&Rules_Type) < 0)
return MOD_ERROR_VAL;
if (PyType_Ready(&Match_Type) < 0)
return MOD_ERROR_VAL;
PyModule_AddObject(m, "Error", YaraError);
PyModule_AddObject(m, "SyntaxError", YaraSyntaxError);
PyModule_AddObject(m, "TimeoutError", YaraTimeoutError);
PyModule_AddObject(m, "WarningError", YaraWarningError);
if (yr_initialize() != ERROR_SUCCESS)
{
PyErr_SetString(YaraError, "initialization error");
return MOD_ERROR_VAL;
}
Py_AtExit(finalize);
return MOD_SUCCESS_VAL(m);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment