mirror of
https://github.com/vxcontrol/soldr-modules.git
synced 2026-07-01 12:47:17 -04:00
feat: major update of yara scanner module which brings new caching of scans result with policy timeout
This commit is contained in:
+9
-1
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user