Skip to content

Commit 156eb61

Browse files
authored
Add MUIcache plugin (#87)
1 parent 780e85f commit 156eb61

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from typing import Generator
2+
3+
from flow.record.fieldtypes import path
4+
5+
from dissect.target.helpers.descriptor_extensions import (
6+
UserRecordDescriptorExtension,
7+
RegistryRecordDescriptorExtension,
8+
)
9+
from dissect.target.helpers.record import create_extended_descriptor
10+
from dissect.target.helpers.regutil import RegistryKey
11+
from dissect.target.plugin import Plugin, export
12+
13+
MuiCacheRecord = create_extended_descriptor([RegistryRecordDescriptorExtension, UserRecordDescriptorExtension])(
14+
"windows/registry/muicache",
15+
[
16+
("varint", "index"),
17+
("string", "name"),
18+
("string", "value"),
19+
("path", "path"),
20+
],
21+
)
22+
23+
24+
class MuiCachePlugin(Plugin):
25+
"""Plugin that iterates various MUIcache locations."""
26+
27+
KEYS = [
28+
"HKCU\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache", # NT >= 6.0
29+
"HKCU\\Software\\Classes\\Local Settings\\MuiCache", # NT >= 6.0
30+
"HKCU\\Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache", # NT < 6.0
31+
]
32+
33+
FIELD_NAMES = ("FriendlyAppName", "ApplicationCompany")
34+
35+
def check_compatible(self) -> bool:
36+
return len(list(self.target.registry.keys(self.KEYS)))
37+
38+
@export(record=MuiCacheRecord)
39+
def muicache(self) -> MuiCacheRecord:
40+
"""Iterate various MUIcache key locations.
41+
42+
The MUIcache registry key stores information about executed GUI-based programs. The key is part of
43+
the Multilingual User Interface service in Windows. MUIcache references the file description contained within
44+
the executable's resource section and populates that value.
45+
46+
Sources:
47+
- https://www.magnetforensics.com/blog/forensic-analysis-of-muicache-files-in-windows/
48+
- https://forensafe.com/blogs/muicache.html
49+
50+
Yields MuiCacheRecords with fields:
51+
hostname (string): The target hostname.
52+
domain (string): The target domain.
53+
index (varint): The index of the entry.
54+
name (string): The value name.
55+
value (string): The value.
56+
path (path): The executable path.
57+
"""
58+
for reg_path in self.KEYS:
59+
for key in self.target.registry.keys(reg_path):
60+
if len(key.subkeys()):
61+
for subkey in key.subkeys():
62+
for item in subkey.subkeys():
63+
yield from self._get_records(item)
64+
else:
65+
yield from self._get_records(key)
66+
67+
def _get_records(self, key: RegistryKey) -> Generator[MuiCacheRecord, None, None]:
68+
for index, entry in enumerate(key.values()):
69+
user = self.target.registry.get_user(key)
70+
try:
71+
if entry.name.endswith(self.FIELD_NAMES):
72+
entry_path, name = entry.name.rsplit(".", 1)
73+
else:
74+
name = None
75+
entry_path = entry.name.rsplit(",-", 1)[0]
76+
77+
# Filter on the type of the registry value
78+
if isinstance(entry.value, bytes):
79+
continue
80+
81+
yield MuiCacheRecord(
82+
index=index,
83+
name=name,
84+
value=entry.value,
85+
path=path.from_windows(entry_path),
86+
_target=self.target,
87+
_key=key,
88+
_user=user,
89+
)
90+
except ValueError:
91+
continue
92+
except Exception:
93+
self.target.log.exception("Exception while parsing muicache")
94+
continue
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from dissect.target.helpers.regutil import VirtualKey, VirtualValue
2+
from dissect.target.plugins.os.windows.regf.muicache import MuiCachePlugin
3+
4+
5+
def test_muicache_plugin(target_win_users, hive_hku):
6+
# NT >= 6.0
7+
muicache_shell_key = VirtualKey(
8+
hive_hku,
9+
"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache",
10+
)
11+
muicache_shell_key.add_value("LangID", VirtualValue(hive_hku, "LangID", b"\t\x04"))
12+
muicache_shell_key.add_value(
13+
"C:\\Windows\\System32\\fsquirt.exe.FriendlyAppName",
14+
VirtualValue(hive_hku, "C:\\Windows\\System32\\fsquirt.exe.FriendlyAppName", "fsquir"),
15+
)
16+
muicache_shell_key.add_value(
17+
"C:\\Windows\\System32\\fsquirt.test.exe.ApplicationCompany",
18+
VirtualValue(hive_hku, "C:\\Windows\\System32\\fsquirt.test.exe.ApplicationCompany", "fsquir"),
19+
)
20+
21+
# NT >= 6.0 (subkey)
22+
muicache_key = VirtualKey(
23+
hive_hku,
24+
"Software\\Classes\\Local Settings\\MuiCache\\35\\52C64B7E",
25+
)
26+
muicache_key.add_value(
27+
"@C:\\Windows\\system32\\wsecedit.dll,-718",
28+
VirtualValue(hive_hku, "@C:\\Windows\\system32\\wsecedit.dll,-718", "Local Security Policy"),
29+
)
30+
31+
# NT < 6.0
32+
muicache_shellnoroam_key = VirtualKey(
33+
hive_hku,
34+
"Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache",
35+
)
36+
muicache_shellnoroam_key.add_value(
37+
"@C:\\WINDOWS\\inf\\unregmp2.exe,-4",
38+
VirtualValue(hive_hku, "@C:\\WINDOWS\\inf\\unregmp2.exe,-4", "Windows Media Player"),
39+
)
40+
41+
hive_hku.map_key(muicache_shell_key.path, muicache_shell_key)
42+
hive_hku.map_key(muicache_key.path, muicache_key)
43+
hive_hku.map_key(muicache_shellnoroam_key.path, muicache_shellnoroam_key)
44+
45+
target_win_users.add_plugin(MuiCachePlugin)
46+
47+
results = list(target_win_users.muicache())
48+
49+
assert len(results) == 4
50+
assert results[0].index == 1
51+
assert results[0].name == "FriendlyAppName"
52+
assert results[0].value == "fsquir"
53+
assert str(results[0].path) == "C:\\Windows\\System32\\fsquirt.exe"
54+
assert results[1].index == 2
55+
assert results[1].name == "ApplicationCompany"
56+
assert results[1].value == "fsquir"
57+
assert str(results[1].path) == "C:\\Windows\\System32\\fsquirt.test.exe"
58+
assert results[2].index == 0
59+
assert results[2].name is None
60+
assert results[2].value == "Local Security Policy"
61+
assert str(results[2].path) == "@C:\\Windows\\system32\\wsecedit.dll"
62+
assert results[3].index == 0
63+
assert results[3].name is None
64+
assert results[3].value == "Windows Media Player"
65+
assert str(results[3].path) == "@C:\\WINDOWS\\inf\\unregmp2.exe"

0 commit comments

Comments
 (0)