dotnet: support property feature extraction (#1168)

This commit is contained in:
Mike Hunhoff
2022-09-09 12:09:41 -06:00
committed by GitHub
parent 580948e46b
commit 3c1cd67f60
15 changed files with 580 additions and 92 deletions

View File

@@ -36,6 +36,7 @@ from capa.features.common import (
Arch,
Format,
Feature,
FeatureAccess,
)
from capa.features.address import Address
from capa.features.extractors.base_extractor import BBHandle, InsnHandle, FunctionHandle
@@ -279,6 +280,10 @@ def get_data_path_by_name(name):
return os.path.join(CD, "data", "dotnet", "1c444ebeba24dcba8628b7dfe5fec7c6.exe_")
elif name.startswith("_692f"):
return os.path.join(CD, "data", "dotnet", "692f7fd6d198e804d6af98eb9e390d61.exe_")
elif name.startswith("_0953c"):
return os.path.join(CD, "data", "0953cc3b77ed2974b09e3a00708f88de931d681e2d0cb64afbaf714610beabe6.exe_")
elif name.startswith("_039a6"):
return os.path.join(CD, "data", "039a6336d0802a2255669e6867a5679c7eb83313dbc61fb1c7232147379bd304.exe_")
else:
raise ValueError("unexpected sample fixture: %s" % name)
@@ -758,6 +763,106 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
("_1c444", "function=0x1F68, bb=0x1F68, insn=0x1FF9", capa.features.insn.API("FromHbitmap"), False),
(
"_1c444",
"token=0x600002B",
capa.features.insn.Property("System.IO.FileInfo::Length", access=FeatureAccess.READ),
True,
), # MemberRef method
(
"_1c444",
"token=0x600002B",
capa.features.insn.Property("System.IO.FileInfo::Length"),
True,
), # MemberRef method
(
"_1c444",
"token=0x6000081",
capa.features.insn.API("System.Diagnostics.Process::Start"),
True,
), # MemberRef method
(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::UseShellExecute", access=FeatureAccess.WRITE
), # MemberRef method
True,
),
(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::WorkingDirectory", access=FeatureAccess.WRITE
), # MemberRef method
True,
),
(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::FileName", access=FeatureAccess.WRITE
), # MemberRef method
True,
),
(
"_1c444",
"token=0x6000087",
capa.features.insn.Property("Sockets.MySocket::reConnectionDelay", access=FeatureAccess.WRITE), # Field
True,
),
(
"_1c444",
"token=0x600008A",
capa.features.insn.Property("Sockets.MySocket::isConnected", access=FeatureAccess.WRITE), # Field
True,
),
(
"_1c444",
"token=0x600008A",
capa.features.insn.Property("Sockets.MySocket::onConnected", access=FeatureAccess.READ), # Field
True,
),
(
"_0953c",
"token=0x6000004",
capa.features.insn.Property("System.Diagnostics.Debugger::IsAttached", access=FeatureAccess.READ),
True,
), # MemberRef method
(
"_692f",
"token=0x6000006",
capa.features.insn.Property(
"System.Management.Automation.PowerShell::Streams", access=FeatureAccess.READ
), # MemberRef method
False,
),
(
"_039a6",
"token=0x6000007",
capa.features.insn.API("System.Reflection.Assembly::Load"),
True,
),
(
"_039a6",
"token=0x600001D",
capa.features.insn.Property("StagelessHollow.Arac::Marka", access=FeatureAccess.READ), # MethodDef method
True,
),
(
"_039a6",
"token=0x600001C",
capa.features.insn.Property("StagelessHollow.Arac::Marka", access=FeatureAccess.READ), # MethodDef method
False,
),
(
"_039a6",
"token=0x6000023",
capa.features.insn.Property(
"System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Task", access=FeatureAccess.READ
), # MemberRef method
False,
),
],
# order tests by (file, item)
# so that our LRU cache is most effective.
@@ -904,3 +1009,13 @@ def _1c444_dotnetfile_extractor():
@pytest.fixture
def _692f_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_692f"))
@pytest.fixture
def _0953c_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_0953c"))
@pytest.fixture
def _039a6_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_039a6"))

View File

@@ -147,6 +147,10 @@ def test_serialize_features():
roundtrip_feature(capa.features.file.Import("kernel32.IsWow64Process"))
roundtrip_feature(capa.features.file.Import("#11"))
roundtrip_feature(capa.features.insn.OperandOffset(0, 0x8))
roundtrip_feature(
capa.features.insn.Property("System.IO.FileInfo::Length", access=capa.features.common.FeatureAccess.READ)
)
roundtrip_feature(capa.features.insn.Property("System.IO.FileInfo::Length"))
def test_freeze_sample(tmpdir, z9324d_extractor):

