0%

python脚本Windows注册表半自动取证

取证的时候没软件有点坐牢,准备写几个小脚本加快手搓速度

演示检材:2025獬豸杯PC镜像

环境:python3.12

​ FTK

获取基本信息

照例需要获取计算机的Build版本,计算机名称,系统名称、安装时间、时区、最后一次关机时间、上次登录用户,这些基本信息,这些信息可以直接从注册表中拿到,不需要进行特殊处理(除了时间戳)

收集一下这些信息在注册表中的位置,以及对应的键值

因为只有在我本机和镜像中找到了,就放在了这里,未进行多次实验,有不当支持欢迎斧正。

1
2
3
4
5
6
7
8
Build信息  SOFTWARE  SOFTWARE\Microsoft\Windows NT\CurrentVersion  BuildLabEx
Build版本 SOFTWARE SOFTWARE\Microsoft\Windows NT\CurrentVersion CurrentBuildNumber
计算机名称 SYSTEM SYSTEM\ControlSet001\Control\ComputerName\ComputerName ComputerName
系统名称 SOFTWARE SOFTWARE\Microsoft\Windows NT\CurrentVersion ProductName
系统安装时间 SOFTWARE SOFTWARE\Microsoft\Windows NT\CurrentVersion InstallDate
系统时区 SYSTEM SYSTEM\ControlSet001\Control\TimeZoneInformation TimeZoneKeyName
最后一次关机时间 SYSTEM SYSTEM\ControlSet001\Control\Windows ShutdownTime
上次登录用户 SOFTWARE SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI LastLoggedOnUser

使用regipy库进行处理,先将dd镜像用FTK挂载到本地,这里是挂在了H盘

regipy:https://github.com/mkorman90/regipy

使用 RegistryHive 来确定注册表文件的路径,使用get_key指定键值路径

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
import re
from regipy import RegistryHive
from regipy.exceptions import RegistryKeyNotFoundException
from datetime import datetime, timedelta


def system_get(hive, key_path, name, def_name=None):
key = hive.get_key(key_path)
try:
date = key.get_value(name)
if def_name == "byte_to_str":
return byte_to_str(date)
else:
return date
except RegistryKeyNotFoundException as e:
print(f"未找到 {name} 键。 {e}")


def analyze_software_registry(registry_path):

result = []
with open('../system_path', 'r', encoding="UTF-8") as file:
lines = file.readlines()
for line in lines:
res = {}
line = line.strip('\n').split(' ')
if len(line) == 4:
res[line[0]] = system_get(RegistryHive(f"{registry_path}/{line[1]}"), line[2], line[3])
else:
res[line[0]] = system_get(RegistryHive(f"{registry_path}/{line[1]}"), line[2], line[3], line[4])
result.append(res)
# print(result)
return result


if __name__ == "__main__":
# 将这里的路径替换为你的挂载路径
registry_path = r'H:/[root]/Windows/System32/config'
result = analyze_software_registry(registry_path)

直接获取的信息中,系统安装时间是unix时间戳,最后一次关机时间是FILETIME 时间戳,还需要处理时间戳。中间还有一个字节序的转换,简单来说就是,大端字节序就是我们日常的阅读顺序

10 2c 05 80。 这个十六进制数据如果按照大端字节序转换成十进制就是 271,320,448

如果是小端字节序,就需要从后往前读,两个十六进制数为一组: 80 05 2c 10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 处理时间戳
def byte_to_str(byte_time):
# 处理 FILETIME 时间戳
if isinstance(byte_time, bytes):
res = little_to_big(byte_time.hex()) / 10000000
unix_seconds = res - 11644473600
unix_seconds = datetime.fromtimestamp(unix_seconds)
return unix_seconds.strftime("%Y-%m-%d %H:%M:%S")
else:
unix_seconds = datetime.fromtimestamp(byte_time)
return unix_seconds.strftime("%Y-%m-%d %H:%M:%S")


# hex小端字节序转大端字节序
def little_to_big(little_hex):
little_bytes = bytes.fromhex(little_hex)
num = int.from_bytes(little_bytes, byteorder='little')
big_bytes = num.to_bytes(len(little_bytes), byteorder='big')
big_hex = int(big_bytes.hex(), 16)
return big_hex

输出结果

1
[{'Build信息': '18362.1.amd64fre.19h1_release.190318-1202'}, {'Build版本': '18363'}, {'计算机名称': 'DESKTOP-SBTC549'}, {'系统名称': 'Windows 10 Education'}, {'系统安装时间': '2024-06-12 19:02:06'}, {'系统时区': 'China Standard Time'}, {'最后一次关机时间': '2025-02-10 16:15:30'}, {'上次登录用户': '.\\TTT'}]

获取账户的信息

