* fix #2307

---------

Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
This commit is contained in:
Harshit Wadhwani
2024-12-05 14:23:15 +05:30
committed by GitHub
parent f57f909e68
commit 28c0234339
5 changed files with 91 additions and 63 deletions

View File

@@ -6,7 +6,7 @@
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
import binascii
from typing import Union, Optional
from typing import Union, Literal, Optional, Annotated
from pydantic import Field, BaseModel, ConfigDict
@@ -209,168 +209,171 @@ def feature_from_capa(f: capa.features.common.Feature) -> "Feature":
class OSFeature(FeatureModel):
type: str = "os"
type: Literal["os"] = "os"
os: str
description: Optional[str] = None
class ArchFeature(FeatureModel):
type: str = "arch"
type: Literal["arch"] = "arch"
arch: str
description: Optional[str] = None
class FormatFeature(FeatureModel):
type: str = "format"
type: Literal["format"] = "format"
format: str
description: Optional[str] = None
class MatchFeature(FeatureModel):
type: str = "match"
type: Literal["match"] = "match"
match: str
description: Optional[str] = None
class CharacteristicFeature(FeatureModel):
type: str = "characteristic"
type: Literal["characteristic"] = "characteristic"
characteristic: str
description: Optional[str] = None
class ExportFeature(FeatureModel):
type: str = "export"
type: Literal["export"] = "export"
export: str
description: Optional[str] = None
class ImportFeature(FeatureModel):
type: str = "import"
type: Literal["import"] = "import"
import_: str = Field(alias="import")
description: Optional[str] = None
class SectionFeature(FeatureModel):
type: str = "section"
type: Literal["section"] = "section"
section: str
description: Optional[str] = None
class FunctionNameFeature(FeatureModel):
type: str = "function name"
type: Literal["function name"] = "function name"
function_name: str = Field(alias="function name")
description: Optional[str] = None
class SubstringFeature(FeatureModel):
type: str = "substring"
type: Literal["substring"] = "substring"
substring: str
description: Optional[str] = None
class RegexFeature(FeatureModel):
type: str = "regex"
type: Literal["regex"] = "regex"
regex: str
description: Optional[str] = None
class StringFeature(FeatureModel):
type: str = "string"
type: Literal["string"] = "string"
string: str
description: Optional[str] = None
class ClassFeature(FeatureModel):
type: str = "class"
type: Literal["class"] = "class"
class_: str = Field(alias="class")
description: Optional[str] = None
class NamespaceFeature(FeatureModel):
type: str = "namespace"
type: Literal["namespace"] = "namespace"
namespace: str
description: Optional[str] = None
class BasicBlockFeature(FeatureModel):
type: str = "basic block"
type: Literal["basic block"] = "basic block"
description: Optional[str] = None
class APIFeature(FeatureModel):
type: str = "api"
type: Literal["api"] = "api"
api: str
description: Optional[str] = None
class PropertyFeature(FeatureModel):
type: str = "property"
type: Literal["property"] = "property"
access: Optional[str] = None
property: str
description: Optional[str] = None
class NumberFeature(FeatureModel):
type: str = "number"
type: Literal["number"] = "number"
number: Union[int, float]
description: Optional[str] = None
class BytesFeature(FeatureModel):
type: str = "bytes"
type: Literal["bytes"] = "bytes"
bytes: str
description: Optional[str] = None
class OffsetFeature(FeatureModel):
type: str = "offset"
type: Literal["offset"] = "offset"
offset: int
description: Optional[str] = None
class MnemonicFeature(FeatureModel):
type: str = "mnemonic"
type: Literal["mnemonic"] = "mnemonic"
mnemonic: str
description: Optional[str] = None
class OperandNumberFeature(FeatureModel):
type: str = "operand number"
type: Literal["operand number"] = "operand number"
index: int
operand_number: int = Field(alias="operand number")
description: Optional[str] = None
class OperandOffsetFeature(FeatureModel):
type: str = "operand offset"
type: Literal["operand offset"] = "operand offset"
index: int
operand_offset: int = Field(alias="operand offset")
description: Optional[str] = None
Feature = Union[
OSFeature,
ArchFeature,
FormatFeature,
MatchFeature,
CharacteristicFeature,
ExportFeature,
ImportFeature,
SectionFeature,
FunctionNameFeature,
SubstringFeature,
RegexFeature,
StringFeature,
ClassFeature,
NamespaceFeature,
APIFeature,
PropertyFeature,
NumberFeature,
BytesFeature,
OffsetFeature,
MnemonicFeature,
OperandNumberFeature,
OperandOffsetFeature,
# Note! this must be last, see #1161
BasicBlockFeature,
Feature = Annotated[
Union[
OSFeature,
ArchFeature,
FormatFeature,
MatchFeature,
CharacteristicFeature,
ExportFeature,
ImportFeature,
SectionFeature,
FunctionNameFeature,
SubstringFeature,
RegexFeature,
StringFeature,
ClassFeature,
NamespaceFeature,
APIFeature,
PropertyFeature,
NumberFeature,
BytesFeature,
OffsetFeature,
MnemonicFeature,
OperandNumberFeature,
OperandOffsetFeature,
# Note! this must be last, see #1161
BasicBlockFeature,
],
Field(discriminator="type"),
]

View File

@@ -271,7 +271,12 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
@param checked: True, item checked, False item not checked
"""
if not isinstance(
item, (CapaExplorerStringViewItem, CapaExplorerInstructionViewItem, CapaExplorerByteViewItem)
item,
(
CapaExplorerStringViewItem,
CapaExplorerInstructionViewItem,
CapaExplorerByteViewItem,
),
):
# ignore other item types
return
@@ -433,11 +438,19 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
if isinstance(match.node, rd.StatementNode):
parent2 = self.render_capa_doc_statement_node(
parent, match, match.node.statement, [addr.to_capa() for addr in match.locations], doc
parent,
match,
match.node.statement,
[addr.to_capa() for addr in match.locations],
doc,
)
elif isinstance(match.node, rd.FeatureNode):
parent2 = self.render_capa_doc_feature_node(
parent, match, match.node.feature, [addr.to_capa() for addr in match.locations], doc
parent,
match,
match.node.feature,
[addr.to_capa() for addr in match.locations],
doc,
)
else:
raise RuntimeError("unexpected node type: " + str(match.node.type))
@@ -494,7 +507,13 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
for rule in rutils.capability_rules(doc):
rule_name = rule.meta.name
rule_namespace = rule.meta.namespace or ""
parent = CapaExplorerRuleItem(self.root_node, rule_name, rule_namespace, len(rule.matches), rule.source)
parent = CapaExplorerRuleItem(
self.root_node,
rule_name,
rule_namespace,
len(rule.matches),
rule.source,
)
for location_, match in rule.matches:
location = location_.to_capa()
@@ -529,12 +548,12 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
# inform model changes have ended
self.endResetModel()
def capa_doc_feature_to_display(self, feature: frzf.Feature):
def capa_doc_feature_to_display(self, feature: frzf.Feature) -> str:
"""convert capa doc feature type string to display string for ui
@param feature: capa feature read from doc
"""
key = feature.type
key = str(feature.type)
value = feature.dict(by_alias=True).get(feature.type)
if value:
@@ -640,7 +659,10 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
assert isinstance(addr, frz.Address)
if location == addr.value:
return CapaExplorerStringViewItem(
parent, display, location, '"' + capa.features.common.escape_string(capture) + '"'
parent,
display,
location,
'"' + capa.features.common.escape_string(capture) + '"',
)
# programming error: the given location should always be found in the regex matches
@@ -671,7 +693,10 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
elif isinstance(feature, frzf.StringFeature):
# display string preview
return CapaExplorerStringViewItem(
parent, display, location, f'"{capa.features.common.escape_string(feature.string)}"'
parent,
display,
location,
f'"{capa.features.common.escape_string(feature.string)}"',
)
elif isinstance(
@@ -713,7 +738,11 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
# recursive search for all instances of old function name
for model_index in self.match(
root_index, QtCore.Qt.DisplayRole, old_name, hits=-1, flags=QtCore.Qt.MatchRecursive
root_index,
QtCore.Qt.DisplayRole,
old_name,
hits=-1,
flags=QtCore.Qt.MatchRecursive,
):
if not isinstance(model_index.internalPointer(), CapaExplorerFunctionItem):
continue

View File

@@ -168,7 +168,7 @@ def render_feature(
):
console.write(" " * indent)
key = feature.type
key = str(feature.type)
value: Optional[str]
if isinstance(feature, frzf.BasicBlockFeature):
# i don't think it makes sense to have standalone basic block features.

View File

@@ -125,7 +125,6 @@ def collect(args):
key = str(file)
for backend in BACKENDS:
if (backend, file.name) in {
("binja", "0953cc3b77ed2974b09e3a00708f88de931d681e2d0cb64afbaf714610beabe6.exe_")
}:

View File

@@ -75,7 +75,6 @@ def _render_expression_tree(
tree_index: int,
o: io.StringIO,
):
expression_index = operand.expression_index[tree_index]
expression = be2.expression[expression_index]
children_tree_indexes: list[int] = expression_tree[tree_index]
@@ -124,7 +123,6 @@ def _render_expression_tree(
return
elif expression.type == BinExport2.Expression.OPERATOR:
if len(children_tree_indexes) == 1:
# prefix operator, like "ds:"
if expression.symbol != "!":
@@ -250,7 +248,6 @@ def inspect_instruction(be2: BinExport2, instruction: BinExport2.Instruction, ad
def main(argv=None):
if argv is None:
argv = sys.argv[1:]