summaryrefslogtreecommitdiffstats
path: root/inspect.py
blob: 5f1842ff4de93eebc4db6625e95ae55302d63227 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3

import argparse
import logging
import os
import re
import sys

from tools import libvmdk, libvslvm, lklfuse, nbdfuse, subfiles, unmount, rmdir
from tools.inspect_apps import (
    list_applications_apk,
    list_applications_dpkg,
    list_applications_pacman,
    list_applications_portage,
    list_applications_rpm,
    list_applications_windows
)
from tools.inspect_os import get_linux_os_info, get_windows_os_info
from tools.pyparted import list_partitions

parser = argparse.ArgumentParser()
parser.description = "Tool for inspecting a disk image file to determine "\
                     "which operating system and applications it contains."
parser.add_argument("image", type=str, help="disk image file to inspect")
parser.add_argument("-b", "--backend", type=str, default="nbdfuse",
                    choices=("libvmdk", "nbdfuse"),
                    help="used backend for mounting disk image files in the " +
                         "local filesystem (default: nbdfuse)")
parser.add_argument("-v", "--verbose", action="store_true",
                    help="print debug messages")

args = parser.parse_args()

if args.verbose:
    fmt = "{asctime}, {name}:{lineno}:{funcName}(), {levelname}, {message}"
    logging.basicConfig(level=logging.DEBUG, format=fmt, style="{")
else:
    logging.basicConfig(level=logging.CRITICAL)

APK = re.compile(r"^(Alpine).*$")
DPKG = re.compile(r"^(Debian|Ubuntu|Linux\sMint|LMDE).*$")
PACMAN = re.compile(r"^(Arch|Manjaro).*$")
PORTAGE = re.compile(r"^(Gentoo).*$")
RPM = re.compile(r"^(CentOS|AlmaLinux|Scientific|Rocky|Oracle|openSUSE|Fedora).*$") # noqa
WIN = re.compile(r"^(Microsoft|Windows).*$")

if args.backend == "libvmdk":
    image_mp = libvmdk.mount(args.image)
else:
    image_mp = nbdfuse.mount(args.image)

if not image_mp:
    print(f"{args.backend} could not mount {args.image}", file=sys.stderr)
    sys.exit(1)

if args.backend == "libvmdk":
    raw = os.path.join(image_mp, "vmdk1")
else:
    raw = os.path.join(image_mp, "nbd")

parts = list_partitions(raw)
if not parts:
    unmount(image_mp)
    rmdir(image_mp)
    print("could not find any partitions", file=sys.stderr)
    sys.exit(1)

fs_mps = []
lvm = [part for part in parts if part["type"] == "lvm"]
lvm_mp = None

if not lvm:
    for part in parts:
        if fs_mp := lklfuse.mount(raw, part["type"], part["nr"]):
            fs_mps.append((fs_mp, part["type"]))
elif lvm_mp := libvslvm.mount(raw, lvm[0]["offset"]):
    for vol in subfiles(lvm_mp):
        vol_path = os.path.join(lvm_mp, vol)
        vol_part = list_partitions(vol_path)
        if not vol_part:
            continue
        if fs_mp := lklfuse.mount(vol_path, vol_part[0]["type"]):
            fs_mps.append((fs_mp, vol_part[0]["type"]))

os_info = {}
for k, v in fs_mps:
    if v == "ntfs":
        os_info = get_windows_os_info(k)
    else:
        os_info = get_linux_os_info(k)
    if os_info:
        break

apps = []
if os_name := os_info.get("name"):
    for k, _ in fs_mps:
        if APK.match(os_name):
            apps = list_applications_apk(k)
        elif DPKG.match(os_name):
            apps = list_applications_dpkg(k)
        elif PACMAN.match(os_name):
            apps = list_applications_pacman(k)
        elif PORTAGE.match(os_name):
            apps = list_applications_portage(k)
        elif RPM.match(os_name):
            apps = list_applications_rpm(k)
        elif WIN.match(os_name):
            apps = list_applications_windows(k)
        if apps:
            break

for k, _ in fs_mps:
    unmount(k)
    rmdir(k)

if lvm_mp:
    unmount(lvm_mp)
    rmdir(lvm_mp)

unmount(image_mp)
rmdir(image_mp)

print({
    "name": os_info.get("name", ""),
    "version": os_info.get("version", ""),
    "apps": apps
})