yara-ffi 2.1.1 → 3.0.0
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.
- checksums.yaml +4 -4
- data/Dockerfile +17 -0
- data/Gemfile.lock +3 -3
- data/README.md +28 -1
- data/lib/yara/ffi.rb +3 -3
- data/lib/yara/scan_result.rb +34 -37
- data/lib/yara/scanner.rb +81 -0
- data/lib/yara/version.rb +1 -1
- data/lib/yara.rb +14 -55
- data/script/bootstrap +3 -0
- data/script/test +3 -0
- metadata +6 -18
- data/vendor/cache/ast-2.4.2.gem +0 -0
- data/vendor/cache/coderay-1.1.3.gem +0 -0
- data/vendor/cache/ffi-1.15.0.gem +0 -0
- data/vendor/cache/method_source-1.0.0.gem +0 -0
- data/vendor/cache/minitest-5.14.4.gem +0 -0
- data/vendor/cache/parallel-1.20.1.gem +0 -0
- data/vendor/cache/parser-3.0.0.0.gem +0 -0
- data/vendor/cache/pry-0.14.0.gem +0 -0
- data/vendor/cache/rainbow-3.0.0.gem +0 -0
- data/vendor/cache/rake-13.0.3.gem +0 -0
- data/vendor/cache/regexp_parser-2.1.1.gem +0 -0
- data/vendor/cache/rexml-3.2.4.gem +0 -0
- data/vendor/cache/rubocop-1.11.0.gem +0 -0
- data/vendor/cache/rubocop-ast-1.4.1.gem +0 -0
- data/vendor/cache/ruby-progressbar-1.11.0.gem +0 -0
- data/vendor/cache/unicode-display_width-2.0.0.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 814d5fa21cda6f98707e038549321eab8ce359f013c47d36e8d0845c05ff0b2c
|
|
4
|
+
data.tar.gz: 531d85f217da5f9bec89de9319ee5f9f33c3a801ec75db439de1fa377612c88e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3fd910b4f3ea435dcc5cfaa1763fe7cd33727bc77929b52d326d4f3baaad7caff2671c73100ec5102bdb61f9ac34d330ad2a33626faaeebf65ff16dde7d7e6fe
|
|
7
|
+
data.tar.gz: 53133f057a23dc32227a6c603322cdd5b9da273416bdd6aad9245b4c270249c83dd8e8f5211f531be1180a12c28825824977966425456bd9c18ff8883566f7bb
|
data/Dockerfile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
FROM ruby:2.6.6
|
|
2
|
+
|
|
3
|
+
RUN apt-get update -qq
|
|
4
|
+
RUN apt-get install -y flex bison
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
COPY . ./
|
|
9
|
+
RUN gem install bundler:2.2.15
|
|
10
|
+
RUN bundle install
|
|
11
|
+
|
|
12
|
+
RUN git clone --recursive --branch v4.1.1 https://github.com/VirusTotal/yara.git /tmp/yara && \
|
|
13
|
+
cd /tmp/yara/ && \
|
|
14
|
+
./bootstrap.sh && \
|
|
15
|
+
./configure && \
|
|
16
|
+
make && \
|
|
17
|
+
make install
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
yara-ffi (1.
|
|
4
|
+
yara-ffi (2.1.1)
|
|
5
5
|
ffi
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -9,7 +9,7 @@ GEM
|
|
|
9
9
|
specs:
|
|
10
10
|
ast (2.4.2)
|
|
11
11
|
coderay (1.1.3)
|
|
12
|
-
ffi (1.15.
|
|
12
|
+
ffi (1.15.4)
|
|
13
13
|
method_source (1.0.0)
|
|
14
14
|
minitest (5.14.4)
|
|
15
15
|
parallel (1.20.1)
|
|
@@ -48,4 +48,4 @@ DEPENDENCIES
|
|
|
48
48
|
yara-ffi!
|
|
49
49
|
|
|
50
50
|
BUNDLED WITH
|
|
51
|
-
2.2.
|
|
51
|
+
2.2.15
|
data/README.md
CHANGED
|
@@ -20,7 +20,34 @@ Or install it yourself as:
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
```ruby
|
|
24
|
+
Yara.start # run before you start using the Yara API.
|
|
25
|
+
|
|
26
|
+
rule = <<-RULE
|
|
27
|
+
rule ExampleRule
|
|
28
|
+
{
|
|
29
|
+
meta:
|
|
30
|
+
string_meta = "an example rule for testing"
|
|
31
|
+
|
|
32
|
+
strings:
|
|
33
|
+
$my_text_string = "we were here"
|
|
34
|
+
$my_text_regex = /were here/
|
|
35
|
+
|
|
36
|
+
condition:
|
|
37
|
+
$my_text_string or $my_text_regex
|
|
38
|
+
}
|
|
39
|
+
RULE
|
|
40
|
+
|
|
41
|
+
scanner = Yara::Scanner.new
|
|
42
|
+
scanner.add_rule(rule)
|
|
43
|
+
scanner.compile
|
|
44
|
+
result = scanner.call("one day we were here and then we were not")
|
|
45
|
+
result.match?
|
|
46
|
+
# => true
|
|
47
|
+
|
|
48
|
+
scanner.close # run when you are done using the scanner API and want to free up memory.
|
|
49
|
+
Yara.stop # run when you are completely done using the Yara API to free up memory.
|
|
50
|
+
```
|
|
24
51
|
|
|
25
52
|
## Development
|
|
26
53
|
|
data/lib/yara/ffi.rb
CHANGED
|
@@ -90,9 +90,9 @@ module Yara
|
|
|
90
90
|
# void* user_data)
|
|
91
91
|
callback :scan_callback, [
|
|
92
92
|
:pointer, # YR_SCAN_CONTEXT*
|
|
93
|
-
:int, #
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
:int, # callback_type
|
|
94
|
+
YrRule.ptr, # rule
|
|
95
|
+
UserData.ptr, # user_data
|
|
96
96
|
], :int
|
|
97
97
|
|
|
98
98
|
# int yr_rules_scan_mem(
|
data/lib/yara/scan_result.rb
CHANGED
|
@@ -6,38 +6,50 @@ module Yara
|
|
|
6
6
|
META_FLAGS_LAST_IN_RULE = 1
|
|
7
7
|
|
|
8
8
|
META_TYPE_INTEGER = 1
|
|
9
|
-
META_TYPE_STRING = 2
|
|
9
|
+
# META_TYPE_STRING = 2
|
|
10
10
|
META_TYPE_BOOLEAN = 3
|
|
11
11
|
|
|
12
12
|
STRING_FLAGS_LAST_IN_RULE = 0
|
|
13
13
|
|
|
14
|
-
STRING_LENGTH = 4
|
|
15
|
-
STRING_POINTER = 5
|
|
16
|
-
|
|
17
|
-
RULE_IDENTIFIER = 1
|
|
18
|
-
METAS_IDENTIFIER = 3
|
|
19
|
-
STRING_IDENTIFIER = 4
|
|
20
|
-
|
|
21
14
|
attr_reader :callback_type, :rule
|
|
22
15
|
|
|
23
|
-
def initialize(callback_type,
|
|
16
|
+
def initialize(callback_type, rule, user_data)
|
|
24
17
|
@callback_type = callback_type
|
|
25
|
-
@rule =
|
|
18
|
+
@rule = rule
|
|
19
|
+
@rule_meta = extract_rule_meta
|
|
20
|
+
@rule_strings = extract_rule_strings
|
|
21
|
+
@user_data_number = user_data[:number]
|
|
26
22
|
end
|
|
27
23
|
|
|
24
|
+
attr_reader :rule_meta, :rule_strings, :user_data_number
|
|
25
|
+
|
|
28
26
|
def rule_name
|
|
29
|
-
@rule
|
|
27
|
+
@rule[:identifier]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scan_complete?
|
|
31
|
+
callback_type == SCAN_FINISHED
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def rule_outcome?
|
|
35
|
+
[RULE_MATCHING, RULE_NOT_MATCHING].include?(callback_type)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def match?
|
|
39
|
+
callback_type == RULE_MATCHING
|
|
30
40
|
end
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def extract_rule_meta
|
|
33
45
|
metas = {}
|
|
34
46
|
reading_metas = true
|
|
35
47
|
meta_index = 0
|
|
36
|
-
meta_pointer = @rule
|
|
48
|
+
meta_pointer = @rule[:metas]
|
|
37
49
|
while reading_metas do
|
|
38
50
|
meta = YrMeta.new(meta_pointer + meta_index * YrMeta.size)
|
|
39
51
|
metas.merge!(meta_as_hash(meta))
|
|
40
|
-
flags = meta
|
|
52
|
+
flags = meta[:flags]
|
|
41
53
|
if flags == META_FLAGS_LAST_IN_RULE
|
|
42
54
|
reading_metas = false
|
|
43
55
|
else
|
|
@@ -47,15 +59,15 @@ module Yara
|
|
|
47
59
|
metas
|
|
48
60
|
end
|
|
49
61
|
|
|
50
|
-
def
|
|
62
|
+
def extract_rule_strings
|
|
51
63
|
strings = {}
|
|
52
64
|
reading_strings = true
|
|
53
65
|
string_index = 0
|
|
54
|
-
string_pointer = @rule
|
|
66
|
+
string_pointer = @rule[:strings]
|
|
55
67
|
while reading_strings do
|
|
56
68
|
string = YrString.new(string_pointer + string_index * YrString.size)
|
|
57
|
-
string_length = string
|
|
58
|
-
flags = string
|
|
69
|
+
string_length = string[:length]
|
|
70
|
+
flags = string[:flags]
|
|
59
71
|
if flags == STRING_FLAGS_LAST_IN_RULE
|
|
60
72
|
reading_strings = false
|
|
61
73
|
else
|
|
@@ -66,29 +78,14 @@ module Yara
|
|
|
66
78
|
strings
|
|
67
79
|
end
|
|
68
80
|
|
|
69
|
-
def scan_complete?
|
|
70
|
-
callback_type == SCAN_FINISHED
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def rule_outcome?
|
|
74
|
-
[RULE_MATCHING, RULE_NOT_MATCHING].include?(callback_type)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def match?
|
|
78
|
-
callback_type == RULE_MATCHING
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
private
|
|
82
|
-
|
|
83
81
|
def meta_as_hash(meta)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{ name.to_sym => value }
|
|
82
|
+
value = meta_value(meta[:string], meta[:integer], meta[:type])
|
|
83
|
+
{ meta[:identifier].to_sym => value }
|
|
87
84
|
end
|
|
88
85
|
|
|
89
86
|
def string_as_hash(yr_string)
|
|
90
|
-
string_pointer = yr_string
|
|
91
|
-
string_identifier = yr_string
|
|
87
|
+
string_pointer = yr_string[:string]
|
|
88
|
+
string_identifier = yr_string[:identifier]
|
|
92
89
|
{ string_identifier.to_sym => string_pointer.read_string }
|
|
93
90
|
end
|
|
94
91
|
|
data/lib/yara/scanner.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Yara
|
|
2
|
+
class Scanner
|
|
3
|
+
class NotCompiledError < StandardError; end
|
|
4
|
+
|
|
5
|
+
ERROR_CALLBACK = proc do |error_level, file_name, line_number, rule, message, user_data|
|
|
6
|
+
# noop
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
SCAN_FINISHED = 3
|
|
10
|
+
|
|
11
|
+
# Public: Initializes instance of scanner. Under the hood this calls yr_initialize, then
|
|
12
|
+
# creates a pointer, then calls yr_compiler_create with that pointer.
|
|
13
|
+
#
|
|
14
|
+
# error_callback: (optional) Proc to be called when an error occurs.
|
|
15
|
+
# user_data: (optional) Instance of UserData to store and pass information.
|
|
16
|
+
def initialize(error_callback: ERROR_CALLBACK, user_data: UserData.new)
|
|
17
|
+
@error_callback = error_callback
|
|
18
|
+
@user_data = user_data
|
|
19
|
+
@compiler_pointer = ::FFI::MemoryPointer.new(:pointer)
|
|
20
|
+
Yara::FFI.yr_compiler_create(@compiler_pointer)
|
|
21
|
+
@compiler_pointer = @compiler_pointer.get_pointer(0)
|
|
22
|
+
Yara::FFI.yr_compiler_set_callback(@compiler_pointer, error_callback, user_data)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Public: Adds a rule to the scanner and returns the namespace value. If a namespace
|
|
26
|
+
# is not provided it will default to nil and use the global namespace.
|
|
27
|
+
#
|
|
28
|
+
# rule_string - String containing the Yara rule to be added.
|
|
29
|
+
# namespace: (optional) String containing the namespace to be used for the rule.
|
|
30
|
+
def add_rule(rule_string, namespace: nil)
|
|
31
|
+
Yara::FFI.yr_compiler_add_string(@compiler_pointer, rule_string, namespace)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def compile
|
|
35
|
+
@rules_pointer = ::FFI::MemoryPointer.new(:pointer)
|
|
36
|
+
Yara::FFI.yr_compiler_get_rules(@compiler_pointer, @rules_pointer)
|
|
37
|
+
@rules_pointer = @rules_pointer.get_pointer(0)
|
|
38
|
+
Yara::FFI.yr_compiler_destroy(@compiler_pointer)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def call(test_string)
|
|
42
|
+
raise NotCompiledError unless @rules_pointer
|
|
43
|
+
|
|
44
|
+
results = []
|
|
45
|
+
scanning = true
|
|
46
|
+
result_callback = proc do |context_ptr, callback_type, rule, user_data|
|
|
47
|
+
if callback_type == SCAN_FINISHED
|
|
48
|
+
scanning = false
|
|
49
|
+
else
|
|
50
|
+
result = ScanResult.new(callback_type, rule, user_data)
|
|
51
|
+
results << result if result.rule_outcome?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
0 # ERROR_SUCCESS
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test_string_bytesize = test_string.bytesize
|
|
58
|
+
test_string_pointer = ::FFI::MemoryPointer.new(:char, test_string_bytesize)
|
|
59
|
+
test_string_pointer.put_bytes(0, test_string)
|
|
60
|
+
|
|
61
|
+
Yara::FFI.yr_rules_scan_mem(
|
|
62
|
+
@rules_pointer,
|
|
63
|
+
test_string_pointer,
|
|
64
|
+
test_string_bytesize,
|
|
65
|
+
0,
|
|
66
|
+
result_callback,
|
|
67
|
+
@user_data,
|
|
68
|
+
1,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
while scanning do
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
results
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def close
|
|
78
|
+
Yara::FFI.yr_rules_destroy(@rules_pointer)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
data/lib/yara/version.rb
CHANGED
data/lib/yara.rb
CHANGED
|
@@ -4,67 +4,26 @@ require "ffi"
|
|
|
4
4
|
require "pry"
|
|
5
5
|
require_relative "yara/ffi"
|
|
6
6
|
require_relative "yara/scan_result"
|
|
7
|
+
require_relative "yara/scanner"
|
|
7
8
|
require_relative "yara/version"
|
|
8
9
|
|
|
9
10
|
module Yara
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Error < StandardError; end
|
|
13
|
-
|
|
14
|
-
def self.test(rule_string, test_string)
|
|
15
|
-
user_data = UserData.new
|
|
16
|
-
scanning = true
|
|
17
|
-
results = []
|
|
18
|
-
|
|
11
|
+
def self.start
|
|
19
12
|
Yara::FFI.yr_initialize
|
|
13
|
+
end
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
Yara::FFI.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
error_callback = proc do |error_level, file_name, line_number, rule, message, user_data|
|
|
26
|
-
# noop
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
Yara::FFI.yr_compiler_set_callback(compiler_pointer, error_callback, user_data)
|
|
30
|
-
Yara::FFI.yr_compiler_add_string(compiler_pointer, rule_string, nil)
|
|
31
|
-
|
|
32
|
-
rules_pointer =::FFI::MemoryPointer.new(:pointer)
|
|
33
|
-
Yara::FFI.yr_compiler_get_rules(compiler_pointer, rules_pointer)
|
|
34
|
-
rules_pointer = rules_pointer.get_pointer(0)
|
|
35
|
-
|
|
36
|
-
result_callback = proc do |context_ptr, callback_type, rule_ptr, user_data_ptr|
|
|
37
|
-
if callback_type == SCAN_FINISHED
|
|
38
|
-
scanning = false
|
|
39
|
-
else
|
|
40
|
-
result = ScanResult.new(callback_type, rule_ptr)
|
|
41
|
-
results << result if result.rule_outcome?
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
0 # ERROR_SUCCESS
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
test_string_bytesize = test_string.bytesize
|
|
48
|
-
test_string_pointer = ::FFI::MemoryPointer.new(:char, test_string_bytesize)
|
|
49
|
-
test_string_pointer.put_bytes(0, test_string)
|
|
50
|
-
|
|
51
|
-
Yara::FFI.yr_rules_scan_mem(
|
|
52
|
-
rules_pointer,
|
|
53
|
-
test_string_pointer,
|
|
54
|
-
test_string_bytesize,
|
|
55
|
-
0,
|
|
56
|
-
result_callback,
|
|
57
|
-
user_data,
|
|
58
|
-
1,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
while scanning do
|
|
62
|
-
end
|
|
15
|
+
def self.stop
|
|
16
|
+
Yara::FFI.yr_finalize
|
|
17
|
+
end
|
|
63
18
|
|
|
64
|
-
|
|
19
|
+
def self.test(rule_string, test_string)
|
|
20
|
+
start
|
|
21
|
+
scanner = Yara::Scanner.new
|
|
22
|
+
scanner.add_rule(rule_string)
|
|
23
|
+
scanner.compile
|
|
24
|
+
scanner.call(test_string)
|
|
65
25
|
ensure
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Yara::FFI.yr_finalize
|
|
26
|
+
scanner.close
|
|
27
|
+
stop
|
|
69
28
|
end
|
|
70
29
|
end
|
data/script/bootstrap
ADDED
data/script/test
ADDED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yara-ffi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonathan Hoyt
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- ".rubocop.yml"
|
|
37
37
|
- CHANGELOG.md
|
|
38
38
|
- CODE_OF_CONDUCT.md
|
|
39
|
+
- Dockerfile
|
|
39
40
|
- Gemfile
|
|
40
41
|
- Gemfile.lock
|
|
41
42
|
- LICENSE.txt
|
|
@@ -46,28 +47,15 @@ files:
|
|
|
46
47
|
- lib/yara.rb
|
|
47
48
|
- lib/yara/ffi.rb
|
|
48
49
|
- lib/yara/scan_result.rb
|
|
50
|
+
- lib/yara/scanner.rb
|
|
49
51
|
- lib/yara/user_data.rb
|
|
50
52
|
- lib/yara/version.rb
|
|
51
53
|
- lib/yara/yr_meta.rb
|
|
52
54
|
- lib/yara/yr_namespace.rb
|
|
53
55
|
- lib/yara/yr_rule.rb
|
|
54
56
|
- lib/yara/yr_string.rb
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
- vendor/cache/ffi-1.15.0.gem
|
|
58
|
-
- vendor/cache/method_source-1.0.0.gem
|
|
59
|
-
- vendor/cache/minitest-5.14.4.gem
|
|
60
|
-
- vendor/cache/parallel-1.20.1.gem
|
|
61
|
-
- vendor/cache/parser-3.0.0.0.gem
|
|
62
|
-
- vendor/cache/pry-0.14.0.gem
|
|
63
|
-
- vendor/cache/rainbow-3.0.0.gem
|
|
64
|
-
- vendor/cache/rake-13.0.3.gem
|
|
65
|
-
- vendor/cache/regexp_parser-2.1.1.gem
|
|
66
|
-
- vendor/cache/rexml-3.2.4.gem
|
|
67
|
-
- vendor/cache/rubocop-1.11.0.gem
|
|
68
|
-
- vendor/cache/rubocop-ast-1.4.1.gem
|
|
69
|
-
- vendor/cache/ruby-progressbar-1.11.0.gem
|
|
70
|
-
- vendor/cache/unicode-display_width-2.0.0.gem
|
|
57
|
+
- script/bootstrap
|
|
58
|
+
- script/test
|
|
71
59
|
- yara-ffi.gemspec
|
|
72
60
|
homepage: https://github.com/jonmagic/yara-ffi
|
|
73
61
|
licenses:
|
data/vendor/cache/ast-2.4.2.gem
DELETED
|
Binary file
|
|
Binary file
|
data/vendor/cache/ffi-1.15.0.gem
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/vendor/cache/pry-0.14.0.gem
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|