yara-ffi 3.1.0 → 4.1.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/.github/copilot-instructions.md +266 -0
- data/.github/workflows/ruby.yml +69 -17
- data/CHANGELOG.md +90 -1
- data/DEVELOPMENT.md +188 -0
- data/Dockerfile +19 -11
- data/Gemfile.lock +38 -23
- data/README.md +56 -36
- data/USAGE.md +747 -0
- data/lib/yara/compiler.rb +161 -0
- data/lib/yara/ffi.rb +500 -111
- data/lib/yara/pattern_match.rb +178 -0
- data/lib/yara/scan_result.rb +573 -71
- data/lib/yara/scan_results.rb +224 -0
- data/lib/yara/scanner.rb +436 -45
- data/lib/yara/version.rb +5 -1
- data/lib/yara.rb +73 -15
- data/yara-ffi.gemspec +4 -4
- metadata +13 -15
- data/lib/yara/user_data.rb +0 -5
- data/lib/yara/yr_meta.rb +0 -10
- data/lib/yara/yr_namespace.rb +0 -5
- data/lib/yara/yr_rule.rb +0 -11
- data/lib/yara/yr_string.rb +0 -15
@@ -0,0 +1,178 @@
|
|
1
|
+
module Yara
|
2
|
+
# Public: Represents a single pattern match found during YARA scanning.
|
3
|
+
#
|
4
|
+
# A PatternMatch contains detailed information about where and how a specific
|
5
|
+
# YARA pattern matched within the scanned data. This includes the exact offset
|
6
|
+
# and length of the match, allowing for precise forensic analysis and data
|
7
|
+
# extraction.
|
8
|
+
#
|
9
|
+
# PatternMatch instances are typically created internally during the scanning
|
10
|
+
# process and accessed through ScanResult methods like pattern_matches.
|
11
|
+
#
|
12
|
+
# Examples
|
13
|
+
#
|
14
|
+
# # Access pattern matches through scan results
|
15
|
+
# results.each do |result|
|
16
|
+
# result.pattern_matches.each do |pattern_name, matches|
|
17
|
+
# matches.each do |match|
|
18
|
+
# puts "Pattern #{pattern_name} matched at offset #{match.offset}"
|
19
|
+
# puts "Matched text: '#{match.matched_data(scanned_data)}'"
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
class PatternMatch
|
24
|
+
# Public: The byte offset where this pattern match begins in the scanned data.
|
25
|
+
attr_reader :offset
|
26
|
+
|
27
|
+
# Public: The length in bytes of this pattern match.
|
28
|
+
attr_reader :length
|
29
|
+
|
30
|
+
# Public: Initialize a new PatternMatch.
|
31
|
+
#
|
32
|
+
# This constructor is typically called internally when processing YARA-X
|
33
|
+
# match results. It captures the precise location and size of a pattern
|
34
|
+
# match within scanned data.
|
35
|
+
#
|
36
|
+
# offset - An Integer byte offset where the match begins
|
37
|
+
# length - An Integer length in bytes of the match
|
38
|
+
#
|
39
|
+
# Examples
|
40
|
+
#
|
41
|
+
# # Typically created internally during scanning
|
42
|
+
# match = PatternMatch.new(42, 10)
|
43
|
+
# match.offset # => 42
|
44
|
+
# match.length # => 10
|
45
|
+
def initialize(offset, length)
|
46
|
+
@offset = offset
|
47
|
+
@length = length
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Extract the actual matched data from the scanned content.
|
51
|
+
#
|
52
|
+
# This method returns the exact bytes that matched the pattern by extracting
|
53
|
+
# the appropriate slice from the original scanned data. This is useful for
|
54
|
+
# forensic analysis, debugging rules, and understanding what triggered a match.
|
55
|
+
#
|
56
|
+
# data - A String containing the original data that was scanned
|
57
|
+
#
|
58
|
+
# Examples
|
59
|
+
#
|
60
|
+
# # Extract what actually matched
|
61
|
+
# scan_data = "hello world test data"
|
62
|
+
# match = PatternMatch.new(6, 5) # matches "world"
|
63
|
+
# match.matched_data(scan_data) # => "world"
|
64
|
+
#
|
65
|
+
# Returns a String containing the matched bytes.
|
66
|
+
# Returns empty String if offset/length are outside data bounds.
|
67
|
+
def matched_data(data)
|
68
|
+
return "" if offset < 0 || offset >= data.bytesize
|
69
|
+
return "" if length <= 0 || offset + length > data.bytesize
|
70
|
+
|
71
|
+
data.byteslice(offset, length)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Public: Get the end offset of this match (exclusive).
|
75
|
+
#
|
76
|
+
# This convenience method calculates the byte position immediately after
|
77
|
+
# the last byte of this match, which is useful for range operations and
|
78
|
+
# avoiding overlapping matches.
|
79
|
+
#
|
80
|
+
# Examples
|
81
|
+
#
|
82
|
+
# match = PatternMatch.new(10, 5)
|
83
|
+
# match.end_offset # => 15
|
84
|
+
#
|
85
|
+
# Returns an Integer representing the end offset (exclusive).
|
86
|
+
def end_offset
|
87
|
+
offset + length
|
88
|
+
end
|
89
|
+
|
90
|
+
# Public: Check if this match overlaps with another match.
|
91
|
+
#
|
92
|
+
# This method determines whether two pattern matches have any overlapping
|
93
|
+
# bytes. This is useful for analyzing complex rules with multiple patterns
|
94
|
+
# or detecting redundant matches.
|
95
|
+
#
|
96
|
+
# other - Another PatternMatch instance to compare against
|
97
|
+
#
|
98
|
+
# Examples
|
99
|
+
#
|
100
|
+
# match1 = PatternMatch.new(10, 5) # bytes 10-14
|
101
|
+
# match2 = PatternMatch.new(12, 5) # bytes 12-16
|
102
|
+
# match1.overlaps?(match2) # => true
|
103
|
+
#
|
104
|
+
# match3 = PatternMatch.new(20, 5) # bytes 20-24
|
105
|
+
# match1.overlaps?(match3) # => false
|
106
|
+
#
|
107
|
+
# Returns a Boolean indicating whether the matches overlap.
|
108
|
+
def overlaps?(other)
|
109
|
+
offset < other.end_offset && end_offset > other.offset
|
110
|
+
end
|
111
|
+
|
112
|
+
# Public: Get a human-readable string representation of this match.
|
113
|
+
#
|
114
|
+
# This method provides a concise string representation showing the key
|
115
|
+
# details of the match, useful for debugging and logging purposes.
|
116
|
+
#
|
117
|
+
# Examples
|
118
|
+
#
|
119
|
+
# match = PatternMatch.new(42, 10)
|
120
|
+
# match.to_s # => "PatternMatch(offset: 42, length: 10)"
|
121
|
+
#
|
122
|
+
# Returns a String representation of this match.
|
123
|
+
def to_s
|
124
|
+
"PatternMatch(offset: #{offset}, length: #{length})"
|
125
|
+
end
|
126
|
+
|
127
|
+
# Public: Detailed inspection string with all attributes.
|
128
|
+
#
|
129
|
+
# Provides a complete string representation including all match attributes,
|
130
|
+
# useful for debugging and development purposes.
|
131
|
+
#
|
132
|
+
# Examples
|
133
|
+
#
|
134
|
+
# match = PatternMatch.new(42, 10)
|
135
|
+
# match.inspect # => "#<Yara::PatternMatch:0x... @offset=42, @length=10>"
|
136
|
+
#
|
137
|
+
# Returns a String with detailed object information.
|
138
|
+
def inspect
|
139
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} @offset=#{@offset}, @length=#{@length}>"
|
140
|
+
end
|
141
|
+
|
142
|
+
# Public: Compare two PatternMatch objects for equality.
|
143
|
+
#
|
144
|
+
# Two matches are considered equal if they have the same offset and length.
|
145
|
+
# This is useful for deduplicating matches or comparing results.
|
146
|
+
#
|
147
|
+
# other - Another PatternMatch instance to compare against
|
148
|
+
#
|
149
|
+
# Examples
|
150
|
+
#
|
151
|
+
# match1 = PatternMatch.new(10, 5)
|
152
|
+
# match2 = PatternMatch.new(10, 5)
|
153
|
+
# match1 == match2 # => true
|
154
|
+
#
|
155
|
+
# Returns a Boolean indicating equality.
|
156
|
+
def ==(other)
|
157
|
+
other.is_a?(PatternMatch) && offset == other.offset && length == other.length
|
158
|
+
end
|
159
|
+
|
160
|
+
# Public: Generate hash code for this match.
|
161
|
+
#
|
162
|
+
# Uses offset and length to generate a hash code, enabling PatternMatch
|
163
|
+
# instances to be used as hash keys or in sets.
|
164
|
+
#
|
165
|
+
# Examples
|
166
|
+
#
|
167
|
+
# match = PatternMatch.new(42, 10)
|
168
|
+
# {match => "info"} # Can be used as hash key
|
169
|
+
#
|
170
|
+
# Returns an Integer hash code.
|
171
|
+
def hash
|
172
|
+
[offset, length].hash
|
173
|
+
end
|
174
|
+
|
175
|
+
# Public: Enable hash equality based on hash code.
|
176
|
+
alias_method :eql?, :==
|
177
|
+
end
|
178
|
+
end
|