使用这款开源工具执行 Linux 内存取证

使用 Volatility 了解应用程序、网络连接、内核模块、文件以及更多信息的运行状况
68 位读者喜欢这篇文章。
Brain on a computer screen

opensource.com

计算机的操作系统和应用程序使用主内存(或 RAM)来执行各种任务。这种易失性内存包含有关正在运行的应用程序、网络连接、内核模块、打开的文件以及几乎所有其他信息的丰富信息,每次计算机重启时都会被清除。

内存取证是一种从内存中查找和提取这些有价值信息的方法。Volatility 是一款开源工具,它使用插件来处理此类信息。但是,存在一个问题:在您可以处理这些信息之前,您必须将物理内存转储到一个文件中,而 Volatility 不具备此功能。

因此,本文分为两个部分

  • 第一部分介绍如何获取物理内存并将其转储到文件中。
  • 第二部分介绍如何使用 Volatility 从此内存转储中读取和处理信息。

我在本教程中使用了以下测试系统,但它适用于任何 Linux 发行版

$ cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.3 (Ootpa)
$ 
$ uname -r
4.18.0-240.el8.x86_64
$ 

注意: 第 1 部分涉及编译和加载内核模块。别担心;它没有听起来那么困难。一些指南:

  • 按照步骤操作。
  • 不要在生产系统或您的主机器上尝试任何这些步骤。
  • 始终使用测试虚拟机 (VM) 来试用,直到您对工具的使用感到舒适并了解它们的工作原理。

安装所需的软件包

在开始之前,请安装必要的工具。如果您使用的是基于 Debian 的发行版,请使用等效的 apt-get 命令。这些软件包中的大多数提供了编译代码所需的内核信息和工具

$ yum install kernel-headers kernel-devel gcc elfutils-libelf-devel make git libdwarf-tools python2-devel.x86_64-y

第 1 部分:使用 LiME 获取内存并将其转储到文件

在您可以开始分析内存之前,您需要一个可供您使用的内存转储。在实际的取证事件中,这可能来自受损或被黑客入侵的系统。此类信息通常会被收集和存储,以分析入侵是如何发生的及其影响。由于您可能没有可用的内存转储,您可以对您的测试 VM 进行内存转储,并使用它来执行内存取证。

Linux Memory Extractor (LiME) 是一款流行的用于在 Linux 系统上获取内存的工具。使用以下命令获取 LiME:

$ git clone https://github.com/504ensicsLabs/LiME.git
$
$ cd LiME/src/
$ 
$ ls
deflate.c  disk.c  hash.c  lime.h  main.c  Makefile  Makefile.sample  tcp.c
$

构建 LiME 内核模块

src 文件夹内运行 make 命令。这将创建一个扩展名为 .ko 的内核模块。理想情况下,在 make 结束时,lime.ko 文件将被重命名为 lime-<your-kernel-version>.ko 格式

$ make
make -C /lib/modules/4.18.0-240.el8.x86_64/build M="/root/LiME/src" modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64'

<< snip >>

make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
strip --strip-unneeded lime.ko
mv lime.ko lime-4.18.0-240.el8.x86_64.ko
$ 
$ 
$ ls -l lime-4.18.0-240.el8.x86_64.ko
-rw-r--r--. 1 root root 25696 Apr 17 14:45 lime-4.18.0-240.el8.x86_64.ko
$ 
$ file lime-4.18.0-240.el8.x86_64.ko
lime-4.18.0-240.el8.x86_64.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=1d0b5cf932389000d960a7e6b57c428b8e46c9cf, not stripped
$

加载 LiME 内核模块

现在是时候加载内核模块以获取系统内存了。 insmod 命令有助于加载内核模块;加载后,该模块会读取您系统上的主内存 (RAM),并将内存的内容转储到命令行上的 path 目录中提供的文件中。另一个重要的参数是 format;保持格式为 lime,如下所示。插入内核模块后,使用 lsmod 命令验证它是否已加载

$ lsmod  | grep lime
$ 
$ insmod ./lime-4.18.0-240.el8.x86_64.ko "path=../RHEL8.3_64bit.mem format=lime"
$ 
$ lsmod  | grep lime
lime                   16384  0
$

您应该看到提供给 path 命令的文件已创建,并且文件大小(毫不奇怪)与您系统上的物理内存大小 (RAM) 相同。获得内存转储后,您可以使用 rmmod 命令删除内核模块

