Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 8 additions & 19 deletions capa/features/extractors/viv/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,21 @@
from capa.features.common import Feature, Characteristic
from capa.features.address import Address, AbsoluteVirtualAddress
from capa.features.extractors import loops
from capa.features.extractors.elf import SymTab
from capa.features.extractors.viv.helpers import get_elf_symbol_functions
from capa.features.extractors.base_extractor import FunctionHandle


def extract_function_symtab_names(
fh: FunctionHandle,
) -> Iterator[tuple[Feature, Address]]:
if fh.inner.vw.metadata["Format"] == "elf":
# the file's symbol table gets added to the metadata of the vivisect workspace.
# this is in order to eliminate the computational overhead of refetching symtab each time.
if "symtab" not in fh.ctx["cache"]:
try:
fh.ctx["cache"]["symtab"] = SymTab.from_viv(fh.inner.vw.parsedbin)
except Exception:
fh.ctx["cache"]["symtab"] = None

symtab = fh.ctx["cache"]["symtab"]
if symtab:
for symbol in symtab.get_symbols():
sym_name = symtab.get_name(symbol)
sym_value = symbol.value
sym_info = symbol.info

STT_FUNC = 0x2
if sym_value == fh.address and sym_info & STT_FUNC != 0:
yield FunctionName(sym_name), fh.address
# index the symbol table once and cache it, to avoid rescanning it for each function.
if "elf_symbol_functions" not in fh.ctx["cache"]:
fh.ctx["cache"]["elf_symbol_functions"] = get_elf_symbol_functions(fh.inner.vw.parsedbin)

funcs = fh.ctx["cache"]["elf_symbol_functions"]
for sym_name in funcs.get(fh.address, ()):
yield FunctionName(sym_name), fh.address


def extract_function_calls_to(fhandle: FunctionHandle) -> Iterator[tuple[Feature, Address]]:
Expand Down
32 changes: 31 additions & 1 deletion capa/features/extractors/viv/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Optional
import struct
import logging
import collections
from typing import Any, Optional

from vivisect import VivWorkspace
from vivisect.const import XR_TO, REF_CODE

logger = logging.getLogger(__name__)

STT_FUNC = 0x2
STT_GNU_IFUNC = 0xA


def get_coderef_from(vw: VivWorkspace, va: int) -> Optional[int]:
"""
Expand All @@ -28,3 +36,25 @@ def get_coderef_from(vw: VivWorkspace, va: int) -> Optional[int]:
return xrefs[0][XR_TO]
else:
return None


def get_elf_symbol_functions(parsedbin: Any) -> dict[int, list[str]]:
"""index elf function symbol names by address."""
symbol_functions: dict[int, list[str]] = collections.defaultdict(list)
try:
from capa.features.extractors.elf import SymTab

symtab = SymTab.from_viv(parsedbin)
if symtab:
for symbol in symtab.get_symbols():
if (symbol.info & 0xF) in (STT_FUNC, STT_GNU_IFUNC):
try:
name = symtab.get_name(symbol)
except (ValueError, UnicodeDecodeError):
continue
if name:
symbol_functions[symbol.value].append(name)
except (ValueError, struct.error, IndexError, AttributeError) as e:
logger.debug("failed to parse elf symbol table: %s", e)

return symbol_functions
31 changes: 9 additions & 22 deletions capa/features/extractors/viv/insn.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

from typing import Callable, Iterator, cast

import Elf
import envi
import envi.exc
import viv_utils
Expand All @@ -30,7 +29,6 @@
from capa.features.insn import API, MAX_STRUCTURE_SIZE, Number, Offset, Mnemonic, OperandNumber, OperandOffset
from capa.features.common import MAX_BYTES_FEATURE_SIZE, THUNK_CHAIN_DEPTH_DELTA, Bytes, String, Feature, Characteristic
from capa.features.address import Address, AbsoluteVirtualAddress
from capa.features.extractors.elf import SymTab
from capa.features.extractors.base_extractor import BBHandle, InsnHandle, FunctionHandle
from capa.features.extractors.viv.indirect_calls import NotFoundError, resolve_indirect_call

Expand Down Expand Up @@ -102,26 +100,15 @@ def extract_insn_api_features(fh: FunctionHandle, bb, ih: InsnHandle) -> Iterato
return

if f.vw.metadata["Format"] == "elf":
if "symtab" not in fh.ctx["cache"]:
# the symbol table gets stored as a function's attribute in order to avoid running
# this code every time the call is made, thus preventing the computational overhead.
try:
parsedbin = f.vw.parsedbin
assert isinstance(parsedbin, Elf.Elf)
fh.ctx["cache"]["symtab"] = SymTab.from_viv(parsedbin)
except Exception:
fh.ctx["cache"]["symtab"] = None

symtab = fh.ctx["cache"]["symtab"]
if symtab:
for symbol in symtab.get_symbols():
sym_name = symtab.get_name(symbol)
sym_value = symbol.value
sym_info = symbol.info

STT_FUNC = 0x2
if sym_value == target and sym_info & STT_FUNC != 0:
yield API(sym_name), ih.address
# index the symbol table once and cache it, to avoid rescanning it for each call instruction.
if "elf_symbol_functions" not in fh.ctx["cache"]:
fh.ctx["cache"]["elf_symbol_functions"] = capa.features.extractors.viv.helpers.get_elf_symbol_functions(
f.vw.parsedbin
)

funcs = fh.ctx["cache"]["elf_symbol_functions"]
for sym_name in funcs.get(target, ()):
yield API(sym_name), ih.address

if viv_utils.flirt.is_library_function(f.vw, target):
name = viv_utils.get_function_name(f.vw, target)
Expand Down
Loading