PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Visual Studio 2008/VBUACSelfElevation/MainForm.vb

#
Visual Basic | 464 lines | 237 code | 57 blank | 170 comment | 1 complexity | f1151439c1b336cbedcf5feac84aafff MD5 | raw file
  1. '********************************* Module Header ***********************************\
  2. ' Module Name: MainForm.vb
  3. ' Project: VBUACSelfElevation
  4. ' Copyright (c) Microsoft Corporation.
  5. '
  6. ' User Account Control (UAC) is a new security component in Windows Vista and newer
  7. ' operating systems. With UAC fully enabled, interactive administrators normally run
  8. ' with least user privileges. This example demonstrates how to check the privilege
  9. ' level of the current process, and how to self-elevate the process by giving
  10. ' explicit consent with the Consent UI.
  11. '
  12. ' This source is subject to the Microsoft Public License.
  13. ' See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  14. ' All other rights reserved.
  15. '
  16. ' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
  17. ' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
  18. ' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  19. '***********************************************************************************/
  20. #Region "Imports directives"
  21. Imports Microsoft.Win32.SafeHandles
  22. Imports System.Runtime.InteropServices
  23. Imports System.Security.Principal
  24. Imports System.ComponentModel
  25. #End Region
  26. Public Class MainForm
  27. #Region "Helper Functions for Admin Privileges and Elevation Status"
  28. ''' <summary>
  29. ''' The function checks whether the primary access token of the process belongs
  30. ''' to user account that is a member of the local Administrators group, even if
  31. ''' it currently is not elevated.
  32. ''' </summary>
  33. ''' <returns>
  34. ''' Returns True if the primary access token of the process belongs to user
  35. ''' account that is a member of the local Administrators group. Returns False
  36. ''' if the token does not.
  37. ''' </returns>
  38. ''' <exception cref="System.ComponentModel.Win32Exception">
  39. ''' When any native Windows API call fails, the function throws a Win32Exception
  40. ''' with the last error code.
  41. ''' </exception>
  42. Friend Function IsUserInAdminGroup() As Boolean
  43. Dim fInAdminGroup As Boolean = False
  44. Dim hToken As SafeTokenHandle = Nothing
  45. Dim hTokenToCheck As SafeTokenHandle = Nothing
  46. Dim pElevationType As IntPtr = IntPtr.Zero
  47. Dim pLinkedToken As IntPtr = IntPtr.Zero
  48. Dim cbSize As Integer = 0
  49. Try
  50. ' Open the access token of the current process for query and duplicate.
  51. If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
  52. NativeMethods.TOKEN_QUERY Or NativeMethods.TOKEN_DUPLICATE, hToken)) Then
  53. Throw New Win32Exception
  54. End If
  55. ' Determine whether system is running Windows Vista or later operating
  56. ' systems (major version >= 6) because they support linked tokens, but
  57. ' previous versions (major version < 6) do not.
  58. If (Environment.OSVersion.Version.Major >= 6) Then
  59. ' Running Windows Vista or later (major version >= 6).
  60. ' Determine token type: limited, elevated, or default.
  61. ' Allocate a buffer for the elevation type information.
  62. cbSize = 4 ' Size of TOKEN_ELEVATION_TYPE
  63. pElevationType = Marshal.AllocHGlobal(cbSize)
  64. If (pElevationType = IntPtr.Zero) Then
  65. Throw New Win32Exception
  66. End If
  67. ' Retrieve token elevation type information.
  68. If (Not NativeMethods.GetTokenInformation(hToken, _
  69. TOKEN_INFORMATION_CLASS.TokenElevationType, _
  70. pElevationType, cbSize, cbSize)) Then
  71. Throw New Win32Exception
  72. End If
  73. ' Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET.
  74. Dim elevType As TOKEN_ELEVATION_TYPE = Marshal.ReadInt32(pElevationType)
  75. ' If limited, get the linked elevated token for further check.
  76. If (elevType = TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited) Then
  77. ' Allocate a buffer for the linked token.
  78. cbSize = IntPtr.Size
  79. pLinkedToken = Marshal.AllocHGlobal(cbSize)
  80. If (pLinkedToken = IntPtr.Zero) Then
  81. Throw New Win32Exception
  82. End If
  83. ' Get the linked token.
  84. If (Not NativeMethods.GetTokenInformation(hToken, _
  85. TOKEN_INFORMATION_CLASS.TokenLinkedToken, _
  86. pLinkedToken, cbSize, cbSize)) Then
  87. Throw New Win32Exception
  88. End If
  89. ' Marshal the linked token value from native to .NET.
  90. Dim hLinkedToken As IntPtr = Marshal.ReadIntPtr(pLinkedToken)
  91. hTokenToCheck = New SafeTokenHandle(hLinkedToken)
  92. End If
  93. End If
  94. ' CheckTokenMembership requires an impersonation token. If we just got a
  95. ' linked token, it already is an impersonation token. If we did not get
  96. ' a linked token, duplicate the original into an impersonation token for
  97. ' CheckTokenMembership.
  98. If (hTokenToCheck Is Nothing) Then
  99. If (Not NativeMethods.DuplicateToken(hToken, _
  100. SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, _
  101. hTokenToCheck)) Then
  102. Throw New Win32Exception
  103. End If
  104. End If
  105. ' Check if the token to be checked contains admin SID.
  106. Dim id As New WindowsIdentity(hTokenToCheck.DangerousGetHandle)
  107. Dim principal As New WindowsPrincipal(id)
  108. fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator)
  109. Finally
  110. ' Centralized cleanup for all allocated resources.
  111. If (Not hToken Is Nothing) Then
  112. hToken.Close()
  113. hToken = Nothing
  114. End If
  115. If (Not hTokenToCheck Is Nothing) Then
  116. hTokenToCheck.Close()
  117. hTokenToCheck = Nothing
  118. End If
  119. If (pElevationType <> IntPtr.Zero) Then
  120. Marshal.FreeHGlobal(pElevationType)
  121. pElevationType = IntPtr.Zero
  122. End If
  123. If (pLinkedToken <> IntPtr.Zero) Then
  124. Marshal.FreeHGlobal(pLinkedToken)
  125. pLinkedToken = IntPtr.Zero
  126. End If
  127. End Try
  128. Return fInAdminGroup
  129. End Function
  130. ''' <summary>
  131. ''' The function checks whether the current process is run as administrator.
  132. ''' In other words, it dictates whether the primary access token of the
  133. ''' process belongs to user account that is a member of the local
  134. ''' Administrators group and it is elevated.
  135. ''' </summary>
  136. ''' <returns>
  137. ''' Returns True if the primary access token of the process belongs to user
  138. ''' account that is a member of the local Administrators group and it is
  139. ''' elevated. Returns False if the token does not.
  140. ''' </returns>
  141. Friend Function IsRunAsAdmin() As Boolean
  142. Dim principal As New WindowsPrincipal(WindowsIdentity.GetCurrent)
  143. Return principal.IsInRole(WindowsBuiltInRole.Administrator)
  144. End Function
  145. ''' <summary>
  146. ''' The function gets the elevation information of the current process. It
  147. ''' dictates whether the process is elevated or not. Token elevation is only
  148. ''' available on Windows Vista and newer operating systems, thus
  149. ''' IsProcessElevated throws a C++ exception if it is called on systems prior
  150. ''' to Windows Vista. It is not appropriate to use this function to determine
  151. ''' whether a process is run as administartor.
  152. ''' </summary>
  153. ''' <returns>
  154. ''' Returns True if the process is elevated. Returns False if it is not.
  155. ''' </returns>
  156. ''' <exception cref="System.ComponentModel.Win32Exception">
  157. ''' When any native Windows API call fails, the function throws a Win32Exception
  158. ''' with the last error code.
  159. ''' </exception>
  160. ''' <remarks>
  161. ''' TOKEN_INFORMATION_CLASS provides TokenElevationType to check the elevation
  162. ''' type (TokenElevationTypeDefault / TokenElevationTypeLimited /
  163. ''' TokenElevationTypeFull) of the process. It is different from TokenElevation
  164. ''' in that, when UAC is turned off, elevation type always returns
  165. ''' TokenElevationTypeDefault even though the process is elevated (Integrity
  166. ''' Level == High). In other words, it is not safe to say if the process is
  167. ''' elevated based on elevation type. Instead, we should use TokenElevation.
  168. ''' </remarks>
  169. Friend Function IsProcessElevated() As Boolean
  170. Dim fIsElevated As Boolean = False
  171. Dim hToken As SafeTokenHandle = Nothing
  172. Dim cbTokenElevation As Integer = 0
  173. Dim pTokenElevation As IntPtr = IntPtr.Zero
  174. Try
  175. ' Open the access token of the current process with TOKEN_QUERY.
  176. If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
  177. NativeMethods.TOKEN_QUERY, hToken)) Then
  178. Throw New Win32Exception
  179. End If
  180. ' Allocate a buffer for the elevation information.
  181. cbTokenElevation = Marshal.SizeOf(GetType(TOKEN_ELEVATION))
  182. pTokenElevation = Marshal.AllocHGlobal(cbTokenElevation)
  183. If (pTokenElevation = IntPtr.Zero) Then
  184. Throw New Win32Exception
  185. End If
  186. ' Retrieve token elevation information.
  187. If (Not NativeMethods.GetTokenInformation(hToken, _
  188. TOKEN_INFORMATION_CLASS.TokenElevation, _
  189. pTokenElevation, cbTokenElevation, cbTokenElevation)) Then
  190. ' When the process is run on operating systems prior to
  191. ' Windows Vista, GetTokenInformation returns false with the
  192. ' ERROR_INVALID_PARAMETER error code because
  193. ' TokenIntegrityLevel is not supported on those OS's.
  194. Throw New Win32Exception
  195. End If
  196. ' Marshal the TOKEN_ELEVATION struct from native to .NET
  197. Dim elevation As TOKEN_ELEVATION = Marshal.PtrToStructure( _
  198. pTokenElevation, GetType(TOKEN_ELEVATION))
  199. ' TOKEN_ELEVATION.TokenIsElevated is a non-zero value if the
  200. ' token has elevated privileges; otherwise, a zero value.
  201. fIsElevated = (elevation.TokenIsElevated <> 0)
  202. Finally
  203. ' Centralized cleanup for all allocated resources.
  204. If (Not hToken Is Nothing) Then
  205. hToken.Close()
  206. hToken = Nothing
  207. End If
  208. If (pTokenElevation <> IntPtr.Zero) Then
  209. Marshal.FreeHGlobal(pTokenElevation)
  210. pTokenElevation = IntPtr.Zero
  211. cbTokenElevation = 0
  212. End If
  213. End Try
  214. Return fIsElevated
  215. End Function
  216. ''' <summary>
  217. ''' The function gets the integrity level of the current process. Integrity
  218. ''' level is only available on Windows Vista and newer operating systems, thus
  219. ''' GetProcessIntegrityLevel throws a C++ exception if it is called on systems
  220. ''' prior to Windows Vista.
  221. ''' </summary>
  222. ''' <returns>
  223. ''' Returns the integrity level of the current process. It is usually one of
  224. ''' these values:
  225. '''
  226. ''' SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used by
  227. ''' processes started by the Anonymous group. Blocks most write access.
  228. ''' (SID: S-1-16-0x0)
  229. '''
  230. ''' SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by
  231. ''' Protected Mode Internet Explorer. Blocks write acess to most objects
  232. ''' (such as files and registry keys) on the system. (SID: S-1-16-0x1000)
  233. '''
  234. ''' SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is used
  235. ''' by normal applications being launched while UAC is enabled.
  236. ''' (SID: S-1-16-0x2000)
  237. '''
  238. ''' SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used by
  239. ''' administrative applications launched through elevation when UAC is
  240. ''' enabled, or normal applications if UAC is disabled and the user is an
  241. ''' administrator. (SID: S-1-16-0x3000)
  242. '''
  243. ''' SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is used
  244. ''' by services and other system-level applications (such as Wininit,
  245. ''' Winlogon, Smss, etc.) (SID: S-1-16-0x4000)
  246. '''
  247. ''' </returns>
  248. ''' <exception cref="System.ComponentModel.Win32Exception">
  249. ''' When any native Windows API call fails, the function throws a Win32Exception
  250. ''' with the last error code.
  251. ''' </exception>
  252. Friend Function GetProcessIntegrityLevel() As Integer
  253. Dim IL As Integer = -1
  254. Dim hToken As SafeTokenHandle = Nothing
  255. Dim cbTokenIL As Integer = 0
  256. Dim pTokenIL As IntPtr = IntPtr.Zero
  257. Try
  258. ' Open the access token of the current process with TOKEN_QUERY.
  259. If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
  260. NativeMethods.TOKEN_QUERY, hToken)) Then
  261. Throw New Win32Exception
  262. End If
  263. ' Then we must query the size of the integrity level information
  264. ' associated with the token. Note that we expect GetTokenInformation to
  265. ' return False with the ERROR_INSUFFICIENT_BUFFER error code because we
  266. ' have given it a null buffer. On exit cbTokenIL will tell the size of
  267. ' the group information.
  268. If (Not NativeMethods.GetTokenInformation(hToken, _
  269. TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, _
  270. IntPtr.Zero, 0, cbTokenIL)) Then
  271. Dim err As Integer = Marshal.GetLastWin32Error
  272. If (err <> NativeMethods.ERROR_INSUFFICIENT_BUFFER) Then
  273. ' When the process is run on operating systems prior to Windows
  274. ' Vista, GetTokenInformation returns false with the
  275. ' ERROR_INVALID_PARAMETER error code because TokenIntegrityLevel
  276. ' is not supported on those OS's.
  277. Throw New Win32Exception(err)
  278. End If
  279. End If
  280. ' Now we allocate a buffer for the integrity level information.
  281. pTokenIL = Marshal.AllocHGlobal(cbTokenIL)
  282. If (pTokenIL = IntPtr.Zero) Then
  283. Throw New Win32Exception
  284. End If
  285. ' Now we ask for the integrity level information again. This may fail if
  286. ' an administrator has added this account to an additional group between
  287. ' our first call to GetTokenInformation and this one.
  288. If (Not NativeMethods.GetTokenInformation(hToken, _
  289. TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, _
  290. pTokenIL, cbTokenIL, cbTokenIL)) Then
  291. Throw New Win32Exception
  292. End If
  293. ' Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object.
  294. Dim tokenIL As TOKEN_MANDATORY_LABEL = Marshal.PtrToStructure( _
  295. pTokenIL, GetType(TOKEN_MANDATORY_LABEL))
  296. ' Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g.
  297. ' S-1-16-0x1000 stands for low integrity level SID). There is one and
  298. ' only one subauthority.
  299. Dim pIL As IntPtr = NativeMethods.GetSidSubAuthority(tokenIL.Label.Sid, 0)
  300. IL = Marshal.ReadInt32(pIL)
  301. Finally
  302. ' Centralized cleanup for all allocated resources.
  303. If (Not hToken Is Nothing) Then
  304. hToken.Close()
  305. hToken = Nothing
  306. End If
  307. If (pTokenIL <> IntPtr.Zero) Then
  308. Marshal.FreeHGlobal(pTokenIL)
  309. pTokenIL = IntPtr.Zero
  310. cbTokenIL = 0
  311. End If
  312. End Try
  313. Return IL
  314. End Function
  315. #End Region
  316. Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
  317. Handles MyBase.Load
  318. ' Get and display whether the primary access token of the process belongs
  319. ' to user account that is a member of the local Administrators group even
  320. ' if it currently is not elevated (IsUserInAdminGroup).
  321. Try
  322. Dim fInAdminGroup As Boolean = Me.IsUserInAdminGroup
  323. Me.lbInAdminGroup.Text = fInAdminGroup.ToString
  324. Catch ex As Exception
  325. Me.lbInAdminGroup.Text = "N/A"
  326. MessageBox.Show(ex.Message, "An error occurred in IsUserInAdminGroup", _
  327. MessageBoxButtons.OK, MessageBoxIcon.Error)
  328. End Try
  329. ' Get and display whether the process is run as administrator or not
  330. ' (IsRunAsAdmin).
  331. Try
  332. Dim fIsRunAsAdmin As Boolean = Me.IsRunAsAdmin
  333. Me.lbIsRunAsAdmin.Text = fIsRunAsAdmin.ToString
  334. Catch ex As Exception
  335. Me.lbIsRunAsAdmin.Text = "N/A"
  336. MessageBox.Show(ex.Message, "An error occurred in IsRunAsAdmin", _
  337. MessageBoxButtons.OK, MessageBoxIcon.Error)
  338. End Try
  339. ' Get and display the process elevation information (IsProcessElevated)
  340. ' and integrity level (GetProcessIntegrityLevel). The information is not
  341. ' available on operating systems prior to Windows Vista.
  342. If (Environment.OSVersion.Version.Major >= 6) Then
  343. ' Running Windows Vista or later (major version >= 6).
  344. Try
  345. ' Get and display the process elevation information.
  346. Dim fIsElevated As Boolean = Me.IsProcessElevated
  347. Me.lbIsElevated.Text = fIsElevated.ToString
  348. ' Update the Self-elevate button to show the UAC shield icon on
  349. ' the UI if the process is not elevated.
  350. Me.btnElevate.FlatStyle = FlatStyle.System
  351. NativeMethods.SendMessage(Me.btnElevate.Handle, NativeMethods.BCM_SETSHIELD, _
  352. 0, IIf(fIsElevated, IntPtr.Zero, New IntPtr(1)))
  353. Catch ex As Exception
  354. Me.lbIsElevated.Text = "N/A"
  355. MessageBox.Show(ex.Message, "An error occurred in IsProcessElevated", _
  356. MessageBoxButtons.OK, MessageBoxIcon.Error)
  357. End Try
  358. Try
  359. ' Get and display the process integrity level.
  360. Dim IL As Integer = Me.GetProcessIntegrityLevel
  361. Select Case IL
  362. Case NativeMethods.SECURITY_MANDATORY_UNTRUSTED_RID
  363. Me.lbIntegrityLevel.Text = "Untrusted"
  364. Case NativeMethods.SECURITY_MANDATORY_LOW_RID
  365. Me.lbIntegrityLevel.Text = "Low"
  366. Case NativeMethods.SECURITY_MANDATORY_MEDIUM_RID
  367. Me.lbIntegrityLevel.Text = "Medium"
  368. Case NativeMethods.SECURITY_MANDATORY_HIGH_RID
  369. Me.lbIntegrityLevel.Text = "High"
  370. Case NativeMethods.SECURITY_MANDATORY_SYSTEM_RID
  371. Me.lbIntegrityLevel.Text = "System"
  372. Case Else
  373. Me.lbIntegrityLevel.Text = "Unknown"
  374. End Select
  375. Catch ex As Exception
  376. Me.lbIntegrityLevel.Text = "N/A"
  377. MessageBox.Show(ex.Message, "An error occurred in GetProcessIntegrityLevel!", _
  378. MessageBoxButtons.OK, MessageBoxIcon.Hand)
  379. End Try
  380. Else
  381. Me.lbIsElevated.Text = "N/A"
  382. Me.lbIntegrityLevel.Text = "N/A"
  383. End If
  384. End Sub
  385. Private Sub btnElevate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
  386. Handles btnElevate.Click
  387. ' Elevate the process if it is not run as administrator.
  388. If (Not Me.IsRunAsAdmin) Then
  389. ' Launch itself as administrator
  390. Dim proc As New ProcessStartInfo
  391. proc.UseShellExecute = True
  392. proc.WorkingDirectory = Environment.CurrentDirectory
  393. proc.FileName = Application.ExecutablePath
  394. proc.Verb = "runas"
  395. Try
  396. Process.Start(proc)
  397. Catch
  398. ' The user refused the elevation.
  399. ' Do nothing and return directly ...
  400. Return
  401. End Try
  402. Application.Exit() ' Quit itself
  403. Else
  404. MessageBox.Show("The process is running as administrator", "UAC")
  405. End If
  406. End Sub
  407. End Class