$ 
$ ls -l ~/LiME/RHEL8.3_64bit.mem
-r--r--r--. 1 root root 4294544480 Apr 17 14:47 /root/LiME/RHEL8.3_64bit.mem
$ 
$ du -sh ~/LiME/RHEL8.3_64bit.mem
4.0G	/root/LiME/RHEL8.3_64bit.mem
$ 
$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3736         220         366           8        3149        3259
Swap:          4059           8        4051
$ 
$ rmmod lime
$ 
$ lsmod  | grep lime
$ 

内存转储中有什么?

此转储文件只是原始数据,您可以使用下面的 file 命令看到。您无法手动理解它;是的,那里确实有一些 ASCII 字符串,但是您无法在编辑器中打开该文件并读出它。hexdump 输出显示最初的几个字节是 EmiL;这是因为您在上面的命令中请求的格式是“lime”

$ file ~/LiME/RHEL8.3_64bit.mem
/root/LiME/RHEL8.3_64bit.mem: data
$ 


$ hexdump -C ~/LiME/RHEL8.3_64bit.mem | head
00000000  45 4d 69 4c 01 00 00 00  00 10 00 00 00 00 00 00  |EMiL............|
00000010  ff fb 09 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  b8 fe 4c cd 21 44 00 32  20 00 00 2a 2a 2a 2a 2a  |..L.!D.2 ..*****|
00000030  2a 2a 2a 2a 2a 2a 2a 2a  2a 2a 2a 2a 2a 2a 2a 2a  |****************|
00000040  2a 2a 2a 2a 2a 2a 2a 2a  2a 2a 2a 2a 2a 20 00 20  |************* . |
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000080  00 00 00 00 00 00 00 00  00 00 00 00 70 78 65 6c  |............pxel|
00000090  69 6e 75 78 2e 30 00 00  00 00 00 00 00 00 00 00  |inux.0..........|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
$

第 2 部分:获取 Volatility 并使用它来分析您的内存转储

现在您有了一个示例内存转储来分析,请使用以下命令获取 Volatility 软件。Volatility 已使用 Python 3 重写,但本教程使用原始的 Volatility 软件包,该软件包使用 Python 2。如果您想尝试使用 Volatility 3,请从相应的 Git 仓库下载它,并在以下命令中使用 Python 3 而不是 Python 2

$ git clone https://github.com/volatilityfoundation/volatility.git
$ 
$ cd volatility/
$ 
$ ls
AUTHORS.txt    contrib      LEGAL.txt    Makefile     PKG-INFO     pyinstaller.spec  resources  tools       vol.py
CHANGELOG.txt  CREDITS.txt  LICENSE.txt  MANIFEST.in  pyinstaller  README.txt        setup.py   volatility
$

Volatility 使用两个 Python 库来实现某些功能,因此请使用以下命令安装它们。否则,当您运行 Volatility 工具时,您可能会看到一些导入错误;您可以忽略它们,除非您正在运行需要这些库的插件;在这种情况下,该工具将报错

$ pip2 install pycrypto
$ pip2 install distorm3

列出 Volatility 的 Linux 配置文件

您要运行的第一个 Volatility 命令是列出可用的 Linux 配置文件。运行任何 Volatility 命令的主要入口点是 vol.py 脚本。使用 Python 2 解释器调用它,并提供 --info 选项。为了缩小输出范围,请查找以 Linux 开头的字符串。如您所见,未列出许多 Linux 配置文件

$ python2 vol.py --info  | grep ^Linux
Volatility Foundation Volatility Framework 2.6.1
LinuxAMD64PagedMemory          - Linux-specific AMD 64-bit address space.
$

构建您自己的 Linux 配置文件

Linux 发行版种类繁多,并且是为各种架构构建的。这就是为什么配置文件至关重要——Volatility 必须知道获取内存转储的系统和架构,然后才能提取信息。有一些 Volatility 命令可以查找此信息;但是,此方法非常耗时。为了加快速度,请使用以下命令构建自定义 Linux 配置文件。

移动到 Volatility 仓库中的 tools/linux 目录,并运行 make 命令

$ cd tools/linux/
$ 
$ pwd
/root/volatility/tools/linux
$ 
$ ls
kcore  Makefile  Makefile.enterprise  module.c
$ 
$ make
make -C //lib/modules/4.18.0-240.el8.x86_64/build CONFIG_DEBUG_INFO=y M="/root/volatility/tools/linux" modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
<< snip >>
make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
$

