|
4 | 4 |
|
5 | 5 | import collections.abc |
6 | 6 | import logging |
| 7 | +import stat |
| 8 | +from datetime import datetime |
7 | 9 | import socket as socket_module |
8 | | -from typing import Generator, Iterable, Iterator, Optional, Tuple, List |
| 10 | +from typing import Generator, Iterable, Iterator, Optional, Tuple, List, Union |
9 | 11 |
|
10 | 12 | from volatility3.framework import constants, exceptions, objects, interfaces, symbols |
| 13 | +from volatility3.framework.renderers import conversion |
11 | 14 | from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY |
12 | 15 | from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS |
13 | 16 | from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS |
@@ -1761,3 +1764,136 @@ def get_capabilities(self) -> int: |
1761 | 1764 | ) |
1762 | 1765 |
|
1763 | 1766 | return cap_value & self.get_kernel_cap_full() |
| 1767 | + |
| 1768 | + |
| 1769 | +class timespec64(objects.StructType): |
| 1770 | + def to_datetime(self) -> datetime: |
| 1771 | + """Returns the respective aware datetime""" |
| 1772 | + |
| 1773 | + dt = conversion.unixtime_to_datetime(self.tv_sec + self.tv_nsec / 1e9) |
| 1774 | + return dt |
| 1775 | + |
| 1776 | + |
| 1777 | +class inode(objects.StructType): |
| 1778 | + def is_valid(self) -> bool: |
| 1779 | + # i_count is a 'signed' counter (atomic_t). Smear, or essentially a wrong inode |
| 1780 | + # pointer, will easily cause an integer overflow here. |
| 1781 | + return self.i_ino > 0 and self.i_count.counter >= 0 |
| 1782 | + |
| 1783 | + @property |
| 1784 | + def is_dir(self) -> bool: |
| 1785 | + """Returns True if the inode is a directory""" |
| 1786 | + return stat.S_ISDIR(self.i_mode) != 0 |
| 1787 | + |
| 1788 | + @property |
| 1789 | + def is_reg(self) -> bool: |
| 1790 | + """Returns True if the inode is a regular file""" |
| 1791 | + return stat.S_ISREG(self.i_mode) != 0 |
| 1792 | + |
| 1793 | + @property |
| 1794 | + def is_link(self) -> bool: |
| 1795 | + """Returns True if the inode is a symlink""" |
| 1796 | + return stat.S_ISLNK(self.i_mode) != 0 |
| 1797 | + |
| 1798 | + @property |
| 1799 | + def is_fifo(self) -> bool: |
| 1800 | + """Returns True if the inode is a FIFO""" |
| 1801 | + return stat.S_ISFIFO(self.i_mode) != 0 |
| 1802 | + |
| 1803 | + @property |
| 1804 | + def is_sock(self) -> bool: |
| 1805 | + """Returns True if the inode is a socket""" |
| 1806 | + return stat.S_ISSOCK(self.i_mode) != 0 |
| 1807 | + |
| 1808 | + @property |
| 1809 | + def is_block(self) -> bool: |
| 1810 | + """Returns True if the inode is a block device""" |
| 1811 | + return stat.S_ISBLK(self.i_mode) != 0 |
| 1812 | + |
| 1813 | + @property |
| 1814 | + def is_char(self) -> bool: |
| 1815 | + """Returns True if the inode is a char device""" |
| 1816 | + return stat.S_ISCHR(self.i_mode) != 0 |
| 1817 | + |
| 1818 | + @property |
| 1819 | + def is_sticky(self) -> bool: |
| 1820 | + """Returns True if the sticky bit is set""" |
| 1821 | + return (self.i_mode & stat.S_ISVTX) != 0 |
| 1822 | + |
| 1823 | + def get_inode_type(self) -> Union[str, None]: |
| 1824 | + """Returns inode type name |
| 1825 | +
|
| 1826 | + Returns: |
| 1827 | + The inode type name |
| 1828 | + """ |
| 1829 | + if self.is_dir: |
| 1830 | + return "DIR" |
| 1831 | + elif self.is_reg: |
| 1832 | + return "REG" |
| 1833 | + elif self.is_link: |
| 1834 | + return "LNK" |
| 1835 | + elif self.is_fifo: |
| 1836 | + return "FIFO" |
| 1837 | + elif self.is_sock: |
| 1838 | + return "SOCK" |
| 1839 | + elif self.is_char: |
| 1840 | + return "CHR" |
| 1841 | + elif self.is_block: |
| 1842 | + return "BLK" |
| 1843 | + else: |
| 1844 | + return None |
| 1845 | + |
| 1846 | + def _time_member_to_datetime(self, member) -> datetime: |
| 1847 | + if self.has_member(f"{member}_sec") and self.has_member(f"{member}_nsec"): |
| 1848 | + # kernels >= 6.11 it's i_*_sec -> time64_t and i_*_nsec -> u32 |
| 1849 | + # Ref Linux commit 3aa63a569c64e708df547a8913c84e64a06e7853 |
| 1850 | + return conversion.unixtime_to_datetime( |
| 1851 | + self.member(f"{member}_sec") + self.has_member(f"{member}_nsec") / 1e9 |
| 1852 | + ) |
| 1853 | + elif self.has_member(f"__{member}"): |
| 1854 | + # 6.6 <= kernels < 6.11 it's a timespec64 |
| 1855 | + # Ref Linux commit 13bc24457850583a2e7203ded05b7209ab4bc5ef / 12cd44023651666bd44baa36a5c999698890debb |
| 1856 | + return self.member(f"__{member}").to_datetime() |
| 1857 | + elif self.has_member(member): |
| 1858 | + # In kernels < 6.6 it's a timespec64 or timespec |
| 1859 | + return self.member(member).to_datetime() |
| 1860 | + else: |
| 1861 | + raise exceptions.VolatilityException( |
| 1862 | + "Unsupported kernel inode type implementation" |
| 1863 | + ) |
| 1864 | + |
| 1865 | + def get_access_time(self) -> datetime: |
| 1866 | + """Returns the inode's last access time |
| 1867 | + This is updated when inode contents are read |
| 1868 | +
|
| 1869 | + Returns: |
| 1870 | + A datetime with the inode's last access time |
| 1871 | + """ |
| 1872 | + return self._time_member_to_datetime("i_atime") |
| 1873 | + |
| 1874 | + def get_modification_time(self) -> datetime: |
| 1875 | + """Returns the inode's last modification time |
| 1876 | + This is updated when the inode contents change |
| 1877 | +
|
| 1878 | + Returns: |
| 1879 | + A datetime with the inode's last data modification time |
| 1880 | + """ |
| 1881 | + |
| 1882 | + return self._time_member_to_datetime("i_mtime") |
| 1883 | + |
| 1884 | + def get_change_time(self) -> datetime: |
| 1885 | + """Returns the inode's last change time |
| 1886 | + This is updated when the inode metadata changes |
| 1887 | +
|
| 1888 | + Returns: |
| 1889 | + A datetime with the inode's last change time |
| 1890 | + """ |
| 1891 | + return self._time_member_to_datetime("i_ctime") |
| 1892 | + |
| 1893 | + def get_file_mode(self) -> str: |
| 1894 | + """Returns the inode's file mode as string of the form '-rwxrwxrwx'. |
| 1895 | +
|
| 1896 | + Returns: |
| 1897 | + The inode's file mode string |
| 1898 | + """ |
| 1899 | + return stat.filemode(self.i_mode) |
0 commit comments