├── .gitattributes ├── .gitignore ├── DelphiUiLib.Strings.pas ├── DelphiUtils.Arrays.pas ├── DelphiUtils.Async.pas ├── DelphiUtils.AutoEvents.pas ├── DelphiUtils.AutoObjects.pas ├── DelphiUtils.ExternalImport.pas ├── DelphiUtils.Lists.pas ├── DelphiUtils.RangeChecks.pas ├── Headers ├── DelphiApi.DelayLoad.pas ├── DelphiApi.Reflection.pas ├── Ntapi.AppModel.Policy.pas ├── Ntapi.Bits.pas ├── Ntapi.CommCtrls.pas ├── Ntapi.ConsoleApi.pas ├── Ntapi.DbgHelp.pas ├── Ntapi.ImageHlp.pas ├── Ntapi.NtSecApi.pas ├── Ntapi.ObjBase.pas ├── Ntapi.ObjIdl.pas ├── Ntapi.ProcessThreadsApi.pas ├── Ntapi.ShellApi.pas ├── Ntapi.Shlwapi.pas ├── Ntapi.UserEnv.pas ├── Ntapi.Versions.pas ├── Ntapi.WinBase.pas ├── Ntapi.WinError.pas ├── Ntapi.WinNt.DebugRegisters.pas ├── Ntapi.WinNt.pas ├── Ntapi.WinSafer.pas ├── Ntapi.WinSvc.pas ├── Ntapi.WinUser.pas ├── Ntapi.actctx.pas ├── Ntapi.appmodel.ExecAlias.pas ├── Ntapi.appmodel.pas ├── Ntapi.crt.pas ├── Ntapi.dismapi.pas ├── Ntapi.minidump.pas ├── Ntapi.msdia.pas ├── Ntapi.ntcsrapi.pas ├── Ntapi.ntdbg.pas ├── Ntapi.ntdef.pas ├── Ntapi.ntexapi.pas ├── Ntapi.ntioapi.fsctl.pas ├── Ntapi.ntioapi.pas ├── Ntapi.ntldr.pas ├── Ntapi.ntlpcapi.pas ├── Ntapi.ntlsa.pas ├── Ntapi.ntmmapi.pas ├── Ntapi.ntobapi.pas ├── Ntapi.ntpebteb.pas ├── Ntapi.ntpoapi.pas ├── Ntapi.ntpsapi.pas ├── Ntapi.ntregapi.pas ├── Ntapi.ntrtl.pas ├── Ntapi.ntsam.pas ├── Ntapi.ntseapi.pas ├── Ntapi.ntsmss.pas ├── Ntapi.ntstatus.pas ├── Ntapi.nttmapi.pas ├── Ntapi.nttp.pas ├── Ntapi.ntwow64.pas ├── Ntapi.offreg.pas ├── Ntapi.taskschd.pas ├── Ntapi.usermgr.pas ├── Ntapi.wimgapi.pas ├── Ntapi.wincred.pas ├── Ntapi.winrt.appmodel.pas ├── Ntapi.winrt.pas ├── Ntapi.winsta.pas ├── Ntapi.xmllite.pas └── Readme.md ├── LICENSE.txt ├── NtUiLib.Console.pas ├── NtUiLib.Errors.mc ├── NtUiLib.Errors.pas ├── NtUiLib.Errors.res ├── NtUiLib.Exceptions.pas ├── NtUiLib ├── DelphiUiLib.HysteresisList.pas ├── DelphiUiLib.Reflection.Numeric.pas ├── DelphiUiLib.Reflection.Records.pas ├── DelphiUiLib.Reflection.Strings.pas ├── DelphiUiLib.Reflection.pas ├── NtUiLib.AutoCompletion.Namespace.pas ├── NtUiLib.AutoCompletion.Sid.AppContainer.pas ├── NtUiLib.AutoCompletion.Sid.Capabilities.pas ├── NtUiLib.AutoCompletion.Sid.Capabilities.rc ├── NtUiLib.AutoCompletion.Sid.Capabilities.res ├── NtUiLib.AutoCompletion.Sid.Common.pas ├── NtUiLib.AutoCompletion.Sid.pas ├── NtUiLib.AutoCompletion.pas ├── NtUiLib.Errors.Dialog.pas ├── NtUiLib.Exceptions.Dialog.pas ├── NtUiLib.Reflection.Types.pas ├── NtUiLib.TaskDialog.pas ├── NtUiLib.WinCred.pas └── Readme.md ├── NtUtils.ActCtx.pas ├── NtUtils.AntiHooking.pas ├── NtUtils.Com.pas ├── NtUtils.Csr.pas ├── NtUtils.DbgHelp.Dia.pas ├── NtUtils.DbgHelp.pas ├── NtUtils.Debug.pas ├── NtUtils.Dism.pas ├── NtUtils.Environment.Remote.pas ├── NtUtils.Environment.User.pas ├── NtUtils.Environment.pas ├── NtUtils.Errors.pas ├── NtUtils.Files.Control.pas ├── NtUtils.Files.Directories.pas ├── NtUtils.Files.FltMgr.pas ├── NtUtils.Files.Mup.pas ├── NtUtils.Files.Open.pas ├── NtUtils.Files.Operations.pas ├── NtUtils.Files.Volumes.pas ├── NtUtils.Files.pas ├── NtUtils.ImageHlp.DbgHelp.pas ├── NtUtils.ImageHlp.Syscalls.pas ├── NtUtils.ImageHlp.pas ├── NtUtils.Jobs.Remote.pas ├── NtUtils.Jobs.pas ├── NtUtils.Ldr.pas ├── NtUtils.Lpc.pas ├── NtUtils.Lsa.Audit.pas ├── NtUtils.Lsa.Logon.pas ├── NtUtils.Lsa.Sid.pas ├── NtUtils.Lsa.pas ├── NtUtils.Manifests.pas ├── NtUtils.Memory.pas ├── NtUtils.MiniDumps.pas ├── NtUtils.Objects.Compare.pas ├── NtUtils.Objects.Namespace.pas ├── NtUtils.Objects.Remote.pas ├── NtUtils.Objects.Snapshots.pas ├── NtUtils.Objects.pas ├── NtUtils.Packages.ExecAlias.pas ├── NtUtils.Packages.SRCache.pas ├── NtUtils.Packages.WinRT.pas ├── NtUtils.Packages.pas ├── NtUtils.Power.pas ├── NtUtils.Processes.Create.Clone.pas ├── NtUtils.Processes.Create.Com.pas ├── NtUtils.Processes.Create.Csr.pas ├── NtUtils.Processes.Create.Manual.pas ├── NtUtils.Processes.Create.Native.pas ├── NtUtils.Processes.Create.Package.pas ├── NtUtils.Processes.Create.Remote.pas ├── NtUtils.Processes.Create.Shell.pas ├── NtUtils.Processes.Create.Win32.pas ├── NtUtils.Processes.Create.pas ├── NtUtils.Processes.Info.Remote.pas ├── NtUtils.Processes.Info.pas ├── NtUtils.Processes.Modules.pas ├── NtUtils.Processes.Snapshots.pas ├── NtUtils.Processes.pas ├── NtUtils.Profiles.Reloader.pas ├── NtUtils.Profiles.pas ├── NtUtils.Registry.Offline.pas ├── NtUtils.Registry.VReg.pas ├── NtUtils.Registry.pas ├── NtUtils.Sam.pas ├── NtUtils.Sections.pas ├── NtUtils.Security.Acl.pas ├── NtUtils.Security.AppContainer.pas ├── NtUtils.Security.Sid.pas ├── NtUtils.Security.pas ├── NtUtils.Shellcode.Dll.pas ├── NtUtils.Shellcode.Exe.pas ├── NtUtils.Shellcode.pas ├── NtUtils.Svc.SingleTaskSvc.pas ├── NtUtils.Svc.pas ├── NtUtils.Synchronization.pas ├── NtUtils.SysUtils.pas ├── NtUtils.System.pas ├── NtUtils.TaskScheduler.pas ├── NtUtils.Threads.Worker.pas ├── NtUtils.Threads.pas ├── NtUtils.Tokens.AppModel.pas ├── NtUtils.Tokens.Impersonate.pas ├── NtUtils.Tokens.Info.pas ├── NtUtils.Tokens.Logon.pas ├── NtUtils.Tokens.Misc.pas ├── NtUtils.Tokens.pas ├── NtUtils.Transactions.Remote.pas ├── NtUtils.Transactions.pas ├── NtUtils.UserManager.pas ├── NtUtils.Wim.pas ├── NtUtils.WinSafer.pas ├── NtUtils.WinStation.pas ├── NtUtils.WinUser.WindowAffinity.pas ├── NtUtils.WinUser.WinstaLock.pas ├── NtUtils.WinUser.pas ├── NtUtils.XmlLite.pas ├── NtUtils.pas └── Readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | -------------------------------------------------------------------------------- /DelphiUiLib.Strings.pas: -------------------------------------------------------------------------------- 1 | unit DelphiUiLib.Strings; 2 | 3 | { 4 | This module includes functions for preparing text for showing it to users. 5 | } 6 | 7 | interface 8 | 9 | { Text prettification } 10 | 11 | // Insert spaces into CamelCase strings and remove a prefix/suffix 12 | function PrettifyCamelCase( 13 | const CamelCaseText: String; 14 | const Prefix: String = ''; 15 | const Suffix: String = '' 16 | ): String; 17 | 18 | // Convert CamelCase to SNAKE_CASE string 19 | function CamelCaseToSnakeCase( 20 | const Text: String 21 | ): string; 22 | 23 | // Adjust capitalization and add spaces to SNAKE_CASE strings 24 | function PrettifySnakeCase( 25 | const CapsText: String; 26 | const Prefix: String = ''; 27 | const Suffix: String = '' 28 | ): String; 29 | 30 | { Integers } 31 | 32 | // Convert an integer to a readable decimal representation (as 12 345 678) 33 | function IntToStrEx(const Value: UInt64; Width: Integer = 0): String; 34 | 35 | // Convert an integer to a readable hexadecimal representation (as 0x0FFE FFF0) 36 | function IntToHexEx(const Value: UInt64; Digits: Integer = 0): String; 37 | 38 | // Convert a pointer to a readable hexadecimal representation (as 0x0FFE FFF0) 39 | function PtrToHexEx(Value: Pointer; Digits: Integer = 8): String; 40 | 41 | implementation 42 | 43 | uses 44 | NtUtils.SysUtils; 45 | 46 | {$BOOLEVAL OFF} 47 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 48 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 49 | 50 | function PrettifyCamelCase; 51 | var 52 | i: Integer; 53 | begin 54 | // Convert a string with from CamelCase to a spaced string removing a 55 | // prefix/suffix: '[Prefix]MyExampleIDTest[Suffix]' => 'My Example ID Test' 56 | 57 | Result := CamelCaseText; 58 | 59 | // Remove prefix & suffix 60 | RtlxPrefixStripString(Prefix, Result, True); 61 | RtlxSuffixStripString(Suffix, Result, True); 62 | 63 | // Add a space before a capital that has a non-capital on either side of it 64 | 65 | i := Low(Result); 66 | 67 | // Skip leading lower-case word 68 | while (i <= High(Result)) and (AnsiChar(Result[i]) in ['a'..'z']) do 69 | Inc(i); 70 | 71 | Inc(i); 72 | while i <= High(Result) do 73 | begin 74 | if (AnsiChar(Result[i]) in ['A'..'Z', '0'..'9']) and 75 | (not (AnsiChar(Result[i - 1]) in ['A'..'Z', '0'..'9']) or 76 | ((i < High(Result)) and not (AnsiChar(Result[i + 1]) in 77 | ['A'..'Z', '0'..'9']))) then 78 | begin 79 | Insert(' ', Result, i); 80 | Inc(i); 81 | end; 82 | Inc(i); 83 | end; 84 | end; 85 | 86 | function CamelCaseToSnakeCase; 87 | var 88 | i: Integer; 89 | begin 90 | Result := PrettifyCamelCase(Text); 91 | 92 | for i := Low(Result) to High(Result) do 93 | if AnsiChar(Result[i]) in ['a'..'z'] then 94 | Result[i] := Chr(Ord('A') + Ord(Result[i]) - Ord('a')) 95 | else if Result[i] = ' ' then 96 | Result[i] := '_'; 97 | end; 98 | 99 | function PrettifySnakeCase; 100 | var 101 | i: Integer; 102 | begin 103 | // Convert a string with from capitals with underscores to a spaced string 104 | // removing a prefix/suffix, ex.: 'ERROR_ACCESS_DENIED' => 'Access Denied' 105 | 106 | Result := CapsText; 107 | 108 | // Remove prefix & suffix 109 | RtlxPrefixStripString(Prefix, Result, True); 110 | RtlxSuffixStripString(Suffix, Result, True); 111 | RtlxPrefixStripString('_', Result, True); 112 | RtlxSuffixStripString('_', Result, True); 113 | 114 | i := Succ(Low(Result)); 115 | while i <= High(Result) do 116 | begin 117 | case Result[i] of 118 | 'A'..'Z': 119 | Result[i] := Chr(Ord('a') + Ord(Result[i]) - Ord('A')); 120 | '_': 121 | begin 122 | Result[i] := ' '; 123 | Inc(i); // Skip the next letter 124 | end; 125 | end; 126 | Inc(i); 127 | end; 128 | end; 129 | 130 | function IntToStrEx; 131 | var 132 | ShortResult: ShortString; 133 | i: Integer; 134 | begin 135 | Str(Value, ShortResult); 136 | Result := String(ShortResult); 137 | 138 | i := High(Result) - 2; 139 | 140 | while i > Low(Result) do 141 | begin 142 | Insert(' ', Result, i); 143 | Dec(i, 3); 144 | end; 145 | 146 | if Width > Length(Result) then 147 | Result := RtlxBuildString(' ', Width - Length(Result)) + Result; 148 | end; 149 | 150 | function IntToHexEx; 151 | var 152 | i: Integer; 153 | begin 154 | if Digits <= 0 then 155 | begin 156 | // Add leading zeros 157 | if Value > $FFFFFFFFFFFF then 158 | Digits := 16 159 | else if Value > $FFFFFFFF then 160 | Digits := 12 161 | else if Value > $FFFF then 162 | Digits := 8 163 | else if Value > $FF then 164 | Digits := 4 165 | else 166 | Digits := 2; 167 | end; 168 | 169 | Result := RtlxUInt64ToStr(Value, nsHexadecimal, Digits); 170 | 171 | if Length(Result) > 6 then 172 | begin 173 | // Split digits into groups of four 174 | i := High(Result) - 3; 175 | while i > Low(Result) + 3 do 176 | begin 177 | Insert(' ', Result, i); 178 | Dec(i, 4) 179 | end; 180 | end; 181 | end; 182 | 183 | function PtrToHexEx; 184 | begin 185 | Result := IntToHexEx(UIntPtr(Value), Digits); 186 | end; 187 | 188 | end. 189 | -------------------------------------------------------------------------------- /DelphiUtils.Async.pas: -------------------------------------------------------------------------------- 1 | unit DelphiUtils.Async; 2 | 3 | { 4 | This module provides infrastructure for using anonymous functions as APC 5 | callbacks in asynchronous operations. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.ntioapi, DelphiApi.Reflection, DelphiUtils.AutoObjects; 12 | 13 | type 14 | // A prototype for an anonymous APC callback 15 | TAnonymousApcCallback = reference to procedure ( 16 | const IoStatusBlock: TIoStatusBlock 17 | ); 18 | 19 | { Interfaces } 20 | 21 | IAnonymousApcContext = interface (IAutoReleasable) 22 | function GetCallback: TAnonymousApcCallback; 23 | property Callback: TAnonymousApcCallback read GetCallback; 24 | end; 25 | 26 | // An APC context with a dedicated I/O Status Block 27 | IAnonymousIoApcContext = interface (IAnonymousApcContext) 28 | function IoStatusBlock: PIoStatusBlock; 29 | end; 30 | 31 | { Default Implementations } 32 | 33 | TAnonymousApcContext = class (TCustomAutoReleasable, IAnonymousApcContext) 34 | protected 35 | Payload: TAnonymousApcCallback; 36 | procedure Release; override; 37 | public 38 | function GetCallback: TAnonymousApcCallback; 39 | constructor Create(ApcCallback: TAnonymousApcCallback); 40 | end; 41 | 42 | TAnonymousIoApcContext = class (TAnonymousApcContext, IAnonymousIoApcContext) 43 | protected 44 | Iob: TIoStatusBlock; 45 | public 46 | function IoStatusBlock: PIoStatusBlock; 47 | end; 48 | 49 | // Get an APC routine for an anonymous APC callback 50 | function GetApcRoutine( 51 | [opt] AsyncCallback: TAnonymousApcCallback 52 | ): TIoApcRoutine; 53 | 54 | // Prepare an APC context with an I/O status block for asynchronous operations 55 | // or reference the I/O status block from the stack for synchronous calls 56 | function PrepareApcIsb( 57 | out ApcContext: IAnonymousIoApcContext; 58 | [opt] AsyncCallback: TAnonymousApcCallback; 59 | const [ref] IoStatusBlock: TIoStatusBlock 60 | ): PIoStatusBlock; 61 | 62 | // Prepare an APC context with an I/O status block for asynchronous operations 63 | // or allocate one from the heap 64 | function PrepareApcIsbEx( 65 | out ApcContext: IAnonymousIoApcContext; 66 | [opt] AsyncCallback: TAnonymousApcCallback; 67 | out xIoStatusBlock: IMemory 68 | ): PIoStatusBlock; 69 | 70 | implementation 71 | 72 | {$BOOLEVAL OFF} 73 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 74 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 75 | 76 | { TAnonymousApcContext } 77 | 78 | constructor TAnonymousApcContext.Create; 79 | var 80 | CallbackIntf: IInterface absolute ApcCallback; 81 | begin 82 | inherited Create; 83 | Payload := ApcCallback; 84 | CallbackIntf._AddRef; 85 | end; 86 | 87 | function TAnonymousApcContext.GetCallback; 88 | begin 89 | Result := Payload; 90 | end; 91 | 92 | procedure TAnonymousApcContext.Release; 93 | var 94 | Callback: TAnonymousApcCallback; 95 | CallbackIntf: IInterface absolute Callback; 96 | begin 97 | Callback := Payload; 98 | CallbackIntf._Release; 99 | inherited; 100 | end; 101 | 102 | { TAnonymousIoApcContext } 103 | 104 | function TAnonymousIoApcContext.IoStatusBlock; 105 | begin 106 | Result := @Iob; 107 | end; 108 | 109 | { Functions } 110 | 111 | // An APC-compatible wrapper for calling anonymous functions 112 | procedure ApcCallbackForwarder( 113 | [in] ApcContext: Pointer; 114 | const IoStatusBlock: TIoStatusBlock; 115 | [Reserved] Reserved: Cardinal 116 | ); stdcall; 117 | var 118 | ContextData: IAnonymousApcContext absolute ApcContext; 119 | begin 120 | if Assigned(ContextData) then 121 | try 122 | ContextData.Callback(IoStatusBlock); 123 | finally 124 | // Clean-up the captured variables of a one-time callbacks 125 | if ContextData.AutoRelease then 126 | ContextData._Release; 127 | end; 128 | end; 129 | 130 | function GetApcRoutine; 131 | begin 132 | // All anonymous functions go through a forwarder that manages their lifetime 133 | if Assigned(AsyncCallback) then 134 | Result := ApcCallbackForwarder 135 | else 136 | Result := nil; 137 | end; 138 | 139 | function PrepareApcIsb; 140 | begin 141 | if Assigned(AsyncCallback) then 142 | begin 143 | // Allocate the context and use its I/O status block 144 | ApcContext := TAnonymousIoApcContext.Create(AsyncCallback); 145 | Result := ApcContext.IoStatusBlock; 146 | end 147 | else 148 | begin 149 | // Use the I/O status block from the stack 150 | ApcContext := nil; 151 | Result := @IoStatusBlock; 152 | end; 153 | end; 154 | 155 | function PrepareApcIsbEx; 156 | begin 157 | if Assigned(AsyncCallback) then 158 | begin 159 | // Allocate the context and use its I/O status block 160 | ApcContext := TAnonymousIoApcContext.Create(AsyncCallback); 161 | Result := ApcContext.IoStatusBlock; 162 | end 163 | else 164 | begin 165 | // Allocate just the I/O status block 166 | ApcContext := nil; 167 | IMemory(xIoStatusBlock) := Auto.AllocateDynamic(SizeOf(TIoStatusBlock)); 168 | Result := xIoStatusBlock.Data; 169 | end; 170 | end; 171 | 172 | end. 173 | -------------------------------------------------------------------------------- /DelphiUtils.ExternalImport.pas: -------------------------------------------------------------------------------- 1 | unit DelphiUtils.ExternalImport; 2 | 3 | { 4 | This module allows working with Import Address Table for hooking 5 | local imports from external DLLs. 6 | } 7 | 8 | interface 9 | 10 | // Returns a pointer to a location in the IAT that stores the target of the jump 11 | // used by Delphi external import 12 | function ExternalImportTarget( 13 | ExternalImport: Pointer 14 | ): PPointer; 15 | 16 | // Determines the target of a Delphi external import 17 | function GetExternalImportTarget( 18 | ExternalImport: Pointer; 19 | out Target: Pointer 20 | ): Boolean; 21 | 22 | // Overwrites IAT to set a target of a Delphi external import 23 | function SetExternalImportTarget( 24 | ExternalImport: Pointer; 25 | const Target: Pointer 26 | ): Boolean; 27 | 28 | // Atomic exchange of the target of a Delphi external import 29 | function ExchangeExternalImportTarget( 30 | ExternalImport: Pointer; 31 | const NewTarget: Pointer; 32 | out OldTarget: Pointer 33 | ): Boolean; 34 | 35 | implementation 36 | 37 | {$BOOLEVAL OFF} 38 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 39 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 40 | 41 | const 42 | JMP = $25FF; 43 | 44 | type 45 | // Delphi's external import is a jmp instruction that uses a value from IAT 46 | // (Import Address Table). In case of a delayed import, it initially points to 47 | // an internal routine that resolves the import and adjusts the target address 48 | TExternalJump = packed record 49 | Opcode: Word; // FF 25 50 | Address: Integer; 51 | end; 52 | PExternalJump = ^TExternalJump; 53 | 54 | function ExternalImportTarget; 55 | begin 56 | // Expecting a jump instruction 57 | if PExternalJump(ExternalImport).Opcode <> JMP then 58 | Exit(nil); 59 | 60 | {$IFDEF Win64} 61 | // Relative address from the end of instruction on x64 62 | Result := PPointer(NativeInt(ExternalImport) + 63 | PExternalJump(ExternalImport).Address + SizeOf(TExternalJump)); 64 | {$ELSE} 65 | // Absolute address on x86 66 | Result := PPointer(PExternalJump(ExternalImport).Address); 67 | {$ENDIF} 68 | end; 69 | 70 | function GetExternalImportTarget; 71 | var 72 | pTarget: PPointer; 73 | begin 74 | pTarget := ExternalImportTarget(ExternalImport); 75 | Result := Assigned(pTarget); 76 | 77 | if Result then 78 | Target := pTarget^; 79 | end; 80 | 81 | function SetExternalImportTarget; 82 | var 83 | pTarget: PPointer; 84 | begin 85 | pTarget := ExternalImportTarget(ExternalImport); 86 | Result := Assigned(pTarget); 87 | 88 | if Result then 89 | AtomicExchange(pTarget^, Target); 90 | end; 91 | 92 | function ExchangeExternalImportTarget; 93 | var 94 | pTarget: PPointer; 95 | begin 96 | pTarget := ExternalImportTarget(ExternalImport); 97 | Result := Assigned(pTarget); 98 | 99 | if Result then 100 | OldTarget := AtomicExchange(pTarget^, NewTarget); 101 | end; 102 | 103 | end. 104 | -------------------------------------------------------------------------------- /DelphiUtils.RangeChecks.pas: -------------------------------------------------------------------------------- 1 | unit DelphiUtils.RangeChecks; 2 | 3 | { 4 | This module introduces helper functions for performing strict range checks 5 | during parsing. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | NtUtils; 12 | 13 | // Check if an address belongs to a memory region 14 | function CheckAddress( 15 | const Region: TMemory; 16 | [in] Address: Pointer 17 | ): Boolean; 18 | 19 | // Check if a range of addresses belongs to a memory region 20 | function CheckRange( 21 | const Region: TMemory; 22 | [in] BlockStart: Pointer; 23 | [in] BlockEnd: Pointer 24 | ): Boolean; 25 | 26 | // Check if a structure allocated at an address belongs to a memory region 27 | function CheckStruct( 28 | const Region: TMemory; 29 | [in] BlockStart: Pointer; 30 | const BlockSize: UInt64 31 | ): Boolean; 32 | 33 | // Check if an offset belongs to the memory region 34 | function CheckOffset( 35 | RegionSize: NativeUInt; 36 | const Offset: UInt64 37 | ): Boolean; 38 | 39 | // Check if a structure allocated at an offset fits into a memory region 40 | function CheckOffsetStruct( 41 | const Region: TMemory; 42 | const BlockOffset: UInt64; 43 | const BlockSize: UInt64 44 | ): Boolean; 45 | 46 | // Check if an array of elements fits into a buffer 47 | function CheckArraySize( 48 | RangeSize: NativeUInt; 49 | const ElementSize: UInt64; 50 | const ElementCount: UInt64 51 | ): Boolean; 52 | 53 | // Check if an array allocated at an address belongs to a memory region 54 | function CheckArray( 55 | const Region: TMemory; 56 | [in] ArrayStart: Pointer; 57 | const ElementSize: UInt64; 58 | const ElementCount: UInt64 59 | ): Boolean; 60 | 61 | // Check if an array allocated at an offset fits into a memory region 62 | function CheckOffsetArray( 63 | const Region: TMemory; 64 | const ArrayOffset: UInt64; 65 | const ElementSize: UInt64; 66 | const ElementCount: UInt64 67 | ): Boolean; 68 | 69 | implementation 70 | 71 | {$BOOLEVAL OFF} 72 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 73 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 74 | 75 | function CheckAddress; 76 | begin 77 | Result := (UIntPtr(Address) >= UIntPtr(Region.Address)) and 78 | ((UIntPtr(Address) <= UIntPtr(Region.Offset(Region.Size)))); 79 | end; 80 | 81 | function CheckRange; 82 | begin 83 | Result := (UIntPtr(BlockEnd) >= UIntPtr(BlockStart)) and 84 | CheckAddress(Region, BlockStart) and CheckAddress(Region, BlockEnd); 85 | end; 86 | 87 | function CheckStruct; 88 | begin 89 | Result := (BlockSize <= Region.Size) and CheckAddress(Region, BlockStart) and 90 | CheckAddress(Region, PByte(BlockStart) + BlockSize); 91 | end; 92 | 93 | function CheckOffset; 94 | begin 95 | Result := Offset <= RegionSize; 96 | end; 97 | 98 | function CheckOffsetStruct; 99 | begin 100 | {$Q-} 101 | Result := (BlockSize <= Region.Size) and (BlockOffset <= Region.Size) and 102 | (Region.Size - BlockOffset >= BlockSize); 103 | {$IFDEF Q+}{$Q+}{$ENDIF} 104 | end; 105 | 106 | function CheckArraySize; 107 | begin 108 | Result := (ElementSize = 0) or ((RangeSize div ElementSize) >= ElementCount); 109 | end; 110 | 111 | function CheckArray; 112 | begin 113 | {$Q-} 114 | Result := CheckAddress(Region, ArrayStart) and CheckArraySize( 115 | UIntPtr(Region.Offset(Region.Size)) - UIntPtr(ArrayStart), ElementSize, 116 | ElementCount); 117 | {$IFDEF Q+}{$Q+}{$ENDIF} 118 | end; 119 | 120 | function CheckOffsetArray; 121 | begin 122 | {$Q-} 123 | Result := (ArrayOffset <= Region.Size) and CheckArraySize( 124 | Region.Size - ArrayOffset, ElementSize, ElementCount); 125 | {$IFDEF Q+}{$Q+}{$ENDIF} 126 | end; 127 | 128 | end. 129 | -------------------------------------------------------------------------------- /Headers/DelphiApi.DelayLoad.pas: -------------------------------------------------------------------------------- 1 | unit DelphiApi.DelayLoad; 2 | 3 | { 4 | This header defines types for checking delay loaded imports in other modules. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | type 12 | TDelayedLoadDll = record 13 | Initialized: UIntPtr; // inline TRtlRunOnce 14 | DllName: PWideChar; 15 | DllAddress: Pointer; 16 | end; 17 | PDelayedLoadDll = ^TDelayedLoadDll; 18 | 19 | TDelayedLoadFunction = record 20 | Initialized: UIntPtr; // inline TRtlRunOnce 21 | Dll: PDelayedLoadDll; 22 | FunctionName: PAnsiChar; 23 | FunctionAddress: Pointer; 24 | CheckStatus: Cardinal; // NTSTATUS 25 | function IsImportByOrdinal: Boolean; 26 | function Ordinal: Word; 27 | end; 28 | PDelayedLoadFunction = ^TDelayedLoadFunction; 29 | 30 | implementation 31 | 32 | {$BOOLEVAL OFF} 33 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 34 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 35 | 36 | { TDelayedLoadFunction } 37 | 38 | function TDelayedLoadFunction.IsImportByOrdinal; 39 | begin 40 | Result := UIntPtr(FunctionName) <= High(Word); 41 | end; 42 | 43 | function TDelayedLoadFunction.Ordinal; 44 | begin 45 | Result := Word(FunctionName); 46 | end; 47 | 48 | end. 49 | -------------------------------------------------------------------------------- /Headers/Ntapi.CommCtrls.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.CommCtrls; 2 | 3 | interface 4 | 5 | {$WARN SYMBOL_PLATFORM OFF} 6 | {$MINENUMSIZE 4} 7 | 8 | uses 9 | Ntapi.WinNt, Ntapi.WinUser, DelphiApi.Reflection, DelphiApi.DelayLoad; 10 | 11 | const 12 | comctl32 = 'comctl32.dll'; 13 | 14 | var 15 | delayed_comctl32: TDelayedLoadDll = (DllName: comctl32); 16 | 17 | const 18 | // SDK::Commctrl.h - task dialog flags 19 | TDF_ENABLE_HYPERLINKS = $0001; 20 | TDF_USE_HICON_MAIN = $0002; 21 | TDF_USE_HICON_FOOTER = $0004; 22 | TDF_ALLOW_DIALOG_CANCELLATION = $0008; 23 | TDF_USE_COMMAND_LINKS = $0010; 24 | TDF_USE_COMMAND_LINKS_NO_ICON = $0020; 25 | TDF_EXPAND_FOOTER_AREA = $0040; 26 | TDF_EXPANDED_BY_DEFAULT = $0080; 27 | TDF_VERIFICATION_FLAG_CHECKED = $0100; 28 | TDF_SHOW_PROGRESS_BAR = $0200; 29 | TDF_SHOW_MARQUEE_PROGRESS_BAR = $0400; 30 | TDF_CALLBACK_TIMER = $0800; 31 | TDF_POSITION_RELATIVE_TO_WINDOW = $1000; 32 | TDF_RTL_LAYOUT = $2000; 33 | TDF_NO_DEFAULT_RADIO_BUTTON = $4000; 34 | TDF_CAN_BE_MINIMIZED = $8000; 35 | TDF_NO_SET_FOREGROUND = $00010000; // Win 8+ 36 | TDF_SIZE_TO_CONTENT = $01000000; 37 | 38 | // SDK::Commctrl.h - known icons 39 | TD_WARNING_ICON = MAKEINTRESOURCE(Word(-1)); 40 | TD_ERROR_ICON = MAKEINTRESOURCE(Word(-2)); 41 | TD_INFORMATION_ICON = MAKEINTRESOURCE(Word(-3)); 42 | TD_SHIELD_ICON = MAKEINTRESOURCE(Word(-4)); 43 | 44 | // SDK::Commctrl.h - common button flags 45 | TDCBF_OK_BUTTON = $0001; 46 | TDCBF_YES_BUTTON = $0002; 47 | TDCBF_NO_BUTTON = $0004; 48 | TDCBF_CANCEL_BUTTON = $0008; 49 | TDCBF_RETRY_BUTTON = $0010; 50 | TDCBF_CLOSE_BUTTON = $0020; 51 | 52 | type 53 | [SDKName('TASKDIALOG_FLAGS')] 54 | [FlagName(TDF_ENABLE_HYPERLINKS, 'Enable Hyperlinks')] 55 | [FlagName(TDF_USE_HICON_MAIN, 'Use HICON Main')] 56 | [FlagName(TDF_USE_HICON_FOOTER, 'Use HICON Footer')] 57 | [FlagName(TDF_ALLOW_DIALOG_CANCELLATION, 'Allow Dialog Cancellation')] 58 | [FlagName(TDF_USE_COMMAND_LINKS, 'Use Command Links')] 59 | [FlagName(TDF_USE_COMMAND_LINKS_NO_ICON, 'Use Command Links No Icon')] 60 | [FlagName(TDF_EXPAND_FOOTER_AREA, 'Expand Footer Area')] 61 | [FlagName(TDF_EXPANDED_BY_DEFAULT, 'Expanded By Default')] 62 | [FlagName(TDF_VERIFICATION_FLAG_CHECKED, 'Verification Flag Checked')] 63 | [FlagName(TDF_SHOW_PROGRESS_BAR, 'Show Progress Bar')] 64 | [FlagName(TDF_SHOW_MARQUEE_PROGRESS_BAR, 'Show Marquee Progress Bar')] 65 | [FlagName(TDF_CALLBACK_TIMER, 'Callback Timer')] 66 | [FlagName(TDF_POSITION_RELATIVE_TO_WINDOW, 'Position Relative To Window')] 67 | [FlagName(TDF_RTL_LAYOUT, 'RTL Layout')] 68 | [FlagName(TDF_NO_DEFAULT_RADIO_BUTTON, 'No Default Radio Button')] 69 | [FlagName(TDF_CAN_BE_MINIMIZED, 'Can Be Minimized')] 70 | [FlagName(TDF_NO_SET_FOREGROUND, 'No Set Foreground')] 71 | [FlagName(TDF_SIZE_TO_CONTENT, 'Size To Content')] 72 | TTaskDialogFlags = type Cardinal; 73 | 74 | [SDKName('TASKDIALOG_COMMON_BUTTON_FLAGS')] 75 | [FlagName(TDCBF_OK_BUTTON, 'OK')] 76 | [FlagName(TDCBF_YES_BUTTON, 'Yes')] 77 | [FlagName(TDCBF_NO_BUTTON, 'No')] 78 | [FlagName(TDCBF_CANCEL_BUTTON, 'Cancel')] 79 | [FlagName(TDCBF_RETRY_BUTTON, 'Retry')] 80 | [FlagName(TDCBF_CLOSE_BUTTON, 'Close')] 81 | TTaskDialogCommonButtonFlags = type Cardinal; 82 | 83 | HICON = type THwnd; 84 | 85 | // (extracted union) 86 | TTaskDialogIcon = record 87 | case Boolean of 88 | False: (hIcon: HICON); 89 | True: (pszIcon: PWideChar) 90 | end; 91 | 92 | // SDK::Commctrl.h 93 | [SDKName('TASKDIALOG_BUTTON')] 94 | TTaskDialogButton = packed record 95 | ButtonID: TMessageResponse; 96 | ButtonText: PWideChar; 97 | end; 98 | 99 | // SDK::Commctrl.h 100 | [SDKName('PFTASKDIALOGCALLBACK')] 101 | TTaskDialogCallback = function ( 102 | [in] hwnd: THwnd; 103 | [in] msg: Cardinal; 104 | [in] wParam: WPARAM; 105 | [in] lParam: LPARAM; 106 | [in, opt] lpRefData: UIntPtr 107 | ): HResult; stdcall; 108 | 109 | // SDK::Commctrl.h 110 | [SDKName('TASKDIALOGCONFIG')] 111 | TTaskDialogConfig = packed record 112 | [RecordSize] cbSize: Cardinal; 113 | Owner: THwnd; 114 | hInstance: HINST; 115 | Flags: TTaskDialogFlags; 116 | CommonButtons: TTaskDialogCommonButtonFlags; 117 | WindowTitle: PWideChar; 118 | MainIcon: TTaskDialogIcon; // Can be TD_* 119 | MainInstruction: PWideChar; 120 | Content: PWideChar; 121 | [Counter(ctElements)] cButtons: Cardinal; 122 | Buttons: ^TAnysizeArray; 123 | nDefaultButton: TMessageResponse; 124 | [Counter(ctElements)] cRadioButtons: Cardinal; 125 | RadioButtons: ^TAnysizeArray; 126 | nDefaultRadioButton: TMessageResponse; 127 | VerificationText: PWideChar; 128 | ExpandedInformation: PWideChar; 129 | ExpandedControlText: PWideChar; 130 | CollapsedControlText: PWideChar; 131 | FooterIcon: TTaskDialogIcon; 132 | Footer: PWideChar; 133 | Callback: TTaskDialogCallback; 134 | CallbackData: UIntPtr; 135 | Width: Cardinal; 136 | end; 137 | PTaskDialogConfig = ^TTaskDialogConfig; 138 | 139 | // SDK::Commctrl.h 140 | function TaskDialogIndirect( 141 | [in] const TaskConfig: TTaskDialogConfig; 142 | [out, opt] pnButton: PMessageResponse; 143 | [out, opt] pnRadioButton: PMessageResponse; 144 | [out, opt] pfVerificationFlagChecked: PLongBool 145 | ): HRESULT; stdcall; external comctl32 delayed; 146 | 147 | var delayed_TaskDialogIndirect: TDelayedLoadFunction = ( 148 | Dll: @delayed_comctl32; 149 | FunctionName: 'TaskDialogIndirect'; 150 | ); 151 | 152 | implementation 153 | 154 | {$BOOLEVAL OFF} 155 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 156 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 157 | 158 | end. 159 | -------------------------------------------------------------------------------- /Headers/Ntapi.ConsoleApi.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ConsoleApi; 2 | 3 | { 4 | This file contains declarations for using in console applications. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | {$WARN SYMBOL_PLATFORM OFF} 11 | 12 | uses 13 | Ntapi.WinNt, Ntapi.WinUser, DelphiApi.Reflection, DelphiApi.DelayLoad; 14 | 15 | const 16 | // SDK::consoleapi2.h 17 | FOREGROUND_BLUE = $0001; 18 | FOREGROUND_GREEN = $0002; 19 | FOREGROUND_RED = $0004; 20 | FOREGROUND_INTENSITY = $0008; 21 | BACKGROUND_BLUE = $0010; 22 | BACKGROUND_GREEN = $0020; 23 | BACKGROUND_RED = $0040; 24 | BACKGROUND_INTENSITY = $0080; 25 | 26 | // Special flag for AttachConsole 27 | ATTACH_PARENT_PROCESS = TProcessId32(-1); 28 | 29 | type 30 | [FlagName(FOREGROUND_BLUE, 'Foreground Blue')] 31 | [FlagName(FOREGROUND_GREEN, 'Foreground Green')] 32 | [FlagName(FOREGROUND_RED, 'Foreground Red')] 33 | [FlagName(FOREGROUND_INTENSITY, 'Foreground Intensity')] 34 | [FlagName(BACKGROUND_BLUE, 'Background Blue')] 35 | [FlagName(BACKGROUND_GREEN, 'Background Green')] 36 | [FlagName(BACKGROUND_RED, 'Background Red')] 37 | [FlagName(BACKGROUND_INTENSITY, 'Background Intensity')] 38 | TConsoleFill = type Cardinal; 39 | 40 | // SDK::WinBase.h 41 | [NamingStyle(nsSnakeCase, 'STD')] 42 | TStdHandle = ( 43 | STD_INPUT_HANDLE = -10, 44 | STD_OUTPUT_HANDLE = -11, 45 | STD_ERROR_HANDLE = -12 46 | ); 47 | 48 | // SDK::consoleapi.h 49 | [NamingStyle(nsSnakeCase, '', 'EVENT')] 50 | TCtrlEvent = ( 51 | CTRL_C_EVENT = 0, 52 | CTRL_BREAK_EVENT = 1, 53 | CTRL_CLOSE_EVENT = 2, 54 | CTRL_RESERVED3 = 3, 55 | CTRL_RESERVED4 = 4, 56 | CTRL_LOGOFF_EVENT = 5, 57 | CTRL_SHUTDOWN_EVENT = 6 58 | ); 59 | 60 | // SDK::consoleapi.h 61 | [SDKName('PHANDLER_ROUTINE')] 62 | THandlerRoutine = function ( 63 | [in] CtrlType: TCtrlEvent 64 | ): LongBool; stdcall; 65 | 66 | // SDK::wincontypes.h 67 | [SDKName('COORD')] 68 | TCoord = record 69 | X: Int16; 70 | Y: Int16; 71 | end; 72 | 73 | [SDKName('SMALL_RECT')] 74 | TSmallRect = record 75 | Left: Int16; 76 | Top: Int16; 77 | Right: Int16; 78 | Bottom: Int16; 79 | end; 80 | 81 | [SDKName('CONSOLE_SCREEN_BUFFER_INFO')] 82 | TConsoleScreenBufferInfo = record 83 | Size: TCoord; 84 | CursorPosition: TCoord; 85 | Attributes: Word; 86 | Window: TSmallRect; 87 | MaximumWindowSize: TCoord; 88 | end; 89 | 90 | // SDK::processenv.h 91 | [SetsLastError] 92 | function GetStdHandle( 93 | [in] StdHandle: TStdHandle 94 | ): THandle; stdcall; external kernel32; 95 | 96 | // SDK::consoleapi.h 97 | [SetsLastError] 98 | [Result: ReleaseWith('FreeConsole')] 99 | function AllocConsole( 100 | ): LongBool; stdcall; external kernel32; 101 | 102 | // SDK::consoleapi.h 103 | [SetsLastError] 104 | function FreeConsole( 105 | ): LongBool; stdcall; external kernel32; 106 | 107 | // SDK::consoleapi.h 108 | [SetsLastError] 109 | function AttachConsole( 110 | [in] ProcessId: TProcessId32 111 | ): LongBool; stdcall; external kernel32; 112 | 113 | // SDK::consoleapi.h 114 | [SetsLastError] 115 | function SetConsoleCtrlHandler( 116 | [in] HandlerRoutine: THandlerRoutine; 117 | [in] Add: LongBool 118 | ): LongBool; stdcall; external kernel32; 119 | 120 | // SDK::consoleapi2.h 121 | [SetsLastError] 122 | function GetConsoleScreenBufferInfo( 123 | [in] hConsoleOutput: THandle; 124 | [out] out ConsoleScreenBufferInfo: TConsoleScreenBufferInfo 125 | ): LongBool; stdcall; external kernel32; 126 | 127 | // SDK::consoleapi2.h 128 | [SetsLastError] 129 | function SetConsoleTextAttribute( 130 | [in] hConsoleOutput: THandle; 131 | [in] Attributes: Word 132 | ): LongBool; stdcall; external kernel32; 133 | 134 | // SDK::consoleapi3.h 135 | [SetsLastError] 136 | function GetConsoleWindow( 137 | ): THwnd; stdcall; external kernel32; 138 | 139 | // SDK::consoleapi3.h 140 | [SetsLastError] 141 | [Result: NumberOfElements] 142 | function GetConsoleProcessList( 143 | [out] ProcessList: PProcessId32; 144 | [in, NumberOfElements] ProcessCount: Cardinal 145 | ): Cardinal; stdcall; external kernel32; 146 | 147 | // rev 148 | function BaseGetConsoleReference( 149 | ): THandle; stdcall; external kernelbase delayed; 150 | 151 | var delayed_BaseGetConsoleReference: TDelayedLoadFunction = ( 152 | Dll: @delayed_kernelbase; 153 | FunctionName: 'BaseGetConsoleReference'; 154 | ); 155 | 156 | implementation 157 | 158 | {$BOOLEVAL OFF} 159 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 160 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 161 | 162 | end. 163 | -------------------------------------------------------------------------------- /Headers/Ntapi.ShellApi.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ShellApi; 2 | 3 | { 4 | This module includes some definitions for Shell API functions. 5 | } 6 | 7 | interface 8 | 9 | {$WARN SYMBOL_PLATFORM OFF} 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.ProcessThreadsApi, Ntapi.WinUser, DelphiApi.Reflection, 14 | DelphiApi.DelayLoad; 15 | 16 | const 17 | shell32 = 'shell32.dll'; 18 | wdc = 'wdc.dll'; 19 | 20 | var 21 | delayed_shell32: TDelayedLoadDll = (DllName: shell32); 22 | delayed_wdc: TDelayedLoadDll = (DllName: wdc); 23 | 24 | const 25 | // SDK::shellapi.h 26 | SEE_MASK_DEFAULT = $00000000; 27 | SEE_MASK_NOCLOSEPROCESS = $00000040; 28 | SEE_MASK_NOASYNC = $00000100; 29 | SEE_MASK_FLAG_NO_UI = $00000400; 30 | SEE_MASK_UNICODE = $000004000; 31 | SEE_MASK_NO_CONSOLE = $00008000; 32 | SEE_MASK_NOZONECHECKS = $00800000; 33 | SEE_MASK_FLAG_HINST_IS_SITE = $08000000; 34 | 35 | // ReactOs::undocshell.h 36 | SECL_NO_UI = $02; 37 | SECL_LOG_USAGE = $08; 38 | SECL_USE_IDLIST = $10; 39 | SECL_ALLOW_NONEXE = $20; 40 | SECL_RUNAS = $40; 41 | 42 | // SDK::ShObjIdl_core.h - service ID for ICreatingProcess 43 | SID_ExecuteCreatingProcess: TGuid = '{C2B937A9-3110-4398-8A56-F34C6342D244}'; 44 | 45 | type 46 | [FlagName(SEE_MASK_NOCLOSEPROCESS, 'Don''t Close Process')] 47 | [FlagName(SEE_MASK_NOASYNC, 'No Async')] 48 | [FlagName(SEE_MASK_FLAG_NO_UI, 'No UI')] 49 | [FlagName(SEE_MASK_UNICODE, 'Unicode')] 50 | [FlagName(SEE_MASK_NO_CONSOLE, 'No Console')] 51 | [FlagName(SEE_MASK_NOZONECHECKS, 'No Zone Checks')] 52 | [FlagName(SEE_MASK_FLAG_HINST_IS_SITE, 'HInst Is Site')] 53 | TShellExecuteMask = type Cardinal; 54 | 55 | // SDK::shellapi.h 56 | [SDKName('SHELLEXECUTEINFOW')] 57 | TShellExecuteInfoW = record 58 | [in, RecordSize] cbSize: Cardinal; 59 | [in] Mask: TShellExecuteMask; 60 | [in, opt] Wnd: THwnd; 61 | [in, opt] Verb: PWideChar; 62 | [in] FileName: PWideChar; 63 | [in, opt] Parameters: PWideChar; 64 | [in, opt] Directory: PWideChar; 65 | [in] Show: TShowMode32; 66 | [in, out, opt] hInstApp: HINST; // can also be IServiceProvider 67 | [in, opt] IDList: Pointer; 68 | [in, opt] &Class: PWideChar; 69 | [in, opt] hKeyClass: THandle; 70 | [in, opt] HotKey: Cardinal; 71 | [in, opt] hMonitor: THandle; 72 | [out, ReleaseWith('NtClose')] hProcess: THandle; 73 | end; 74 | 75 | [FlagName(SECL_NO_UI, 'No UI')] 76 | [FlagName(SECL_LOG_USAGE, 'Log Usage')] 77 | [FlagName(SECL_USE_IDLIST, 'Use IDList')] 78 | [FlagName(SECL_ALLOW_NONEXE, 'Allow Non-Exe')] 79 | [FlagName(SECL_RUNAS, 'Run As')] 80 | TSeclFlags = type Cardinal; 81 | 82 | // SDK::ShObjIdl_core.h 83 | ICreateProcessInputs = interface(IUnknown) 84 | ['{F6EF6140-E26F-4D82-BAC4-E9BA5FD239A8}'] 85 | function GetCreateFlags( 86 | [out] out CreationFlags: TProcessCreateFlags 87 | ): HResult; stdcall; 88 | 89 | function SetCreateFlags( 90 | [in] CreationFlags: TProcessCreateFlags 91 | ): HResult; stdcall; 92 | 93 | function AddCreateFlags( 94 | [in] CreationFlags: TProcessCreateFlags 95 | ): HResult; stdcall; 96 | 97 | function SetHotKey( 98 | [in] wHotKey: Word 99 | ): HResult; stdcall; 100 | 101 | function AddStartupFlags( 102 | [in] StartupInfoFlags: TStartupFlags 103 | ): HResult; stdcall; 104 | 105 | function SetTitle( 106 | [in, opt] Title: PWideChar 107 | ): HResult; stdcall; 108 | 109 | function SetEnvironmentVariable( 110 | [in] Name: PWideChar; 111 | [in] Value: PWideChar 112 | ): HResult; stdcall; 113 | end; 114 | 115 | // SDK::ShObjIdl_core.h 116 | ICreatingProcess = interface(IUnknown) 117 | ['{C2B937A9-3110-4398-8A56-F34C6342D244}'] 118 | function OnCreating( 119 | [in] const cpi: ICreateProcessInputs 120 | ): HResult; stdcall; 121 | end; 122 | 123 | // SDK::shellapi.h 124 | [SetsLastError] 125 | [Result: NumberOfElements] 126 | function ExtractIconExW( 127 | [in] FileName: PWideChar; 128 | [in] IconIndex: Integer; 129 | [out, opt] phIconLarge: PHIcon; 130 | [out, opt] phIconSmall: PHIcon; 131 | [in, NumberOfElements] Icons: Cardinal 132 | ): Cardinal; stdcall; external shell32; 133 | 134 | // SDK::shellapi.h 135 | [SetsLastError] 136 | function ShellExecuteExW( 137 | [in, out, ReleaseWith('NtClose')] var ExecInfo: TShellExecuteInfoW 138 | ): LongBool; stdcall; external shell32; 139 | 140 | // ReactOs::undocshell.h 141 | function ShellExecCmdLine( 142 | [in] hwnd: THwnd; 143 | [in] CommandLine: PWideChar; 144 | [in, opt] StartDir: PWideChar; 145 | [in] Show: TShowMode32; 146 | [Reserved] Unused: Pointer; 147 | [in] SeclFlags: TSeclFlags 148 | ): HRESULT; stdcall; external shell32 index 265; 149 | 150 | // SDK::shellapi.h 151 | function SHEvaluateSystemCommandTemplate( 152 | [in] CmdTemplate: PWideChar; 153 | [out, ReleaseWith('CoTaskMemFree')] out Application: PWideChar; 154 | [out, opt, ReleaseWith('CoTaskMemFree')] out CommandLine: PWideChar; 155 | [out, opt, ReleaseWith('CoTaskMemFree')] out Parameters: PWideChar 156 | ): HResult; stdcall external shell32; 157 | 158 | { WDC } 159 | 160 | // rev 161 | function WdcRunTaskAsInteractiveUser( 162 | [in] CommandLine: PWideChar; 163 | [in, opt] CurrentDirectory: PWideChar; 164 | [in] SeclFlags: TSeclFlags 165 | ): HResult; stdcall; external wdc delayed; 166 | 167 | var delayed_WdcRunTaskAsInteractiveUser: TDelayedLoadFunction = ( 168 | Dll: @delayed_wdc; 169 | FunctionName: 'WdcRunTaskAsInteractiveUser'; 170 | ); 171 | 172 | implementation 173 | 174 | {$BOOLEVAL OFF} 175 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 176 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 177 | 178 | end. 179 | -------------------------------------------------------------------------------- /Headers/Ntapi.Shlwapi.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.Shlwapi; 2 | 3 | { 4 | This module includes definitions for some Lightweight Shell API functions. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | uses 12 | Ntapi.WinUser, Ntapi.ntioapi, Ntapi.ObjIdl, DelphiApi.Reflection, 13 | DelphiApi.DelayLoad; 14 | 15 | const 16 | shlwapi = 'shlwapi.dll'; 17 | 18 | var 19 | delayed_shlwapi: TDelayedLoadDll = (DllName: shlwapi); 20 | 21 | const 22 | // SDK::ShlDisp.h - flags for IAutoComplete2 23 | ACO_AUTOSUGGEST = $0001; 24 | ACO_AUTOAPPEND = $0002; 25 | ACO_SEARCH = $0004; 26 | ACO_FILTERPREFIXES = $0008; 27 | ACO_USETAB = $0010; 28 | ACO_UPDOWNKEYDROPSLIST = $0020; 29 | ACO_RTLREADING = $0040; 30 | ACO_WORD_FILTER = $0080; 31 | ACO_NOPREFIXFILTERING = $0100; 32 | 33 | // SDK::ShlGuid.h 34 | CLSID_AutoComplete: TGUID = '{00BB2763-6A77-11D0-A535-00C04FD7D062}'; 35 | 36 | // SDK::Shlwapi.h - shell auto-complete flags 37 | SHACF_FILESYSTEM = $00000001; 38 | SHACF_URLHISTORY = $00000002; 39 | SHACF_URLMRU = $00000004; 40 | SHACF_USETAB = $00000008; 41 | SHACF_FILESYS_ONLY = $00000010; 42 | SHACF_FILESYS_DIRS = $00000020; 43 | SHACF_VIRTUAL_NAMESPACE = $00000040; 44 | SHACF_AUTOSUGGEST_FORCE_ON = $10000000; 45 | SHACF_AUTOSUGGEST_FORCE_OFF = $20000000; 46 | SHACF_AUTOAPPEND_FORCE_ON = $40000000; 47 | SHACF_AUTOAPPEND_FORCE_OFF = $80000000; 48 | 49 | type 50 | [FlagName(ACO_AUTOSUGGEST, 'Auto-suggest')] 51 | [FlagName(ACO_AUTOAPPEND, 'Auto-append')] 52 | [FlagName(ACO_SEARCH, 'Search')] 53 | [FlagName(ACO_FILTERPREFIXES, 'Filter Prefixes')] 54 | [FlagName(ACO_USETAB, 'Use Tab')] 55 | [FlagName(ACO_UPDOWNKEYDROPSLIST, 'Up/Down Key Drops List')] 56 | [FlagName(ACO_RTLREADING, 'Right-To-Left')] 57 | [FlagName(ACO_WORD_FILTER, 'Word Filter')] 58 | [FlagName(ACO_NOPREFIXFILTERING, 'No Prefix Filtering')] 59 | TAutoCompleteFlags = type Cardinal; 60 | 61 | [FlagName(SHACF_FILESYSTEM, 'Filesystem')] 62 | [FlagName(SHACF_URLHISTORY, 'URL History')] 63 | [FlagName(SHACF_URLMRU, 'URLS in Recently Used')] 64 | [FlagName(SHACF_USETAB, 'Use Tab')] 65 | [FlagName(SHACF_FILESYS_ONLY, 'Filesystem Only')] 66 | [FlagName(SHACF_FILESYS_DIRS, 'Filesystem Directories')] 67 | [FlagName(SHACF_VIRTUAL_NAMESPACE, 'Virtual Namespace')] 68 | [FlagName(SHACF_AUTOSUGGEST_FORCE_ON, 'Auto-suggest Force On')] 69 | [FlagName(SHACF_AUTOSUGGEST_FORCE_OFF, 'Auto-suggest Force Off')] 70 | [FlagName(SHACF_AUTOAPPEND_FORCE_ON, 'Auto-append Force On')] 71 | [FlagName(SHACF_AUTOAPPEND_FORCE_OFF, 'Auto-append Force Off')] 72 | TShAutoCompleteFlags = type Cardinal; 73 | 74 | // SDK::ShlDisp.h 75 | [SDKName('IAutoComplete')] 76 | IAutoComplete = interface(IUnknown) 77 | ['{00BB2762-6A77-11D0-A535-00C04FD7D062}'] 78 | 79 | function Init( 80 | [in] hwndEdit: THwnd; 81 | [in] const punkACL: IUnknown; 82 | [in, opt] pwszRegKeyPath: PWideChar; 83 | [in, opt] pwszQuickComplete: PWideChar 84 | ): HResult; stdcall; 85 | 86 | function Enable( 87 | [in] fEnable: LongBool 88 | ): HResult; stdcall; 89 | end; 90 | 91 | // SDK::ShlDisp.h 92 | [SDKName('IAutoComplete2')] 93 | IAutoComplete2 = interface(IAutoComplete) 94 | ['{EAC04BC0-3791-11D2-BB95-0060977B464C}'] 95 | 96 | function SetOptions( 97 | [in] Flags: TAutoCompleteFlags 98 | ): HResult; stdcall; 99 | 100 | function GetOptions( 101 | [out] out Flag: TAutoCompleteFlags 102 | ): HResult; stdcall; 103 | end; 104 | 105 | // SDK::ShlObj_core.h 106 | [SDKName('IACList')] 107 | IACList = interface(IUnknown) 108 | ['{77A130B0-94FD-11D0-A544-00C04FD7D062}'] 109 | 110 | function Expand( 111 | [in] Root: PWideChar 112 | ): HResult; stdcall; 113 | end; 114 | 115 | // SDK::Shlwapi.h 116 | function SHAutoComplete( 117 | [in] hwndEdit: THwnd; 118 | [in] Flags: TShAutoCompleteFlags 119 | ): HResult; stdcall; external shlwapi; 120 | 121 | // SDK::Shlwapi.h 122 | [Result: MayReturnNil] 123 | function SHCreateMemStream( 124 | [in, opt, ReadsFrom] pInit: Pointer; 125 | [in, NumberOfBytes] cbInit: Cardinal 126 | ): IStream; stdcall; external shlwapi; 127 | 128 | // SDK::Shlwapi.h 129 | function SHCreateStreamOnFileEx( 130 | [in] pszFile: PWideChar; 131 | [in] grfMode: TStgm; 132 | [in] Attributes: TFileAttributes; 133 | [in] fCreate: LongBool; 134 | [Reserved] const stmTemplate: IStream; 135 | [out] out stm: IStream 136 | ): HResult; stdcall; external shlwapi; 137 | 138 | implementation 139 | 140 | {$BOOLEVAL OFF} 141 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 142 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 143 | 144 | end. 145 | -------------------------------------------------------------------------------- /Headers/Ntapi.UserEnv.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.UserEnv; 2 | 3 | { 4 | This file defines functions for working with user and AppContainer profiles. 5 | } 6 | 7 | interface 8 | 9 | {$WARN SYMBOL_PLATFORM OFF} 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.WinNt, Ntapi.ntseapi, Ntapi.ntregapi, Ntapi.Versions, 14 | DelphiApi.Reflection, DelphiApi.DelayLoad; 15 | 16 | const 17 | userenv = 'userenv.dll'; 18 | 19 | var 20 | delayed_userenv: TDelayedLoadDll = (DllName: userenv); 21 | 22 | const 23 | // SDK::UserEnv.h - profile flags 24 | PT_TEMPORARY = $00000001; 25 | PT_ROAMING = $00000002; 26 | PT_MANDATORY = $00000004; 27 | PT_ROAMING_PREEXISTING = $00000008; 28 | 29 | // For annotations 30 | TOKEN_LOAD_PROFILE = TOKEN_QUERY or TOKEN_IMPERSONATE or TOKEN_DUPLICATE; 31 | 32 | type 33 | [FlagName(PT_TEMPORARY, 'Temporary')] 34 | [FlagName(PT_ROAMING, 'Roaming')] 35 | [FlagName(PT_MANDATORY, 'Mandatory')] 36 | [FlagName(PT_ROAMING_PREEXISTING, 'Roaming Pre-existing')] 37 | TProfileType = type Cardinal; 38 | 39 | // SDK::ProfInfo.h 40 | [SDKName('PROFILEINFOW')] 41 | TProfileInfoW = record 42 | [RecordSize] Size: Cardinal; 43 | Flags: TProfileType; 44 | UserName: PWideChar; 45 | ProfilePath: PWideChar; 46 | DefaultPath: PWideChar; 47 | ServerName: PWideChar; 48 | PolicyPath: PWideChar; 49 | hProfile: THandle; 50 | end; 51 | PProfileInfoW = ^TProfileInfoW; 52 | 53 | // SDK::UserEnv.h 54 | [SetsLastError] 55 | [Result: ReleaseWith('UnloadUserProfile')] 56 | [RequiredPrivilege(SE_BACKUP_PRIVILEGE, rpAlways)] 57 | [RequiredPrivilege(SE_RESTORE_PRIVILEGE, rpAlways)] 58 | function LoadUserProfileW( 59 | [in, Access(TOKEN_LOAD_PROFILE)] hToken: THandle; 60 | [in, out] var ProfileInfo: TProfileInfoW 61 | ): LongBool; stdcall; external userenv delayed; 62 | 63 | var delayed_LoadUserProfileW: TDelayedLoadFunction = ( 64 | Dll: @delayed_userenv; 65 | FunctionName: 'LoadUserProfileW'; 66 | ); 67 | 68 | // SDK::UserEnv.h 69 | [SetsLastError] 70 | [RequiredPrivilege(SE_BACKUP_PRIVILEGE, rpAlways)] 71 | [RequiredPrivilege(SE_RESTORE_PRIVILEGE, rpAlways)] 72 | function UnloadUserProfile( 73 | [in, Access(TOKEN_LOAD_PROFILE)] hToken: THandle; 74 | [in] hProfile: THandle 75 | ): LongBool; stdcall; external userenv delayed; 76 | 77 | var delayed_UnloadUserProfile: TDelayedLoadFunction = ( 78 | Dll: @delayed_userenv; 79 | FunctionName: 'UnloadUserProfile'; 80 | ); 81 | 82 | // SDK::UserEnv.h 83 | [SetsLastError] 84 | function GetProfilesDirectoryW( 85 | [out, WritesTo] ProfileDir: PWideChar; 86 | [in, out, NumberOfElements] var Size: Cardinal 87 | ): LongBool; stdcall; external userenv delayed; 88 | 89 | var delayed_GetProfilesDirectoryW: TDelayedLoadFunction = ( 90 | Dll: @delayed_userenv; 91 | FunctionName: 'GetProfilesDirectoryW'; 92 | ); 93 | 94 | // SDK::UserEnv.h 95 | [SetsLastError] 96 | function GetProfileType( 97 | [out] out Flags: TProfileType 98 | ): LongBool; stdcall; external userenv delayed; 99 | 100 | var delayed_GetProfileType: TDelayedLoadFunction = ( 101 | Dll: @delayed_userenv; 102 | FunctionName: 'GetProfileType'; 103 | ); 104 | 105 | // SDK::UserEnv.h 106 | [SetsLastError] 107 | function CreateEnvironmentBlock( 108 | [out, ReleaseWith('RtlDestroyEnvironment')] out Environment: PEnvironment; 109 | [in, opt] hToken: THandle; 110 | [in] bInherit: LongBool 111 | ): LongBool; stdcall; external userenv delayed; 112 | 113 | var delayed_CreateEnvironmentBlock: TDelayedLoadFunction = ( 114 | Dll: @delayed_userenv; 115 | FunctionName: 'CreateEnvironmentBlock'; 116 | ); 117 | 118 | // SDK::UserEnv.h 119 | [MinOSVersion(OsWin8)] 120 | [Result: ReleaseWith('DeleteAppContainerProfile')] 121 | function CreateAppContainerProfile( 122 | [in] AppContainerName: PWideChar; 123 | [in] DisplayName: PWideChar; 124 | [in] Description: PWideChar; 125 | [in, opt, ReadsFrom] const Capabilities: TArray; 126 | [in, opt, NumberOfElements] CapabilityCount: Integer; 127 | [out, ReleaseWith('RtlFreeSid')] out SidAppContainerSid: PSid 128 | ): HResult; stdcall; external userenv delayed; 129 | 130 | var delayed_CreateAppContainerProfile: TDelayedLoadFunction = ( 131 | Dll: @delayed_userenv; 132 | FunctionName: 'CreateAppContainerProfile'; 133 | ); 134 | 135 | // SDK::UserEnv.h 136 | [MinOSVersion(OsWin8)] 137 | function DeleteAppContainerProfile( 138 | [in] AppContainerName: PWideChar 139 | ): HResult; stdcall; external userenv delayed; 140 | 141 | var delayed_DeleteAppContainerProfile: TDelayedLoadFunction = ( 142 | Dll: @delayed_userenv; 143 | FunctionName: 'DeleteAppContainerProfile'; 144 | ); 145 | 146 | // SDK::UserEnv.h 147 | [MinOSVersion(OsWin8)] 148 | function GetAppContainerRegistryLocation( 149 | [in] DesiredAccess: TRegKeyAccessMask; 150 | [out, ReleaseWith('NtClose')] out hAppContainerKey: THandle 151 | ): HResult; stdcall; external userenv delayed; 152 | 153 | var delayed_GetAppContainerRegistryLocation: TDelayedLoadFunction = ( 154 | Dll: @delayed_userenv; 155 | FunctionName: 'GetAppContainerRegistryLocation'; 156 | ); 157 | 158 | // SDK::UserEnv.h 159 | [MinOSVersion(OsWin8)] 160 | function GetAppContainerFolderPath( 161 | [in] AppContainerSid: PWideChar; 162 | [out, ReleaseWith('CoTaskMemFree')] out Path: PWideChar 163 | ): HResult; stdcall; external userenv delayed; 164 | 165 | var delayed_GetAppContainerFolderPath: TDelayedLoadFunction = ( 166 | Dll: @delayed_userenv; 167 | FunctionName: 'GetAppContainerFolderPath'; 168 | ); 169 | 170 | // MSDN 171 | [MinOSVersion(OsWin8)] 172 | function AppContainerDeriveSidFromMoniker( 173 | [in] Moniker: PWideChar; 174 | [out, ReleaseWith('RtlFreeSid')] out AppContainerSid: PSid 175 | ): HResult; stdcall; external kernelbase delayed; 176 | 177 | var delayed_AppContainerDeriveSidFromMoniker: TDelayedLoadFunction = ( 178 | Dll: @delayed_kernelbase; 179 | FunctionName: 'AppContainerDeriveSidFromMoniker'; 180 | ); 181 | 182 | // rev 183 | [MinOSVersion(OsWin8)] 184 | function AppContainerFreeMemory( 185 | [in] Memory: Pointer 186 | ): Boolean; stdcall; external kernelbase delayed; 187 | 188 | var delayed_AppContainerFreeMemory: TDelayedLoadFunction = ( 189 | Dll: @delayed_kernelbase; 190 | FunctionName: 'AppContainerFreeMemory'; 191 | ); 192 | 193 | // rev 194 | [MinOSVersion(OsWin8)] 195 | function AppContainerLookupMoniker( 196 | [in] Sid: PSid; 197 | [out, ReleaseWith('AppContainerFreeMemory')] out Moniker: PWideChar 198 | ): HResult; stdcall; external kernelbase delayed; 199 | 200 | var delayed_AppContainerLookupMoniker: TDelayedLoadFunction = ( 201 | Dll: @delayed_kernelbase; 202 | FunctionName: 'AppContainerLookupMoniker'; 203 | ); 204 | 205 | // SDK::UserEnv.h 206 | [MinOSVersion(OsWin81)] 207 | function DeriveRestrictedAppContainerSidFromAppContainerSidAndRestrictedName( 208 | [in] AppContainerSid: PSid; 209 | [in] RestrictedAppContainerName: PWideChar; 210 | [out, ReleaseWith('RtlFreeSid')] out RestrictedAppContainerSid: PSid 211 | ): HResult; stdcall; external userenv delayed; 212 | 213 | var delayed_DeriveRestrictedAppContainerSidFromAppContainerSidAndRestrictedName: TDelayedLoadFunction = ( 214 | Dll: @delayed_userenv; 215 | FunctionName: 'DeriveRestrictedAppContainerSidFromAppContainerSidAndRestrictedName'; 216 | ); 217 | 218 | implementation 219 | 220 | {$BOOLEVAL OFF} 221 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 222 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 223 | 224 | end. 225 | -------------------------------------------------------------------------------- /Headers/Ntapi.Versions.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.Versions; 2 | 3 | { 4 | This module allows checking Windows version in runtime and provides a custom 5 | attribute for annotating entities that require a minimal specific version. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | DelphiApi.Reflection; 12 | 13 | {$MINENUMSIZE 4} 14 | 15 | type 16 | [NamingStyle(nsCamelCase, 'Os')] 17 | TWindowsVersion = ( 18 | OsWinOld, 19 | OsWinXP, 20 | OsWinVista, 21 | OsWin7, 22 | OsWin8, 23 | OsWin81, 24 | OsWin10, 25 | OsWin10TH1, 26 | OsWin10TH2, 27 | OsWin10RS1, 28 | OsWin10RS2, 29 | OsWin10RS3, 30 | OsWin10RS4, 31 | OsWin10RS5, 32 | OsWin1019H1, 33 | OsWin1019H2, 34 | OsWin1020H1, 35 | OsWin1020H2, 36 | OsWin1021H1, 37 | OsWin1021H2, 38 | OsWin1022H2, 39 | OsWin11, 40 | OsWin1122H2, 41 | OsWin1123H2, 42 | OsWin1124H2 43 | ); 44 | 45 | TOsBuild = record 46 | OSMajorVersion: Cardinal; 47 | OSMinorVersion: Cardinal; 48 | OSBuildNumber: Word; 49 | end; 50 | 51 | // Marks a type or a field that requires a particular version of Windows 52 | MinOSVersionAttribute = class(TCustomAttribute) 53 | Version: TWindowsVersion; 54 | constructor Create(OsVersion: TWindowsVersion); 55 | end; 56 | 57 | const 58 | KnownOsBuilds: array [TWindowsVersion] of TOsBuild = ( 59 | (OSMajorVersion: 0; OSMinorVersion: 0; OSBuildNumber: 0), // Older 60 | (OSMajorVersion: 5; OSMinorVersion: 1; OSBuildNumber: 0), // XP 61 | (OSMajorVersion: 6; OSMinorVersion: 0; OSBuildNumber: 0), // Vista 62 | (OSMajorVersion: 6; OSMinorVersion: 1; OSBuildNumber: 0), // 7 63 | (OSMajorVersion: 6; OSMinorVersion: 2; OSBuildNumber: 0), // 8 64 | (OSMajorVersion: 6; OSMinorVersion: 3; OSBuildNumber: 0), // 8.1 65 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 0), // 10 66 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 10240), // 10 TH1 67 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 10586), // 10 TH2 68 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 14393), // 10 RS1 69 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 15063), // 10 RS2 70 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 16299), // 10 RS3 71 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 17134), // 10 RS4 72 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 17763), // 10 RS5 73 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 18362), // 10 19H1 74 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 18363), // 10 19H2 75 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 19041), // 10 20H1 76 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 19042), // 10 20H2 77 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 19043), // 10 21H1 78 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 19044), // 10 21H2 79 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 19045), // 10 22H2 80 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 22000), // 11 21H2 81 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 22621), // 11 22H2 82 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 22631), // 11 23H2 83 | (OSMajorVersion: 10; OSMinorVersion: 0; OSBuildNumber: 26100) // 11 24H2 84 | ); 85 | 86 | // Make sure that Windows version matches the minimum requirement 87 | function RtlOsVersionAtLeast(Version: TWindowsVersion): Boolean; 88 | 89 | // Get the version of Windows 90 | function RtlOsVersion: TWindowsVersion; 91 | 92 | implementation 93 | 94 | uses 95 | Ntapi.Ntpebteb; 96 | 97 | {$BOOLEVAL OFF} 98 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 99 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 100 | 101 | function RtlOsVersionAtLeast; 102 | var 103 | Peb: PPeb; 104 | begin 105 | Peb := RtlGetCurrentPeb; 106 | 107 | with KnownOsBuilds[Version] do 108 | Result := (Peb.OSMajorVersion > OSMajorVersion) or ( 109 | (Peb.OSMajorVersion = OSMajorVersion) and ( 110 | (Peb.OSMinorVersion > OSMinorVersion) or 111 | ((Peb.OSMinorVersion = OSMinorVersion) and 112 | (Peb.OSBuildNumber >= OSBuildNumber)))); 113 | end; 114 | 115 | function RtlOsVersion; 116 | var 117 | Peb: PPeb; 118 | begin 119 | Peb := RtlGetCurrentPeb; 120 | 121 | // More than Windows 10 122 | if Peb.OSMajorVersion > KnownOsBuilds[OsWin10].OSMajorVersion then 123 | Exit(High(TWindowsVersion)); 124 | 125 | // Less than Windows 7 126 | if Peb.OSMajorVersion < KnownOsBuilds[OsWin7].OSMajorVersion then 127 | Exit(OsWinOld); 128 | 129 | // Before Windows 10 130 | if Peb.OSMajorVersion < KnownOsBuilds[OsWin10].OSMajorVersion then 131 | begin 132 | if Peb.OSMajorVersion > KnownOsBuilds[OsWin81].OSMajorVersion then 133 | Exit(OsWin81); 134 | 135 | for Result := OsWin81 downto OsWin7 do 136 | if Peb.OSMinorVersion >= KnownOsBuilds[Result].OSMinorVersion then 137 | Exit; 138 | 139 | Exit(OsWinOld); 140 | end; 141 | 142 | // Windows 10, but too new minor 143 | if Peb.OSMinorVersion > KnownOsBuilds[OsWin10].OSMinorVersion then 144 | Exit(High(TWindowsVersion)); 145 | 146 | // One of the known Windows 10 147 | for Result := High(TWindowsVersion) downto OsWin10TH1 do 148 | if Peb.OSBuildNumber >= KnownOsBuilds[Result].OSBuildNumber then 149 | Exit; 150 | 151 | // Too old Windows 10 152 | Result := OsWin10; 153 | end; 154 | 155 | { MinOSVersionAttribute } 156 | 157 | constructor MinOSVersionAttribute.Create; 158 | begin 159 | Version := OsVersion; 160 | end; 161 | 162 | end. 163 | -------------------------------------------------------------------------------- /Headers/Ntapi.WinError.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.WinError; 2 | 3 | { 4 | This file defines commonly encountered Win32 error codes and HRESULT values. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | uses 12 | Ntapi.WinNt, Ntapi.ntstatus; 13 | 14 | const 15 | HRESULT_SEVERITY_MASK = $80000000; 16 | FACILITY_NT_BIT = $10000000; 17 | FACILITY_MASK = $07FF0000; 18 | WIN32_CODE_MASK = $0000FFFF; 19 | 20 | FACILITY_SHIFT = 16; 21 | FACILITY_WIN32 = 7; 22 | FACILITY_WIN32_BITS = FACILITY_WIN32 shl FACILITY_SHIFT; 23 | WIN32_HRESULT_BITS = HRESULT_SEVERITY_MASK or FACILITY_WIN32_BITS; 24 | 25 | FACILITY_WIM = $142; 26 | 27 | ERROR_SUCCESS = 0; 28 | ERROR_PATH_NOT_FOUND = 3; 29 | ERROR_ACCESS_DENIED = 5; 30 | ERROR_NOT_ENOUGH_MEMORY = 8; 31 | ERROR_BAD_LENGTH = 24; 32 | ERROR_INVALID_PARAMETER = 87; 33 | ERROR_CALL_NOT_IMPLEMENTED = 120; 34 | ERROR_INSUFFICIENT_BUFFER = 122; 35 | ERROR_ALREADY_EXISTS = 183; 36 | ERROR_BAD_EXE_FORMAT = 193; 37 | ERROR_MORE_DATA = 234; 38 | ERROR_NO_MORE_ITEMS = 259; 39 | ERROR_MR_MID_NOT_FOUND = 317; 40 | ERROR_CANT_ENABLE_DENY_ONLY = 629; 41 | ERROR_NO_TOKEN = 1008; 42 | ERROR_CANCELLED = 1223; 43 | ERROR_IMPLEMENTATION_LIMIT = 1292; 44 | ERROR_NOT_ALL_ASSIGNED = 1300; 45 | ERROR_INVALID_OWNER = 1307; 46 | ERROR_INVALID_PRIMARY_GROUP = 1308; 47 | ERROR_CANT_DISABLE_MANDATORY = 1310; 48 | ERROR_PRIVILEGE_NOT_HELD = 1314; 49 | ERROR_BAD_IMPERSONATION_LEVEL = 1346; 50 | ERROR_TIMEOUT = 1460; 51 | APPMODEL_ERROR_PACKAGE_IDENTITY_CORRUPT = 15702; 52 | APPMODEL_ERROR_NO_APPLICATION = 15703; 53 | 54 | WAIT_TIMEOUT = STATUS_TIMEOUT; 55 | WAIT_IO_COMPLETION = STATUS_USER_APC; 56 | STILL_ACTIVE = STATUS_PENDING; 57 | WAIT_FAILED = $FFFFFFFF; 58 | 59 | S_OK = $00000000; 60 | S_FALSE = $00000001; 61 | S_FALSE_AS_ERROR = HResult($C0000001); 62 | E_NOTIMPL = HResult($80004001); 63 | E_NOINTERFACE = HResult($80004002); 64 | E_INVALIDARG = HResult($80070057); 65 | E_UNEXPECTED = HResult($8000FFFF); 66 | 67 | RPC_E_CHANGED_MODE = HResult($80010106); 68 | DISP_E_EXCEPTION = HResult($80020009); 69 | 70 | implementation 71 | 72 | {$BOOLEVAL OFF} 73 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 74 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 75 | 76 | end. 77 | -------------------------------------------------------------------------------- /Headers/Ntapi.WinNt.DebugRegisters.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.WinNt.DebugRegisters; 2 | 3 | { 4 | The module extends TContext with properties that simplify usage of debug 5 | registers (hardware breakpoints). 6 | } 7 | 8 | interface 9 | 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.WinNt; 14 | 15 | type 16 | // There are four hardware breakpoints: Dr0, Dr1, Dr2, Dr3 17 | THwBpIndex = 0..3; 18 | 19 | THwBreakOn = ( 20 | BreakOnExecution = 0, // X 21 | BreakOnWrite = 1, // W 22 | BreakOnIO = 2, // RWX (sometimes not supported) 23 | BreakOnReadWrite = 3 // RW 24 | ); 25 | 26 | THwBreakpointWidth = ( 27 | BreakpointWidthByte = 0, // 1 byte (must be used for execute breakpoints) 28 | BreakpointWidthWord = 1, // 2 bytes 29 | BreakpointWidthDWord = 3, // 4 bytes 30 | BreakpointWidthQWord = 2 // 8 bytes (sometimes not supported on 32-bit) 31 | ); 32 | 33 | TDebugHelper = record helper for TContext 34 | private 35 | function EnabledMask(i: THwBpIndex): NativeUInt; inline; 36 | function TypeMask(i: THwBpIndex): NativeUInt; inline; 37 | function TypeShift(i: THwBpIndex): Cardinal; inline; 38 | function WidthMask(i: THwBpIndex): NativeUInt; inline; 39 | function WidthShift(i: THwBpIndex): Cardinal; inline; 40 | private 41 | function GetEnabled(i: THwBpIndex): Boolean; inline; 42 | procedure SetEnabled(i: THwBpIndex; Enable: Boolean); inline; 43 | function GetType(i: THwBpIndex): THwBreakOn; inline; 44 | procedure SetType(i: THwBpIndex; const Value: THwBreakOn); inline; 45 | function GetWidth(i: THwBpIndex): THwBreakpointWidth; inline; 46 | procedure SetWidth(i: THwBpIndex; const Value: THwBreakpointWidth); inline; 47 | function GetDetected(i: THwBpIndex): Boolean; inline; 48 | function GetAddress(i: THwBpIndex): Pointer; inline; 49 | procedure SetAddress(i: THwBpIndex; const Value: Pointer); inline; 50 | public 51 | // Addresses 52 | property Dr[i: THwBpIndex]: Pointer read GetAddress write SetAddress; 53 | 54 | // Control 55 | property BreakpointEnabled[i: THwBpIndex]: Boolean read GetEnabled write SetEnabled; 56 | property BreakpointType[i: THwBpIndex]: THwBreakOn read GetType write SetType; 57 | property BreakpointWidth[i: THwBpIndex]: THwBreakpointWidth read GetWidth write SetWidth; 58 | 59 | // Status 60 | property BreakpointDetected[i: THwBpIndex]: Boolean read GetDetected; 61 | end; 62 | 63 | implementation 64 | 65 | {$BOOLEVAL OFF} 66 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 67 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 68 | 69 | { Bit masking and shifting } 70 | 71 | function TDebugHelper.EnabledMask; 72 | begin 73 | // Enabled flags are stored in bits 0, 2, 4, and 6 respectively 74 | Result := 1 shl (Cardinal(i) shl 1); 75 | end; 76 | 77 | function TDebugHelper.TypeMask; 78 | begin 79 | // Each breakpoint has its type stored within two bits: 80 | // Bits 16..17 for breakpoint 0 81 | // Bits 20..21 for breakpoint 1 82 | // Bits 24..25 for breakpoint 2 83 | // Bits 28..29 for breakpoint 3 84 | 85 | Result := NativeUInt($30000) shl (Cardinal(i) shl 2); 86 | end; 87 | 88 | function TDebugHelper.TypeShift; 89 | begin 90 | // See explanation for the type mask 91 | Result := (Cardinal(i) shl 2) or $10; // 16, 20, 24, 28 92 | end; 93 | 94 | function TDebugHelper.WidthMask; 95 | begin 96 | // Each breakpoint has its width stored within two bits: 97 | // Bits 18..19 for breakpoint 0 98 | // Bits 22..23 for breakpoint 1 99 | // Bits 26..27 for breakpoint 2 100 | // Bits 30..31 for breakpoint 3 101 | 102 | Result := NativeUInt($C0000) shl (Cardinal(i) shl 2); 103 | end; 104 | 105 | function TDebugHelper.WidthShift; 106 | begin 107 | // See explanation for the width mask 108 | Result := (Cardinal(i) shl 2) + 18; // 18, 22, 26, 30 109 | end; 110 | 111 | { State inspection / modification } 112 | 113 | function TDebugHelper.GetAddress; 114 | begin 115 | case i of 116 | 0: Result := Pointer(Dr0); 117 | 1: Result := Pointer(Dr1); 118 | 2: Result := Pointer(Dr2); 119 | 3: Result := Pointer(Dr3); 120 | else 121 | Result := nil; 122 | end; 123 | end; 124 | 125 | function TDebugHelper.GetDetected; 126 | begin 127 | // Bits 0..3 of the debug status register indicate which breakpoint was hit 128 | Result := (Dr6 and Byte(i)) <> 0; 129 | end; 130 | 131 | function TDebugHelper.GetEnabled; 132 | begin 133 | // Check the bits in the control register 134 | Result := (Dr7 and EnabledMask(i)) <> 0; 135 | end; 136 | 137 | function TDebugHelper.GetType; 138 | begin 139 | // Select two specific bits from Dr7 with a mask, 140 | // and then shift it to the lower bits. 141 | Result := THwBreakOn((Dr7 and TypeMask(i)) shr TypeShift(i)); 142 | end; 143 | 144 | function TDebugHelper.GetWidth; 145 | begin 146 | // Select two specific bits from Dr7 with a mask, 147 | // and then shift it to the lower bits. 148 | Result := THwBreakpointWidth((Dr7 and WidthMask(i)) shr WidthShift(i)); 149 | end; 150 | 151 | procedure TDebugHelper.SetAddress; 152 | begin 153 | case i of 154 | 0: Dr0 := NativeUInt(Value); 155 | 1: Dr1 := NativeUInt(Value); 156 | 2: Dr2 := NativeUInt(Value); 157 | 3: Dr3 := NativeUInt(Value); 158 | end; 159 | end; 160 | 161 | procedure TDebugHelper.SetEnabled; 162 | begin 163 | if Enable then 164 | Dr7 := Dr7 or EnabledMask(i) 165 | else 166 | Dr7 := Dr7 and not EnabledMask(i); 167 | end; 168 | 169 | procedure TDebugHelper.SetType; 170 | begin 171 | // Clear specific bits with a mask and then add them back shifting the input 172 | Dr7 := Dr7 and not TypeMask(i) or (Cardinal(Value) shl TypeShift(i)); 173 | end; 174 | 175 | procedure TDebugHelper.SetWidth; 176 | begin 177 | // Clear specific bits with a mask and then add them back shifting the input 178 | Dr7 := Dr7 and not WidthMask(i) or (Cardinal(Value) shl WidthShift(i)); 179 | end; 180 | 181 | end. 182 | -------------------------------------------------------------------------------- /Headers/Ntapi.WinSafer.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.WinSafer; 2 | 3 | { 4 | This file defines Win Safer API functions for restricting access tokens. 5 | See SDK::winsafer.h for sources. 6 | } 7 | 8 | interface 9 | 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.WinNt, DelphiApi.Reflection; 14 | 15 | const 16 | SAFER_LEVEL_OPEN = 1; 17 | 18 | SAFER_TOKEN_NULL_IF_EQUAL = $00000001; 19 | SAFER_TOKEN_COMPARE_ONLY = $00000002; 20 | SAFER_TOKEN_MAKE_INERT = $00000004; 21 | SAFER_TOKEN_WANT_FLAGS = $00000008; 22 | 23 | type 24 | TSaferHandle = type THandle; 25 | 26 | [NamingStyle(nsSnakeCase, 'SAFER_SCOPEID'), Range(1)] 27 | TSaferScopeId = ( 28 | [Reserved] SAFER_SCOPEID_RESERVED = 0, 29 | SAFER_SCOPEID_MACHINE = 1, 30 | SAFER_SCOPEID_USER = 2 31 | ); 32 | 33 | [NamingStyle(nsSnakeCase, 'SAFER_LEVELID')] 34 | TSaferLevelId = ( 35 | SAFER_LEVELID_FULLYTRUSTED = $40000, 36 | SAFER_LEVELID_NORMALUSER = $20000, 37 | SAFER_LEVELID_CONSTRAINED = $10000, 38 | SAFER_LEVELID_UNTRUSTED = $01000, 39 | SAFER_LEVELID_DISALLOWED = $00000 40 | ); 41 | 42 | [NamingStyle(nsCamelCase, 'SaferObject'), Range(1)] 43 | TSaferObjectInfoClass = ( 44 | [Reserved] SaferObjectReserved = 0, 45 | SaferObjectLevelID = 1, // q: TSaferLevelId 46 | SaferObjectScopeID = 2, // q: TSaferScopeId 47 | SaferObjectFriendlyName = 3, // q, s: PWideChar 48 | SaferObjectDescription = 4, // q, s: PWideChar 49 | SaferObjectBuiltin = 5, // q: LongBool 50 | 51 | SaferObjectDisallowed = 6, // q: LongBool 52 | SaferObjectDisableMaxPrivilege = 7, // q: LongBool 53 | SaferObjectInvertDeletedPrivileges = 8, // q: LongBool 54 | SaferObjectDeletedPrivileges = 9, // q: TTokenPrivileges 55 | SaferObjectDefaultOwner = 10, // q: TTokenOwner 56 | SaferObjectSidsToDisable = 11, // q: TTokenGroups 57 | SaferObjectRestrictedSidsInverted = 12, // q: TTokenGroups 58 | SaferObjectRestrictedSidsAdded = 13, // q: TTokenGroups 59 | 60 | SaferObjectAllIdentificationGuids = 14, // q: 61 | SaferObjectSingleIdentification = 15, // q, s: 62 | 63 | SaferObjectExtendedError = 16 // q: TWin32Error 64 | ); 65 | 66 | [FlagName(SAFER_LEVEL_OPEN, 'Open')] 67 | TSaferCreateOptions = type Cardinal; 68 | 69 | [FlagName(SAFER_TOKEN_NULL_IF_EQUAL, 'Null If Equal')] 70 | [FlagName(SAFER_TOKEN_COMPARE_ONLY, 'Compare Only')] 71 | [FlagName(SAFER_TOKEN_MAKE_INERT, 'Make Sandbox Inert')] 72 | [FlagName(SAFER_TOKEN_WANT_FLAGS, 'Want Flags')] 73 | TSaferComputeOptions = type Cardinal; 74 | 75 | [SetsLastError] 76 | function SaferCreateLevel( 77 | [in] ScopeId: TSaferScopeId; 78 | [in] LevelId: TSaferLevelId; 79 | [in] OpenFlags: TSaferCreateOptions; 80 | [out, ReleaseWith('SaferCloseLevel')] out LevelHandle: TSaferHandle; 81 | [Reserved] Reserved: Pointer = nil 82 | ): LongBool; stdcall; external advapi32; 83 | 84 | function SaferCloseLevel( 85 | [in] hLevelHandle: TSaferHandle 86 | ): LongBool; stdcall; external advapi32; 87 | 88 | [SetsLastError] 89 | function SaferComputeTokenFromLevel( 90 | [in] LevelHandle: TSaferHandle; 91 | [in, opt] InAccessToken: THandle; 92 | [in, ReleaseWith('NtClose')] out OutAccessToken: THandle; 93 | [in] Flags: TSaferComputeOptions; 94 | [Reserved] Reserved: PCardinal = nil 95 | ): LongBool; stdcall; external advapi32; 96 | 97 | [SetsLastError] 98 | function SaferGetLevelInformation( 99 | [in] LevelHandle: TSaferHandle; 100 | [in] InfoType: TSaferObjectInfoClass; 101 | [out, WritesTo] QueryBuffer: Pointer; 102 | [in, NumberOfBytes] InBufferSize: Cardinal; 103 | [out, NumberOfBytes] out OutBufferSize: Cardinal 104 | ): LongBool; stdcall; external advapi32; 105 | 106 | [SetsLastError] 107 | function SaferSetLevelInformation( 108 | [in] LevelHandle: TSaferHandle; 109 | [in] InfoType: TSaferObjectInfoClass; 110 | [in, ReadsFrom] QueryBuffer: Pointer; 111 | [in, NumberOfBytes] InBufferSize: Cardinal 112 | ): LongBool; stdcall; external advapi32; 113 | 114 | implementation 115 | 116 | {$BOOLEVAL OFF} 117 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 118 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 119 | 120 | end. 121 | -------------------------------------------------------------------------------- /Headers/Ntapi.appmodel.ExecAlias.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.appmodel.ExecAlias; 2 | 3 | { 4 | This module provides definitions for working with app execution aliases. 5 | } 6 | 7 | interface 8 | 9 | {$WARN SYMBOL_PLATFORM OFF} 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.ntdef, Ntapi.ntseapi, Ntapi.ntioapi, Ntapi.ObjBase, Ntapi.Versions, 14 | DelphiApi.Reflection, DelphiApi.DelayLoad; 15 | 16 | const 17 | AppExecAliasHost = 'ApiSetHost.AppExecutionAlias.dll'; 18 | 19 | TOKEN_LOAD_ALIAS = TOKEN_QUERY or TOKEN_DUPLICATE or TOKEN_IMPERSONATE or 20 | TOKEN_ADJUST_DEFAULT; 21 | 22 | var 23 | delayed_AppExecAliasHost: TDelayedLoadDll = (DllName: AppExecAliasHost); 24 | 25 | type 26 | TAppExecAliasData = record end; 27 | PAppExecAliasData = ^TAppExecAliasData; 28 | 29 | // rev 30 | [SDKName('AppExecutionAliasType')] 31 | [NamingStyle(nsCamelCase, 'AppExecAlias')] 32 | TAppExecutionAliasType = ( 33 | AppExecAliasDesktop = 0, 34 | AppExecAliasUWPSingleInstance = 1, 35 | AppExecAliasUWPMultiInstance = 2, 36 | AppExecAliasUWPConsole = 3 37 | ); 38 | 39 | // private 40 | [SDKName('APPEXECUTIONALIASINFO')] 41 | TAppExecutionAliasInfo = record 42 | PackageFullName: PWideChar; 43 | ApplicationPath: PWideChar; 44 | ActivationToken: THandle; 45 | end; 46 | PAppExecutionAliasInfo = ^TAppExecutionAliasInfo; 47 | 48 | // rev 49 | [MinOSVersion(OsWin10RS3)] 50 | procedure FreeAppExecutionAliasInfoEx( 51 | [in] ExecutionAliasInfo: PAppExecutionAliasInfo 52 | ); stdcall external AppExecAliasHost delayed; 53 | 54 | var delayed_FreeAppExecutionAliasInfoEx: TDelayedLoadFunction = ( 55 | Dll: @delayed_AppExecAliasHost; 56 | FunctionName: 'FreeAppExecutionAliasInfoEx'; 57 | ); 58 | 59 | // rev 60 | [MinOSVersion(OsWin10RS3)] 61 | function LoadAppExecutionAliasInfoEx( 62 | [in] ApplicationPath: PWideChar; 63 | [in, opt, Access(TOKEN_LOAD_ALIAS)] hIncomingToken: THandle; 64 | [out, ReleaseWith('FreeAppExecutionAliasInfoEx')] out ExecutionAliasInfo: 65 | PAppExecutionAliasInfo 66 | ): NTSTATUS; stdcall external AppExecAliasHost delayed; 67 | 68 | var delayed_LoadAppExecutionAliasInfoEx: TDelayedLoadFunction = ( 69 | Dll: @delayed_AppExecAliasHost; 70 | FunctionName: 'LoadAppExecutionAliasInfoEx'; 71 | ); 72 | 73 | // rev 74 | [MinOSVersion(OsWin10RS3)] 75 | procedure CloseAppExecutionAliasEx( 76 | [in] Alias: PAppExecAliasData 77 | ); stdcall external AppExecAliasHost delayed; 78 | 79 | var delayed_CloseAppExecutionAliasEx: TDelayedLoadFunction = ( 80 | Dll: @delayed_AppExecAliasHost; 81 | FunctionName: 'CloseAppExecutionAliasEx'; 82 | ); 83 | 84 | // rev 85 | [MinOSVersion(OsWin10RS3)] 86 | function OpenAppExecutionAliasForUserEx( 87 | [in] Path: PWideChar; 88 | [in, opt, Access(TOKEN_QUERY)] Token: THandle; 89 | [out, ReleaseWith('CloseAppExecutionAliasEx')] out Alias: PAppExecAliasData 90 | ): HResult; stdcall external AppExecAliasHost delayed; 91 | 92 | var delayed_OpenAppExecutionAliasForUserEx: TDelayedLoadFunction = ( 93 | Dll: @delayed_AppExecAliasHost; 94 | FunctionName: 'OpenAppExecutionAliasForUserEx'; 95 | ); 96 | 97 | // rev 98 | [MinOSVersion(OsWin10RS4)] 99 | function CreateAppExecutionAliasEx2( 100 | [in] PackageFamilyName: PWideChar; 101 | [in] ApplicationUserModelId: PWideChar; 102 | [in] PackageRelativeExecutable: PWideChar; 103 | [in] AliasType: TAppExecutionAliasType; 104 | [out, ReleaseWith('CloseAppExecutionAliasEx')] out Alias: PAppExecAliasData 105 | ): HResult; stdcall external AppExecAliasHost delayed; 106 | 107 | var delayed_CreateAppExecutionAliasEx2: TDelayedLoadFunction = ( 108 | Dll: @delayed_AppExecAliasHost; 109 | FunctionName: 'CreateAppExecutionAliasEx2'; 110 | ); 111 | 112 | // rev 113 | [MinOSVersion(OsWin10RS3)] 114 | function PersistAppExecutionAliasToFileEx( 115 | [in] Alias: PAppExecAliasData; 116 | [in] Path: PWideChar 117 | ): HResult; stdcall external AppExecAliasHost delayed; 118 | 119 | var delayed_PersistAppExecutionAliasToFileEx: TDelayedLoadFunction = ( 120 | Dll: @delayed_AppExecAliasHost; 121 | FunctionName: 'PersistAppExecutionAliasToFileEx'; 122 | ); 123 | 124 | // rev 125 | [MinOSVersion(OsWin1019H2)] 126 | function PersistAppExecutionAliasToFileHandleEx( 127 | [in] Alias: PAppExecAliasData; 128 | [in, Access(FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES)] FileHandle: THandle 129 | ): HResult; stdcall external AppExecAliasHost delayed; 130 | 131 | var delayed_PersistAppExecutionAliasToFileHandleEx: TDelayedLoadFunction = ( 132 | Dll: @delayed_AppExecAliasHost; 133 | FunctionName: 'PersistAppExecutionAliasToFileHandleEx'; 134 | ); 135 | 136 | // rev 137 | [MinOSVersion(OsWin11)] 138 | function CreateAndPersistAppExecutionAliasEx( 139 | [in] PackageFamilyName: PWideChar; 140 | [in] ApplicationUserModelId: PWideChar; 141 | [in] PackageRelativeExecutable: PWideChar; 142 | [in] AliasType: TAppExecutionAliasType; 143 | [in] FileName: PWideChar 144 | ): HResult; stdcall external AppExecAliasHost delayed; 145 | 146 | var delayed_CreateAndPersistAppExecutionAliasEx: TDelayedLoadFunction = ( 147 | Dll: @delayed_AppExecAliasHost; 148 | FunctionName: 'CreateAndPersistAppExecutionAliasEx'; 149 | ); 150 | 151 | // rev 152 | [MinOSVersion(OsWin10RS3)] 153 | function GetAppExecutionAliasExecutableEx( 154 | [in] Alias: PAppExecAliasData; 155 | [out, WritesTo] Executable: PWideChar; 156 | [in, out, NumberOfElements] var PathCch: Cardinal 157 | ): HResult; stdcall external AppExecAliasHost delayed; 158 | 159 | var delayed_GetAppExecutionAliasExecutableEx: TDelayedLoadFunction = ( 160 | Dll: @delayed_AppExecAliasHost; 161 | FunctionName: 'GetAppExecutionAliasExecutableEx'; 162 | ); 163 | 164 | // rev 165 | [MinOSVersion(OsWin10RS3)] 166 | function GetAppExecutionAliasApplicationUserModelIdEx( 167 | [in] Alias: PAppExecAliasData; 168 | [out, WritesTo] Aumid: PWideChar; 169 | [in, out, NumberOfElements] var PathCch: Cardinal 170 | ): HResult; stdcall external AppExecAliasHost delayed; 171 | 172 | var delayed_GetAppExecutionAliasApplicationUserModelIdEx: TDelayedLoadFunction = ( 173 | Dll: @delayed_AppExecAliasHost; 174 | FunctionName: 'GetAppExecutionAliasApplicationUserModelIdEx'; 175 | ); 176 | // rev 177 | [RequiresCOM] 178 | [MinOSVersion(OsWin10RS3)] 179 | function GetAppExecutionAliasPackageFullNameEx( 180 | [in] Alias: PAppExecAliasData; 181 | [out, WritesTo] PackageFullName: PWideChar; 182 | [in, out, NumberOfElements] var PathCch: Cardinal 183 | ): HResult; stdcall external AppExecAliasHost delayed; 184 | 185 | var delayed_GetAppExecutionAliasPackageFullNameEx: TDelayedLoadFunction = ( 186 | Dll: @delayed_AppExecAliasHost; 187 | FunctionName: 'GetAppExecutionAliasPackageFullNameEx'; 188 | ); 189 | 190 | // rev 191 | [MinOSVersion(OsWin10RS3)] 192 | function GetAppExecutionAliasPackageFamilyNameEx( 193 | [in] Alias: PAppExecAliasData; 194 | [out, WritesTo] PackageFamilyName: PWideChar; 195 | [in, out, NumberOfElements] var PathCch: Cardinal 196 | ): HResult; stdcall external AppExecAliasHost delayed; 197 | 198 | var delayed_GetAppExecutionAliasPackageFamilyNameEx: TDelayedLoadFunction = ( 199 | Dll: @delayed_AppExecAliasHost; 200 | FunctionName: 'GetAppExecutionAliasPackageFamilyNameEx'; 201 | ); 202 | 203 | // rev 204 | [MinOSVersion(OsWin10RS4)] 205 | function GetAppExecutionAliasApplicationType( 206 | [in] Alias: PAppExecAliasData; 207 | [out] out AliasType: TAppExecutionAliasType 208 | ): HResult; stdcall external AppExecAliasHost delayed; 209 | 210 | var delayed_GetAppExecutionAliasApplicationType: TDelayedLoadFunction = ( 211 | Dll: @delayed_AppExecAliasHost; 212 | FunctionName: 'GetAppExecutionAliasApplicationType'; 213 | ); 214 | 215 | implementation 216 | 217 | end. 218 | -------------------------------------------------------------------------------- /Headers/Ntapi.ntdbg.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ntdbg; 2 | 3 | { 4 | This file includes definitions for debugging via Native API. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | uses 12 | Ntapi.WinNt, Ntapi.ntdef, DelphiApi.Reflection; 13 | 14 | const 15 | // PHNT::ntdbg.h - debug object access masks 16 | DEBUG_READ_EVENT = $0001; 17 | DEBUG_PROCESS_ASSIGN = $0002; 18 | DEBUG_SET_INFORMATION = $0004; 19 | DEBUG_QUERY_INFORMATION = $0008; 20 | DEBUG_ALL_ACCESS = STANDARD_RIGHTS_ALL or $000F; 21 | 22 | // PHNT::ntdbg.h - creation flag 23 | DEBUG_KILL_ON_CLOSE = $1; 24 | 25 | type 26 | [FriendlyName('debug object'), ValidBits(DEBUG_ALL_ACCESS)] 27 | [SubEnum(DEBUG_ALL_ACCESS, DEBUG_ALL_ACCESS, 'Full Access')] 28 | [FlagName(DEBUG_READ_EVENT, 'Read Events')] 29 | [FlagName(DEBUG_PROCESS_ASSIGN, 'Assign Process')] 30 | [FlagName(DEBUG_SET_INFORMATION, 'Set Information')] 31 | [FlagName(DEBUG_QUERY_INFORMATION, 'Query Information')] 32 | [InheritsFrom(System.TypeInfo(TAccessMask))] 33 | TDebugObjectAccessMask = type TAccessMask; 34 | 35 | [FlagName(DEBUG_KILL_ON_CLOSE, 'Kill-On-Close')] 36 | TDebugCreateFlags = type Cardinal; 37 | 38 | // PHNT::ntdbg.h 39 | [SDKName('DBGKM_EXCEPTION')] 40 | TDbgKmException = record 41 | ExceptionRecord: TExceptionRecord; 42 | FirstChance: LongBool; 43 | end; 44 | PDbgKmException = ^TDbgKmException; 45 | 46 | // PHNT::ntdbg.h 47 | [SDKName('DBGKM_CREATE_THREAD')] 48 | TDbgKmCreateThread = record 49 | SubsystemKey: Cardinal; 50 | StartAddress: Pointer; 51 | end; 52 | PDbgKmCreateThread = ^TDbgKmCreateThread; 53 | 54 | // PHNT::ntdbg.h 55 | [SDKName('DBGKM_CREATE_PROCESS')] 56 | TDbgKmCreateProcess = record 57 | SubsystemKey: Cardinal; 58 | FileHandle: THandle; 59 | BaseOfImage: Pointer; 60 | [Hex] DebugInfoFileOffset: Cardinal; 61 | [Bytes] DebugInfoSize: Cardinal; 62 | InitialThread: TDbgKmCreateThread; 63 | end; 64 | PDbgKmCreateProcess = ^TDbgKmCreateProcess; 65 | 66 | // PHNT::ntdbg.h 67 | [SDKName('DBGKM_LOAD_DLL')] 68 | TDbgKmLoadDll = record 69 | FileHandle: THandle; 70 | BaseOfDll: Pointer; 71 | [Hex] DebugInfoFileOffset: Cardinal; 72 | [Bytes] DebugInfoSize: Cardinal; 73 | NamePointer: Pointer; 74 | end; 75 | PDbgKmLoadDll = ^TDbgKmLoadDll; 76 | 77 | // PHNT::ntdbg.h 78 | [SDKName('DBG_STATE')] 79 | [NamingStyle(nsCamelCase, 'Dbg', 'StateChange')] 80 | TDbgState = ( 81 | DbgIdle = 0, 82 | DbgReplyPending = 1, 83 | DbgCreateThreadStateChange = 2, 84 | DbgCreateProcessStateChange = 3, 85 | DbgExitThreadStateChange = 4, 86 | DbgExitProcessStateChange = 5, 87 | DbgExceptionStateChange = 6, 88 | DbgBreakpointStateChange = 7, 89 | DbgSingleStepStateChange = 8, 90 | DbgLoadDllStateChange = 9, 91 | DbgUnloadDllStateChange = 10 92 | ); 93 | 94 | // PHNT::ntdbg.h 95 | [SDKName('DBGUI_CREATE_THREAD')] 96 | TDbgUiCreateThread = record 97 | HandleToThread: THandle; 98 | NewThread: TDbgKmCreateThread; 99 | end; 100 | PDbgUiCreateThread = ^TDbgUiCreateThread; 101 | 102 | // PHNT::ntdbg.h 103 | [SDKName('DBGUI_CREATE_PROCESS')] 104 | TDbgUiCreateProcess = record 105 | HandleToProcess: THandle; 106 | HandleToThread: THandle; 107 | NewProcess: TDbgKmCreateProcess; 108 | end; 109 | PDbgUiCreateProcess = ^TDbgUiCreateProcess; 110 | 111 | // PHNT::ntdbg.h 112 | [SDKName('DBGKM_EXIT_THREAD')] 113 | TDgbKmExitThread = record 114 | ExitStatus: NTSTATUS; 115 | end; 116 | PDgbKmExitThread = ^TDgbKmExitThread; 117 | TDgbKmExitProcess = TDgbKmExitThread; 118 | PDgbKmExitProcess = ^TDgbKmExitProcess; 119 | 120 | // PHNT::ntdbg.h 121 | [SDKName('DBGKM_UNLOAD_DLL')] 122 | TDbgKmUnloadDll = record 123 | BaseAddress: Pointer; 124 | end; 125 | PDbgKmUnloadDll = ^TDbgKmUnloadDll; 126 | 127 | // PHNT::ntdbg.h 128 | [SDKName('DBGUI_WAIT_STATE_CHANGE')] 129 | TDbgUiWaitStateChange = record 130 | NewState: TDbgState; 131 | AppClientId: TClientId; 132 | case Integer of 133 | 0: (Exception: TDbgKmException); 134 | 1: (CreateThread: TDbgUiCreateThread); 135 | 2: (CreateProcessInfo: TDbgUiCreateProcess); 136 | 3: (ExitThread: TDgbKmExitThread); 137 | 4: (ExitProcess: TDgbKmExitProcess); 138 | 5: (LoadDll: TDbgKmLoadDll); 139 | 6: (UnloadDll: TDbgKmUnloadDll); 140 | end; 141 | PDbgUiWaitStateChange = ^TDbgUiWaitStateChange; 142 | 143 | // PHNT::ntdbg.h 144 | [SDKName('DEBUGOBJECTINFOCLASS')] 145 | [NamingStyle(nsCamelCase, 'DebugObject'), Range(1)] 146 | TDebugObjectInfoClass = ( 147 | [Reserved] DebugObjectUnusedInformation = 0, 148 | DebugObjectKillProcessOnExitInformation = 1 // s: LongBool 149 | ); 150 | 151 | // PHNT::ntdbg.h 152 | function NtCreateDebugObject( 153 | [out, ReleaseWith('NtClose')] out DebugObjectHandle: THandle; 154 | [in] DesiredAccess: TDebugObjectAccessMask; 155 | [in, opt] ObjectAttributes: PObjectAttributes; 156 | [in] Flags: TDebugCreateFlags 157 | ): NTSTATUS; stdcall; external ntdll; 158 | 159 | // PHNT::ntdbg.h 160 | function NtDebugActiveProcess( 161 | [in, Access(PROCESS_SUSPEND_RESUME)] ProcessHandle: THandle; 162 | [in, Access(DEBUG_PROCESS_ASSIGN)] DebugObjectHandle: THandle 163 | ): NTSTATUS; stdcall; external ntdll; 164 | 165 | // PHNT::ntdbg.h 166 | function NtDebugContinue( 167 | [in, Access(DEBUG_READ_EVENT)] DebugObjectHandle: THandle; 168 | [in] const ClientId: TClientId; 169 | [in] ContinueStatus: NTSTATUS 170 | ): NTSTATUS; stdcall; external ntdll; 171 | 172 | // PHNT::ntdbg.h 173 | function NtRemoveProcessDebug( 174 | [in, Access(PROCESS_SUSPEND_RESUME)] ProcessHandle: THandle; 175 | [in, Access(DEBUG_PROCESS_ASSIGN)] DebugObjectHandle: THandle 176 | ): NTSTATUS; stdcall; external ntdll; 177 | 178 | // PHNT::ntdbg.h 179 | function NtSetInformationDebugObject( 180 | [in, Access(DEBUG_SET_INFORMATION)] DebugObjectHandle: THandle; 181 | [in] DebugObjectInformationClass: TDebugObjectInfoClass; 182 | [in, ReadsFrom] DebugInformation: Pointer; 183 | [in, NumberOfBytes] DebugInformationLength: Cardinal; 184 | [out, opt] ReturnLength: PCardinal 185 | ): NTSTATUS; stdcall; external ntdll; 186 | 187 | // Debug UI 188 | 189 | // PHNT::ntdbg.h 190 | function DbgUiConnectToDbg( 191 | ): NTSTATUS; stdcall; external ntdll; 192 | 193 | // PHNT::ntdbg.h 194 | function NtWaitForDebugEvent( 195 | [in, Access(DEBUG_READ_EVENT)] DebugObjectHandle: THandle; 196 | [in] Alertable: Boolean; 197 | [in, opt] Timeout: PLargeInteger; 198 | [out] out WaitStateChange: TDbgUiWaitStateChange 199 | ): NTSTATUS; stdcall; external ntdll; 200 | 201 | // PHNT::ntdbg.h 202 | function DbgUiGetThreadDebugObject( 203 | ): THandle; stdcall; external ntdll; 204 | 205 | // PHNT::ntdbg.h 206 | procedure DbgUiSetThreadDebugObject( 207 | [in] DebugObject: THandle 208 | ); stdcall; external ntdll; 209 | 210 | // PHNT::ntdbg.h 211 | function DbgUiDebugActiveProcess( 212 | [in, Access(PROCESS_SUSPEND_RESUME or PROCESS_CREATE_THREAD)] Process: THandle 213 | ): NTSTATUS; stdcall; external ntdll; 214 | 215 | // PHNT::ntdbg.h 216 | procedure DbgUiRemoteBreakin( 217 | [in, opt] Context: Pointer 218 | ); stdcall; external ntdll; 219 | 220 | // PHNT::ntdbg.h 221 | function DbgUiIssueRemoteBreakin( 222 | [in, Access(PROCESS_CREATE_THREAD)] Process: THandle 223 | ): NTSTATUS; stdcall; external ntdll; 224 | 225 | // Local debugging 226 | 227 | // WDK::wdm.h 228 | procedure DbgBreakPoint( 229 | ); stdcall; external ntdll; 230 | 231 | // WDK::wdm.h 232 | function DbgPrint( 233 | [in] Format: PAnsiChar 234 | ): NTSTATUS; cdecl; varargs; external ntdll; 235 | 236 | implementation 237 | 238 | {$BOOLEVAL OFF} 239 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 240 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 241 | 242 | end. 243 | -------------------------------------------------------------------------------- /Headers/Ntapi.ntlpcapi.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ntlpcapi; 2 | 3 | { 4 | This file includes definitions for LPC/ALPC functions. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.WinNt, Ntapi.ntdef, DelphiApi.Reflection; 11 | 12 | {$MINENUMSIZE 4} 13 | 14 | const 15 | // PHNT::ntlpcapi.h - LPC port access masks 16 | PORT_CONNECT = $0001; 17 | PORT_ALL_ACCESS = STANDARD_RIGHTS_ALL or PORT_CONNECT; 18 | 19 | type 20 | [FriendlyName('ALPC port'), ValidBits(PORT_ALL_ACCESS)] 21 | [SubEnum(PORT_ALL_ACCESS, PORT_ALL_ACCESS, 'Full Access')] 22 | [FlagName(PORT_CONNECT, 'Connect')] 23 | [InheritsFrom(System.TypeInfo(TAccessMask))] 24 | TAlpcAccessMask = type TAccessMask; 25 | 26 | TPortMessageUnion1 = record 27 | case Integer of 28 | 0: (DataLength: Word; TotalLength: Word); 29 | 1: (Length: Cardinal); 30 | end; 31 | 32 | TPortMessageUnion2 = record 33 | case Integer of 34 | 0: (_Type: Word; DataInfoOffset: Word); 35 | 1: (ZeroInit: Cardinal); 36 | end; 37 | 38 | TPortMessageUnion4 = record 39 | case Integer of 40 | 0: (ClientViewSize: NativeUInt); 41 | 1: (CallbackId: Cardinal); 42 | end; 43 | 44 | // PHNT::ntlpcapi.h 45 | [SDKName('PORT_MESSAGE')] 46 | TPortMessage = record 47 | u1: TPortMessageUnion1; 48 | u2: TPortMessageUnion2; 49 | ClientId: TClientId; 50 | MessageId: Cardinal; 51 | u4: TPortMessageUnion4; 52 | end; 53 | PPortMessage = ^TPortMessage; 54 | 55 | // PHNT::ntlpcapi.h 56 | [SDKName('PORT_VIEW')] 57 | TPortView = record 58 | [RecordSize] Length: Cardinal; 59 | SectionHandle: THandle; 60 | SectionOffset: Cardinal; 61 | ViewSize: NativeUInt; 62 | ViewBase: Pointer; 63 | ViewRemoteBase: Pointer; 64 | end; 65 | PPortView = ^TPortView; 66 | 67 | // PHNT::ntlpcapi.h 68 | [SDKName('REMOTE_PORT_VIEW')] 69 | TRemotePortView = record 70 | [RecordSize] Length: Cardinal; 71 | ViewSize: NativeUInt; 72 | ViewBase: Pointer; 73 | end; 74 | PRemotePortView = ^TRemotePortView; 75 | 76 | // PHNT::ntlpcapi.h 77 | function NtConnectPort( 78 | [out, ReleaseWith('NtClose')] out PortHandle: THandle; 79 | [in] const PortName: TNtUnicodeString; 80 | [in] const SecurityQos: TSecurityQualityOfService; 81 | [in, out, opt] ClientView: PPortView; 82 | [in, out, opt] ServerView: PRemotePortView; 83 | [out, opt] MaxMessageLength: PCardinal; 84 | [in, out, opt, ReadsFrom, WritesTo] ConnectionInformation: Pointer; 85 | [in, out, opt, NumberOfBytes] ConnectionInformationLength: PCardinal 86 | ): NTSTATUS; stdcall; external ntdll; 87 | 88 | // PHNT::ntlpcapi.h 89 | function NtRequestWaitReplyPort( 90 | [in] PortHandle: THandle; 91 | [in, ReadsFrom] const RequestMessage: TPortMessage; 92 | [out, WritesTo] out ReplyMessage: TPortMessage 93 | ): NTSTATUS; stdcall; external ntdll; 94 | 95 | implementation 96 | 97 | {$BOOLEVAL OFF} 98 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 99 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 100 | 101 | end. 102 | -------------------------------------------------------------------------------- /Headers/Ntapi.ntpoapi.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ntpoapi; 2 | 3 | { 4 | This file includes definitions for the system power management API. 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | uses 12 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.Versions, DelphiApi.Reflection; 13 | 14 | const 15 | // WDK::ntpoapi.h - counted reason version 16 | DIAGNOSTIC_REASON_VERSION = 0; 17 | 18 | // WDK::ntpoapi.h - counted reason flags 19 | DIAGNOSTIC_REASON_SIMPLE_STRING = $00000001; 20 | DIAGNOSTIC_REASON_DETAILED_STRING = $00000002; 21 | DIAGNOSTIC_REASON_NOT_SPECIFIED = $80000000; 22 | 23 | type 24 | // WDK::ntpoapi.h & PHNT::ntpoapi.h 25 | [SDKName('POWER_INFORMATION_LEVEL')] 26 | [NamingStyle(nsCamelCase)] 27 | TPowerInformationLevel = ( 28 | SystemPowerPolicyAc = 0, 29 | SystemPowerPolicyDc = 1, 30 | VerifySystemPolicyAc = 2, 31 | VerifySystemPolicyDc = 3, 32 | SystemPowerCapabilities = 4, 33 | SystemBatteryState = 5, 34 | SystemPowerStateHandler = 6, 35 | ProcessorStateHandler = 7, 36 | SystemPowerPolicyCurrent = 8, 37 | AdministratorPowerPolicy = 9, 38 | SystemReserveHiberFile = 10, 39 | ProcessorInformation = 11, // out: TProcessorPowerInformationArray (for the number of processors) 40 | SystemPowerInformation = 12, 41 | ProcessorStateHandler2 = 13, 42 | LastWakeTime = 14, 43 | LastSleepTime = 15, 44 | SystemExecutionState = 16, 45 | SystemPowerStateNotifyHandler = 17, 46 | ProcessorPowerPolicyAc = 18, 47 | ProcessorPowerPolicyDc = 19, 48 | VerifyProcessorPowerPolicyAc = 20, 49 | VerifyProcessorPowerPolicyDc = 21, 50 | ProcessorPowerPolicyCurrent = 22, 51 | SystemPowerStateLogging = 23, 52 | SystemPowerLoggingEntry = 24, 53 | SetPowerSettingValue = 25, 54 | NotifyUserPowerSetting = 26, 55 | PowerInformationLevelUnused0 = 27, 56 | SystemMonitorHiberBootPowerOff = 28, 57 | SystemVideoState = 29, 58 | TraceApplicationPowerMessage = 30, 59 | TraceApplicationPowerMessageEnd = 31, 60 | ProcessorPerfStates = 32, 61 | ProcessorIdleStates = 33, 62 | ProcessorCap = 34, 63 | SystemWakeSource = 35, 64 | SystemHiberFileInformation = 36, 65 | TraceServicePowerMessage = 37, 66 | ProcessorLoad = 38, 67 | PowerShutdownNotification = 39, 68 | MonitorCapabilities = 40, 69 | SessionPowerInit = 41, 70 | SessionDisplayState = 42, 71 | PowerRequestCreate = 43, // in: TCountedReasonContext; out: THandle 72 | PowerRequestAction = 44, // in: TPowerRequestAction 73 | GetPowerRequestList = 45, 74 | ProcessorInformationEx = 46, 75 | NotifyUserModeLegacyPowerEvent = 47, 76 | GroupPark = 48, 77 | ProcessorIdleDomains = 49, 78 | WakeTimerList = 50, 79 | SystemHiberFileSize = 51, 80 | ProcessorIdleStatesHv = 52, 81 | ProcessorPerfStatesHv = 53, 82 | ProcessorPerfCapHv = 54, 83 | ProcessorSetIdle = 55, 84 | LogicalProcessorIdling = 56, 85 | UserPresence = 57, 86 | PowerSettingNotificationName = 58, 87 | GetPowerSettingValue = 59, 88 | IdleResiliency = 60, 89 | SessionRITState = 61, 90 | SessionConnectNotification = 62, 91 | SessionPowerCleanup = 63, 92 | SessionLockState = 64, 93 | SystemHiberbootState = 65, 94 | PlatformInformation = 66, 95 | PdcInvocation = 67, 96 | MonitorInvocation = 68, 97 | FirmwareTableInformationRegistered = 69, 98 | SetShutdownSelectedTime = 70, 99 | SuspendResumeInvocation = 71, 100 | PlmPowerRequestCreate = 72, // in: TCountedReasonContext; out: THandle, Windows 8+ 101 | ScreenOff = 73, 102 | CsDeviceNotification = 74, 103 | PlatformRole = 75, 104 | LastResumePerformance = 76, 105 | DisplayBurst = 77, 106 | ExitLatencySamplingPercentage = 78, 107 | RegisterSpmPowerSettings = 79, 108 | PlatformIdleStates = 80, 109 | ProcessorIdleVeto = 81, 110 | PlatformIdleVeto = 82, 111 | SystemBatteryStatePrecise = 83, 112 | ThermalEvent = 84 , 113 | PowerRequestActionInternal = 85, 114 | BatteryDeviceState = 86, 115 | PowerInformationInternal = 87, 116 | ThermalStandby = 88, 117 | SystemHiberFileType = 89, 118 | PhysicalPowerButtonPress = 90, 119 | QueryPotentialDripsConstraint = 91, 120 | EnergyTrackerCreate = 92, 121 | EnergyTrackerQuery = 93, 122 | UpdateBlackBoxRecorder = 94, 123 | SessionAllowExternalDmaDevices = 95, 124 | SendSuspendResumeNotification = 96, 125 | BlackBoxRecorderDirectAccessBuffer = 97 126 | ); 127 | 128 | // WDK::ntpoapi.h - info class 11 129 | [SDKName('PROCESSOR_POWER_INFORMATION')] 130 | TProcessorPowerInformation = record 131 | Number: Cardinal; 132 | MaxMhz: Cardinal; 133 | CurrentMhz: Cardinal; 134 | MhzLimit: Cardinal; 135 | MaxIdleState: Cardinal; 136 | CurrentIdleState: Cardinal; 137 | end; 138 | PProcessorPowerInformation = ^TProcessorPowerInformation; 139 | TProcessorPowerInformationArray = TAnysizeArray; 140 | PProcessorPowerInformationArray = ^TProcessorPowerInformationArray; 141 | 142 | [FlagName(DIAGNOSTIC_REASON_SIMPLE_STRING, 'Simple String')] 143 | [FlagName(DIAGNOSTIC_REASON_DETAILED_STRING, 'Detailed String')] 144 | [FlagName(DIAGNOSTIC_REASON_NOT_SPECIFIED, 'Not Specified')] 145 | TCountedReasonContextFlags = type Cardinal; 146 | 147 | // WDK::ntpoapi.h - info class 43 148 | [SDKName('COUNTED_REASON_CONTEXT')] 149 | TCountedReasonContext = record 150 | [Reserved(DIAGNOSTIC_REASON_VERSION)] Version: Cardinal; 151 | case Flags: TCountedReasonContextFlags of 152 | DIAGNOSTIC_REASON_SIMPLE_STRING: ( 153 | SimpleString: TNtUnicodeString 154 | ); 155 | 156 | DIAGNOSTIC_REASON_DETAILED_STRING: ( 157 | ResourceFileName: TNtUnicodeString; 158 | ResourceReasonId: Word; 159 | [NumberOfElements] StringCount: Cardinal; 160 | ReasonStrings: PNtUnicodeStringArray; 161 | ); 162 | end; 163 | PCountedReasonContext = ^TCountedReasonContext; 164 | 165 | // PHNT::ntpoapi.h 166 | [SDKName('POWER_REQUEST_TYPE_INTERNAL')] 167 | [NamingStyle(nsCamelCase, 'PowerRequest', 'Internal'), ValidBits([0..5, 8])] 168 | TPowerRequestTypeInternal = ( 169 | PowerRequestDisplayRequiredInternal = 0, 170 | PowerRequestSystemRequiredInternal = 1, 171 | PowerRequestAwayModeRequiredInternal = 2, 172 | PowerRequestExecutionRequiredInternal = 3, // Windows 8+ 173 | PowerRequestPerfBoostRequiredInternal = 4, // Windows 8+ 174 | PowerRequestActiveLockScreenInternal = 5, // Windows 10 RS1+ (reserved on Windows 8) 175 | PowerRequestReserved6, PowerRequestReserved7, // Windows 8 only 176 | PowerRequestFullScreenVideoRequired = 8 // Windows 8 only 177 | ); 178 | 179 | // PHNT::ntpoapi.h - info class 44 180 | [SDKName('POWER_REQUEST_ACTION')] 181 | TPowerRequestAction = record 182 | PowerRequestHandle: THandle; 183 | RequestType: TPowerRequestTypeInternal; 184 | SetAction: Boolean; 185 | [MinOSVersion(OsWin8), Access(PROCESS_SET_LIMITED_INFORMATION)] 186 | ProcessHandle: THandle; // Windows 8+ and only for requests created via PlmPowerRequestCreate 187 | end; 188 | 189 | // WDK::ntpoapi.h 190 | function NtPowerInformation( 191 | [in] InformationLevel: TPowerInformationLevel; 192 | [in, ReadsFrom] InputBuffer: Pointer; 193 | [in, NumberOfBytes] InputBufferLength: Cardinal; 194 | [out, WritesTo] OutputBuffer: Pointer; 195 | [in, NumberOfBytes] OutputBufferLength: Cardinal 196 | ): NTSTATUS; stdcall; external ntdll; 197 | 198 | implementation 199 | 200 | {$BOOLEVAL OFF} 201 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 202 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 203 | 204 | end. 205 | -------------------------------------------------------------------------------- /Headers/Ntapi.ntsam.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diversenok/NtUtilsLibrary/7e01c0b21491f1e66c33d447acc47c01b1a630f2/Headers/Ntapi.ntsam.pas -------------------------------------------------------------------------------- /Headers/Ntapi.ntsmss.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.ntsmss; 2 | 3 | { 4 | This module defines types for interacting with SMSS and CSRSS session ports. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.ntrtl, Ntapi.ntlpcapi, Ntapi.ImageHlp, 11 | DelphiApi.Reflection; 12 | 13 | {$MINENUMSIZE 4} 14 | 15 | const 16 | // PHNT::ntsmss.h - SB process creation flags 17 | SMP_DEBUG_FLAG = $00000001; 18 | SMP_ASYNC_FLAG = $00000002; 19 | SMP_DONT_START = $00000004; 20 | 21 | type 22 | // PHNT::ntsmss.h 23 | [SDKName('SMAPINUMBER')] 24 | [NamingStyle(nsCamelCase, 'Sm', 'Api'), ValidBits([1, 3..7])] 25 | TSmApiNumber = ( 26 | [Reserved] SmNotImplementedApi = 0, 27 | SmSessionCompleteApi = 1, 28 | [Reserved] SmNotImplemented2Api = 2, 29 | SmExecPgmApi = 3, 30 | SmLoadDeferedSubsystemApi = 4, 31 | SmStartCsrApi = 5, 32 | SmStopCsrApi = 6, 33 | SmStartServerSiloApi = 7 34 | ); 35 | 36 | // PHNT::ntsmss.h 37 | [SDKName('SMSESSIONCOMPLETE')] 38 | TSmSessionComplete = record 39 | [in] SessionId: TSessionId; 40 | [in] CompletionStatus: NTSTATUS; 41 | end; 42 | PSmSessionComplete = ^TSmSessionComplete; 43 | 44 | // PHNT::ntsmss.h 45 | [SDKName('SMEXECPGM')] 46 | TSmExecPgm = record 47 | [in] ProcessInformation: TRtlUserProcessInformation; 48 | [in] DebugFlag: Boolean; 49 | end; 50 | PSmExecPgm = ^TSmExecPgm; 51 | 52 | // PHNT::ntsmss.h 53 | [SDKName('SMLOADDEFERED')] 54 | TSmLoadDefered = record 55 | [in, NumberOfBytes] SubsystemNameLength: Cardinal; 56 | [in] SubsystemName: array [0..31] of WideChar; 57 | end; 58 | PSmLoadDefered = ^TSmLoadDefered; 59 | 60 | // PHNT::ntsmss.h 61 | [SDKName('SMSTARTCSR')] 62 | TSmStartCsr = record 63 | [in, out] MuSessionId: TSessionId; 64 | [in, NumberOfBytes] InitialCommandLength: Cardinal; 65 | [in] InitialCommand: array [0..127] of WideChar; 66 | [out] InitialCommandProcessId: TProcessId; 67 | [out] WindowsSubSysProcessId: TProcessId; 68 | end; 69 | PSmStartCsr = ^TSmStartCsr; 70 | 71 | // PHNT::ntsmss.h 72 | [SDKName('SMSTOPCSR')] 73 | TSmStopCsr = record 74 | [in] MuSessionId: TSessionId; 75 | end; 76 | PSmStopCsr = ^TSmStopCsr; 77 | 78 | // PHNT::ntsmss.h 79 | [SDKName('SMSTARTSERVERSILO')] 80 | TSmStartServerSilo = record 81 | [in] JobHandle: THandle; 82 | [in] CreateSuspended: Boolean; 83 | end; 84 | PSmStartServerSilo = ^TSmStartServerSilo; 85 | 86 | // PHNT::ntsmss.h 87 | [SDKName('SMAPIMSG')] 88 | TSmApiMsg = record 89 | [in] h: TPortMessage; 90 | [in] ApiNumber: TSmApiNumber; 91 | [out] ReturnedStatus: NTSTATUS; 92 | case TSmApiNumber of 93 | SmSessionCompleteApi: (SessionComplete: TSmSessionComplete); 94 | SmExecPgmApi: (ExecPgm: TSmExecPgm); 95 | SmLoadDeferedSubsystemApi: (LoadDefered: TSmLoadDefered); 96 | SmStartCsrApi: (StartCsr: TSmStartCsr); 97 | SmStopCsrApi: (StopCsr: TSmStopCsr); 98 | SmStartServerSiloApi: (StartServerSilo: TSmStartServerSilo); 99 | end; 100 | PSmApiMsg = ^TSmApiMsg; 101 | 102 | // PHNT::ntsmss.h 103 | [SDKName('SBAPINUMBER')] 104 | [NamingStyle(nsCamelCase, 'Sb', 'Api')] 105 | TSbApiNumber = ( 106 | SbCreateSessionApi = 0, 107 | SbTerminateSessionApi = 1, 108 | SbForeignSessionCompleteApi = 2, 109 | SbCreateProcessApi = 3 110 | ); 111 | 112 | // PHNT::ntsmss.h 113 | [SDKName('SBCREATESESSION')] 114 | TSbCreateSession = record 115 | [in] SessionId: TSessionId; 116 | [in] ProcessInformation: TRtlUserProcessInformation; 117 | [in, opt] UserProfile: Pointer; 118 | [in] DebugSession: LongBool; 119 | [in] DebugUiClientId: TClientId; 120 | end; 121 | PSbCreateSession = ^TSbCreateSession; 122 | 123 | [FlagName(SMP_DEBUG_FLAG, 'Debug')] 124 | [FlagName(SMP_ASYNC_FLAG, 'Async')] 125 | [FlagName(SMP_DONT_START, 'Don''t Start')] 126 | TSbCreateProcessInFlags = type Cardinal; 127 | 128 | // PHNT::ntsmss.h 129 | [SDKName('SBCREATEPROCESSIN')] 130 | TSbCreateProcessIn = record 131 | ImageFileName: PNtUnicodeString; 132 | CurrentDirectory: PNtUnicodeString; 133 | CommandLine: PNtUnicodeString; 134 | DefaultLibPath: PNtUnicodeString; 135 | Flags: TSbCreateProcessInFlags; 136 | DefaultDebugFlags: TRtlUserProcessParametersDebugFlags; 137 | end; 138 | 139 | // PHNT::ntsmss.h 140 | [SDKName('SBCREATEPROCESSOUT')] 141 | TSbCreateProcessOut = record 142 | Process: THandle; 143 | Thread: THandle; 144 | SubSystemType: TImageSubsystem; 145 | ClientId: TClientId; 146 | end; 147 | 148 | // PHNT::ntsmss.h 149 | [SDKName('SBCREATEPROCESS')] 150 | TSbCreateProcess = record 151 | case Integer of 152 | 0: (i: TSbCreateProcessIn); 153 | 1: (o: TSbCreateProcessOut) 154 | end; 155 | PSbCreateProcess = ^TSbCreateProcess; 156 | 157 | // PHNT::ntsmss.h 158 | [SDKName('SBAPIMSG')] 159 | TSbApiMsg = record 160 | h: TPortMessage; 161 | ApiNumber: TSbApiNumber; 162 | ReturnedStatus: NTSTATUS; 163 | case TSbApiNumber of 164 | SbCreateSessionApi: (CreateSession: TSbCreateSession); 165 | SbCreateProcessApi: (CreateProcessA: TSbCreateProcess); 166 | end; 167 | PSbApiMsg = ^TSbApiMsg; 168 | 169 | // PHNT::ntsmss.h 170 | function RtlConnectToSm( 171 | [in, opt] ApiPortName: PNtUnicodeString; 172 | [in, opt] ApiPortHandle: THandle; 173 | [in] ProcessImageType: Cardinal; 174 | [out, ReleaseWith('NtClose')] out SmssConnection: THandle 175 | ): NTSTATUS; stdcall; external ntdll; 176 | 177 | // PHNT::ntsmss.h 178 | function RtlSendMsgToSm( 179 | [in] ApiPortHandle: THandle; 180 | [in] const MessageData: TPortMessage 181 | ): NTSTATUS; stdcall; external ntdll; 182 | 183 | implementation 184 | 185 | {$BOOLEVAL OFF} 186 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 187 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 188 | 189 | end. 190 | -------------------------------------------------------------------------------- /Headers/Ntapi.nttp.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.nttp; 2 | 3 | { 4 | This module provides functions for using Worker Factories (aka Thread Pool). 5 | } 6 | 7 | interface 8 | 9 | {$MINENUMSIZE 4} 10 | 11 | uses 12 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.ntioapi, DelphiApi.Reflection; 13 | 14 | const 15 | // PHNT::ntexapi.h 16 | WORKER_FACTORY_RELEASE_WORKER = $0001; 17 | WORKER_FACTORY_WAIT = $0002; 18 | WORKER_FACTORY_SET_INFORMATION = $0004; 19 | WORKER_FACTORY_QUERY_INFORMATION = $0008; 20 | WORKER_FACTORY_READY_WORKER = $0010; 21 | WORKER_FACTORY_SHUTDOWN = $0020; 22 | 23 | WORKER_FACTORY_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or $3F; 24 | 25 | type 26 | [FriendlyName('worker factory'), ValidBits(WORKER_FACTORY_ALL_ACCESS)] 27 | [SubEnum(WORKER_FACTORY_ALL_ACCESS, WORKER_FACTORY_ALL_ACCESS, 'Full Access')] 28 | [FlagName(WORKER_FACTORY_RELEASE_WORKER, 'Release Worker')] 29 | [FlagName(WORKER_FACTORY_WAIT, 'Wait')] 30 | [FlagName(WORKER_FACTORY_SET_INFORMATION, 'Set Information')] 31 | [FlagName(WORKER_FACTORY_QUERY_INFORMATION, 'Query Information')] 32 | [FlagName(WORKER_FACTORY_READY_WORKER, 'Ready Worker')] 33 | [FlagName(WORKER_FACTORY_SHUTDOWN, 'Shutdown')] 34 | [InheritsFrom(System.TypeInfo(TAccessMask))] 35 | TWorkerFactoryAccessMask = type TAccessMask; 36 | 37 | TWorkerFactoryRoutine = procedure ( 38 | [in, opt] StartParameter: Pointer 39 | ); stdcall; 40 | 41 | // PHNT::ntexapi.h 42 | [SDKName('WORKERFACTORYINFOCLASS')] 43 | [NamingStyle(nsCamelCase, 'WorkerFactory')] 44 | TWorkerFactoryInfoClass = ( 45 | WorkerFactoryTimeout = 0, // 46 | WorkerFactoryRetryTimeout = 1, // 47 | WorkerFactoryIdleTimeout = 2, // s: TLargeInteger 48 | WorkerFactoryBindingCount = 3, // s: Cardinal 49 | WorkerFactoryThreadMinimum = 4, // s: Cardinal 50 | WorkerFactoryThreadMaximum = 5, // s: Cardinal 51 | WorkerFactoryPaused = 6, // 52 | WorkerFactoryBasicInformation = 7, // q: TWorkerFactoryBasicInformation 53 | WorkerFactoryAdjustThreadGoal = 8, // s: Cardinal 54 | WorkerFactoryCallbackType = 9, // s: 55 | WorkerFactoryStackInformation = 10, // s: 56 | WorkerFactoryThreadBasePriority = 11, // s: TPriority 57 | WorkerFactoryTimeoutWaiters = 12, // s: Cardinal, Win 10 TH1+ 58 | WorkerFactoryFlags = 13, // s: Cardinal 59 | WorkerFactoryThreadSoftMaximum = 14, // s: Cardinal 60 | WorkerFactoryThreadCpuSets = 15 // s: ?, Win 10 RS5+ 61 | ); 62 | 63 | // PHNT::ntexapi.h 64 | [SDKName('WORKER_FACTORY_BASIC_INFORMATION')] 65 | TWorkerFactoryBasicInformation = record 66 | Timeout: TLargeInteger; 67 | RetryTimeout: TLargeInteger; 68 | IdleTimeout: TLargeInteger; 69 | Paused: Boolean; 70 | TimerSet: Boolean; 71 | QueuedToExWorker: Boolean; 72 | MayCreate: Boolean; 73 | CreateInProgress: Boolean; 74 | InsertedIntoQueue: Boolean; 75 | Shutdown: Boolean; 76 | BindingCount: Cardinal; 77 | ThreadMinimum: Cardinal; 78 | ThreadMaximum: Cardinal; 79 | PendingWorkerCount: Cardinal; 80 | WaitingWorkerCount: Cardinal; 81 | TotalWorkerCount: Cardinal; 82 | ReleaseCount: Cardinal; 83 | InfiniteWaitGoal: Int64; 84 | StartRoutine: Pointer; 85 | StartParameter: Pointer; 86 | ProcessID: TProcessId; 87 | StackReserve: NativeUInt; 88 | StackCommit: NativeUInt; 89 | LastThreadCreationStatus: NTSTATUS; 90 | end; 91 | 92 | // Worker Factory 93 | 94 | // PHNT::ntexapi.h 95 | function NtCreateWorkerFactory( 96 | [out, ReleaseWith('NtClose')] out WorkerFactoryHandle: THandle; 97 | [in] DesiredAccess: TWorkerFactoryAccessMask; 98 | [in, opt] ObjectAttributes: PObjectAttributes; 99 | [in, Access(IO_COMPLETION_MODIFY_STATE)] CompletionPortHandle: THandle; 100 | [in, Access(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or 101 | PROCESS_VM_WRITE)] WorkerProcessHandle: THandle; // Current process only 102 | [in] StartRoutine: TWorkerFactoryRoutine; 103 | [in, opt] StartParameter: Pointer; 104 | [in] MaxThreadCount: Cardinal; 105 | [in, opt] StackReserve: NativeUInt; 106 | [in, opt] StackCommit: NativeUInt 107 | ): NTSTATUS; stdcall; external ntdll; 108 | 109 | // PHNT::ntexapi.h 110 | function NtQueryInformationWorkerFactory( 111 | [in, Access(WORKER_FACTORY_QUERY_INFORMATION)] WorkerFactoryHandle: THandle; 112 | [in] WorkerFactoryInformationClass: TWorkerFactoryInfoClass; 113 | [out, WritesTo] WorkerFactoryInformation: Pointer; 114 | [in, NumberOfBytes] WorkerFactoryInformationLength: Cardinal; 115 | [out, opt, NumberOfBytes] ReturnLength: PCardinal 116 | ): NTSTATUS; stdcall; external ntdll; 117 | 118 | // PHNT::ntexapi.h 119 | function NtSetInformationWorkerFactory( 120 | [in, Access(WORKER_FACTORY_SET_INFORMATION)] WorkerFactoryHandle: THandle; 121 | [in] WorkerFactoryInformationClass: TWorkerFactoryInfoClass; 122 | [in, ReadsFrom] WorkerFactoryInformation: Pointer; 123 | [in, NumberOfBytes] WorkerFactoryInformationLength: Cardinal 124 | ): NTSTATUS; stdcall; external ntdll; 125 | 126 | // PHNT::ntexapi.h 127 | function NtShutdownWorkerFactory( 128 | [in, Access(WORKER_FACTORY_SHUTDOWN)] WorkerFactoryHandle: THandle; 129 | [in, out] var PendingWorkerCount: Integer 130 | ): NTSTATUS; stdcall; external ntdll; 131 | 132 | // PHNT::ntexapi.h 133 | function NtReleaseWorkerFactoryWorker( 134 | [in, Access(WORKER_FACTORY_RELEASE_WORKER)] WorkerFactoryHandle: THandle 135 | ): NTSTATUS; stdcall; external ntdll; 136 | 137 | // PHNT::ntexapi.h 138 | function NtWorkerFactoryWorkerReady( 139 | [in, Access(WORKER_FACTORY_READY_WORKER)] WorkerFactoryHandle: THandle 140 | ): NTSTATUS; stdcall; external ntdll; 141 | 142 | // PHNT::ntexapi.h 143 | function NtWaitForWorkViaWorkerFactory( 144 | [in, Access(WORKER_FACTORY_WAIT)] WorkerFactoryHandle: THandle; 145 | [out] out MiniPacket: TFileIoCompletionInformation 146 | ): NTSTATUS; stdcall; external ntdll; 147 | 148 | implementation 149 | 150 | {$BOOLEVAL OFF} 151 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 152 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 153 | 154 | end. 155 | -------------------------------------------------------------------------------- /Headers/Ntapi.wincred.pas: -------------------------------------------------------------------------------- 1 | unit Ntapi.wincred; 2 | 3 | { 4 | This module provides definitions for credentials API. 5 | } 6 | 7 | interface 8 | 9 | {$WARN SYMBOL_PLATFORM OFF} 10 | {$MINENUMSIZE 4} 11 | 12 | uses 13 | Ntapi.WinNt, Ntapi.WinUser, DelphiApi.Reflection, DelphiApi.DelayLoad; 14 | 15 | const 16 | credui = 'credui.dll'; 17 | 18 | var 19 | delayed_credui: TDelayedLoadDll = (DllName: credui); 20 | 21 | const 22 | // SDK:wincred.h 23 | CREDUIWIN_GENERIC = $00000001; 24 | CREDUIWIN_CHECKBOX = $00000002; 25 | CREDUIWIN_AUTHPACKAGE_ONLY = $00000010; 26 | CREDUIWIN_IN_CRED_ONLY = $00000020; 27 | CREDUIWIN_ENUMERATE_ADMINS = $00000100; 28 | CREDUIWIN_ENUMERATE_CURRENT_USER = $00000200; 29 | CREDUIWIN_SECURE_PROMPT = $00001000; 30 | CREDUIWIN_PREPROMPTING = $00002000; 31 | CREDUIWIN_PACK_32_WOW = $10000000; 32 | CREDUIWIN_IGNORE_CLOUDAUTHORITY_NAME = $00040000; // Win 10 RS1+ 33 | CREDUIWIN_DOWNLEVEL_HELLO_AS_SMART_CARD = $80000000; // Win 10 RS5+ 34 | 35 | // SDK:wincred.h 36 | CRED_PACK_PROTECTED_CREDENTIALS = $01; 37 | CRED_PACK_WOW_BUFFER = $02; 38 | CRED_PACK_GENERIC_CREDENTIALS = $04; 39 | CRED_PACK_ID_PROVIDER_CREDENTIALS = $08; 40 | 41 | type 42 | [FlagName(CREDUIWIN_GENERIC, 'Generic')] 43 | [FlagName(CREDUIWIN_CHECKBOX, 'Checkbox')] 44 | [FlagName(CREDUIWIN_AUTHPACKAGE_ONLY, 'Auth Package Only')] 45 | [FlagName(CREDUIWIN_IN_CRED_ONLY, 'In Cred. Only')] 46 | [FlagName(CREDUIWIN_ENUMERATE_ADMINS, 'Enumerate Admins')] 47 | [FlagName(CREDUIWIN_ENUMERATE_CURRENT_USER, 'Enumerate Current User')] 48 | [FlagName(CREDUIWIN_SECURE_PROMPT, 'Secure Prompt')] 49 | [FlagName(CREDUIWIN_PREPROMPTING, 'Pre-prompting')] 50 | [FlagName(CREDUIWIN_PACK_32_WOW, 'Pack 32 WoW')] 51 | [FlagName(CREDUIWIN_IGNORE_CLOUDAUTHORITY_NAME, 'Ignore Cloud Authority Name')] 52 | [FlagName(CREDUIWIN_DOWNLEVEL_HELLO_AS_SMART_CARD, 'Downlevel Hello As Smart Card')] 53 | TCredUiWinFlags = type Cardinal; 54 | 55 | [FlagName(CRED_PACK_PROTECTED_CREDENTIALS, 'Protected Credentials')] 56 | [FlagName(CRED_PACK_WOW_BUFFER, 'WoW Buffer')] 57 | [FlagName(CRED_PACK_GENERIC_CREDENTIALS, 'Generic Credentials')] 58 | [FlagName(CRED_PACK_ID_PROVIDER_CREDENTIALS, 'ID Provider Credentials')] 59 | TCredPackFlags = type Cardinal; 60 | 61 | // SDK:wincred.h 62 | [SDKName('CREDUI_INFOW')] 63 | TCredUIInfoW = record 64 | [RecordSize] Size: Cardinal; 65 | Parent: THwnd; 66 | MessageText: PWideChar; 67 | CaptionText: PWideChar; 68 | Banner: THBitmap; 69 | end; 70 | PCredUIInfoW = ^TCredUIInfoW; 71 | 72 | // SDK:wincred.h 73 | function CredUIPromptForWindowsCredentialsW( 74 | [in, opt] UiInfo: PCredUIInfoW; 75 | [in] AuthError: TWin32Error; 76 | [in, out] var AuthPackage: Cardinal; 77 | [in, opt, ReadsFrom] InAuthBuffer: Pointer; 78 | [in, NumberOfBytes] InAuthBufferSize: Cardinal; 79 | [out, WritesTo, ReleaseWith('CoTaskMemFree')] out OutAuthBuffer: Pointer; 80 | [out, NumberOfBytes] out OutAuthBufferSize: Cardinal; 81 | [in, out, opt] Save: PLongBool; 82 | [in] Flags: TCredUiWinFlags 83 | ): TWin32Error; stdcall; external credui delayed; 84 | 85 | var delayed_CredUIPromptForWindowsCredentialsW: TDelayedLoadFunction = ( 86 | Dll: @delayed_credui; 87 | FunctionName: 'CredUIPromptForWindowsCredentialsW'; 88 | ); 89 | 90 | // SDK:wincred.h 91 | function CredUnPackAuthenticationBufferW( 92 | [in] Flags: TCredPackFlags; 93 | [in, ReadsFrom] AuthBuffer: Pointer; 94 | [in, NumberOfBytes] AuthBufferSize: Cardinal; 95 | [out, opt, WritesTo] UserName: PWideChar; 96 | [in, out, NumberOfElements] var MaxUserName: Cardinal; 97 | [out, opt, WritesTo] DomainName: PWideChar; 98 | [in, out, NumberOfElements] var MaxDomainName: Cardinal; 99 | [out, opt, WritesTo] Password: PWideChar; 100 | [in, out, NumberOfElements] var MaxPassword: Cardinal 101 | ): LongBool; stdcall; external credui delayed; 102 | 103 | var delayed_CredUnPackAuthenticationBufferW: TDelayedLoadFunction = ( 104 | Dll: @delayed_credui; 105 | FunctionName: 'CredUnPackAuthenticationBufferW'; 106 | ); 107 | 108 | implementation 109 | 110 | {$BOOLEVAL OFF} 111 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 112 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 113 | 114 | end. 115 | -------------------------------------------------------------------------------- /Headers/Readme.md: -------------------------------------------------------------------------------- 1 | ## Header files 2 | 3 | These files contain **Windows API** and **Native API** definitions adapted to use with Delphi. They include information from multiple sources, as marked per declaration: 4 | 5 | - **SDK** - Windows [Software Development Kit](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/) provides a significant portion of basic types plus non-ntdll function prototypes. 6 | - **WDK** - [Windows Driver Kit](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) provides definitions for various system calls. 7 | - **DDK** - Windows Driver Development Kit is an outdated version of WDK (from the times of Windows 7) that includes a few removed definitions. 8 | - **ADK** - [Windows Assessment and Deployment Kit](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install) includes DISM and WIM API definitions. 9 | - **DIA** - [Debug Interface Access SDK](https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/debug-interface-access-sdk) provides interfaces for working with debug symbols. 10 | - **MSDocs** - [Microsoft Documentation](https://docs.microsoft.com/en-us/windows/) provides a few types and functions that do not appear in the official headers. 11 | - **MS-WINPROTLP** - official [Windows Protocol Specifications](https://docs.microsoft.com/en-us/openspecs/windows_protocols/) that thoroughly document RPC-based functions. 12 | - **PHNT** - [Process Hacker/System Informer Headers](https://github.com/winsiderss/systeminformer/tree/master/phnt/include) that contain a detailed collection of Native API definitions. 13 | - **ReactOS** - an open-source [operating system](https://github.com/reactos/reactos) that is binary compatible with Windows. 14 | - **NtApiDotNet** - [a .NET library for system programming](https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools/tree/master/NtApiDotNet). 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 diversenok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NtUiLib.Errors.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.Errors; 2 | 3 | { 4 | This module adds support for representing error codes as constant names, 5 | short summaries, and long descriptions. 6 | } 7 | 8 | interface 9 | 10 | {$RESOURCE NtUiLib.Errors.res} 11 | 12 | uses 13 | Ntapi.ntdef, NtUtils; 14 | 15 | type 16 | TNtxStatusHelper = record helper for TNtxStatus 17 | // Provide textual representation of the error 18 | function Name: String; 19 | function Description: String; 20 | function Summary: String; 21 | function ToString: String; 22 | end; 23 | 24 | // Find a constant name (like STATUS_ACCESS_DENIED) for an error 25 | function RtlxNtStatusName(Status: NTSTATUS): String; 26 | 27 | // Find a short failure description (like "Access Denied") for an error 28 | function RtlxNtStatusSummary(Status: NTSTATUS): String; 29 | 30 | // Find a description for an NTSTATUS, HRESULT, or Win32 error 31 | function RtlxNtStatusMessage(Status: NTSTATUS): String; 32 | 33 | implementation 34 | 35 | uses 36 | Ntapi.WinNt, Ntapi.ntldr, Ntapi.WinError, Ntapi.wimgapi, NtUtils.Ldr, 37 | NtUtils.SysUtils, NtUtils.Errors, DelphiUiLib.Strings, DelphiApi.DelayLoad; 38 | 39 | {$BOOLEVAL OFF} 40 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 41 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 42 | 43 | function RemoveSummaryAndNewLines(const Source: String): String; 44 | var 45 | StartIndex, EndIndex: Integer; 46 | begin 47 | // Skip leading summary in curly brackets for messages that look like: 48 | // {Summary} 49 | // Message description. 50 | StartIndex := Low(Source); 51 | 52 | if (Length(Source) > 0) and (Source[Low(Source)] = '{') then 53 | StartIndex := Pos('}'#$D#$A, Source) + 3; 54 | 55 | // Remove trailing new lines 56 | EndIndex := High(Source); 57 | while (EndIndex >= Low(Source)) and (AnsiChar(Source[EndIndex]) in 58 | [#$D, #$A]) do 59 | Dec(EndIndex); 60 | 61 | Result := Copy(Source, StartIndex, EndIndex - StartIndex + 1); 62 | end; 63 | 64 | function RtlxNtStatusName; 65 | begin 66 | // Use embedded resource to locate the constant name 67 | if RtlxFindMessage(Result, Pointer(@ImageBase), 68 | Status.Canonicalize).IsSuccess then 69 | Result := RemoveSummaryAndNewLines(Result) 70 | else 71 | begin 72 | // No name available. Prepare a numeric value. 73 | if Status.IsWin32Error then 74 | Result := RtlxUIntToStr(Status.ToWin32Error) + ' [Win32]' 75 | else if Status.IsHResult then 76 | Result := RtlxUIntToStr(Cardinal(Status.ToHResult), nsHexadecimal, 8) + 77 | ' [HRESULT]' 78 | else 79 | Result := RtlxUIntToStr(Status, nsHexadecimal, 8) + ' [NTSTATUS]'; 80 | end; 81 | end; 82 | 83 | function RtlxNtStatusSummary; 84 | const 85 | KnownPrefixes: array [0 .. 97] of String = ('STATUS_VOLMGR_', 86 | 'STATUS_XMLLITE_', 'STATUS_LOG_', 'STATUS_DS_', 'STATUS_CTX_', 87 | 'STATUS_SXS_', 'STATUS_VHD_', 'STATUS_FLT_', 'STATUS_CLOUD_FILE_', 88 | 'STATUS_SMARTCARD_', 'STATUS_APPEXEC_', 'STATUS_SYSTEM_INTEGRITY_', 89 | 'STATUS_WX86_', 'STATUS_WMI_', 'STATUS_DIF_', 'STATUS_FT_', 'STATUS_VRF_', 90 | 'STATUS_PNP_', 'STATUS_THREADPOOL_', 'STATUS_VIRTDISK_', 'STATUS_RXACT_', 91 | 'STATUS_KDC_', 'STATUS_CTLOG_', 'STATUS_CS_ENCRYPTION_', 92 | 'STATUS_TRANSACTIONMANAGER_', 'STATUS_RWRAW_ENCRYPTED_', 'STATUS_BCD_', 93 | 'STATUS_', 'ERROR_VOLMGR_', 'ERROR_SXS_XML_E_', 'ERROR_SXS_', 'ERROR_LOG_', 94 | 'ERROR_CTX_', 'ERROR_INSTALL_', 'ERROR_EVT_', 'ERROR_VHD_', 95 | 'ERROR_CLOUD_FILE_', 'ERROR_MRM_', 'ERROR_STATE_', 'ERROR_WMI_', 96 | 'ERROR_DBG_', 'ERROR_APPEXEC_', 'ERROR_SYSTEM_INTEGRITY_', 97 | 'ERROR_PRI_MERGE_', 'ERROR_CAPAUTHZ_', 'ERROR_FT_', 'ERROR_VIRTDISK_', 98 | 'ERROR_RXACT_', 'ERROR_CTLOG_', 'ERROR_CS_ENCRYPTION_', 99 | 'ERROR_RWRAW_ENCRYPTED_', 'ERROR_WOF_', 'ERROR_EDP_', 'ERROR_BCD_', 100 | 'ERROR_', 'CO_E_', 'RPC_NT_', 'RPC_S_', 'SEC_E_', 'RPC_E_', 'STG_E_', 101 | 'SCHED_E_', 'E_', 'APPX_E_', 'RPC_X_', 'OLE_E_', 'DISP_E_', 'DISMAPI_E_', 102 | 'MK_E_', 'EVENT_E_', 'DBG_', 'SEC_I_', 'RO_E_', 'WER_S_', 'SCHED_S_', 103 | 'CS_E_', 'CONTEXT_E_', 'WER_E_', 'STG_S_', 'REGDB_E_', 'APPMODEL_ERROR_', 104 | 'DWM_E_', 'MK_S_', 'CLIPBRD_E_', 'STORE_ERROR_', 'S_', 'EPT_S_', 'EPT_NT_', 105 | 'OR_', 'OLEOBJ_S_', 'OLE_S_', 'MEM_E_', 'CLASS_E_', 'OLEOBJ_E_', 'EVENT_S_', 106 | 'DWM_S_', 'CO_S_', 'CAT_E_'); 107 | var 108 | Prefix: String; 109 | begin 110 | // Use embedded resource to locate the constant name 111 | if not RtlxFindMessage(Result, Pointer(@ImageBase), 112 | Status.Canonicalize).IsSuccess then 113 | Exit('System Error'); 114 | 115 | Result := RemoveSummaryAndNewLines(Result); 116 | 117 | // Skip known constant prefixes 118 | for Prefix in KnownPrefixes do 119 | if RtlxPrefixStripString(Prefix, Result, True) then 120 | Break; 121 | 122 | // Convert names from looking like "ACCESS_DENIED" to "Access Denied" 123 | Result := PrettifySnakeCase(Result); 124 | end; 125 | 126 | function RtlxNtStatusMessage; 127 | var 128 | Module: PDelayedLoadDll; 129 | Code: Cardinal; 130 | begin 131 | if Status.IsHResult and (NT_FACILITY(Status) = FACILITY_WIM) then 132 | begin 133 | // WIM codes are HRESULTs with messages in wimgapi 134 | Module := @delayed_wimgapi; 135 | HResult(Code) := Status.ToHResult; 136 | end 137 | else if Status.IsWin32Error or Status.IsHResult then 138 | begin 139 | // Win32 errors and HRESULT code messages are in kernel32 140 | Module := @delayed_kernel32; 141 | Code := Status.ToWin32Error; 142 | end 143 | else 144 | begin 145 | // Other NTSTATUS messages are in ntdll 146 | Module := @delayed_ntdll; 147 | Code := Status; 148 | end; 149 | 150 | // Load the message 151 | if LdrxCheckDelayedModule(Module^).IsSuccess and 152 | RtlxFindMessage(Result, Module.DllAddress, Code).IsSuccess then 153 | Result := RemoveSummaryAndNewLines(Result) 154 | else 155 | Result := ''; 156 | 157 | if Result = '' then 158 | Result := ''; 159 | end; 160 | 161 | { TNtxStatusHelper } 162 | 163 | function TNtxStatusHelper.Description; 164 | begin 165 | Result := RtlxNtStatusMessage(Status); 166 | end; 167 | 168 | function TNtxStatusHelper.Name; 169 | begin 170 | Result := RtlxNtStatusName(Status); 171 | end; 172 | 173 | function TNtxStatusHelper.Summary; 174 | begin 175 | Result := RtlxNtStatusSummary(Status); 176 | end; 177 | 178 | function TNtxStatusHelper.ToString; 179 | begin 180 | Result := Location; 181 | 182 | if Result = '' then 183 | Result := 'Function'; 184 | 185 | Result := Result + ' returned ' + Name; 186 | end; 187 | 188 | initialization 189 | RtlxNtStatusRepresenter := RtlxNtStatusName; 190 | end. 191 | -------------------------------------------------------------------------------- /NtUiLib.Errors.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diversenok/NtUtilsLibrary/7e01c0b21491f1e66c33d447acc47c01b1a630f2/NtUiLib.Errors.res -------------------------------------------------------------------------------- /NtUiLib.Exceptions.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.Exceptions; 2 | 3 | { 4 | This module adds support for raising unsuccessful error codes as Delphi 5 | exceptions. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | NtUtils, System.SysUtils; 12 | 13 | type 14 | // An exception type thrown by RaiseOnError method of TNtxStatus 15 | ENtError = class(EOSError) 16 | private 17 | xStatus: TNtxStatus; 18 | public 19 | constructor Create(const Status: TNtxStatus); 20 | property NtxStatus: TNtxStatus read xStatus; 21 | end; 22 | 23 | // Make a TNtxStatus containing exception information 24 | function CaptureExceptionToNtxStatus(E: Exception): TNtxStatus; 25 | 26 | implementation 27 | 28 | uses 29 | Ntapi.ntstatus, NtUiLib.Errors, NtUtils.Ldr, NtUtils.DbgHelp; 30 | 31 | {$BOOLEVAL OFF} 32 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 33 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 34 | 35 | { ENtError } 36 | 37 | constructor ENtError.Create; 38 | begin 39 | xStatus := Status; 40 | ErrorCode := Cardinal(Status.Win32Error); 41 | Message := Status.ToString; 42 | end; 43 | 44 | // A callback for raising NT exceptions via Status.RaiseOnError; 45 | procedure NtxUiLibExceptionRaiser(const Status: TNtxStatus); 46 | begin 47 | raise ENtError.Create(Status); 48 | end; 49 | 50 | { Capturing } 51 | 52 | function CaptureExceptionToNtxStatus; 53 | begin 54 | if E is ENtError then 55 | Result := ENtError(E).NtxStatus 56 | else 57 | begin 58 | Result.Location := E.ClassName; 59 | Result.LastCall.Parameter := E.Message; 60 | 61 | if E is EOSError then 62 | Result.Win32Error := EOSError(E).ErrorCode 63 | else if E is EAccessViolation then 64 | Result.Status := STATUS_ACCESS_VIOLATION 65 | else if E is EOutOfMemory then 66 | Result.Status := STATUS_NO_MEMORY 67 | else if (E is EArgumentException) or (E is EArgumentOutOfRangeException) or 68 | (E is EArgumentNilException) then 69 | Result.Status := STATUS_INVALID_PARAMETER 70 | else if E is ENotSupportedException then 71 | Result.Status := STATUS_NOT_SUPPORTED 72 | else if E is ENotImplemented then 73 | Result.Status := STATUS_NOT_IMPLEMENTED 74 | else if (E is EAbort) or (E is EOperationCancelled) then 75 | Result.Status := STATUS_CANCELLED 76 | else if (E is EDirectoryNotFoundException) or (E is EFileNotFoundException) 77 | or (E is EPathNotFoundException) then 78 | Result.Status := STATUS_NOT_FOUND 79 | else if E is EPathTooLongException then 80 | Result.Status := STATUS_NAME_TOO_LONG 81 | else if (E is EDivByZero) or (E is EZeroDivide) then 82 | Result.Status := STATUS_FLOAT_DIVIDE_BY_ZERO 83 | else if E is ERangeError then 84 | Result.Status := STATUS_ARRAY_BOUNDS_EXCEEDED 85 | else if E is EIntOverflow then 86 | Result.Status := STATUS_INTEGER_OVERFLOW 87 | else 88 | Result.Status := STATUS_UNHANDLED_EXCEPTION; 89 | end; 90 | end; 91 | 92 | { Stack Trace Support } 93 | 94 | // A callback for capturing the stack trace when an exception occurs 95 | function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer; 96 | var 97 | Trace: TArray absolute Result; 98 | i: Integer; 99 | begin 100 | // Clean-up before assigning 101 | Result := nil; 102 | 103 | // Capture the backtrace 104 | Trace := RtlxCaptureStackTrace; 105 | 106 | // Trim it by removing exception-handling frames 107 | for i := 0 to High(Trace) do 108 | if Trace[i] = P.ExceptionAddress then 109 | begin 110 | Delete(Trace, 0, i); 111 | Break; 112 | end; 113 | end; 114 | 115 | // A callback for representing the stack trace 116 | function GetStackInfoStringProc(Info: Pointer): string; 117 | var 118 | Trace: TArray absolute Info; 119 | Modules: TArray; 120 | Frames: TArray; 121 | i: Integer; 122 | begin 123 | if not LdrxEnumerateModuleInfo(Modules).IsSuccess then 124 | Modules := nil; 125 | 126 | SetLength(Frames, Length(Trace)); 127 | 128 | for i := 0 to High(Trace) do 129 | Frames[i] := SymxFindBestMatch(Modules, Trace[i]).ToString; 130 | 131 | Result := String.Join(#$D#$A, Frames); 132 | end; 133 | 134 | procedure CleanUpStackInfoProc(Info: Pointer); 135 | var 136 | Trace: TArray absolute Info; 137 | begin 138 | Finalize(Trace); 139 | end; 140 | 141 | initialization 142 | TNtxStatus.NtxExceptionRaiser := NtxUiLibExceptionRaiser; 143 | 144 | // Add support for exception stack-tracing 145 | if not Assigned(@Exception.GetExceptionStackInfoProc) then 146 | begin 147 | Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc; 148 | Exception.GetStackInfoStringProc := GetStackInfoStringProc; 149 | Exception.CleanUpStackInfoProc := CleanUpStackInfoProc; 150 | end; 151 | end. 152 | -------------------------------------------------------------------------------- /NtUiLib/DelphiUiLib.Reflection.Records.pas: -------------------------------------------------------------------------------- 1 | unit DelphiUiLib.Reflection.Records; 2 | 3 | { 4 | This module allows traversing fields in records and representing each of them 5 | as a string using Runtime Type Information. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | DelphiUiLib.Reflection; 12 | 13 | type 14 | TFieldReflection = record 15 | FieldName: String; 16 | Offset: IntPtr; 17 | Reflection: TRepresentation; 18 | end; 19 | 20 | TFieldReflectionCallback = reference to procedure ( 21 | const Field: TFieldReflection 22 | ); 23 | 24 | TFieldReflectionOptions = set of ( 25 | foIncludeUntyped, 26 | foIncludeUnlisted, 27 | foIncludeInternal // i.e., offets and record size fields 28 | ); 29 | 30 | // Introspect a record type traversing its fields via TypeInfo 31 | procedure TraverseFields( 32 | AType: Pointer; 33 | const Instance; 34 | Callback: TFieldReflectionCallback; 35 | Options: TFieldReflectionOptions = [] 36 | ); 37 | 38 | type 39 | TRecord = class abstract 40 | // Introspect a record type traversing its fields via generic method 41 | class procedure Traverse( 42 | const Instance: T; 43 | Callback: TFieldReflectionCallback; 44 | Options: TFieldReflectionOptions = [] 45 | ); static; 46 | end; 47 | 48 | implementation 49 | 50 | uses 51 | System.Rtti, DelphiApi.Reflection, Ntapi.Versions; 52 | 53 | {$BOOLEVAL OFF} 54 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 55 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 56 | 57 | procedure ExtractReferredType( 58 | var RttiType: TRttiType; 59 | var pInstance: Pointer 60 | ); 61 | begin 62 | // Use the underlying type for pointer types 63 | if (RttiType is TRttiPointerType) and 64 | Assigned(TRttiPointerType(RttiType).ReferredType) then 65 | begin 66 | RttiType := TRttiPointerType(RttiType).ReferredType; 67 | 68 | if Assigned(pInstance) then 69 | pInstance := Pointer(pInstance^); 70 | end; 71 | end; 72 | 73 | procedure TraverseRttiFields( 74 | RttiType: TRttiType; 75 | pInstance: Pointer; 76 | const Callback: TFieldReflectionCallback; 77 | Options: TFieldReflectionOptions; 78 | AggregationOffset: IntPtr 79 | ); 80 | var 81 | RttiField: TRttiField; 82 | FieldInfo: TFieldReflection; 83 | pField: Pointer; 84 | Attributes: TArray; 85 | a: TCustomAttribute; 86 | Unlisted, Internal, Aggregate: Boolean; 87 | OsVersion: TWindowsVersion; 88 | MinVersion: MinOSVersionAttribute; 89 | begin 90 | // Pointers to records do not have any fields. If the passed type is PRecord, 91 | // dereference it, and use TRecord to access the fields 92 | ExtractReferredType(RttiType, pInstance); 93 | 94 | OsVersion := RtlOsVersion; 95 | 96 | for RttiField in RttiType.GetFields do 97 | begin 98 | FieldInfo.FieldName := RttiField.Name; 99 | FieldInfo.Offset := AggregationOffset + RttiField.Offset; 100 | FieldInfo.Reflection.Text := ''; 101 | FieldInfo.Reflection.Hint := ''; 102 | 103 | Internal := False; 104 | Unlisted := False; 105 | Aggregate := False; 106 | MinVersion := nil; 107 | Attributes := RttiField.GetAttributes; 108 | 109 | // Find known field attributes 110 | for a in Attributes do 111 | begin 112 | Unlisted := Unlisted or (a is UnlistedAttribute); 113 | Internal := Internal or (a is RecordSizeAttribute) or 114 | (a is OffsetAttribute); 115 | Aggregate := Aggregate or (a is AggregateAttribute); 116 | 117 | if a is MinOSVersionAttribute then 118 | MinVersion := MinOSVersionAttribute(a); 119 | end; 120 | 121 | // Skip unlisted 122 | if Unlisted and not (foIncludeUnlisted in Options) then 123 | Continue; 124 | 125 | // Skip internal 126 | if Internal and not (foIncludeInternal in Options) then 127 | Continue; 128 | 129 | // Skip fields that require a newer OS than we run on 130 | if Assigned(MinVersion) and not (MinVersion.Version <= OsVersion) then 131 | Continue; 132 | 133 | // Can't reflect on fields without a known type 134 | if not Assigned(RttiField.FieldType) then 135 | begin 136 | if foIncludeUntyped in Options then 137 | Callback(FieldInfo); 138 | Continue; 139 | end; 140 | 141 | if Assigned(pInstance) then 142 | pField := PByte(pInstance) + RttiField.Offset 143 | else 144 | pField := nil; // In case we traverse without an instance 145 | 146 | // Perform aggregation 147 | if Aggregate then 148 | begin 149 | TraverseRttiFields(RttiField.FieldType, pField, Callback, 150 | Options, RttiField.Offset); 151 | Continue; 152 | end; 153 | 154 | if Assigned(pField) then 155 | FieldInfo.Reflection := RepresentRttiType(TRttiContext.Create, 156 | RttiField.FieldType, pField^, Attributes) 157 | else 158 | FieldInfo.Reflection.Text := 'Unknown'; 159 | 160 | Callback(FieldInfo); 161 | end; 162 | end; 163 | 164 | procedure TraverseFields; 165 | var 166 | RttiContext: TRttiContext; 167 | begin 168 | RttiContext := TRttiContext.Create; 169 | 170 | TraverseRttiFields(RttiContext.GetType(AType), @Instance, Callback, Options, 171 | 0); 172 | end; 173 | 174 | { TRecord } 175 | 176 | class procedure TRecord.Traverse; 177 | begin 178 | TraverseFields(TypeInfo(T), Instance, Callback, Options); 179 | end; 180 | 181 | end. 182 | -------------------------------------------------------------------------------- /NtUiLib/NtUiLib.AutoCompletion.Sid.Capabilities.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diversenok/NtUtilsLibrary/7e01c0b21491f1e66c33d447acc47c01b1a630f2/NtUiLib/NtUiLib.AutoCompletion.Sid.Capabilities.res -------------------------------------------------------------------------------- /NtUiLib/NtUiLib.AutoCompletion.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.AutoCompletion; 2 | 3 | { 4 | The module provides functions for creating custom auto-completion lists 5 | similar to those created by SHAutoComplete. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.WinUser, Ntapi.Shlwapi, Ntapi.ObjBase, NtUtils; 12 | 13 | type 14 | TExpandProvider = reference to function ( 15 | const Root: String; 16 | out Suggestions: TArray 17 | ): TNtxStatus; 18 | 19 | // Add a static list of suggestions to an Edit-derived control. 20 | [RequiresCOM] 21 | function ShlxEnableStaticSuggestions( 22 | EditControl: THwnd; 23 | const Strings: TArray; 24 | Options: Cardinal = ACO_AUTOSUGGEST or ACO_UPDOWNKEYDROPSLIST 25 | ): TNtxStatus; 26 | 27 | // Register dynamic (hierarchical) suggestions for an Edit-derived control. 28 | [RequiresCOM] 29 | function ShlxEnableDynamicSuggestions( 30 | EditControl: THwnd; 31 | Provider: TExpandProvider; 32 | Options: Cardinal = ACO_AUTOSUGGEST or ACO_UPDOWNKEYDROPSLIST 33 | ): TNtxStatus; 34 | 35 | implementation 36 | 37 | uses 38 | Ntapi.WinNt, Ntapi.ObjIdl, Ntapi.WinError, Ntapi.ShellApi, NtUtils.WinUser, 39 | DelphiApi.Reflection, NtUtils.Errors, NtUtils.Com; 40 | 41 | {$BOOLEVAL OFF} 42 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 43 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 44 | 45 | type 46 | TStringEnumerator = class(TInterfacedObject, IEnumString, IACList) 47 | private 48 | function Next( 49 | [in, NumberOfElements] Count: Integer; 50 | [out, WritesTo, ReleaseWith('CoTaskMemFree')] out Elements: 51 | TAnysizeArray; 52 | [out, NumberOfElements] out Fetched: Integer 53 | ): HResult; stdcall; 54 | 55 | function Skip( 56 | [in, NumberOfElements] Count: Integer 57 | ): HResult; stdcall; 58 | 59 | function Reset( 60 | ): HResult; stdcall; 61 | 62 | function Clone( 63 | [out] out Enm: IEnumString 64 | ): HResult; stdcall; 65 | 66 | function Expand(Root: PWideChar): HResult; stdcall; 67 | protected 68 | EditControl: THwnd; 69 | Provider: TExpandProvider; 70 | Strings: TArray; 71 | Index: Integer; 72 | constructor CreateCopy(Source: TStringEnumerator); 73 | public 74 | constructor CreateStatic(EditControl: THwnd; Strings: TArray); 75 | constructor CreateDynamic(EditControl: THwnd; Provider: TExpandProvider); 76 | end; 77 | 78 | { TStringEnumerator } 79 | 80 | function TStringEnumerator.Clone; 81 | begin 82 | Enm := TStringEnumerator.CreateCopy(Self); 83 | Result := S_OK; 84 | end; 85 | 86 | constructor TStringEnumerator.CreateCopy; 87 | begin 88 | inherited Create; 89 | EditControl := Source.EditControl; 90 | Provider := Source.Provider; 91 | Strings := Source.Strings; 92 | Index := Source.Index; 93 | end; 94 | 95 | constructor TStringEnumerator.CreateDynamic; 96 | begin 97 | inherited Create; 98 | Self.EditControl := EditControl; 99 | Self.Provider := Provider; 100 | end; 101 | 102 | constructor TStringEnumerator.CreateStatic; 103 | begin 104 | inherited Create; 105 | Self.EditControl := EditControl; 106 | Self.Strings := Strings; 107 | end; 108 | 109 | function TStringEnumerator.Expand; 110 | begin 111 | // Use the callback to enumerate suggestions in a hierarchy 112 | if Assigned(Provider) then 113 | Result := Provider(String(Root), Strings).HResult 114 | else 115 | Result := S_FALSE; 116 | end; 117 | 118 | function TStringEnumerator.Next; 119 | var 120 | i: Integer; 121 | Buffer: PWideChar; 122 | begin 123 | i := 0; 124 | 125 | // Return strings until we satisfy the count or have nothing left 126 | while (i < Count) and (Index <= High(Strings)) do 127 | begin 128 | // The caller is responsible for freeing each string 129 | Buffer := CoTaskMemAlloc(StringSizeZero(Strings[Index])); 130 | 131 | if not Assigned(Buffer) then 132 | Exit(TWin32Error(ERROR_NOT_ENOUGH_MEMORY).ToHResult); 133 | 134 | MarshalString(Strings[Index], Buffer); 135 | Elements{$R-}[i]{$IFDEF R+}{$R+}{$ENDIF} := Buffer; 136 | Inc(i); 137 | Inc(Index); 138 | end; 139 | 140 | Fetched := i; 141 | 142 | if i = Count then 143 | Result := S_OK 144 | else 145 | Result := S_FALSE; 146 | end; 147 | 148 | function TStringEnumerator.Reset; 149 | var 150 | CurrentText: String; 151 | begin 152 | // For some reason, AutoComplete does not call Expand on the root; fix it. 153 | if UsrxGetWindowText(EditControl, CurrentText).IsSuccess and 154 | (Pos('\', CurrentText) <= 0) then 155 | Expand(nil); 156 | 157 | Index := 0; 158 | Result := S_OK; 159 | end; 160 | 161 | function TStringEnumerator.Skip; 162 | begin 163 | Inc(Index, Count); 164 | 165 | if Index > High(Strings) then 166 | Result := S_FALSE 167 | else 168 | Result := S_OK; 169 | end; 170 | 171 | { Functions } 172 | 173 | [RequiresCOM] 174 | function ShlxpEnableSuggestions( 175 | EditControl: THwnd; 176 | ACList: IUnknown; 177 | Options: Cardinal 178 | ): TNtxStatus; 179 | var 180 | AutoComplete: IAutoComplete2; 181 | begin 182 | // Create an instance of CLSID_AutoComplete (provided by the OS) 183 | Result := ComxCreateInstanceWithFallback(shell32, CLSID_AutoComplete, 184 | IAutoComplete2, AutoComplete, 'CLSID_AutoComplete'); 185 | 186 | if not Result.IsSuccess then 187 | Exit; 188 | 189 | // Adjust options 190 | Result.Location := 'IAutoComplete2::SetOptions'; 191 | Result.HResult := AutoComplete.SetOptions(Options); 192 | 193 | if not Result.IsSuccess then 194 | Exit; 195 | 196 | // Register our suggestions 197 | Result.Location := 'IAutoComplete::Init'; 198 | Result.HResult := AutoComplete.Init(EditControl, ACList, nil, nil); 199 | end; 200 | 201 | function ShlxEnableStaticSuggestions; 202 | begin 203 | Result := ShlxpEnableSuggestions( 204 | EditControl, 205 | TStringEnumerator.CreateStatic(EditControl, Strings), 206 | Options 207 | ); 208 | end; 209 | 210 | function ShlxEnableDynamicSuggestions; 211 | begin 212 | Result := ShlxpEnableSuggestions( 213 | EditControl, 214 | TStringEnumerator.CreateDynamic(EditControl, Provider), 215 | Options 216 | ); 217 | end; 218 | 219 | end. 220 | -------------------------------------------------------------------------------- /NtUiLib/NtUiLib.Errors.Dialog.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.Errors.Dialog; 2 | 3 | interface 4 | 5 | uses 6 | Ntapi.WinUser, NtUtils; 7 | 8 | const 9 | DEFAULT_CROSS_SESSION_MESSAGE_TIMEOUT = 60; // sec 10 | 11 | // Show a modal error message dialog 12 | function ShowNtxStatus( 13 | ParentWnd: THwnd; 14 | const Status: TNtxStatus 15 | ): TNtxStatus; 16 | 17 | // Show a error message dialog to the interactive user 18 | function ShowNtxStatusAlwaysInteractive( 19 | const Status: TNtxStatus; 20 | TimeoutSeconds: Cardinal = DEFAULT_CROSS_SESSION_MESSAGE_TIMEOUT 21 | ): TNtxStatus; 22 | 23 | // Format a status message (without using reflection) 24 | function NtxVerboseStatusMessageNoReflection( 25 | const Status: TNtxStatus 26 | ): String; 27 | 28 | var 29 | // A custom status formatter (provided by NtUiLib.Exceptions.Dialog) 30 | NtxVerboseStatusMessageFormatter: function (const Status: TNtxStatus): String; 31 | 32 | implementation 33 | 34 | uses 35 | Ntapi.ntdef, NtUtils.SysUtils, NtUiLib.Errors, NtUiLib.TaskDialog; 36 | 37 | {$BOOLEVAL OFF} 38 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 39 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 40 | 41 | function NtxVerboseStatusMessageNoReflection; 42 | begin 43 | // LastCall: 44 | Result := 'Last call: ' + RtlxStringOrDefault(Status.Location, ''); 45 | 46 | if Status.LastCall.Parameter <> '' then 47 | Result := Result + #$D#$A'Parameter: ' + Status.LastCall.Parameter; 48 | 49 | // Result: 50 | Result := Result + #$D#$A'Result: ' + Status.Name; 51 | 52 | // 53 | Result := Result + #$D#$A#$D#$A + Status.Description; 54 | end; 55 | 56 | procedure RtlxpPrepareStatusMessage( 57 | const Status: TNtxStatus; 58 | out Icon: TDialogIcon; 59 | out Title: String; 60 | out Summary: String; 61 | out Content: String 62 | ); 63 | begin 64 | if Status.IsHResult or NT_ERROR(Status.Status) then 65 | begin 66 | Icon := diError; 67 | Title := 'Error'; 68 | end 69 | else 70 | begin 71 | Icon := diWarning; 72 | Title := 'Warning'; 73 | end; 74 | 75 | // Make a pretty header 76 | Summary := Status.Summary; 77 | 78 | if Summary = '' then 79 | Summary := 'System error'; 80 | 81 | if Assigned(NtxVerboseStatusMessageFormatter) then 82 | Content := NtxVerboseStatusMessageFormatter(Status) 83 | else 84 | Content := NtxVerboseStatusMessageNoReflection(Status); 85 | end; 86 | 87 | function ShowNtxStatus; 88 | var 89 | Icon: TDialogIcon; 90 | Title, Summary, Content: String; 91 | Response: TMessageResponse; 92 | begin 93 | RtlxpPrepareStatusMessage(Status, Icon, Title, Summary, Content); 94 | Result := UsrxShowTaskDialogWithStatus(Response, ParentWnd, Title, Summary, 95 | Content, Icon, dbOk, IDOK); 96 | end; 97 | 98 | function ShowNtxStatusAlwaysInteractive; 99 | var 100 | Icon: TDialogIcon; 101 | Title, Summary, Content: String; 102 | Response: TMessageResponse; 103 | begin 104 | RtlxpPrepareStatusMessage(Status, Icon, Title, Summary, Content); 105 | Result := UsrxShowMessageAlwaysInteractiveWithStatus(Response, Title, Summary, 106 | Content, Icon, dbOk, IDOK, TimeoutSeconds); 107 | end; 108 | 109 | end. 110 | -------------------------------------------------------------------------------- /NtUiLib/NtUiLib.Exceptions.Dialog.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.Exceptions.Dialog; 2 | 3 | { 4 | This module shows a detailed error dialog for a given TNtxStatus error. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.WinUser, NtUtils, NtUiLib.Errors.Dialog; 11 | 12 | var 13 | BUG_TITLE: String = 'This is definitely a bug...'; 14 | BUG_MESSAGE: String = 'If you known how to reproduce this error, please ' + 15 | 'help us by opening an issue on our project''s page.'; 16 | 17 | type 18 | // A callback function that might suggest solutions for specific problems 19 | TSuggester = function (const NtxStatus: TNtxStatus): String; 20 | 21 | // Register a suggestion callback 22 | procedure RegisterSuggestions(const Callback: TSuggester); 23 | 24 | // Construct a verbose report about an error 25 | function NtxVerboseFormatStatusMessage(const Status: TNtxStatus): String; 26 | 27 | // Show a modal exception message to a user 28 | function ShowNtxException( 29 | ParentWnd: THwnd; 30 | E: TObject 31 | ): TNtxStatus; 32 | 33 | // Show an exception message dialog to the interactive user 34 | function ShowNtxExceptionAlwaysInteractive( 35 | E: TObject; 36 | TimeoutSeconds: Cardinal = DEFAULT_CROSS_SESSION_MESSAGE_TIMEOUT 37 | ): TNtxStatus; 38 | 39 | implementation 40 | 41 | uses 42 | Ntapi.ntseapi, Ntapi.ntstatus, Ntapi.WinError, DelphiApi.Reflection, 43 | NtUtils.SysUtils, NtUiLib.Errors, NtUiLib.TaskDialog, NtUiLib.Exceptions, 44 | DelphiUiLib.Reflection, DelphiUiLib.Reflection.Strings, System.SysUtils, 45 | System.TypInfo, System.Rtti; 46 | 47 | {$BOOLEVAL OFF} 48 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 49 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 50 | 51 | var 52 | Suggesters: TArray; 53 | 54 | procedure RegisterSuggestions; 55 | begin 56 | SetLength(Suggesters, Length(Suggesters) + 1); 57 | Suggesters[High(Suggesters)] := Callback; 58 | end; 59 | 60 | function CollectSuggestions(const NtxStatus: TNtxStatus): String; 61 | var 62 | Suggestions: TArray; 63 | i: Integer; 64 | begin 65 | for i := 0 to High(Suggesters) do 66 | begin 67 | Result := Suggesters[i](NtxStatus); 68 | 69 | if Result <> '' then 70 | begin 71 | SetLength(Suggestions, Length(Suggestions) + 1); 72 | Suggestions[High(Suggestions)] := Result; 73 | end; 74 | end; 75 | 76 | if Length(Suggestions) > 0 then 77 | Result := #$D#$A#$D#$A'--- Suggestions ---'#$D#$A + 78 | String.Join(#$D#$A#$D#$A, Suggestions) 79 | else 80 | Result := ''; 81 | end; 82 | 83 | { Formatting } 84 | 85 | function ProvidesPrivilege(const LastCall: TLastCallInfo): Boolean; 86 | begin 87 | Result := (LastCall.ExpectedPrivilege >= SE_CREATE_TOKEN_PRIVILEGE) and 88 | (LastCall.ExpectedPrivilege <= High(TSeWellKnownPrivilege)); 89 | end; 90 | 91 | function GetFriendlyName(AType: Pointer): String; 92 | var 93 | RttiContext: TRttiContext; 94 | RttiType: TRttiType; 95 | a: TCustomAttribute; 96 | begin 97 | RttiContext := TRttiContext.Create; 98 | RttiType := RttiContext.GetType(AType); 99 | 100 | for a in RttiType.GetAttributes do 101 | if a is FriendlyNameAttribute then 102 | Exit(FriendlyNameAttribute(a).Name); 103 | 104 | Result := 'object'; 105 | end; 106 | 107 | function NtxVerboseFormatStatusMessage; 108 | var 109 | i: Integer; 110 | begin 111 | // LastCall: 112 | Result := 'Last call: ' + RtlxStringOrDefault(Status.Location, ''); 113 | 114 | if Status.LastCall.Parameter <> '' then 115 | Result := Result + #$D#$A'Parameter: ' + Status.LastCall.Parameter; 116 | 117 | case Status.LastCall.CallType of 118 | lcOpenCall: 119 | // Desired access: 120 | Result := Result + #$D#$A'Desired ' + 121 | GetFriendlyName(Status.LastCall.AccessMaskType) + ' access: ' + 122 | RepresentType(Status.LastCall.AccessMaskType, 123 | Status.LastCall.AccessMask).Text; 124 | 125 | lcQuerySetCall: 126 | // Information class: 127 | Result := Result + #$D#$A'Information class: ' + GetEnumName( 128 | Status.LastCall.InfoClassType, Integer(Status.LastCall.InfoClass)); 129 | end; 130 | 131 | // Expected access: 132 | if Status.Status = STATUS_ACCESS_DENIED then 133 | for i := 0 to High(Status.LastCall.ExpectedAccess) do 134 | with Status.LastCall.ExpectedAccess[i] do 135 | Result := Result + #$D#$A'Expected ' + 136 | GetFriendlyName(AccessMaskType) + ' access: ' + 137 | RepresentType(AccessMaskType, AccessMask).Text; 138 | 139 | // Result: 140 | Result := Result + #$D#$A'Result: ' + Status.Name; 141 | 142 | // 143 | Result := Result + #$D#$A#$D#$A + Status.Description; 144 | 145 | // 146 | if ((Status.Status = STATUS_PRIVILEGE_NOT_HELD) or 147 | (Status.IsWin32 and (Status.Win32Error = ERROR_PRIVILEGE_NOT_HELD))) and 148 | ProvidesPrivilege(Status.LastCall) then 149 | begin 150 | RtlxSuffixStripString('.', Result, True); 151 | Result := Result + ': "' + PrettifySnakeCaseEnum( 152 | TypeInfo(TSeWellKnownPrivilege), 153 | Integer(Status.LastCall.ExpectedPrivilege), 'SE_') + '"'; 154 | end; 155 | end; 156 | 157 | procedure RtlxpPrepareExceptionMessage( 158 | E: TObject; 159 | out Summary: String; 160 | out Content: String 161 | ); 162 | begin 163 | if E is Exception then 164 | begin 165 | Content := Exception(E).Message; 166 | 167 | // Include the stack trace when available 168 | if Assigned(Exception.GetStackInfoStringProc) then 169 | Content := Content + #$D#$A#$D#$A + 'Stack Trace:'#$D#$A + 170 | Exception(E).StackTrace; 171 | end 172 | else 173 | Content := E.ClassName + ' exception'; 174 | 175 | if not (E is Exception) or (E is EAccessViolation) or (E is EInvalidPointer) 176 | or (E is EAssertionFailed) or (E is EArgumentNilException) then 177 | begin 178 | Content := Content + #$D#$A#$D#$A + BUG_MESSAGE; 179 | Summary := BUG_TITLE; 180 | end 181 | else if E is EConvertError then 182 | Summary := 'Conversion error' 183 | else 184 | Summary := E.ClassName; 185 | end; 186 | 187 | { Showing } 188 | 189 | function ShowNtxException; 190 | var 191 | Summary, Content: String; 192 | Response: TMessageResponse; 193 | begin 194 | // Extract and use TNtxStatus from the exception 195 | if E is ENtError then 196 | Exit(ShowNtxStatus(ParentWnd, ENtError(E).NtxStatus)); 197 | 198 | RtlxpPrepareExceptionMessage(Exception(E), Summary, Content); 199 | Result := UsrxShowTaskDialogWithStatus(Response, ParentWnd, 'Exception', 200 | Summary, Content, diError, dbOk, IDOK); 201 | end; 202 | 203 | function ShowNtxExceptionAlwaysInteractive; 204 | var 205 | Summary, Content: String; 206 | Response: TMessageResponse; 207 | begin 208 | // Extract and use TNtxStatus from the exception 209 | if E is ENtError then 210 | Exit(ShowNtxStatusAlwaysInteractive(ENtError(E).NtxStatus, 211 | TimeoutSeconds)); 212 | 213 | RtlxpPrepareExceptionMessage(E, Summary, Content); 214 | Result := UsrxShowMessageAlwaysInteractiveWithStatus(Response, 'Exception', 215 | Summary, Content, diError, dbOk, IDOK, TimeoutSeconds); 216 | end; 217 | 218 | initialization 219 | NtxVerboseStatusMessageFormatter := NtxVerboseFormatStatusMessage; 220 | end. 221 | -------------------------------------------------------------------------------- /NtUiLib/NtUiLib.WinCred.pas: -------------------------------------------------------------------------------- 1 | unit NtUiLib.WinCred; 2 | 3 | interface 4 | 5 | uses 6 | Ntapi.WinNt, Ntapi.wincred, Ntapi.WinUser, Ntapi.WinError, Ntapi.NtSecApi, 7 | NtUtils, NtUtils.Tokens.Logon; 8 | 9 | // Show a Windows credentials prompt UI 10 | function CredxPromptForWindowsCredentials( 11 | [opt] ParentHwnd: THwnd; 12 | const CaptionText: String; 13 | const MessageText: String; 14 | out Credentials: TLogonCredentials; 15 | [in] PromptFlags: TCredUiWinFlags = 0; 16 | [in] UnpackFlags: TCredPackFlags = 0; 17 | AuthPackage: AnsiString = NEGOSSP_NAME_A; 18 | [opt] AuthError: TWin32Error = ERROR_SUCCESS; 19 | [in, out, opt] Save: PLongBool = nil 20 | ): TNtxStatus; 21 | 22 | implementation 23 | 24 | uses 25 | Ntapi.ObjBase, NtUtils.Ldr, NtUtils.Lsa, NtUtils.Lsa.Sid, NtUtils.SysUtils; 26 | 27 | {$BOOLEVAL OFF} 28 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 29 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 30 | 31 | function CredxDelayFree( 32 | [in] Buffer: Pointer 33 | ): IAutoReleasable; 34 | begin 35 | Result := Auto.Delay( 36 | procedure 37 | begin 38 | CoTaskMemFree(Buffer); 39 | end 40 | ); 41 | end; 42 | 43 | function CredxPromptForWindowsCredentials; 44 | var 45 | Info: TCredUIInfoW; 46 | PackageId: Cardinal; 47 | OutAuthBuffer: Pointer; 48 | OutAuthBufferSize: Cardinal; 49 | OutAuthBufferDeallocator: IAutoReleasable; 50 | Domain, Username, Password: IMemory; 51 | DomainLength, UsernameLength, PasswordLength: Cardinal; 52 | FullName: String; 53 | TranslatedName: TTranslatedName; 54 | begin 55 | Result := LdrxCheckDelayedImport(delayed_CredUIPromptForWindowsCredentialsW); 56 | 57 | if not Result.IsSuccess then 58 | Exit; 59 | 60 | Result := LdrxCheckDelayedImport(delayed_CredUnPackAuthenticationBufferW); 61 | 62 | if not Result.IsSuccess then 63 | Exit; 64 | 65 | Result := LsaxLookupAuthPackage(PackageId, AuthPackage); 66 | 67 | if not Result.IsSuccess then 68 | Exit; 69 | 70 | Info := Default(TCredUIInfoW); 71 | Info.Size := SizeOf(Info); 72 | Info.Parent := ParentHwnd; 73 | Info.MessageText := PWideChar(MessageText); 74 | Info.CaptionText := PWideChar(CaptionText); 75 | 76 | // Show the prompt 77 | Result.Location := 'CredUIPromptForWindowsCredentialsW'; 78 | Result.Win32ErrorOrSuccess := CredUIPromptForWindowsCredentialsW(@Info, 79 | AuthError, PackageId, nil, 0, OutAuthBuffer, OutAuthBufferSize, Save, 80 | PromptFlags); 81 | 82 | if not Result.IsSuccess then 83 | Exit; 84 | 85 | OutAuthBufferDeallocator := CredxDelayFree(OutAuthBuffer); 86 | Domain := Auto.AllocateDynamic(0); 87 | Username := Auto.AllocateDynamic(0); 88 | Password := Auto.AllocateDynamic(0); 89 | 90 | repeat 91 | DomainLength := Domain.Size div SizeOf(WideChar); 92 | UsernameLength := Username.Size div SizeOf(WideChar); 93 | PasswordLength := Password.Size div SizeOf(WideChar); 94 | 95 | // Extract the credentials (which might be encrypted) 96 | Result.Location := 'CredUnPackAuthenticationBufferW'; 97 | Result.Win32Result := CredUnPackAuthenticationBufferW(UnpackFlags, 98 | OutAuthBuffer, OutAuthBufferSize, Username.Data, UsernameLength, 99 | Domain.Data, DomainLength, Password.Data, PasswordLength); 100 | 101 | until not NtxExpandBufferEx(Result, Domain, Succ(DomainLength) * 102 | SizeOf(WideChar), nil) or not NtxExpandBufferEx(Result, Username, 103 | Succ(UsernameLength) * SizeOf(WideChar), nil) or not NtxExpandBufferEx( 104 | Result, Password, Succ(PasswordLength) * SizeOf(WideChar), nil); 105 | 106 | if not Result.IsSuccess then 107 | Exit; 108 | 109 | // Save the credentials 110 | Credentials := Default(TLogonCredentials); 111 | 112 | if DomainLength > 0 then 113 | SetString(Credentials.Domain, PWideChar(Domain.Data), Pred(DomainLength)); 114 | 115 | if UsernameLength > 0 then 116 | SetString(Credentials.Username, PWideChar(Username.Data), 117 | Pred(UsernameLength)); 118 | 119 | if PasswordLength > 0 then 120 | SetString(Credentials.Password, PWideChar(Password.Data), 121 | Pred(PasswordLength)); 122 | 123 | // The function can return the domain inside the username; fix that with 124 | // canonicalization 125 | if Credentials.Domain <> '' then 126 | FullName := Credentials.Domain + '\' + Credentials.Username 127 | else 128 | FullName := Credentials.Username; 129 | 130 | Result := LsaxCanonicalizeName(FullName, TranslatedName); 131 | 132 | // When the user doesn't provide a domain, the function tends to append the 133 | // default one, which might be wrong. As a workaround, strip the domain and 134 | // retry canonicalization. Note that this problem doesn't happen with plaintext 135 | // credentials, so we skip them. 136 | if not Result.IsSuccess and 137 | not BitTest(PromptFlags and CREDUIWIN_GENERIC) then 138 | Result := LsaxCanonicalizeName(RtlxExtractNamePath(FullName), 139 | TranslatedName); 140 | 141 | if not Result.IsSuccess then 142 | Exit; 143 | 144 | Credentials.Domain := TranslatedName.DomainName; 145 | Credentials.Username := TranslatedName.UserName; 146 | end; 147 | 148 | end. 149 | -------------------------------------------------------------------------------- /NtUiLib/Readme.md: -------------------------------------------------------------------------------- 1 | # NtUiLib 2 | 3 | These modules provide some useful functionality related to implementing user interfaces for tools that use NtUtils. They are not necessary for functioning of other parts of this project. -------------------------------------------------------------------------------- /NtUtils.Environment.User.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Environment.User; 2 | 3 | { 4 | The functions for constructing user environment using a token. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.WinNt, Ntapi.ntseapi, NtUtils; 11 | 12 | const 13 | TOKEN_CREATE_ENVIRONMEMT = TOKEN_QUERY or TOKEN_DUPLICATE or TOKEN_IMPERSONATE; 14 | 15 | // Prepare an environment for a user. If the token is not specified, the 16 | // function returns only system environmental variables. Supports AppContainers. 17 | function UnvxCreateUserEnvironment( 18 | out Environment: IEnvironment; 19 | [opt, Access(TOKEN_CREATE_ENVIRONMEMT)] hxToken: IHandle = nil; 20 | InheritCurrent: Boolean = False; 21 | FixAppContainers: Boolean = True 22 | ): TNtxStatus; 23 | 24 | // Update an environment to point to correct folders in case of AppContainer 25 | function UnvxUpdateAppContainerEnvironment( 26 | var Environment: IEnvironment; 27 | const AppContainerSid: ISid 28 | ): TNtxStatus; 29 | 30 | implementation 31 | 32 | uses 33 | Ntapi.UserEnv, NtUtils.Profiles, NtUtils.Ldr, NtUtils.Tokens, 34 | NtUtils.Tokens.Info, NtUtils.Security.Sid, NtUtils.Objects, Ntapi.Versions, 35 | NtUtils.Environment; 36 | 37 | {$BOOLEVAL OFF} 38 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 39 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 40 | 41 | function UnvxCreateUserEnvironment; 42 | var 43 | hToken: THandle; 44 | EnvBlock: PEnvironment; 45 | Package: ISid; 46 | begin 47 | Result := LdrxCheckDelayedImport(delayed_CreateEnvironmentBlock); 48 | 49 | if not Result.IsSuccess then 50 | Exit; 51 | 52 | if Assigned(hxToken) then 53 | begin 54 | // Add support for pseudo-handles 55 | Result := NtxExpandToken(hxToken, TOKEN_CREATE_ENVIRONMEMT); 56 | 57 | if not Result.IsSuccess then 58 | Exit; 59 | 60 | hToken := hxToken.Handle; 61 | end 62 | else 63 | hToken := 0; // System environment only 64 | 65 | Result.Location := 'CreateEnvironmentBlock'; 66 | 67 | if hToken <> 0 then 68 | Result.LastCall.Expects(TOKEN_CREATE_ENVIRONMEMT); 69 | 70 | Result.Win32Result := CreateEnvironmentBlock(EnvBlock, hToken, 71 | InheritCurrent); 72 | 73 | // Capture the environment block 74 | if Result.IsSuccess then 75 | Environment := RtlxCaptureEnvironment(EnvBlock) 76 | else 77 | Exit; 78 | 79 | // On Win8+ we might need to fix AppContainer profile path 80 | if FixAppContainers and Assigned(hxToken) and RtlOsVersionAtLeast(OsWin8) then 81 | begin 82 | // Get the package SID 83 | Result := NtxQuerySidToken(hxToken, TokenAppContainerSid, Package); 84 | 85 | if not Result.IsSuccess then 86 | Exit; 87 | 88 | // Fix AppContainer paths 89 | if Result.IsSuccess and Assigned(Package) then 90 | Result := UnvxUpdateAppContainerEnvironment(Environment, Package); 91 | end; 92 | end; 93 | 94 | function UnvxUpdateAppContainerEnvironment; 95 | var 96 | ProfilePath, TempPath: String; 97 | begin 98 | // Obtain the profile path 99 | Result := UnvxQueryFolderAppContainer(AppContainerSid, ProfilePath); 100 | 101 | if not Result.IsSuccess then 102 | Exit; 103 | 104 | // Fix AppData 105 | Result := RtlxSetVariableEnvironment(Environment, 'LOCALAPPDATA', ProfilePath); 106 | 107 | // Fix Temp 108 | TempPath := ProfilePath + '\Temp'; 109 | 110 | if Result.IsSuccess then 111 | Result := RtlxSetVariableEnvironment(Environment, 'TEMP', TempPath); 112 | 113 | if Result.IsSuccess then 114 | Result := RtlxSetVariableEnvironment(Environment, 'TMP', TempPath); 115 | end; 116 | 117 | end. 118 | -------------------------------------------------------------------------------- /NtUtils.ImageHlp.DbgHelp.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.ImageHlp.DbgHelp; 2 | 3 | { 4 | This module provides lightweight functionality for working with debug symbols 5 | directly exported by executable images. For PDB symbols, use 6 | NtUtils.DbgHelp instead. 7 | } 8 | 9 | 10 | interface 11 | 12 | uses 13 | NtUtils, NtUtils.Ldr, NtUtils.Files, DelphiApi.Reflection; 14 | 15 | type 16 | TImageHlpSymbol = record 17 | [Hex] RVA: Cardinal; 18 | Name: String; 19 | end; 20 | 21 | TRtlxBestMatchSymbol = record 22 | Module: TLdrxModuleInfo; 23 | Symbol: TImageHlpSymbol; 24 | [Hex] Offset: Cardinal; 25 | function ToString: String; 26 | end; 27 | 28 | // Lookup all exported symbols in a module 29 | function RtlxEnumSymbols( 30 | out Symbols: TArray; 31 | const Image: TMemory; 32 | MappedAsImage: Boolean; 33 | RangeChecks: Boolean = True 34 | ): TNtxStatus; 35 | 36 | // Lookup all exported symbols in a file 37 | function RtlxEnumSymbolsFile( 38 | out Symbols: TArray; 39 | const FileParameters: IFileParameters 40 | ): TNtxStatus; 41 | 42 | // Find a nearest symbol in a module 43 | function RtlxFindBestMatchModule( 44 | const Module: TLdrxModuleInfo; 45 | const Symbols: TArray; 46 | RVA: Cardinal 47 | ): TRtlxBestMatchSymbol; 48 | 49 | implementation 50 | 51 | uses 52 | NtUtils.SysUtils, NtUtils.ImageHlp, NtUtils.Sections, DelphiUtils.Arrays; 53 | 54 | {$BOOLEVAL OFF} 55 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 56 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 57 | 58 | function TRtlxBestMatchSymbol.ToString; 59 | begin 60 | Result := Module.BaseDllName; 61 | 62 | if Symbol.Name <> '' then 63 | Result := Result + '!' + Symbol.Name; 64 | 65 | if Offset <> 0 then 66 | begin 67 | if Result <> '' then 68 | Result := Result + '+'; 69 | 70 | Result := Result + RtlxUInt64ToStr(Offset, nsHexadecimal); 71 | end; 72 | end; 73 | 74 | function RtlxEnumSymbols; 75 | var 76 | ExportEntries: TArray; 77 | begin 78 | Result := RtlxEnumerateExportImage(ExportEntries, Image, MappedAsImage, 79 | RangeChecks); 80 | 81 | if not Result.IsSuccess then 82 | Exit; 83 | 84 | // Capture all non-forwarder exports 85 | Symbols := TArray.Convert(ExportEntries, 86 | function (const Entry: TExportEntry; out Symbol: TImageHlpSymbol): Boolean 87 | begin 88 | Result := not Entry.Forwards; 89 | 90 | if not Result then 91 | Exit; 92 | 93 | Symbol.RVA := Entry.VirtualAddress; 94 | 95 | if Entry.Name <> '' then 96 | Symbol.Name := String(Entry.Name) 97 | else 98 | Symbol.Name := 'Ordinal#' + RtlxUIntToStr(Entry.Ordinal); 99 | end 100 | ); 101 | 102 | // Sort them to allow binary search 103 | TArray.SortInline(Symbols, 104 | function (const A, B: TImageHlpSymbol): Integer 105 | begin 106 | {$Q-}{$R-} 107 | Cardinal(Result) := A.RVA - B.RVA; 108 | {$IFDEF R+}{$R+}{$ENDIF}{$IFDEF Q+}{$Q+}{$ENDIF} 109 | end 110 | ); 111 | end; 112 | 113 | function RtlxEnumSymbolsFile; 114 | var 115 | MappedFile: IMemory; 116 | begin 117 | Result := RtlxMapFileByName(FileParameters, NtxCurrentProcess, MappedFile); 118 | 119 | if not Result.IsSuccess then 120 | Exit; 121 | 122 | Result := RtlxEnumSymbols(Symbols, MappedFile.Region, False); 123 | end; 124 | 125 | function RtlxFindBestMatchModule; 126 | var 127 | BestMatch: Integer; 128 | begin 129 | // We expect the symbols to be sorted 130 | BestMatch := TArray.BinarySearchEx(Symbols, 131 | function (const Entry: TImageHlpSymbol): Integer 132 | begin 133 | {$Q-}{$R-} 134 | Cardinal(Result) := Entry.RVA - RVA; 135 | {$IFDEF R+}{$R+}{$ENDIF}{$IFDEF Q+}{$Q+}{$ENDIF} 136 | end 137 | ); 138 | 139 | if BestMatch = -1 then 140 | begin 141 | // Make a pseudo-symbol for the entire module 142 | Result.Symbol.RVA := 0; 143 | Result.Symbol.Name := ''; 144 | end 145 | else if BestMatch >= 0 then 146 | Result.Symbol := Symbols[BestMatch] // Exact match 147 | else 148 | Result.Symbol := Symbols[-(BestMatch + 2)]; // Nearest symbol below 149 | 150 | Result.Module := Module; 151 | Result.Offset := RVA - Result.Symbol.RVA; 152 | end; 153 | 154 | end. 155 | 156 | -------------------------------------------------------------------------------- /NtUtils.ImageHlp.Syscalls.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.ImageHlp.Syscalls; 2 | 3 | interface 4 | 5 | { 6 | This module allows extracting system call numbers from images that include 7 | stubs for issuing syscalls such as ntdll and win32u. 8 | } 9 | 10 | uses 11 | Ntapi.ImageHlp, NtUtils, NtUtils.ImageHlp; 12 | 13 | type 14 | TSyscallEntry = record 15 | ExportEntry: TExportEntry; 16 | SyscallNumber: Cardinal; 17 | end; 18 | 19 | // Extract syscall numbers from DLLs like ntdll.dll or win32u.dll 20 | function RtlxEnumerateSycallsDll( 21 | out SysCalls: TArray; 22 | const Image: TMemory; 23 | MappedAsImage: Boolean; 24 | RangeChecks: Boolean = True 25 | ): TNtxStatus; 26 | 27 | implementation 28 | 29 | uses 30 | Ntapi.WinNt, Ntapi.ntstatus, DelphiUtils.Arrays; 31 | 32 | {$BOOLEVAL OFF} 33 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 34 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 35 | 36 | const 37 | // For parsing x64 38 | MOV_R10_RCX_MOV_EAX = Cardinal($B8D18B4C); 39 | SYSCALL = Word($050F); 40 | JNE_3 = Word($0375); 41 | RET_INT2E_RET = Cardinal($C32ECDC3); 42 | 43 | type 44 | // Expected format of a syscall function on x64 systems 45 | TSyscallBody64 = record 46 | Head: Cardinal; {4C 8B D1 B8} // mov r10, rcx; mov eax, ... 47 | SyscallNumber: Cardinal; {xx xx xx xx} 48 | case Integer of 49 | 7: (SyscallNoUserShared: Word; {0F 05} ); 50 | 10: 51 | ( 52 | UserSharedTest: UInt64; {F6 04 25 08 03 FE 7F 01} 53 | // test USER_SHARED_DATA.SystemCall, 1 54 | Jne3: Word; {75 03} // jne 3 55 | SysCallUserShared: Word; {0F 05} // syscall 56 | Int2EPath: Cardinal; {C3 CD 2E C3 } // ret; int 2E; ret 57 | ); 58 | end; 59 | PSyscallBody64 = ^TSyscallBody64; 60 | 61 | function RtlxGetSyscallConverter( 62 | const Image: TMemory; 63 | MappedAsImage: Boolean; 64 | NtHeaders: PImageNtHeaders; 65 | RangeChecks: Boolean 66 | ): TConvertRoutine; 67 | begin 68 | Result := function ( 69 | const Entry: TExportEntry; 70 | out SyscallEntry: TSyscallEntry 71 | ): Boolean 72 | var 73 | Body: PSyscallBody64; 74 | begin 75 | // Locate the function's code 76 | Result := RtlxExpandVirtualAddress(Pointer(Body), Image, MappedAsImage, 77 | Entry.VirtualAddress, SizeOf(TSyscallBody64), NtHeaders, 78 | RangeChecks).IsSuccess; 79 | 80 | if not Result then 81 | Exit; 82 | 83 | // Check if it matches the template of a function that issues a syscall 84 | Result := (Body.Head = MOV_R10_RCX_MOV_EAX) and 85 | ((Body.SyscallNoUserShared = SYSCALL) or 86 | (Body.SysCallUserShared = SYSCALL)); 87 | 88 | // Save it 89 | if Result then 90 | begin 91 | SyscallEntry.ExportEntry := Entry; 92 | SyscallEntry.SyscallNumber := Body.SyscallNumber; 93 | end; 94 | end; 95 | end; 96 | 97 | function RtlxEnumerateSycallsDll; 98 | var 99 | ExportEntries: TArray; 100 | NtHeaders: PImageNtHeaders; 101 | begin 102 | try 103 | // We might need to use the headers for images that are mapped as files 104 | Result := RtlxGetImageNtHeader(NtHeaders, Image, RangeChecks); 105 | 106 | if not Result.IsSuccess then 107 | Exit; 108 | 109 | // Find all exported functions 110 | Result := RtlxEnumerateExportImage(ExportEntries, Image, MappedAsImage, 111 | RangeChecks); 112 | 113 | if not Result.IsSuccess then 114 | Exit; 115 | 116 | // Find all functions that issue syscalls and determine their numbers 117 | SysCalls := TArray.Convert(ExportEntries, 118 | RtlxGetSyscallConverter(Image, MappedAsImage, NtHeaders, RangeChecks)); 119 | except 120 | Result.Location := 'RtlxEnumerateSycallsDll'; 121 | Result.Status := STATUS_UNHANDLED_EXCEPTION; 122 | end; 123 | end; 124 | 125 | end. 126 | -------------------------------------------------------------------------------- /NtUtils.Lpc.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Lpc; 2 | 3 | { 4 | This module providers wrappers for using LPC/ALPC (local inter-process 5 | communication). 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.ntlpcapi, NtUtils, DelphiApi.Reflection; 12 | 13 | // Connect to an LPC port 14 | function NtxConnectPort( 15 | out hxPort: IHandle; 16 | const PortName: String; 17 | ImpersonationLevel: TSecurityImpersonationLevel = SecurityImpersonation; 18 | ContextTrackingMode: Boolean = False; 19 | EffectiveOnly: Boolean = False 20 | ): TNtxStatus; 21 | 22 | // Send a message to an LPC port 23 | function NtxRequestWaitReplyPort( 24 | const hxPort: IHandle; 25 | var Msg: TPortMessage 26 | ): TNtxStatus; 27 | 28 | implementation 29 | 30 | uses 31 | NtUtils.Objects; 32 | 33 | {$BOOLEVAL OFF} 34 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 35 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 36 | 37 | function NtxConnectPort; 38 | var 39 | PortNameStr: TNtUnicodeString; 40 | QoS: TSecurityQualityOfService; 41 | hPort: THandle; 42 | begin 43 | Result := RtlxInitUnicodeString(PortNameStr, PortName); 44 | 45 | if not Result.IsSuccess then 46 | Exit; 47 | 48 | QoS.Length := SizeOf(QoS); 49 | QoS.ImpersonationLevel := ImpersonationLevel; 50 | QoS.ContextTrackingMode := ContextTrackingMode; 51 | QoS.EffectiveOnly := EffectiveOnly; 52 | 53 | Result.Location := 'NtConnectPort'; 54 | Result.LastCall.Parameter := PortName; 55 | Result.Status := NtConnectPort(hPort, PortNameStr, QoS, nil, nil, nil, nil, 56 | nil); 57 | 58 | if Result.IsSuccess then 59 | hxPort := Auto.CaptureHandle(hPort); 60 | end; 61 | 62 | function NtxRequestWaitReplyPort; 63 | begin 64 | Result.Location := 'NtRequestWaitReplyPort'; 65 | Result.Status := NtRequestWaitReplyPort(HandleOrDefault(hxPort), Msg, Msg); 66 | end; 67 | 68 | end. 69 | -------------------------------------------------------------------------------- /NtUtils.Lsa.Logon.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Lsa.Logon; 2 | 3 | { 4 | This module enumerating and retrieving information about active logon sessions 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.WinNt, Ntapi.NtSecApi, NtUtils, DelphiUtils.AutoObjects; 11 | 12 | type 13 | ILogonSession = IMemory; 14 | 15 | // Enumerate logon sessions 16 | function LsaxEnumerateLogonSessions( 17 | out Luids: TArray 18 | ): TNtxStatus; 19 | 20 | // Query logon session information 21 | function LsaxQueryLogonSession( 22 | const LogonId: TLogonId; 23 | out Data: ILogonSession 24 | ): TNtxStatus; 25 | 26 | // Construct a SID for one of well-known logon sessions. May return nil. 27 | [Result: opt] 28 | function LsaxLookupKnownLogonSessionSid( 29 | const LogonId: TLogonId 30 | ): ISid; 31 | 32 | implementation 33 | 34 | uses 35 | NtUtils.Security.Sid, NtUtils.Processes.Info; 36 | 37 | {$BOOLEVAL OFF} 38 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 39 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 40 | 41 | type 42 | TLsaAutoMemory = class (TCustomAutoMemory, IMemory, IAutoPointer, IAutoReleasable) 43 | procedure Release; override; 44 | end; 45 | 46 | { TLogonAutoMemory } 47 | 48 | procedure TLsaAutoMemory.Release; 49 | begin 50 | if Assigned(FData) then 51 | LsaFreeReturnBuffer(FData); 52 | 53 | FData := nil; 54 | inherited; 55 | end; 56 | 57 | function LsaxDelayFreeReturnBuffer( 58 | [in] Buffer: Pointer 59 | ): IAutoReleasable; 60 | begin 61 | Result := Auto.Delay( 62 | procedure 63 | begin 64 | LsaFreeReturnBuffer(Buffer); 65 | end 66 | ); 67 | end; 68 | 69 | { Functions } 70 | 71 | function LsaxEnumerateLogonSessions; 72 | var 73 | Count, i: Integer; 74 | Buffer: PLuidArray; 75 | BufferDeallocator: IAutoReleasable; 76 | HasAnonymousLogon: Boolean; 77 | begin 78 | Result.Location := 'LsaEnumerateLogonSessions'; 79 | Result.Status := LsaEnumerateLogonSessions(Count, Buffer); 80 | 81 | if not Result.IsSuccess then 82 | Exit; 83 | 84 | BufferDeallocator := LsaxDelayFreeReturnBuffer(Buffer); 85 | SetLength(Luids, Count); 86 | 87 | // Invert the order so that later logons appear later in the list 88 | for i := 0 to High(Luids) do 89 | Luids[i] := Buffer{$R-}[Count - 1 - i]{$IFDEF R+}{$R+}{$ENDIF}; 90 | 91 | // Make sure anonymous logon is in the list (most likely it is not) 92 | HasAnonymousLogon := False; 93 | 94 | for i := 0 to High(Luids) do 95 | if Luids[i] = ANONYMOUS_LOGON_LUID then 96 | begin 97 | HasAnonymousLogon := True; 98 | Break; 99 | end; 100 | 101 | if not HasAnonymousLogon then 102 | Insert(ANONYMOUS_LOGON_LUID, Luids, 0); 103 | end; 104 | 105 | function LsaxQueryLogonSession; 106 | var 107 | Buffer: PSecurityLogonSessionData; 108 | begin 109 | {$IFDEF Win32} 110 | // LsaGetLogonSessionData returns an invalid pointer under WoW64 111 | if RtlxAssertNotWoW64(Result) then 112 | Exit; 113 | {$ENDIF} 114 | 115 | Result.Location := 'LsaGetLogonSessionData'; 116 | Result.Status := LsaGetLogonSessionData(LogonId, Buffer); 117 | 118 | if not Result.IsSuccess then 119 | Exit; 120 | 121 | // Fix missing logon ID 122 | if Buffer.LogonId = 0 then 123 | Buffer.LogonId := LogonId; 124 | 125 | IMemory(Data) := TLsaAutoMemory.Capture(Buffer, Buffer.Size); 126 | end; 127 | 128 | function LsaxLookupKnownLogonSessionSid; 129 | begin 130 | case LogonId of 131 | SYSTEM_LUID: 132 | Result := RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_LOCAL_SYSTEM_RID]); 133 | 134 | ANONYMOUS_LOGON_LUID: 135 | Result := RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_ANONYMOUS_LOGON_RID]); 136 | 137 | LOCALSERVICE_LUID: 138 | Result := RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_LOCAL_SERVICE_RID]); 139 | 140 | NETWORKSERVICE_LUID: 141 | Result := RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_NETWORK_SERVICE_RID]); 142 | 143 | IUSER_LUID: 144 | Result := RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_IUSER_RID]); 145 | else 146 | Result := nil; 147 | end; 148 | end; 149 | 150 | end. 151 | -------------------------------------------------------------------------------- /NtUtils.Objects.Compare.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Objects.Compare; 2 | 3 | { 4 | This modules provides a routine that checks if two handles point to the same 5 | kernel object. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.WinNt, NtUtils; 12 | 13 | { Helper functions } 14 | 15 | type 16 | THashingRoutine = function ( 17 | const hxObject: IHandle; 18 | out Hash: UInt64 19 | ): TNtxStatus; 20 | 21 | // Compute an object hash. Can reopen the object for required access. 22 | function NtxQueryHandleHash( 23 | hxObject: IHandle; 24 | HashingRoutine: THashingRoutine; 25 | RequiredAccess: TAccessMask; 26 | out Hash: UInt64; 27 | [opt] AccessMaskType: Pointer = nil 28 | ): TNtxStatus; 29 | 30 | // Compare two objects by computing their hashes 31 | function NtxCompareHandlesByHash( 32 | const hxObject1: IHandle; 33 | const hxObject2: IHandle; 34 | HashingRoutine: THashingRoutine; 35 | RequiredAccess: TAccessMask; 36 | out Equal: Boolean 37 | ): TNtxStatus; 38 | 39 | // Hashing routines 40 | function NtxHashToken(const hxToken: IHandle; out Hash: UInt64): TNtxStatus; 41 | function NtxHashProcess(const hxProcess: IHandle; out Hash: UInt64): TNtxStatus; 42 | function NtxHashThread(const hxThread: IHandle; out Hash: UInt64): TNtxStatus; 43 | 44 | { Generic comparison } 45 | 46 | // Check whether two handles point to the same kernel object 47 | function NtxCompareObjects( 48 | out Equal: Boolean; 49 | hxObject1: IHandle; 50 | hxObject2: IHandle; 51 | [opt] ObjectTypeName: String = '' 52 | ): TNtxStatus; 53 | 54 | implementation 55 | 56 | uses 57 | Ntapi.ntstatus, Ntapi.ntdef, Ntapi.ntobapi, Ntapi.ntpsapi, Ntapi.ntseapi, 58 | NtUtils.Objects, NtUtils.Ldr, NtUtils.Objects.Snapshots, DelphiUtils.Arrays, 59 | NtUtils.Tokens, NtUtils.Tokens.Info, NtUtils.Processes.Info, NtUtils.Threads; 60 | 61 | {$BOOLEVAL OFF} 62 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 63 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 64 | 65 | function NtxQueryHandleHash; 66 | begin 67 | Result := NtxEnsureAccessHandle(hxObject, TOKEN_QUERY, 0, 0, AccessMaskType); 68 | 69 | // Try to perform hashing 70 | if Result.IsSuccess then 71 | Result := HashingRoutine(hxObject, Hash); 72 | end; 73 | 74 | function NtxCompareHandlesByHash; 75 | var 76 | Hash1, Hash2: UInt64; 77 | begin 78 | // Hash the first handle 79 | Result := NtxQueryHandleHash(hxObject1, HashingRoutine, RequiredAccess, Hash1); 80 | 81 | if not Result.IsSuccess then 82 | Exit; 83 | 84 | // Hash the second handle 85 | Result := NtxQueryHandleHash(hxObject2, HashingRoutine, RequiredAccess, Hash2); 86 | 87 | if not Result.IsSuccess then 88 | Exit; 89 | 90 | Equal := Hash1 = Hash2; 91 | end; 92 | 93 | function NtxHashToken; 94 | var 95 | Stats: TTokenStatistics; 96 | begin 97 | // Use TokenId as a hash value 98 | Result := NtxToken.Query(hxToken, TokenStatistics, Stats); 99 | 100 | if Result.IsSuccess then 101 | Hash := UInt64(Stats.TokenId); 102 | end; 103 | 104 | function NtxHashProcess; 105 | var 106 | Info: TProcessBasicInformation; 107 | begin 108 | // Use ProcessId as a hash value 109 | Result := NtxProcess.Query(hxProcess, ProcessBasicInformation, Info); 110 | 111 | if Result.IsSuccess then 112 | Hash := UInt64(Info.UniqueProcessId); 113 | end; 114 | 115 | function NtxHashThread; 116 | var 117 | Info: TThreadBasicInformation; 118 | begin 119 | // Use ThreadId as a hash value 120 | Result := NtxThread.Query(hxThread, ThreadBasicInformation, Info); 121 | 122 | if Result.IsSuccess then 123 | Hash := UInt64(Info.ClientId.UniqueThread); 124 | end; 125 | 126 | function ExpandCustomPseudoHandles(var hxObject: IHandle): TNtxStatus; 127 | begin 128 | // Only tokens for now 129 | if (hxObject.Handle = NtCurrentProcessToken) or 130 | (hxObject.Handle = NtCurrentThreadToken) or 131 | (hxObject.Handle = NtCurrentEffectiveToken) then 132 | Result := NtxExpandToken(hxObject, TOKEN_QUERY) 133 | else 134 | Result := NtxSuccess; 135 | end; 136 | 137 | function NtxCompareObjects; 138 | var 139 | Type1, Type2: TObjectTypeInfo; 140 | Handles: TArray; 141 | HashFunction: THashingRoutine; 142 | RequiredAccess: TAccessMask; 143 | i, j: Integer; 144 | begin 145 | if not Assigned(hxObject1) or not Assigned(hxObject2) then 146 | begin 147 | Result.Location := 'NtxCompareObjects'; 148 | Result.Status := STATUS_INVALID_HANDLE; 149 | Exit; 150 | end; 151 | 152 | if hxObject1.Handle = hxObject2.Handle then 153 | begin 154 | Equal := True; 155 | Exit(NtxSuccess); 156 | end; 157 | 158 | // Add support for token pseudo-handles 159 | Result := ExpandCustomPseudoHandles(hxObject1); 160 | 161 | if not Result.IsSuccess then 162 | Exit; 163 | 164 | Result := ExpandCustomPseudoHandles(hxObject2); 165 | 166 | if not Result.IsSuccess then 167 | Exit; 168 | 169 | // Win 10 TH+ makes things way easier 170 | if LdrxCheckDelayedImport(delayed_NtCompareObjects).IsSuccess then 171 | begin 172 | Result.Location := 'NtCompareObjects'; 173 | Result.Status := NtCompareObjects(hxObject1.Handle, hxObject2.Handle); 174 | Equal := Result.Status <> STATUS_NOT_SAME_OBJECT; 175 | Exit; 176 | end; 177 | 178 | // Get object's type if the caller didn't specify it 179 | if ObjectTypeName = '' then 180 | if NtxQueryTypeObject(hxObject1, Type1).IsSuccess and 181 | NtxQueryTypeObject(hxObject2, Type2).IsSuccess then 182 | begin 183 | if Type1.TypeName <> Type2.TypeName then 184 | begin 185 | Equal := False; 186 | Exit(NtxSuccess); 187 | end; 188 | 189 | ObjectTypeName := Type1.TypeName; 190 | end; 191 | 192 | // Perform type-specific comparison 193 | if ObjectTypeName <> '' then 194 | begin 195 | if ObjectTypeName = 'Token' then 196 | begin 197 | HashFunction := NtxHashToken; 198 | RequiredAccess := TOKEN_QUERY; 199 | end 200 | else if ObjectTypeName = 'Process' then 201 | begin 202 | HashFunction := NtxHashProcess; 203 | RequiredAccess := PROCESS_QUERY_LIMITED_INFORMATION; 204 | end 205 | else if ObjectTypeName = 'Thread' then 206 | begin 207 | HashFunction := NtxHashThread; 208 | RequiredAccess := THREAD_QUERY_LIMITED_INFORMATION; 209 | end 210 | else 211 | begin 212 | HashFunction := nil; 213 | RequiredAccess := 0; 214 | end; 215 | 216 | if Assigned(HashFunction) and NtxCompareHandlesByHash(hxObject1, hxObject2, 217 | HashFunction, RequiredAccess, Equal).IsSuccess then 218 | Exit(NtxSuccess); 219 | end; 220 | 221 | // The last resort is to proceed via a handle snapshot 222 | Result := NtxEnumerateHandles(Handles); 223 | 224 | if not Result.IsSuccess then 225 | Exit; 226 | 227 | TArray.FilterInline(Handles, 228 | ByProcess(NtCurrentProcessId)); 229 | 230 | for i := 0 to High(Handles) do 231 | if Handles[i].HandleValue = hxObject1.Handle then 232 | begin 233 | for j := 0 to High(Handles) do 234 | if Handles[j].HandleValue = hxObject2.Handle then 235 | begin 236 | if not Assigned(Handles[i].PObject) then 237 | begin 238 | // Kernel address leak prevention stops us from comparing pointers 239 | Result.Location := 'NtxCompareObjects'; 240 | Result.LastCall.ExpectedPrivilege := SE_DEBUG_PRIVILEGE; 241 | Result.Status := STATUS_PRIVILEGE_NOT_HELD; 242 | Exit; 243 | end; 244 | 245 | Equal := (Handles[i].PObject = Handles[j].PObject); 246 | Exit(NtxSuccess); 247 | end; 248 | 249 | Break; 250 | end; 251 | 252 | Result.Location := 'NtxCompareObjects'; 253 | Result.Status := STATUS_INVALID_HANDLE; 254 | end; 255 | 256 | end. 257 | -------------------------------------------------------------------------------- /NtUtils.Packages.WinRT.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Packages.WinRT; 2 | 3 | { 4 | This module provides definitions for application package support in 5 | Windows Runtime. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.winrt.appmodel, Ntapi.winrt, Ntapi.Versions, NtUtils; 12 | 13 | // Enumerate packages 14 | [RequiresWinRT] 15 | [MinOSVersion(OsWin8)] 16 | function RoxEnumeratePackages( 17 | out Packages: TArray; 18 | AllUser: Boolean = False; 19 | [opt] const UserSid: ISid = nil 20 | ): TNtxStatus; 21 | 22 | // Enumerate packages returning full names 23 | [RequiresWinRT] 24 | [MinOSVersion(OsWin8)] 25 | function RoxEnumeratePackageNames( 26 | out FullNames: TArray; 27 | AllUser: Boolean; 28 | [opt] const UserSid: ISid = nil 29 | ): TNtxStatus; 30 | 31 | // Enumerate packages returning family names 32 | [RequiresWinRT] 33 | [MinOSVersion(OsWin8)] 34 | function RoxEnumeratePackageFamilyNames( 35 | out FamilyNames: TArray; 36 | AllUser: Boolean; 37 | [opt] const UserSid: ISid = nil 38 | ): TNtxStatus; 39 | 40 | // Enumerate packages returning application user-mode IDs 41 | [RequiresWinRT] 42 | [MinOSVersion(OsWin81)] 43 | function RoxEnumeratePackageApps( 44 | out AppUserModelIDs: TArray; 45 | AllUser: Boolean = False; 46 | [opt] const UserSid: ISid = nil 47 | ): TNtxStatus; 48 | 49 | implementation 50 | 51 | uses 52 | NtUtils.Com, NtUtils.Security.Sid, NtUtils.Packages, DelphiUtils.Arrays; 53 | 54 | {$BOOLEVAL OFF} 55 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 56 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 57 | 58 | function RoxEnumeratePackages; 59 | var 60 | Inspectable: IInspectable; 61 | PackageManager: IPackageManager; 62 | Iterable: IIterable; 63 | Iterator: IIterator; 64 | Package: IPackage; 65 | SidString: IHString; 66 | HasCurrent: Boolean; 67 | begin 68 | Result := RoxActivateInstance('Windows.Management.Deployment.PackageManager', 69 | Inspectable); 70 | 71 | if not Result.IsSuccess then 72 | Exit; 73 | 74 | Result.Location := 'IInspectable::QueryInterface'; 75 | Result.LastCall.Parameter := 'IPackageManager'; 76 | Result.HResult := Inspectable.QueryInterface(IPackageManager, 77 | PackageManager); 78 | 79 | if not Result.IsSuccess then 80 | Exit; 81 | 82 | if AllUser then 83 | begin 84 | // Find all packages 85 | Result.Location := 'IPackageManager::FindPackages'; 86 | Result.HResult := PackageManager.FindPackages(Iterable); 87 | end 88 | else 89 | begin 90 | // Prepare the user SID 91 | if Assigned(UserSid) then 92 | begin 93 | Result := RoxCreateString(RtlxSidToString(UserSid), SidString); 94 | 95 | if not Result.IsSuccess then 96 | Exit; 97 | end 98 | else 99 | SidString := nil; 100 | 101 | // Find all user packages 102 | Result.Location := 'IPackageManager::FindPackagesByUserSecurityId'; 103 | Result.HResult := PackageManager.FindPackagesByUserSecurityId( 104 | Auto.RefOrNil(SidString), Iterable); 105 | end; 106 | 107 | if not Result.IsSuccess then 108 | Exit; 109 | 110 | Result.Location := 'IIterable::First'; 111 | Result.HResultAllowFalse := Iterable.First(Iterator); 112 | 113 | if not Result.IsSuccess then 114 | Exit; 115 | 116 | Result.Location := 'IIterator::get_HasCurrent'; 117 | Result.HResult := Iterator.get_HasCurrent(HasCurrent); 118 | 119 | if not Result.IsSuccess then 120 | Exit; 121 | 122 | Packages := nil; 123 | 124 | while hasCurrent do 125 | begin 126 | Result.Location := 'IIterator::get_Current'; 127 | Result.HResult := Iterator.get_Current(Package); 128 | 129 | if not Result.IsSuccess then 130 | Exit; 131 | 132 | SetLength(Packages, Length(Packages) + 1); 133 | Packages[High(Packages)] := Package; 134 | 135 | Result.Location := 'IIterator.MoveNext'; 136 | Result.HResult := Iterator.MoveNext(HasCurrent); 137 | 138 | if not Result.IsSuccess then 139 | Exit; 140 | end; 141 | end; 142 | 143 | function RoxEnumeratePackageNames; 144 | var 145 | Packages: TArray; 146 | PackageId: IPackageId; 147 | i: Integer; 148 | hString: THString; 149 | hStringDeallocator: IAutoReleasable; 150 | begin 151 | Result := RoxEnumeratePackages(Packages, AllUser, UserSid); 152 | 153 | if not Result.IsSuccess then 154 | Exit; 155 | 156 | SetLength(FullNames, Length(Packages)); 157 | 158 | for i := 0 to High(Packages) do 159 | begin 160 | Result.Location := 'IPackage::Get_Id'; 161 | Result.HResult := Packages[i].Get_Id(PackageId); 162 | 163 | if not Result.IsSuccess then 164 | Exit; 165 | 166 | Result.Location := 'IPackageId::get_FullName'; 167 | Result.HResult := PackageId.get_FullName(hString); 168 | 169 | if not Result.IsSuccess then 170 | Exit; 171 | 172 | hStringDeallocator := RoxCaptureString(hString); 173 | FullNames[i] := RoxSaveString(hString); 174 | end; 175 | end; 176 | 177 | function RoxEnumeratePackageFamilyNames; 178 | var 179 | Packages: TArray; 180 | PackageId: IPackageId; 181 | i: Integer; 182 | hString: THString; 183 | hStringDeallocator: IAutoReleasable; 184 | begin 185 | Result := RoxEnumeratePackages(Packages, AllUser, UserSid); 186 | 187 | if not Result.IsSuccess then 188 | Exit; 189 | 190 | SetLength(FamilyNames, Length(Packages)); 191 | 192 | for i := 0 to High(Packages) do 193 | begin 194 | Result.Location := 'IPackage::Get_Id'; 195 | Result.HResult := Packages[i].Get_Id(PackageId); 196 | 197 | if not Result.IsSuccess then 198 | Exit; 199 | 200 | Result.Location := 'IPackageId::get_FamilyName'; 201 | Result.HResult := PackageId.get_FamilyName(hString); 202 | 203 | if not Result.IsSuccess then 204 | Exit; 205 | 206 | hStringDeallocator := RoxCaptureString(hString); 207 | FamilyNames[i] := RoxSaveString(hString); 208 | end; 209 | end; 210 | 211 | function RoxEnumeratePackageApps; 212 | var 213 | FullNames: TArray; 214 | IDs: TArray>; 215 | InfoReference: IPackageInfoReference; 216 | i: Integer; 217 | begin 218 | // Collect all packages 219 | Result := RoxEnumeratePackageNames(FullNames, AllUser, UserSid); 220 | 221 | if not Result.IsSuccess then 222 | Exit; 223 | 224 | SetLength(IDs, Length(FullNames)); 225 | 226 | // Enumerate applications in each package 227 | for i := 0 to High(FullNames) do 228 | begin 229 | Result := PkgxOpenPackageInfo(InfoReference, FullNames[i]); 230 | 231 | if not Result.IsSuccess then 232 | Exit; 233 | 234 | Result := PkgxEnumerateAppUserModelIds(IDs[i], InfoReference); 235 | 236 | if not Result.IsSuccess then 237 | Exit; 238 | end; 239 | 240 | // Merge them 241 | AppUserModelIDs := TArray.Flatten(IDs); 242 | end; 243 | 244 | end. 245 | -------------------------------------------------------------------------------- /NtUtils.Power.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Power; 2 | 3 | { 4 | This module provides support to power management-related APIs. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.ntpoapi, Ntapi.WinNt, NtUtils, DelphiApi.Reflection; 11 | 12 | // Issue a power information query/set request 13 | function NtxPowerInformation( 14 | InfoClass: TPowerInformationLevel; 15 | [in, ReadsFrom] InputBuffer: Pointer; 16 | [in, NumberOfBytes] InputBufferLength: Cardinal; 17 | [out, WritesTo] OutputBuffer: Pointer; 18 | [in, NumberOfBytes] OutputBufferLength: Cardinal 19 | ): TNtxStatus; 20 | 21 | // Query the processsor power information 22 | function NtxQueryProcessorPower( 23 | out Info: TArray 24 | ): TNtxStatus; 25 | 26 | // Create a regular/PLM power request object 27 | function NtxCreatePowerRequest( 28 | out hxPowerRequest: IHandle; 29 | UsePLM: Boolean = False; 30 | [opt] const Reason: String = '' 31 | ): TNtxStatus; 32 | 33 | // Enable/disable a power request 34 | // - Regular requests don't use the process handle parameter 35 | // - PLM requests require a process handle and an execution request type 36 | function NtxActivatePowerRequest( 37 | const hxPowerRequest: IHandle; 38 | RequestType: TPowerRequestTypeInternal; 39 | [opt, Access(PROCESS_SET_LIMITED_INFORMATION)] const hxProcess: IHandle = nil; 40 | Enable: Boolean = True 41 | ): TNtxStatus; 42 | 43 | implementation 44 | 45 | {$BOOLEVAL OFF} 46 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 47 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 48 | 49 | uses 50 | Ntapi.ntexapi, Ntapi.ntstatus, NtUtils.System, NtUtils.Objects, 51 | DelphiUtils.AutoObjects; 52 | 53 | function NtxPowerInformation; 54 | begin 55 | Result.Location := 'NtPowerInformation'; 56 | Result.LastCall.UsesInfoClass(InfoClass, icPerform); 57 | Result.Status := NtPowerInformation(InfoClass, InputBuffer, InputBufferLength, 58 | OutputBuffer, OutputBufferLength); 59 | end; 60 | 61 | function NtxQueryProcessorPower; 62 | var 63 | SystemInfo: TSystemBasicInformation; 64 | Buffer: IMemory; 65 | i: Integer; 66 | begin 67 | // We need to know the number of processors 68 | Result := NtxSystem.Query(SystemBasicInformation, SystemInfo); 69 | 70 | if not Result.IsSuccess then 71 | Exit; 72 | 73 | IMemory(Buffer) := Auto.AllocateDynamic(SystemInfo.NumberOfProcessors * 74 | SizeOf(TProcessorPowerInformation)); 75 | 76 | // Issue the reuqest 77 | Result := NtxPowerInformation(ProcessorInformation, nil, 0, Buffer.Data, 78 | Buffer.Size); 79 | 80 | if not Result.IsSuccess then 81 | Exit; 82 | 83 | // Capture the result 84 | SetLength(Info, SystemInfo.NumberOfProcessors); 85 | 86 | for i := 0 to High(Info) do 87 | Info[i] := Buffer.Data{$R-}[i]{$IFDEF R+}{$R+}{$ENDIF}; 88 | end; 89 | 90 | function NtxCreatePowerRequest; 91 | var 92 | InfoClass: TPowerInformationLevel; 93 | Input: TCountedReasonContext; 94 | hPowerRequest: THandle; 95 | begin 96 | if UsePLM then 97 | InfoClass := PlmPowerRequestCreate 98 | else 99 | InfoClass := PowerRequestCreate; 100 | 101 | // Prepare the reason structure 102 | Input := Default(TCountedReasonContext); 103 | Input.Version := DIAGNOSTIC_REASON_VERSION; 104 | 105 | if Reason <> '' then 106 | begin 107 | Input.Flags := DIAGNOSTIC_REASON_SIMPLE_STRING; 108 | 109 | Result := RtlxInitUnicodeString(Input.SimpleString, Reason); 110 | 111 | if not Result.IsSuccess then 112 | Exit; 113 | end 114 | else 115 | Input.Flags := DIAGNOSTIC_REASON_NOT_SPECIFIED; 116 | 117 | // Issue the request 118 | Result := NtxPowerInformation(InfoClass, @Input, SizeOf(Input), 119 | @hPowerRequest, SizeOf(hPowerRequest)); 120 | 121 | if Result.IsSuccess then 122 | hxPowerRequest := Auto.CaptureHandle(hPowerRequest); 123 | end; 124 | 125 | function NtxActivatePowerRequest; 126 | var 127 | Input: TPowerRequestAction; 128 | begin 129 | Input := Default(TPowerRequestAction); 130 | Input.PowerRequestHandle := HandleOrDefault(hxPowerRequest); 131 | Input.RequestType := RequestType; 132 | Input.SetAction := Enable; 133 | Input.ProcessHandle := HandleOrDefault(hxProcess); 134 | 135 | Result := NtxPowerInformation(PowerRequestAction, @Input, SizeOf(Input), 136 | nil, 0); 137 | end; 138 | 139 | end. 140 | -------------------------------------------------------------------------------- /NtUtils.Processes.Create.Csr.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Processes.Create.Csr; 2 | 3 | { 4 | This module provides support for asking CSR to create processes on our behalf. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | NtUtils, NtUtils.Processes.Create; 11 | 12 | // Connect to SbApiPort and ask CSRSS to create a process 13 | [SupportedOption(spoCurrentDirectory)] 14 | [SupportedOption(spoSessionId)] 15 | function CsrxCreateProcess( 16 | const Options: TCreateProcessOptions; 17 | out Info: TProcessInfo 18 | ): TNtxStatus; 19 | 20 | implementation 21 | 22 | uses 23 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.ntsmss, Ntapi.ntlpcapi, Ntapi.ntpebteb, 24 | NtUtils.Lpc, NtUtils.Files, NtUtils.SysUtils, NtUtils.Objects, NtUtils.Threads; 25 | 26 | {$BOOLEVAL OFF} 27 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 28 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 29 | 30 | function CsrxCreateProcess; 31 | var 32 | hxPort: IHandle; 33 | SessionId: TSessionId; 34 | PortName, ImageFileName, CommandLine, CurrentDirectory: String; 35 | ImageFileNameStr, CommandLineStr, CurrentDirectoryStr: TNtUnicodeString; 36 | Msg: TSbApiMsg; 37 | begin 38 | // Prepare the image name 39 | ImageFileName := Options.ApplicationNative; 40 | Result := RtlxInitUnicodeString(ImageFileNameStr, ImageFileName); 41 | 42 | if not Result.IsSuccess then 43 | Exit; 44 | 45 | // Prepare the command line 46 | CommandLine := Options.CommandLine; 47 | Result := RtlxInitUnicodeString(CommandLineStr, CommandLine); 48 | 49 | if not Result.IsSuccess then 50 | Exit; 51 | 52 | // Prepare the current directory 53 | if Options.CurrentDirectory <> '' then 54 | CurrentDirectory := Options.CurrentDirectory 55 | else 56 | CurrentDirectory := RtlxGetCurrentDirectory; 57 | 58 | if CurrentDirectory = '' then 59 | CurrentDirectory := USER_SHARED_DATA.NtSystemRoot; 60 | 61 | Result := RtlxInitUnicodeString(CurrentDirectoryStr, CurrentDirectory); 62 | 63 | if not Result.IsSuccess then 64 | Exit; 65 | 66 | // Prepare the message 67 | Msg := Default(TSbApiMsg); 68 | Msg.h.u1.TotalLength := SizeOf(TSbApiMsg); 69 | Msg.h.u1.DataLength := SizeOf(TSbApiMsg) - SizeOf(TPortMessage); 70 | Msg.ApiNumber := SbCreateProcessApi; 71 | Msg.CreateProcessA.i.ImageFileName := @ImageFileNameStr; 72 | Msg.CreateProcessA.i.CommandLine := @CommandLineStr; 73 | Msg.CreateProcessA.i.CurrentDirectory := @CurrentDirectoryStr; 74 | Msg.CreateProcessA.i.Flags := SMP_DONT_START or SMP_ASYNC_FLAG; 75 | 76 | // Choose which CSRSS to connect to 77 | if poUseSessionId in Options.Flags then 78 | SessionId := Options.SessionId 79 | else 80 | SessionId := RtlGetCurrentPeb.SessionID; 81 | 82 | if SessionId <> 0 then 83 | PortName := RtlxFormatString('\Sessions\%d\Windows\SbApiPort', [SessionId]) 84 | else 85 | PortName := '\Windows\SbApiPort'; 86 | 87 | // Open the connection 88 | Result := NtxConnectPort(hxPort, PortName); 89 | 90 | if not Result.IsSuccess then 91 | Exit; 92 | 93 | // Send the request and wait 94 | Result := NtxRequestWaitReplyPort(hxPort, Msg.h); 95 | 96 | if not Result.IsSuccess then 97 | Exit; 98 | 99 | // Forward the operation status 100 | Result.Location := 'SbApiPort::SbCreateProcessApi'; 101 | Result.Status := Msg.ReturnedStatus; 102 | 103 | if not Result.IsSuccess then 104 | Exit; 105 | 106 | // Capture available information 107 | Info.ValidFields := [piProcessID, piThreadID]; 108 | Info.ClientId := Msg.CreateProcessA.o.ClientId; 109 | Info.ImageInformation.SubSystemType := Msg.CreateProcessA.o.SubSystemType; 110 | 111 | if Msg.CreateProcessA.o.Process <> 0 then 112 | begin 113 | Include(Info.ValidFields, piProcessHandle); 114 | Info.hxProcess := Auto.CaptureHandle(Msg.CreateProcessA.o.Process); 115 | end; 116 | 117 | if Msg.CreateProcessA.o.Thread <> 0 then 118 | begin 119 | Include(Info.ValidFields, piThreadHandle); 120 | Info.hxThread := Auto.CaptureHandle(Msg.CreateProcessA.o.Thread); 121 | 122 | // Resume if necessary 123 | if not (poSuspended in Options.Flags) then 124 | NtxResumeThread(Info.hxThread); 125 | end; 126 | end; 127 | 128 | end. 129 | -------------------------------------------------------------------------------- /NtUtils.Svc.SingleTaskSvc.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Svc.SingleTaskSvc; 2 | 3 | { 4 | This module provides a template for a simple service application that executes 5 | its payload and exits. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | NtUtils, NtUtils.Svc; 12 | 13 | type 14 | TSvcxPayload = reference to procedure(const ScvParams: TArray); 15 | 16 | // Starts service control dispatcher. 17 | function SvcxMain( 18 | const ServiceName: String; 19 | Payload: TSvcxPayload 20 | ): TNtxStatus; 21 | 22 | implementation 23 | 24 | uses 25 | Ntapi.WinNt, Ntapi.WinSvc, Ntapi.WinError, Ntapi.WinBase; 26 | 27 | {$BOOLEVAL OFF} 28 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 29 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 30 | 31 | var 32 | SvcxName: String; 33 | SvcxPayload: TSvcxPayload = nil; 34 | 35 | SvcxStatusHandle: THandle; 36 | SvcxStatus: TServiceStatus = ( 37 | ServiceType: SERVICE_WIN32_OWN_PROCESS; 38 | CurrentState: SERVICE_RUNNING; 39 | ControlsAccepted: 0; 40 | Win32ExitCode: 0; 41 | ServiceSpecificExitCode: 0; 42 | CheckPoint: 0; 43 | WaitHint: 5000 44 | ); 45 | 46 | function SvcxHandlerEx( 47 | Control: TServiceControl; 48 | EventType: Cardinal; 49 | EventData: Pointer; 50 | var Context 51 | ): TWin32Error; stdcall; 52 | begin 53 | if Control = SERVICE_CONTROL_INTERROGATE then 54 | Result := ERROR_SUCCESS 55 | else 56 | Result := ERROR_CALL_NOT_IMPLEMENTED; 57 | end; 58 | 59 | procedure SvcxServiceMain( 60 | dwNumServicesArgs: Integer; 61 | const [ref] ServiceArgVectors: TAnysizeArray 62 | ); stdcall; 63 | var 64 | i: Integer; 65 | Parameters: TArray; 66 | begin 67 | // Register service control handler 68 | SvcxStatusHandle := RegisterServiceCtrlHandlerExW(PWideChar(SvcxName), 69 | SvcxHandlerEx, nil); 70 | 71 | // Report running status 72 | SetServiceStatus(SvcxStatusHandle, SvcxStatus); 73 | 74 | // Prepare passed parameters 75 | SetLength(Parameters, dwNumServicesArgs); 76 | 77 | for i := 0 to High(Parameters) do 78 | Parameters[i] := String(ServiceArgVectors{$R-}[i]{$IFDEF R+}{$R+}{$ENDIF}); 79 | 80 | {$IFDEF DEBUG} 81 | OutputDebugStringW(PWideChar(ParamStr(0))); 82 | OutputDebugStringW('Service parameters: '); 83 | 84 | for i := 0 to dwNumServicesArgs - 1 do 85 | OutputDebugStringW(PWideChar(Parameters[i])); 86 | {$ENDIF} 87 | 88 | // Call the payload 89 | try 90 | if Assigned(SvcxPayload) then 91 | SvcxPayload(Parameters); 92 | except 93 | OutputDebugStringW('Exception in ServiceMain'); 94 | end; 95 | 96 | // Report that we have finished 97 | SvcxStatus.CurrentState := SERVICE_STOPPED; 98 | SetServiceStatus(SvcxStatusHandle, SvcxStatus); 99 | end; 100 | 101 | function SvcxMain; 102 | var 103 | ServiceTable: array [0 .. 1] of TServiceTableEntry; 104 | begin 105 | SvcxName := ServiceName; 106 | SvcxPayload := Payload; 107 | 108 | ServiceTable[0].ServiceName := PWideChar(SvcxName); 109 | ServiceTable[0].ServiceProc := SvcxServiceMain; 110 | ServiceTable[1].ServiceName := nil; 111 | ServiceTable[1].ServiceProc := nil; 112 | 113 | Result.Location := 'StartServiceCtrlDispatcherW'; 114 | Result.Win32Result := StartServiceCtrlDispatcherW(PServiceTableEntry( 115 | @ServiceTable)); 116 | end; 117 | 118 | end. 119 | -------------------------------------------------------------------------------- /NtUtils.System.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.System; 2 | 3 | { 4 | The module provides support for querying information about the system. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.ntexapi, NtUtils, NtUtils.Ldr; 11 | 12 | // Query variable-size system information 13 | function NtxQuerySystem( 14 | InfoClass: TSystemInformationClass; 15 | out xMemory: IMemory; 16 | InitialBuffer: Cardinal = 0; 17 | [opt] GrowthMethod: TBufferGrowthMethod = nil 18 | ): TNtxStatus; 19 | 20 | type 21 | NtxSystem = class abstract 22 | // Query fixed-size information 23 | class function Query( 24 | InfoClass: TSystemInformationClass; 25 | var Buffer: T 26 | ): TNtxStatus; static; 27 | end; 28 | 29 | // Enumerate kernel modules and drivers 30 | function NtxEnumerateModulesSystem( 31 | out Modules: TArray 32 | ): TNtxStatus; 33 | 34 | implementation 35 | 36 | uses 37 | Ntapi.ntrtl, NtUtils.Files, DelphiUtils.AutoObjects; 38 | 39 | {$BOOLEVAL OFF} 40 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 41 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 42 | 43 | function NtxQuerySystem; 44 | var 45 | Required: Cardinal; 46 | begin 47 | Result.Location := 'NtQuerySystemInformation'; 48 | Result.LastCall.UsesInfoClass(InfoClass, icQuery); 49 | 50 | xMemory := Auto.AllocateDynamic(InitialBuffer); 51 | repeat 52 | Required := 0; 53 | Result.Status := NtQuerySystemInformation(InfoClass, xMemory.Data, 54 | xMemory.Size, @Required); 55 | until not NtxExpandBufferEx(Result, xMemory, Required, GrowthMethod); 56 | end; 57 | 58 | { NtxSystem } 59 | 60 | class function NtxSystem.Query; 61 | begin 62 | Result.Location := 'NtQuerySystemInformation'; 63 | Result.LastCall.UsesInfoClass(InfoClass, icQuery); 64 | 65 | Result.Status := NtQuerySystemInformation(InfoClass, @Buffer, SizeOf(Buffer), 66 | nil); 67 | end; 68 | 69 | function NtxEnumerateModulesSystem; 70 | var 71 | xMemory: IMemory; 72 | Module: PRtlProcessModuleInformation; 73 | i: Integer; 74 | begin 75 | Result := NtxQuerySystem(SystemModuleInformation, IMemory(xMemory), 76 | SizeOf(TRtlProcessModules)); 77 | 78 | if not Result.IsSuccess then 79 | Exit; 80 | 81 | SetLength(Modules, xMemory.Data.NumberOfModules); 82 | 83 | for i := 0 to High(Modules) do 84 | with Modules[i] do 85 | begin 86 | Module := @xMemory.Data.Modules{$R-}[i]{$IFDEF R+}{$R+}{$ENDIF}; 87 | DllBase := Module.ImageBase; 88 | SizeOfImage := Module.ImageSize; 89 | LoadCount := Module.LoadCount; 90 | FullDllName := String(UTF8String(PAnsiChar(@Module.FullPathName))); 91 | BaseDllName := String(UTF8String(PAnsiChar(@Module.FullPathName[ 92 | Module.OffsetToFileName]))); 93 | 94 | // Include the default drivers directory for names without a path 95 | if Module.OffsetToFileName = 0 then 96 | Insert('\SystemRoot\System32\drivers\', FullDllName, Low(String)); 97 | 98 | // Convert paths to the Win32 format 99 | FullDllName := RtlxNativePathToDosPath(FullDllName); 100 | end; 101 | end; 102 | 103 | end. 104 | -------------------------------------------------------------------------------- /NtUtils.Threads.Worker.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Threads.Worker; 2 | 3 | { 4 | The module provides support for using worker factories (the type of kernel 5 | objects behind thread pools). 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.nttp, Ntapi.ntioapi, NtUtils, NtUtils.Synchronization; 12 | 13 | // Create a worker factory object 14 | function NtxCreateWorkerFactory( 15 | out hxWorkerFactory: IHandle; 16 | [Access(IO_COMPLETION_MODIFY_STATE)] const hxCompletionPort: IHandle; 17 | [in] StartRoutine: TWorkerFactoryRoutine; 18 | [in, opt] StartParameter: Pointer; 19 | MaxThreadCount: Cardinal = 0; 20 | [opt] const ObjectAttributes: IObjectAttributes = nil; 21 | StackReserve: NativeUInt = 0; 22 | StackCommit: NativeUInt = 0 23 | ): TNtxStatus; 24 | 25 | // Query basic information about a worker factory 26 | function NtxQueryWorkerFactory( 27 | [Access(WORKER_FACTORY_QUERY_INFORMATION)] const hxWorkerFactory: IHandle; 28 | out Info: TWorkerFactoryBasicInformation 29 | ): TNtxStatus; 30 | 31 | type 32 | NtxWorkerFactory = class abstract 33 | // Set fixed-size information for a worker factory 34 | class function &Set( 35 | [Access(WORKER_FACTORY_SET_INFORMATION)] const hxWorkerFactory: IHandle; 36 | InfoClass: TWorkerFactoryInfoClass; 37 | const Buffer: T 38 | ): TNtxStatus; static; 39 | end; 40 | 41 | // Shutdown a worker factory 42 | function NtxShutdownWorkerFactory( 43 | [Access(WORKER_FACTORY_SHUTDOWN)] const hxWorkerFactory: IHandle; 44 | out PendingWorkerCount: Cardinal 45 | ): TNtxStatus; 46 | 47 | // Release a worker from a worker factory 48 | function NtxReleaseWorkerFactoryWorker( 49 | [Access(WORKER_FACTORY_RELEASE_WORKER)] const hxWorkerFactory: IHandle 50 | ): TNtxStatus; 51 | 52 | // Mark a worker from a worker factory as being ready 53 | function NtxWorkerFactoryWorkerReady( 54 | [Access(WORKER_FACTORY_READY_WORKER)] const hxWorkerFactory: IHandle 55 | ): TNtxStatus; 56 | 57 | // Wait for a queued task on the I/O completion port of the worker factory 58 | function NtxWaitForWorkViaWorkerFactory( 59 | [Access(WORKER_FACTORY_WAIT)] const hxWorkerFactory: IHandle; 60 | out MiniPacket: TIoCompletionPacket 61 | ): TNtxStatus; 62 | 63 | implementation 64 | 65 | uses 66 | Ntapi.ntdef, Ntapi.ntpsapi, NtUtils.Objects; 67 | 68 | {$BOOLEVAL OFF} 69 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 70 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 71 | 72 | function NtxCreateWorkerFactory; 73 | var 74 | ObjAttr: PObjectAttributes; 75 | hWorkerFactory: THandle; 76 | begin 77 | Result := AttributesRefOrNil(ObjAttr, ObjectAttributes); 78 | 79 | if not Result.IsSuccess then 80 | Exit; 81 | 82 | Result.Location := 'NtCreateWorkerFactory'; 83 | Result.LastCall.Expects(IO_COMPLETION_MODIFY_STATE); 84 | Result.Status := NtCreateWorkerFactory( 85 | hWorkerFactory, 86 | AccessMaskOverride(WORKER_FACTORY_ALL_ACCESS, ObjectAttributes), 87 | ObjAttr, 88 | HandleOrDefault(hxCompletionPort), 89 | NtCurrentProcess, 90 | StartRoutine, 91 | StartParameter, 92 | MaxThreadCount, 93 | StackReserve, 94 | StackCommit 95 | ); 96 | 97 | if Result.IsSuccess then 98 | hxWorkerFactory := Auto.CaptureHandle(hWorkerFactory); 99 | end; 100 | 101 | function NtxQueryWorkerFactory; 102 | begin 103 | Result.Location := 'NtQueryInformationWorkerFactory'; 104 | Result.LastCall.UsesInfoClass(WorkerFactoryBasicInformation, icQuery); 105 | Result.LastCall.Expects(WORKER_FACTORY_QUERY_INFORMATION); 106 | 107 | Result.Status := NtQueryInformationWorkerFactory( 108 | HandleOrDefault(hxWorkerFactory), 109 | WorkerFactoryBasicInformation, 110 | @Info, SizeOf(Info), nil); 111 | end; 112 | 113 | class function NtxWorkerFactory.&Set; 114 | begin 115 | Result.Location := 'NtSetInformationWorkerFactory'; 116 | Result.LastCall.UsesInfoClass(InfoClass, icSet); 117 | Result.LastCall.Expects(WORKER_FACTORY_SET_INFORMATION); 118 | 119 | Result.Status := NtSetInformationWorkerFactory( 120 | HandleOrDefault(hxWorkerFactory), InfoClass, @Buffer, SizeOf(Buffer)); 121 | end; 122 | 123 | function NtxShutdownWorkerFactory; 124 | begin 125 | PendingWorkerCount := 0; 126 | 127 | Result.Location := 'NtShutdownWorkerFactory'; 128 | Result.LastCall.Expects(WORKER_FACTORY_SHUTDOWN); 129 | Result.Status := NtShutdownWorkerFactory(HandleOrDefault(hxWorkerFactory), 130 | Integer(PendingWorkerCount)); 131 | end; 132 | 133 | function NtxReleaseWorkerFactoryWorker; 134 | begin 135 | Result.Location := 'NtReleaseWorkerFactoryWorker'; 136 | Result.LastCall.Expects(WORKER_FACTORY_RELEASE_WORKER); 137 | Result.Status := NtReleaseWorkerFactoryWorker(HandleOrDefault(hxWorkerFactory)); 138 | end; 139 | 140 | function NtxWorkerFactoryWorkerReady; 141 | begin 142 | Result.Location := 'NtWorkerFactoryWorkerReady'; 143 | Result.LastCall.Expects(WORKER_FACTORY_READY_WORKER); 144 | Result.Status := NtWorkerFactoryWorkerReady(HandleOrDefault(hxWorkerFactory)); 145 | end; 146 | 147 | function NtxWaitForWorkViaWorkerFactory; 148 | begin 149 | Result.Location := 'NtWaitForWorkViaWorkerFactory'; 150 | Result.LastCall.Expects(WORKER_FACTORY_WAIT); 151 | Result.Status := NtWaitForWorkViaWorkerFactory( 152 | HandleOrDefault(hxWorkerFactory), MiniPacket); 153 | end; 154 | 155 | end. 156 | -------------------------------------------------------------------------------- /NtUtils.Transactions.Remote.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.Transactions.Remote; 2 | 3 | { 4 | This module allows querying and setting current transaction for threads in 5 | other processes. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.ntpsapi, Ntapi.ntseapi, NtUtils; 12 | 13 | const 14 | PROCESS_GET_THREAD_TRANSACTION = PROCESS_VM_READ; 15 | PROCESS_SET_THREAD_TRANSACTION = PROCESS_VM_WRITE 16 | {$IFDEF Win64}or PROCESS_VM_READ{$ENDIF}; 17 | 18 | PROCESS_SET_PROCESS_TRANSACTION = PROCESS_QUERY_INFORMATION or 19 | PROCESS_SUSPEND_RESUME or PROCESS_SET_THREAD_TRANSACTION; 20 | 21 | THREAD_GET_TRANSACTION = THREAD_QUERY_LIMITED_INFORMATION; 22 | THREAD_SET_TRANSACTION = THREAD_QUERY_LIMITED_INFORMATION; 23 | 24 | // Get a handle value of the current transaction on a remote thread 25 | function RtlxGetTransactionThread( 26 | [Access(PROCESS_GET_THREAD_TRANSACTION)] const hxProcess: IHandle; 27 | [Access(THREAD_GET_TRANSACTION)] const hxThread: IHandle; 28 | out HandleValue: THandle 29 | ): TNtxStatus; 30 | 31 | // Set a handle value of the current transaction on a remote thread 32 | function RtlxSetTransactionThread( 33 | [Access(PROCESS_SET_THREAD_TRANSACTION)] const hxProcess: IHandle; 34 | [Access(THREAD_SET_TRANSACTION)] const hxThread: IHandle; 35 | [opt] HandleValue: THandle 36 | ): TNtxStatus; 37 | 38 | // Set a handle value as a current transaction on all threads in a process 39 | [RequiredPrivilege(SE_DEBUG_PRIVILEGE, rpForBypassingChecks)] 40 | function RtlxSetTransactionProcess( 41 | [Access(PROCESS_SET_PROCESS_TRANSACTION)] const hxProcess: IHandle; 42 | [opt] HandleValue: THandle 43 | ): TNtxStatus; 44 | 45 | implementation 46 | 47 | uses 48 | Ntapi.ntwow64, Ntapi.ntstatus, NtUtils.Threads, NtUtils.Processes, 49 | NtUtils.Memory, NtUtils.Processes.Info; 50 | 51 | {$BOOLEVAL OFF} 52 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 53 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 54 | 55 | function RtlxGetTransactionThread; 56 | var 57 | ThreadInfo: TThreadBasicInformation; 58 | begin 59 | {$IFDEF Win32} 60 | // Although under WoW64 we can work with other WoW64 processes we won't 61 | // since we still need to update 64-bit TEB, so it gets complicated. 62 | if RtlxAssertNotWoW64(Result) then 63 | Exit; 64 | {$ENDIF} 65 | 66 | // Query TEB location for the thread 67 | Result := NtxThread.Query(hxThread, ThreadBasicInformation, ThreadInfo); 68 | 69 | if not Result.IsSuccess then 70 | Exit; 71 | 72 | // Make sure the thread is alive 73 | if not Assigned(ThreadInfo.TebBaseAddress) then 74 | begin 75 | Result.Location := 'RtlxGetTransactionThread'; 76 | Result.Status := STATUS_THREAD_IS_TERMINATING; 77 | Exit; 78 | end; 79 | 80 | // Read the handle value from thread's TEB. 81 | // In case of a WoW64 target it has two TEBs, and both of them should 82 | // store the same handle value. However, 64-bit TEB has precedence, so 83 | // the following code also works for WoW64 processes. 84 | 85 | Result := NtxMemory.Read( 86 | hxProcess, 87 | @ThreadInfo.TebBaseAddress.CurrentTransactionHandle, 88 | HandleValue 89 | ); 90 | end; 91 | 92 | function RtlxSetTransactionThread; 93 | var 94 | ThreadInfo: TThreadBasicInformation; 95 | {$IFDEF Win64} 96 | IsWow64Target: Boolean; 97 | Teb32Offset: Integer; 98 | Teb32: PTeb32; 99 | HandleValue32: Cardinal; 100 | {$ENDIF} 101 | begin 102 | {$IFDEF Win32} 103 | // Although under WoW64 we can work with other WoW64 processes we won't 104 | // since we still need to update 64-bit TEB, so it gets complicated. 105 | if RtlxAssertNotWoW64(Result) then 106 | Exit; 107 | {$ENDIF} 108 | 109 | if not Result.IsSuccess then 110 | Exit; 111 | 112 | // Query TEB location for the thread 113 | Result := NtxThread.Query(hxThread, ThreadBasicInformation, ThreadInfo); 114 | 115 | if not Result.IsSuccess then 116 | Exit; 117 | 118 | // Make sure the thread is alive 119 | if not Assigned(ThreadInfo.TebBaseAddress) then 120 | begin 121 | Result.Location := 'RtlxGetTransactionThread'; 122 | Result.Status := STATUS_THREAD_IS_TERMINATING; 123 | Exit; 124 | end; 125 | 126 | // Write the handle value to thread's TEB 127 | Result := NtxMemory.Write(hxProcess, 128 | @ThreadInfo.TebBaseAddress.CurrentTransactionHandle, 129 | HandleValue 130 | ); 131 | 132 | if not Result.IsSuccess then 133 | Exit; 134 | 135 | // Threads in WoW64 processes have two TEBs, so we should update both of them. 136 | // However, this operation is optional since 64-bit TEB has precedence, 137 | // therefore we ignore errors in the following code. 138 | 139 | {$IFDEF Win64} 140 | if NtxQueryIsWoW64Process(hxProcess, IsWow64Target).IsSuccess and 141 | IsWow64Target then 142 | begin 143 | // 64-bit TEB stores an offset to a 32-bit TEB, read it 144 | if not NtxMemory.Read(hxProcess, @ThreadInfo.TebBaseAddress.WowTebOffset, 145 | Teb32Offset).IsSuccess then 146 | Exit; 147 | 148 | if Teb32Offset = 0 then 149 | Exit; 150 | 151 | HandleValue32 := Cardinal(HandleValue); 152 | Teb32 := PTeb32(NativeInt(ThreadInfo.TebBaseAddress) + Teb32Offset); 153 | 154 | // Write the handle to the 32-bit TEB 155 | NtxMemory.Write(hxProcess, @Teb32.CurrentTransactionHandle, HandleValue32); 156 | end; 157 | {$ENDIF} 158 | end; 159 | 160 | function RtlxSetTransactionProcess; 161 | var 162 | hxThread: IHandle; 163 | IsTerminated: LongBool; 164 | DelayedResumer: IAutoReleasable; 165 | begin 166 | // Suspend the process to avoid race conditions 167 | Result := NtxSuspendProcess(hxProcess); 168 | 169 | if not Result.IsSuccess then 170 | Exit; 171 | 172 | // Resume automatically when we are done 173 | DelayedResumer := NtxDelayedResumeProcess(hxProcess); 174 | 175 | hxThread := nil; 176 | 177 | while NtxGetNextThread(hxProcess, hxThread, 178 | THREAD_QUERY_LIMITED_INFORMATION).HasEntry(Result) do 179 | begin 180 | // Skip terminated threads 181 | Result := NtxThread.Query(hxThread, ThreadIsTerminated, IsTerminated); 182 | 183 | // Update current transaction 184 | if Result.IsSuccess and not IsTerminated then 185 | Result := RtlxSetTransactionThread(hxProcess, hxThread, HandleValue); 186 | 187 | if not Result.IsSuccess then 188 | Exit; 189 | end; 190 | end; 191 | 192 | end. 193 | -------------------------------------------------------------------------------- /NtUtils.WinSafer.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.WinSafer; 2 | 3 | { 4 | This module provides functions for restricting tokens via Safer API. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.ntseapi, Ntapi.WinSafer, NtUtils, NtUtils.Objects; 11 | 12 | type 13 | ISaferHandle = IHandle; 14 | 15 | // Open a Safer level 16 | function SafexOpenLevel( 17 | out hxLevel: ISaferHandle; 18 | ScopeId: TSaferScopeId; 19 | LevelId: TSaferLevelId 20 | ): TNtxStatus; 21 | 22 | // Query Safer level information 23 | function SafexQueryLevel( 24 | hLevel: TSaferHandle; 25 | InfoClass: TSaferObjectInfoClass; 26 | out xMemory: IMemory; 27 | InitialBuffer: Cardinal = 0; 28 | [opt] GrowthMethod: TBufferGrowthMethod = nil 29 | ): TNtxStatus; 30 | 31 | // Query Safer level name 32 | function SafexQueryNameLevel( 33 | hLevel: TSaferHandle; 34 | out Name: String 35 | ): TNtxStatus; 36 | 37 | // Query Safer level description 38 | function SafexQueryDescriptionLevel( 39 | hLevel: TSaferHandle; 40 | out Description: String 41 | ): TNtxStatus; 42 | 43 | // Restricts a token using Safer API level 44 | function SafexComputeSaferToken( 45 | out hxNewToken: IHandle; 46 | [Access(TOKEN_DUPLICATE or TOKEN_QUERY)] hxExistingToken: IHandle; 47 | hLevel: TSaferHandle; 48 | MakeSandboxInert: Boolean = False 49 | ): TNtxStatus; 50 | 51 | // Restricts a token using Safer API level identified by its IDs 52 | function SafexComputeSaferTokenById( 53 | out hxNewToken: IHandle; 54 | [Access(TOKEN_DUPLICATE or TOKEN_QUERY)] const hxExistingToken: IHandle; 55 | ScopeId: TSaferScopeId; 56 | LevelId: TSaferLevelId; 57 | MakeSandboxInert: Boolean = False 58 | ): TNtxStatus; 59 | 60 | implementation 61 | 62 | uses 63 | NtUtils.Tokens, DelphiUtils.AutoObjects, NtUtils.SysUtils; 64 | 65 | {$BOOLEVAL OFF} 66 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 67 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 68 | 69 | type 70 | TSaferAutoHandle = class(TCustomAutoHandle, ISaferHandle, IAutoReleasable) 71 | procedure Release; override; 72 | end; 73 | 74 | procedure TSaferAutoHandle.Release; 75 | begin 76 | if FHandle <> 0 then 77 | SaferCloseLevel(FHandle); 78 | 79 | FHandle := 0; 80 | inherited; 81 | end; 82 | 83 | function SafexOpenLevel; 84 | var 85 | hLevel: TSaferHandle; 86 | begin 87 | Result.Location := 'SaferCreateLevel'; 88 | Result.Win32Result := SaferCreateLevel(ScopeId, LevelId, SAFER_LEVEL_OPEN, 89 | hLevel); 90 | 91 | if Result.IsSuccess then 92 | hxLevel := TSaferAutoHandle.Capture(hLevel); 93 | end; 94 | 95 | function SafexQueryLevel; 96 | var 97 | Required: Cardinal; 98 | begin 99 | Result.Location := 'SaferGetLevelInformation'; 100 | Result.LastCall.UsesInfoClass(InfoClass, icQuery); 101 | 102 | xMemory := Auto.AllocateDynamic(InitialBuffer); 103 | repeat 104 | Required := 0; 105 | Result.Win32Result := SaferGetLevelInformation(hLevel, InfoClass, 106 | xMemory.Data, xMemory.Size, Required); 107 | until not NtxExpandBufferEx(Result, xMemory, Required, GrowthMethod); 108 | end; 109 | 110 | function SafexQueryNameLevel; 111 | var 112 | xMemory: IMemory; 113 | begin 114 | Result := SafexQueryLevel(hLevel, SaferObjectFriendlyName, IMemory(xMemory), 115 | SizeOf(WideChar)); 116 | 117 | if Result.IsSuccess then 118 | Name := RtlxCaptureString(xMemory.Data, xMemory.Size div SizeOf(WideChar)); 119 | end; 120 | 121 | function SafexQueryDescriptionLevel; 122 | var 123 | xMemory: IMemory; 124 | begin 125 | Result := SafexQueryLevel(hLevel, SaferObjectDescription, IMemory(xMemory), 126 | SizeOf(WideChar)); 127 | 128 | if Result.IsSuccess then 129 | Description := RtlxCaptureString(xMemory.Data, 130 | xMemory.Size div SizeOf(WideChar)); 131 | end; 132 | 133 | function SafexComputeSaferToken; 134 | var 135 | hNewToken: THandle; 136 | Flags: TSaferComputeOptions; 137 | begin 138 | // Add support for pseudo-handles on input 139 | Result := NtxExpandToken(hxExistingToken, TOKEN_DUPLICATE or TOKEN_QUERY); 140 | 141 | if not Result.IsSuccess then 142 | Exit; 143 | 144 | Flags := 0; 145 | if MakeSandboxInert then 146 | Flags := Flags or SAFER_TOKEN_MAKE_INERT; 147 | 148 | Result.Location := 'SaferComputeTokenFromLevel'; 149 | Result.LastCall.Expects(TOKEN_DUPLICATE or TOKEN_QUERY); 150 | 151 | Result.Win32Result := SaferComputeTokenFromLevel(hLevel, 152 | hxExistingToken.Handle, hNewToken, Flags); 153 | 154 | if Result.IsSuccess then 155 | hxNewToken := Auto.CaptureHandle(hNewToken); 156 | end; 157 | 158 | function SafexComputeSaferTokenById; 159 | var 160 | hxLevel: ISaferHandle; 161 | begin 162 | Result := SafexOpenLevel(hxLevel, ScopeId, LevelId); 163 | 164 | if Result.IsSuccess then 165 | Result := SafexComputeSaferToken(hxNewToken, hxExistingToken, 166 | hxLevel.Handle, MakeSandboxInert); 167 | end; 168 | 169 | end. 170 | -------------------------------------------------------------------------------- /NtUtils.WinUser.WindowAffinity.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.WinUser.WindowAffinity; 2 | 3 | { 4 | The module allows managing window affinity (i.e., visibility for 5 | screen capture) for windows that belong to other processes. 6 | } 7 | 8 | interface 9 | 10 | uses 11 | Ntapi.WinUser, Ntapi.ntseapi, NtUtils, NtUtils.Shellcode; 12 | 13 | const 14 | WDA_NONE = Ntapi.WinUser.WDA_NONE; 15 | WDA_MONITOR = Ntapi.WinUser.WDA_MONITOR; 16 | WDA_EXCLUDEFROMCAPTURE = Ntapi.WinUser.WDA_EXCLUDEFROMCAPTURE; 17 | 18 | // Determine if a window is visible for screen capturing 19 | function UsrxGetWindowAffinity( 20 | Wnd: THwnd; 21 | out Affinity: Cardinal 22 | ): TNtxStatus; 23 | 24 | // Change whether a window is visible for screen capturing 25 | [RequiredPrivilege(SE_DEBUG_PRIVILEGE, rpForBypassingChecks)] 26 | function UsrxSetWindowAffinity( 27 | Wnd: THwnd; 28 | Affinity: Cardinal; 29 | const Timeout: Int64 = DEFAULT_REMOTE_TIMEOUT 30 | ): TNtxStatus; 31 | 32 | implementation 33 | 34 | uses 35 | Ntapi.WinNt, Ntapi.ntpebteb, Ntapi.ntdef, Ntapi.ntstatus, 36 | NtUtils.Processes.Info, NtUtils.Processes, DelphiUtils.AutoObjects; 37 | 38 | {$BOOLEVAL OFF} 39 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 40 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 41 | 42 | type 43 | // Injected thread requires some context 44 | TPayloadContext = record 45 | SetWindowDisplayAffinity: function ( 46 | hWnd: UIntPtr; 47 | Affinity: Cardinal 48 | ): LongBool; stdcall; 49 | {$IFDEF Win32}WoW64Padding1: Cardinal;{$ENDIF} 50 | 51 | RtlGetLastWin32Error: function: TWin32Error; stdcall; 52 | {$IFDEF Win32}WoW64Padding2: Cardinal;{$ENDIF} 53 | 54 | Window: THwnd; 55 | {$IFDEF Win32}WoW64Padding3: Cardinal;{$ENDIF} 56 | 57 | Affinity: Cardinal; 58 | Reserved: Cardinal; 59 | end; 60 | PDisplayAffinityContext = ^TPayloadContext; 61 | 62 | // This is the function we are going to inject as a thread. Be consistent with 63 | // the raw assembly listing below. 64 | function AffinitySetter(Context: PDisplayAffinityContext): NTSTATUS; stdcall; 65 | begin 66 | if Context.SetWindowDisplayAffinity(Context.Window, Context.Affinity) then 67 | Result := STATUS_SUCCESS 68 | else 69 | Result := NTSTATUS_FROM_WIN32(Context.RtlGetLastWin32Error); 70 | end; 71 | 72 | const 73 | {$IFDEF Win64} 74 | // 64-bit assembly. Be consistent with the function definition above 75 | PayloadAssembly64: array [0..47] of Byte = ( 76 | $53, $48, $83, $EC, $20, $48, $89, $CB, $48, $8B, $4B, $10, $8B, $53, $18, 77 | $FF, $13, $85, $C0, $74, $04, $33, $C0, $EB, $0F, $FF, $53, $08, $81, $E0, 78 | $FF, $FF, $00, $00, $81, $C8, $00, $00, $07, $C0, $48, $83, $C4, $20, $5B, 79 | $C3, $CC, $CC 80 | ); 81 | {$ENDIF} 82 | 83 | // 32-bit assembly. Be consistent with the function definition above 84 | PayloadAssembly32: array [0..47] of Byte = ( 85 | $55, $8B, $EC, $53, $8B, $5D, $08, $8B, $43, $18, $50, $8B, $43, $10, $50, 86 | $FF, $13, $85, $C0, $74, $04, $33, $C0, $EB, $0D, $FF, $53, $08, $25, $FF, 87 | $FF, $00, $00, $0D, $00, $00, $07, $C0, $5B, $5D, $C2, $04, $00, $CC, $CC, 88 | $CC, $CC, $CC 89 | ); 90 | 91 | function UsrxGetWindowAffinity; 92 | begin 93 | Result.Location := 'GetWindowDisplayAffinity'; 94 | Result.Win32Result := GetWindowDisplayAffinity(Wnd, Affinity); 95 | end; 96 | 97 | function UsrxSetWindowAffinity; 98 | var 99 | TID: TThreadId32; 100 | PID: TProcessId32; 101 | hxProcess: IHandle; 102 | TargetIsWoW64: Boolean; 103 | LocalMapping: IMemory; 104 | RemoteMapping: IMemory; 105 | CodeRef: TMemory; 106 | begin 107 | // Determine the creator of the window 108 | Result.Location := 'GetWindowThreadProcessId'; 109 | TID := GetWindowThreadProcessId(Wnd, PID); 110 | Result.Win32Result := TID <> 0; 111 | 112 | if not Result.IsSuccess then 113 | Exit; 114 | 115 | if PID = NtCurrentTeb.ClientID.UniqueProcess then 116 | begin 117 | // The thread belongs to our process, we can access it directly 118 | Result.Location := 'SetWindowDisplayAffinity'; 119 | Result.Win32Result := SetWindowDisplayAffinity(Wnd, Affinity); 120 | Exit; 121 | end; 122 | 123 | // Open the process for code injection 124 | Result := NtxOpenProcess(hxProcess, PID, PROCESS_REMOTE_EXECUTE); 125 | 126 | if not Result.IsSuccess then 127 | Exit; 128 | 129 | // Prevent WoW64 -> Native access 130 | Result := RtlxAssertWoW64Compatible(hxProcess, TargetIsWoW64); 131 | 132 | if not Result.IsSuccess then 133 | Exit; 134 | 135 | {$IFDEF Win64} 136 | if not TargetIsWoW64 then 137 | CodeRef := TMemory.Reference(PayloadAssembly64) 138 | else 139 | {$ENDIF} 140 | CodeRef := TMemory.Reference(PayloadAssembly32); 141 | 142 | // Map a shared memory region with the target 143 | Result := RtlxMapSharedMemory(hxProcess, SizeOf(TPayloadContext) + 144 | CodeRef.Size, IMemory(LocalMapping), RemoteMapping, [mmAllowExecute]); 145 | 146 | if not Result.IsSuccess then 147 | Exit; 148 | 149 | LocalMapping.Data.Window := Wnd; 150 | LocalMapping.Data.Affinity := Affinity; 151 | Move(CodeRef.Address^, LocalMapping.Offset(SizeOf(TPayloadContext))^, 152 | CodeRef.Size); 153 | 154 | // Locate user32 import 155 | Result := RtlxFindKnownDllExport(user32, TargetIsWoW64, 156 | 'SetWindowDisplayAffinity', @LocalMapping.Data.SetWindowDisplayAffinity); 157 | 158 | if not Result.IsSuccess then 159 | Exit; 160 | 161 | // Locate ntdll import 162 | Result := RtlxFindKnownDllExport(ntdll, TargetIsWoW64, 163 | 'RtlGetLastWin32Error', @LocalMapping.Data.RtlGetLastWin32Error); 164 | 165 | if not Result.IsSuccess then 166 | Exit; 167 | 168 | // Create a thread to execute the code and sync with it 169 | Result := RtlxRemoteExecute( 170 | hxProcess, 171 | 'Remote::SetWindowDisplayAffinity', 172 | RemoteMapping.Offset(SizeOf(TPayloadContext)), 173 | CodeRef.Size, 174 | RemoteMapping.Data, 175 | 0, 176 | Timeout, 177 | [RemoteMapping] 178 | ); 179 | end; 180 | 181 | end. 182 | -------------------------------------------------------------------------------- /NtUtils.WinUser.WinstaLock.pas: -------------------------------------------------------------------------------- 1 | unit NtUtils.WinUser.WinstaLock; 2 | 3 | { 4 | This module allows locking and unlocking window stations. 5 | } 6 | 7 | interface 8 | 9 | uses 10 | Ntapi.ntseapi, NtUtils, NtUtils.Shellcode; 11 | 12 | // Lock/unlock the current session's window station 13 | [RequiredPrivilege(SE_DEBUG_PRIVILEGE, rpForBypassingChecks)] 14 | function UsrxLockWindowStation( 15 | Lock: Boolean; 16 | const Timeout: Int64 = DEFAULT_REMOTE_TIMEOUT 17 | ): TNtxStatus; 18 | 19 | implementation 20 | 21 | uses 22 | Ntapi.WinNt, Ntapi.ntdef, Ntapi.ntstatus, Ntapi.ntldr, Ntapi.WinUser, 23 | NtUtils.Ldr, NtUtils.Processes.Snapshots, NtUtils.Processes.Info; 24 | 25 | {$BOOLEVAL OFF} 26 | {$IFOPT R+}{$DEFINE R+}{$ENDIF} 27 | {$IFOPT Q+}{$DEFINE Q+}{$ENDIF} 28 | 29 | // User32.dll has a pair of functions called LockWindowStation and 30 | // UnlockWindowStation. Although any application can call them, only calls 31 | // issued by a registered instance of winlogon.exe will succeed. 32 | // So, we inject a thread to winlogon to execute this call in its context. 33 | 34 | type 35 | TLockerContext = record 36 | GetProcessWindowStation: function: THandle; stdcall; 37 | LockWindowStation: function (hWinStation: THandle): LongBool; stdcall; 38 | RtlGetLastWin32Error: function: TWin32Error; stdcall; 39 | end; 40 | PLockerContext = ^TLockerContext; 41 | 42 | // We are going to execute the following function inside winlogon, so make sure 43 | // to use only functions and variables referenced through the Data parameter. 44 | // Note: be consistent with the raw assembly below (the one we actually use). 45 | function UsrxLockerPayload(Data: PLockerContext): NTSTATUS; stdcall; 46 | begin 47 | if Data.LockWindowStation(Data.GetProcessWindowStation) then 48 | Result := STATUS_SUCCESS 49 | else 50 | Result := NTSTATUS_FROM_WIN32(Data.RtlGetLastWin32Error); 51 | end; 52 | 53 | const 54 | // Be consistent with function code above 55 | {$IFDEF WIN64} 56 | UsrxLockerAsm: array [0..47] of Byte = ( 57 | $53, $48, $83, $EC, $20, $48, $89, $CB, $FF, $13, $48, $89, $C1, $FF, $53, 58 | $08, $85, $C0, $74, $04, $33, $C0, $EB, $0F, $FF, $53, $10, $81, $E0, $FF, 59 | $FF, $00, $00, $81, $C8, $00, $00, $07, $C0, $48, $83, $C4, $20, $5B, $C3, 60 | $CC, $CC, $CC 61 | ); 62 | {$ENDIF} 63 | {$IFDEF WIN32} 64 | UsrxLockerAsm: array [0..39] of Byte = ( 65 | $55, $8B, $EC, $53, $8B, $5D, $08, $FF, $13, $50, $FF, $53, $04, $85, $C0, 66 | $74, $04, $33, $C0, $EB, $0D, $FF, $53, $08, $25, $FF, $FF, $00, $00, $0D, 67 | $00, $00, $07, $C0, $5B, $5D, $C2, $04, $00, $CC 68 | ); 69 | {$ENDIF} 70 | 71 | function GetLockerFunctionName(Lock: Boolean): String; 72 | begin 73 | if Lock then 74 | Result := 'LockWindowStation' 75 | else 76 | Result := 'UnlockWindowStation'; 77 | end; 78 | 79 | function UsrxLockerPrepare( 80 | var Data: TLockerContext; 81 | Lock: Boolean 82 | ): TNtxStatus; 83 | var 84 | hUser32: PDllBase; 85 | begin 86 | // Winlogon always loads user32.dll, so we don't need to check it 87 | Result := LdrxGetDllHandle(user32, hUser32); 88 | 89 | if not Result.IsSuccess then 90 | Exit; 91 | 92 | Result := LdrxGetProcedureAddress(hUser32, 'GetProcessWindowStation', 93 | Pointer(@Data.GetProcessWindowStation)); 94 | 95 | if not Result.IsSuccess then 96 | Exit; 97 | 98 | Result := LdrxGetProcedureAddress(hUser32, 99 | AnsiString(GetLockerFunctionName(Lock)), Pointer(@Data.LockWindowStation)); 100 | 101 | if not Result.IsSuccess then 102 | Exit; 103 | 104 | Result := LdrxCheckDelayedModule(delayed_ntdll); 105 | 106 | if not Result.IsSuccess then 107 | Exit; 108 | 109 | Result := LdrxGetProcedureAddress(delayed_ntdll.DllAddress, 110 | 'RtlGetLastWin32Error', Pointer(@Data.RtlGetLastWin32Error)); 111 | end; 112 | 113 | function UsrxLockWindowStation; 114 | var 115 | hxProcess: IHandle; 116 | LocalMapping: IMemory; 117 | RemoteMapping: IMemory; 118 | begin 119 | {$IFDEF Win32} 120 | // Winlogon always has the same bitness as the OS. So should we. 121 | if RtlxAssertNotWoW64(Result) then 122 | Exit; 123 | {$ENDIF} 124 | 125 | // Find Winlogon and open it for code injection 126 | Result := NtxOpenProcessByName(hxProcess, 'winlogon.exe', 127 | PROCESS_REMOTE_EXECUTE, [pnCurrentSessionOnly]); 128 | 129 | if not Result.IsSuccess then 130 | Exit; 131 | 132 | // Map shared memory region 133 | Result := RtlxMapSharedMemory(hxProcess, SizeOf(TLockerContext) + 134 | SizeOf(UsrxLockerAsm), LocalMapping, RemoteMapping, [mmAllowExecute]); 135 | 136 | if not Result.IsSuccess then 137 | Exit; 138 | 139 | // Prepare the thread parameter 140 | Result := UsrxLockerPrepare(PLockerContext(LocalMapping.Data)^, Lock); 141 | 142 | if not Result.IsSuccess then 143 | Exit; 144 | 145 | Move(UsrxLockerAsm, LocalMapping.Offset(SizeOf(TLockerContext))^, 146 | SizeOf(UsrxLockerAsm)); 147 | 148 | // Create a thread to execute the code and sync with it 149 | Result := RtlxRemoteExecute( 150 | hxProcess, 151 | 'Remote::' + GetLockerFunctionName(Lock), 152 | RemoteMapping.Offset(SizeOf(TLockerContext)), 153 | SizeOf(UsrxLockerAsm), 154 | RemoteMapping.Data, 155 | 0, 156 | Timeout, 157 | [RemoteMapping] 158 | ); 159 | end; 160 | 161 | end. 162 | --------------------------------------------------------------------------------