您应该看到一个新的 module.dwarf 文件。您还需要 /boot 目录中的 System.map 文件,因为它包含与当前运行的内核相关的所有符号

$ ls
kcore  Makefile  Makefile.enterprise  module.c  module.dwarf
$ 
$ ls -l module.dwarf 
-rw-r--r--. 1 root root 3987904 Apr 17 15:17 module.dwarf
$ 
$ ls -l /boot/System.map-4.18.0-240.el8.x86_64 
-rw-------. 1 root root 4032815 Sep 23  2020 /boot/System.map-4.18.0-240.el8.x86_64
$ 
$ 

要创建自定义配置文件,请返回 Volatility 目录并运行以下命令。第一个参数提供了一个自定义 .zip 文件,文件名由您选择。我在名称中使用了操作系统和内核版本。下一个参数是上面创建的 module.dwarf 文件,最后一个参数是 /boot 目录中的 System.map 文件

$ 
$ cd volatility/
$ 
$ zip volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip tools/linux/module.dwarf /boot/System.map-4.18.0-240.el8.x86_64 
  adding: tools/linux/module.dwarf (deflated 91%)
  adding: boot/System.map-4.18.0-240.el8.x86_64 (deflated 79%)
$ 

您的自定义配置文件现已准备就绪,因此请验证 .zip 文件是否在上面给出的位置创建。如果您想知道 Volatility 是否检测到此自定义配置文件,请再次运行 --info 命令。这次,您应该看到下面列出的新配置文件

$ 
$ ls -l volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip
-rw-r--r--. 1 root root 1190360 Apr 17 15:20 volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip
$ 
$ 
$ python2 vol.py --info  | grep Redhat
Volatility Foundation Volatility Framework 2.6.1
LinuxRedhat8_3_4_18_0-240x64 - A Profile for Linux Redhat8.3_4.18.0-240 x64
$ 
$

开始使用 Volatility

现在您已准备好进行一些实际的内存取证。请记住,Volatility 由自定义插件组成,您可以针对内存转储运行这些插件以获取信息。该命令的通用格式是

python2 vol.py -f <memory-dump-file-taken-by-Lime> <plugin-name> --profile=<name-of-our-custom-profile>

掌握了这些信息后,运行 linux_banner 插件,看看您是否可以从内存转储中识别出正确的发行版信息

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_banner --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Linux version 4.18.0-240.el8.x86_64 (mockbuild@vm09.test.com) (gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC)) #1 SMP Wed Sep 23 05:13:10 EDT 2020
$

查找 Linux 插件

效果很好,所以现在您可能很好奇如何查找所有 Linux 插件的名称。有一个简单的技巧:运行 --info 命令并 grep linux_ 字符串。有各种各样的插件可用于不同的用途。这是一个部分列表

$ python2 vol.py --info  | grep linux_
Volatility Foundation Volatility Framework 2.6.1
linux_apihooks             - Checks for userland apihooks
linux_arp                  - Print the ARP table
linux_aslr_shift           - Automatically detect the Linux ASLR shift

<< snip >>

linux_banner               - Prints the Linux banner information
linux_vma_cache            - Gather VMAs from the vm_area_struct cache
linux_volshell             - Shell in the memory image
linux_yarascan             - A shell in the Linux memory image
$

使用 linux_psaux 插件检查当您进行内存转储时系统上正在运行哪些进程。请注意列表中的最后一个命令:它是您在转储之前运行的 insmod 命令

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_psaux --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Pid    Uid    Gid    Arguments                                                       
1      0      0      /usr/lib/systemd/systemd --switched-root --system --deserialize 18
2      0      0      [kthreadd]                                                      
3      0      0      [rcu_gp]                                                        
4      0      0      [rcu_par_gp]                                                    
861    0      0      /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P          
869    0      0      /usr/bin/rhsmcertd                                              
875    0      0      /usr/libexec/sssd/sssd_be --domain implicit_files --uid 0 --gid 0 --logger=files
878    0      0      /usr/libexec/sssd/sssd_nss --uid 0 --gid 0 --logger=files       

<<< snip >>>