账户信息存在SAM文件中,这一部分主要对主要对 SAM\SAM\Domains\Account\Users 目录中的 V 键值对进行分析

这个是镜像 TTT 用户的V值

1
00000000f400000003000100f40000000600000000000000fc00000008000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000000000000000000000401000015000000a80000001c01000008000000010000002401000018000000000000003c01000038000000000000007401000018000000000000008c010000180000000000000001001480d4000000e40000001400000044000000020030000200000002c014004400050101010000000000010000000002c01400ff070f0001010000000000050700000002009000040000000000240044000200010500000000000515000000064a693cc7d5a71f57e1f80de8030000000038001b030200010a00000000000f0300000000040000dea22867213ed2af19ad5d79b0c107292756fc20d8ad66f610f268fadf2af80f00001800ff070f0001020000000000052000000020020000000014005b030200010100000000000100000000010200000000000520000000200200000102000000000005200000002002000054005400540000004600610063006500ffffffffffffffffffffffffffffffffffffffffffe1a1a401020000070000000200020000000000e4cf9dd0d3d5d720b26d66d9dc712cb50200020010000000af5788fbd9902cb7182bd4f96e6271cf30bdb1ec83090591fec6c0de090fa8b0e7013610157b17c9bc7b2d313bda45a602000200000000004c2a126f2dceb6ee0b14ed1e27995aff02000200000000005249e2b181774f9b48f9c306ca163d46

获取RID

SAM\SAM\Domains\Account\Users 下的目录代表账户,names目录除外,其余目录的名称就是对应账户的RID值,例如:Administrator账户,对应的RID就是500,那么他的目录名就是 000001F4 (这个转换成十进制就是500)

再记录一个小信息,F的值转换成十六进制,十六进制数的 96-104 位以小端字节序的形式记录了RID。

获取SID

从这个值中找到 0105 这样的数据,05后面还会有几个0,这一串代表的是该用户(非系统用户,系统用户的我不太明白)的 SID值,

拿出来0105以及之后的数据

1
010500000000000515000000064a693cc7d5a71f57e1f80de80300000000380…………………………

这个 01 是固定的,表示SID版本号后面的 05 表示的是 子权限数量 ,我是直接找的 0105,从网上也没有发现具体的说法,AI更扯,说起始位置可能为 0x30 0x38。

从 0105 之后再获取6字节,这里就是 00 00 00 00 00 05 呈现为大端字节序,是标识符权威值,然后根据子权限数量,来获取 (x-1)段 4字节的子权限列表,这里为 15 00 00 00 06 4a 69 3c c7 d5 a7 1f 57 e1 f8 0d ,将获取的四字节从小端序转成大端序,然后转成十进制,得到结果 21 1013533190 531092935 234414423 ,为什么是 x-1呢,一般子权限列表中的最后一个是 RID。

拼接得出SID S-1-5-21-1013533190-531092935-234414423-1000 。完美

获取user

user的存储前面是有标识符的,结尾需要自己判断,我使用了 5200000002002000001020000000000052000000020020000 来进行判断,那到这个标识符后面的数据

1
54005400540000004600610063006500ffffffffffffffffffffffffffffffffffffffffffe1a1a401020000070000000200020000000000e4cf9dd0d3d5d720b26d66d9dc712cb50200020010000000af5788fbd9902cb7182bd4f96e6271cf30bdb1ec83090591fec6c0de090fa8b0e7013610157b17c9bc7b2d313bda45a602000200000000004c2a126f2dceb6ee0b14ed1e27995aff02000200000000005249e2b181774f9b48f9c306ca163d46

结尾的判断并不是使用标识符,而是从 00 下手,用户名有一个规律,两位十六进制数之后,间隔一个 00 才是下一个十六进制数,我们判断用户名结束就可以使用这样的方法,先将 0000 之前的数据提取出来,防止获取过长,因为出现0000很显然是结束了,节省时间,这样拿到数据 5400540054 而我们需要即对 00 这个间隔来进行判断,有需要将00取出,避免转码是出现乱码。提取结束是 545454 hex解码正是 TTT。

PS:至于密码的NT hash我也找到对应的了,但是还不知道怎么处理成取证软件中的形式,下次再说

image-20250311181720530

image-20250311181815422

小工具代码

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import re
from regipy import RegistryHive
from regipy.exceptions import RegistryKeyNotFoundException
from datetime import datetime, timedelta


def system_get(hive, key_path, name, def_name=None):
key = hive.get_key(key_path)
try:
date = key.get_value(name)
if def_name == "byte_to_str":
return byte_to_str(date)
else:
return date
except RegistryKeyNotFoundException as e:
print(f"未找到 SOFTWARE 键。 {e}")


# 获取电脑基本信息
def analyze_software_registry(registry_path):

