yara 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +15 -0
- data/History.txt +6 -0
- data/LICENSE.txt +165 -0
- data/README.rdoc +43 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/ext/yara_native/Match.c +191 -0
- data/ext/yara_native/Match.h +22 -0
- data/ext/yara_native/Rules.c +203 -0
- data/ext/yara_native/Rules.h +12 -0
- data/ext/yara_native/Yara_native.c +20 -0
- data/ext/yara_native/Yara_native.h +9 -0
- data/ext/yara_native/errors.c +11 -0
- data/ext/yara_native/errors.h +9 -0
- data/ext/yara_native/extconf.rb +14 -0
- data/lib/yara.rb +45 -0
- data/samples/ispe.rb +14 -0
- data/samples/upx.rb +39 -0
- data/spec/rules_spec.rb +208 -0
- data/spec/samples/DumpMem.exe +0 -0
- data/spec/samples/packers.yara +118 -0
- data/spec/samples/upx.yara +22 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/yara_spec.rb +8 -0
- metadata +169 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
#include "errors.h"
|
2
|
+
#include "Rules.h"
|
3
|
+
#include "Match.h"
|
4
|
+
#include <stdio.h>
|
5
|
+
|
6
|
+
static VALUE class_Rules = Qnil;
|
7
|
+
|
8
|
+
void rules_mark(YARA_CONTEXT *ctx) { }
|
9
|
+
|
10
|
+
void rules_free(YARA_CONTEXT *ctx) {
|
11
|
+
yr_destroy_context(ctx);
|
12
|
+
}
|
13
|
+
|
14
|
+
VALUE rules_allocate(VALUE klass) {
|
15
|
+
YARA_CONTEXT *ctx = yr_create_context();
|
16
|
+
|
17
|
+
return Data_Wrap_Struct(klass, rules_mark, rules_free, ctx);
|
18
|
+
}
|
19
|
+
|
20
|
+
VALUE rules_compile_file(VALUE self, VALUE rb_fname) {
|
21
|
+
FILE * file;
|
22
|
+
char * fname;
|
23
|
+
YARA_CONTEXT *ctx;
|
24
|
+
char error_message[256];
|
25
|
+
|
26
|
+
Check_Type(rb_fname, T_STRING);
|
27
|
+
fname = RSTRING_PTR(rb_fname);
|
28
|
+
|
29
|
+
if( !(file=fopen(fname, "r")) ) {
|
30
|
+
rb_raise(error_CompileError, "No such file: %s", fname);
|
31
|
+
} else {
|
32
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
33
|
+
|
34
|
+
if( yr_compile_file(file, ctx) != 0 ) {
|
35
|
+
yr_get_error_message(ctx, error_message, sizeof(error_message));
|
36
|
+
fclose(file);
|
37
|
+
rb_raise(error_CompileError, "Syntax Error - %s(%d): %s", fname, ctx->last_error_line, error_message);
|
38
|
+
}
|
39
|
+
|
40
|
+
yr_push_file_name(ctx, fname);
|
41
|
+
fclose(file);
|
42
|
+
return Qtrue;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE rules_compile_string(VALUE self, VALUE rb_rules) {
|
47
|
+
YARA_CONTEXT *ctx;
|
48
|
+
char *rules;
|
49
|
+
char error_message[256];
|
50
|
+
|
51
|
+
Check_Type(rb_rules, T_STRING);
|
52
|
+
rules = RSTRING_PTR(rb_rules);
|
53
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
54
|
+
|
55
|
+
if( yr_compile_string(rules, ctx) != 0) {
|
56
|
+
yr_get_error_message(ctx, error_message, sizeof(error_message));
|
57
|
+
rb_raise(error_CompileError, "Syntax Error - line(%d): %s", ctx->last_error_line, error_message);
|
58
|
+
}
|
59
|
+
|
60
|
+
return Qtrue;
|
61
|
+
}
|
62
|
+
|
63
|
+
VALUE rules_weight(VALUE self) {
|
64
|
+
YARA_CONTEXT *ctx;
|
65
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
66
|
+
return INT2NUM(yr_calculate_rules_weight(ctx));
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
VALUE rules_current_namespace(VALUE self) {
|
71
|
+
YARA_CONTEXT *ctx;
|
72
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
73
|
+
if(ctx->current_namespace && ctx->current_namespace->name)
|
74
|
+
return rb_str_new2(ctx->current_namespace->name);
|
75
|
+
else
|
76
|
+
return Qnil;
|
77
|
+
}
|
78
|
+
|
79
|
+
VALUE rules_namespaces(VALUE self) {
|
80
|
+
YARA_CONTEXT *ctx;
|
81
|
+
NAMESPACE *ns;
|
82
|
+
VALUE ary = rb_ary_new();
|
83
|
+
|
84
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
85
|
+
ns = ctx->namespaces;
|
86
|
+
while(ns && ns->name) {
|
87
|
+
rb_ary_push(ary, rb_str_new2(ns->name));
|
88
|
+
ns = ns->next;
|
89
|
+
}
|
90
|
+
return ary;
|
91
|
+
}
|
92
|
+
|
93
|
+
NAMESPACE * find_namespace(YARA_CONTEXT *ctx, const char *name) {
|
94
|
+
NAMESPACE *ns = ctx->namespaces;
|
95
|
+
|
96
|
+
while(ns && ns->name) {
|
97
|
+
if(strcmp(name, ns->name) == 0)
|
98
|
+
return(ns);
|
99
|
+
else
|
100
|
+
ns = ns->next;
|
101
|
+
}
|
102
|
+
return (NAMESPACE*) NULL;
|
103
|
+
}
|
104
|
+
|
105
|
+
VALUE rules_set_namespace(VALUE self, VALUE rb_namespace) {
|
106
|
+
YARA_CONTEXT *ctx;
|
107
|
+
NAMESPACE *ns = NULL;
|
108
|
+
const char *name;
|
109
|
+
|
110
|
+
Check_Type(rb_namespace, T_STRING);
|
111
|
+
name = RSTRING_PTR(rb_namespace);
|
112
|
+
|
113
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
114
|
+
|
115
|
+
if (!(ns = find_namespace(ctx, name)))
|
116
|
+
ns = yr_create_namespace(ctx, name);
|
117
|
+
|
118
|
+
if (ns) {
|
119
|
+
ctx->current_namespace = ns;
|
120
|
+
return rb_namespace;
|
121
|
+
} else {
|
122
|
+
return Qnil;
|
123
|
+
}
|
124
|
+
|
125
|
+
}
|
126
|
+
|
127
|
+
static int
|
128
|
+
scan_callback(RULE *rule, unsigned char *buffer, unsigned int buffer_size, void *data) {
|
129
|
+
int match_ret;
|
130
|
+
VALUE match = Qnil;
|
131
|
+
VALUE results = *((VALUE *) data);
|
132
|
+
|
133
|
+
Check_Type(results, T_ARRAY);
|
134
|
+
|
135
|
+
match_ret = Match_NEW_from_rule(rule, buffer, &match);
|
136
|
+
if(match_ret == 0 && !NIL_P(match))
|
137
|
+
rb_ary_push(results,match);
|
138
|
+
|
139
|
+
return match_ret;
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
VALUE rules_scan_file(VALUE self, VALUE rb_fname) {
|
144
|
+
YARA_CONTEXT *ctx;
|
145
|
+
VALUE results;
|
146
|
+
unsigned int ret;
|
147
|
+
char *fname;
|
148
|
+
|
149
|
+
Check_Type(rb_fname, T_STRING);
|
150
|
+
results = rb_ary_new();
|
151
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
152
|
+
fname = RSTRING_PTR(rb_fname);
|
153
|
+
|
154
|
+
ret = yr_scan_file(fname, ctx, scan_callback, &results);
|
155
|
+
if (ret == ERROR_COULD_NOT_OPEN_FILE)
|
156
|
+
rb_raise(error_ScanError, "Could not open file: '%s'", fname);
|
157
|
+
else if (ret != 0)
|
158
|
+
rb_raise(error_ScanError, "A error occurred while scanning: %s",
|
159
|
+
((ret > MAX_SCAN_ERROR)? "unknown error" : SCAN_ERRORS[ret]));
|
160
|
+
|
161
|
+
return results;
|
162
|
+
}
|
163
|
+
|
164
|
+
VALUE rules_scan_string(VALUE self, VALUE rb_dat) {
|
165
|
+
YARA_CONTEXT *ctx;
|
166
|
+
VALUE results;
|
167
|
+
char *buf;
|
168
|
+
long buflen;
|
169
|
+
int ret;
|
170
|
+
|
171
|
+
Check_Type(rb_dat, T_STRING);
|
172
|
+
buf = RSTRING_PTR(rb_dat);
|
173
|
+
buflen = RSTRING_LEN(rb_dat);
|
174
|
+
|
175
|
+
results = rb_ary_new();
|
176
|
+
|
177
|
+
Data_Get_Struct(self, YARA_CONTEXT, ctx);
|
178
|
+
|
179
|
+
ret = yr_scan_mem(buf, buflen, ctx, scan_callback, &results);
|
180
|
+
if (ret != 0)
|
181
|
+
rb_raise(error_ScanError, "A error occurred while scanning: %s",
|
182
|
+
((ret > MAX_SCAN_ERROR)? "unknown error" : SCAN_ERRORS[ret]));
|
183
|
+
|
184
|
+
return results;
|
185
|
+
}
|
186
|
+
|
187
|
+
void init_rules(VALUE rb_ns) {
|
188
|
+
|
189
|
+
class_Rules = rb_define_class_under(rb_ns, "Rules", rb_cObject);
|
190
|
+
rb_define_alloc_func(class_Rules, rules_allocate);
|
191
|
+
|
192
|
+
rb_define_method(class_Rules, "compile_file", rules_compile_file, 1);
|
193
|
+
rb_define_method(class_Rules, "compile_string", rules_compile_string, 1);
|
194
|
+
rb_define_method(class_Rules, "weight", rules_weight, 0);
|
195
|
+
rb_define_method(class_Rules, "current_namespace", rules_current_namespace, 0);
|
196
|
+
rb_define_method(class_Rules, "namespaces", rules_namespaces, 0);
|
197
|
+
rb_define_method(class_Rules, "set_namespace", rules_set_namespace, 1);
|
198
|
+
rb_define_method(class_Rules, "scan_file", rules_scan_file, 1);
|
199
|
+
rb_define_method(class_Rules, "scan_string", rules_scan_string, 1);
|
200
|
+
|
201
|
+
init_match(rb_ns);
|
202
|
+
}
|
203
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <yara.h>
|
3
|
+
|
4
|
+
#include "Yara_native.h"
|
5
|
+
#include "Rules.h"
|
6
|
+
#include "errors.h"
|
7
|
+
|
8
|
+
static VALUE module_Yara = Qnil;
|
9
|
+
|
10
|
+
void Init_yara_native() {
|
11
|
+
yr_init();
|
12
|
+
|
13
|
+
module_Yara = rb_define_module("Yara");
|
14
|
+
|
15
|
+
init_errors(module_Yara);
|
16
|
+
init_rules(module_Yara);
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#include "errors.h"
|
2
|
+
#include "ruby.h"
|
3
|
+
|
4
|
+
VALUE error_CompileError = Qnil;
|
5
|
+
VALUE error_ScanError = Qnil;
|
6
|
+
|
7
|
+
void
|
8
|
+
init_errors(VALUE rb_ns) {
|
9
|
+
error_CompileError = rb_define_class_under(rb_ns, "CompileError", rb_eStandardError);
|
10
|
+
error_ScanError = rb_define_class_under(rb_ns, "ScanError", rb_eStandardError);
|
11
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
extension_name = "yara_native"
|
5
|
+
|
6
|
+
dir_config(extension_name)
|
7
|
+
|
8
|
+
unless have_library("yara") and
|
9
|
+
find_header("yara.h", "/usr/local/include")
|
10
|
+
raise "You must install the yara library"
|
11
|
+
end
|
12
|
+
|
13
|
+
create_makefile(extension_name)
|
14
|
+
|
data/lib/yara.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
require 'yara_native'
|
3
|
+
|
4
|
+
module Yara
|
5
|
+
class Rules
|
6
|
+
end
|
7
|
+
|
8
|
+
class Match
|
9
|
+
def to_hash
|
10
|
+
{ :rule => self.rule,
|
11
|
+
:namespace => self.namespace,
|
12
|
+
:tags => self.tags,
|
13
|
+
:meta => self.meta,
|
14
|
+
:strings => self.strings }
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
h=to_hash
|
19
|
+
h.inspect
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class MatchString
|
24
|
+
|
25
|
+
alias ident identifier
|
26
|
+
|
27
|
+
def <=>(other)
|
28
|
+
self.offset <=> other.offset
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_a
|
32
|
+
[self.offset, self.ident, self.buffer]
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_hash
|
36
|
+
{ :offset => self.offset, :identifier => self.ident, :buffer => self.buffer}
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
h=to_a
|
41
|
+
h.inspect
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/samples/ispe.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Usage example:
|
4
|
+
# ruby ispe.rb /win_c/windows/system32/*.???
|
5
|
+
#
|
6
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
7
|
+
require 'yara'
|
8
|
+
|
9
|
+
ctx = Yara::Rules.new
|
10
|
+
ctx.compile_string "rule IsPE { condition: uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 }"
|
11
|
+
|
12
|
+
ARGV.each do |fname|
|
13
|
+
ctx.scan_file(fname).each {|match| puts "#{fname} -> #{match.rule}" }
|
14
|
+
end
|
data/samples/upx.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << 'lib'
|
4
|
+
require 'yara'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
rule_str = <<_EOF_
|
8
|
+
rule UPX {
|
9
|
+
strings:
|
10
|
+
$noep1 = { B8 ?? ?? ?? ?? B9 ?? ?? ?? ?? 33 D2 EB 01 0F 56 EB 01 0F E8 03 00 00 00 EB 01 0F EB 01 0F 5E EB 01 }
|
11
|
+
$noep2 = { 5E 89 F7 B9 ?? ?? ?? ?? 8A 07 47 2C E8 3C 01 77 F7 80 3F ?? 75 F2 8B 07 8A 5F 04 66 C1 E8 08 C1 C0 10 86 C4 29 F8 80 EB E8 01 F0 89 07 83 C7 }
|
12
|
+
$noep3 = { 01 DB [0-1] 07 8B 1E 83 EE FC 11 DB [1-4] B8 01 00 00 00 01 DB }
|
13
|
+
$noep4 = { 9C 60 E8 00 00 00 00 5D B8 B3 85 40 00 2D AC 85 40 00 2B E8 8D B5 D5 FE FF FF 8B 06 83 F8 00 74 11 8D B5 E1 FE FF FF 8B 06 83 F8 01 0F 84 F1 }
|
14
|
+
$noep5 = { 8A 06 46 88 07 47 01 DB 75 07 8B 1E 83 EE FC 11 DB }
|
15
|
+
$noep6 = { FF D5 80 A7 ?? ?? ?? ?? ?? 58 50 54 50 53 57 FF D5 58 61 8D 44 24 ?? 6A 00 39 C4 75 FA 83 EC 80 E9 }
|
16
|
+
$noep7 = { 55 FF 96 ?? ?? ?? ?? 09 C0 74 07 89 03 83 C3 04 EB ?? FF 96 ?? ?? ?? ?? 8B AE ?? ?? ?? ?? 8D BE 00 F0 FF FF BB 00 10 00 00 50 54 6A 04 53 57 }
|
17
|
+
$noep8 = { FF D5 8D 87 ?? ?? ?? ?? 80 20 ?? 80 60 ?? ?? 58 50 54 50 53 57 FF D5 58 61 8D 44 24 ?? 6A 00 39 C4 75 FA 83 EC 80 E9 }
|
18
|
+
$ep1 = { 60 E8 00 00 00 00 58 83 E8 3D }
|
19
|
+
$ep2 = { 60 E8 00 00 00 00 83 CD FF 31 DB 5E }
|
20
|
+
$ep3 = { 50 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 83 CD }
|
21
|
+
|
22
|
+
condition: any of ($noep*) or for any of ($ep*) : ($ at entrypoint)
|
23
|
+
}
|
24
|
+
_EOF_
|
25
|
+
|
26
|
+
ctx = Yara::Rules.new
|
27
|
+
ctx.compile_string rule_str
|
28
|
+
|
29
|
+
ARGV.each do |fname|
|
30
|
+
begin
|
31
|
+
ctx.scan_file(fname).each do |match|
|
32
|
+
pp match
|
33
|
+
end
|
34
|
+
rescue Yara::ScanError => e
|
35
|
+
STDERR.puts e
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
data/spec/rules_spec.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Yara::Rules do
|
4
|
+
it "should be a class" do
|
5
|
+
Yara::Rules.should be_kind_of(Class)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should initialize cleanly" do
|
9
|
+
lambda { Yara::Rules.new }.should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
context "Instances" do
|
13
|
+
before(:each) do
|
14
|
+
@rules = Yara::Rules.new
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should indicate rules weight" do
|
18
|
+
@rules.weight.should be_kind_of(Numeric)
|
19
|
+
@rules.weight.should == 0
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should compile a file" do
|
23
|
+
lambda { @rules.compile_file(sample_file("upx.yara")) }.should_not raise_error
|
24
|
+
@rules.weight.should > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should compile an empty file" do
|
28
|
+
lambda { @rules.compile_file("/dev/null") }.should_not raise_error
|
29
|
+
@rules.weight.should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it "should raise an error if compiling an invalid filename" do
|
34
|
+
lambda { @rules.compile_file("so totally bogus a file") }.should raise_error
|
35
|
+
@rules.weight.should == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise an error if compiling a file with bad syntax" do
|
39
|
+
lambda { @rules.compile_file(__FILE__) }.should raise_error(Yara::CompileError)
|
40
|
+
@rules.weight.should == 0
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise an error if duplicate file data is compiled" do
|
44
|
+
lambda { @rules.compile_file(sample_file("upx.yara")) }.should_not raise_error
|
45
|
+
lambda { @rules.compile_file(sample_file("upx.yara")) }.should raise_error(Yara::CompileError)
|
46
|
+
@rules.weight.should > 0
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should compile a string" do
|
50
|
+
rules = File.read(sample_file("upx.yara"))
|
51
|
+
lambda { @rules.compile_string(rules) }.should_not raise_error
|
52
|
+
@rules.weight.should > 0
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should compile an empty string" do
|
56
|
+
lambda { @rules.compile_string("") }.should_not raise_error
|
57
|
+
@rules.weight.should == 0
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should raise an error if compiling a string with bad syntax" do
|
61
|
+
rules = File.read(sample_file("upx.yara")) << "some bogus stuff\n"
|
62
|
+
lambda { @rules.compile_string(rules) }.should raise_error(Yara::CompileError)
|
63
|
+
@rules.weight.should > 0 # it parsed everything up to the error
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should raise an error if duplicate string data is compiled" do
|
67
|
+
rules = File.read(sample_file("upx.yara"))
|
68
|
+
lambda { @rules.compile_string(rules) }.should_not raise_error
|
69
|
+
lambda { @rules.compile_string(rules) }.should raise_error(Yara::CompileError)
|
70
|
+
@rules.weight.should > 0
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should indicate the current namespace" do
|
74
|
+
@rules.current_namespace.should be_kind_of(String)
|
75
|
+
@rules.current_namespace.should == "default"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should indicate all known namespaces" do
|
79
|
+
@rules.namespaces.should be_kind_of(Array)
|
80
|
+
@rules.namespaces.should == ["default"]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should support setting a new namespace" do
|
84
|
+
@rules.namespaces.should be_kind_of(Array)
|
85
|
+
@rules.namespaces.should == ["default"]
|
86
|
+
|
87
|
+
@rules.set_namespace("a_new_namespace").should == "a_new_namespace"
|
88
|
+
@rules.current_namespace.should == "a_new_namespace"
|
89
|
+
@rules.namespaces.should be_kind_of(Array)
|
90
|
+
@rules.namespaces.should == ["a_new_namespace", "default"]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should not create duplicate namespaces" do
|
94
|
+
@rules.namespaces.should be_kind_of(Array)
|
95
|
+
@rules.namespaces.should == ["default"]
|
96
|
+
|
97
|
+
@rules.set_namespace("a_new_namespace").should == "a_new_namespace"
|
98
|
+
@rules.current_namespace.should == "a_new_namespace"
|
99
|
+
@rules.namespaces.should be_kind_of(Array)
|
100
|
+
@rules.namespaces.should == ["a_new_namespace", "default"]
|
101
|
+
|
102
|
+
@rules.set_namespace("default").should == "default"
|
103
|
+
@rules.current_namespace.should == "default"
|
104
|
+
@rules.namespaces.should == ["a_new_namespace", "default"]
|
105
|
+
|
106
|
+
@rules.set_namespace("a_new_namespace").should == "a_new_namespace"
|
107
|
+
@rules.current_namespace.should == "a_new_namespace"
|
108
|
+
@rules.namespaces.should == ["a_new_namespace", "default"]
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should scan a file" do
|
112
|
+
@rules.compile_file(sample_file("packers.yara"))
|
113
|
+
@rules.weight.should > 0
|
114
|
+
results = @rules.scan_file(sample_file("DumpMem.exe"))
|
115
|
+
results.should be_kind_of(Array)
|
116
|
+
results.size.should == 1
|
117
|
+
m = results.first
|
118
|
+
m.should be_kind_of(Yara::Match)
|
119
|
+
m.should be_frozen
|
120
|
+
|
121
|
+
m.rule.should == "UPX"
|
122
|
+
m.rule.should be_frozen
|
123
|
+
|
124
|
+
m.namespace.should == "default"
|
125
|
+
m.namespace.should be_frozen
|
126
|
+
|
127
|
+
m.tags.should == ["compression", "packer", "shady"]
|
128
|
+
m.tags.should be_frozen
|
129
|
+
m.tags.map{|v| v.should be_frozen }
|
130
|
+
|
131
|
+
strings = m.strings.sort
|
132
|
+
strings.each do |ms|
|
133
|
+
ms.should be_kind_of(Yara::MatchString)
|
134
|
+
ms.identifier.should be_frozen
|
135
|
+
ms.buffer.should be_frozen
|
136
|
+
end
|
137
|
+
|
138
|
+
strings.map{|ms| [ms.offset, ms.identifier, md5(ms.buffer)] }.should == [
|
139
|
+
[2824, "$noep5", "af79592a2fc536596fcbe87409734626"],
|
140
|
+
[2830, "$noep3", "04b044f4bfeb6899b6b60ff7d6b1d103"],
|
141
|
+
[3010, "$noep2", "8711f47b104922246e5733211cd832b1"],
|
142
|
+
[3110, "$noep7", "71be53d1049f47219ad8f26a77255229"],
|
143
|
+
[3157, "$noep8", "b9beede7f0d05ee657501cea72e1a453"]
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should raise an error if scanning an invalid file" do
|
148
|
+
@rules.compile_file(sample_file("packers.yara"))
|
149
|
+
@rules.weight.should > 0
|
150
|
+
lambda { @rules.scan_file(sample_file("not a real file at all")) }.should raise_error(Yara::ScanError)
|
151
|
+
lambda { @rules.scan_file(Object.new)}.should raise_error(TypeError)
|
152
|
+
lambda { @rules.scan_file(nil)}.should raise_error(TypeError)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should raise an error if scanning a zero-length file" do
|
156
|
+
@rules.compile_file(sample_file("packers.yara"))
|
157
|
+
@rules.weight.should > 0
|
158
|
+
lambda { @rules.scan_file("/dev/null")}.should raise_error(Yara::ScanError)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should scan a string" do
|
162
|
+
@rules.compile_file(sample_file("packers.yara"))
|
163
|
+
@rules.weight.should > 0
|
164
|
+
results = @rules.scan_string(File.read(sample_file("DumpMem.exe")))
|
165
|
+
results.should be_kind_of(Array)
|
166
|
+
results.size.should == 1
|
167
|
+
m = results.first
|
168
|
+
m.should be_kind_of(Yara::Match)
|
169
|
+
m.should be_frozen
|
170
|
+
|
171
|
+
m.rule.should == "UPX"
|
172
|
+
m.rule.should be_frozen
|
173
|
+
|
174
|
+
m.namespace.should == "default"
|
175
|
+
m.namespace.should be_frozen
|
176
|
+
|
177
|
+
m.tags.should == ["compression", "packer", "shady"]
|
178
|
+
m.tags.should be_frozen
|
179
|
+
m.tags.map{|v| v.should be_frozen }
|
180
|
+
|
181
|
+
m.strings.should be_frozen
|
182
|
+
strings = m.strings.sort
|
183
|
+
strings.each do |ms|
|
184
|
+
ms.should be_kind_of(Yara::MatchString)
|
185
|
+
ms.identifier.should be_frozen
|
186
|
+
ms.buffer.should be_frozen
|
187
|
+
end
|
188
|
+
|
189
|
+
strings.map{|ms| [ms.offset, ms.identifier, md5(ms.buffer)] }.should == [
|
190
|
+
[2824, "$noep5", "af79592a2fc536596fcbe87409734626"],
|
191
|
+
[2830, "$noep3", "04b044f4bfeb6899b6b60ff7d6b1d103"],
|
192
|
+
[3010, "$noep2", "8711f47b104922246e5733211cd832b1"],
|
193
|
+
[3110, "$noep7", "71be53d1049f47219ad8f26a77255229"],
|
194
|
+
[3157, "$noep8", "b9beede7f0d05ee657501cea72e1a453"]
|
195
|
+
]
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should raise an error if scanning an invalid string" do
|
200
|
+
@rules.compile_file(sample_file("packers.yara"))
|
201
|
+
@rules.weight.should > 0
|
202
|
+
lambda { @rules.scan_string(Object.new)}.should raise_error(TypeError)
|
203
|
+
lambda { @rules.scan_string(nil)}.should raise_error(TypeError)
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
Binary file
|