The program calls NtImpersonateThread in a loop. Since Sandboxie does not implement this function, each function call triggers it to log a message and leak a thread handle.
You can find the binary below.
| program ThreadLeakDemo; | |
| {$APPTYPE CONSOLE} | |
| {$MINENUMSIZE 4} | |
| {$R *.res} | |
| const | |
| ntdll = 'ntdll.dll'; | |
| NtCurrentThread: THandle = THandle(-2); | |
| MILLISEC = -10000; | |
| type | |
| NTSTATUS = type Cardinal; | |
| TSecurityImpersonationLevel = ( | |
| SecurityAnonymous = 0, | |
| SecurityIdentification = 1, | |
| SecurityImpersonation = 2, | |
| SecurityDelegation = 3 | |
| ); | |
| TSecurityQualityOfService = record | |
| Length: Cardinal; | |
| ImpersonationLevel: TSecurityImpersonationLevel; | |
| ContextTrackingMode: Boolean; | |
| EffectiveOnly: Boolean; | |
| end; | |
| function NtImpersonateThread(ServerThreadHandle: THandle; | |
| ClientThreadHandle: THandle; const SecurityQos: TSecurityQualityOfService): | |
| NTSTATUS; stdcall; external ntdll; | |
| function NtDelayExecution(Alertable: Boolean; const [ref] DelayInterval: | |
| Int64): NTSTATUS; stdcall; external ntdll; | |
| procedure Main; | |
| var | |
| QS: TSecurityQualityOfService; | |
| begin | |
| QS := Default(TSecurityQualityOfService); | |
| QS.Length := SizeOf(QS); | |
| QS.ImpersonationLevel := SecurityImpersonation; | |
| while True do | |
| begin | |
| writeln(NtImpersonateThread(NtCurrentThread, NtCurrentThread, QS)); | |
| NtDelayExecution(False, 100 * MILLISEC); | |
| end; | |
| end; | |
| begin | |
| Main; | |
| end. |