result = []
with open('../system_path', 'r', encoding="UTF-8") as file:
lines = file.readlines()
for line in lines:
res = {}
line = line.strip('\n').split(' ')
if len(line) == 4:
res[line[0]] = system_get(RegistryHive(f"{registry_path}/{line[1]}"), line[2], line[3])
else:
res[line[0]] = system_get(RegistryHive(f"{registry_path}/{line[1]}"), line[2], line[3], line[4])
result.append(res)
# print(result)
return result


# 获取用户信息
def get_sid_name(registry_path):
user_list = []
hive_sam = RegistryHive(f"{registry_path}/SAM")
users_key = hive_sam.get_key('SAM\\SAM\\Domains\\Account\\Users')

# 遍历 Users 键下的子键(每个子键对应一个用户)
for subkey in users_key.iter_subkeys():
# 获取用户 RID(子键名称通常是用户的 RID,例如 "000001F4")
user = {}
user_rid = subkey.name
# print(user_rid)

pattern = r'^0{1,5}'
if bool(re.match(pattern, user_rid)):
try:
# RID
f_full_name_key = subkey.get_value('F')
rid = little_to_big(f_full_name_key.hex()[96:104])
user['RID'] = rid
except RegistryKeyNotFoundException:
username = f"User_{user_rid}"

# 获取 V键下的 Values
try:
# SID
v_key = subkey.get_value('V')
print(v_key.hex())
sid = v_key.hex()[576:624]
sid = f"S-{get_sid(sid)}-{rid}"
# print(sid)
user['SID'] = sid


# USERNAME
pattern_name = r'(5200000002002000001020000000000052000000020020000.*)'
match = re.search(pattern_name, v_key.hex())

if match:
result = match.group(1)
# print(result[49:])
temp = result[49:].split('0000')[0]
temp = temp.split('00')
res = ""
for i in temp:
if len(i) > 2:
break
else:
res += i
res = bytes.fromhex(res).decode('utf-8')
user['username'] = res
else:
print("未找到匹配的内容。")
except RegistryKeyNotFoundException:
print(f"用户名: {username}, 无法找到 SID 信息")
user_list.append(user)
else:
continue
return user_list


def get_network():
print()


# 处理时间戳
def byte_to_str(byte_time):
# 处理 FILETIME 时间戳
if isinstance(byte_time, bytes):
res = little_to_big(byte_time.hex()) / 10000000
unix_seconds = res - 11644473600
unix_seconds = datetime.fromtimestamp(unix_seconds)
return unix_seconds.strftime("%Y-%m-%d %H:%M:%S")
else:
unix_seconds = datetime.fromtimestamp(byte_time)
return unix_seconds.strftime("%Y-%m-%d %H:%M:%S")


# hex小端字节序转大端字节序
def little_to_big(little_hex):
little_bytes = bytes.fromhex(little_hex)
num = int.from_bytes(little_bytes, byteorder='little')
big_bytes = num.to_bytes(len(little_bytes), byteorder='big')
big_hex = int(big_bytes.hex(), 16)
return big_hex


# 计算SID
def get_sid(data):
sid = []
temp = str(int(data[:2], 16))
sid.append(temp)
num = int(data[2:4], 16)
value = str(int(data[4:16], 16))
sid.append(value)
for i in range(num-1):
test = str(little_to_big(data[16+i*8:24+i*8]))
sid.append(test)
sid = '-'.join(sid)
return sid


if __name__ == "__main__":
# 将这里的路径替换为你的挂载路径
registry_path = r'H:/[root]/Windows/System32/config'
result = analyze_software_registry(registry_path)
res = get_sid_name(registry_path)
print(res)
print(result)

运行结果

系统用户的SID处理有点问题,这里的用户的是正常的,还需要大量的验证,时区没进行处理,常见的形式是 (UTC+08:00)北京

1
2
3
[{'RID': 500, 'SID': 'S-1-1-500', 'username': 'Administrator'}, {'RID': 501, 'SID': 'S-1-1-501', 'username': 'Guest'}, {'RID': 503, 'SID': 'S-1-1-503', 'username': 'DefaultAccount'}, {'RID': 504, 'SID': 'S-1-1-504', 'username': 'WDAGUtilityAccount'}, {'RID': 1000, 'SID': 'S-1-5-21-1013533190-531092935-234414423-1000', 'username': 'TTT'}]

[{'Build信息': '18362.1.amd64fre.19h1_release.190318-1202'}, {'Build版本': '18363'}, {'计算机名称': 'DESKTOP-SBTC549'}, {'系统名称': 'Windows 10 Education'}, {'系统安装时间': '2024-06-12 19:02:06'}, {'系统时区': 'China Standard Time'}, {'最后一次关机时间': '2025-02-10 16:15:30'}, {'上次登录用户': '.\\TTT'}]