11Imports Microsoft.VisualBasic.ControlChars
2+ Imports System.IO
3+ Imports Microsoft.Win32
24
35Module WindowsServiceHelper
46
57 Private PrivilegeConstantDictionary As New Dictionary( Of String , NTSecurityPrivilegeConstant)
68 Private PrivilegeMappingDictionary As New Dictionary( Of String , String )
79
810 Sub FillInConstants()
11+ PrivilegeConstantDictionary.Clear()
12+ PrivilegeMappingDictionary.Clear()
913 PrivilegeConstantDictionary.Add( "SE_ASSIGNPRIMARYTOKEN_NAME" ,
1014 New NTSecurityPrivilegeConstant(
1115 "SeAssignPrimaryTokenPrivilege" ,
@@ -193,4 +197,101 @@ Module WindowsServiceHelper
193197 Next
194198 End Sub
195199
200+ Function GetServiceList(MountPath As String ) As List( Of WindowsService)
201+ ' For the required privileges a service may have, we have to fill in the constants first so that we don't have things like
202+ ' "SeUndockPrivilege", "SeShutdownPrivilege"; but rather "Remove computer from docking station", and so on... we want the
203+ ' friendly things.
204+ FillInConstants()
205+ Dim serviceList As New List( Of WindowsService)
206+
207+ ' Time to load up a registry hive
208+ If RegistryHelper.LoadRegistryHive(Path.Combine(MountPath, "Windows" , "system32" , "config" , "SYSTEM" ), "HKLM\zSYS" ) = 0 Then
209+ Try
210+ ' First we need to grab the default control set of the target image
211+ Dim DefaultControlSet As Integer = RegistryHelper.GetDefaultControlSet( "zSYS" )
212+ If DefaultControlSet = - 1 Then
213+ Throw New Exception( "Registry control set could not be obtained" )
214+ End If
215+ ' We only document a maximum of 999 control sets. CurrentControlSet is not a thing in an offline system, as the registry
216+ ' subsystems guess the control set to use based on values in HKLM\SYSTEM\Select.
217+ Dim ServiceRk As RegistryKey = Registry.LocalMachine.OpenSubKey( String .Format( "zSYS\ControlSet{0}\Services" , DefaultControlSet.ToString().PadLeft( 3 , "0" )), False )
218+ ' For some stupid reason, .NET keys are stored in HKLM\SYSTEM\ControlSet<nnn>\Services. GUID keys are also not allowed
219+ Dim ServiceNames() As String = ServiceRk.GetSubKeyNames().Where( Function (serviceName) Not serviceName.StartsWith( ".NET" , StringComparison.OrdinalIgnoreCase) AndAlso Not serviceName.StartsWith( "{" )).ToArray()
220+ ServiceRk.Close()
221+
222+ ' Now we have to grab as much information as we can
223+ For Each ServiceName In ServiceNames
224+ Dim serviceImagePath As String = "" ,
225+ serviceEntryName As String = "" ,
226+ serviceDisplayName As String = "" ,
227+ serviceDescription As String = "" ,
228+ serviceObjectName As String = "" ,
229+ serviceStartType As WindowsService.ServiceStartType = WindowsService.ServiceStartType.Unknown,
230+ serviceDelayedStart As Boolean = False ,
231+ serviceType As WindowsService.ServiceType = WindowsService.ServiceType.Unknown,
232+ serviceErrorControl As WindowsService.ServiceErrorControl = WindowsService.ServiceErrorControl.Unknown,
233+ serviceRequiredPrivilegesString() As String = New String () {}
234+ Using ServiceInfoRk As RegistryKey = Registry.LocalMachine.OpenSubKey( String .Format( "zSYS\ControlSet{0}\Services\{1}" , DefaultControlSet.ToString().PadLeft( 3 , "0" ), ServiceName), False )
235+ serviceImagePath = ServiceInfoRk.GetValue( "ImagePath" , "" , RegistryValueOptions.DoNotExpandEnvironmentNames)
236+ If serviceImagePath = "" Then
237+ ' This "service" is bogus
238+ Continue For
239+ End If
240+ ' TODO: display names and descriptions can also be pulled from resources that are embedded in executables or libraries.
241+ ' TODO: failure/recovery actions need to be implemented, which will require us to understand binary data
242+ ' TODO: relationships with services a service depends on or services that depend on a service need to be implemented
243+
244+ serviceEntryName = ServiceName
245+
246+ ' We explicitly tell that we want to grab the raw data without env var expansion because REG_EXPAND_SZ values
247+ ' are still string values, but with unexpanded environment variables. If the variable exists in the target system,
248+ ' it will show that value. This is true FOR THE IMAGE PATH, but we'll also do it for the display name and the description,
249+ ' just in case.
250+ serviceDisplayName = ServiceInfoRk.GetValue( "DisplayName" , "" , RegistryValueOptions.DoNotExpandEnvironmentNames)
251+ serviceDescription = ServiceInfoRk.GetValue( "Description" , "" , RegistryValueOptions.DoNotExpandEnvironmentNames)
252+ serviceObjectName = ServiceInfoRk.GetValue( "ObjectName" , "" )
253+ serviceStartType = ServiceInfoRk.GetValue( "Start" , - 1 )
254+ serviceDelayedStart = (ServiceInfoRk.GetValue( "DelayedAutoStart" , 0 ) = 1 )
255+ serviceType = ServiceInfoRk.GetValue( "Type" , - 1 )
256+ serviceErrorControl = ServiceInfoRk.GetValue( "ErrorControl" , - 1 )
257+ ' The required privileges property is a multi-value registry value, so we need an array
258+ serviceRequiredPrivilegesString = ServiceInfoRk.GetValue( "RequiredPrivileges" , New String () {})
259+
260+ Dim serviceRequiredPrivilegeList As New List( Of NTSecurityPrivilegeConstant)
261+
262+ If serviceRequiredPrivilegesString.Count > 0 Then
263+ ' Parse the items themselves to keys that are available in the dictionary we filled
264+ ' stuff in
265+ For Each serviceRequiredPrivilegeString In serviceRequiredPrivilegesString
266+ If PrivilegeMappingDictionary.Keys.Contains(serviceRequiredPrivilegeString) Then
267+ ' Then add it
268+ Dim constantInHeader As String = PrivilegeMappingDictionary(serviceRequiredPrivilegeString)
269+ serviceRequiredPrivilegeList.Add(PrivilegeConstantDictionary(constantInHeader))
270+ End If
271+ Next
272+ End If
273+
274+ serviceList.Add( New WindowsService(serviceEntryName,
275+ serviceDisplayName,
276+ serviceDescription,
277+ serviceObjectName,
278+ serviceImagePath,
279+ serviceStartType,
280+ serviceDelayedStart,
281+ serviceType,
282+ serviceErrorControl,
283+ serviceRequiredPrivilegeList))
284+ End Using
285+ Next
286+ Catch ex As Exception
287+
288+ End Try
289+
290+ ' Now we unload that hive
291+ RegistryHelper.UnloadRegistryHive( "HKLM\zSYS" )
292+ End If
293+
294+ Return serviceList
295+ End Function
296+
196297End Module
0 commit comments