11064  89     89     qmgr -l -t unix -u                                              
227148 0      0      [kworker/0:0]                                                   
227298 0      0      -bash                                                           
227374 0      0      [kworker/u2:1]                                                  
227375 0      0      [kworker/0:2]                                                   
227884 0      0      [kworker/0:3]                                                   
228573 0      0      insmod ./lime-4.18.0-240.el8.x86_64.ko path=../RHEL8.3_64bit.mem format=lime
228576 0      0                                                                      
$

想知道系统的网络统计信息吗?运行 linux_netstat 插件以查找内存转储期间的网络连接状态

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_netstat --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
UNIX 18113              systemd/1     /run/systemd/private
UNIX 11411              systemd/1     /run/systemd/notify
UNIX 11413              systemd/1     /run/systemd/cgroups-agent
UNIX 11415              systemd/1     
UNIX 11416              systemd/1     
<< snip>>
$

接下来,使用 linux_mount 插件查看内存转储期间挂载了哪些文件系统

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_mount --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
tmpfs                     /sys/fs/cgroup                      tmpfs        ro,nosuid,nodev,noexec                  
cgroup                    /sys/fs/cgroup/pids                 cgroup       rw,relatime,nosuid,nodev,noexec         
systemd-1                 /proc/sys/fs/binfmt_misc            autofs       rw,relatime                             
sunrpc                    /var/lib/nfs/rpc_pipefs             rpc_pipefs   rw,relatime                             
/dev/mapper/rhel_kvm--03--guest11-root /                                   xfs          rw,relatime                
tmpfs                     /dev/shm                            tmpfs        rw,nosuid,nodev                         
selinuxfs                 /sys/fs/selinux                     selinuxfs    rw,relatime                                                       
<< snip>>

cgroup                    /sys/fs/cgroup/net_cls,net_prio     cgroup       rw,relatime,nosuid,nodev,noexec         
cgroup                    /sys/fs/cgroup/cpu,cpuacct          cgroup       rw,relatime,nosuid,nodev,noexec         
bpf                       /sys/fs/bpf                         bpf          rw,relatime,nosuid,nodev,noexec         
cgroup                    /sys/fs/cgroup/memory               cgroup       ro,relatime,nosuid,nodev,noexec         
cgroup                    /sys/fs/cgroup/cpuset               cgroup       rw,relatime,nosuid,nodev,noexec         
mqueue                    /dev/mqueue                         mqueue       rw,relatime                             
$

想知道加载了哪些内核模块?Volatility 也有一个插件,恰如其分地命名为 linux_lsmod

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsmod --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
ffffffffc0535040 lime 20480
ffffffffc0530540 binfmt_misc 20480
ffffffffc05e8040 sunrpc 479232
<< snip >>
ffffffffc04f9540 nfit 65536
ffffffffc0266280 dm_mirror 28672
ffffffffc025e040 dm_region_hash 20480
ffffffffc0258180 dm_log 20480
ffffffffc024bbc0 dm_mod 151552
$

想查找用户运行的所有存储在 Bash 历史记录中的命令吗?运行 linux_bash 插件

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_bash --profile=LinuxRedhat8_3_4_18_0-240x64 -v
Volatility Foundation Volatility Framework 2.6.1
Pid      Name                 Command Time                   Command
-------- -------------------- ------------------------------ -------
  227221 bash                 2021-04-17 18:38:24 UTC+0000   lsmod 
  227221 bash                 2021-04-17 18:38:24 UTC+0000   rm -f .log
  227221 bash                 2021-04-17 18:38:24 UTC+0000   ls -l /etc/zzz 
  227221 bash                 2021-04-17 18:38:24 UTC+0000   cat ~/.vimrc 
  227221 bash                 2021-04-17 18:38:24 UTC+0000   ls
  227221 bash                 2021-04-17 18:38:24 UTC+0000   cat /proc/817/cwd
  227221 bash                 2021-04-17 18:38:24 UTC+0000   ls -l /proc/817/cwd
  227221 bash                 2021-04-17 18:38:24 UTC+0000   ls /proc/817/
<< snip >>
  227298 bash                 2021-04-17 18:40:30 UTC+0000   gcc prt.c 
  227298 bash                 2021-04-17 18:40:30 UTC+0000   ls
  227298 bash                 2021-04-17 18:40:30 UTC+0000   ./a.out 
  227298 bash                 2021-04-17 18:40:30 UTC+0000   vim prt.c
  227298 bash                 2021-04-17 18:40:30 UTC+0000   gcc prt.c 
  227298 bash                 2021-04-17 18:40:30 UTC+0000   ./a.out 
  227298 bash                 2021-04-17 18:40:30 UTC+0000   ls
