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,161 @@
|
|
1
|
+
module Yara
|
2
|
+
# Public: Wrapper around the YARA-X YRX_COMPILER API.
|
3
|
+
#
|
4
|
+
# This class allows adding multiple sources, defining globals before
|
5
|
+
# compilation, and building a YRX_RULES object that can be used by
|
6
|
+
# {Yara::Scanner}.
|
7
|
+
class Compiler
|
8
|
+
class CompileError < StandardError; end
|
9
|
+
|
10
|
+
def initialize(flags = 0)
|
11
|
+
@compiler_ptr_holder = ::FFI::MemoryPointer.new(:pointer)
|
12
|
+
result = Yara::FFI.yrx_compiler_create(flags, @compiler_ptr_holder)
|
13
|
+
if result != Yara::FFI.yrx_last_error && result != Yara::FFI::YRX_SUCCESS
|
14
|
+
# Defensive: use yrx_last_error for message but prefer success check
|
15
|
+
end
|
16
|
+
|
17
|
+
if result != Yara::FFI::YRX_SUCCESS
|
18
|
+
raise CompileError, "Failed to create compiler: #{Yara::FFI.yrx_last_error}"
|
19
|
+
end
|
20
|
+
@compiler = @compiler_ptr_holder.get_pointer(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_source(src, origin = nil)
|
24
|
+
if origin
|
25
|
+
result = Yara::FFI.yrx_compiler_add_source_with_origin(@compiler, src, origin)
|
26
|
+
else
|
27
|
+
result = Yara::FFI.yrx_compiler_add_source(@compiler, src)
|
28
|
+
end
|
29
|
+
|
30
|
+
if result != Yara::FFI::YRX_SUCCESS
|
31
|
+
raise CompileError, "Failed to add source: #{Yara::FFI.yrx_last_error}"
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_global_str(ident, value)
|
37
|
+
result = Yara::FFI.yrx_compiler_define_global_str(@compiler, ident, value)
|
38
|
+
if result != Yara::FFI::YRX_SUCCESS
|
39
|
+
raise CompileError, "Failed to define global str #{ident}: #{Yara::FFI.yrx_last_error}"
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_global_bool(ident, value)
|
45
|
+
result = Yara::FFI.yrx_compiler_define_global_bool(@compiler, ident, !!value)
|
46
|
+
if result != Yara::FFI::YRX_SUCCESS
|
47
|
+
raise CompileError, "Failed to define global bool #{ident}: #{Yara::FFI.yrx_last_error}"
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def define_global_int(ident, value)
|
53
|
+
result = Yara::FFI.yrx_compiler_define_global_int(@compiler, ident, value)
|
54
|
+
if result != Yara::FFI::YRX_SUCCESS
|
55
|
+
raise CompileError, "Failed to define global int #{ident}: #{Yara::FFI.yrx_last_error}"
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def define_global_float(ident, value)
|
61
|
+
result = Yara::FFI.yrx_compiler_define_global_float(@compiler, ident, value)
|
62
|
+
if result != Yara::FFI::YRX_SUCCESS
|
63
|
+
raise CompileError, "Failed to define global float #{ident}: #{Yara::FFI.yrx_last_error}"
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Build and return a pointer to YRX_RULES. The caller is responsible for
|
69
|
+
# calling yrx_rules_destroy on the returned pointer when finished.
|
70
|
+
def build
|
71
|
+
rules_ptr = Yara::FFI.yrx_compiler_build(@compiler)
|
72
|
+
if rules_ptr.nil? || rules_ptr.null?
|
73
|
+
raise CompileError, "Failed to build rules: #{Yara::FFI.yrx_last_error}"
|
74
|
+
end
|
75
|
+
rules_ptr
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Build and return a serialized representation of compiled rules.
|
79
|
+
#
|
80
|
+
# Compiles the currently added sources (via add_source) into a YRX_RULES
|
81
|
+
# object and serializes it into a binary blob suitable for persistence or
|
82
|
+
# transport. The returned String contains the serialized rules and can be
|
83
|
+
# deserialized later with Yara::Scanner.from_serialized or the underlying
|
84
|
+
# yrx_rules_deserialize FFI call.
|
85
|
+
#
|
86
|
+
# Returns a String containing binary serialized rules (raw bytes).
|
87
|
+
# Raises CompileError if compilation or serialization fails.
|
88
|
+
def build_serialized
|
89
|
+
rules_ptr = build
|
90
|
+
|
91
|
+
buf_ptr_holder = ::FFI::MemoryPointer.new(:pointer)
|
92
|
+
result = Yara::FFI.yrx_rules_serialize(rules_ptr, buf_ptr_holder)
|
93
|
+
|
94
|
+
# Destroy the rules pointer after serialization (we own it from build)
|
95
|
+
Yara::FFI.yrx_rules_destroy(rules_ptr)
|
96
|
+
|
97
|
+
if result != Yara::FFI::YRX_SUCCESS
|
98
|
+
raise CompileError, "Failed to serialize rules: #{Yara::FFI.yrx_last_error}"
|
99
|
+
end
|
100
|
+
|
101
|
+
buf_ptr = buf_ptr_holder.get_pointer(0)
|
102
|
+
buffer = Yara::FFI::YRX_BUFFER.new(buf_ptr)
|
103
|
+
begin
|
104
|
+
data_ptr = buffer[:data]
|
105
|
+
length = buffer[:length]
|
106
|
+
str = data_ptr.get_bytes(0, length)
|
107
|
+
return str
|
108
|
+
ensure
|
109
|
+
Yara::FFI.yrx_buffer_destroy(buf_ptr)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return compilation errors as a parsed JSON object (Array of error objects).
|
114
|
+
# This uses yrx_compiler_errors_json which returns a YRX_BUFFER containing
|
115
|
+
# the JSON serialization. The buffer is destroyed after being converted.
|
116
|
+
def errors_json
|
117
|
+
buf_ptr_holder = ::FFI::MemoryPointer.new(:pointer)
|
118
|
+
result = Yara::FFI.yrx_compiler_errors_json(@compiler, buf_ptr_holder)
|
119
|
+
if result != Yara::FFI::YRX_SUCCESS
|
120
|
+
raise CompileError, "Failed to get errors JSON: #{Yara::FFI.yrx_last_error}"
|
121
|
+
end
|
122
|
+
|
123
|
+
buf_ptr = buf_ptr_holder.get_pointer(0)
|
124
|
+
buffer = Yara::FFI::YRX_BUFFER.new(buf_ptr)
|
125
|
+
data_ptr = buffer[:data]
|
126
|
+
length = buffer[:length]
|
127
|
+
json_str = data_ptr.read_string_length(length)
|
128
|
+
Yara::FFI.yrx_buffer_destroy(buf_ptr)
|
129
|
+
|
130
|
+
JSON.parse(json_str)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return compilation warnings as parsed JSON (Array of warning objects).
|
134
|
+
def warnings_json
|
135
|
+
buf_ptr_holder = ::FFI::MemoryPointer.new(:pointer)
|
136
|
+
result = Yara::FFI.yrx_compiler_warnings_json(@compiler, buf_ptr_holder)
|
137
|
+
if result != Yara::FFI::YRX_SUCCESS
|
138
|
+
raise CompileError, "Failed to get warnings JSON: #{Yara::FFI.yrx_last_error}"
|
139
|
+
end
|
140
|
+
|
141
|
+
buf_ptr = buf_ptr_holder.get_pointer(0)
|
142
|
+
buffer = Yara::FFI::YRX_BUFFER.new(buf_ptr)
|
143
|
+
data_ptr = buffer[:data]
|
144
|
+
length = buffer[:length]
|
145
|
+
json_str = data_ptr.read_string_length(length)
|
146
|
+
Yara::FFI.yrx_buffer_destroy(buf_ptr)
|
147
|
+
|
148
|
+
JSON.parse(json_str)
|
149
|
+
end
|
150
|
+
|
151
|
+
def destroy
|
152
|
+
Yara::FFI.yrx_compiler_destroy(@compiler) if @compiler
|
153
|
+
@compiler = nil
|
154
|
+
end
|
155
|
+
|
156
|
+
# Ensure resources are cleaned up.
|
157
|
+
def finalize
|
158
|
+
destroy
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|