首页
社区
课程
招聘
4
[分享]从注册表查询USB插拔记录
发表于: 2024-1-14 00:32 11781

[分享]从注册表查询USB插拔记录

2024-1-14 00:32
11781

前言

感谢看雪论坛,和感谢论坛上每位无私奉献大神让我在这里学习到很多知识。也感谢看雪论坛能让我可以在这里分享自已的小工具。有位朋友问USB外接设备记录实现原理,当时本想单独抽源代码出来,但在抽取过程中,发现引用了很多单元,比如注册表读写单元是自已实现的,而这个单元又引用了很多其他的单元,如字符串处理(前辈实现),抽了20分钟都没有抽出来,很麻烦,所以在这里发一下大致原理。




正文


重点:注册表有一些键值,平时我们看不到,要模拟系统用户才能读取出来。用“Windows 可视化管理”的“进程页”选中“winlogon.exe”,右键-->创建进程-->以此为父进程创建。会弹出一个输入窗口,在进程路径中输入 C:\Windows\regedit.exe 点确定就可以打开注册表。


 这时就可以查询到像下面这些不能查询的键值了。

 


  比如注册表中每个USB设备的properties \{83da6326-97a6-4088-9453-a1923f573b29}子键下,只有系统用户可以读取它们。



  安装时间: 指定安装USB设备的日期/时间。仅在以管理员身份运行时才能读取此属性。此属性存储在Properties \ {83da6326-97a6-4088-9453-a1923f573b29}子项下,属性号为0064。


  首次安装时间: 指定首次安装USB设备的时间。仅在以管理员身份运行时才能读取此属性。该属性存储在Properties \ {83da6326-97a6-4088-9453-a1923f573b29}子项下,属性号为0065。


  连接时间: 指定上次插入USB设备的时间。仅在以管理员身份运行时才能读取此属性。此属性仅在Windows 10/8上可用。此属性存储在Properties \ {83da6326-97a6-4088-9453-a1923f573b29}子项下,属性号为0066。


  断开时间: 指定上次拔出USB设备的时间。此属性仅在Windows 10/8上可用。仅在以管理员身份运行时才能读取此属性。此属性存储在Properties \ {83da6326-97a6-4088-9453-a1923f573b29}子项下,属性号为0067。



