QueryData genYara(QueryContext& context) { QueryData results; Status status; auto paths = context.constraints["path"].getAll(EQUALS); auto patterns = context.constraints["pattern"].getAll(EQUALS); auto groups = context.constraints["sig_group"].getAll(EQUALS); auto sigfiles = context.constraints["sigfile"].getAll(EQUALS); // Must specify a path constraint and at least one of sig_group or sigfile. if (groups.size() == 0 && sigfiles.size() == 0) { return results; } // XXX: Abstract this into a common "get rules for group" function. ConfigDataInstance config; const auto& parser = config.getParser("yara"); if (parser == nullptr) { return results; } const auto& yaraParser = std::static_pointer_cast<YARAConfigParserPlugin>(parser); if (yaraParser == nullptr) { return results; } auto rules = yaraParser->rules(); // Store resolved paths in a vector of pairs. // Each pair has the first element as the path to scan and the second // element as the pattern which generated it. std::vector<std::pair<std::string, std::string> > path_pairs; // Expand patterns and push onto path_pairs. for (const auto& pattern : patterns) { std::vector<std::string> expanded_patterns; auto status = resolveFilePattern(pattern, expanded_patterns); if (!status.ok()) { VLOG(1) << "Could not expand pattern properly: " << status.toString(); return results; } for (const auto& resolved : expanded_patterns) { if (!isReadable(resolved)) { continue; } path_pairs.push_back(make_pair(resolved, pattern)); } } // Collect all paths specified too. for (const auto& path_string : paths) { if (!isReadable(path_string)) { continue; } path_pairs.push_back(make_pair(path_string, "")); } // Compile all sigfiles into a map. std::map<std::string, YR_RULES*> compiled_rules; for (const auto& file : sigfiles) { YR_RULES *rules = nullptr; std::string full_path; if (file[0] != '/') { full_path = std::string("/etc/osquery/yara/") + file; } else { full_path = file; } status = compileSingleFile(full_path, &rules); if (!status.ok()) { VLOG(1) << "YARA error: " << status.toString(); } else { compiled_rules[file] = rules; } } // Scan every path pair. for (const auto& path_pair : path_pairs) { // Scan using siggroups. for (const auto& group : groups) { if (rules.count(group) == 0) { continue; } VLOG(1) << "Scanning with group: " << group; status = doYARAScan(rules[group], path_pair.first.c_str(), path_pair.second, results, group, ""); if (!status.ok()) { VLOG(1) << "YARA error: " << status.toString(); } } // Scan using files. for (const auto& element : compiled_rules) { VLOG(1) << "Scanning with file: " << element.first; status = doYARAScan(element.second, path_pair.first.c_str(), path_pair.second, results, "", element.first); if (!status.ok()) { VLOG(1) << "YARA error: " << status.toString(); } } } // Cleanup compiled rules for (const auto& element : compiled_rules) { yr_rules_destroy(element.second); } return results; }
QueryData genYara(QueryContext& context) { QueryData results; auto paths = context.constraints["path"].getAll(EQUALS); auto patterns = context.constraints["pattern"].getAll(EQUALS); auto groups = context.constraints["sig_group"].getAll(EQUALS); auto sigfiles = context.constraints["sigfile"].getAll(EQUALS); // Must specify a path constraint and at least one of sig_group or sigfile. if (groups.size() == 0 && sigfiles.size() == 0) { return results; } // XXX: Abstract this into a common "get rules for group" function. auto parser = Config::getParser("yara"); if (parser == nullptr || parser.get() == nullptr) { LOG(ERROR) << "YARA config parser plugin has no pointer"; return results; } std::shared_ptr<YARAConfigParserPlugin> yaraParser; try { yaraParser = std::dynamic_pointer_cast<YARAConfigParserPlugin>(parser); } catch (const std::bad_cast& e) { LOG(ERROR) << "Error casting yara config parser plugin"; return results; } if (yaraParser == nullptr || yaraParser.get() == nullptr) { LOG(ERROR) << "YARA config parser plugin has no pointer"; return results; } auto& rules = yaraParser->rules(); // Store resolved paths in a vector of pairs. // Each pair has the first element as the path to scan and the second // element as the pattern which generated it. std::vector<std::pair<std::string, std::string> > path_pairs; // Expand patterns and push onto path_pairs. for (const auto& pattern : patterns) { std::vector<std::string> expanded_patterns; auto status = resolveFilePattern(pattern, expanded_patterns); if (!status.ok()) { VLOG(1) << "Could not expand pattern properly: " << status.toString(); return results; } for (const auto& resolved : expanded_patterns) { if (!isReadable(resolved)) { continue; } path_pairs.push_back(make_pair(resolved, pattern)); } } // Collect all paths specified too. for (const auto& path_string : paths) { if (!isReadable(path_string)) { continue; } path_pairs.push_back(make_pair(path_string, "")); } // Compile all sigfiles into a map. for (const auto& file : sigfiles) { // Check if this on-demand sigfile has not been used/compiled. if (rules.count(file) == 0) { auto path = (file[0] != '/') ? std::string("/etc/osquery/yara/") : ""; path += file; YR_RULES* tmp_rules = nullptr; auto status = compileSingleFile(path, &tmp_rules); if (!status.ok()) { VLOG(1) << "YARA error: " << status.toString(); } else { rules[file] = tmp_rules; groups.insert(file); } } } // Scan every path pair. for (const auto& path_pair : path_pairs) { // Scan using siggroups. for (const auto& group : groups) { if (rules.count(group) == 0) { continue; } VLOG(1) << "Scanning with group: " << group; auto status = doYARAScan(rules[group], path_pair.first.c_str(), path_pair.second, results, group, group); if (!status.ok()) { VLOG(1) << "YARA error: " << status.toString(); } } } return results; }
QueryData genYara(QueryContext& context) { QueryData results; // Must specify a path constraint and at least one of sig_group or sigfile. auto groups = context.constraints["sig_group"].getAll(EQUALS); auto sigfiles = context.constraints["sigfile"].getAll(EQUALS); if (groups.size() == 0 && sigfiles.size() == 0) { return results; } // This could be abstracted into a common "get rules for group" function. auto parser = Config::getParser("yara"); if (parser == nullptr || parser.get() == nullptr) { LOG(ERROR) << "YARA config parser plugin has no pointer"; return results; } std::shared_ptr<YARAConfigParserPlugin> yaraParser = nullptr; try { yaraParser = std::dynamic_pointer_cast<YARAConfigParserPlugin>(parser); } catch (const std::bad_cast& e) { LOG(ERROR) << "Error casting yara config parser plugin"; return results; } if (yaraParser == nullptr || yaraParser.get() == nullptr) { LOG(ERROR) << "YARA config parser plugin has no pointer"; return results; } auto& rules = yaraParser->rules(); // Store resolved paths in a vector of pairs. // Each pair has the first element as the path to scan and the second // element as the pattern which generated it. std::vector<std::pair<std::string, std::string> > path_pairs; // Expand patterns and push onto path_pairs. auto patterns = context.constraints["pattern"].getAll(EQUALS); for (const auto& pattern : patterns) { std::vector<std::string> expanded_patterns; if (!resolveFilePattern(pattern, expanded_patterns).ok()) { continue; } // Check that each resolved path is readable. for (const auto& resolved : expanded_patterns) { if (isReadable(resolved)) { path_pairs.push_back(make_pair(resolved, pattern)); } } } // Collect all paths specified too. auto paths = context.constraints["path"].getAll(EQUALS); for (const auto& path_string : paths) { if (isReadable(path_string)) { path_pairs.push_back(make_pair(path_string, "")); } } // Compile all sigfiles into a map. for (const auto& file : sigfiles) { // Check if this "ad-hoc" signature file has not been used/compiled. if (rules.count(file) == 0) { // If this is a relative path append the default yara search path. auto path = (file[0] != '/') ? std::string("/etc/osquery/yara/") : ""; path += file; YR_RULES* tmp_rules = nullptr; auto status = compileSingleFile(path, &tmp_rules); if (!status.ok()) { VLOG(1) << "YARA compile error: " << status.toString(); continue; } // Cache the compiled rules by setting the unique signature file path // as the lookup name. Additional signature file uses will skip the // compile step and be added as rule groups. rules[file] = tmp_rules; } // Assemble an "ad-hoc" group using the signature file path as the name. groups.insert(file); } // Scan every path pair. for (const auto& path_pair : path_pairs) { // Scan using the signature groups. for (const auto& group : groups) { if (rules.count(group) > 0) { doYARAScan(rules[group], path_pair.first.c_str(), path_pair.second, results, group, group); } } } return results; }