151 lines
3.9 KiB
Python
151 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
|
#
|
|
|
|
import json
|
|
from typing import Any, List, MutableMapping, Mapping, Optional, Set
|
|
|
|
class QlOsStats:
|
|
"""Record basic OS statistics, such as API calls and strings.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.syscalls: MutableMapping[str, List] = {}
|
|
self.strings: MutableMapping[str, Set] = {}
|
|
|
|
self.position = 0
|
|
|
|
def clear(self):
|
|
"""Reset collected stats.
|
|
"""
|
|
|
|
self.syscalls.clear()
|
|
self.strings.clear()
|
|
|
|
self.position = 0
|
|
|
|
@staticmethod
|
|
def _banner(caption: str) -> List[str]:
|
|
bar = '-' * 24
|
|
|
|
return ['', caption, bar]
|
|
|
|
def summary(self) -> List[str]:
|
|
ret = []
|
|
|
|
ret.extend(QlOsStats._banner('syscalls called'))
|
|
|
|
for key, values in self.syscalls.items():
|
|
ret.append(f'{key}:')
|
|
ret.extend(f' {json.dumps(value):s}' for value in values)
|
|
|
|
ret.extend(QlOsStats._banner('strings ocurrences'))
|
|
|
|
for key, values in self.strings.items():
|
|
ret.append(f'{key}: {", ".join(str(word) for word in values)}')
|
|
|
|
return ret
|
|
|
|
def log_api_call(self, address: int, name: str, params: Mapping, retval: Any, retaddr: int) -> None:
|
|
"""Record API calls along with their details.
|
|
|
|
Args:
|
|
address : location of the calling instruction
|
|
name : api function name
|
|
params : mapping of the parameters name to their effective values
|
|
retval : value returned by the api function
|
|
retaddr : address to which the api function returned
|
|
"""
|
|
|
|
if name.startswith('hook_'):
|
|
name = name[5:]
|
|
|
|
self.syscalls.setdefault(name, []).append({
|
|
'params' : params,
|
|
'retval' : retval,
|
|
'address' : address,
|
|
'retaddr' : retaddr,
|
|
'position' : self.position
|
|
})
|
|
|
|
self.position += 1
|
|
|
|
def log_string(self, s: str) -> None:
|
|
"""Record strings appearance as they are encountered during emulation.
|
|
|
|
Args:
|
|
s : string to record
|
|
"""
|
|
|
|
for token in s.split(' '):
|
|
self.strings.setdefault(token, set()).add(self.position)
|
|
|
|
|
|
class QlWinStats(QlOsStats):
|
|
"""OS statistics object for Windows OS. Includes registry access stats.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.registry: MutableMapping[str, List] = {}
|
|
|
|
def clear(self):
|
|
super().clear()
|
|
|
|
self.registry.clear()
|
|
|
|
def summary(self) -> List[str]:
|
|
ret = super().summary()
|
|
|
|
ret.extend(QlOsStats._banner('registry keys accessed'))
|
|
|
|
for key, values in self.registry.items():
|
|
ret.append(f'{key}:')
|
|
ret.extend(f' {json.dumps(value):s}' for value in values)
|
|
|
|
return ret
|
|
|
|
def log_reg_access(self, key: str, item: Optional[str], type: Optional[int], value: Any) -> None:
|
|
"""Record registry access.
|
|
|
|
Args:
|
|
key : accessed key name
|
|
name : sub item name (if provided)
|
|
type : sub item type (if provided)
|
|
value : value set to item, in case of a registry modification
|
|
"""
|
|
|
|
self.registry.setdefault(key, []).append({
|
|
'item' : item,
|
|
'type' : type,
|
|
'value' : value,
|
|
'position' : self.position
|
|
})
|
|
|
|
|
|
class QlOsNullStats(QlOsStats):
|
|
"""Nullified OS statistics object.
|
|
"""
|
|
|
|
def clear(self):
|
|
pass
|
|
|
|
def summary(self) -> List[str]:
|
|
return []
|
|
|
|
def log_api_call(self, address: int, name: str, params: Mapping, retval: Any, retaddr: int) -> None:
|
|
pass
|
|
|
|
def log_string(self, s: str) -> None:
|
|
pass
|
|
|
|
|
|
class QlWinNullStats(QlOsNullStats):
|
|
"""Nullified Windows statistics object.
|
|
"""
|
|
|
|
def log_reg_access(self, key: str, item: Optional[str], type: Optional[int], value: Any) -> None:
|
|
pass
|