2 Attachment(s)
[VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privileges
Attachment 184070
RunAs TrustedInstaller (NT AUTHORITY\SYSTEM)
VB6 Version 2.0 (Updated 24 Feb 2022)
twinBASIC Version 2.3.2 (Updated 16 Apr 2025)
If you've been running Windows 10, 11, and even to some extent 7, you've no doubt found even a so-called Administrator account doesn't have permission to do many things. Certain files, registry keys, etc, can only be accessed with the SYSTEM account under which many OS services run. TrustedInstaller is particularly privileged among system processes: It's the owner of many protected files and registry keys, so even just running as SYSTEM would leave you needing adjust permissions to take ownership (which you might not even be able to do) before altering them. So if you look at programs that do one of the most highly protected operations, disabling Windows Defender, they impersonate this process rather than simply run as SYSTEM (and why programs like AdvancedRun have 'Run as TrustedInstaller' as a separate option in addition to 'Run as SYSTEM').
This code shows you how to use the undocumented API NtImpersonateThread to have your thread impersonate TrustedInstaller, which allows full access to duplicate it's security token and start a process with the same privileges, running as the NT AUTHORITY\SYSTEM user. A big advantage of doing it this way is that we don't have to be running as a service, like the methods used by some other privilege escalating techniques.
Nirsoft's AdvancedRun has a feature to do this, but which sadly isn't open source much less written in VB, so I wanted to create something like it in VB6. I'll probably add more features like it has in the future, but this was the most complicated and useful.
Important: You must use Run As Administrator to use this code; it simply allows an admin to be an actual admin, it cannot escalate a unprivileged normal user to administrator. It does appear to work when run from the IDE, but that will of course need to also have been run with administrator privileges.
Also, while the code is fully Unicode-supporting, the built-in VB6 TextBox is of course not. If you need to launch a command line with non-ASCII characters, you can replace the TextBox with one that supports Unicode, or otherwise pass Unicode strings to the LaunchAsTI function.
Requirements
No external requirements, but the program does require a manifest with Common Controls 6 enabled to show the security shield icon on the button.
I've only tested this code on Windows 10 (1809/Enterprise LTSC), but it should work on all versions from XP through 11.
Version 2.0 Changes
-Added support for command line arguments. You can enter them with normal syntax: If the path to the program has a space, you must put it in quotes. Examples:
Code:
"C:\folder name\prog.exe" /arg
C:\path\prog.exe "C:\folder\filearg"
C:\prog.exe -a -b -c:d
Arguments parsed by PathGetArgsW in shlwapi.dll
CreateProcessWithTokenW, like CreateProcess supports command line arguments via setting both lpApplicationName and lpCommandLine. The trick is you have to pass just the path as the former, and the full path+args as the latter.
IMPORTANT: Supporting arguments this way means that even if you have none, if there's a space, the path must be enclosed in quotes, e.g.
"C:\Program Files\Process Hacker 2\ProcessHacker.exe"
-Error messages now have their descriptions looked up.
-Replaced the 3-second wait for the TrustedInstaller service to start with continuous monitoring with system-suggested wait. This will avoid false errors on a busy system or if e.g. a hard drive spinup pauses the service launch.
This also saved us from having to search for the process id as the QueryServiceStatusEx call returns that information.
How the code works:
(incomplete snippets included for conceptual illustration, download the entire project for complete code)
- The SeDebugPrivilege and SeImpersonatePrivilege privileges are enabled with AdjustTokenPrivileges so we can play around with other processes.
Code:
lRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, hToken)
If SetPrivilege(hToken, SE_DEBUG_NAME, True) Then
[...]
If SetPrivilege(hToken, SE_IMPERSONATE_NAME, True) Then
- This is used to access the token for winlogon.exe and use ImpersonateLoggedOnUser to elevate our access.
Code:
Dim hWinLogon As Long
Dim pidWinLogon As Long
pidWinLogon = FindProcessByName("winlogon.exe")
If pidWinLogon Then
hWinLogon = OpenProcess(PROCESS_DUP_HANDLE Or PROCESS_QUERY_INFORMATION, 0&, pidWinLogon)
If hWinLogon Then
lRet = OpenProcessToken(hWinLogon, TOKEN_QUERY Or TOKEN_DUPLICATE, hSysTkn)
If lRet Then
If ImpersonateLoggedOnUser(hSysTkn) Then
- We start the TrustedInstaller service via Service APIs (and ignore any 'already running' error).
Code:
hSCM = OpenSCManagerW(0&, 0&, SC_MANAGER_ALL_ACCESS)
hSvc = OpenServiceW(hSCM, StrPtr("TrustedInstaller"), SERVICE_START)
hr = StartServiceW(hSvc, 0&, 0&)
- We use CreateToolhelp32Snapshot to get a list of running processes, identify the process id of the TrustedInstaller service, then use the same API to get a list of threads to find TI's thread.
Code:
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0&)
If hSnapshot Then
te32.dwSize = Len(te32)
hr = Thread32First(hSnapshot, te32)
Do
If te32.th32OwnerProcessID = pid Then
GetFirstThreadId = te32.th32ThreadID
Exit Function
End If
Loop While Thread32Next(hSnapshot, te32)
End If
- NtImpersonateThread is used to have our thread impersonate TrustedInstaller.
Code:
hThread = OpenThread(THREAD_DIRECT_IMPERSONATION, 0&, hTiTid)
If hThread Then
Dim sqos As SECURITY_QUALITY_OF_SERVICE
sqos.Length = Len(sqos)
sqos.ImpersonationLevel = SecurityImpersonation
status = NtImpersonateThread(GetCurrentThread(), hThread, sqos)
- Since it's now in our thread, we can open 'our' token (impersonating TI) with TOKEN_ALL_ACCESS and duplicate it.
Code:
lRet = OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, 0&, hTiToken)
Dim satr As SECURITY_ATTRIBUTES
Dim hStolenToken As Long
satr.nLength = Len(satr)
lRet = DuplicateTokenEx(hTiToken, MAXIMUM_ALLOWED, VarPtr(satr), SecurityImpersonation, TokenImpersonation, hStolenToken)
- The duplicate token can now be used with CreateProcessWithTokenW to start a process with the fully privileged token copied from TrustedInstaller running as NT AUTHORITY\SYSTEM.
Code:
Dim tStartInfo As STARTUPINFOW
Dim tProcInfo As PROCESS_INFORMATION
sDesktop = "WinSta0\Default"
tStartInfo.cbSize = Len(tStartInfo)
tStartInfo.lpDesktop = StrPtr(sDesktop)
LaunchAsTI = CreateProcessWithTokenW(hStolenToken, LOGON_WITH_PROFILE, 0&, StrPtr(sCommandLine), CREATE_UNICODE_ENVIRONMENT, 0&, 0&, tStartInfo, tProcInfo)
You can start something like ProcessExplorer or ProcessHacker, and right in the titlebar it will note it's running as SYSTEM, and you'll see all the stuff that previously just said access denied, even if you ran as administrator.
Thanks
This project was based on Norbert Federa's run-as-trustedinstaller and APTortellini's unDefender.
SetPrivilege function borrowed from Nayan Patel at BinaryWorld's code found here. Some declares from DavidUK's Viewing Token Privileges CodeBank project.
Background: The Art of Becoming TrustedInstaller.
The VB6 version attached to this post is out of date and missing features and fixes of the latest version.
The twinBASIC version is the latest (v2.3.2, 15 Apr 2025) and the project source and binaries can be found in the project repository: https://github.com/fafalone/RunAsTrustedInstaller
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
x64-compatible twinBASIC version
There's now a native x64 compatible version of this project for twinBASIC: https://www.vbforums.com/showthread....HORITY-SYSTEM) (or direct link to GitHub)
Repository includes binary builds.
Includes several additional features currently exclusive to the tB version: Unicode support for path, ComboBox with MRU instead of TextBox, a file picker to browse for the program to run, ability to specify process priority, support for environment variables in the path string, and the ability to run a program as TI by opening it with this program, bypassing the GUI.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
Project updated.
Version 2.0 Changes
- Added support for command line arguments. You can enter them with normal syntax: If the path to the program has a space, you must put it in quotes. Examples:
Code:
"C:\folder name\prog.exe" /arg
C:\path\prog.exe "C:\folder\filearg"
C:\prog.exe -a -b -c:d
Arguments parsed by PathGetArgsW in shlwapi.dll.
CreateProcessWithTokenW, like CreateProcess supports command line arguments via setting both lpApplicationName and lpCommandLine. The trick is you have to pass just the path as the former, and the full path+args as the latter.
- Error messages now have their descriptions looked up.
- Replaced the 3-second wait for the TrustedInstaller service to start with continuous monitoring with system-suggested wait. This will avoid false errors on a busy system or if e.g. a hard drive spinup pauses the service launch.
This also saved us from having to search for the process id as the QueryServiceStatusEx call returns that information.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
Run As Local Security Authority
Just wanted to add... while TrustedInstaller is under NT AUTHORITY\SYSTEM and has a lot of powerful privileges and is the owner of many critical objects, there are still some permissions denied to it. For instance, you can't enable the SeCreateToken privilege.
To gain that privilege, you can use the same method to steal the token from lsass.exe (Local Security Authority Process) instead.
The following is a drop-in alternative for this project. Simply replace the StartAndAcquireToken procedure with the one below:
Code:
Private Function StartAndAcquireToken() As Long
'Yoink lsass.exe's token
Dim hSCM As Long
Dim hSvc As Long
Dim hToken As Long
Dim lPid As Long
Dim lTiPid As Long
Dim hThread As Long
Dim hTiTid As Long
Dim hr As Long
Dim lastErr As Long
Dim Status As Long
Dim lRet As Long
lTiPid = FindProcessByName("lsass.exe")
If lTiPid > 0& Then
PostLog "Found security authority pid..."
hTiTid = GetFirstThreadId(lTiPid)
PostLog "First thread id for pid=" & hTiTid
If hTiTid Then
lastErr = 0&
hThread = OpenThread(THREAD_DIRECT_IMPERSONATION, 0&, hTiTid)
lastErr = Err.LastDllError
If hThread Then
Dim sqos As SECURITY_QUALITY_OF_SERVICE
sqos.Length = Len(sqos)
sqos.ImpersonationLevel = SecurityImpersonation
Status = NtImpersonateThread(GetCurrentThread(), hThread, sqos)
If Status = STATUS_SUCCESS Then
PostLog "NtImpersonateThread STATUS_SUCCESS. Opening current token..."
lastErr = 0&: lRet = 0&
lRet = OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, 0&, hTiToken)
lastErr = Err.LastDllError
If lRet Then
PostLog "OpenThreadToken success, return=0x" & lRet
lastErr = 0&: lRet = 0&
Else
PostLog "Failed to open own token after NtIT, lastErr=" & GetErrorName(lastErr) & " (0x" & Hex$(lastErr) & ")"
End If
Else
PostLog "NtImpersonateThread failed, NTSTATUS=" & GetNtStatusName(Status) & "(0x" & Hex$(Status) & ")"
End If
Else
PostLog "Failed to open Security Authority thread, lastErr=" & GetErrorName(lastErr) & " (0x" & Hex$(lastErr) & ")"
End If
Else
PostLog "Failed to get Security Authority thread id: 0x" & Hex$(hTiTid)
End If
Else
PostLog "Failed to find Security Authority process, code 0x" & lTiPid
End If
End Function
(There's no service to start, lsass is always running)
Self-Elevate
I've also written the following Sub Main (to use as the Startup object instead of Form1 etc in project properties) that makes your app (assuming you run as admin) immediately respawns itself as running as NT AUTHORITY\SYSTEM if compiled (or bypasses additional elevation if in the IDE). Note that in the Demo with the way it logs events, you also need to replace the PostLog function with the one below to prevent the form from being loaded by Form1.AppendLog if it's the initial run that just launches the elevated version:
Code:
Private Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long
Private bUI As Boolean
Private Sub PostLog(smsg As String)
If bUI Then Form1.AppendLog smsg
End Sub
Sub Main()
'We need to not only run as admin, but run as superadmin.
'Easiest way is to just impersonate the system, hijack the
'security service's token, and respawn running natively
'as NT AUTHORITY\SYSTEM with full security service
'abilities, including enabled the create token privilege.
If IsUserAnAdmin() Then
If IsIDE() Then
bUI = True
Form1.Show
PostLog "Running from IDE, bypassed elevation routine."
Else
If InStr(Command(), "/GO") Then
bUI = True
Form1.Show
PostLog "Now running as fully elevated. Ready..."
'AdjustPrivilegesEx - if you want to add more privileges just expand that routine
Else
Dim sLA As String
'Put in quotes in case path has space; neccessary for parser.
sLA = Chr$(34) & App.Path & "\" & App.EXEName & ".exe" & Chr$(34) & " /GO"
LaunchAsTI sLA
Sleep 1000
End If
End If
Else
MsgBox "You must run this program as Administrator.", vbCritical + vbOKOnly, App.Title
End If
End Sub
Private Function IsIDE() As Boolean
Dim buff As String
Dim Success As Long
buff = Space$(255)
Success = GetModuleFileName(App.hInstance, buff, Len(buff))
If Success > 0 Then
IsIDE = InStr(LCase$(buff), "vb6.exe") > 0
End If
End Function
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
Very good job works very well.
a little problem i found
using Main()
two processes are created in taskmanager
RunAsTI.exe /GO SYSTEM
RunAsTI.exe USER
when closed, RunAsTI.exe USER remains open in the taskmanager.
a greeting
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
Sorry forgot to note that with the way the Demo logs messages you need to set a flag so that calls to PostLog don't load the form. I've updated the post.
Note that the only reason you'd want to self elevate is if you were writing an app that itself needed elevated privileges; if you're just launching other apps, running the original Demo as administrator is fine; you don't need to self-elevate.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
There's now a native x64 compatible version of this project for twinBASIC: https://www.vbforums.com/showthread....HORITY-SYSTEM) (or direct link to GitHub)
Repository includes binary builds.
Includes several additional features currently exclusive to the tB version: Unicode support for path, ComboBox with MRU instead of TextBox, a file picker to browse for the program to run, ability to specify process priority, support for environment variables in the path string, and the ability to run a program as TI by opening it with this program, bypassing the GUI.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
twinbasic make exe ,1.4-1.8M(X64)
VB6 MAKE exe ,only 60kb
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
twinBASIC does not have compiler optimization yet; and part of that is if you use GUI elements, it builds in the entire WinNativeForms package instead of only the parts you use. The roadmap has compiler optimizations starting within the next couple months.
As an example, I built a command-line only version of this project, and the .exe was only 152KB, a much smaller difference.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
Waiting...
Enabling privileges...
AdjustPrivileges::Got process token.
AdjustPrivileges::Enabled debug privilege.
AdjustPrivileges::Enabled impersonate privilege.
Impersonating system...
Got winlogon pid, opening process...
Got winlogon process handle, opening token...
Successfully impersonated system!
Service manager opened. Opening TrustedInstaller service...
Attempting to start TrustedInstaller service...
Service running, pid=4540
First thread id for pid=4188
NtImpersonateThread STATUS_SUCCESS. Opening current token...
OpenThreadToken success, return=0x1
Duplicating stolen TI token...
Token duplicated. Creating process...
LaunchAsTI::CreateProcessWithTokenW failed, lastErr=无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动。 (0x422)
LaunchAsTI return code=0x0 (FAIL)
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
That error is saying a service failed to start; what service are you trying to start? It may depend on a service that's not yet started.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil
The twinBASIC version of this project has been updated. Command line launching wasn't working, and it now supports an optional argument to set the priority via command line as well.
RunAsTI64.exe [/p:n] path
See details in repo readme.
This is in addition to several upgrades the tB already got over the VB6 version, including a file picker, MRU list, setting priority, command line use, and Unicode support in the GUI.
Re: [VB6] Run process as TrustedInstalled (NT AUTHORITY\SYSTEM) w/ full system privil