Skip to content

Commit 42d918c

Browse files
committed
Linux: Boottime API: Refactor Timespec Methods.
Move TimespecVol3 methods to an abstract class Timespec64Abstract, which is now inherited by Timespec64 and Timespec64Concrete.
1 parent bee2a39 commit 42d918c

File tree

2 files changed

+126
-118
lines changed

2 files changed

+126
-118
lines changed

volatility3/framework/symbols/linux/__init__.py

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@
33
#
44
import math
55
import contextlib
6-
import datetime
7-
import dataclasses
86
from abc import ABC, abstractmethod
97
from typing import Iterator, List, Tuple, Optional, Union
108

119
from volatility3 import framework
1210
from volatility3.framework import constants, exceptions, interfaces, objects
13-
from volatility3.framework.renderers import conversion
14-
from volatility3.framework.constants.linux import NSEC_PER_SEC
1511
from volatility3.framework.objects import utility
1612
from volatility3.framework.symbols import intermed
1713
from volatility3.framework.symbols.linux import extensions
@@ -836,105 +832,3 @@ def get_cached_pages(self) -> interfaces.objects.ObjectInterface:
836832
page = self.vmlinux.object("page", offset=page_addr, absolute=True)
837833
if page:
838834
yield page
839-
840-
841-
@dataclasses.dataclass
842-
class TimespecVol3(object):
843-
"""Internal helper class to handle all required timespec operations, convertions and
844-
adjustments.
845-
846-
NOTE: This is intended for exclusive use with get_boottime() and its related functions.
847-
"""
848-
849-
tv_sec: int = 0
850-
tv_nsec: int = 0
851-
852-
@classmethod
853-
def new_from_timespec(cls, timespec) -> "TimespecVol3":
854-
"""Creates a new instance from a TimespecVol3 or timespec64 object"""
855-
if not isinstance(timespec, (TimespecVol3, extensions.timespec64)):
856-
raise TypeError("It requires either a TimespecVol3 or timespec64 type")
857-
858-
tv_sec = int(timespec.tv_sec)
859-
tv_nsec = int(timespec.tv_nsec)
860-
return cls(tv_sec=tv_sec, tv_nsec=tv_nsec)
861-
862-
@classmethod
863-
def new_from_nsec(cls, nsec) -> "TimespecVol3":
864-
"""Creates a new instance from an integer in nanoseconds"""
865-
866-
# Based on ns_to_timespec64()
867-
if nsec > 0:
868-
tv_sec = nsec // NSEC_PER_SEC
869-
tv_nsec = nsec % NSEC_PER_SEC
870-
elif nsec < 0:
871-
tv_sec = -((-nsec - 1) // NSEC_PER_SEC) - 1
872-
rem = (-nsec - 1) % NSEC_PER_SEC
873-
tv_nsec = NSEC_PER_SEC - rem - 1
874-
else:
875-
tv_sec = tv_nsec = 0
876-
877-
return cls(tv_sec=tv_sec, tv_nsec=tv_nsec)
878-
879-
def to_datetime(self) -> datetime.datetime:
880-
"""Converts this TimespecVol3 to a UTC aware datetime"""
881-
return conversion.unixtime_to_datetime(
882-
self.tv_sec + self.tv_nsec / NSEC_PER_SEC
883-
)
884-
885-
def to_timedelta(self) -> datetime.timedelta:
886-
"""Converts this TimespecVol3 to timedelta"""
887-
return datetime.timedelta(seconds=self.tv_sec + self.tv_nsec / NSEC_PER_SEC)
888-
889-
def __add__(self, timespec) -> "TimespecVol3":
890-
"""Returns a new TimespecVol3 object that sums the current values with those
891-
in the timespec argument"""
892-
if not isinstance(timespec, (TimespecVol3, extensions.timespec64)):
893-
raise TypeError("Cannot add a TimespecVol3 to this object")
894-
895-
result = TimespecVol3(
896-
tv_sec=self.tv_sec + timespec.tv_sec,
897-
tv_nsec=self.tv_nsec + timespec.tv_nsec,
898-
)
899-
900-
result.normalize()
901-
902-
return result
903-
904-
def __sub__(self, timespec) -> "TimespecVol3":
905-
"""Returns a new TimespecVol3 object that subtracts the values in the timespec
906-
argument from the current object's values"""
907-
if not isinstance(timespec, (TimespecVol3, extensions.timespec64)):
908-
raise TypeError("Cannot substract this object to a TimespecVol3")
909-
910-
result = TimespecVol3(
911-
tv_sec=self.tv_sec - timespec.tv_sec,
912-
tv_nsec=self.tv_nsec - timespec.tv_nsec,
913-
)
914-
915-
result.normalize()
916-
917-
return result
918-
919-
def normalize(self):
920-
"""Normalize any overflow in tv_sec and tv_nsec after previous addition or subtractions"""
921-
# Based on kernel's set_normalized_timespec64()
922-
while self.tv_nsec >= NSEC_PER_SEC:
923-
self.tv_nsec -= NSEC_PER_SEC
924-
self.tv_sec += 1
925-
926-
while self.tv_nsec < 0:
927-
self.tv_nsec += NSEC_PER_SEC
928-
self.tv_sec -= 1
929-
930-
def negate(self):
931-
"""Returns a new TimespecVol3 object with the values of the current object negated"""
932-
933-
result = TimespecVol3(
934-
tv_sec=-self.tv_sec,
935-
tv_nsec=-self.tv_nsec,
936-
)
937-
938-
result.normalize()
939-
940-
return result

volatility3/framework/symbols/linux/extensions/__init__.py

Lines changed: 126 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33
#
44

5+
import abc
56
import collections.abc
67
import logging
78
import functools
@@ -18,12 +19,13 @@
1819
from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS
1920
from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES
2021
from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_STATES
21-
from volatility3.framework.constants.linux import CAPABILITIES, PT_FLAGS
22+
from volatility3.framework.constants.linux import CAPABILITIES, PT_FLAGS, NSEC_PER_SEC
2223
from volatility3.framework.layers import linear
2324
from volatility3.framework.objects import utility
2425
from volatility3.framework.symbols import generic, linux, intermed
2526
from volatility3.framework.symbols.linux.extensions import elf
2627

28+
2729
vollog = logging.getLogger(__name__)
2830

2931
# Keep these in a basic module, to prevent import cycles when symbol providers require them
@@ -431,10 +433,10 @@ def _get_task_start_time(self) -> datetime.timedelta:
431433
if start_time_obj_type_name != "timespec":
432434
# kernels >= 3.17 real_start_time and start_time are u64
433435
# kernels >= 5.5 uses start_boottime which is also a u64
434-
start_time = linux.TimespecVol3.new_from_nsec(start_time_obj)
436+
start_time = Timespec64Concrete.new_from_nsec(start_time_obj)
435437
else:
436438
# kernels < 3.17 real_start_time and start_time are timespec
437-
start_time = linux.TimespecVol3.new_from_timespec(start_time_obj)
439+
start_time = Timespec64Concrete.new_from_timespec(start_time_obj)
438440

439441
# This is relative to the boot time so it makes sense to be a timedelta.
440442
return start_time.to_timedelta()
@@ -508,8 +510,8 @@ def _get_time_namespace_boottime_offset(
508510

509511
return time_namespace_offsets.boottime
510512

511-
def _get_boottime_raw(self) -> "linux.TimespecVol3":
512-
"""Returns the boot time in a TimespecVol3."""
513+
def _get_boottime_raw(self) -> "Timespec64Concrete":
514+
"""Returns the boot time in a Timespec64Concrete object."""
513515

514516
vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self)
515517
if vmlinux.has_symbol("tk_core"):
@@ -522,7 +524,7 @@ def _get_boottime_raw(self) -> "linux.TimespecVol3":
522524
else:
523525
# 3.17 <= kernels < 4.10 - Tested on Ubuntu 4.4.0-142
524526
boottime_nsec = timekeeper.offs_real.tv64 - timekeeper.offs_boot.tv64
525-
return linux.TimespecVol3.new_from_nsec(boottime_nsec)
527+
return Timespec64Concrete.new_from_nsec(boottime_nsec)
526528

527529
elif vmlinux.has_symbol("timekeeper") and vmlinux.get_type(
528530
"timekeeper"
@@ -531,7 +533,7 @@ def _get_boottime_raw(self) -> "linux.TimespecVol3":
531533
timekeeper = vmlinux.object_from_symbol("timekeeper")
532534

533535
# timekeeper.wall_to_monotonic is timespec
534-
boottime = linux.TimespecVol3.new_from_timespec(
536+
boottime = Timespec64Concrete.new_from_timespec(
535537
timekeeper.wall_to_monotonic
536538
)
537539

@@ -542,7 +544,7 @@ def _get_boottime_raw(self) -> "linux.TimespecVol3":
542544
elif vmlinux.has_symbol("wall_to_monotonic"):
543545
# kernels < 3.4 - Tested on Debian7 3.2.0-4 (3.2.57-3+deb7u2)
544546
wall_to_monotonic = vmlinux.object_from_symbol("wall_to_monotonic")
545-
boottime = linux.TimespecVol3.new_from_timespec(wall_to_monotonic)
547+
boottime = Timespec64Concrete.new_from_timespec(wall_to_monotonic)
546548
if vmlinux.has_symbol("total_sleep_time"):
547549
# 2.6.23 <= kernels < 3.4 7c3f1a573237b90ef331267260358a0ec4ac9079
548550
total_sleep_time = vmlinux.object_from_symbol("total_sleep_time")
@@ -2168,12 +2170,124 @@ def get_capabilities(self) -> int:
21682170
return cap_value & self.get_kernel_cap_full()
21692171

21702172

2171-
class timespec64(objects.StructType):
2173+
class Timespec64Abstract(abc.ABC):
2174+
"""Abstract class to handle all required timespec64 operations, convertions and
2175+
adjustments."""
2176+
2177+
@classmethod
2178+
def new_from_timespec(cls, other) -> "Timespec64Concrete":
2179+
"""Creates a new instance from an Timespec64Abstract subclass object"""
2180+
if not isinstance(other, Timespec64Abstract):
2181+
raise TypeError("Requires an object subclass of Timespec64Abstract")
2182+
2183+
tv_sec = int(other.tv_sec)
2184+
tv_nsec = int(other.tv_nsec)
2185+
return Timespec64Concrete(tv_sec=tv_sec, tv_nsec=tv_nsec)
2186+
2187+
@classmethod
2188+
def new_from_nsec(cls, nsec) -> "Timespec64Concrete":
2189+
"""Creates a new instance from an integer in nanoseconds"""
2190+
2191+
# Based on ns_to_timespec64()
2192+
if nsec > 0:
2193+
tv_sec = nsec // NSEC_PER_SEC
2194+
tv_nsec = nsec % NSEC_PER_SEC
2195+
elif nsec < 0:
2196+
tv_sec = -((-nsec - 1) // NSEC_PER_SEC) - 1
2197+
rem = (-nsec - 1) % NSEC_PER_SEC
2198+
tv_nsec = NSEC_PER_SEC - rem - 1
2199+
else:
2200+
tv_sec = tv_nsec = 0
2201+
2202+
return Timespec64Concrete(tv_sec=tv_sec, tv_nsec=tv_nsec)
2203+
21722204
def to_datetime(self) -> datetime.datetime:
2173-
"""Returns the respective aware datetime"""
2205+
"""Converts this Timespec64Abstract subclass object to a UTC aware datetime"""
2206+
2207+
# pylint: disable=E1101
2208+
return conversion.unixtime_to_datetime(
2209+
self.tv_sec + self.tv_nsec / NSEC_PER_SEC
2210+
)
2211+
2212+
def to_timedelta(self) -> datetime.timedelta:
2213+
"""Converts this Timespec64Abstract subclass object to timedelta"""
2214+
# pylint: disable=E1101
2215+
return datetime.timedelta(seconds=self.tv_sec + self.tv_nsec / NSEC_PER_SEC)
2216+
2217+
def __add__(self, other) -> "Timespec64Concrete":
2218+
"""Returns a new Timespec64Concrete object that sums the current values with those
2219+
in the timespec argument"""
2220+
if not isinstance(other, Timespec64Abstract):
2221+
raise TypeError("Requires an object subclass of Timespec64Abstract")
2222+
2223+
# pylint: disable=E1101
2224+
result = Timespec64Concrete(
2225+
tv_sec=self.tv_sec + other.tv_sec,
2226+
tv_nsec=self.tv_nsec + other.tv_nsec,
2227+
)
2228+
2229+
result.normalize()
2230+
2231+
return result
2232+
2233+
def __sub__(self, other) -> "Timespec64Concrete":
2234+
"""Returns a new Timespec64Abstract object that subtracts the values in the timespec
2235+
argument from the current object's values"""
2236+
if not isinstance(other, Timespec64Abstract):
2237+
raise TypeError("Requires an object subclass of Timespec64Abstract")
2238+
2239+
# pylint: disable=E1101
2240+
result = Timespec64Concrete(
2241+
tv_sec=self.tv_sec - other.tv_sec,
2242+
tv_nsec=self.tv_nsec - other.tv_nsec,
2243+
)
2244+
2245+
result.normalize()
2246+
2247+
return result
2248+
2249+
def normalize(self):
2250+
"""Normalize any overflow in tv_sec and tv_nsec."""
2251+
# Based on kernel's set_normalized_timespec64()
2252+
2253+
# pylint: disable=E1101
2254+
while self.tv_nsec >= NSEC_PER_SEC:
2255+
self.tv_nsec -= NSEC_PER_SEC
2256+
self.tv_sec += 1
2257+
2258+
while self.tv_nsec < 0:
2259+
self.tv_nsec += NSEC_PER_SEC
2260+
self.tv_sec -= 1
2261+
2262+
def negate(self):
2263+
"""Returns a new Timespec64Concrete object with the values of the current object negated"""
2264+
# pylint: disable=E1101
2265+
result = Timespec64Concrete(
2266+
tv_sec=-self.tv_sec,
2267+
tv_nsec=-self.tv_nsec,
2268+
)
2269+
2270+
result.normalize()
2271+
2272+
return result
2273+
2274+
2275+
class Timespec64Concrete(Timespec64Abstract):
2276+
"""Handle all required timespec64 operations, convertions and adjustments.
2277+
This is used to dynamically create timespec64-like objects, each its own variables
2278+
and the same methods as a timespec64 object extension.
2279+
"""
2280+
2281+
def __init__(self, tv_sec=0, tv_nsec=0):
2282+
self.tv_sec = tv_sec
2283+
self.tv_nsec = tv_nsec
2284+
21742285

2175-
dt = conversion.unixtime_to_datetime(self.tv_sec + self.tv_nsec / 1e9)
2176-
return dt
2286+
class timespec64(Timespec64Abstract, objects.StructType):
2287+
"""Handle all required timespec64 operations, convertions and adjustments.
2288+
This works as an extension of the timespec64 object while maintaining the same methods
2289+
as a Timespec64Concrete object.
2290+
"""
21772291

21782292

21792293
class inode(objects.StructType):

0 commit comments

Comments
 (0)