相关代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
type
  TMyUSBDeviceInfo = record  // 自定义结构,这些从注册表读入的
    USBDI_RegKeyPath : array [0..255of AnsiChar;
    USBDI_ContainerID : array [0..38of AnsiChar
    USBDI_ClassGUID : array [0..38of AnsiChar;
    USBDI_DeviceType, USBDI_DeviceVolumeName : array [0..38of AnsiChar;  //设备类型
    USBDI_DeviceSerialNumber : array [0..67of AnsiChar;   // 设备序列号
    USBDI_DeviceMfg : array [0..67of AnsiChar;  //制造商
    USBDI_FriendlyName : array [0..127of AnsiChar;  //设备名称
    USBDI_LocationInformation  : array [0..63of AnsiChar// 位置 Port_#0001.Hub_#0001
    USBDI_SetupDateTime, USBDI_ConnectTime, USBDI_LastUesrDateTime : TDateTime;
  end;
  TMyUSBDeviceInfoArray = array of TMyUSBDeviceInfo;



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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
function MySetThreadTokenOnProcess(const dwPID : DWORD; const Thread : PHANDLE; const bSetThreadToken : Booleanvar StrErrorInfo : String) : Boolean;
var
  process_handle: THandle;
  token_handle: THandle;
  dup_token_handle: THandle;
  token_attributes: SECURITY_ATTRIBUTES;
begin
  Result:=False;
  StrErrorInfo:='';
  if not bSetThreadToken then
  begin
  Result:=windows.SetThreadToken(Thread, //指向函数向其分配模拟令牌的线程的句柄的指针。
                            // 如果 Thread 为 NULL,则该函数会将模拟令牌分配给调用线程。
                 0); // 如果 Token 为 NULL,该函数将导致线程停止使用模拟令牌。
  Exit;
  end;
  
  process_handle := MyOpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, dwPID, True);
  if (process_handle = 0then
  begin
    StrErrorInfo:='OpenProcess 失败:' + MyGetErrorToText;
    Exit;
  end;
  // 打开与进程关联的访问令牌。
  if (not OpenProcessToken(
    process_handle,  // 打开其访问令牌的进程的句柄。该进程必须具有 PROCESS_QUERY_INFORMATION 访问权限。
    TOKEN_DUPLICATE, // 指定一个访问掩码,该掩码指定对访问令牌的请求访问类型。这些请求的访问类型与令牌的自由访问控制列表(DACL) 进行比较,以确定哪些访问被授予或拒绝。
    token_handle)) then  // 指向句柄的指针,该句柄在函数返回时标识新打开的访问令牌。
  begin
    windows.CloseHandle(process_handle);
    StrErrorInfo:='OpenProcessToken 失败: ' + MyGetErrorToText;
    Exit;
  end;
  windows.CloseHandle(process_handle);
  token_attributes.nLength := sizeof(SECURITY_ATTRIBUTES);
  token_attributes.lpSecurityDescriptor := nil;
  token_attributes.bInheritHandle := FALSE;
   // 创建一个复制现有令牌的新访问令牌。此函数可以创建主令牌或模拟令牌。
  if (not DuplicateTokenEx(
    token_handle,  // 使用 TOKEN_DUPLICATE 访问打开的访问令牌的句柄。
    TOKEN_IMPERSONATE, // 指定新令牌的请求访问权限。
                      //DuplicateTokenEx函数将请求的访问权限与现有令牌的自主访问控制列表(DACL) 进行比较,以确定授予或拒绝哪些权限。
                      //要请求与现有令牌相同的访问权限,请指定零。要请求对调用者有效的所有访问权限,请指定 MAXIMUM_ALLOWED。
    @token_attributes, // 指向 SECURITY_ATTRIBUTES结构的指针,该结构指定新令牌的安全描述符并确定子进程是否可以继承令牌。
                       //如果lpTokenAttributes为NULL,则令牌获取默认安全描述符,并且无法继承句柄。
                       //如果安全描述符包含系统访问控制列表(SACL),则令牌将获得 ACCESS_SYSTEM_SECURITY 访问权限,即使它没有在dwDesiredAccess中请求。
                       //要在新令牌的安全描述符中设置所有者,调用者的进程令牌必须具有SE_RESTORE_NAME权限集。
    SecurityImpersonation,  // 从 SECURITY_IMPERSONATION_LEVEL 枚举中指定一个值,该值指示新令牌的模拟级别。
                            // 安全模拟    服务器进程可以在其本地系统上模拟客户端的安全上下文。服务器无法在远程系统上模拟客户端。
    TokenImpersonation,    //表示模拟令牌。
    dup_token_handle)    //指向接收新标记的HANDLE变量的指针。
    then
  begin
    windows.CloseHandle(token_handle);
    StrErrorInfo:='DuplicateTokenEx 失败:' + MyGetErrorToText;
    Exit;
  end;
  windows.CloseHandle(token_handle);
  if windows.SetThreadToken(Thread, //指向函数向其分配模拟令牌的线程的句柄的指针。
                            // 如果 Thread 为 NULL,则该函数会将模拟令牌分配给调用线程。
                    dup_token_handle  // 要分配给线程的模拟令牌的句柄。 此句柄必须已使用 TOKEN_IMPERSONATE 访问权限打开。
                                      // 如果 Token 为 NULL,该函数将导致线程停止使用模拟令牌。
                    then
  begin
  Result:=True;
  end
  else
  begin
  StrErrorInfo:='SetThreadToken 失败:' + MyGetErrorToText;
  end;
  windows.CloseHandle(dup_token_handle);
end;
 
 
// 自己简单封装的 RegOpenKeyEx
function MyRegOpenKeyEx(const RootKey : HKEY; const lpSubKey : PCharconst KEYsamDesired : REGSAM; const isKEY_WOW64_64KEY : Booleanvar phkResult : HKEY) : Boolean;
var
  samDesired : REGSAM;
begin
  samDesired:=KEYsamDesired;
  if isKEY_WOW64_64KEY and G_IsWin64 then
  begin
  samDesired:=samDesired or KEY_WOW64_64KEY;  // 注意,这里是禁止重定向
  end;
  Result:= ERROR_SUCCESS = RegOpenKeyEx(RootKey, lpSubKey, 0, samDesired, phkResult);
end;
 
 
// 自己简单封装的 RegEnumKeyEx
function MyRegEnumKeyNameToList(const phkResult: HKEY; var KeyNamesList: TStringList) : Boolean;
var
  I : Integer;
  dwDataSize, dwLen : DWORD;
  status : DWORD;
  buffer : array [0..255of AnsiChar;  //255 个字符
begin
  Result:=False;
  if not Assigned(KeyNamesList) then exit;
  dwLen:=Length(buffer);
  I:=0;
  repeat
    ZeroMemory(@buffer[0], dwLen);
    dwDataSize:=dwLen;
    status:=RegEnumKeyEx(phkResult, // 一个已打开项的句柄,或者指定一个标准项名
                         I, // 欲获取的子项的索引。第一个子项的索引编号为零
                         buffer, //用于装载指定索引处项名的一个缓冲区
                         dwDataSize, // 指定一个变量,用于装载lpName缓冲区的实际长度(包括空字符)。一旦返回,它会设为实际装载到lpName缓冲区的字符数量
                         nil//未用,设为零
                         nil//项使用的类名。可以为vbNullString
                         nil// 用于装载lpClass缓冲区长度的一个变量。一旦返回,它会设为实际装载到缓冲区的字符数量
                         nil); // 枚举子项上一次修改的时间
    if ERROR_SUCCESS = status then  //枚举子健
    begin
    KeyNamesList.Add(buffer);
    Inc(I);
    end;
  until status <> ERROR_SUCCESS; // 返回值, 零(ERROR_SUCCESS)表示成功。其他任何值都代表一个错误代码
  Result:=I > 0;
end;
 
// 取出注册表中二进制(数组长度为8)的值转换为时间
function MyRegGetPropertiesDateTime(const RootKey : HKEY; const lpSubKey : PCharconst isKEY_WOW64_64KEY : Booleanvar DTTime : TDateTime) : Boolean;
var
  //RegValue :PChar;
  samDesired : REGSAM;
  phkResult : HKEY;
  AData: TMyArrayOfBytes;
  dwOutBufSize : DWORD;
  //iValue64 : Int64;
begin
  Result:=False;
  DTTime:=0;
  samDesired:=KEY_READ or KEY_QUERY_VALUE;
  if isKEY_WOW64_64KEY and G_IsWin64 then
  begin
  samDesired:=samDesired or KEY_WOW64_64KEY;  // 注意,这里是禁止重定向
  end;
 
  if ERROR_SUCCESS = RegOpenKeyEx(RootKey, lpSubKey, 0, samDesired, phkResult) then
  begin
    if MyRegQueryBinaryValueEx(phkResult, '', AData, dwOutBufSize) then
    begin
    DTTime:=MyRegBinaryValueToDateTime(AData, 0, dwOutBufSize);
    Result:=True;
    SetLength(AData, 0);
    end
    else
    begin
    Result:=MyRegGetKeyLastWriteTime(phkResult, DTTime);
    end;
  RegCloseKey(phkResult);
  end;
 
end;
 
// 读取注册表USB设备信息到 TMyUSBDeviceInfoArray
function MyGetUSBDeviceUserInfo(var MyUSBDeviceInfo : TMyUSBDeviceInfoArray) : Boolean;
var
  I, J, iLen, iTtemCount : Integer;
phkResult, phkResult2, phkResult3, hkKeyLastUesrDateTime : HKEY;
  KeyNamesList1, KeyNamesList2, KeyNamesList3: TStringList;
  StrKeyName, StrKey_WPDBUSENUM : PAnsiChar;
  StrFriendlyName, StrClassGUID, StrSerialNumber, StrLastUesrDateTime, StrMfg, StrVolumeName,
    StrSetupDateTime, StrConnectTime, StrKeyName2, StrKeyName3, StrProperties, StrContainerID, StrService, StrErrorInfo : string;
  DTLastWriteTime, DTConnectTime, DTSetupDateTime : TDateTime;
begin
  Result:=False;
  KeyNamesList1:=TStringList.Create;
  KeyNamesList2:=TStringList.Create;
  KeyNamesList3:=TStringList.Create;
  // 接入过的 USB 设备都保存在这里
  iLen:=0// 数组长度
  iTtemCount:=0;
  StrKeyName:='SYSTEM\CurrentControlSet\Enum\USB\';
  if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, StrKeyName, KEY_READ or KEY_QUERY_VALUE, True, phkResult) then
  begin
    if MyRegEnumKeyNameToList(phkResult, KeyNamesList1) then
    begin
      for I:=0 to KeyNamesList1.Count-1 do
      begin
      StrKeyName2:=StrKeyName + KeyNamesList1[I];
        if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, PAnsiChar(StrKeyName2), KEY_READ or KEY_QUERY_VALUE, True, phkResult2) then
        begin
        KeyNamesList2.Clear;
          if MyRegEnumKeyNameToList(phkResult2, KeyNamesList2) then
          begin
            for J:=0 to KeyNamesList2.Count-1 do
            begin
            StrKeyName3:= StrKeyName2 + '\' + KeyNamesList2[J];
              if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, PAnsiChar(StrKeyName3), KEY_READ or KEY_QUERY_VALUE, True, phkResult3) then
              begin
                if MyRegQueryStringValueEx(phkResult3, 'Service', StrService) and (SameText(StrService, 'USBSTOR')) then
                begin
                  if MyRegQueryStringValueEx(phkResult3, 'ContainerID', StrContainerID) then  // 通过这里来定位
                  begin
                  MyRegQueryStringValueEx(phkResult3, 'LocationInformation', StrMfg);
                  Inc(iLen);// 数组长度
                  SetLength(MyUSBDeviceInfo, iLen);
                    with MyUSBDeviceInfo[iLen - 1do
                    begin
                    StringCbCopyA(@USBDI_RegKeyPath[0], SizeOf(USBDI_RegKeyPath), PAnsiChar(StrKeyName3));
                    StringCbCopyA(@USBDI_ContainerID[0], SizeOf(USBDI_ContainerID), PAnsiChar(StrContainerID));
                    StringCbCopyA(@USBDI_LocationInformation[0], SizeOf(USBDI_LocationInformation), PAnsiChar(StrMfg));
                    end;
                  end;
                end;
              RegCloseKey(phkResult3);
              end;
            end;
          end;
        RegCloseKey(phkResult2);
        end;
      end;
    end;
  RegCloseKey(phkResult);
  end;
                 
  if (iLen > 0and MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Enum\USBSTOR', KEY_READ or KEY_QUERY_VALUE, True, phkResult) then
  begin
    if MyRegEnumKeyNameToList(phkResult, KeyNamesList1) then
    begin
      if MySetThreadTokenOnProcess(MyGetProcessPIDByNameEx('winlogon.exe'3), nilTrue, StrErrorInfo) then
      begin
        for I:=0 to KeyNamesList1.Count -1 do
        begin
        StrKeyName:=PAnsiChar('SYSTEM\CurrentControlSet\Enum\USBSTOR' + '\' + KeyNamesList1[I]);
          if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, StrKeyName, KEY_READ or KEY_QUERY_VALUE, True, phkResult2) then
          begin
          KeyNamesList2.Clear;
            if MyRegEnumKeyNameToList(phkResult2, KeyNamesList2) then
            begin
            StrSerialNumber:=KeyNamesList2[0];
            StrKeyName2:=StrKeyName + '\' + StrSerialNumber;
            StrConnectTime:='';
            StrLastUesrDateTime:='';
            StrSetupDateTime:='';
              if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, PAnsiChar(StrKeyName2), KEY_READ or KEY_QUERY_VALUE, True, phkResult3) then
              begin
                if MyRegQueryStringValueEx(phkResult3, 'ContainerID', StrContainerID) then
                begin
                  for J:=Low(MyUSBDeviceInfo) to High(MyUSBDeviceInfo) do
                  begin
                    if SameText(StrContainerID, MyUSBDeviceInfo[J].USBDI_ContainerID)  then
                    begin    
                    StrProperties:=MyUSBDeviceInfo[J].USBDI_RegKeyPath + '\Properties\{83da6326-97a6-4088-9453-a1923f573b29}\';
                      // 首次安装时间: 仅在以管理员身份运行时才能读取此属性。
                      // 该属性存储在Properties \ {83da6326-97a6-4088-9453-a1923f573b29}子项下,属性号为0065。
                      if MyRegGetPropertiesDateTime(HKEY_LOCAL_MACHINE, PAnsiChar(StrProperties + '0065'), True, DTSetupDateTime) then
                      begin
                      end
                      //安装时间
                      else if MyRegGetPropertiesDateTime(HKEY_LOCAL_MACHINE, PAnsiChar(StrProperties + '0064'), True, DTSetupDateTime) then
                      begin
                      end;
                      // 所有时间戳均采用标准 Windows 64 位 (FILETIME) 格式。
                      // 最近插入时间   上一次到达日期
                      MyRegGetPropertiesDateTime(HKEY_LOCAL_MACHINE, PAnsiChar(StrProperties + '0066'), True, DTConnectTime);
                      // 最近拔出时间 断开时间    上一次删除日期
                      MyRegGetPropertiesDateTime(HKEY_LOCAL_MACHINE, PAnsiChar(StrProperties + '0067'), True, DTLastWriteTime);
                      // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\STORAGE\Volume\**
                      MyRegQueryStringValueEx(phkResult3, 'FriendlyName', StrFriendlyName);
                      MyRegQueryStringValueEx(phkResult3, 'ClassGUID', StrClassGUID);
                      StrClassGUID:=MyGetUSBDeviceClassGUIDToStr(StrClassGUID);
                      StrKey_WPDBUSENUM:=PAnsiChar('SYSTEM\CurrentControlSet\Enum\SWD\WPDBUSENUM\_??_USBSTOR#' + KeyNamesList1[I]  + '#' + StrSerialNumber + '#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}');
                      if MyRegOpenKeyEx(HKEY_LOCAL_MACHINE, StrKey_WPDBUSENUM, KEY_READ or KEY_QUERY_VALUE, True, hkKeyLastUesrDateTime) then
                      begin
                      MyRegQueryStringValueEx(hkKeyLastUesrDateTime, 'Mfg', StrMfg);
                      MyRegQueryStringValueEx(hkKeyLastUesrDateTime, 'FriendlyName', StrVolumeName);
                      RegCloseKey(hkKeyLastUesrDateTime);
                      end
                      else
                      begin
                      StrMfg:='';
                      StrVolumeName:='';
                      end;
                      with MyUSBDeviceInfo[J] do
                      begin
                      StringCbCopyA(@USBDI_FriendlyName[0], SizeOf(USBDI_FriendlyName), PAnsiChar(StrFriendlyName));
                      StringCbCopyA(@USBDI_DeviceType[0], SizeOf(USBDI_DeviceType), PAnsiChar(StrClassGUID));  //设备类型
                      StrSerialNumber:=MyGetUSBSerialNumberOnRegKey(StrSerialNumber);
                      StringCbCopyA(@USBDI_DeviceSerialNumber[0], SizeOf(USBDI_DeviceSerialNumber), PAnsiChar(StrSerialNumber));   // 设备序列号
                      StringCbCopyA(@USBDI_DeviceMfg[0], SizeOf(USBDI_DeviceMfg), PAnsiChar(StrMfg));   // 制造商
                      StringCbCopyA(@USBDI_DeviceVolumeName[0], SizeOf(USBDI_DeviceVolumeName), PAnsiChar(StrVolumeName));  // 卷标
                      USBDI_SetupDateTime:=DTSetupDateTime; // 安装时间
                      USBDI_ConnectTime:=DTConnectTime;  // 最近插入时间 连接时间
                      USBDI_LastUesrDateTime:=DTLastWriteTime; // 最近拔出时间 最后一次使用时间  断开时间
                      end;
                    Inc(iTtemCount);
                    Break;
                    end;
                  end;
                end;
              RegCloseKey(phkResult3);
              end;
            end;
          RegCloseKey(phkResult2);
          end;
        end;
      MySetThreadTokenOnProcess(0nilFalse, StrErrorInfo);
      end
      else
      begin
      MyAddDebugInfo('Err Reg Token:' + StrErrorInfo);
      end;
    end;
  KeyNamesList1.Free;
  KeyNamesList2.Free;
  KeyNamesList3.Free;
  RegCloseKey(phkResult);
  end;
  Result:=iTtemCount > 0;
end;
 
 
// 查询 USB 插拔记录
procedure TWindowsVisualManage_MainForm.PopupMenu_other2_USBSTORClick(
  Sender: TObject);
var
  cListItem : TListItem;
  cListColumn : TListColumn;
  MyUSBDeviceInfo : TMyUSBDeviceInfoArray;
  I : Integer;
begin
  MyPopupMenu_other2_AsClick(Sender);  // 当初考虑把所有查询功能都共用一个"ListView"控件,以节省不必要的开销。
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='设备名称';
  cListColumn.Width:=250;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='设备类型';
  cListColumn.Width:=68;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='设备位置';
  cListColumn.Width:=132;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='设备序列号';
  cListColumn.Width:=158;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='制造商';
  cListColumn.Width:=82;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='卷标';
  cListColumn.Width:=82;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='首次插入时间';
  cListColumn.Width:=126;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='最近插入时间'// 连接时间
  cListColumn.Width:=126;
  cListColumn:=ListView_VCLShareQuery.Columns.Add;
  cListColumn.Caption:='最近拔出时间'// 断开时间
  cListColumn.Width:=126;
  if MyGetUSBDeviceUserInfo(MyUSBDeviceInfo) then
  begin
    for I:=Low(MyUSBDeviceInfo) to High(MyUSBDeviceInfo) do
    begin
      with MyUSBDeviceInfo[I] do
      begin
      cListItem:=ListView_VCLShareQuery.Items.Add;
      cListItem.Caption:=USBDI_FriendlyName; //设备名称
      cListItem.SubItems.Add(USBDI_DeviceType); //设备类型
      cListItem.SubItems.Add(USBDI_LocationInformation); // 位置
      cListItem.SubItems.Add(USBDI_DeviceSerialNumber); //设备序列号
      cListItem.SubItems.Add(USBDI_DeviceMfg);  //制造商
      cListItem.SubItems.Add(USBDI_DeviceVolumeName); // 卷标
      cListItem.SubItems.Add(FormatDateTime('yyyy/mm/dd hh:mm:ss', USBDI_SetupDateTime)); // 安装时间
      cListItem.SubItems.Add(FormatDateTime('yyyy/mm/dd hh:mm:ss', USBDI_ConnectTime)); // 连接时间
      cListItem.SubItems.Add(FormatDateTime('yyyy/mm/dd hh:mm:ss', USBDI_LastUesrDateTime)); // 最后一次使用时间  断开时间
      end;
    end;
  StatusBar1.Panels.Items[4].Text:='USB外接设备记录:' + IntToStr(ListView_VCLShareQuery.Items.Count);
  end;
end;





[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2024-5-9 15:02 被kagayaki编辑 ,原因:
收藏
免费 4
支持
分享
赞赏记录
参与人
雪币
留言
时间
shuyangzjg
为你点赞!
2024-10-24 09:45
hkdong
为你点赞~
2024-1-15 09:44
badboyl
为你点赞~
2024-1-14 15:59
Grav1ty
为你点赞~
2024-1-14 00:40
最新回复 (12)
雪    币: 4163
活跃值: (5928)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
感谢楼主的解答
2024-1-14 15:59
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
badboyl 感谢楼主的解答
不用
2024-1-14 20:44
0
雪    币: 4723
活跃值: (31636)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2024-1-14 23:09
1
雪    币: 5573
活跃值: (4427)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
5


USBDriveLog

https://www.nirsoft.net/utils/usb_drive_log.html

http://www.linux-usb.org/usb.ids



USBDriveLog是第三方工具,将Win10、Win11内置USB日志以更人性化的方式展示出来。该工具可查看U盘插入时间、拔出时间、容量、文件系统类型等等,还有许多可用作U盘指纹的数据。一般关心这几列数据:


Serial Number   // U盘序列号

Plug Time       // U盘插入时间

Unplug Time     // U盘拔出时间

Capacity        // U盘容量(大小)

File System     // U盘文件系统类型


涉及USB设备取证场景时,USBDriveLog是一种选择。该工具只有一个EXE,无需安装,便携。为了显示Vendor Name、Product Name,需要额外下载usb.ids,与EXE置于同一目录。


缺省在线查看USB日志,但可以离线查看USB日志,只要找得着日志,比如这种位置:


X:\Windows\System32\winevt\Logs\Microsoft-Windows-Partition%4Diagnostic.evtx

X:\Windows\System32\winevt\Logs\Microsoft-Windows-Storsvc%4Diagnostic.evtx


若日志位于C盘,需要管理员权限访问这两个文件,离线查看时无所谓。具体操作如下:


File->Choose Data Source (F7)->Load from->External Folder


☆ 参考资源


[1] USBDriveLog

    https://www.nirsoft.net/utils/usb_drive_log.html

    http://www.linux-usb.org/usb.ids


[2] USB storage forensics in Win10 #1 - Events - [2019-08-03]

    https://www.senturean.com/posts/19_08_03_usb_storage_forensics_1/


[3] Get-WinEvent

    https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.diagnostics/get-winevent


    Creating Get-WinEvent queries with FilterHashtable

    https://learn.microsoft.com/en-us/powershell/scripting/samples/creating-get-winevent-queries-with-filterhashtable


[4] Consuming Events (Windows Event Log)

    https://learn.microsoft.com/en-us/windows/win32/wes/consuming-events


[5] wevtutil

    https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/wevtutil


最后于 2024-1-15 09:18 被scz编辑 ,原因: 补充参考资源
2024-1-15 09:16
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
scz 参USBDriveLoghttps://www.nirsoft.net/utils/usb_drive_log.htmlhttp://www.linux-usb.org/usb.idsUSBDrive ...
谢谢
2024-1-16 19:51
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
秋狝 感谢分享
谢谢
2024-1-20 14:46
0
雪    币: 7518
活跃值: (3498)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
学习了 
2024-3-12 18:07
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
shuyangzjg 学习了
共同进步
2024-3-13 18:12
0
雪    币: 4825
活跃值: (5588)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学到了
2024-3-13 19:03
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
青眼白龙 学到了[em_67]
谢谢
2024-3-14 15:04
0
雪    币: 3697
活跃值: (939)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学到了,对工作帮助很大~
2024-6-22 11:43
0
雪    币: 3284
活跃值: (6042)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
yuanyouran 学到了,对工作帮助很大~[em_63]
2024-6-22 13:17
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册