feat: major update of yara scanner module which brings new caching of scans result with policy timeout

This commit is contained in:
Dmitry Ng
2023-05-24 19:39:50 +03:00
parent 1e827cf5a0
commit 599b5180e9
27 changed files with 2303 additions and 760 deletions
+9 -1
View File
@@ -275,9 +275,11 @@
},
"actions": [
"yr_object_scan_proc",
"yr_object_scan_proc_non_cached",
"yr_object_task_scan_proc",
"yr_scan_fs",
"yr_subject_scan_proc",
"yr_subject_scan_proc_non_cached",
"yr_subject_task_scan_proc",
"yr_task_fastscan_fs",
"yr_task_fastscan_proc",
@@ -293,12 +295,18 @@
"yr_module_started",
"yr_module_stopped",
"yr_object_process_matched_high",
"yr_object_process_matched_high_cached",
"yr_object_process_matched_low",
"yr_object_process_matched_low_cached",
"yr_object_process_matched_medium",
"yr_object_process_matched_medium_cached",
"yr_process_matched_custom",
"yr_subject_process_matched_high",
"yr_subject_process_matched_high_cached",
"yr_subject_process_matched_low",
"yr_subject_process_matched_medium"
"yr_subject_process_matched_low_cached",
"yr_subject_process_matched_medium",
"yr_subject_process_matched_medium_cached"
],
"fields": [
"malware_class",
+2 -2
View File
@@ -324,7 +324,7 @@ module.exports = {
return this.checkForm.scope.type !== this.CheckScope.Scan || (this.checkForm.scope.type === this.CheckScope.Scan && this.checkForm.scope.value && !(/(\/\/)|(\\\\)/g.test(this.checkForm.scope.value)));
},
isValidProcess() {
return this.checkForm.type === this.CheckType.Process && this.checkForm.scope.type === this.CheckScope.Scan ? this.checkForm.scope.id : true;
return this.checkForm.type === this.CheckType.Process && this.checkForm.scope.type === this.CheckScope.Scan ? (this.checkForm.scope.value ? true : this.checkForm.scope.id) : true;
},
isValidEditor() {
return this.checkForm.rules.type === this.CheckRules.Custom ? this.editor.getValue() : true;
@@ -515,7 +515,7 @@ module.exports = {
async save() {
this.hasFirstSave = true;
if (this.canStartCheck && this.isValidPath && this.isValidProcess && this.isValidEditor) {
if (this.canStartCheck && (this.isValidPath || this.isValidProcess) && this.isValidEditor) {
this.isSaving = true;
this.checkForm.rules.value = this.editor.getValue();
this.checkForm.scope.id = +this.checkForm.scope.id;
+105
View File
@@ -0,0 +1,105 @@
require("yaci")
local glue = require("glue")
local CacheLogic = newclass("CacheLogic")
local DEFAULT_YARA_PROCESS_CACHING_TIME = 60.0
function CacheLogic:init()
local YaraUtils = require("utils.yara_utils")
self.yara_utils = YaraUtils()
end
function CacheLogic:set_yara_process_caching_time(minutes)
assert((type(minutes) == "number"), "CacheLogic:set_yara_process_caching_time ~ invalid minutes parameter")
self.yara_process_caching_time = minutes
end
function CacheLogic:get_yara_process_caching_time()
assert((type(self.yara_process_caching_time) == "number"), "CacheLogic:get_yara_process_caching_time ~ invalid yara_process_caching_time")
return self.yara_process_caching_time
end
function CacheLogic:get_yara_process_caching_time_in_seconds()
return self.yara_utils:to_seconds(self:get_yara_process_caching_time())
end
function CacheLogic:get_default_yara_process_caching_time()
return DEFAULT_YARA_PROCESS_CACHING_TIME
end
function CacheLogic:is_make_scan( --[[proc_id, proc_image, ]] scan_results)
if not scan_results then
return true
end
if not scan_results.yara_scan_results then
return true
end
local last_scan_time = scan_results.last_scan_time
if type(last_scan_time) ~= "number" then
__log.error("Non-valid last_scan_time type: " .. type(last_scan_time))
return true
end
if self:get_yara_process_caching_time_in_seconds() <= 0 then
return true
end
local elapsed_time = os.difftime(self.yara_utils:get_UTC_time(), last_scan_time)
__log.info("CacheLogic:is_make_scan ~ elapsed time between scans: " .. elapsed_time)
if elapsed_time > self:get_yara_process_caching_time_in_seconds() then
return true
end
return false
end
function CacheLogic:is_make_caching(yara_scan_results)
if not yara_scan_results then
return false
end
if self:is_yara_error(yara_scan_results) then
return false
end
if self:get_yara_process_caching_time_in_seconds() <= 0 then
return false
end
return true
end
function CacheLogic:is_yara_error(yara_scan_results)
if type(yara_scan_results) ~= "table" then
return false
end
if glue.count(yara_scan_results) ~= 1 then
return false
end
for _, item in ipairs(yara_scan_results) do
if not item then
return false
end
if not item.error then
return false
end
end
return true
end
function CacheLogic:is_use_cached_results(action_name)
if type(action_name) ~= "string" then
return false
end
if action_name == "yr_subject_scan_proc_non_cached" then
return false
end
if action_name == "yr_object_scan_proc_non_cached" then
return false
end
return true
end
function CacheLogic:cleanup()
self.yara_utils = nil
end
return CacheLogic
+106
View File
@@ -0,0 +1,106 @@
require("yaci")
require("engines.db_engine")
local cjson = require("cjson")
local ScanResultsCache = newclass("ScanResultsCache")
function ScanResultsCache:init(db_engine)
self.db_engine = db_engine
end
function ScanResultsCache:add(process_id, process_image, scan_results)
if not self.db_engine then
__log.error("ScanResultsCache:add ~ db_engine is not initialized")
return false
end
if (type(process_id) ~= "number") then
__log.error("ScanResultsCache:add ~ non-valid process_id parameter")
return false
end
if (type(process_image) ~= "string") then
__log.error("ScanResultsCache:add ~ non-valid process_image parameter")
return false
end
if not scan_results then
__log.error("ScanResultsCache:add ~ non-valid scan_results parameter")
return false
end
if not scan_results.yara_scan_results then
__log.error("ScanResultsCache:add ~ non-valid scan_results.yara_scan_results")
return false
end
local yara_scan_results = scan_results.yara_scan_results
if type(yara_scan_results) ~= "table" then
__log.error("ScanResultsCache:add ~ non-table scan_results.yara_scan_results")
return false
end
yara_scan_results = cjson.encode(yara_scan_results)
return self.db_engine:add_to_process_cache(process_id, process_image, scan_results.last_scan_time, yara_scan_results)
end
function ScanResultsCache:get(process_id, process_image)
if not self.db_engine then
__log.error("ScanResultsCache:get ~ db_engine is not initialized")
return nil
end
if (type(process_id) ~= "number") then
__log.error("ScanResultsCache:add ~ non-valid process_id parameter")
return nil
end
if (type(process_image) ~= "string") then
__log.error("ScanResultsCache:add ~ non-valid process_image parameter")
return nil
end
local query_scan_results = self.db_engine:get_from_process_cache(process_id, process_image)
if not query_scan_results then
__log.debug("ScanResultsCache:get ~ no scan results in process cache")
return nil
end
if type(query_scan_results) ~= "table" then
__log.error("ScanResultsCache:get ~ non-valid scan results ~ process cache")
return nil
end
query_scan_results.scan_results = cjson.decode(query_scan_results.scan_results)
return query_scan_results
end
function ScanResultsCache:remove(process_id, process_image)
-- TODO return success \ failure
if not self.db_engine then
__log.error("ScanResultsCache:remove ~ db_engine is not initialized")
return
end
if (type(process_id) ~= "number") then
__log.error("ScanResultsCache:add ~ non-valid process_id parameter")
return
end
if (type(process_image) ~= "string") then
__log.error("ScanResultsCache:add ~ non-valid process_image parameter")
return
end
self.db_engine:delete_from_process_cache(process_id, process_image)
end
function ScanResultsCache:cleanup()
self.db_engine = nil
end
return ScanResultsCache
File diff suppressed because it is too large Load Diff
+240 -143
View File
@@ -5,7 +5,6 @@ local glue = require("glue")
CDatabaseEngine = newclass("CDatabaseEngine")
function CDatabaseEngine:init(db)
self.db = assert(db)
self.queries = {}
@@ -100,8 +99,6 @@ function CDatabaseEngine:init(db)
);
]]
-- TODO: add caches with sha256
self.queries.add_rule = [[
REPLACE INTO rules VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
]]
@@ -166,6 +163,65 @@ function CDatabaseEngine:init(db)
WHERE rule_id == ?
]]
-- cache queries
-- file cache
self.queries.create_table_file_cache = [[
CREATE TABLE IF NOT EXISTS file_cache (
filepath TEXT,
sha256 TEXT,
scan_results TEXT
);
]]
self.queries.create_index_for_file_cache = [[
CREATE INDEX IF NOT EXISTS file_cache_index ON file_cache (filepath, sha256);
]]
self.queries.add_to_file_cache = [[
INSERT INTO file_cache VALUES(?, ?, ?, ?);
]]
-- process cache
self.queries.create_table_process_cache = [[
CREATE TABLE IF NOT EXISTS process_cache (
process_id INTEGER,
process_image TEXT,
last_scan_time DOUBLE,
scan_results TEXT
);
]]
self.queries.create_index_for_process_cache = [[
CREATE INDEX IF NOT EXISTS process_cache_index ON process_cache (process_id, process_image);
]]
self.queries.add_to_process_cache = [[
INSERT INTO process_cache VALUES(?, ?, ?, ?);
]]
self.queries.get_from_process_cache = [[
SELECT scan_results, last_scan_time
FROM process_cache
WHERE process_id == ? AND process_image == ?
]]
-- test query
self.queries.get_all_from_process_cache = [[
SELECT *
FROM process_cache
]]
self.queries.delete_from_process_cache = [[
DELETE
FROM process_cache
WHERE process_id == ? AND process_image == ?
]]
self.queries.delete_all_from_process_cache = [[
DELETE
FROM process_cache
]]
end
function CDatabaseEngine:free()
@@ -173,7 +229,7 @@ function CDatabaseEngine:free()
end
function CDatabaseEngine:create_tables()
__log.debug("create_tables CDatabaseEngine")
__log.debug("CDatabaseEngine:create_tables")
self:exec_query(self.queries.create_table_rules)
self:exec_query(self.queries.create_table_tasks)
@@ -183,80 +239,80 @@ function CDatabaseEngine:create_tables()
self:exec_query(self.queries.create_table_task_result_process)
self:exec_query(self.queries.create_table_task_result_file)
self:exec_query(self.queries.create_table_task_detects)
self:exec_query(self.queries.create_table_file_cache)
self:exec_query(self.queries.create_index_for_file_cache)
self:exec_query(self.queries.create_table_process_cache)
self:exec_query(self.queries.create_index_for_process_cache)
end
-- from json
function CDatabaseEngine:add_rule(rule_id, rule_meta)
__log.debug("add_rule CDatabaseEngine")
assert(type(rule_id) == "string", "rule_id must be a value of string type")
assert(type(rule_meta) == "table", "rule_meta must be a value of table type")
assert(type(rule_meta['rule_name']) == "string", "rule_name must be a value of string type")
assert(type(rule_meta['malware_class']) == "string", "malware_class must be a value of string type")
assert(type(rule_meta['malware_family']) == "string", "malware_family must be a value of string type")
assert(type(rule_meta['rule_severity']) == "string", "rule_severity must be a value of string type")
assert(type(rule_meta['rule_type']) == "string", "rule_type must be a value of string type")
assert(type(rule_meta['is_silent']) == "boolean", "is_silent must be a value of string type")
assert(type(rule_meta['description']) == "string", "description must be a value of string type")
assert(type(rule_meta['date']) == "string", "date must be a value of string type")
assert(type(rule_meta['hash']) == "table", "hash must be a value of table type")
assert(type(rule_meta['reference']) == "table", "reference must be a value of table type")
assert(type(rule_meta['rule_precision']) == "number", "rule_precision must be a value of number type")
assert(type(rule_meta["rule_name"]) == "string", "rule_name must be a value of string type")
assert(type(rule_meta["malware_class"]) == "string", "malware_class must be a value of string type")
assert(type(rule_meta["malware_family"]) == "string", "malware_family must be a value of string type")
assert(type(rule_meta["rule_severity"]) == "string", "rule_severity must be a value of string type")
assert(type(rule_meta["rule_type"]) == "string", "rule_type must be a value of string type")
assert(type(rule_meta["is_silent"]) == "boolean", "is_silent must be a value of string type")
assert(type(rule_meta["description"]) == "string", "description must be a value of string type")
assert(type(rule_meta["date"]) == "string", "date must be a value of string type")
assert(type(rule_meta["hash"]) == "table", "hash must be a value of table type")
assert(type(rule_meta["reference"]) == "table", "reference must be a value of table type")
assert(type(rule_meta["rule_precision"]) == "number", "rule_precision must be a value of number type")
return self:exec_query(self.queries.add_rule,
1,
rule_id,
rule_meta['rule_name'],
rule_meta['malware_class'],
rule_meta['malware_family'],
rule_meta['rule_severity'],
rule_meta['rule_type'],
rule_meta['is_silent'],
rule_meta['description'],
rule_meta['date'],
table.concat(rule_meta['hash'], "|"),
table.concat(rule_meta['reference'], "|"),
rule_meta['rule_precision'])
1,
rule_id,
rule_meta["rule_name"],
rule_meta["malware_class"],
rule_meta["malware_family"],
rule_meta["rule_severity"],
rule_meta["rule_type"],
rule_meta["is_silent"],
rule_meta["description"],
rule_meta["date"],
table.concat(rule_meta["hash"], "|"),
table.concat(rule_meta["reference"], "|"),
rule_meta["rule_precision"])
end
function CDatabaseEngine:get_task(task_id)
__log.debug("get_task CDatabaseEngine")
assert(type(task_id) == "string","task_id must be a value of string type")
assert(type(task_id) == "string", "task_id must be a value of string type")
local query = self:select_query_unpack_single(self.queries.get_task, task_id)
if not query then
__log.debug("get_task CDatabaseEngine, empty query results")
__log.debug("CDatabaseEngine:get_task, empty query results")
return nil
end
local res = {
task_id = query['task_id'],
task_type = query['task_type'],
objects_type = query['objects_type'],
custom_rules = query['custom_rules']
task_id = query["task_id"],
task_type = query["task_type"],
objects_type = query["objects_type"],
custom_rules = query["custom_rules"],
}
return res
end
function CDatabaseEngine:update_rules(active_rules)
__log.debug("update_rules CDatabaseEngine")
__log.debug("CDatabaseEngine:update_rules")
assert(type(active_rules) == "table", "active_rules must be a value of table type")
local current_rules = self:select_query("SELECT rule_id,active FROM rules")
if not current_rules then
__log.debug("get_task CDatabaseEngine, empty query results")
__log.debug("CDatabaseEngine:get_task, empty query results")
return nil
end
local active_rules_in_db = {}
for _,rule in ipairs(current_rules) do
for _, rule in ipairs(current_rules) do
if active_rules[rule.rule_id] ~= nil then
active_rules_in_db[rule.rule_id] = true;
if rule.active == 0 then
__log.infof("mark rule as active, rule_id = %s", rule.rule_id)
__log.debugf("mark rule as active, rule_id = %s", rule.rule_id)
self:exec_query("UPDATE rules SET active = 1 WHERE rule_id = ?", rule.rule_id)
end
else
@@ -269,28 +325,27 @@ function CDatabaseEngine:update_rules(active_rules)
AND B.rule_id = ?
]], rule.rule_id)
if not detects or detects['COUNT(*)'] == 0 then
__log.infof("delete rule, rule_id = %s", rule.rule_id)
if not detects or detects["COUNT(*)"] == 0 then
__log.debugf("delete rule, rule_id = %s", rule.rule_id)
self:exec_query("DELETE FROM rules WHERE rule_id = ?", rule.rule_id)
else
if rule.active == 1 then
__log.infof("mark rule as inactive, rule_id = %s", rule.rule_id)
__log.debugf("mark rule as inactive, rule_id = %s", rule.rule_id)
self:exec_query("UPDATE rules SET active = 0 WHERE rule_id = ?", rule.rule_id)
end
end
end
end
for rule_id,rule_meta in pairs(active_rules) do
for rule_id, rule_meta in pairs(active_rules) do
if active_rules_in_db[rule_id] == nil then
__log.infof("add new rule, rule_id = %s", rule_id)
self:add_rule(rule_id, rule_meta)
end
end
end
function CDatabaseEngine:add_task_proc(task_id, proc_id, proc_image, task_type, rules)
__log.debug("add_task_proc CDatabaseEngine")
__log.debug("CDatabaseEngine:add_task_proc")
assert(type(task_id) == "string", "task_id must be a value of string type")
assert(type(proc_id) == "nil" or type(proc_id) == "number", "proc_id must be a value of nil or number type")
assert(type(proc_image) == "nil" or type(proc_image) == "string", "proc_image must be a value of nil or string type")
@@ -308,16 +363,16 @@ function CDatabaseEngine:add_task_proc(task_id, proc_id, proc_image, task_type,
end
return self:exec_query(self.queries.add_task_status,
task_id,
0,
os.date('!%Y-%m-%dT%H:%M:%SZ', os.time()), -- GMT
nil,
0,
nil)
task_id,
0,
os.date("!%Y-%m-%dT%H:%M:%SZ", os.time()), -- GMT
nil,
0,
nil)
end
function CDatabaseEngine:add_task_fs(task_id, filepath, recursive, task_type, rules)
__log.debug("add_task_fs CDatabaseEngine")
__log.debug("CDatabaseEngine:add_task_fs")
assert(type(task_id) == "string", "task_id must be a value of string type")
assert(type(filepath) == "nil" or type(filepath) == "string", "filepath must be a value of nil or string type")
assert(type(recursive) == "nil" or type(recursive) == "boolean", "recursive must be a value of nil or boolean type")
@@ -335,16 +390,16 @@ function CDatabaseEngine:add_task_fs(task_id, filepath, recursive, task_type, ru
end
return self:exec_query(self.queries.add_task_status,
task_id,
0,
os.date('!%Y-%m-%dT%H:%M:%SZ', os.time()), -- GMT
nil,
0,
nil)
task_id,
0,
os.date("!%Y-%m-%dT%H:%M:%SZ", os.time()), -- GMT
nil,
0,
nil)
end
function CDatabaseEngine:add_task_result(task_id, status, time_end, err)
__log.debug("add_task_result CDatabaseEngine")
__log.debug("CDatabaseEngine:add_task_result")
assert(type(task_id) == "string", "task_id must be a value of string type")
assert(type(status) == "number", "status must be a value of number type")
assert(type(time_end) == "nil" or type(time_end) == "string", "time_end must be a value of nil or string type")
@@ -355,7 +410,7 @@ function CDatabaseEngine:add_task_result(task_id, status, time_end, err)
return nil
end
if query['status'] ~= 0 then -- specific status is set
if query["status"] ~= 0 then -- specific status is set
return nil
end
@@ -363,7 +418,7 @@ function CDatabaseEngine:add_task_result(task_id, status, time_end, err)
end
function CDatabaseEngine:add_task_result_process(task_id, proc_id, proc_image, err, detects)
__log.debug("add_task_result_process CDatabaseEngine")
__log.debug("CDatabaseEngine:add_task_result_process")
assert(type(task_id) == "string", "task_id must be a value of string type")
assert(type(proc_id) == "nil" or type(proc_id) == "number", "proc_id must be a value of nil or string type")
assert(type(proc_image) == "nil" or type(proc_image) == "string", "proc_image must be a value of nil or string type")
@@ -375,7 +430,7 @@ function CDatabaseEngine:add_task_result_process(task_id, proc_id, proc_image, e
return nil
end
local object = query['objects']
local object = query["objects"]
local res = self:exec_query(self.queries.update_task_status_add_object, object + 1, task_id)
if not res then
@@ -388,8 +443,7 @@ function CDatabaseEngine:add_task_result_process(task_id, proc_id, proc_image, e
end
if detects ~= nil then
for _,rule in ipairs(detects) do
for _, rule in ipairs(detects) do
res = self:exec_query(self.queries.add_task_detect, task_id, object, rule.rule_id)
if not res then
@@ -402,7 +456,7 @@ function CDatabaseEngine:add_task_result_process(task_id, proc_id, proc_image, e
end
function CDatabaseEngine:add_task_result_file(task_id, filepath, sha256, err, detects)
__log.debug("add_task_result_file CDatabaseEngine")
__log.debug("CDatabaseEngine:add_task_result_file")
assert(type(task_id) == "string", "task_id must be a value of string type")
assert(type(filepath) == "nil" or type(filepath) == "string", "filepath must be a value of nil or string type")
assert(type(sha256) == "nil" or type(sha256) == "string", "sha256 must be a value of nil or string type")
@@ -414,7 +468,7 @@ function CDatabaseEngine:add_task_result_file(task_id, filepath, sha256, err, de
return nil
end
local object = query['objects']
local object = query["objects"]
local res = self:exec_query(self.queries.update_task_status_add_object, object + 1, task_id)
if not res then
@@ -427,7 +481,7 @@ function CDatabaseEngine:add_task_result_file(task_id, filepath, sha256, err, de
end
if detects ~= nil then
for _,rule in ipairs(detects) do
for _, rule in ipairs(detects) do
res = self:exec_query(self.queries.add_task_detect, task_id, object, rule.rule_id)
if not res then
return res
@@ -438,76 +492,127 @@ function CDatabaseEngine:add_task_result_file(task_id, filepath, sha256, err, de
return true
end
-- execute cache queries
function CDatabaseEngine:add_to_process_cache(process_id, process_image, last_scan_time, scan_results)
__log.info("CDatabaseEngine:add_to_process_cache")
assert(process_id and type(process_id) == "number", "invalid process_id parameter")
assert(process_image and type(process_image) == "string", "invalid process_image parameter")
assert(last_scan_time and type(last_scan_time) == "number", "invalid last_scan_time parameter")
assert(scan_results and type(scan_results) == "string", "invalid scan_results parameter")
return self:exec_query(self.queries.add_to_process_cache, process_id, process_image, last_scan_time, scan_results)
end
function CDatabaseEngine:get_from_process_cache(process_id, process_image)
__log.info("CDatabaseEngine:get_from_process_cache")
assert(type(process_id) == "number", "process_id must be a value of number type")
assert(type(process_image) == "string", "process_image must be a value of number type")
local results = self:select_query_unpack_single(self.queries.get_from_process_cache, process_id, process_image)
if not results then
__log.info("CDatabaseEngine:get_scan_results_from_cache ~ empty query results")
return nil
end
return results
end
function CDatabaseEngine:get_all_from_process_cache()
__log.info("CDatabaseEngine:get_all_from_process_cache")
local results = self:select_query(self.queries.get_all_from_process_cache)
if not results then
__log.info("CDatabaseEngine:get_all_from_process_cache ~ empty query results")
return nil
end
return results
end
function CDatabaseEngine:delete_from_process_cache(process_id, process_image)
__log.info("CDatabaseEngine:delete_from_process_cache")
assert(type(process_id) == "number", "process_id must be a value of number type")
assert(type(process_image) == "string", "process_image must be a value of number type")
-- TODO? self:delete_query(<>)
self:select_query_unpack_single(self.queries.delete_from_process_cache, process_id, process_image)
end
function CDatabaseEngine:clear_process_cache()
__log.info("CDatabaseEngine:clear_process_cache")
self:exec_query(self.queries.delete_all_from_process_cache)
end
function CDatabaseEngine:get_rule_meta(rule_id)
__log.debug("get_rule_meta CDatabaseEngine")
assert(type(rule_id) == "string","rule_id must be a value of string type")
__log.debug("CDatabaseEngine:get_rule_meta")
assert(type(rule_id) == "string", "rule_id must be a value of string type")
local query = self:select_query_unpack_single(self.queries.get_rule_meta, rule_id)
if not query then
__log.debug("get_rule_meta CDatabaseEngine, empty query results")
__log.debug("CDatabaseEngine:get_rule_meta, empty query results")
return nil
end
local res = {
rule_name = query['rule_name'],
malware_class = query['malware_class'],
malware_family = query['malware_family'],
rule_severity = query['rule_severity'],
rule_type = query['rule_type'],
is_silent = query['is_silent'] == 1,
description = query['description'],
date = query['date'],
hash = glue.gsplit(query['hash'], "|"),
reference = glue.gsplit(query['reference'], "|"),
rule_precision = query['rule_precision']
rule_name = query["rule_name"],
malware_class = query["malware_class"],
malware_family = query["malware_family"],
rule_severity = query["rule_severity"],
rule_type = query["rule_type"],
is_silent = query["is_silent"] == 1,
description = query["description"],
date = query["date"],
hash = glue.gsplit(query["hash"], "|"),
reference = glue.gsplit(query["reference"], "|"),
rule_precision = query["rule_precision"],
}
return res
end
local function compose_filter(filters)
if filters == nil or #filters == 0 then
return ''
return ""
end
local str = ''
local str = ""
for i,filter in ipairs(filters) do
if filter.value == '' then
for i, filter in ipairs(filters) do
if filter.value == "" then
goto continue
end
if type(filter.value) == "string" then
str = str .. filter.field .. ' LIKE \"%' .. filter.value .. '%\"'
str = str .. filter.field .. " LIKE \"%" .. filter.value .. "%\""
else
str = str .. filter.field .. ' == ' .. filter.value
str = str .. filter.field .. " == " .. filter.value
end
if i ~= #filters then
str = str .. ' AND '
str = str .. " AND "
end
::continue::
end
if str ~= '' then
str = ' WHERE ' .. str
if str ~= "" then
str = " WHERE " .. str
end
return str
end
local function compose_order_and_limit(page, page_size, sort)
local str = ''
local str = ""
if sort ~= nil and sort.prop ~= nil and sort.order ~= nil then
str = str .. ' ORDER BY ' .. sort.prop .. (sort.order == 'ascending' and ' ASC' or ' DESC')
str = str .. " ORDER BY " .. sort.prop .. (sort.order == "ascending" and " ASC" or " DESC")
end
if page ~= nil and page_size ~= nil then
str = str .. ' LIMIT ' .. page_size .. ' OFFSET ' .. (page - 1) * page_size
str = str .. " LIMIT " .. page_size .. " OFFSET " .. (page - 1) * page_size
end
return str
@@ -516,30 +621,28 @@ end
--
function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters, request_params)
local res = {}
--local query
local filters_str = compose_filter(filters)
local order_and_limit_str = compose_order_and_limit(page, page_size, sort)
if request_type == 'db_req_active_rules' then
if filters_str == '' then
filters_str = ' WHERE active = 1'
if request_type == "db_req_active_rules" then
if filters_str == "" then
filters_str = " WHERE active = 1"
else
filters_str = filters_str .. ' AND active = 1'
filters_str = filters_str .. " AND active = 1"
end
local main_part = 'FROM rules' .. filters_str
local main_part = "FROM rules" .. filters_str
local select = self:select_query_unpack_single('SELECT COUNT(*) ' .. main_part)
local select = self:select_query_unpack_single("SELECT COUNT(*) " .. main_part)
if select == nil then
res.error = "unable to get count of items"
return res
end
res.total = select['COUNT(*)']
res.total = select["COUNT(*)"]
res.rules = self:select_query("SELECT * " .. main_part .. order_and_limit_str)
if not res.rules then
@@ -547,22 +650,20 @@ function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters
res.total = nil
return res
end
elseif request_type == "db_req_tasks" then
if filters_str == '' then
filters_str = ' WHERE A.task_id == B.task_id'
if filters_str == "" then
filters_str = " WHERE A.task_id == B.task_id"
else
filters_str = filters_str .. ' AND A.task_id == B.task_id'
filters_str = filters_str .. " AND A.task_id == B.task_id"
end
local select = self:select_query_unpack_single('SELECT COUNT(*) FROM tasks A,task_status B' .. filters_str)
local select = self:select_query_unpack_single("SELECT COUNT(*) FROM tasks A,task_status B" .. filters_str)
if select == nil then
res.error = "unable to get count of items"
return res
end
res.total = select['COUNT(*)']
res.total = select["COUNT(*)"]
res.tasks = self:select_query([[
SELECT A.task_id,
@@ -588,44 +689,43 @@ function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters
return res
end
for _,task in ipairs(res.tasks) do
if task.task_type == 1 then -- CUSTOM_PROC = 1
task.task_params = self:select_query_unpack_single('SELECT proc_id,proc_image FROM task_params_proc WHERE task_id = ?', task.task_id)
for _, task in ipairs(res.tasks) do
if task.task_type == 1 then -- CUSTOM_PROC = 1
task.task_params = self:select_query_unpack_single(
"SELECT proc_id,proc_image FROM task_params_proc WHERE task_id = ?", task.task_id)
elseif task.task_type == 2 then -- CUSTOM_FS = 2
task.task_params = self:select_query_unpack_single('SELECT filepath,recursive FROM task_params_fs WHERE task_id = ?', task.task_id)
task.task_params = self:select_query_unpack_single(
"SELECT filepath,recursive FROM task_params_fs WHERE task_id = ?", task.task_id)
task.task_params.recursive = task.task_params.recursive == 1
end
end
elseif request_type == 'db_req_task_detects' then
elseif request_type == "db_req_task_detects" then
local task_info = self:select_query_unpack_single(self.queries.get_task, request_params.task_id)
if task_info == nil then
res.error = "unable to get task info"
return res
end
if filters_str == '' then
filters_str = ' WHERE A.task_id = ?'
if filters_str == "" then
filters_str = " WHERE A.task_id = ?"
else
filters_str = filters_str .. ' AND A.task_id = ?'
filters_str = filters_str .. " AND A.task_id = ?"
end
local item_params
local items_table
if task_info.objects_type == 1 then
item_params = ' A.proc_image,A.proc_id,'
items_table = ' task_result_process A,'
item_params = " A.proc_image,A.proc_id,"
items_table = " task_result_process A,"
else
item_params = ' A.filepath,A.sha256,'
items_table = ' task_result_file A,'
item_params = " A.filepath,A.sha256,"
items_table = " task_result_file A,"
end
local rule_params
local main_part
if task_info.custom_rules == nil then
rule_params = "C.rule_name,C.malware_class,C.rule_precision"
main_part = [[
FROM
@@ -637,7 +737,6 @@ function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters
AND A.object_id = B.object_id
]]
else
rule_params = "B.rule_id as rule_name"
main_part = [[
FROM
@@ -649,15 +748,15 @@ function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters
]]
end
local select = self:select_query_unpack_single('SELECT COUNT(*) ' .. main_part, request_params.task_id)
local select = self:select_query_unpack_single("SELECT COUNT(*) " .. main_part, request_params.task_id)
if select == nil then
res.error = "unable to get count of items"
return res
end
res.total = select['COUNT(*)']
res.total = select["COUNT(*)"]
res.detects = self:select_query('SELECT' .. item_params .. rule_params .. main_part .. order_and_limit_str, request_params.task_id)
res.detects = self:select_query("SELECT" .. item_params .. rule_params .. main_part .. order_and_limit_str, request_params.task_id)
if not res.detects then
res.error = "unable to get items"
res.total = nil
@@ -669,6 +768,7 @@ function CDatabaseEngine:db_request(request_type, page, page_size, sort, filters
return res
end
--
-- in: table, table
@@ -679,13 +779,12 @@ end
function CDatabaseEngine:map_columns(cols, rows)
assert(type(cols) == "table", "missing columns list")
assert(type(rows) == "table", "missing rows list")
__log.debugf("map_columns CDatabaseEngine")
if #rows == 0 then
return {}
end
return glue.map(rows, function(tk, row)
return glue.map(rows, function (tk, row)
row = row or tk or {}
local rrow = {}
for i, col in ipairs(cols) do
@@ -703,7 +802,6 @@ end
-- string as error result when execution was failed
function CDatabaseEngine:exec_query(query, ...)
assert(type(query) == "string", "missing query string to exec it")
__log.debugf("exec_query CDatabaseEngine, query: %s", query)
local err
local status, stmt = pcall(self.db.prepare, self.db, query)
@@ -711,7 +809,7 @@ function CDatabaseEngine:exec_query(query, ...)
__log.errorf("failed to prepare db exec query, %s", tostring(stmt))
return status, stmt
end
if select('#', ...) > 0 then
if select("#", ...) > 0 then
stmt:bind_values(...)
end
status, err = pcall(stmt)
@@ -731,15 +829,14 @@ end
-- * empty table otherways
function CDatabaseEngine:select_query(query, ...)
assert(type(query) == "string", "missing query string to select it")
__log.debugf("select_query CDatabaseEngine, query: %s", query)
local status, stmt = pcall(self.db.prepare, self.db, query)
if not status then
local error = tostring(stmt)
__log.errorf("failed to prepare db query: %s", error)
return {error = error}
return { error = error }
end
if select('#', ...) > 0 then
if select("#", ...) > 0 then
stmt:bind_values(...)
end
@@ -755,7 +852,7 @@ function CDatabaseEngine:select_query(query, ...)
return {}
end
for i=0,tonumber(stmt:columns())-1 do
for i = 0, tonumber(stmt:columns()) - 1 do
table.insert(cols, stmt:get_name(i))
end
pcall(stmt.finalize, stmt)
@@ -766,4 +863,4 @@ end
function CDatabaseEngine:select_query_unpack_single(query, ...)
local res = self:select_query(query, ...)
return res and res[1] or nil
end
end
+93 -60
View File
@@ -5,15 +5,28 @@ local cjson = require("cjson")
local ffi = require("ffi")
local osx = ffi.os == 'OSX' or nil
local linux = ffi.os == 'Linux' or nil
local win = ffi.abi'win' or nil
local osx = ffi.os == "OSX" or nil
local linux = ffi.os == "Linux" or nil
local win = ffi.abi "win" or nil
-- base config to actions engine
local cfg = {
config = {},
}
local YARA_DATABASE_FILE = "soldr_yara_v3.db"
local yara_rules_files = {
WINDOWS_JSON = "rules/kb_windows.json",
LINUX_JSON = "rules/kb_linux.json",
OSX_JSON = "rules/kb_osx.json",
MEMORY_JSON = "rules/kb_memory.json",
WINDOWS_YAR = "rules/kb_windows.yar",
LINUX_YAR = "rules/kb_linux.yar",
OSX_YAR = "rules/kb_osx.yar",
MEMORY_YAR = "rules/kb_memory.yar"
}
-- actions engine initialize
local acts_engine
@@ -21,24 +34,22 @@ local acts_engine
__api.set_recv_timeout(5000) -- 5s
__api.add_cbs({
data = function(src, data)
data = function (src, data)
__log.debugf("receive data from '%s' with data %s", src, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_data(src, data)
end,
file = function(src, path, name)
file = function (src, path, name)
__log.infof("receive file from '%s' with name '%s' path '%s'", src, name, path)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_file(src, path, name)
end,
-- text = function(src, text, name)
-- msg = function(src, msg, mtype)
action = function(src, data, name)
action = function (src, data, name)
__log.infof("receive action '%s' from '%s' with data %s", name, src, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -46,8 +57,7 @@ __api.add_cbs({
__log.infof("requested action '%s' was executed: %s", name, action_result)
return action_result
end,
control = function(cmtype, data)
control = function (cmtype, data)
__log.debugf("receive control msg '%s' with data %s", cmtype, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -69,7 +79,7 @@ __api.add_cbs({
})
-- main database
cfg.db = sqlite.open("soldr_yara_v2.db", "create")
cfg.db = sqlite.open(YARA_DATABASE_FILE, "create")
if not cfg.db then
__log.error("failled to open database")
return "failed"
@@ -85,66 +95,87 @@ else
-- add __gc to close database on exit module
local db_prox = newproxy(true)
getmetatable(db_prox).__gc = function() if cfg.db then cfg.db:close() end end
getmetatable(db_prox).__gc = function () if cfg.db then cfg.db:close() end end
cfg.db[db_prox] = true
end
-- rules meta
-- initialize rules
local function get_file_content(path)
assert((type(path) == "string"), "Invalid type of path parameter")
local file_content = __files[path]
if file_content then
return file_content
end
return __files[path:gsub("/", "\\")]
end
local function cleanup_json_files()
__files[yara_rules_files.WINDOWS_JSON] = nil
__files[yara_rules_files.LINUX_JSON] = nil
__files[yara_rules_files.OSX_JSON] = nil
__files[yara_rules_files.MEMORY_JSON] = nil
end
local function cleanup_yar_files()
__files[yara_rules_files.WINDOWS_YAR] = nil
__files[yara_rules_files.LINUX_YAR] = nil
__files[yara_rules_files.OSX_YAR] = nil
__files[yara_rules_files.MEMORY_YAR] = nil
end
local function init_rules()
-- TODO refactoring
do
local rules_meta_json
if win then
rules_meta_json = get_file_content(yara_rules_files.WINDOWS_JSON)
elseif linux then
rules_meta_json = get_file_content(yara_rules_files.LINUX_JSON)
elseif osx then
rules_meta_json = get_file_content(yara_rules_files.OSX_JSON)
else
error("unknown platform")
end
assert(type(rules_meta_json == "string"))
-- TODO handle rules_meta_json ~ nil
cfg.rules_meta_files = cjson.decode(rules_meta_json)
assert(type(cfg.rules_meta_files == "table"))
rules_meta_json = get_file_content(yara_rules_files.MEMORY_JSON)
assert(type(rules_meta_json == "string"))
-- TODO handle rules_meta_json ~ nil
cfg.rules_meta_mem = cjson.decode(rules_meta_json)
assert(type(cfg.rules_meta_mem == "table"))
end
cleanup_json_files()
do
local rules_meta_json
if win then
rules_meta_json = __files['rules/kb_windows.json']
cfg.config_suffix = "_win"
-- TODO handle slashes
cfg.filepath_library = __tmpdir .. "\\yara_scanner.dll"
cfg.rules_files = get_file_content(yara_rules_files.WINDOWS_YAR)
elseif linux then
rules_meta_json = __files['rules/kb_linux.json']
cfg.config_suffix = "_linux"
cfg.filepath_library = __tmpdir .. "/libyara_scanner.so"
cfg.rules_files = get_file_content(yara_rules_files.LINUX_YAR)
elseif osx then
rules_meta_json = __files['rules/kb_osx.json']
cfg.config_suffix = "_mac"
cfg.filepath_library = __tmpdir .. "/libyara_scanner.so"
cfg.rules_files = get_file_content(yara_rules_files.OSX_YAR)
else
error("unknown platform")
end
assert(type(rules_meta_json == "string"))
cfg.rules_meta_files = cjson.decode(rules_meta_json)
assert(type(cfg.rules_meta_files == "table"))
cfg.rules_mem = get_file_content(yara_rules_files.MEMORY_YAR)
rules_meta_json = __files['rules/kb_memory.json']
assert(type(rules_meta_json == "string"))
cleanup_yar_files()
cfg.rules_meta_mem = cjson.decode(rules_meta_json)
assert(type(cfg.rules_meta_mem == "table"))
collectgarbage("collect")
end
__files['rules/kb_windows.json'] = nil
__files['rules/kb_linux.json'] = nil
__files['rules/kb_osx.json'] = nil
__files['rules/kb_memory.json'] = nil
-- files
if win then
cfg.config_suffix = '_win'
cfg.filepath_library = __tmpdir .. '/yara_scanner.dll'
cfg.rules_files = __files['rules/kb_windows.yar']
elseif linux then
cfg.config_suffix = '_linux'
cfg.filepath_library = __tmpdir .. '/libyara_scanner.so'
cfg.rules_files = __files['rules/kb_linux.yar']
elseif osx then
cfg.config_suffix = '_mac'
cfg.filepath_library = __tmpdir .. '/libyara_scanner.so'
cfg.rules_files = __files['rules/kb_osx.yar']
else
error("unknown platform")
end
cfg.rules_mem = __files['rules/kb_memory.yar']
__files['rules/kb_windows.yar'] = nil
__files['rules/kb_linux.yar'] = nil
__files['rules/kb_osx.yar'] = nil
__files['rules/kb_memory.yar'] = nil
collectgarbage("collect")
init_rules()
-- os
@@ -160,13 +191,15 @@ acts_engine = CActsEngine(cfg)
__log.infof("module '%s' was started", __config.ctx.name)
acts_engine:push_event("yr_module_started", {reason = "regular start"})
acts_engine:push_event("yr_module_started", { reason = "regular start" })
acts_engine:run()
acts_engine:push_event("yr_module_stopped", {reason = "regular stop"})
acts_engine:push_event("yr_module_stopped", { reason = "regular stop" })
__api.del_cbs({ "data", "file", "action", "control" })
__log.infof("module '%s' was stopped", __config.ctx.name)
-- explicit destroy engines
acts_engine:cleanup()
-- explicit engine destroy
acts_engine = nil
collectgarbage("collect")
+96 -69
View File
@@ -2,73 +2,94 @@ local ffi = require("ffi")
local M = {}
if ffi.os == 'Linux' then
local function init_if_linux()
if ffi.os ~= "Linux" then
return
end
ffi.cdef [[
typedef int64_t off64_t;
ffi.cdef [[
typedef int64_t off64_t;
int open(const char *pathname, int flags);
int close(int fd);
int open(const char *pathname, int flags);
int close(int fd);
void* mmap(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
int munmap(void *addr, size_t length);
]]
void* mmap(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
int munmap(void *addr, size_t length);
]]
local function try_to_cdef_stat_x64()
if ffi.arch ~= "x64" then
return false
end
if pcall(ffi.typeof, "struct stat") then
-- struct "stat" was already defined
return false
end
ffi.cdef [[
struct stat {
uint64_t st_dev;
uint64_t st_ino;
uint64_t st_nlink;
uint32_t st_mode;
uint32_t st_uid;
uint32_t st_gid;
uint32_t __pad0;
uint64_t st_rdev;
int64_t st_size;
int64_t st_blksize;
int64_t st_blocks;
uint64_t st_atime;
uint64_t st_atime_nsec;
uint64_t st_mtime;
uint64_t st_mtime_nsec;
uint64_t st_ctime;
uint64_t st_ctime_nsec;
int64_t __unused[3];
};
]]
return true
end
if ffi.arch == 'x64' then
ffi.cdef [[
struct stat {
uint64_t st_dev;
uint64_t st_ino;
uint64_t st_nlink;
uint32_t st_mode;
uint32_t st_uid;
uint32_t st_gid;
uint32_t __pad0;
uint64_t st_rdev;
int64_t st_size;
int64_t st_blksize;
int64_t st_blocks;
uint64_t st_atime;
uint64_t st_atime_nsec;
uint64_t st_mtime;
uint64_t st_mtime_nsec;
uint64_t st_ctime;
uint64_t st_ctime_nsec;
int64_t __unused[3];
};
]]
local function try_to_cdef_stat_non_x64()
if ffi.arch == "x64" then
return false
end
if pcall(ffi.typeof, "struct stat") then
-- struct "stat" was already defined
return false
end
ffi.cdef [[
struct stat {
uint64_t st_dev;
uint8_t __pad0[4];
uint32_t __st_ino;
uint32_t st_mode;
uint32_t st_nlink;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_rdev;
uint8_t __pad3[4];
int64_t st_size;
uint32_t st_blksize;
uint64_t st_blocks;
uint32_t st_atime;
uint32_t st_atime_nsec;
uint32_t st_mtime;
uint32_t st_mtime_nsec;
uint32_t st_ctime;
uint32_t st_ctime_nsec;
uint64_t st_ino;
};
]]
return true
end
else
if not try_to_cdef_stat_x64() then
try_to_cdef_stat_non_x64()
end
ffi.cdef [[
struct stat {
uint64_t st_dev;
uint8_t __pad0[4];
uint32_t __st_ino;
uint32_t st_mode;
uint32_t st_nlink;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_rdev;
uint8_t __pad3[4];
int64_t st_size;
uint32_t st_blksize;
uint64_t st_blocks;
uint32_t st_atime;
uint32_t st_atime_nsec;
uint32_t st_mtime;
uint32_t st_mtime_nsec;
uint32_t st_ctime;
uint32_t st_ctime_nsec;
uint64_t st_ino;
};
]]
end
ffi.cdef [[
int fstat(int fd, struct stat *buf);
]]
ffi.cdef [[
int fstat(int fd, struct stat *buf);
]]
local MAP_FAILED = -1;
@@ -77,7 +98,6 @@ ffi.cdef [[
local MAP_PRIVATE = 2
function M.mmap_ro(filepath)
local fd = ffi.C.open(filepath, O_RDONLY)
if fd < 0 then
return nil
@@ -99,19 +119,23 @@ ffi.cdef [[
return nil;
end
return {fd = fd, addr = addr, size = size}
return { fd = fd, addr = addr, size = size }
end
function M.munmap(map)
assert(ffi.C.munmap(map.addr, map.size) == 0)
assert(ffi.C.close(map.fd))
end
end
elseif ffi.abi'win' then
local function init_if_windows()
if not (ffi.abi "win") then
return
end
local lk32 = require("waffi.windows.kernel32")
local INVALID_HANDLE_VALUE = -1;
local INVALID_HANDLE_VALUE = ffi.cast("HANDLE", -1);
local FILE_ATTRIBUTE_NORMAL = 0x00000080
local PAGE_READONLY = 0x02
@@ -131,7 +155,6 @@ elseif ffi.abi'win' then
end
function M.mmap_ro(filepath)
local hFile = lk32.CreateFileW(
wcs(filepath),
lk32.GENERIC_READ,
@@ -173,14 +196,18 @@ elseif ffi.abi'win' then
return nil;
end
return {hMap = hMap, addr = addr, size = size}
return { hMap = hMap, addr = addr, size = size }
end
function M.munmap(map)
assert(lk32.UnmapViewOfFile(map.addr))
assert(lk32.CloseHandle(map.hMap))
end
end
return M
-- TODO extract separate script files for Windows, Linux
init_if_linux()
init_if_windows()
return M
@@ -0,0 +1,4 @@
local ffi = require("ffi")
local os = string.lower(ffi.os)
local os_specific_process_api = ("process_api_%s"):format(os)
return require(os_specific_process_api)
@@ -0,0 +1,139 @@
local ffi = require("ffi")
local lfs = require("lfs")
local luapath = require("path")
local glue = require("glue")
local process_api = {}
local wait_kill_sleep = 300
ffi.cdef [[
typedef uint32_t pid_t;
int kill(pid_t proc_id, int sig);
pid_t getpid();
typedef struct pollfd
{
int fd;
short events;
short revents;
} pollfd_t;
typedef uint32_t nfds_t;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
]]
local function run_callback_safe(callback, args)
local result, retval1 = glue.pcall(callback, args)
if not result then
__log.error("callback failure: " .. retval1)
return false
end
return retval1
end
function process_api.get_process_path(pid)
local attrs = lfs.symlinkattributes(string.format("/proc/%s/exe", pid))
if type(attrs) ~= "table" then
return "", "not found"
elseif attrs["mode"] ~= "link" then
return "", "invalid process id"
elseif type(attrs["target"]) ~= "string" then
return "", "permission deny"
end
return attrs["target"]
end
function process_api.kill_process(pid)
local SIGNOP = 0
local SIGKILL = 9
if ffi.C.kill(pid, SIGKILL) ~= 0 then
return false
end
while ffi.C.kill(pid, SIGNOP) == 0 do
ffi.C.poll(nil, 0, wait_kill_sleep)
end
return true
end
-- might be a number or "self"
local function get_process_info_linux(proc_id_str)
assert(type(proc_id_str) == "string")
local file = io.open("/proc/" .. proc_id_str .. "/stat", "r")
if file ~= nil then
local info = file:read()
local _pid, _ppid = string.match(info or "", "(%S+) %S+ %S+ (%S+)")
local pid = tonumber(_pid)
local parent_pid = tonumber(_ppid)
file:close()
if pid ~= nil and parent_pid ~= nil then
return pid, parent_pid, false
else
return nil, nil, true
end
end
return nil, nil, true
end
function process_api.for_each_process(callback)
if not callback then
error("no callback provided")
end
local imagepath, err
for file in lfs.dir("/proc") do
if file == "." or file == ".." or tonumber(file) == nil then
goto continue
end
local attrs = lfs.attributes(string.format("/proc/%s", file))
if type(attrs) ~= "table" or attrs["mode"] ~= "directory" then
-- __log.debugf("Wrong attributes for '%s'", file)
goto continue
end
imagepath, err = process_api.get_process_path(file)
if err then
-- __log.debugf("Failed to get process path for '%s': %s", file, err)
goto continue
end
local pid, parent_pid
local name = luapath.file(imagepath)
pid, parent_pid, err = get_process_info_linux(file)
if err then
-- __log.debugf("Failed to get process info for '%s': %s", file, err)
goto continue
end
if pid == nil or parent_pid == nil then
__log.info("Invalid PID: -> " .. pid .. " expected ->" .. file)
end
local args = {
pid = tonumber(pid),
name = name,
parent_pid = tonumber(parent_pid),
path = imagepath,
}
if (run_callback_safe(callback, args)) then
return
end
::continue::
end
end
function process_api.update_agent_info()
local aid, apath
aid = get_process_info_linux("self")
apath = process_api.get_process_path("self")
return aid, apath
end
return process_api
@@ -0,0 +1,69 @@
local ffi = require("ffi")
local luapath = require("path")
local glue = require("glue")
local process_api_linux = require("process_api_linux")
local process_api = {}
local function run_callback_safe(callback, args)
local result, retval1 = glue.pcall(callback, args)
if not result then
__log.error("callback failure: " .. retval1)
return false
end
return retval1
end
function process_api.get_process_path(pid)
assert(type(pid) == "number", "PID should a number")
local cmd = "/bin/ps o comm=\"\" " .. tostring(pid) -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local imagepath = assert(cmd_handle:read("*all"), "failed to read from pipe")
imagepath = string.gsub(imagepath, "^%s*(.-)%s*$", "%1")
__log.debugf("handlers.osx.get_process_path for '%d' -> '%s'", pid, imagepath)
cmd_handle:close()
if imagepath ~= nil and imagepath ~= "" then
return imagepath, nil
else
return nil, "Not found"
end
end
function process_api.for_each_process(callback)
-- TODO move this to sysctl syscall to retrieve the process table.
local cmd = "/bin/ps axo pid=\"\",ppid=\"\",comm=\"\"" -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local cmd_res = assert(cmd_handle:read("*all"), "failed to read from pipe")
cmd_handle:close()
for str in string.gmatch(cmd_res, "([^" .. "\n" .. "]+)") do
local pid, parent_pid, imagepath = str:match("%s*(%S+)%s+(%S+) ([^.]+)")
if (imagepath ~= nil) then
imagepath = imagepath:gsub("^%s*(.-)%s*$", "%1")
end
__log.debugf("process_api.for_each_process PID -> '%s' PPID -> '%s' IMAGE -> '%s", pid, parent_pid, imagepath)
if pid ~= nil and parent_pid ~= nil and imagepath ~= nil then
local args = {
pid = tonumber(pid),
name = luapath.file(imagepath),
parent_pid = tonumber(parent_pid),
path = imagepath,
}
if (run_callback_safe(callback, args)) then
return
end
end
end
end
function process_api.update_agent_info()
local aid, apath
aid = ffi.C.getpid()
apath = process_api.get_process_path(aid)
return aid, apath
end
process_api.kill_process = process_api_linux.kill_process
return process_api
@@ -0,0 +1,160 @@
local bit = require("bit")
local ffi = require("ffi")
local glue = require("glue")
local lk32 = require("waffi.windows.kernel32")
local process_api = {}
local win_const = {
TH32CS_SNAPPROCESS = 0x00000002,
SYNCHRONIZE = 0x00100000,
INVALID_HANDLE_VALUE = ffi.cast("HANDLE", -1),
}
ffi.cdef [[
typedef struct tagPROCESSENTRY32W {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
WCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32W;
BOOL Process32FirstW( HANDLE hSnapshot, LPPROCESSENTRY32W lppe);
BOOL Process32NextW( HANDLE hSnapshot, LPPROCESSENTRY32W lppe);
]]
local function run_callback_safe(callback, args)
local result, retval1 = glue.pcall(callback, args)
if not result then
__log.error("callback failure: " .. retval1)
return false
end
return retval1
end
function process_api.create_buffer(size)
return size > 0 and
{ ptr = ffi.new("char[?]", size), size = size } or
{ ptr = nil, size = 0 }
end
function process_api.to_utf8(wstr, buffer)
if wstr == ffi.NULL then return "" end
buffer = buffer ~= nil and buffer or process_api.create_buffer(0)
local size = buffer.ptr and ffi.C.WideCharToMultiByte(lk32.CP_UTF8, 0, wstr, -1, buffer.ptr, buffer.size, nil, nil) or 0
if size == 0 then
size = ffi.C.WideCharToMultiByte(lk32.CP_UTF8, 0, wstr, -1, nil, 0, nil, nil)
if size == 0 then return "" end
buffer = process_api.create_buffer(size)
size = ffi.C.WideCharToMultiByte(lk32.CP_UTF8, 0, wstr, -1, buffer.ptr, buffer.size, nil, nil)
end
local utf8_string = size > 0 and ffi.string(buffer.ptr) or ""
return utf8_string, size
end
--[[
Call given callback for each process
Callback arg table: {
pid - process id
name - process name
parent_pid - process parent id
path - process image path
}
Callback return value:
bool - whether to stop iteration
--]]
function process_api.for_each_process(callback)
if not callback then
__log.error("no callback provided")
return
end
local proc_entry = ffi.new("PROCESSENTRY32W[1]")
proc_entry[0].dwSize = ffi.sizeof("PROCESSENTRY32W")
local snap_handle = lk32.CreateToolhelp32Snapshot(win_const.TH32CS_SNAPPROCESS, 0)
assert(snap_handle ~= win_const.INVALID_HANDLE_VALUE, "failed to get list of processes")
local name_buffer = process_api.create_buffer(1024)
if (lk32.Process32FirstW(snap_handle, proc_entry[0]) == 1) then
while (lk32.Process32NextW(snap_handle, proc_entry[0]) == 1) do
local pid = tonumber(proc_entry[0].th32ProcessID)
local args = {
pid = pid,
name = process_api.to_utf8(proc_entry[0].szExeFile, name_buffer),
parent_pid = tonumber(proc_entry[0].th32ParentProcessID),
}
if (run_callback_safe(callback, args)) then
break
end
end
else
__log.error("failed to get info from snapshot")
end
if snap_handle ~= ffi.NULL then
lk32.CloseHandle(snap_handle)
end
end
function process_api.get_last_error()
local err = lk32.GetLastError()
__log.debugf("winapi last err: %d", tonumber(err))
return err
end
function process_api.get_process_handle(pid)
local flags = bit.bor(lk32.PROCESS_QUERY_LIMITED_INFORMATION, lk32.PROCESS_TERMINATE, lk32.PROCESS_VM_READ, win_const.SYNCHRONIZE)
local handle = lk32.OpenProcess(flags, false, pid)
if handle == ffi.NULL then
return nil, process_api.get_last_error()
end
return handle, nil
end
function process_api.kill_process(pid)
local handle, error = process_api.get_process_handle(pid)
if error then
return false
end
if lk32.TerminateProcess(handle, 0) == 0 then
return false
end
lk32.WaitForSingleObject(handle, lk32.INFINITE)
return true
end
-- by using less priveleged handle we can get path for any process
-- GetModuleFileNameExA didn't work on w7x64
function process_api.get_process_path(pid)
local process_handle, err = lk32.OpenProcess(lk32.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
if process_handle == nil then
return "", err
end
local max_path = lk32.MAX_PATH
local filename = ffi.new("wchar_t[?]", max_path)
local filename_buffer = process_api.create_buffer(1024)
local size = ffi.new("DWORD[1]", 2048)
if lk32.QueryFullProcessImageNameW(process_handle, 0, filename, size) ~= 1 then
lk32.CloseHandle(process_handle)
return "", "failed to get process path"
end
lk32.CloseHandle(process_handle)
local path = process_api.to_utf8(filename, filename_buffer)
return path, nil
end
function process_api.update_agent_info()
local aid, apath
aid = tonumber(lk32.GetCurrentProcessId())
apath = process_api.get_process_path(aid):lower()
return aid, apath
end
return process_api
@@ -0,0 +1,19 @@
require("yaci")
local YaraUtils = newclass("YaraUtils")
local MINUTE_SECONDS = 60
function YaraUtils:init()
end
function YaraUtils:get_UTC_time()
return os.time(os.date("!*t"))
end
function YaraUtils:to_seconds(minutes)
assert((type(minutes) == "number"), "YaraUtils:to_seconds() ~ invalid minutes parameter")
return minutes * MINUTE_SECONDS
end
return YaraUtils
+20 -26
View File
@@ -122,7 +122,6 @@ yara_error_t yara_scan_task_stop(int task_id);
]]
function CYaraModule:init(library_filename)
assert(type(library_filename) == "string", "library filename must be a string")
self.module = ffi.load(library_filename)
@@ -143,8 +142,10 @@ end
function CYaraModule:set_callbacks(callback_result, callback_complete)
assert(self.module ~= nil, "module is not loaded")
assert(type(callback_result) == "nil" or type(callback_result) == "function", "callback_result must be a function or nil")
assert(type(callback_complete) == "nil" or type(callback_complete) == "function", "callback_complete must be a function or nil")
assert(type(callback_result) == "nil" or type(callback_result) == "function",
"callback_result must be a function or nil")
assert(type(callback_complete) == "nil" or type(callback_complete) == "function",
"callback_complete must be a function or nil")
local cbs = ffi.new("yara_callbacks_t[1]")
cbs[0].scan_task_result = callback_result
@@ -201,26 +202,26 @@ function CYaraModule:reload_rules(rules)
local rules_opened = {}
for tag,t in pairs(rules) do
for tag, t in pairs(rules) do
local map, own
if t.string ~= nil then
map = {
addr = ffi.cast("const void*", t.string),
size = #t.string
size = #t.string,
}
elseif t.filepath ~= nil then
map = assert(mmap.mmap_ro(t.filepath))
own = true
end
table.insert(rules_opened, {tag = tag, map = map, own = own})
table.insert(rules_opened, { tag = tag, map = map, own = own })
end
local load_items = ffi.new("yara_rule_load_item_t[?]", #rules_opened)
for i,rule in ipairs(rules_opened) do
for i, rule in ipairs(rules_opened) do
load_items[i - 1].tag = rule.tag
load_items[i - 1].string_data = rule.map.addr
load_items[i - 1].string_size = rule.map.size
@@ -228,7 +229,7 @@ function CYaraModule:reload_rules(rules)
local err = self.api.reload_rules(load_items, #rules_opened)
for _,rule in ipairs(rules_opened) do
for _, rule in ipairs(rules_opened) do
if rule.own then
--rule.map:free()
mmap.munmap(rule.map)
@@ -248,7 +249,7 @@ function CYaraModule:unload_rules(tags)
local unload_tags = ffi.new("uint32_t[?]", #tags)
for i,tag in ipairs(tags) do
for i, tag in ipairs(tags) do
unload_tags[i - 1] = tag
end
@@ -286,7 +287,7 @@ function CYaraModule:decode_result(result)
res.rules = {}
if result.rules_count ~= 0 then
for i=1,result.rules_count do
for i = 1, result.rules_count do
table.insert(res.rules, ffi.string(result.rule_names[i - 1]))
end
end
@@ -309,7 +310,7 @@ function CYaraModule:decode_error(err)
local msg
local decode_error_callback_c = ffi.cast("pfn_yara_cb_decode_error_t", function(str, _)
local decode_error_callback_c = ffi.cast("pfn_yara_cb_decode_error_t", function (str, _)
msg = ffi.string(str)
end)
@@ -324,7 +325,6 @@ function CYaraModule:decode_error(err)
end
function CYaraModule:prepare_process_path(path)
local _ = self
if path == nil then
@@ -349,15 +349,14 @@ function CYaraModule:scan_fs(filepath, recursive, tag, excludes)
local results = {}
local result_callback_c = ffi.cast("pfn_yara_cb_scan_result_file_t", function(err, result, _)
local result_callback_c = ffi.cast("pfn_yara_cb_scan_result_file_t", function (err, result, _)
local msg
if err ~= nil then
msg = self:decode_error(err[0])
end
local data = self:decode_result(result)
table.insert(results, {error = msg, filepath = data.filepath, sha256_filehash = data.sha256, rules = data.rules})
table.insert(results, { error = msg, filepath = data.filepath, sha256_filehash = data.sha256, rules = data.rules })
end)
local params = ffi.new("yara_scan_fs_params_t[1]")
@@ -365,11 +364,10 @@ function CYaraModule:scan_fs(filepath, recursive, tag, excludes)
params[0].recursive = recursive
if excludes ~= nil and #excludes ~= 0 then
params[0].excludes.size = #excludes
params[0].excludes.items = ffi.new("const char*[?]", #excludes)
for i,exclude in ipairs(excludes) do
for i, exclude in ipairs(excludes) do
params[0].excludes.items[i - 1] = exclude
end
else
@@ -395,15 +393,14 @@ function CYaraModule:scan_proc(pid, imagename, tag, excludes)
local results = {}
local result_callback_c = ffi.cast("pfn_yara_cb_scan_result_process_t", function(err, result, _)
local result_callback_c = ffi.cast("pfn_yara_cb_scan_result_process_t", function (err, result, _)
local msg
if err ~= nil then
msg = self:decode_error(err[0])
end
local data = self:decode_result(result)
table.insert(results, {error = msg, proc_image = data.imagepath, proc_id = data.pid, rules = data.rules})
table.insert(results, { error = msg, proc_image = data.imagepath, proc_id = data.pid, rules = data.rules })
end)
local params = ffi.new("yara_scan_proc_params_t[1]")
@@ -411,11 +408,10 @@ function CYaraModule:scan_proc(pid, imagename, tag, excludes)
params[0].imagename_pattern = self:prepare_process_path(imagename)
if excludes ~= nil and #excludes ~= 0 then
params[0].excludes.size = #excludes
params[0].excludes.items = ffi.new("const char*[?]", #excludes)
for i,exclude in ipairs(excludes) do
for i, exclude in ipairs(excludes) do
params[0].excludes.items[i - 1] = exclude
end
else
@@ -444,11 +440,10 @@ function CYaraModule:task_scan_fs(filepath, recursive, tag, excludes)
params[0].recursive = recursive
if excludes ~= nil and #excludes ~= 0 then
params[0].excludes.size = #excludes
params[0].excludes.items = ffi.new("const char*[?]", #excludes)
for i,exclude in ipairs(excludes) do
for i, exclude in ipairs(excludes) do
params[0].excludes.items[i - 1] = exclude
end
else
@@ -477,11 +472,10 @@ function CYaraModule:task_scan_proc(pid, imagename, tag, excludes)
params[0].imagename_pattern = self:prepare_process_path(imagename)
if excludes ~= nil and #excludes ~= 0 then
params[0].excludes.size = #excludes
params[0].excludes.items = ffi.new("const char*[?]", #excludes)
for i,exclude in ipairs(excludes) do
for i, exclude in ipairs(excludes) do
params[0].excludes.items[i - 1] = exclude
end
else
@@ -39,6 +39,44 @@
}
]
},
"yr_object_scan_proc_non_cached": {
"allOf": [
{
"$ref": "#/definitions/base.action"
},
{
"properties": {
"fields": {
"default": [
"object.process.fullpath",
"object.process.id"
],
"items": {
"enum": [
"object.process.fullpath",
"object.process.id"
],
"type": "string"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
},
"priority": {
"default": 78,
"maximum": 78,
"minimum": 78,
"type": "integer"
}
},
"required": [
"fields",
"priority"
],
"type": "object"
}
]
},
"yr_object_task_scan_proc": {
"allOf": [
{
@@ -151,6 +189,44 @@
}
]
},
"yr_subject_scan_proc_non_cached": {
"allOf": [
{
"$ref": "#/definitions/base.action"
},
{
"properties": {
"fields": {
"default": [
"subject.process.fullpath",
"subject.process.id"
],
"items": {
"enum": [
"subject.process.fullpath",
"subject.process.id"
],
"type": "string"
},
"maxItems": 2,
"minItems": 2,
"type": "array"
},
"priority": {
"default": 78,
"maximum": 78,
"minimum": 78,
"type": "integer"
}
},
"required": [
"fields",
"priority"
],
"type": "object"
}
]
},
"yr_subject_task_scan_proc": {
"allOf": [
{
@@ -344,9 +420,11 @@
},
"required": [
"yr_object_scan_proc",
"yr_object_scan_proc_non_cached",
"yr_object_task_scan_proc",
"yr_scan_fs",
"yr_subject_scan_proc",
"yr_subject_scan_proc_non_cached",
"yr_subject_task_scan_proc",
"yr_task_fastscan_fs",
"yr_task_fastscan_proc",
+20 -1
View File
@@ -321,8 +321,27 @@
"itemCollapse": true
}
}
},
"yara_process_caching_time": {
"default": 60,
"rules": {
"number": true,
"required": true
},
"type": "number",
"ui": {
"columns": 10,
"widget": "input-number",
"widgetConfig": {
"clearable": false,
"columns": 10,
"max": 240,
"min": 0,
"step": 1
}
}
}
},
"required": [],
"type": "object"
}
}
@@ -6,6 +6,13 @@
],
"priority": 78
},
"yr_object_scan_proc_non_cached": {
"fields": [
"object.process.fullpath",
"object.process.id"
],
"priority": 78
},
"yr_object_task_scan_proc": {
"fields": [
"object.process.fullpath",
@@ -26,6 +33,13 @@
],
"priority": 78
},
"yr_subject_scan_proc_non_cached": {
"fields": [
"subject.process.fullpath",
"subject.process.id"
],
"priority": 78
},
"yr_subject_task_scan_proc": {
"fields": [
"subject.process.fullpath",
@@ -379,5 +379,6 @@
"enabled": true,
"malware_class": "NET_WORM"
}
]
}
],
"yara_process_caching_time": 60
}
@@ -120,6 +120,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_high_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_object_process_matched_low": {
"actions": [
{
@@ -139,6 +158,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_low_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_object_process_matched_medium": {
"actions": [
{
@@ -158,6 +196,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_medium_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_process_matched_custom": {
"actions": [
{
@@ -194,6 +251,25 @@
],
"type": "atomic"
},
"yr_subject_process_matched_high_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_low": {
"actions": [
{
@@ -213,6 +289,25 @@
],
"type": "atomic"
},
"yr_subject_process_matched_low_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_medium": {
"actions": [
{
@@ -231,5 +326,24 @@
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_medium_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
}
}
@@ -6,6 +6,13 @@
],
"priority": 78
},
"yr_object_scan_proc_non_cached": {
"fields": [
"object.process.fullpath",
"object.process.id"
],
"priority": 78
},
"yr_object_task_scan_proc": {
"fields": [
"object.process.fullpath",
@@ -26,6 +33,13 @@
],
"priority": 78
},
"yr_subject_scan_proc_non_cached": {
"fields": [
"subject.process.fullpath",
"subject.process.id"
],
"priority": 78
},
"yr_subject_task_scan_proc": {
"fields": [
"subject.process.fullpath",
@@ -379,5 +379,6 @@
"enabled": true,
"malware_class": "NET_WORM"
}
]
}
],
"yara_process_caching_time": 60
}
@@ -120,6 +120,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_high_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_object_process_matched_low": {
"actions": [
{
@@ -139,6 +158,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_low_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_object_process_matched_medium": {
"actions": [
{
@@ -158,6 +196,25 @@
],
"type": "atomic"
},
"yr_object_process_matched_medium_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "atomic"
},
"yr_process_matched_custom": {
"actions": [
{
@@ -194,6 +251,25 @@
],
"type": "atomic"
},
"yr_subject_process_matched_high_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_low": {
"actions": [
{
@@ -213,6 +289,25 @@
],
"type": "atomic"
},
"yr_subject_process_matched_low_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_medium": {
"actions": [
{
@@ -231,5 +326,24 @@
"subject.process.id"
],
"type": "atomic"
},
"yr_subject_process_matched_medium_cached": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "atomic"
}
}
@@ -250,6 +250,45 @@
}
]
},
"yr_object_process_matched_high_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"items": {
"enum": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
},
"yr_object_process_matched_low": {
"allOf": [
{
@@ -289,6 +328,45 @@
}
]
},
"yr_object_process_matched_low_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"items": {
"enum": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
},
"yr_object_process_matched_medium": {
"allOf": [
{
@@ -328,6 +406,45 @@
}
]
},
"yr_object_process_matched_medium_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"items": {
"enum": [
"malware_class",
"object.process.fullpath",
"object.process.id",
"rule_name",
"rule_precision",
"rule_type"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
},
"yr_process_matched_custom": {
"allOf": [
{
@@ -402,6 +519,45 @@
}
]
},
"yr_subject_process_matched_high_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"items": {
"enum": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
},
"yr_subject_process_matched_low": {
"allOf": [
{
@@ -441,6 +597,45 @@
}
]
},
"yr_subject_process_matched_low_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"items": {
"enum": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
},
"yr_subject_process_matched_medium": {
"allOf": [
{
@@ -479,6 +674,45 @@
"type": "object"
}
]
},
"yr_subject_process_matched_medium_cached": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"items": {
"enum": [
"malware_class",
"rule_name",
"rule_precision",
"rule_type",
"subject.process.fullpath",
"subject.process.id"
],
"type": "string"
},
"maxItems": 6,
"minItems": 6,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
}
},
"required": [
@@ -489,12 +723,18 @@
"yr_module_started",
"yr_module_stopped",
"yr_object_process_matched_high",
"yr_object_process_matched_high_cached",
"yr_object_process_matched_low",
"yr_object_process_matched_low_cached",
"yr_object_process_matched_medium",
"yr_object_process_matched_medium_cached",
"yr_process_matched_custom",
"yr_subject_process_matched_high",
"yr_subject_process_matched_high_cached",
"yr_subject_process_matched_low",
"yr_subject_process_matched_medium"
"yr_subject_process_matched_low_cached",
"yr_subject_process_matched_medium",
"yr_subject_process_matched_medium_cached"
],
"type": "object"
}
+9 -1
View File
@@ -19,9 +19,11 @@
"system": false,
"actions": [
"yr_object_scan_proc",
"yr_object_scan_proc_non_cached",
"yr_object_task_scan_proc",
"yr_scan_fs",
"yr_subject_scan_proc",
"yr_subject_scan_proc_non_cached",
"yr_subject_task_scan_proc",
"yr_task_fastscan_fs",
"yr_task_fastscan_proc",
@@ -37,12 +39,18 @@
"yr_module_started",
"yr_module_stopped",
"yr_object_process_matched_high",
"yr_object_process_matched_high_cached",
"yr_object_process_matched_low",
"yr_object_process_matched_low_cached",
"yr_object_process_matched_medium",
"yr_object_process_matched_medium_cached",
"yr_process_matched_custom",
"yr_subject_process_matched_high",
"yr_subject_process_matched_high_cached",
"yr_subject_process_matched_low",
"yr_subject_process_matched_medium"
"yr_subject_process_matched_low_cached",
"yr_subject_process_matched_medium",
"yr_subject_process_matched_medium_cached"
],
"fields": [
"malware_class",
+99 -1
View File
@@ -89,6 +89,16 @@
"title": "Классы вредоносного ПО",
"description": "Файл признается вредоносным, если он принадлежит к одному из классов"
}
},
"yara_process_caching_time": {
"en": {
"title": "Storage period for process scan results (in minutes)",
"description": ""
},
"ru": {
"title": "Время хранения результатов сканирования процесса (в минутах)",
"description": ""
}
}
},
"secure_config": {},
@@ -225,6 +235,16 @@
"description": "Проверка процесса-объекта YARA-правилами в синхронном режиме"
}
},
"yr_object_scan_proc_non_cached": {
"en": {
"title": "Scan an object process with YARA rules in priority order (do not use results from cache)",
"description": "Synchronous scanning of an object process with YARA rules. Results from cache will not be used"
},
"ru": {
"title": "Проверить процесс-объект YARA-правилами в приоритетном порядке (не брать результаты из кэша)",
"description": "Проверка процесса-объекта YARA-правилами в синхронном режиме, результаты из кэша учитываться не будут"
}
},
"yr_object_task_scan_proc": {
"en": {
"title": "Start a scan task of an object process with YARA rules",
@@ -255,6 +275,16 @@
"description": "Проверка процесса-субъекта YARA-правилами в синхронном режиме"
}
},
"yr_subject_scan_proc_non_cached": {
"en": {
"title": "Scan a subject process with YARA rules in priority order (do not use results from cache)",
"description": "Synchronous scanning of a subject process with YARA rules. Results from cache will not be used"
},
"ru": {
"title": "Проверить процесс-субъект YARA-правилами в приоритетном порядке (не брать результаты из кэша)",
"description": "Проверка процесса-субъекта YARA-правилами в синхронном режиме, результаты из кэша учитываться не будут"
}
},
"yr_subject_task_scan_proc": {
"en": {
"title": "Start a scan task of a subject process with YARA rules",
@@ -387,6 +417,16 @@
"description": ""
}
},
"yr_object_process_matched_high_cached": {
"en": {
"title": "[Cache] The module detected a malicious object process. Severity: high",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил вредоносный процесс-объект. Уровень опасности: высокий",
"description": ""
}
},
"yr_object_process_matched_low": {
"en": {
"title": "The module detected a suspicious object process. Severity: low",
@@ -397,6 +437,16 @@
"description": ""
}
},
"yr_object_process_matched_low_cached": {
"en": {
"title": "[Cache] The module detected a suspicious object process. Severity: low",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил подозрительный процесс-объект. Уровень опасности: низкий",
"description": ""
}
},
"yr_object_process_matched_medium": {
"en": {
"title": "The module detected a suspicious object process. Severity: medium",
@@ -407,6 +457,16 @@
"description": ""
}
},
"yr_object_process_matched_medium_cached": {
"en": {
"title": "[Cache] The module detected a suspicious object process. Severity: medium",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил подозрительный процесс-объект. Уровень опасности: средний",
"description": ""
}
},
"yr_process_matched_custom": {
"en": {
"title": "The module detected a malicious process by custom rule",
@@ -427,6 +487,16 @@
"description": ""
}
},
"yr_subject_process_matched_high_cached": {
"en": {
"title": "[Cache] The module detected a malicious subject process. Severity: high",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил вредоносный процесс-субъект. Уровень опасности: высокий",
"description": ""
}
},
"yr_subject_process_matched_low": {
"en": {
"title": "The module detected a suspicious subject process. Severity: low",
@@ -437,6 +507,16 @@
"description": ""
}
},
"yr_subject_process_matched_low_cached": {
"en": {
"title": "[Cache] The module detected a suspicious subject process. Severity: low",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил подозрительный процесс-субъект. Уровень опасности: низкий",
"description": ""
}
},
"yr_subject_process_matched_medium": {
"en": {
"title": "The module detected a suspicious subject process. Severity: medium",
@@ -446,13 +526,25 @@
"title": "Модуль обнаружил подозрительный процесс-субъект. Уровень опасности: средний",
"description": ""
}
},
"yr_subject_process_matched_medium_cached": {
"en": {
"title": "[Cache] The module detected a suspicious subject process. Severity: medium",
"description": ""
},
"ru": {
"title": "[Кэш] Модуль обнаружил подозрительный процесс-субъект. Уровень опасности: средний",
"description": ""
}
}
},
"action_config": {
"yr_object_scan_proc": {},
"yr_object_scan_proc_non_cached": {},
"yr_object_task_scan_proc": {},
"yr_scan_fs": {},
"yr_subject_scan_proc": {},
"yr_subject_scan_proc_non_cached": {},
"yr_subject_task_scan_proc": {},
"yr_task_fastscan_fs": {},
"yr_task_fastscan_proc": {},
@@ -468,12 +560,18 @@
"yr_module_started": {},
"yr_module_stopped": {},
"yr_object_process_matched_high": {},
"yr_object_process_matched_high_cached": {},
"yr_object_process_matched_low": {},
"yr_object_process_matched_low_cached": {},
"yr_object_process_matched_medium": {},
"yr_object_process_matched_medium_cached": {},
"yr_process_matched_custom": {},
"yr_subject_process_matched_high": {},
"yr_subject_process_matched_high_cached": {},
"yr_subject_process_matched_low": {},
"yr_subject_process_matched_medium": {}
"yr_subject_process_matched_low_cached": {},
"yr_subject_process_matched_medium": {},
"yr_subject_process_matched_medium_cached": {}
},
"tags": {
"responder": {