-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
more software signatures #1301
base: master
Are you sure you want to change the base?
more software signatures #1301
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -336,6 +336,38 @@ def find_function_ref_strings(function_name): | |
return strings | ||
|
||
|
||
def find_function_constants(function_name): | ||
""" | ||
Get all constants that are used as operands in the function with name `function_name`. | ||
|
||
:param function_name: The name of the function. | ||
:type function_name: str | ||
:return: a list of int/long constants referenced in the function as strings | ||
:rtype: list[str] | ||
""" | ||
try: | ||
function = getGlobalFunctions(function_name)[0] | ||
except (IndexError, TypeError): | ||
print("Error: Function {} not found.".format(function_name)) | ||
return [] | ||
|
||
constants = [] | ||
if function is not None: | ||
body = function.getBody() | ||
instruction_iterator = currentProgram.getListing().getInstructions(body, True) | ||
|
||
for instruction in instruction_iterator: | ||
for i in range(instruction.getNumOperands()): | ||
for operand in instruction.getOpObjects(i): | ||
try: | ||
value = operand.getValue() | ||
except AttributeError: | ||
continue | ||
if value is not None and isinstance(value, (int, long)): | ||
constants.append(str(value)) | ||
return constants | ||
Comment on lines
+355
to
+368
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I personally prefer less indentation, which in this case can be archived by adding: if function is None:
return [] Also: Why can function be none if we did not get an exception in the call to |
||
|
||
|
||
def get_fstring_from_functions(ghidra_analysis, key_string, call_args, called_fstrings): | ||
""" | ||
:param ghidra_analysis: instance of GhidraAnalysis | ||
|
@@ -412,6 +444,7 @@ def find_version_strings(input_data, ghidra_analysis, result_path): | |
print("Error: Function name not found.") | ||
return 1 | ||
result_list = find_function_ref_strings(function_name) | ||
result_list.extend(find_function_constants(function_name)) | ||
else: | ||
print("Error: Invalid mode.") | ||
return 1 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
import logging | ||
import re | ||
import string | ||
from typing import TYPE_CHECKING | ||
|
@@ -50,9 +52,26 @@ def get_version(self, input_string: str, meta_dict: dict) -> str: | |
pattern = re.compile(regex) | ||
version = pattern.search(input_string) | ||
if version is not None: | ||
return self._strip_leading_zeroes(version.group(0)) | ||
version_string = version.group(0) | ||
if '_sub_regex' in meta_dict: | ||
version_string = self._convert_version_str(version_string, meta_dict) | ||
else: | ||
version_string = self._strip_leading_zeroes(version_string) | ||
return version_string | ||
return '' | ||
|
||
def _convert_version_str(self, version_str: str, meta_dict: dict): | ||
""" | ||
The metadata entry "_sub_regex" can be used to change the version string if it does not have the expected | ||
format (e.g. add dots). The entry should contain a regex and replacement for `re.sub()` as JSON string | ||
""" | ||
try: | ||
sub_regex, replacement = json.loads(meta_dict['_sub_regex']) | ||
return re.sub(sub_regex, replacement, version_str) | ||
except json.JSONDecodeError: | ||
logging.warning(f'[{self.NAME}]: signature has invalid substitution regex: {meta_dict}') | ||
return '' | ||
Comment on lines
+63
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function should not take the rule file_libmagic {
meta:
software_name = "file"
// versions are stored as decimal int with three digits
// (first digit: major version, remaining two digits: minor version)
version_regex = "\\d{3}"
_version_function = "magic_version"
_sub_regex = "(\\d)(\\d{2})"
_sub_replacement = "\\1.\\2"
...
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, what is the difference between the version_regex and the _sub_regex? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. version_regex is for reading in the version from the matched string (if it doesn't have the default 1.2.3 form) and _sub_regex (short for substitution regex) is something new that I had to think of, because file/libmagic came with a version in the form XYY (e.g. 524) and there is no way to read this in as X.YY without it. _sub_regex and _sub_replacement are the inputs for re.sub() Why is XYY not enough in this case? Because it is stored as X.YY in the CVE data and we cannot match it otherwise |
||
|
||
@staticmethod | ||
def _get_summary(results: dict) -> list[str]: | ||
summary = set() | ||
|
@@ -86,9 +105,12 @@ def get_version_for_component(self, result, file_object: FileObject): | |
'mode': 'version_function', | ||
'function_name': result['meta']['_version_function'], | ||
} | ||
versions.update( | ||
extract_data_from_ghidra(file_object.file_path, input_data, config.backend.docker_mount_base_dir) | ||
ghidra_data = extract_data_from_ghidra( | ||
file_object.file_path, input_data, config.backend.docker_mount_base_dir | ||
) | ||
for version_str in ghidra_data: | ||
if version := self.get_version(version_str, result['meta']): | ||
versions.add(version) | ||
if '' in versions and len(versions) > 1: # if there are actual version results, remove the "empty" result | ||
versions.remove('') | ||
result['meta']['version'] = list(versions) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this raise a type error?