$

想知道哪些进程打开了哪些文件?使用 linux_lsof 插件列出该信息

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsof --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Offset             Name                           Pid      FD       Path
------------------ ------------------------------ -------- -------- ----
0xffff9c83fb1e9f40 rsyslogd                          71194        0 /dev/null
0xffff9c83fb1e9f40 rsyslogd                          71194        1 /dev/null
0xffff9c83fb1e9f40 rsyslogd                          71194        2 /dev/null
0xffff9c83fb1e9f40 rsyslogd                          71194        3 /dev/urandom
0xffff9c83fb1e9f40 rsyslogd                          71194        4 socket:[83565]
0xffff9c83fb1e9f40 rsyslogd                          71194        5 /var/log/messages
0xffff9c83fb1e9f40 rsyslogd                          71194        6 anon_inode:[9063]
0xffff9c83fb1e9f40 rsyslogd                          71194        7 /var/log/secure

<< snip >>

0xffff9c8365761f40 insmod                           228573        0 /dev/pts/0
0xffff9c8365761f40 insmod                           228573        1 /dev/pts/0
0xffff9c8365761f40 insmod                           228573        2 /dev/pts/0
0xffff9c8365761f40 insmod                           228573        3 /root/LiME/src/lime-4.18.0-240.el8.x86_64.ko
$

访问 Linux 插件脚本位置

通过读取内存转储并处理信息,您可以获得更多信息。如果您懂 Python 并且好奇这些信息是如何处理的,请转到存储所有插件的目录,选择一个您感兴趣的插件,看看 Volatility 是如何获取此信息的

$ ls volatility/plugins/linux/
apihooks.py              common.py            kernel_opened_files.py   malfind.py          psaux.py
apihooks.pyc             common.pyc           kernel_opened_files.pyc  malfind.pyc         psaux.pyc
arp.py                   cpuinfo.py           keyboard_notifiers.py    mount_cache.py      psenv.py
arp.pyc                  cpuinfo.pyc          keyboard_notifiers.pyc   mount_cache.pyc     psenv.pyc
aslr_shift.py            dentry_cache.py      ld_env.py                mount.py            pslist_cache.py
aslr_shift.pyc           dentry_cache.pyc     ld_env.pyc               mount.pyc           pslist_cache.pyc
<< snip >>
check_syscall_arm.py     __init__.py          lsmod.py                 proc_maps.py        tty_check.py
check_syscall_arm.pyc    __init__.pyc         lsmod.pyc                proc_maps.pyc       tty_check.pyc
check_syscall.py         iomem.py             lsof.py                  proc_maps_rb.py     vma_cache.py
check_syscall.pyc        iomem.pyc            lsof.pyc                 proc_maps_rb.pyc    vma_cache.pyc
$ 
$

我喜欢 Volatility 的一个原因是它提供了许多安全插件。这些信息很难手动获取

linux_hidden_modules       - Carves memory to find hidden kernel modules
linux_malfind              - Looks for suspicious process mappings
linux_truecrypt_passphrase - Recovers cached Truecrypt passphrases

Volatility 还允许您在内存转储中打开 shell,因此您可以运行 shell 命令而不是运行上面的所有命令,并获得相同的信息

$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_volshell --profile=LinuxRedhat8_3_4_18_0-240x64 -v
Volatility Foundation Volatility Framework 2.6.1
Current context: process systemd, pid=1 DTB=0x1042dc000
Welcome to volshell! Current memory image is:
file:///root/LiME/RHEL8.3_64bit.mem
To get help, type 'hh()'
>>> 
>>> sc()
Current context: process systemd, pid=1 DTB=0x1042dc000
>>> 

后续步骤

内存取证是了解更多 Linux 内部原理的好方法。尝试 Volatility 的所有插件并详细研究它们的输出。然后考虑这些信息如何帮助您识别入侵或安全问题。深入研究插件的工作原理,甚至尝试改进它们。如果您没有找到您想做的插件,请编写一个并将其提交给 Volatility,以便其他人也可以使用它。

接下来阅读什么
User profile image.
经验丰富的软件工程专业人士。主要兴趣是安全、Linux、恶意软件。喜欢在命令行上工作。对底层软件和了解事物的工作原理感兴趣。此处表达的观点是我个人的,不代表我的雇主

评论已关闭。

© . All rights reserved.