View File

@@ -11,7 +11,12 @@ import textwrap
import fixtures
from fixtures import *
from fixtures import _692f_dotnetfile_extractor, _1c444_dotnetfile_extractor
from fixtures import (
_692f_dotnetfile_extractor,
_1c444_dotnetfile_extractor,
_039a6_dotnetfile_extractor,
_0953c_dotnetfile_extractor,
)
import capa.main
import capa.rules
@@ -469,3 +474,23 @@ def test_main_dotnet2(_692f_dotnetfile_extractor):
assert capa.main.main([path, "-j"]) == 0
assert capa.main.main([path, "-q"]) == 0
assert capa.main.main([path]) == 0
def test_main_dotnet3(_0953c_dotnetfile_extractor):
# tests rules can be loaded successfully and all output modes
path = _0953c_dotnetfile_extractor.path
assert capa.main.main([path, "-vv"]) == 0
assert capa.main.main([path, "-v"]) == 0
assert capa.main.main([path, "-j"]) == 0
assert capa.main.main([path, "-q"]) == 0
assert capa.main.main([path]) == 0
def test_main_dotnet4(_039a6_dotnetfile_extractor):
# tests rules can be loaded successfully and all output modes
path = _039a6_dotnetfile_extractor.path
assert capa.main.main([path, "-vv"]) == 0
assert capa.main.main([path, "-v"]) == 0
assert capa.main.main([path, "-j"]) == 0
assert capa.main.main([path, "-q"]) == 0
assert capa.main.main([path]) == 0

View File

@@ -585,3 +585,44 @@ def test_match_operand_offset():
# mismatching value
_, matches = match([r], {capa.features.insn.OperandOffset(0, 0x11): {1, 2}}, 0x0)
assert "test rule" not in matches
def test_match_property_access():
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- property/read: System.IO.FileInfo::Length
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert capa.features.insn.Property("System.IO.FileInfo::Length", capa.features.common.FeatureAccess.READ) in {
capa.features.insn.Property("System.IO.FileInfo::Length", capa.features.common.FeatureAccess.READ)
}
_, matches = match(
[r],
{capa.features.insn.Property("System.IO.FileInfo::Length", capa.features.common.FeatureAccess.READ): {1, 2}},
0x0,
)
assert "test rule" in matches
# mismatching access
_, matches = match(
[r],
{capa.features.insn.Property("System.IO.FileInfo::Length", capa.features.common.FeatureAccess.WRITE): {1, 2}},
0x0,
)
assert "test rule" not in matches
# mismatching value
_, matches = match(
[r],
{capa.features.insn.Property("System.IO.FileInfo::Size", capa.features.common.FeatureAccess.READ): {1, 2}},
0x0,
)
assert "test rule" not in matches

View File

@@ -15,6 +15,13 @@ def test_render_offset():
assert str(capa.features.insn.Offset(1)) == "offset(0x1)"
def test_render_property():
assert (
str(capa.features.insn.Property("System.IO.FileInfo::Length", access=capa.features.common.FeatureAccess.READ))
== "property/read(System.IO.FileInfo::Length)"
)
def test_render_meta_attack():
# Persistence::Boot or Logon Autostart Execution::Registry Run Keys / Startup Folder [T1547.001]
id = "T1543.003"

View File

@@ -14,7 +14,7 @@ import capa.rules
import capa.engine
import capa.features.common
from capa.features.file import FunctionName
from capa.features.insn import Number, Offset
from capa.features.insn import Number, Offset, Property
from capa.features.common import (
OS,
OS_LINUX,
@@ -27,6 +27,7 @@ from capa.features.common import (
Format,
String,
Substring,
FeatureAccess,
)
@@ -951,3 +952,41 @@ def test_arch_features():
children = list(r.statement.get_children())
assert (Arch(ARCH_AMD64) in children) == True
assert (Arch(ARCH_I386) not in children) == True
def test_property_access():
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- property/read: System.IO.FileInfo::Length
"""
)
)
assert r.evaluate({Property("System.IO.FileInfo::Length", access=FeatureAccess.READ): {1}}) == True
assert r.evaluate({Property("System.IO.FileInfo::Length"): {1}}) == False
assert r.evaluate({Property("System.IO.FileInfo::Length", access=FeatureAccess.WRITE): {1}}) == False
def test_property_access_symbol():
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- property/read: System.IO.FileInfo::Length = some property
"""
)
)
assert (
r.evaluate(
{Property("System.IO.FileInfo::Length", access=FeatureAccess.READ, description="some property"): {1}}
)
== True
)