Valid HTML 4.01 Transitional Valid CSS Valid SVG 1.0

Me, myself & IT

Tidbits – Tiny Console Applications plus some Scripts

Click the appropriate button to select and display your tidbit, or the last (empty) button to reset your selection and hide the tidbit.

Group Policy Scripts Client Registration MSVC Helper Library

Debug String Monitor Directory Change Notifier Privilege Twiddler

Really Known SIDs Enumerator Security Descriptor Inspector 8dot3 File and Directory Name Changer

Registry INF Dumper Registry Policy Reader Offline Registry Reader

UU Encoder Base64 Encoder  • 

Group Policy Scripts

Purpose
Operation

Purpose

Add startup, shutdown, logon and logoff scripts (really: arbitrary command lines, which are executed during shutdown, startup, logoff and logon) programmatically, without a Group Policy, using just what’s already installed with Windows®.

Operation

Download the setup script SCRIPTS.INF and save it in an arbitrary directory, then right-click the downloaded file to display its context menu and click Install to run it.

Note: on Windows Vista and newer versions of Windows NT, InfDefaultInstall.exe, the application registered for the Install verb of *.inf files, requests administrative privileges and access rights.

Client Registration

Purpose
Background Information
Operation

Purpose

Demonstrate how to add an additional WWW browser, an additional USENET news reader, an additional mail client and an additional calendar with all bells and whistles so that Windows 2000 and newer versions of Windows NT recognise them as fully functional client programs which can be selected by every user as the default program for their associated file types and URL protocols.

Background Information

The MSDN article Default Programs provides background information.

Operation

Download the setup script CLIENTS.INF and save it in an arbitrary directory, then right-click the downloaded file to display its context menu and click Install to run it.

Note: on Windows Vista and newer versions of Windows NT, InfDefaultInstall.exe, the application registered for the Install verb of *.inf files, requests administrative privileges.

Import Library for 32-bit Helper Functions of Microsoft® Visual C Compiler

Purpose
Build Instructions
Use Instructions

Purpose

For code running on the i386 processor architecture, the Microsoft Visual C compiler generates calls to the (almost) undocumented helper routines _alldiv(), _alldvrm(), _allmul(), _allrem(), _allshl() and _allshr() for signed 64-bit integer arithmetic and shift operations, to _aulldiv(), _aulldvrm(), _aullrem() and _aullshr() for unsigned 64-bit integer arithmetic and shift operations, ... _alloca(), _chkstk() ... _CIacos(), _CIasin(), _CIatan(), _CIatan2(), _CIcos(), _CIcosh(), _CIexp(), _CIfmod(), _CIlog(), _CIlog10(), _CIpow(), _CIsin(), _CIsinh(), _CIsqrt(), _CItan(), _CItanh(), _ftol(), ...

Additionally ... memchr(), memcmp(), memcpy(), memmove(), memset(), ... strcat(), strcat_s(), strchr(), strcmp(), strcpy(), strcpy_s(), strcspn(), strlen(), strncat(), strncat_s(), strncmp(), strncpy(), strncpy_s(), strnlen(), strpbrk(), strrchr(), strspn(), strstr(), strtok_s(), strtol(), strtoul(), ... wcscat(), wcscat_s(), wcschr(), wcscmp(), wcscpy(), wcscpy_s(), wcscspn(), wcslen(), wcsncat(), wcsncat_s(), wcsncmp(), wcsncpy(), wcsncpy_s(), wcsnlen(), wcspbrk(), wcsrchr(), wcsspn(), wcsstr(), wcstol(), wcstoul(), ... available from ... on MSDN. ...

Shipped in the MSVCRT libraries, for static linkage. ...

Exported from NTDLL.dll, ... ...

Caveat: the routines for 64-bit integer arithmetic are but SLOW, and the trigonometric floating-point routines are MUCH less accurate than claimed by Intel in their Software Developer’s Manuals.

My article Fast(est) 128÷128-bit and 64÷64-bit Integer Division presents division routines that are 4 to 6 times faster and a (branch-free) multiplication routine that is 3 to 9 times faster!

Build Instructions

Run the following command lines to create the import library MSC_I386.LIB and cleanup afterwards:
LINK.EXE /LIB /DEF /EXPORT:_CIcos /EXPORT:_CIlog /EXPORT:_CIpow /EXPORT:_CIsin /EXPORT:_CIsqrt /EXPORT:_alldiv /EXPORT:_alldvrm /EXPORT:_allmul /EXPORT:_alloca_probe /EXPORT:_alloca_probe_8 /EXPORT:_alloca_probe_16 /EXPORT:_allrem /EXPORT:_allshl /EXPORT:_allshr /EXPORT:_aulldiv /EXPORT:_aulldvrm /EXPORT:_aullrem /EXPORT:_aullshr /EXPORT:_chkstk /EXPORT:_fltused /EXPORT:_ftol /EXPORT:memchr /EXPORT:memcmp /EXPORT:memcpy /EXPORT:memmove /EXPORT:memset /MACHINE:I386 /NAME:NTDLL /NODEFAULTLIB /OUT:MSC_I386.LIB
ERASE MSC_I386.EXP
Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.
Microsoft (R) Library Manager Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library msc_i386.lib and object msc_i386.exp

Use Instructions

Link the import library MSC_I386.LIB instead of or before the MSVCRT libraries.

Support Library for 32-bit Microsoft® Visual C Compiler

Purpose
Build Instructions
Use Instructions

Purpose

... sixfold:
  1. Floating-point intrinsics ...

  2. Compiler Security Checks In Depth /GS (Buffer Security Check) _load_config_used referenced in /SAFESEH (Image has Safe Exception Handlers) IMAGE_LOAD_CONFIG_DIRECTORY32 __security_check_cookie() __fastcall __fastfail()

  3. 64-bit integer arithmetic intrinsics ...

  4. Memory ... ...

  5. Stack ... ...

  6. Thread-local storage Thread Local Storage (TLS) Rules and Limitations for TLS ... variables declared with __declspec (thread) ... ...

Build Instructions

Perform the following 6 simple steps to build the object modules FPU_I386.OBJ, GS_I386.OBJ, I64_I386.OBJ, MEM_I386.OBJ, STK_I386.OBJ and TLS_I386.OBJ from the sources presented hereafter, then create the object library MSC_I386.LIB:
  1. Create the text file FPU_I386.ASM with the following content in an arbitrary, preferable empty directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.686
    	.model	flat, C
    
    single	record	sign:1, exponent:8, mantissa:23
    
    bias	equ	1 shl (width exponent - 1) - 1
    
    	.const
    
    	public	_fltused
    _fltused dword	9876h
    
    	.code
    
    ; MSC internal intrinsic _CIacos():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    ; NOTE: _CIacos() returns correct result for ±0.0 and ±1.0
    
    _CIacos	proc	public
    
    	fld	st(0)		; st(0) = st(1) = argument
    	fmul	st(0), st(0)	; st(0) = argument**2,
    				; st(1) = argument
    	fld1			; st(0) = 1.0,
    				; st(1) = argument**2,
    				; st(2) = argument
    	fsubrp	st(1), st(0)	; st(0) = 1.0 - argument**2,
    				; st(1) = argument
    	fsqrt			; st(0) = square root of (1.0 - argument**2),
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = square root of (1.0 - argument**2)
    	fpatan			; st(0) = inverse circular cosine of argument
    	ret
    
    _CIacos	endp
    
    ; MSC internal intrinsic _CIasin():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    ; NOTE: _CIasin() returns correct result for ±0.0 and ±1.0
    
    _CIasin	proc	public
    
    	fld	st(0)		; st(0) = st(1) = argument
    	fmul	st(0), st(0)	; st(0) = argument**2,
    				; st(1) = argument
    	fld1			; st(0) = 1.0,
    				; st(1) = argument**2,
    				; st(2) = argument
    	fsubrp	st(1), st(0)	; st(0) = 1.0 - argument**2,
    				; st(1) = argument
    	fsqrt			; st(0) = square root of (1.0 - argument**2),
    				; st(1) = argument
    	fpatan			; st(0) = inverse circular sine of argument
    	ret
    
    _CIasin	endp
    
    ; MSC internal intrinsic _CIatan():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    ; NOTE: _CIatan() returns correct result for ±0.0 and ±INFINITY
    
    _CIatan	proc	public
    
    	fld1			; st(0) = 1.0,
    				; st(1) = argument
    	fpatan			; st(0) = inverse circular tangent of (argument / 1.0)
    	ret
    
    _CIatan	endp
    
    ; MSC internal intrinsic _CIatan2():
    ; receives arguments in FPU st(0) and st(1), returns result in FPU st(0)
    
    ; NOTE: _CIatan2() returns correct result for ±0.0 and ±INFINITY
    
    _CIatan2 proc	public
    
    	fxch	st(1)		; st(0) = denominator,
    				; st(1) = numerator
    	fpatan			; st(0) = inverse circular tangent of (numerator / denominator)
    	ret
    
    _CIatan2 endp
    
    ; MSC internal intrinsic _CIcos():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIcos	proc	public
    
    	fcos			; st(0) = cosine of argument
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jnp	short exit	; |argument| < 2**63?
    tau:
    	fld1			; st(0) = 1.0,
    				; st(1) = argument
    	fldpi			; st(0) = pi,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fscale			; st(0) = pi * 2**1,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fstp	st(1)		; st(0) = pi * 2**1,
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = pi * 2**1
    reduce:
    	fprem1			; st(0) = argument modulo (pi * 2**1)
    				;       = argument',
    				; st(1) = pi * 2**1
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jp	short reduce	; |argument'| > pi?
    
    	fstp	st(1)		; st(0) = argument'
    	fcos			; st(0) = cosine of argument'
    exit:
    	ret
    
    _CIcos	endp
    
    ; MSC internal intrinsic _CIcosh():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIcosh	proc	public
    
    	call	_CIexp		; st(0) = e**argument
    	fld1			; st(0) = 1.0,
    				; st(1) = e**argument
    	fdiv	st(0), st(1)	; st(0) = 1.0 / e**argument = e**-argument,
    				; st(1) = e**argument
    	faddp	st(1), st(0)	; st(0) = e**argument + e**-argument
    	push	(bias - 1) shl width mantissa
    				; [esp] = 0x3F000000
    				;       = 0.5F
    	fmul	real4 ptr [esp]	; st(0) = hyperbolic cosine of argument
    	pop	eax
    	ret
    
    _CIcosh	endp
    
    ; MSC internal intrinsic _CIexp():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    ; NOTE: _CIexp() returns correct result for ±INFINITY
    
    _CIexp	proc	public
    
    	fldl2e			; st(0) = log2(e),
    				; st(1) = exponent
    	fmulp	st(1), st(0)	; st(0) = exponent * log2(e)
    if 0
    	fld1			; st(0) = 1.0,
    				; st(1) = exponent * log2(e)
    	fld	st(1)		; st(0) = exponent * log2(e),
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(e)
    	fprem			; st(0) = (exponent * log2(e)) modulo 1.0,
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(e)
    	f2xm1			; st(0) = 2.0**((exponent * log2(e)) modulo 1.0) - 1.0,
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(e)
    	faddp	st(1), st(0)	; st(0) = 2.0**((exponent * log2(e)) modulo 1.0),
    				; st(1) = exponent * log2(e)
    	fscale			; st(0) = e**exponent,
    				; st(1) = exponent * log2(e)
    else
    	fld	st(0)		; st(0) = st(1) = exponent * log2(e)
    	frndint			; st(0) = integer(exponent * log2(e)),
    				; st(1) = exponent * log2(e)
    	fsub	st(1), st(0)	; st(0) = integer(exponent * log2(e)),
    				; st(1) = fraction(exponent * log2(e))
    	fxch	st(1)		; st(0) = fraction(exponent * log2(e)),
    				; st(1) = integer(exponent * log2(e))
    	f2xm1			; st(0) = 2.0**fraction(exponent * log2(e)) - 1.0,
    				; st(1) = integer(exponent * log2(e))
    	fld1			; st(0) = 1.0,
    				; st(1) = 2.0**fraction(exponent * log2(e)) - 1.0,
    				; st(2) = integer(exponent * log2(e))
    	faddp	st(1), st(0)	; st(0) = 2.0**fraction(exponent * log2(e)),
    				; st(1) = integer(exponent * log2(e))
    	fscale			; st(0) = e**exponent,
    				; st(1) = integer(exponent * log2(e))
    endif
    	fstp	st(1)		; st(0) = e**exponent
    	ret
    
    _CIexp	endp
    
    ; MSC internal intrinsic _CIfmod():
    ; receives arguments in FPU st(0) and st(1), returns result in FPU st(0)
    
    _CIfmod	proc	public
    
    reduce:
    	fprem			; st(0) = remainder,
    				; st(1) = divisor
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jp	short reduce
    
    	fstp	st(1)		; st(0) = remainder
    	ret
    
    _CIfmod	endp
    
    ; MSC internal intrinsic _CIlog():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIlog	proc	public
    
    	fldln2			; st(0) = ln(2.0),
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = ln(2.0)
    	fyl2x			; st(0) = natural logarithm of argument
    	ret
    
    _CIlog	endp
    
    ; MSC internal intrinsic _CIlog10():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIlog10 proc	public
    
    	fldlg2			; st(0) = log10(2.0),
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = log10(2.0)
    	fyl2x			; st(0) = logarithm to base 10 of argument
    	ret
    
    _CIlog10 endp
    
    ; MSC internal intrinsic _CIpow():
    ; receives arguments in FPU st(0) and st(1), returns result in FPU st(0)
    
    _CIpow	proc	public
    
    	fxch	st(1)		; st(0) = base,
    				; st(1) = exponent
    	fyl2x			; st(0) = exponent * log2(base)
    	fld1			; st(0) = 1.0,
    				; st(1) = exponent * log2(base)
    	fld	st(1)		; st(0) = exponent * log2(base),
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(base)
    	fprem			; st(0) = (exponent * log2(base)) modulo 1.0
    				;       = fraction(exponent * log2(base)),
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(base)
    	f2xm1			; st(0) = 2.0**fraction(exponent * log2(base)) - 1.0,
    				; st(1) = 1.0,
    				; st(2) = exponent * log2(base)
    	faddp	st(1), st(0)	; st(0) = 2.0**fraction(exponent * log2(base)),
    				; st(1) = exponent * log2(base)
    	fscale			; st(0) = base**exponent,
    				; st(1) = exponent * log2(base)
    	fstp	st(1)		; st(0) = base**exponent
    	ret
    
    _CIpow	endp
    
    ; MSC internal intrinsic _CIsin():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIsin	proc	public
    
    	fsin			; st(0) = sine of argument
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jnp	short exit	; |argument| < 2**63?
    tau:
    	fld1			; st(0) = 1.0,
    				; st(1) = argument
    	fldpi			; st(0) = pi,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fscale			; st(0) = pi * 2**1,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fstp	st(1)		; st(0) = pi * 2**1,
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = pi * 2**1
    reduce:
    	fprem1			; st(0) = argument modulo (pi * 2**1)
    				;       = argument',
    				; st(1) = pi * 2**1
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jp	short reduce	; |argument'| > pi?
    
    	fstp	st(1)		; st(0) = argument'
    	fsin			; st(0) = sine of argument'
    exit:
    	ret
    
    _CIsin	endp
    
    ; MSC internal intrinsic _CIsinh():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIsinh	proc	public
    
    	call	_CIexp		; st(0) = e**argument
    	fld1			; st(0) = 1.0,
    				; st(1) = e**argument
    	fdiv	st(0), st(1)	; st(0) = 1.0 / e**argument = e**-argument,
    				; st(1) = e**argument
    	fsubp	st(1), st(0)	; st(0) = e**argument - e**-argument
    	push	(bias - 1) shl width mantissa
    				; [esp] = 0x3F000000
    				;       = 0.5F
    	fmul	real4 ptr [esp]	; st(0) = hyperbolic sine of argument
    	pop	eax
    	ret
    
    _CIsinh	endp
    
    ; MSC internal intrinsic _CIsqrt():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CIsqrt	proc	public
    
    	fsqrt			; st(0) = square root of radicand
    	ret
    
    _CIsqrt	endp
    
    ; MSC internal intrinsic _CItan():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CItan	proc	public
    
    	fptan			; st(0) = 1.0,
    				; st(1) = tangent of argument
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jnp	short done	; |argument| < 2**63?
    tau:
    	fld1			; st(0) = 1.0,
    				; st(1) = argument
    	fldpi			; st(0) = pi,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fscale			; st(0) = pi * 2**1,
    				; st(1) = 1.0,
    				; st(2) = argument
    	fstp	st(1)		; st(0) = pi * 2**1,
    				; st(1) = argument
    	fxch	st(1)		; st(0) = argument,
    				; st(1) = pi * 2**1
    reduce:
    	fprem1			; st(0) = argument modulo (pi * 2**1)
    				;       = argument',
    				; st(1) = pi * 2**1
    	fstsw	ax		; ax = FPU status word,
    				; ah = B:C3:T:O:P:C2:C1:C0
    	sahf			; SF:ZF:0:AF:0:PF:1:CF = ah
    	jp	short reduce	; |argument'| > pi?
    
    	fstp	st(1)		; st(0) = argument'
    	fptan			; st(0) = 1.0,
    				; st(1) = tangent of argument'
    done:
    	fstp	st(0)		; st(0) = tangent of argument
    	ret
    
    _CItan	endp
    
    ; MSC internal intrinsic _CItanh():
    ; receives argument in FPU st(0), returns result in FPU st(0)
    
    _CItanh	proc	public
    
    	call	_CIexp		; st(0) = e**argument
    	fmul	st(0), st(0)	; st(0) = e**argument * e**argument
    				;       = e**(argument + argument)
    	fld1			; st(0) = 1.0,
    				; st(1) = e**(argument + argument)
    	fadd	st(1), st(0)	; st(0) = 1.0,
    				; st(1) = e**(argument + argument) + 1.0
    	fadd	st(0), st(0)	; st(0) = 2.0,
    				; st(1) = e**(argument + argument) + 1.0
    	fdivrp	st(1), st(0)	; st(0) = 2.0 / (e**(argument + argument) + 1.0)
    	fld1			; st(0) = 1.0,
    				; st(1) = 2.0 / (e**(argument + argument) + 1.0)
    	fsubrp	st(1), st(0)	; st(0) = 1.0 - 2.0 / (e**(argument + argument) + 1.0)
    				;       = hyperbolic tangent of argument
    	ret
    
    _CItanh	endp
    
    ; MSC internal intrinsic _ftol():
    ; receives argument in FPU st(0), returns result in eax
    
    ; NOTE: fistp rounds to nearest (even) integer!
    
    _ftol	proc	public
    
    	push	eax
    	fistp	dword ptr [esp]	; [esp] = integer(argument)
    	pop	eax		; eax = integer(argument)
    	ret
    
    _ftol	endp
    
    ; MSC internal intrinsic _ftol2():
    ; receives argument in FPU st(0), returns result in edx:eax
    
    ; NOTE: fistp rounds to nearest (even) integer!
    
    _ftol2	proc	public
    
    	push	edx
    	push	eax
    	fistp	qword ptr [esp]	; [esp] = integer(argument)
    	pop	eax
    	pop	edx		; edx:eax = integer(argument)
    	ret
    
    _ftol2	endp
    
    ; MSC internal intrinsic _ftol2_sse():
    ; receives argument in FPU st(0), returns result in edx:eax
    
    ; NOTE: fisttp truncates, i.e. rounds towards ±0!
    
    _ftol2_sse proc	public
    
    	push	edx
    	push	eax
    	fisttp	qword ptr [esp]	; [esp] = integer(argument)
    	pop	eax
    	pop	edx		; edx:eax = integer(argument)
    	ret
    
    _ftol2_sse endp
    	end
  2. Create the text file GS_I386.ASM with the following content in the same directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.586
    	.model	flat; C
    
    	extern	___safe_se_handler_count:abs
    	extern	___safe_se_handler_table:dword
    
    _lcu_32	struct	4		; IMAGE_LOAD_CONFIG_DIRECTORY32
    	dword	sizeof _lcu_32
    	dword	"VOID"
    	word	0, 815
    	dword	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    	word	0, 2048
    	dword	0
    	dword	offset ___security_cookie
    	dword	offset ___safe_se_handler_table
    	dword	offset ___safe_se_handler_count
    	dword	0, 0, 0, 0, 0
    _lcu_32	ends
    
    	.const
    
    	public	__load_config_used
    __load_config_used \
    	_lcu_32	<>
    
    	.data
    
    	public	___security_cookie
    ___security_cookie \
    	dword	3141592654
    
    	.code
    
    @__security_check_cookie@4 \
    	proc	public		; void __fastcall __security_check_cookie(dword cookie)
    
    	cmp	ecx, ___security_cookie
    	jne	short fastfail
    	ret
    
    fastfail:
    	xor	ecx, ecx
    	int	41
    
    @__security_check_cookie@4 \
    	endp
    
    ___security_init_cookie \
    	proc	public		; void __cdecl __security_init_cookie(void)
    
    	mov	eax, ___security_cookie
    	cmp	eax, 3141592654
    	je	short init
    
    	test	eax, eax
    	jne	short exit
    init:
    	rdtsc			; eax = low dword of time stamp counter,
    				; edx = high dword of time stamp counter
    	xor	eax, edx	; eax = pseudo random number
    	mov	___security_cookie, eax
    exit:
    	ret
    
    ___security_init_cookie	\
    	endp
    	end
  3. Create the text file I64_I386.ASM with the following content in the same directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.386
    	.model	flat, C
    	.code
    
    ; MSC internal _alldiv():
    ; receives arguments on stack, returns quotient in edx:eax
    
    ; NOTE: _alldiv() can raise 'division by zero' exception; it prevents
    ;       quotient overflow and does not raise 'integer overflow' exception,
    ;       but returns ±2**63 for -2**63 / -1!
    
    _alldiv	proc	public		; sqword _alldiv(sqword dividend, sqword divisor)
    
    	push	ebx
    
    	; determine sign of dividend and compute |dividend|
    
    	mov	edx, [esp+12]	; edx = high dword of dividend
    	mov	eax, [esp+8]	; eax = low dword of dividend
    
    	mov	ebx, edx
    	sar	ebx, 31		; ebx = (dividend < 0) ? -1 : 0
    	xor	eax, ebx
    	xor	edx, ebx	; edx:eax = (dividend < 0) ? ~dividend : dividend
    	sub	eax, ebx
    	sbb	edx, ebx	; edx:eax = (dividend < 0) ? -dividend : dividend
    				;         = |dividend|
    
    	mov	[esp+8], eax	; write |dividend| back on stack
    	mov	[esp+12], edx
    
    	; determine sign of divisor and compute |divisor|
    
    	mov	edx, [esp+20]	; edx = high dword of divisor
    	mov	eax, [esp+16]	; eax = low dword of divisor
    
    	mov	ecx, edx
    	sar	ecx, 31		; ecx = (divisor < 0) ? -1 : 0
    	xor	eax, ecx
    	xor	edx, ecx	; edx:eax = (divisor < 0) ? ~divisor : divisor
    	sub	eax, ecx
    	sbb	edx, ecx	; edx:eax = (divisor < 0) ? -divisor : divisor
    				;         = |divisor|
    
    	mov	[esp+16], eax	; write |divisor| back on stack
    	mov	[esp+20], edx
    
    	xor	ecx, ebx	; ecx = sign of dividend ^ sign of divisor
    				;     = sign of quotient
    	push	ecx		; save sign of quotient on stack
    ifdef TRIVIAL
    	mov	ecx, [esp+16]	; ecx = high dword of dividend
    	cmp	ecx, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, eax	; ecx = (low dword of) divisor
    	mov	eax, [esp+16]	; eax = high dword of dividend
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	mov	eax, [esp+12]	; edx:eax = dividend
    	div	ecx		; eax = (low dword of) quotient,
    				; edx = (low dword of) remainder
    ;;	xor	edx, edx	; edx:eax = |quotient|
    
    	pop	edx		; edx = sign of quotient
    	xor	eax, edx
    	sub	eax, edx
    	sbb	edx, edx	; edx:eax = quotient
    
    	pop	ebx
    	ret	16		; callee restores stack
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    	mov	ebx, eax	; ebx = high dword of quotient
    	mov	eax, [esp+12]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    	mov	edx, ebx	; edx:eax = |quotient|
    
    	pop	ecx		; ecx = sign of quotient
    	xor	eax, ecx
    	xor	edx, ecx
    	sub	eax, ecx
    	sbb	edx, ecx	; edx:eax = quotient
    
    	pop	ebx
    	ret	16		; callee restores stack
    
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	edi
    
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+20]	; edx = high dword of dividend
    	mov	eax, [esp+16]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+24]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+16]
    	mov	ebx, [esp+20]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+28]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	add	eax, edi	; eax = quotient" - (remainder" < 0)
    				;     = (low dword of) |quotient|
    ;;	xor	edx, edx	; edx:eax = |quotient|
    
    	pop	edi
    
    	pop	edx		; edx = sign of quotient
    	xor	eax, edx
    	sub	eax, edx
    	sbb	edx, edx	; edx:eax = quotient
    
    	pop	ebx
    	ret	16		; callee restores stack
    
    _alldiv	endp
    
    ; MSC internal _alldvrm():
    ; receives arguments on stack, returns quotient in edx:eax and remainder in ebx:ecx
    
    ; NOTE: _alldvrm() can raise 'division by zero' exception; it prevents
    ;       quotient overflow and does not raise 'integer overflow' exception,
    ;       but returns ±2**63 for -2**63 / -1!
    
    _alldvrm proc	public		; sqword _alldvrm(sqword dividend, sqword divisor)
    
    	push	edi
    
    	; determine sign of dividend and compute |dividend|
    
    	mov	edx, [esp+12]	; edx = high dword of dividend
    	mov	eax, [esp+8]	; eax = low dword of dividend
    
    	mov	edi, edx
    	sar	edi, 31		; edi = (dividend < 0) ? -1 : 0
    				;     = sign of dividend
    				;     = sign of remainder
    	xor	eax, edi
    	xor	edx, edi	; edx:eax = (dividend < 0) ? ~dividend : dividend
    	sub	eax, edi
    	sbb	edx, edi	; edx:eax = (dividend < 0) ? -dividend : dividend
    				;         = |dividend|
    
    	mov	[esp+8], eax	; write |dividend| back on stack
    	mov	[esp+12], edx
    
    	; determine sign of divisor and compute |divisor|
    
    	mov	edx, [esp+20]	; edx = high dword of divisor
    	mov	eax, [esp+16]	; eax = low dword of divisor
    
    	mov	ecx, edx
    	sar	ecx, 31		; ecx = (divisor < 0) ? -1 : 0
    	xor	eax, ecx
    	xor	edx, ecx	; edx:eax = (divisor < 0) ? ~divisor : divisor
    	sub	eax, ecx
    	sbb	edx, ecx	; edx:eax = (divisor < 0) ? -divisor : divisor
    				;         = |divisor|
    
    	mov	[esp+16], eax	; write |divisor| back on stack
    	mov	[esp+20], edx
    
    	xor	ecx, edi	; ecx = sign of divisor ^ sign of dividend
    				;     = sign of quotient
    	push	ecx		; save sign of quotient on stack
    ifdef TRIVIAL
    	mov	ecx, [esp+16]	; ecx = high dword of dividend
    	cmp	ecx, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, eax	; ecx = (low dword of) divisor
    	mov	eax, [esp+16]	; eax = high dword of dividend
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	xor	ebx, ebx	; ebx = high dword of quotient = 0
    	jmp	short next
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    	mov	ebx, eax	; ebx = high dword of quotient
    next:
    	mov	eax, [esp+12]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    
    	mov	ecx, edx	; ecx = (low dword of) |remainder|
    	mov	edx, ebx	; edx:eax = |quotient|
    ;;	xor	ebx, ebx	; ebx:ecx = |remainder|
    
    	pop	ebx		; ebx = sign of quotient
    	xor	eax, ebx
    	xor	edx, ebx
    	sub	eax, ebx
    	sbb	edx, ebx	; edx:eax = quotient
    
    	mov	ebx, edi	; ebx = sign of remainder
    	xor	ecx, ebx
    	sub	ecx, ebx
    	sbb	ebx, ebx	; ebx:ecx = remainder
    
    	pop	edi
    	ret	16		; callee restores stack
    
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	edi		; save sign of remainder
    
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+20]	; edx = high dword of dividend
    	mov	eax, [esp+16]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+24]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+16]
    	mov	ebx, [esp+20]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+28]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    ifndef JCCLESS
    	jnb	short @f	; remainder" >= 0?
    				; with borrow, it is off by divisor,
    				;  and quotient" is off by 1
    if 0
    	sbb	edi, 0		; edi = quotient" - 1
    				;     = |quotient|
    else
    	dec	edi		; edi = quotient" - 1
    				;     = |quotient|
    endif
    	add	ecx, [esp+24]
    	adc	ebx, [esp+28]	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = |remainder|
    @@:
    else
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	cdq			; edx = (remainder" < 0) ? -1 : 0
    	add	edi, eax	; edi = quotient" - 1
    				;     = |quotient|
    	and	eax, [esp+24]
    	and	edx, [esp+28]	; edx:eax = (remainder" < 0) ? divisor : 0
    	add	ecx, eax
    	adc	ebx, edx	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = |remainder|
    endif ; JCCLESS
    	mov	eax, edi	; eax = (low dword of) |quotient|
    ;;	xor	edx, edx	; edx:eax = |quotient|
    
    	pop	edi		; edi = sign of remainder
    
    	pop	edx		; edx = sign of quotient
    	xor	eax, edx
    	sub	eax, edx
    	sbb	edx, edx	; edx:eax = quotient
    
    	xor	ecx, edi
    	xor	ebx, edi
    	sub	ecx, edi
    	sbb	ebx, edi	; ebx:ecx = remainder
    
    	pop	edi
    	ret	16		; callee restores stack
    
    _alldvrm endp
    
    ; MSC internal _allmul():
    ; receives arguments on stack, returns product in edx:eax
    
    _allmul	proc	public		; sqword _allmul(sqword multiplicand, sqword multiplier)
    
    	push	ebx
    
    	mov	eax, [esp+8]	; eax = low dword of multiplicand
    	mov	ecx, [esp+12]	; ecx = high dword of multiplicand
    	mov	edx, [esp+16]	; edx = low dword of multiplier
    	mov	ebx, [esp+20]	; ebx = high dword of multiplier
    	imul	ecx, edx	; ecx = high dword of multiplicand
    				;     * low dword of multiplier
    	imul	ebx, eax	; ebx = high dword of multiplier
    				;     * low dword of multiplicand
    	mul	edx		; edx:eax = low dword of multiplicand
    				;         * low dword of multiplier
    	add	ecx, ebx	; ecx = high dword of multiplicand
    				;     * low dword of multiplier
    				;     + high dword of multiplier
    				;     * low dword of multiplicand
    	add	edx, ecx	; edx:eax = product % 2**64
    
    	pop	ebx
    	ret	16		; callee restores stack
    
    _allmul	endp
    
    ; MSC internal _allrem():
    ; receives arguments on stack, returns remainder in edx:eax
    
    ; NOTE: _allrem() can raise 'division by zero' exception; it prevents
    ;       quotient overflow and does not raise 'integer overflow' exception!
    
    _allrem	proc	public		; sqword _allrem(sqword dividend, sqword divisor)
    
    	; determine sign of dividend and compute |dividend|
    
    	mov	eax, [esp+8]	; eax = high dword of dividend
    	mov	ecx, [esp+4]	; ecx = low dword of dividend
    
    	cdq			; edx = (dividend < 0) ? -1 : 0
    	xor	ecx, edx
    	xor	eax, edx	; ecx:eax = (dividend < 0) ? ~dividend : dividend
    	sub	ecx, edx
    	sbb	eax, edx	; ecx:eax = (dividend < 0) ? -dividend : dividend
    				;         = |dividend|
    
    	mov	[esp+4], ecx	; write |dividend| back on stack
    	mov	[esp+8], eax
    
    	push	edx		; save sign of dividend on stack
    
    	; determine sign of divisor and compute |divisor|
    
    	mov	edx, [esp+20]	; edx = high dword of divisor
    	mov	eax, [esp+16]	; eax = low dword of divisor
    
    	mov	ecx, edx
    	sar	ecx, 31		; ecx = (divisor < 0) ? -1 : 0
    	xor	eax, ecx
    	xor	edx, ecx	; edx:eax = (divisor < 0) ? ~divisor : divisor
    	sub	eax, ecx
    	sbb	edx, ecx	; edx:eax = (divisor < 0) ? -divisor : divisor
    				;         = |divisor|
    
    	mov	[esp+16], eax	; write |divisor| back on stack
    	mov	[esp+20], edx
    ifdef TRIVIAL
    	mov	ecx, [esp+12]	; ecx = high dword of dividend
    	cmp	ecx, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, eax	; ecx = (low dword of) divisor
    	mov	eax, [esp+12]	; eax = high dword of dividend
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	jmp	short next
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    next:
    	mov	eax, [esp+8]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    	mov	eax, edx	; eax = (low dword of) |remainder|
    ;;	xor	edx, edx	; edx:eax = |remainder|
    
    	pop	edx		; edx = sign of remainder
    	xor	eax, edx
    	sub	eax, edx
    	sbb	edx, edx	; edx:eax = remainder
    
    	ret	16		; callee restores stack
    
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	ebx
    	push	edi
    
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+20]	; edx = high dword of dividend
    	mov	eax, [esp+16]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+24]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+16]
    	mov	ebx, [esp+20]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+28]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    ifndef JCCLESS
    	jnb	short @f	; remainder" >= 0?
    				; with borrow, it is off by divisor
    				;  (and quotient" is off by 1)
    	add	ecx, [esp+24]
    	adc	ebx, [esp+28]	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = remainder
    @@:
    	mov	eax, ecx
    	mov	edx, ebx	; edx:eax = |remainder|
    else
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	cdq			; edx = (remainder" < 0) ? -1 : 0
    	and	eax, [esp+24]
    	and	edx, [esp+28]	; edx:eax = (remainder" < 0) ? divisor : 0
    	add	eax, ecx
    	adc	edx, ebx	; edx:eax = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = |remainder|
    endif ; JCCLESS
    	pop	edi
    	pop	ebx
    
    	pop	ecx		; ecx = sign of remainder
    	xor	eax, ecx
    	xor	edx, ecx
    	sub	eax, ecx
    	sbb	edx, ecx	; edx:eax = remainder
    
    	ret	16		; callee restores stack
    
    _allrem	endp
    
    ; MSC internal _allshl():
    ; receives arguments in edx:eax and cl, returns result in edx:eax
    
    ; NOTE: _allshl() applies shift count modulo 64
    
    _allshl	proc	public		; sqword _allshl(sqword value, byte count)
    
    	test	cl, 32
    	jnz	short @f	; count > 31?
    
    	shld	edx, eax, cl
    	shl	eax, cl
    	ret
    @@:
    	mov	edx, eax
    	shl	edx, cl
    	xor	eax, eax
    	ret
    
    _allshl	endp
    
    ; MSC internal _allshr():
    ; receives arguments in edx:eax and cl, returns result in edx:eax
    
    ; NOTE: _allshr() applies shift count modulo 64
    
    _allshr	proc	public		; sqword _allshr(sqword value, byte count)
    
    	test	cl, 32
    	jnz	short @f	; count > 31?
    
    	shrd	eax, edx, cl
    	sar	edx, cl
    	ret
    @@:
    	mov	eax, edx
    	sar	eax, cl
    	sar	edx, 31
    	ret
    
    _allshr	endp
    
    ; MSC internal _aulldiv():
    ; receives arguments on stack, returns quotient in edx:eax
    
    ; NOTE: _aulldiv() can raise 'division by zero' exception!
    
    _aulldiv proc	public		; qword _aulldiv(qword dividend, qword divisor)
    
    	mov	edx, [esp+16]	; edx = high dword of divisor
    ifdef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    	cmp	eax, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, [esp+12]	; ecx = (low dword of) divisor
    ifndef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    endif
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	mov	eax, [esp+4]	; edx:eax = dividend
    	div	ecx		; eax = (low dword of) quotient,
    				; edx = (low dword of) remainder
    	xor	edx, edx	; edx:eax = quotient
    
    	ret	16		; callee restores stack
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    	push	eax		; [esp] = high dword of quotient
    	mov	eax, [esp+8]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    	pop	edx		; edx:eax = quotient
    
    	ret	16		; callee restores stack
    ifdef TRIVIAL
    	; dividend < divisor
    trivial:
    	xor	eax, eax
    	xor	edx, edx	; edx:eax = quotient = 0
    
    	ret	16		; callee restores stack
    endif
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	ebx
    	push	edi
    
    	mov	eax, [esp+20]	; edx:eax = divisor
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+16]	; edx = high dword of dividend
    	mov	eax, [esp+12]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+20]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+12]
    	mov	ebx, [esp+16]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+24]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    if 0
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	add	eax, edi	; eax = quotient" - (remainder" < 0)
    				;     = (low dword of) quotient
    	xor	edx, edx	; edx:eax = quotient
    else
    	xor	edx, edx	; edx = high dword of quotient = 0
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    	mov	eax, edi	; eax = quotient"
    	sbb	eax, edx	; eax = quotient" - (remainder" < 0)
    				;     = (low dword of) quotient
    endif
    	pop	edi
    	pop	ebx
    	ret	16		; callee restores stack
    
    _aulldiv endp
    
    ; MSC internal _aulldvrm():
    ; receives arguments on stack, returns quotient in edx:eax and remainder in ebx:ecx
    
    ; NOTE: _aulldvrm() can raise 'division by zero' exception!
    
    _aulldvrm proc	public		; qword _aulldvrm(qword dividend, qword divisor)
    
    	mov	edx, [esp+16]	; edx = high dword of divisor
    ifdef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    	cmp	eax, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, [esp+12]	; ecx = (low dword of) divisor
    ifndef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    endif
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	mov	eax, [esp+4]	; edx:eax = dividend
    	div	ecx		; eax = (low dword of) quotient,
    				; edx = (low dword of) remainder
    	mov	ecx, edx	; ecx = (low dword of) remainder
    	xor	ebx, ebx	; ebx:ecx = remainder
    	xor	edx, edx	; edx:eax = quotient
    
    	ret	16		; callee restores stack
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    	mov	ebx, eax	; ebx = high dword of quotient
    	mov	eax, [esp+4]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    	mov	ecx, edx	; ecx = (low dword of) remainder
    	mov	edx, ebx	; edx:eax = quotient
    	xor	ebx, ebx	; ebx:ecx = remainder
    
    	ret	16		; callee restores stack
    ifdef TRIVIAL
    	; dividend < divisor
    trivial:
    	mov	ecx, [esp+4]	; ecx = low dword of dividend
    	mov	ebx, eax	; ebx:ecx = remainder = dividend
    	xor	eax, eax
    	xor	edx, edx	; edx:eax = quotient = 0
    
    	ret	16		; callee restores stack
    endif
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	edi
    
    	mov	eax, [esp+16]	; edx:eax = divisor
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+12]	; edx = high dword of dividend
    	mov	eax, [esp+8]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+16]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+8]
    	mov	ebx, [esp+12]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+20]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    ifndef JCCLESS
    	jnb	short @f	; remainder" >= 0?
    				; with borrow, it is off by divisor,
    				;  and quotient" is off by 1
    if 0
    	sbb	edi, 0		; edi = quotient" - 1
    				;     = quotient
    else
    	dec	edi		; edi = quotient" - 1
    				;     = quotient
    endif
    	add	ecx, [esp+16]
    	adc	ebx, [esp+20]	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = remainder
    @@:
    else
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	cdq			; edx = (remainder" < 0) ? -1 : 0
    	add	edi, eax	; edi = quotient" - 1
    				;     = quotient
    	and	eax, [esp+16]
    	and	edx, [esp+20]	; edx:eax = (remainder" < 0) ? divisor : 0
    	add	ecx, eax
    	adc	ebx, edx	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = remainder
    endif ; JCCLESS
    	mov	eax, edi	; eax = (low dword of) quotient
    	xor	edx, edx	; edx:eax = quotient
    
    	pop	edi
    	ret	16		; callee restores stack
    
    _aulldvrm endp
    
    ; MSC internal _aullrem():
    ; receives arguments on stack, returns remainder in edx:eax
    
    ; NOTE: _aullrem() can raise 'division by zero' exception!
    
    _aullrem proc	public		; qword _aullrem(qword dividend, qword divisor)
    
    	mov	edx, [esp+16]	; edx = high dword of divisor
    ifdef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    	cmp	eax, edx
    	jb	short trivial	; (high dword of) dividend < (high dword of) divisor?
    endif
    	bsr	ecx, edx	; ecx = index of leading '1' bit in high dword of divisor
    	jnz	short extended	; high dword of divisor <> 0?
    
    	; high dword of divisor = 0 (so high dword of remainder will be 0 too)
    
    	mov	ecx, [esp+12]	; ecx = (low dword of) divisor
    ifndef TRIVIAL
    	mov	eax, [esp+8]	; eax = high dword of dividend
    endif
    	cmp	eax, ecx
    	jae	short long	; high dword of dividend >= divisor?
    
    	; perform normal division
    normal:
    	mov	edx, eax	; edx = high dword of dividend
    	mov	eax, [esp+4]	; edx:eax = dividend
    	div	ecx		; eax = (low dword of) quotient,
    				; edx = (low dword of) remainder
    	mov	eax, edx	; eax = (low dword of) remainder
    	xor	edx, edx	; edx:eax = remainder
    
    	ret	16		; callee restores stack
    
    	; perform "long" alias "schoolbook" division
    long:
    ;;	xor	edx, edx	; edx:eax = high dword of dividend
    	div	ecx		; eax = high dword of quotient,
    				; edx = high dword of remainder'
    	mov	eax, [esp+4]	; eax = low dword of dividend
    	div	ecx		; eax = low dword of quotient,
    				; edx = (low dword of) remainder
    	mov	eax, edx	; eax = (low dword of) remainder
    	xor	edx, edx	; edx:eax = remainder
    
    	ret	16		; callee restores stack
    ifdef TRIVIAL
    	; dividend < divisor
    trivial:
    	mov	edx, eax
    	mov	eax, [esp+4]	; edx:eax = remainder = dividend
    
    	ret	16		; callee restores stack
    endif
    	; high dword of divisor <> 0 (so high dword of quotient will be 0):
    	; perform "extended & adjusted" division
    extended:
    	push	ebx
    	push	edi
    
    	mov	eax, [esp+20]	; edx:eax = divisor
    	not	ecx		; ecx = number of leading '0' bits in (high dword of) divisor
    	shld	edx, eax, cl	; edx = divisor / 2**(index + 1)
    				;     = divisor'
    ;;	shl	eax, cl
    	mov	ebx, edx	; ebx = divisor'
    
    	mov	edx, [esp+16]	; edx = high dword of dividend
    	mov	eax, [esp+12]	; eax = low dword of dividend
    ifndef JCCLESS
    	xor	edi, edi	; edi = high dword of quotient' = 0
    
    	cmp	edx, ebx
    	jb	short @f	; high dword of dividend < divisor'?
    
    	; high dword of dividend >= divisor':
    	; subtract divisor' from high dword of dividend to prevent possible
    	; division overflow and set most significant bit of quotient"
    
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    				;     = high dword of dividend'
    	inc	edi		; edi = high dword of quotient' = 1
    @@:
    else
    	sub	edx, ebx	; edx = high dword of dividend - divisor'
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	and	edi, ebx	; edi = (high dword of dividend < divisor') ? divisor' : 0
    	add	edx, edi	; edx = high dword of dividend
    				;     - (high dword of dividend < divisor') ? 0 : divisor'
    				;     = high dword of dividend'
    	neg	edi		; CF = (high dword of dividend < divisor')
    	sbb	edi, edi	; edi = (high dword of dividend < divisor') ? -1 : 0
    	inc	edi		; edi = (high dword of dividend < divisor') ? 0 : 1
    				;     = high dword of quotient'
    endif ; JCCLESS
    	; high dword of dividend' < divisor'
    
    	div	ebx		; eax = dividend' / divisor'
    				;     = low dword of quotient',
    				; edx = remainder'
    	shld	edi, eax, cl	; edi = quotient' / 2**(index + 1)
    				;     = dividend / divisor
    				;     = quotient"
    ;;	shl	eax, cl
    
    	mov	eax, [esp+20]	; eax = low dword of divisor
    	mul	edi		; edx:eax = low dword of divisor * quotient"
    
    	mov	ecx, [esp+12]
    	mov	ebx, [esp+16]	; ebx:ecx = dividend
    	sub	ecx, eax
    	sbb	ebx, edx	; ebx:ecx = dividend - low dword of divisor * quotient"
    
    	mov	eax, [esp+24]	; eax = high dword of divisor
    	imul	eax, edi	; eax = high dword of divisor * quotient"
    
    	sub	ebx, eax	; ebx:ecx = dividend - divisor * quotient"
    				;         = remainder"
    ifndef JCCLESS
    	jnb	short @f	; remainder" >= 0?
    				; with borrow, it is off by divisor
    				;  (and quotient" is off by 1)
    	add	ecx, [esp+20]
    	adc	ebx, [esp+24]	; ebx:ecx = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = remainder
    @@:
    	mov	eax, ecx
    	mov	edx, ebx	; edx:eax = remainder
    else
    	sbb	eax, eax	; eax = (remainder" < 0) ? -1 : 0
    	cdq			; edx = (remainder" < 0) ? -1 : 0
    	and	eax, [esp+20]
    	and	edx, [esp+24]	; edx:eax = (remainder" < 0) ? divisor : 0
    	add	eax, ecx
    	adc	edx, ebx	; edx:eax = remainder" + divisor
    				;         = dividend - divisor * (quotient" - 1)
    				;         = dividend - divisor * quotient
    				;         = remainder
    endif ; JCCLESS
    	pop	edi
    	pop	ebx
    	ret	16		; callee restores stack
    
    _aullrem endp
    
    ; MSC internal _aullshr():
    ; receives arguments in edx:eax and cl, returns result in edx:eax
    
    ; NOTE: _aullshr() applies shift count modulo 64
    
    _aullshr proc	public		; qword _aullshr(qword value, byte count)
    
    	test	cl, 32
    	jnz	short @f	; count > 31?
    
    	shrd	eax, edx, cl
    	shr	edx, cl
    	ret
    @@:
    	mov	eax, edx
    	shr	eax, cl
    	xor	edx, edx
    	ret
    
    _aullshr endp
    	end
  4. Create the text file MEM_I386.ASM with the following content in the same directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.386
    	.model	flat, C
    	.code
    
    memchr	proc	public		; void *memchr(void *buffer, int cb, size_t count)
    
    	mov	edx, edi
    	mov	ecx, [esp+12]	; ecx = count
    	mov	eax, [esp+8]	; eax = cb
    	mov	edi, [esp+4]	; edi = buffer
    	repne	scasb
    	neg	ecx
    	sbb	eax, eax	; eax = (ecx = 0) ? 0 : -1
    	add	edi, eax	; edi = (ecx = 0) ? * : memchr
    	and	eax, edi	; eax = (ecx = 0) ? 0 : memchr
    	mov	edi, edx
    	ret
    
    memchr	endp
    
    memcmp	proc	public		; int memcmp(void *left, void *right, size_t count)
    
    	mov	ecx, [esp+12]	; ecx = count
    	mov	edx, [esp+8]	; edx = right
    	mov	eax, [esp+4]	; eax = left
    	cmp	eax, edx
    	je	short same	; left = right?
    
    	push	edi
    	push	esi
    	mov	edi, eax	; edi = left
    	mov	esi, edx	; esi = right
    	xor	eax, eax	; eax = 0,
    				; CF = 0, ZF = 1 (required when ecx is zero)
    	repe	cmpsb
    	pop	esi
    	pop	edi
    	seta	al		; eax = (left > right) ? 1 : 0
    	sbb	eax, 0		; eax = (left > right) ? 1 : (left < right) ? -1 : 0
    	ret
    same:
    	xor	eax, eax
    	ret
    
    memcmp	endp
    
    memcpy	proc	public		; void *memcpy(void *destination, void *source, size_t count)
    
    	mov	ecx, [esp+12]	; ecx = count
    	mov	edx, [esp+8]	; edx = source
    	mov	eax, [esp+4]	; eax = destination
    	cmp	eax, edx
    	je	short same	; destination = source?
    
    	push	edi
    	push	esi
    	mov	edi, eax	; edi = destination
    	mov	esi, edx	; esi = source
    	rep	movsb
    	pop	esi
    	pop	edi
    same:
    	ret
    
    memcpy	endp
    
    memmove	proc	public		; void *memmove(void *destination, void *source, size_t count)
    
    	mov	ecx, [esp+12]	; ecx = count
    	mov	edx, [esp+8]	; edx = source
    	mov	eax, [esp+4]	; eax = destination
    	cmp	eax, edx
    	je	short same	; destination = source?
    
    	push	edi
    	push	esi
    	mov	edi, eax	; edi = destination
    	mov	esi, edx	; esi = source
    	jb	short default	; destination < source?
    
    ;;	add	edx, ecx
    ;;	cmp	edx, eax
    ;;	jbe	short default	; source + count <= destination?
    overlap:
    	add	edi, ecx
    	add	esi, ecx
    	dec	edi
    	dec	esi
    	std
    default:
    	rep	movsb
    	cld
    	pop	esi
    	pop	edi
    same:
    	ret
    
    memmove	endp
    
    memrchr	proc	public		; void *memrchr(void *buffer, int cb, size_t count)
    
    	mov	edx, edi
    	mov	ecx, [esp+12]	; ecx = count
    	mov	eax, [esp+8]	; eax = cb
    	mov	edi, [esp+4]	; edi = buffer
    	lea	edi, [edi+ecx-1]; edi = end of buffer
    	std
    	repne	scasb
    	cld
    	neg	ecx
    	sbb	eax, eax	; eax = (ecx = 0) ? 0 : -1
    	sub	edi, eax	; edi = (ecx = 0) ? * : memrchr
    	and	eax, edi	; eax = (ecx = 0) ? 0 : memrchr
    	mov	edi, edx
    	ret
    
    memrchr	endp
    
    memset	proc	public		; void *memset(void *buffer, int cb, size_t count)
    
    	mov	edx, edi
    	mov	ecx, [esp+12]	; ecx = count
    	mov	eax, [esp+8]	; eax = cb
    	mov	edi, [esp+4]	; edi = buffer
    	rep	stosb
    	mov	eax, [esp+4]	; eax = buffer
    	mov	edi, edx
    	ret
    
    memset	endp
    
    	end
  5. Create the text file STK_I386.ASM with the following content in the same directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.686
    	.model	flat, C
    
    _nt_tib	struct	4		; thread information block
    chain	dword	?		; address of first exception registration record
    base	dword	?		; stack base
    limit	dword	?		; stack limit
    	dword	?		; address of subsystem thread information block
    fiber	dword	?		; fiber data
    pointer	dword	?		; arbitrary user pointer
    self	dword	?		; address of _nt_tib
    _nt_tib	ends
    
    	.code
    
    ; MSC internal intrinsic _alloca() alias _chkstk():
    ; receives argument in eax, returns result in esp
    
    ; NOTE: _alloca() must preserve ALL argument registers;
    ;       it can raise 'stack overflow' exception!
    
    ;;	alias	<_chkstk> = <_alloca_probe_16>
    
    _alloca_probe_16 proc	public	; void *_alloca_probe_16(dword size)
    _chkstk	proc	public		; void _chkstk(dword size)
    
    	push	ebx		; decrement stack pointer, save register
    	lea	ebx, [esp+8]	; ebx = stack pointer of caller
    	sub	ebx, eax	; ebx = new (unaligned) stack pointer
    
    	sbb	eax, eax	; eax = (ebx < 0) ? -1 : 0
    	not	eax		; eax = (ebx < 0) ? 0 : -1
    	shl	eax, 4		; eax = (ebx < 0) ? 0 : -16
    	and	eax, ebx	; eax = (ebx < 0) ? 0 : new (aligned) stack pointer
    
    	assume	fs:nothing
    	mov	ebx, fs:[_nt_tib.limit]
    				; ebx = (current) stack limit
    	cmp	ebx, eax
    	jna	short done	; stack limit not above new stack pointer?
    probe:
    	sub	ebx, 4096	; ebx = next stack page
    	test	ebx, [ebx]	; probe next stack page, eventually raise
    				; 'guard page' or 'stack overflow' exception
    	cmp	ebx, eax
    	ja	short probe	; stack limit above new stack pointer?
    done:
    	pop	ebx		; restore register
    	xchg	eax, esp	; esp = new stack pointer,
    				; eax = old stack pointer
    	push	[eax]
    	ret
    
    _chkstk	endp
    _alloca_probe_16 endp
    	end
  6. Create the text file TLS_I386.ASM with the following content in the same directory:

    ; Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.386
    	.model	flat, C
    
    	public	_tls_array
    _tls_array equ	44		; offset of 'ThreadLocalStoragePointer' member in TEB
    
    _tls_32	struct	4		; IMAGE_TLS_DIRECTORY32
    	dword	offset _tls_start
    	dword	offset _tls_end
    	dword	offset _tls_index
    	dword	0		; no callback functions!
    	dword	0		; BUG: the module loader does NOT support the 'SizeOfZeroFill' member!
    	dword	0
    _tls_32	ends
    
    _tls_start segment alias(".tls")
    _tls_start ends
    
    _tls_end segment alias(".tls$zzz") byte
    _tls_end ends
    
    _tls_data segment alias(".data$T") dword
    	public	_tls_index
    _tls_index dword -1		; NOTE: updated by the module loader!
    _tls_data ends
    
    _tls_rdata segment alias(".rdata$T") dword readonly
    	public	_tls_used
    _tls_used _tls_32 <>
    _tls_rdata ends
    	end
  7. Run the following command lines to generate the object modules FPU_I386.OBJ, GS_I386.OBJ, I64_I386.OBJ, MEM_I386.OBJ, STK_I386.OBJ and TLS_I386.OBJ from the assembly source files created in steps 1. to 6., build the object library MSC_I386.LIB and cleanup afterwards:

    SET ML=/Cp /Cx /c /safeseh /W3 /X
    ML.EXE FPU_I386.ASM
    ML.EXE GS_I386.ASM
    ML.EXE /DJCCLESS I64_I386.ASM
    ML.EXE MEM_I386.ASM
    ML.EXE STK_I386.ASM
    ML.EXE TLS_I386.ASM
    LINK.EXE /LIB /MACHINE:I386 /NODEFAULTLIB /OUT:MSC_I386.LIB FPU_I386.OBJ GS_I386.OBJ I64_I386.OBJ MEM_I386.OBJ STK_I386.OBJ TLS_I386.OBJ
    ERASE FPU_I386.OBJ GS_I386.OBJ I64_I386.OBJ MEM_I386.OBJ STK_I386.OBJ TLS_I386.OBJ
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: FPU_I386.ASM
    
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: GS_I386.ASM
    
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: I64_I386.ASM
    
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: MEM_I386.ASM
    
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: STK_I386.ASM
    
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: TLS_I386.ASM
    
    Microsoft (R) Library Manager Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Use Instructions

Link the object library MSC_I386.LIB instead of or before the MSVCRT libraries.

Debug String Monitor

Purpose
Background Information
Implementation and Build Details
Source and Build Instructions

Purpose

Monitor debug strings written from all processes running in the current (user) session with the Win32 function OutputDebugString().

Background Information

In Win32 processes that are run under a debugger, debug strings written with the Win32 function OutputDebugString() are catched and typically displayed by the debugger.

In Win32 processes that are not run under a debugger, the Win32 function OutputDebugString() checks whether the shared memory section DBWIN_BUFFER as well as the events DBWIN_BUFFER_READY and DBWIN_DATA_READY exist; if yes, it waits until the event DBWIN_BUFFER_READY is signaled, writes the process identification and its argument into the shared memory section DBWIN_BUFFER, signals the event DBWIN_DATA_READY and returns to its caller.

Implementation and Build Details

Debug String Monitor is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source and Build Instructions

Perform the following 2 simple steps to build the console application Debug String Monitor from the source presented hereafter.
  1. Create the text file DBWINNER.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    const	LPCSTR	szCtrlType[] = {"Ctrl-C",
    		                "Ctrl-Break",
    		                "Ctrl-Close",
    		                NULL,
    		                NULL,
    		                "Ctrl-Logoff",
    		                "Ctrl-Shutdown"};
    
    BOOL	WINAPI	CtrlHandler(DWORD dwCtrlType)
    {
    	switch (dwCtrlType)
    	{
    	case CTRL_C_EVENT:
    	case CTRL_BREAK_EVENT:
    	case CTRL_CLOSE_EVENT:
    	case CTRL_LOGOFF_EVENT:
    	case CTRL_SHUTDOWN_EVENT:
    
    		OutputDebugStringA(szCtrlType[dwCtrlType]);
    
    		return TRUE;
    
    	default:
    		return FALSE;
    	}
    }
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    typedef	struct	_dbwin_buffer
    {
    	DWORD	dwProcessId;
    	CHAR	szString[4096 - sizeof(DWORD)];
    } DBWIN_BUFFER;
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	DBWIN_BUFFER	*lpDBWin;
    
    	HANDLE	hDBWin;
    	HANDLE	hDBWinBuffer;
    	HANDLE	hDBWinData;
    	DWORD	dwDBWinData;
    	DWORD	dwString;
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwProcessId = 0;
    	DWORD	dwCurrentProcessId = GetCurrentProcessId();
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		hDBWin = CreateFileMapping(INVALID_HANDLE_VALUE,
    		                           (LPSECURITY_ATTRIBUTES) NULL,
    		                           PAGE_READWRITE,
    		                           0,
    		                           sizeof(DBWIN_BUFFER),
    		                           L"DBWIN_BUFFER");
    		dwError = GetLastError();
    
    		if (hDBWin == NULL)
    			PrintConsole(hConsole,
    			             L"CreateFileMapping() returned error %lu\n",
    			             dwError);
    		else
    		{
    			if (dwError == ERROR_ALREADY_EXISTS)
    				PrintConsole(hConsole,
    				             L"Shared section \'DBWIN_BUFFER\' already created by another process!\n");
    			else
    			{
    				hDBWinBuffer = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
    				                           FALSE,
    				                           FALSE,
    				                           L"DBWIN_BUFFER_READY");
    				dwError = GetLastError();
    
    				if (hDBWinBuffer == NULL)
    					PrintConsole(hConsole,
    					             L"CreateEvent() returned error %lu\n",
    					             dwError);
    				else
    				{
    					if (dwError == ERROR_ALREADY_EXISTS)
    						PrintConsole(hConsole,
    						             L"Event \'DBWIN_BUFFER_READY\' already created by another process!\n");
    					else
    					{
    						hDBWinData = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
    						                         FALSE,
    						                         FALSE,
    						                         L"DBWIN_DATA_READY");
    						dwError = GetLastError();
    
    						if (hDBWinData == NULL)
    							PrintConsole(hConsole,
    							             L"CreateEvent() returned error %lu\n",
    							             dwError);
    						else
    						{
    							if (dwError == ERROR_ALREADY_EXISTS)
    								PrintConsole(hConsole,
    								             L"Event \'DBWIN_DATA_READY\' already created by another process!\n");
    							else
    							{
    								lpDBWin = MapViewOfFile(hDBWin,
    								                        SECTION_MAP_READ | SECTION_MAP_WRITE,
    								                        0, 0, 0);
    
    								if (lpDBWin == NULL)
    									PrintConsole(hConsole,
    									             L"MapViewOfFile() returned error %lu\n",
    									             dwError = GetLastError());
    								else
    								{
    									if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
    										PrintConsole(hConsole,
    										             L"SetConsoleCtrlHandler() returned error %lu\n",
    										             dwError = GetLastError());
    
    									PrintConsole(hConsole,
    									             L"Press \'Ctrl-C\' or \'Ctrl-Break\' to terminate!\n"
    									             L"\n"
    									             L"Process\tDebug String\n");
    
    									do
    									{
    										dwDBWinData = SignalObjectAndWait(hDBWinBuffer,
    										                                  hDBWinData,
    										                                  INFINITE,
    										                                  FALSE);
    
    										if (dwDBWinData != WAIT_OBJECT_0)
    											break;
    
    										dwString = strlen(lpDBWin->szString);
    
    										while ((dwString-- > 0)
    										    && ((lpDBWin->szString[dwString] == ' ')
    										     || (lpDBWin->szString[dwString] == '\a')
    										     || (lpDBWin->szString[dwString] == '\b')
    										     || (lpDBWin->szString[dwString] == '\f')
    										     || (lpDBWin->szString[dwString] == '\n')
    										     || (lpDBWin->szString[dwString] == '\r')
    										     || (lpDBWin->szString[dwString] == '\t')
    										     || (lpDBWin->szString[dwString] == '\v')))
    											/* lpDBWin->szString[dwString] = '\0' */;
    
    										lpDBWin->szString[++dwString] = '\0';
    
    										if (lpDBWin->dwProcessId != dwProcessId)
    											PrintConsole(hConsole,
    											             L"\n"
    											             L"%7lu\t%hs\n",
    											             dwProcessId = lpDBWin->dwProcessId,
    											             lpDBWin->szString);
    										else
    											PrintConsole(hConsole,
    											             L"\t%hs\n",
    											             lpDBWin->szString);
    									}
    									while (dwProcessId != dwCurrentProcessId);
    
    									if (dwDBWinData == WAIT_FAILED)
    										PrintConsole(hConsole,
    										             L"SignalObjectAndWait() returned error %lu\n",
    										             dwError = GetLastError());
    
    									if (!SetConsoleCtrlHandler(CtrlHandler, FALSE))
    										PrintConsole(hConsole,
    										             L"SetConsoleCtrlHandler() returned error %lu\n",
    										             GetLastError());
    
    									if (!UnmapViewOfFile(lpDBWin))
    										PrintConsole(hConsole,
    										             L"UnmapViewOfFile() returned error %lu\n",
    										             GetLastError());
    								}
    							}
    
    							if (!CloseHandle(hDBWinData))
    								PrintConsole(hConsole,
    								             L"CloseHandle() returned error %lu\n",
    								             GetLastError());
    						}
    					}
    
    					if (!CloseHandle(hDBWinBuffer))
    						PrintConsole(hConsole,
    						             L"CloseHandle() returned error %lu\n",
    						             GetLastError());
    				}
    			}
    
    			if (!CloseHandle(hDBWin))
    				PrintConsole(hConsole,
    				             L"CloseHandle() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file DBWINNER.C created in step 1., link the compiled object file DBWINNER.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Debug String Monitor.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE DBWINNER.C
    ERASE DBWINNER.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    DBWINNER.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Directory Change Notifier

Purpose
Implementation and Build Details
Source and Build Instructions

Purpose

Monitor up to 64 directory trees, specified by their absolute or relative pathnames, for changes.

Implementation and Build Details

Directory Change Notifier is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows Vista and newer versions of Windows NT as well as Windows PE 2.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source and Build Instructions

Perform the following 2 simple steps to build the console application Directory Change Notifier from the source presented hereafter.
  1. Create the text file NOTIFIER.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    
    #define FILE_NOTIFY_CHANGE_UNDOCUMENTED	0x00000E80UL
    #define FILE_NOTIFY_CHANGE_ANY		0x00000FFFUL
    #define FILE_NOTIFY_CHANGE_INVALID	0xFFFFF000UL
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    HANDLE	hThreads[MAXIMUM_WAIT_OBJECTS];
    DWORD	dwThreads = 0;
    
    const	LPCWSTR	szCtrlType[] = {L"C",
    		                L"Break",
    		                L"Close",
    		                NULL,
    		                NULL,
    		                L"Logoff",
    		                L"Shutdown"};
    
    BOOL	WINAPI	CtrlHandler(DWORD dwCtrlType)
    {
    	DWORD	dwThread = dwThreads;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	switch (dwCtrlType)
    	{
    	case CTRL_C_EVENT:
    	case CTRL_BREAK_EVENT:
    	case CTRL_CLOSE_EVENT:
    	case CTRL_LOGOFF_EVENT:
    	case CTRL_SHUTDOWN_EVENT:
    
    		PrintConsole(hConsole,
    		             L"Ctrl-%ls\n",
    		             szCtrlType[dwCtrlType]);
    
    		while (dwThread-- > 0)
    			if (!CancelSynchronousIo(hThreads[dwThread]))
    				PrintConsole(hConsole,
    				             L"CancelSynchronousIo() returned error %lu for thread 0x%p\n",
    				             GetLastError(), hThreads[dwThread]);
    
    		return TRUE;
    
    	default:
    		return FALSE;
    	}
    }
    
    const	LPCWSTR	lpAction[] = {NULL,
    		              L"Added",
    		              L"Removed",
    		              L"Modified",
    		              L"Renamed from",
    		              L"Renamed to"};
    
    __declspec(safebuffers)
    DWORD	WINAPI	ThreadProc(LPCWSTR lpArgument)
    {
    	FILE_NOTIFY_INFORMATION	*lpEntry;
    #if 0
    	BY_HANDLE_FILE_INFORMATION	bhfi;
    #else
    	FILE_ATTRIBUTE_TAG_INFO		fati;
    #endif
    	HANDLE	hArgument;
    	WCHAR	szArgument[MAX_PATH];
    	DWORD	dwArgument;
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwEntry;
    	DWORD	dwBuffer[65536 / sizeof(DWORD)];
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		hArgument = CreateFile(lpArgument,
    		                       FILE_LIST_DIRECTORY,
    		                       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    		                       (LPSECURITY_ATTRIBUTES) NULL,
    		                       OPEN_EXISTING,
    		                       FILE_FLAG_BACKUP_SEMANTICS,
    		                       (HANDLE) NULL);
    
    		if (hArgument == INVALID_HANDLE_VALUE)
    			PrintConsole(hConsole,
    			             L"CreateFile() returned error %lu for argument \'%ls\'\n",
    			             dwError = GetLastError(), lpArgument);
    		else
    		{
    #if 0
    			if (!GetFileInformationByHandle(hArgument,
    			                                &bhfi))
    				PrintConsole(hConsole,
    				             L"GetFileInformationByHandle() returned error %lu for argument \'%ls\'\n",
    				             dwError = GetLastError(), lpArgument);
    			else
    				if (((bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
    				 || ((bhfi.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT))
    #else
    			if (!GetFileInformationByHandleEx(hArgument,
    			                                  FileAttributeTagInfo,
    			                                  &fati,
    			                                  sizeof(fati)))
    				PrintConsole(hConsole,
    				             L"GetFileInformationByHandleEx() returned error %lu for argument \'%ls\'\n",
    				             dwError = GetLastError(), lpArgument);
    			else
    				if (((fati.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
    				 || ((fati.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT))
    #endif
    					PrintConsole(hConsole,
    					             L"Argument \'%ls\' is not a directory!\n",
    					             lpArgument);
    				else
    				{
    					dwArgument = GetFinalPathNameByHandle(hArgument,
    					                                      szArgument,
    					                                      sizeof(szArgument) / sizeof(*szArgument),
    					                                      FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
    
    					if (dwArgument == 0)
    						PrintConsole(hConsole,
    						             L"GetFinalPathNameByHandle() returned error %lu for argument \'%ls\'\n",
    						             dwError = GetLastError(), lpArgument);
    					else
    					{
    						while (ReadDirectoryChangesW(hArgument,
    						                             dwBuffer,
    						                             sizeof(dwBuffer),
    						                             TRUE,
    #ifdef FILE_NOTIFY_CHANGE_ALL
    						                             FILE_NOTIFY_CHANGE_ALL,
    #else
    						                             FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_SIZE,
    #endif
    						                             &dwEntry,
    						                             (LPOVERLAPPED) NULL,
    						                             (LPOVERLAPPED_COMPLETION_ROUTINE) NULL))
    							for (lpEntry = (PFILE_NOTIFY_INFORMATION) dwBuffer; dwEntry != 0; (LPBYTE) lpEntry += dwEntry)
    							{
    								dwEntry = lpEntry->NextEntryOffset;
    								lpEntry->FileName[lpEntry->FileNameLength / sizeof(lpEntry->FileName[0])] = L'\0';
    
    								PrintConsole(hConsole,
    								             L"%ls %ls\\%ls\n",
    								             lpAction[lpEntry->Action], szArgument + 4, lpEntry->FileName);
    							}
    
    						PrintConsole(hConsole,
    						             L"ReadDirectoryChanges() returned error %lu for directory \'%ls\'\n",
    						             dwError = GetLastError(), szArgument + 4);
    					}
    				}
    
    			if (!CloseHandle(hArgument))
    				PrintConsole(hConsole,
    				             L"CloseHandle() returned error %lu\n",
    				             GetLastError());
    		}
    	}
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	LPWSTR	*lpArguments;
    	INT	nArguments;
    	INT	nArgument = 1;
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwThreadId;
    	HANDLE	hThread;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"Too few arguments: at least one directory name must be given!\n");
    			else if (nArguments > sizeof(hThreads) / sizeof(*hThreads) + 1)
    				PrintConsole(hConsole,
    				             L"Too many arguments: at most %lu directory names may be given!\n",
    				             sizeof(hThreads) / sizeof(*hThreads));
    			else
    			{
    				do
    				{
    					hThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
    					                       (SIZE_T) 65536,
    					                       ThreadProc,
    					                       lpArguments[nArgument],
    					                       0,
    					                       &dwThreadId);
    
    					if (hThread == NULL)
    						PrintConsole(hConsole,
    						             L"CreateThread() returned error %lu\n",
    						             dwError = GetLastError());
    					else
    					{
    						hThreads[dwThreads++] = hThread;
    
    						PrintConsole(hConsole,
    						             L"Thread %lu created for argument \'%ls\'\n",
    						             dwThreadId, lpArguments[nArgument]);
    					}
    				} while (++nArgument < nArguments);
    
    				if (dwThreads > 0)
    				{
    					if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
    						PrintConsole(hConsole,
    						             L"SetConsoleCtrlHandler() returned error %lu\n",
    						             dwError = GetLastError());
    
    					PrintConsole(hConsole,
    					             L"Press \'Ctrl-C\' or \'Ctrl-Break\' to terminate!\n"
    					             L"\n");
    
    					if (WaitForMultipleObjects(dwThreads,
    					                           hThreads,
    					                           TRUE,
    					                           INFINITE) == WAIT_FAILED)
    						PrintConsole(hConsole,
    						             L"WaitForMultipleObjects() returned error %lu\n",
    						             dwError = GetLastError());
    					else
    						do
    							if (!CloseHandle(hThreads[--dwThreads]))
    								PrintConsole(hConsole,
    								             L"CloseHandle() returned error %lu\n",
    								             GetLastError());
    						while (dwThreads > 0);
    
    					if (!SetConsoleCtrlHandler(CtrlHandler, FALSE))
    						PrintConsole(hConsole,
    						             L"SetConsoleCtrlHandler() returned error %lu\n",
    						             GetLastError());
    				}
    			}
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file NOTIFIER.C created in step 1., link the compiled object file NOTIFIER.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gs69632 /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:6.0 /OUT:"Directory Change Notifier.com" /RELEASE /SUBSYSTEM:CONSOLE,6.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE NOTIFIER.C
    ERASE NOTIFIER.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    NOTIFIER.C
    NOTIFIER.C(185) : warning C4213: nonstandard extension used : cast on l-value
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Registry Policy Reader

Purpose
Background Information
Implementation and Build Details
Source, Build Instructions and Sample Output

Purpose

Display the content of Registry Policy files in text format similar to that of Registry Editor script files, and optionally compare their contents against the machines’s or the (current) user’s Registry.

Background Information

Registry Policy files, typically created with the extension .pol, are used either to store the Registry keys and entries (to be) applied by Group Policies as well as Local Security Policies, or to restore the previous contents of Registry keys and entries modified by Group Policies as well as Local Security Policies.

The MSDN article Registry Policy File Format documents their format.

The MSKB article How to add, modify, or delete registry subkeys and values by using a .reg file documents the format of Registry Editor script files, typically created with the extension .reg.

Implementation and Build Details

Registry Policy Reader is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source, Build Instructions and Sample Output

Perform the following 3 simple steps to build the console application Registry Policy Reader from the source presented hereafter and execute it.
  1. Create the text file POLYGLOT.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    
    #define REGFILE_SIGNATURE	'geRP'
    #define REGISTRY_FILE_VERSION	1UL
    
    typedef	unsigned __int64	QWORD, *LPQWORD;
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    const	LPCWSTR	szHKEY[8] = {L"HKEY_CLASSES_ROOT",
    		             L"HKEY_CURRENT_USER",
    		             L"HKEY_LOCAL_MACHINE",
    		             L"HKEY_USERS",
    		             L"HKEY_PERFORMANCE_DATA",
    		             L"HKEY_CURRENT_CONFIG",
    		             L"HKEY_DYN_DATA",
    		             L"HKEY_CURRENT_USER_LOCAL_SETTINGS"};
    
    const	LPCWSTR	szTYPE[12] = {L"NONE",
    		              L"SZ",
    		              L"EXPAND_SZ",
    		              L"BINARY",
    		              L"DWORD",		// alias DWORD_LITTLE_ENDIAN
    		              L"DWORD_BIG_ENDIAN",
    		              L"LINK",
    		              L"MULTI_SZ",
    		              L"RESOURCE_LIST",
    		              L"FULL_RESOURCE_DESCRIPTOR",
    		              L"RESOURCE_REQUIREMENTS_LIST",
    		              L"QWORD"};	// alias QWORD_LITTLE_ENDIAN
    
    const	LPCWSTR	szType[12] = {L"none:",
    		              L"",
    		              L"expand:",
    		              L"hex:",
    		              L"dword:",
    		              L"dword:",
    		              L"link:",
    		              L"multi:",
    		              L"hex(8):",
    		              L"hex(9):",
    		              L"hex(a):",
    		              L"qword:"};
    
    DWORD	WINAPI	Polyglot(HANDLE hConsole, HKEY hkHKEY, LPCWSTR lpArgument)
    {
    #ifdef REGISTRY
    	LPCWSTR	lpHKEY = szHKEY[(DWORD) hkHKEY ^ (DWORD) HKEY_CLASSES_ROOT];
    	HKEY	hkKey;
    	BYTE	cbData[65536];
    #endif
    	HANDLE	hInput;
    	DWORD	dwInput;
    	LPCWSTR	lpInput;
    	WCHAR	cwInput;
    	HANDLE	hPolicy;
    	DWORD	dwPolicy;
    	LPDWORD	lpPolicy;
    	LPCWSTR	lpKey, lpValue, lpData, lp;
    	DWORD	dwKey, dwValue, dwData, dwType, dwSize;
    	DWORD	dwError = ERROR_SUCCESS;
    
    	hInput = CreateFile(lpArgument,
    	                    FILE_READ_DATA,
    	                    FILE_SHARE_READ,
    	                    (LPSECURITY_ATTRIBUTES) NULL,
    	                    OPEN_EXISTING,
    	                    FILE_FLAG_SEQUENTIAL_SCAN,
    	                    (HANDLE) NULL);
    
    	if (hInput == INVALID_HANDLE_VALUE)
    		PrintConsole(hConsole,
    		             L"CreateFile() returned error %lu for file \'%ls\'\n",
    		             dwError = GetLastError(), lpArgument);
    	else
    	{
    		dwInput = GetFileSize(hInput, (LPDWORD) NULL);
    
    		if (dwInput == INVALID_FILE_SIZE)
    			PrintConsole(hConsole,
    			             L"GetFileSize() returned error %lu for file \'%ls\'\n",
    			             dwError = GetLastError(), lpArgument);
    		else
    		{
    			hPolicy = CreateFileMapping(hInput,
    			                            (LPSECURITY_ATTRIBUTES) NULL,
    			                            PAGE_READONLY,
    			                            0, 0,
    			                            (LPCWSTR) NULL);
    
    			if (hPolicy == NULL)
    				PrintConsole(hConsole,
    				             L"CreateFileMapping() returned error %lu for file \'%ls\'\n",
    				             dwError = GetLastError(), lpArgument);
    			else
    			{
    				lpPolicy = MapViewOfFile(hPolicy,
    				                         FILE_MAP_READ,
    				                         0, 0,
    				                         (SIZE_T) 0);
    
    				if (lpPolicy == NULL)
    					PrintConsole(hConsole,
    					             L"MapViewOfFile() returned error %lu for file \'%ls\'\n",
    					             dwError = GetLastError(), lpArgument);
    				else
    				{
    					if ((lpPolicy[0] != REGFILE_SIGNATURE)
    					 || (lpPolicy[1] != REGISTRY_FILE_VERSION))
    						PrintConsole(hConsole,
    						             L"Signature \'PReg\\x01\\0\\0\\0\' missing in file \'%ls\'!\n",
    						             lpArgument);
    					else
    					{
    						PrintConsole(hConsole,
    						             L"Windows Registry Editor Version 5.00\n"
    						             L"\n"
    						             L"; Registry Policy File \'%ls\'\n",
    						             lpArgument);
    
    						// L'[' key L']'
    						// L'[' key L';'           value L';' type L';' size L';' data L']'
    						// L'[' key L';' L"**Del." value L';' type L';' size L';' data L']'
    						// L'[' key L';' L"**DeleteKeys" { L';' key } ... L']'
    						// L'[' key L';' L"**DeleteVals" L']'
    						// L'[' key L';' L"**DeleteValues" { L';' value } ... L']'
    						// L'[' key L';' L"**SecureKey=0" L']'
    						// L'[' key L';' L"**SecureKey=1" L']'
    						//
    						// WCHAR key[]    NUL-terminated path of registry key beneath
    						//                HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER
    						//
    						// WCHAR value[]  NUL-terminated name of registry entry
    						//
    						// DWORD type     registry data type
    						//
    						// DWORD size     size of registry data in bytes
    						//
    						// BYTE  data[]   registry data
    
    						lpInput = (LPCWSTR) (lpPolicy + 2);
    						lpKey = NULL;
    						dwKey = 0;
    						dwPolicy = dwInput;
    
    						while ((LPBYTE) lpInput < (LPBYTE) lpPolicy + dwPolicy)
    						{
    							if (*lpInput++ != L'[')
    								break;
    
    							dwInput = wcslen(lpInput);
    
    							if ((dwKey == 0)
    							 || (dwKey != dwInput)
    							 || (memcmp(lpInput, lpKey, dwKey * sizeof(L'\0')) != 0))
    								PrintConsole(hConsole,
    								             L"\n"
    								             L"[HKEY_RELATIVE\\%ls]\n",
    								             lpInput);
    
    							lpKey = lpInput;
    							dwKey = dwInput;
    							lpInput += dwInput + 1;
    							cwInput = *lpInput++;
    
    							if (cwInput == L']')
    								continue;
    
    							if (cwInput != L';')
    								break;
    
    							lpValue = lpInput;
    							dwValue = wcslen(lpInput);
    							lpInput += dwValue + 1;
    
    							if ((dwValue == sizeof("**securekey=1") - 1)
    							 && ((memcmp(lpValue, L"**securekey=1", sizeof(L"**securekey=1") - sizeof(L"")) == 0)
    							  || (memcmp(lpValue, L"**SecureKey=1", sizeof(L"**securekey=1") - sizeof(L"")) == 0)))
    							{
    								PrintConsole(hConsole,
    								             L"; SecureKey=1\n");
    
    								if (*lpInput++ != L']')
    									break;
    							}
    							else if ((dwValue == sizeof("**securekey=0") - 1)
    							      && ((memcmp(lpValue, L"**securekey=0", sizeof(L"**securekey=0") - sizeof(L"")) == 0)
    							       || (memcmp(lpValue, L"**SecureKey=0", sizeof(L"**securekey=0") - sizeof(L"")) == 0)))
    							{
    								PrintConsole(hConsole,
    								             L"; SecureKey=0\n");
    
    								if (*lpInput++ != L']')
    									break;
    							}
    							else if ((dwValue == sizeof("**deletevals") - 1)
    							      && ((memcmp(lpValue, L"**deletevals", sizeof(L"**deletevals") - sizeof(L"")) == 0)
    							       || (memcmp(lpValue, L"**Deletevals", sizeof(L"**Deletevals") - sizeof(L"")) == 0)))
    							{
    								PrintConsole(hConsole,
    								             L"*=-\n");
    
    								if (*lpInput++ != L']')
    									break;
    							}
    							else if ((dwValue == sizeof("**deletevalues") - 1)
    							      && ((memcmp(lpValue, L"**deletevalues", sizeof(L"**deletevalues") - sizeof(L"")) == 0)
    							       || (memcmp(lpValue, L"**Deletevalues", sizeof(L"**Deletevalues") - sizeof(L"")) == 0)))
    							{
    								while (*lpInput == L';')
    								{
    									PrintConsole(hConsole,
    									             L"\'%ls\'=-\n",
    									             ++lpInput);
    
    									lpInput += wcslen(lpInput) + 1;
    								}
    
    								if (*lpInput++ != L']')
    									break;
    							}
    							else if ((dwValue == sizeof("**deletekeys") - 1)
    							      && ((memcmp(lpValue, L"**deletekeys", sizeof(L"**deletekeys") - sizeof(L"")) == 0)
    							       || (memcmp(lpValue, L"**Deletekeys", sizeof(L"**Deletekeys") - sizeof(L"")) == 0)))
    							{
    								while (*lpInput == L';')
    								{
    									PrintConsole(hConsole,
    									             L"[-HKEY_RELATIVE\\%ls\\%ls]\n",
    									             lpKey, ++lpInput);
    
    									lpInput += wcslen(lpInput) + 1;
    								}
    
    								if (*lpInput++ != L']')
    									break;
    							}
    							else
    							{
    								if ((dwValue > sizeof("**del."))
    								 && ((memcmp(lpValue, L"**del.", sizeof(L"**del.") - sizeof(L"")) == 0)
    								  || (memcmp(lpValue, L"**Del.", sizeof(L"**Del.") - sizeof(L"")) == 0)))
    								{
    									lpValue += sizeof("**Del.") - 1;
    
    									if (dwValue == sizeof("**Del.") - 1)
    										PrintConsole(hConsole,
    										             L"@=- ; =");
    									else
    										PrintConsole(hConsole,
    										             L"\'%ls\'=- ; =", lpValue);
    								}
    								else
    									if (dwValue == 0)
    										PrintConsole(hConsole,
    										             L"@=");
    									else
    										PrintConsole(hConsole,
    										             L"\'%ls\'=", lpValue);
    
    								cwInput = *lpInput++;
    
    								if (cwInput == L']')
    									continue;
    
    								if (cwInput != L';')
    									break;
    
    								dwType = *((LPDWORD) lpInput)++;
    
    								if (dwType > sizeof(szType) / sizeof(*szType))
    									PrintConsole(hConsole,
    									             L"hex(%lx):", dwType);
    								else
    									PrintConsole(hConsole,
    									             L"%ls", szType[dwType]);
    
    								cwInput = *lpInput++;
    
    								if (cwInput == L']')
    									continue;
    
    								if (cwInput != L';')
    									break;
    
    								dwSize = *((LPDWORD) lpInput)++;
    
    								cwInput = *lpInput++;
    
    								if (cwInput == L']')
    									continue;
    
    								if (cwInput != L';')
    									break;
    
    								lpData = lpInput;
    								(LPBYTE) lpInput += dwSize;
    
    								switch (dwType)
    								{
    								case REG_SZ:
    								case REG_EXPAND_SZ:
    								case REG_LINK:
    
    									if (dwSize == 0)
    										goto NEWLINE;
    
    									PrintConsole(hConsole,
    									             L"\'%ls\'\n",
    									             lpData);
    
    									dwData = wcslen(lpData);
    
    									if (dwSize != (dwData + 1) * sizeof(L'\0'))
    										PrintConsole(hConsole,
    										             L"Size %lu of REG_%ls value data not equal length %lu of string plus terminating \'NUL\' character!\n",
    										             dwSize, szTYPE[dwType], dwData);
    									break;
    
    								case REG_DWORD_BIG_ENDIAN:
    
    									*(LPDWORD) lpData = _byteswap_ulong(*(LPDWORD) lpData);
    
    								case REG_DWORD_LITTLE_ENDIAN:
    							//	case REG_DWORD:
    
    									PrintConsole(hConsole,
    									             L"%08lx\n",
    									             *(LPDWORD) lpData);
    
    									if (dwSize != sizeof(DWORD))
    										PrintConsole(hConsole,
    										             L"Size %lu of REG_%ls value data not equal \'sizeof(DWORD)\'!\n",
    										             dwSize, szTYPE[dwType]);
    									break;
    
    								case REG_QWORD_LITTLE_ENDIAN:
    							//	case REG_QWORD:
    
    									PrintConsole(hConsole,
    									             L"%016I64x\n",
    									             *(LPQWORD) lpData);
    
    									if (dwSize != sizeof(QWORD))
    										PrintConsole(hConsole,
    										             L"Size %lu of REG_QWORD value data not equal \'sizeof(QWORD)\'!\n",
    										             dwSize);
    									break;
    
    								case REG_MULTI_SZ:
    
    									if (dwSize == 0)
    										goto NEWLINE;
    
    									for (lp = lpData; (lp < lpInput) && (*lp != L'\0'); lp += wcslen(lp) + 1)
    										PrintConsole(hConsole,
    										             lp == lpData ? L"\'%ls\'" : L",\'%ls\'",
    										             lp);
    
    									PrintConsole(hConsole,
    									             L"\n");
    
    									if ((lp > lpInput) || (*lp != L'\0'))
    										PrintConsole(hConsole,
    										             L"REG_MULTI_SZ value data not terminated with extra \'NUL\' character!\n");
    									else
    										lp++;
    
    									if (lp != lpInput)
    										PrintConsole(hConsole,
    										             L"Size %lu of REG_MULTI_SZ value data not equal sum of string lengths plus terminating \'NUL\' characters!\n",
    										             dwSize);
    									break;
    
    							//	case REG_NONE:
    							//	case REG_BINARY:
    							//	case REG_RESOURCE_LIST:
    							//	case REG_FULL_RESOURCE_DESCRIPTOR:
    							//	case REG_RESOURCE_REQUIREMENTS_LIST:
    								default:
    									for (lp = lpData; lp < lpInput; (LPBYTE) lp += 1)
    										PrintConsole(hConsole,
    										             lp == lpData ? L"%02x" : L",%02x",
    										             *(LPBYTE) lp);
    								NEWLINE:
    									PrintConsole(hConsole,
    									             L"\n");
    								}
    #ifdef REGISTRY
    								if (hkHKEY != HKEY_CLASSES_ROOT)
    								{
    									dwError = RegOpenKeyEx(hkHKEY,
    									                       lpKey,
    									                       REG_OPTION_RESERVED,
    									                       KEY_QUERY_VALUE,
    									                       &hkKey);
    
    									if (dwError != ERROR_SUCCESS)
    										PrintConsole(hConsole,
    										             L"RegOpenKeyEx() returned error %lu for registry key \'%ls\\%ls\'\n",
    										             dwError, lpHKEY, lpKey);
    									else
    									{
    										dwData = sizeof(cbData);
    
    										dwError = RegQueryValueEx(hkKey,
    										                          lpValue,
    										                          (LPDWORD) NULL,
    										                          &dwValue,
    										                          cbData,
    										                          &dwData);
    
    										if (dwError != ERROR_SUCCESS)
    											PrintConsole(hConsole,
    											             L"RegQueryValueEx() returned error %lu for value \'%ls\' of registry key \'%ls\\%ls\'\n",
    											             dwError, lpValue, lpHKEY, lpKey);
    										else
    											if ((dwValue != dwType)
    											 || (dwData != dwSize)
    											 || (memcmp(lpData, cbData, dwData) != 0))
    												PrintConsole(hConsole,
    												             L"MISMATCH: data type, size or value in policy file differs from registry!\n");
    
    										dwError = RegCloseKey(hkKey);
    
    										if (dwError != ERROR_SUCCESS)
    											PrintConsole(hConsole,
    											             L"RegCloseKey() returned error %lu for registry key \'%ls\\%ls\'\n",
    											             dwError, lpHKEY, lpKey);
    									}
    								}
    #endif // REGISTRY
    								if (*lpInput++ != L']')
    									break;
    							}
    						}
    
    						if ((LPBYTE) lpInput != (LPBYTE) lpPolicy + dwPolicy)
    							PrintConsole(hConsole,
    							             L"Format error in policy file \'%ls\'!\n",
    							             lpArgument);
    					}
    
    					if (!UnmapViewOfFile(lpPolicy))
    						PrintConsole(hConsole,
    						             L"UnmapViewOfFile() returned error %lu for file \'%ls\'\n",
    						             GetLastError(), lpArgument);
    				}
    
    				if (!CloseHandle(hPolicy))
    					PrintConsole(hConsole,
    					             L"CloseHandle() returned error %lu for file mapping \'%ls\'\n",
    					             GetLastError(), lpArgument);
    			}
    		}
    
    		if (!CloseHandle(hInput))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu for file \'%ls\'\n",
    			             GetLastError(), lpArgument);
    	}
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    #ifdef WILDCARD
    	WIN32_FIND_DATA	wfd;
    
    	HANDLE	hWildCard;
    	DWORD	dwWildCard;
    	WCHAR	szWildCard[32768];
    	LPWSTR	lpWildCard;
    #endif
    	LPWSTR	*lpArguments;
    	INT	nArguments;
    	INT	nArgument = 1;
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	HKEY	hkHKEY = HKEY_CLASSES_ROOT;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    #ifdef REGISTRY
    			if (nArguments > 2)
    				if (wcscmp(lpArguments[1], L"/MACHINE") == 0)
    				{
    					hkHKEY = HKEY_LOCAL_MACHINE;
    					nArgument = 2;
    				}
    				else if (wcscmp(lpArguments[1], L"/USER") == 0)
    				{
    					hkHKEY = HKEY_CURRENT_USER;
    					nArgument = 2;
    				}
    				else if (*lpArguments[1] == L'/')
    					PrintConsole(hConsole,
    					             L"Optional first argument must be '/MACHINE' or '/USER'!\n");
    #endif
    			if (nArguments <= nArgument)
    				PrintConsole(hConsole,
    				             L"No arguments: at least one \'.pol\' file name must be given!\n");
    			else
    #ifndef WILDCARD
    				do
    					dwError = Polyglot(hConsole, hkHKEY, lpArguments[nArgument]);
    				while (++nArgument < nArguments);
    #else
    				do
    				{
    					hWildCard = FindFirstFile(lpArguments[nArgument], &wfd);
    
    					if (hWildCard == INVALID_HANDLE_VALUE)
    						PrintConsole(hConsole,
    						             L"FindFirstFile() returned error %lu for argument \'%ls\'\n",
    						             dwError = GetLastError(), lpArguments[nArgument]);
    					else
    					{
    						wcscpy(szWildCard, lpArguments[nArgument]);
    
    						dwWildCard = 0;
    						lpWildCard = NULL;
    
    						do
    							if (szWildCard[dwWildCard] == L'\\')
    								lpWildCard = szWildCard + dwWildCard;
    						while (szWildCard[dwWildCard++] != L'\0');
    
    						if (dwWildCard > MAX_PATH)
    							PrintConsole(hConsole,
    							             L"Argument \'%ls\' exceeds MAX_PATH!\n",
    							             lpArguments[nArgument]);
    
    						if (lpWildCard != NULL)
    							lpWildCard++;
    						else
    							lpWildCard = szWildCard + 2 * (szWildCard[1] == L':');
    
    						dwWildCard = 0;
    
    						do
    						{
    							if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    								continue;
    
    							dwWildCard++;
    
    							wcscpy(lpWildCard, wfd.cFileName);
    
    							dwError = Polyglot(hConsole, hkHKEY, szWildCard);
    						}
    						while (FindNextFile(hWildCard, &wfd));
    
    						dwError = GetLastError();
    
    						if (dwError == ERROR_NO_MORE_FILES)
    							dwError = ERROR_SUCCESS;
    						else
    							PrintConsole(hConsole,
    							             L"FindNextFile() returned error %lu for argument \'%ls\'\n",
    							             dwError, lpArguments[nArgument]);
    
    						if (dwWildCard == 0)
    							PrintConsole(hConsole,
    							             L"No match for argument \'%ls\'!\n",
    							             lpArguments[nArgument]);
    
    						if (!FindClose(hWildCard))
    							PrintConsole(hConsole,
    							             L"FindClose() returned error %lu for argument \'%ls\'\n",
    							             GetLastError(), lpArguments[nArgument]);
    					}
    				} while (++nArgument < nArguments);
    #endif // WILDCARD
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
    Note: with the preprocessor macro REGISTRY defined, an option /MACHINE or /USER is accepted as first command line argument to compare the settings against the HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER branch of the Registry!

    Note: with the preprocessor macro WILDCARD defined, wildcard expansion of matching file names is performed for the command line arguments!

  2. Run the following four command lines to compile the source file POLYGLOT.C created in step 1., link the compiled object file POLYGLOT.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Registry Policy Reader.com" /RELEASE /STACK:1048576,65536 /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE POLYGLOT.C
    ERASE POLYGLOT.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    POLYGLOT.C
    POLYGLOT.C(313) : warning C4213: nonstandard extension used : cast on l-value
    POLYGLOT.C(330) : warning C4213: nonstandard extension used : cast on l-value
    POLYGLOT.C(341) : warning C4213: nonstandard extension used : cast on l-value
    POLYGLOT.C(425) : warning C4213: nonstandard extension used : cast on l-value
    POLYGLOT.C(89) : warning C4100: 'hkHKEY' : unreferenced formal parameter
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …
  3. Finally execute the console application Registry Policy Reader.com built in step 2. to display the contents of some NTUser.pol and Registry.pol files that eventually are (not yet) present on your machine:

    VER
    ".\Registry Policy Reader.com" "%USERPROFILE%\NTUser.pol" "%ALLUSERSPROFILE%\NTUser.pol" "%SystemRoot%\System32\GroupPolicy\Machine\Registry.pol" "%SystemRoot%\System32\GroupPolicy\User\Registry.pol"
    NET.EXE HelpMsg %ERRORLEVEL%
    Microsoft Windows [Version 10.0.19044]
    
    Windows Registry Editor Version 5.00
    
    ; Registry Policy File 'C:\Users\Stefan\NTUser.pol'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Group Policy Objects\Local Group Policy Objects]
    '**Comment:GPO Name: Local Group Policy Objects'=
    
    [HKEY_RELATIVE\Software\Microsoft\Windows\CurrentVersion\Policies\System]
    'LogonHoursAction'=dword:00000002
    'DontDisplayLogonHoursWarnings'=dword:00000001
    
    Windows Registry Editor Version 5.00
    
    ; Registry Policy File 'C:\ProgramData\NTUser.pol'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Group Policy Objects\Local Group Policy Objects]
    '**Comment:GPO Name: Local Group Policy Objects'=
    
    Windows Registry Editor Version 5.00
    
    ; Registry Policy File 'C:\Windows\System32\GroupPolicy\Machine\Registry.pol'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\SystemCertificates\TrustedPublisher\Safer]
    'AuthentiCodeFlags'=dword:00000300
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers]
    'PolicyScope'=dword:00000001
    'TransparentEnabled'=dword:00000002
    'ExecutableTypes'=multi:'WSF','WSC','VBS','VBE','VB','TMP','SHS','SCR','PIF','PCD','OCX','MST','MSP','MSI','MDE','MDB','JSE','JS','ISP','INS','HTA','HLP','EXE','DLL','CRT','CPL','COM','CMD','BAT','BAS','AX','ADP','ADE'
    'DefaultLevel'=dword:00000000
    'Levels'=dword:00071000
    'LogFileName'='C:\Windows\System32\LogFiles\SAFER.Log'
    'AuthenticodeEnabled'=dword:00000001
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\0\Hashes]
    @=none:
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\262144\Paths\{191cd7fa-f240-4a17-8986-94d480a6c8ca}]
    'LastModified'=qword:01cf68d87b202417
    'Description'=''
    'SaferFlags'=dword:00000000
    'ItemData'=expand:'%HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot%'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\262144\Paths\{d2c34ab2-529a-46b2-b293-fc853fce72ea}]
    'LastModified'=qword:01cf68d87b202417
    'Description'=''
    'SaferFlags'=dword:00000000
    'ItemData'=expand:'%HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir%'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\262144\Paths\{4fcf2556-cf02-4356-ad71-f82ca93ccd0b}]
    'LastModified'=qword:01cf68d979215214
    'Description'=''
    'SaferFlags'=dword:00000000
    'ItemData'=expand:'%HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir (x86)%'
    
    [HKEY_RELATIVE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\262144\Paths\{21c0b260-2d89-4fe0-8275-1c76746b3d2b}]
    'LastModified'=qword:01d57587bb48c5c4
    'Description'=''
    'SaferFlags'=dword:00000000
    'ItemData'=expand:'%HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramW6432Dir%'
    
    CreateFile() returned error 3 for file 'C:\Windows\System32\GroupPolicy\User\Registry.pol'
    
    The system cannot find the path specified.

Offline Registry Reader

Purpose
Background Information
Implementation and Build Details
Source and Build Instructions

Purpose

Print an offline registry hive formatted as .inf file in UTF-16LE encoding on standard output (which must be redirected to a file).

Background Information

Implementation and Build Details

Offline Registry Reader is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Source and Build Instructions

Perform the following 2 simple steps to build the console application Offline Registry Reader from the source presented hereafter.
  1. Create the text file OFFREG.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    #include <offreg.h>
    
    #define MAX_DEPTH		512UL
    #define MAX_KEY_LENGTH		255UL
    #define MAX_VALUE_NAME		16383UL
    #define MAX_VALUE_DATA		1048576UL
    
    typedef	unsigned __int64	QWORD, *LPQWORD;
    
    BYTE	cbData[MAX_VALUE_DATA];
    WCHAR	szKey[(MAX_KEY_LENGTH + 1) * MAX_DEPTH];
    WCHAR	szValue[MAX_VALUE_NAME + 1];
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    __declspec(safebuffers)
    BOOL	PrintFormat(HANDLE hFile, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	LPBYTE	lpBuffer;
    	DWORD	dwBuffer;
    	DWORD	dwFile;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	dwBuffer *= sizeof(*szBuffer);
    	lpBuffer = (LPBYTE) szBuffer;
    
    	do
    	{
    		if (!WriteFile(hFile, lpBuffer, dwBuffer, &dwFile, (LPOVERLAPPED) NULL))
    			return FALSE;
    
    		lpBuffer += dwFile;
    		dwBuffer -= dwFile;
    	} while (dwBuffer > 0);
    
    	return TRUE;
    }
    
    __inline
    BOOL	WINAPI	PrintString(HANDLE hFile, LPCWSTR lpString, DWORD dwString)
    {
    	DWORD	dwFile;
    
    	dwString *= sizeof(*lpString);
    
    	do
    	{
    		if (!WriteFile(hFile, lpString, dwString, &dwFile, (LPOVERLAPPED) NULL))
    			return FALSE;
    
    		(LPBYTE) lpString += dwFile;
    		dwString -= dwFile;
    	} while (dwString > 0);
    
    	return TRUE;
    }
    
    __inline
    LPCWSTR	WINAPI	InfEscape(LPCWSTR lpString)
    {
    	do
    		if ((*lpString == L'"')
    		 || (*lpString == L'%'))
    			return lpString;
    	while (*lpString++ != L'\0');
    
    	return NULL;
    }
    
    const	WCHAR	szBytes[256][4] = {L",00", L",01", L",02", L",03", L",04", L",05", L",06", L",07", L",08", L",09", L",0a", L",0b", L",0c", L",0d", L",0e", L",0f",
    		                   L",10", L",11", L",12", L",13", L",14", L",15", L",16", L",17", L",18", L",19", L",1a", L",1b", L",1c", L",1d", L",1e", L",1f",
    		                   L",20", L",21", L",22", L",23", L",24", L",25", L",26", L",27", L",28", L",29", L",2a", L",2b", L",2c", L",2d", L",2e", L",2f",
    		                   L",30", L",31", L",32", L",33", L",34", L",35", L",36", L",37", L",38", L",39", L",3a", L",3b", L",3c", L",3d", L",3e", L",3f",
    		                   L",40", L",41", L",42", L",43", L",44", L",45", L",46", L",47", L",48", L",49", L",4a", L",4b", L",4c", L",4d", L",4e", L",4f",
    		                   L",50", L",51", L",52", L",53", L",54", L",55", L",56", L",57", L",58", L",59", L",5a", L",5b", L",5c", L",5d", L",5e", L",5f",
    		                   L",60", L",61", L",62", L",63", L",64", L",65", L",66", L",67", L",68", L",69", L",6a", L",6b", L",6c", L",6d", L",6e", L",6f",
    		                   L",70", L",71", L",72", L",73", L",74", L",75", L",76", L",77", L",78", L",79", L",7a", L",7b", L",7c", L",7d", L",7e", L",7f",
    		                   L",80", L",81", L",82", L",83", L",84", L",85", L",86", L",87", L",88", L",89", L",8a", L",8b", L",8c", L",8d", L",8e", L",8f",
    		                   L",90", L",91", L",92", L",93", L",94", L",95", L",96", L",97", L",98", L",99", L",9a", L",9b", L",9c", L",9d", L",9e", L",9f",
    		                   L",a0", L",a1", L",a2", L",a3", L",a4", L",a5", L",a6", L",a7", L",a8", L",a9", L",aa", L",ab", L",ac", L",ad", L",ae", L",af",
    		                   L",b0", L",b1", L",b2", L",b3", L",b4", L",b5", L",b6", L",b7", L",b8", L",b9", L",ba", L",bb", L",bc", L",bd", L",be", L",bf",
    		                   L",c0", L",c1", L",c2", L",c3", L",c4", L",c5", L",c6", L",c7", L",c8", L",c9", L",ca", L",cb", L",cc", L",cd", L",ce", L",cf",
    		                   L",d0", L",d1", L",d2", L",d3", L",d4", L",d5", L",d6", L",d7", L",d8", L",d9", L",da", L",db", L",dc", L",dd", L",de", L",df",
    		                   L",e0", L",e1", L",e2", L",e3", L",e4", L",e5", L",e6", L",e7", L",e8", L",e9", L",ea", L",eb", L",ec", L",ed", L",ee", L",ef",
    		                   L",f0", L",f1", L",f2", L",f3", L",f4", L",f5", L",f6", L",f7", L",f8", L",f9", L",fa", L",fb", L",fc", L",fd", L",fe", L",ff"};
    
    const	LPCWSTR	szType[12] = {L"NONE",
    		              L"SZ",
    		              L"EXPAND_SZ",
    		              L"BINARY",
    		              L"DWORD",		// alias DWORD_LITTLE_ENDIAN
    		              L"DWORD_BIG_ENDIAN",
    		              L"LINK",
    		              L"MULTI_SZ",
    		              L"RESOURCE_LIST",
    		              L"FULL_RESOURCE_DESCRIPTOR",
    		              L"RESOURCE_REQUIREMENTS_LIST",
    		              L"QWORD"};	// alias QWORD_LITTLE_ENDIAN
    
    DWORD	WINAPI	Offline(HANDLE hConsole, HANDLE hOutput, ORHKEY hkKey, DWORD dwKey)
    {
    	BOOL	bOutput;
    	DWORD	dwError;
    	DWORD	dwSubKeys, dwSubKey;
    	DWORD	dwValues, dwValue, dwType, dwData, dwBytes;
    #ifdef SANITY
    	DWORD	dwCount, dwChars;
    	LPCWSTR	lpCount;
    #endif
    	LPCWSTR	lpData, lpEscape, lpLast, lpMulti;
    	LPCWSTR	lpSubKey = szKey + dwKey + 1;
    	ORHKEY	hkSubKey;
    
    	for (dwValues = 0;; dwValues++)
    	{
    	//	*szValue = L'\0';
    		dwValue = sizeof(szValue) / sizeof(*szValue);
    		dwData = sizeof(cbData);
    
    		dwError = OREnumValue(hkKey,
    		                      dwValues,
    		                      szValue,
    		                      &dwValue,
    		                      &dwType,
    		                      cbData,
    		                      &dwData);
    
    		if (dwError == ERROR_NO_MORE_ITEMS)
    			break;
    
    		if (dwError != ERROR_SUCCESS)
    			PrintConsole(hConsole,
    			             L"OREnumValue() returned error %lu for registry key \'%ls\'\n",
    			             dwError, szKey);
    		else
    		{
    #ifdef SANITY
    			dwChars = wcslen(szValue);
    
    			if (dwValue < dwChars)
    				PrintConsole(hConsole,
    				             L"ERROR: size (%lu characters) of value name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu characters)!\n",
    				             dwValue, szValue, dwChars, szKey);
    			else if (dwValue > dwChars)
    				PrintConsole(hConsole,
    				             L"WARNING: size (%lu characters) of value name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu characters)\n",
    				             dwValue, szValue, dwChars, szKey);
    
    			if (dwData == 0)
    				PrintConsole(hConsole,
    				             L"WARNING: no value data for value name \'%ls\' in registry key \'%ls\'\n",
    				             szValue, szKey);
    			else
    				switch (dwType)
    				{
    				case REG_LINK:
    
    					if (dwData % sizeof(L'\0'))
    						PrintConsole(hConsole,
    						             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' not a multiple of WCHAR size!\n",
    						             dwData, szValue, szKey);
    					break;
    
    				case REG_DWORD_BIG_ENDIAN:
    				case REG_DWORD_LITTLE_ENDIAN:
    			//	case REG_DWORD:
    
    					if (dwData < sizeof(DWORD))
    						PrintConsole(hConsole,
    						             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than DWORD size!\n",
    						             dwData, szValue, szKey);
    					else if (dwData > sizeof(DWORD))
    						PrintConsole(hConsole,
    						             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than DWORD size\n",
    						             dwData, szValue, szKey);
    					break;
    
    				case REG_QWORD_LITTLE_ENDIAN:
    			//	case REG_QWORD:
    
    					if (dwData < sizeof(QWORD))
    						PrintConsole(hConsole,
    						             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than QWORD size!\n",
    						             dwData, szValue, szKey);
    					else if (dwData > sizeof(QWORD))
    						PrintConsole(hConsole,
    						             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than QWORD size\n",
    						             dwData, szValue, szKey);
    					break;
    
    				case REG_SZ:
    				case REG_EXPAND_SZ:
    
    					dwChars = wcslen((LPCWSTR) cbData);
    					dwBytes = (dwChars + 1) * sizeof(L'\0');
    
    					if (dwData < dwBytes)
    						PrintConsole(hConsole,
    						             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu + 1 characters = %lu bytes)\n",
    						             dwData, szValue, szKey, dwChars, dwBytes);
    					else if (dwData > dwBytes)
    						PrintConsole(hConsole,
    						             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu + 1 characters = %lu bytes)\n",
    						             dwData, szValue, szKey, dwChars, dwBytes);
    					break;
    
    				case REG_MULTI_SZ:
    
    					dwChars = 0;
    					dwCount = 1;
    					lpCount = (LPCWSTR) cbData;
    
    					while (*lpCount != L'\0')
    					{
    						dwChars += wcslen(lpCount);
    						dwCount++;
    						lpCount += wcslen(lpCount) + 1;
    					}
    
    					dwBytes = (dwChars + dwCount) * sizeof(L'\0');
    
    					if (dwData < dwBytes)
    						PrintConsole(hConsole,
    						             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than sum of actual string lengths (%lu + %lu characters = %lu bytes)\n",
    						             dwData, szValue, szKey, dwChars, dwCount, dwBytes);
    					else if (dwData > dwBytes)
    						PrintConsole(hConsole,
    						             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than sum of actual string lengths (%lu + %lu characters = %lu bytes)\n",
    						             dwData, szValue, szKey, dwChars, dwCount, dwBytes);
    					break;
    
    			//	case REG_NONE:
    			//	case REG_BINARY:
    			//	case REG_RESOURCE_LIST:
    			//	case REG_FULL_RESOURCE_DESCRIPTOR:
    			//	case REG_RESOURCE_REQUIREMENTS_LIST:
    				}
    #ifdef UNKNOWN
    			if (dwType > REG_QWORD)
    				PrintConsole(hConsole,
    				             L"WARNING: unknown data type (0x%08lx) for value name \'%ls\' in registry key \'%ls\'\n",
    				             dwType, szValue, szKey);
    #endif
    #endif // SANITY
    			if (dwKey < sizeof("HKEY_OFFLINE"))
    				bOutput = PrintFormat(hOutput,
    				                      L"HKO,,");
    			else
    				bOutput = PrintFormat(hOutput,
    				                      L"HKO,\"%ls\",",
    				                      szKey + sizeof("HKEY_OFFLINE"));
    
    			if (dwType > sizeof(szType) / sizeof(*szType))
    				if (dwValue == 0)
    					bOutput &= PrintFormat(hOutput, L",0x%08lx", dwType);
    				else
    					bOutput &= PrintFormat(hOutput, L"\"%ls\",0x%08lx", szValue, dwType);
    			else
    				if (dwValue == 0)
    					bOutput &= PrintFormat(hOutput, L",%%REG_%ls%%", szType[dwType]);
    				else
    					bOutput &= PrintFormat(hOutput, L"\"%ls\",%%REG_%ls%%", szValue, szType[dwType]);
    
    			if (dwData == 0)
    				bOutput &= PrintString(hOutput, L"\r\n", 3);
    			else
    				switch (dwType)
    				{
    				case REG_LINK:
    
    					if (dwData % sizeof(L'\0'))
    						goto DEFAULT;
    
    					bOutput &= PrintString(hOutput, L",\"", 2);
    					bOutput &= PrintString(hOutput, (LPCWSTR) cbData, dwData / sizeof(L'\0'));
    					bOutput &= PrintString(hOutput, L"\"\r\n", 3);
    
    					break;
    
    				case REG_SZ:
    				case REG_EXPAND_SZ:
    
    					if (dwData % sizeof(L'\0'))
    						goto DEFAULT;
    
    					if (*(LPCWSTR) cbData == L'\0')
    						bOutput &= PrintString(hOutput, L",\"\"\r\n", 5);
    					else
    					{
    						lpData = (LPCWSTR) cbData;
    						((LPWSTR) lpData)[dwData / sizeof(L'\0')] = L'\0';
    
    						dwData = wcslen(lpData);
    
    						bOutput &= PrintString(hOutput, L",\"", 2);
    
    						for (lpEscape = InfEscape(lpData); lpEscape != NULL; lpData = lpEscape, lpEscape = InfEscape(lpEscape + 1))
    							bOutput &= PrintString(hOutput, lpData, lpEscape + 1 - lpData);
    
    						bOutput &= PrintString(hOutput, lpData, (LPCWSTR) cbData + dwData - lpData);
    						bOutput &= PrintString(hOutput, L"\"\r\n", 3);
    					}
    
    					break;
    
    				case REG_MULTI_SZ:
    
    					if (dwData % sizeof(L'\0'))
    						goto DEFAULT;
    
    					if ((dwData == sizeof(L'\0'))
    					 && (*(LPCWSTR) cbData == L'\0'))
    						bOutput &= PrintString(hOutput, L";\r\n", 3);
    					else
    					{
    						lpData = (LPCWSTR) cbData;
    						dwData /= sizeof(L'\0');
    						lpLast = lpData + dwData;
    
    						if ((dwData > 1)
    						 && (lpData[dwData - 1] == L'\0')
    						 && (lpData[dwData - 2] == L'\0'))
    							lpLast--;
    						else
    							*(LPWSTR) lpLast = L'\0';
    
    						do
    							if (*lpData == L'\0')
    								bOutput &= PrintString(hOutput, L";\"\"", 3);
    							else
    							{
    								dwData = wcslen(lpData);
    
    								bOutput &= PrintString(hOutput, L",\"", 2);
    
    								for (lpEscape = InfEscape(lpMulti = lpData), lpData += dwData;
    								     lpEscape != NULL; lpMulti = lpEscape,
    								     lpEscape = InfEscape(lpEscape + 1))
    									bOutput &= PrintString(hOutput, lpMulti, lpEscape + 1 - lpMulti);
    
    								bOutput &= PrintString(hOutput, lpMulti, lpData - lpMulti);
    								bOutput &= PrintString(hOutput, L"\"", 1);
    							}
    						while (++lpData < lpLast);
    
    						bOutput &= PrintString(hOutput, L"\r\n", 2);
    					}
    
    					break;
    
    				case REG_DWORD_BIG_ENDIAN:
    
    					if (dwData != sizeof(DWORD))
    						goto DEFAULT;
    #if 0
    					bOutput &= PrintFormat(hOutput, L",%lu\r\n", _byteswap_ulong(*(LPDWORD) cbData));
    #else
    					bOutput &= PrintFormat(hOutput, L",%lu ; 0x%08lx\r\n", _byteswap_ulong(*(LPDWORD) cbData), *(LPDWORD) cbData);
    #endif
    					break;
    
    				case REG_DWORD_LITTLE_ENDIAN:
    			//	case REG_DWORD:
    
    					if (dwData != sizeof(DWORD))
    						goto DEFAULT;
    #if 0
    					bOutput &= PrintFormat(hOutput, L",%lu\r\n", *(LPDWORD) cbData);
    #else
    					bOutput &= PrintFormat(hOutput, L",%lu ; 0x%08lx\r\n", *(LPDWORD) cbData, *(LPDWORD) cbData);
    #endif
    					break;
    
    				case REG_QWORD_LITTLE_ENDIAN:
    			//	case REG_QWORD:
    
    					if (dwData != sizeof(QWORD))
    						goto DEFAULT;
    #if 0
    					bOutput &= PrintFormat(hOutput, L",%I64u\r\n", *(LPQWORD) cbData);
    #else
    					bOutput &= PrintFormat(hOutput, L",%I64u ; 0x%016I64x\r\n", *(LPQWORD) cbData, *(LPQWORD) cbData);
    #endif
    					break;
    
    			//	case REG_NONE:
    			//	case REG_BINARY:
    			//	case REG_RESOURCE_LIST:
    			//	case REG_FULL_RESOURCE_DESCRIPTOR:
    			//	case REG_RESOURCE_REQUIREMENTS_LIST:
    				default:
    				DEFAULT:
    					for (dwBytes = 0; dwBytes < dwData; dwBytes++)
    #if 0
    						bOutput &= PrintFormat(hOutput, L",%02x", cbData[dwBytes]);
    #else
    						bOutput &= PrintString(hOutput, szBytes[cbData[dwBytes]], 3);
    #endif
    					bOutput &= PrintString(hOutput, L"\r\n", 2);
    				}
    
    			if (!bOutput)
    				PrintConsole(hConsole,
    				             L"WriteFile() returned error %lu for value \'%ls\' of registry key \'%ls\'\n",
    				             dwError = GetLastError(), szValue, szKey);
    		}
    	}
    
    	for (dwSubKeys = 0;; dwSubKeys++)
    	{
    		dwSubKey = sizeof(szKey) / sizeof(*szKey) - dwKey - 1;
    
    		dwError = OREnumKey(hkKey,
    		                    dwSubKeys,
    		                    lpSubKey,
    		                    &dwSubKey,
    		                    (LPWSTR) NULL,
    		                    (LPDWORD) NULL,
    		                    (LPFILETIME) NULL);
    
    		if (dwError == ERROR_NO_MORE_ITEMS)
    			break;
    
    		if (dwError != ERROR_SUCCESS)
    			PrintConsole(hConsole,
    			             L"OREnumKey() returned error %lu for registry key \'%ls\'\n",
    			             dwError, szKey);
    		else
    		{
    #ifdef SANITY
    			dwChars = wcslen(lpSubKey);
    
    			if (dwChars > dwSubKey)
    				PrintConsole(hConsole,
    				             L"ERROR: size (%lu characters) of subkey name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu characters)\n",
    				             dwSubKey, lpSubKey, szKey, dwChars);
    			else if (dwChars < dwSubKey)
    				PrintConsole(hConsole,
    				             L"WARNING: size (%lu characters) of subkey name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu characters)\n",
    				             dwSubKey, lpSubKey, szKey, dwChars);
    #endif // SANITY
    			szKey[dwKey] = L'\\';
    
    			dwError = OROpenKey(hkKey,
    			                    lpSubKey,
    			                    &hkSubKey);
    
    			if (dwError != ERROR_SUCCESS)
    				PrintConsole(hConsole,
    				             L"OROpenKey() returned error %lu for registry key \'%ls\'\n",
    				             dwError, szKey);
    			else
    			{
    				dwError = Offline(hConsole, hOutput, hkSubKey, dwKey + 1 + dwSubKey);
    
    				dwValue = ORCloseKey(hkSubKey);
    
    				if (dwValue != ERROR_SUCCESS)
    					PrintConsole(hConsole,
    					             L"ORCloseKey() returned error %lu for registry key \'%ls\'\n",
    					             dwValue, szKey);
    			}
    
    			szKey[dwKey] = L'\0';
    		}
    	}
    
    	if ((dwValues == 0) && (dwSubKeys == 0))
    	{
    		if (dwKey < sizeof("HKEY_OFFLINE"))
    			bOutput = PrintFormat(hOutput,
    			                      L"HKO,,,%%REG_KEYONLY%%\r\n");
    		else
    			bOutput = PrintFormat(hOutput,
    			                      L"HKO,\"%ls\",,%%REG_KEYONLY%%\r\n",
    			                      szKey + sizeof("HKEY_OFFLINE"));
    
    		if (!bOutput)
    			PrintConsole(hConsole,
    			             L"WriteFile() returned error %lu for empty registry key \'%ls\'\n",
    			             dwError = GetLastError(), szKey);
    	}
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	SYSTEMTIME st;
    
    	LPWSTR	*lpArguments;
    	INT	nArguments;
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwMajor, dwMinor;
    	HKEY	hkRoot;
    	HANDLE	hOutput;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"No argument: a single filename of a registry hive must be given!\n");
    			else if (nArguments > 2)
    				PrintConsole(hConsole,
    				             L"Too many arguments: a single filename of a registry hive must be given!\n");
    			else
    			{
    				hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    				if (hOutput == INVALID_HANDLE_VALUE)
    					PrintConsole(hConsole,
    					             L"GetStdHandle() returned error %lu\n",
    					             dwError = GetLastError());
    				else
    				{
    					if (!FlushFileBuffers(hOutput))
    						PrintConsole(hConsole,
    						             L"FlushFileBuffers() returned error %lu: standard output is not redirected to a file!\n",
    						             dwError = GetLastError());
    					else
    					{
    						ORGetVersion(&dwMajor, &dwMinor);
    
    						PrintConsole(hConsole,
    						             L"OFFREG.DLL version %lu.%lu\n",
    						             dwMajor, dwMinor);
    
    						dwError = OROpenHive(lpArguments[1], &hkRoot);
    
    						if (dwError != ERROR_SUCCESS)
    							PrintConsole(hConsole,
    							             L"OROpenHive() returned error %lu\n",
    							             dwError);
    						else
    						{
    							GetSystemTime(&st);
    
    							if (!PrintFormat(hOutput,
    							                 L"\xfeff"	// UTF-16LE BOM
    							                 L"[Version]\r\n"
    							                 L"DriverVer = %02hu/%02hu/%04hu,%02hu.%02hu.%02hu.%03hu ; UTC\r\n"
    							                 L"Provider  = \"Stefan Kanthak\"\r\n"
    							                 L"Signature = \"$Windows NT$\"\r\n"
    							                 L"\r\n"
    							                 L"[Strings]\r\n"
    							                 L"REG_SZ                         = 0x00000000\r\n"
    							                 L"REG_BINARY                     = 0x00000001\r\n"
    							                 L"REG_KEYONLY                    = 0x00000010\r\n"
    							                 L"REG_MULTI_SZ                   = 0x00010000\r\n"
    							                 L"REG_DWORD                      = 0x00010001\r\n"
    							                 L"REG_EXPAND_SZ                  = 0x00020000\r\n"
    							                 L"REG_NONE                       = 0x00020001\r\n"
    							                 L"REG_COMPATIBLE                 = 0x00030001 ; same as REG_BINARY\r\n"
    							                 L"REG_DWORD_LITTLE_ENDIAN        = 0x00040001 ; same as REG_DWORD\r\n"
    							                 L"REG_DWORD_BIG_ENDIAN           = 0x00050001\r\n"
    							                 L"REG_LINK                       = 0x00060000\r\n"
    							                 L"REG_RESOURCE_LIST              = 0x00080001\r\n"
    							                 L"REG_FULL_RESOURCE_DESCRIPTOR   = 0x00090001\r\n"
    							                 L"REG_RESOURCE_REQUIREMENTS_LIST = 0x000a0001\r\n"
    							                 L"REG_QWORD                      = 0x000b0001\r\n"
    							                 L"REG_QWORD_LITTLE_ENDIAN        = 0x000b0001 ; same as REG_QWORD\r\n"
    							                 L"\r\n"
    							                 L"[DefaultInstall.NT]\r\n"
    							                 L";AddReg = AddReg.HKO\r\n"
    							                 L"\r\n"
    							                 L"[AddReg.HKO]\r\n",
    							                 st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds))
    								PrintConsole(hConsole,
    								             L"WriteFile() returned error %lu\n",
    								             dwError = GetLastError());
    
    							memcpy(szKey, L"HKEY_OFFLINE", sizeof(L"HKEY_OFFLINE"));
    
    							dwError = Offline(hConsole, hOutput, hkRoot, sizeof("HKEY_OFFLINE") - 1);
    
    							if (!PrintString(hOutput,
    							                 L"\r\n"
    							                 L"; EOF\r\n", 9))
    								PrintConsole(hConsole,
    								             L"WriteFile() returned error %lu\n",
    								             dwError = GetLastError());
    
    							dwError = ORCloseHive(hkRoot);
    
    							if (dwError != ERROR_SUCCESS)
    								PrintConsole(hConsole,
    								             L"ORCloseHive() returned error %lu\n",
    								             dwError);
    						}
    					}
    
    					if (!CloseHandle(hOutput))
    						PrintConsole(hConsole,
    						             L"CloseHandle() returned error %lu\n",
    						             GetLastError());
    				}
    			}
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file OFFREG.C created in step 1., link the compiled object file OFFREG.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:OFFREG.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:mainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Offline Registry Reader.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE OFFREG.C
    ERASE OFFREG.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    OFFREG.C
    OFFREG.C(106) : warning C4213: nonstandard extension used : cast on l-value
    OFFREG.C(470) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Registry INF Dumper

Purpose
Implementation and Build Details
Source, Build Instructions and Sample Output

Purpose

Enumerate all keys and values of one or more predefined Registry branches (HKCC, HKCR, HKCU, HKLM, HKLS, HKPD, HKU) and print them as an .inf file in UTF-16LE encoding on standard output (which must be redirected to a file).

Note: to dump the entire Registry specify the branches HKLM and HKU.

Implementation and Build Details

Registry INF Dumper is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error is not supported!

The MSDN article Console Handles provides background information.

Source, Build Instructions and Sample Output

Perform the following 3 simple steps to build the console application Registry INF Dumper from the source presented hereafter and execute it.
  1. Create the text file REGISTRY.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define _CRT_SECURE_NO_WARNINGS
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    #include <sddl.h>
    
    #define memcpy	__movsb
    #define wmemcpy	__movsw
    
    #define MAX_DEPTH		512UL
    #define MAX_KEY_LENGTH		255UL
    #define MAX_VALUE_NAME		16383UL
    #define MAX_VALUE_DATA		1048576UL
    
    typedef	unsigned __int64	QWORD, *LPQWORD;
    
    BYTE	cbData[MAX_VALUE_DATA];
    WCHAR	szKey[(MAX_KEY_LENGTH + 1) * MAX_DEPTH];
    WCHAR	szValue[MAX_VALUE_NAME + 1];
    #ifdef SECURITY
    BYTE	cbSD[65536];
    #endif
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    __declspec(safebuffers)
    BOOL	PrintFormat(HANDLE hFile, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	LPBYTE	lpBuffer;
    	DWORD	dwBuffer;
    	DWORD	dwFile;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	dwBuffer *= sizeof(*szBuffer);
    	lpBuffer = (LPBYTE) szBuffer;
    
    	do
    	{
    		if (!WriteFile(hFile, lpBuffer, dwBuffer, &dwFile, (LPOVERLAPPED) NULL))
    			return FALSE;
    
    		lpBuffer += dwFile;
    		dwBuffer -= dwFile;
    	} while (dwBuffer > 0);
    
    	return TRUE;
    }
    
    __inline
    BOOL	WINAPI	PrintString(HANDLE hFile, LPCWSTR lpString, DWORD dwString)
    {
    	DWORD	dwFile;
    
    	dwString *= sizeof(*lpString);
    
    	do
    	{
    		if (!WriteFile(hFile, lpString, dwString, &dwFile, (LPOVERLAPPED) NULL))
    			return FALSE;
    
    		(LPBYTE) lpString += dwFile;
    		dwString -= dwFile;
    	} while (dwString > 0);
    
    	return TRUE;
    }
    
    __inline
    LPCWSTR	WINAPI	InfEscape(LPCWSTR lpString)
    {
    	do
    		if ((*lpString == L'"')
    		 || (*lpString == L'%'))
    			return lpString;
    	while (*lpString++ != L'\0');
    
    	return NULL;
    }
    
    const	WCHAR	szBytes[256][4] = {L",00", L",01", L",02", L",03", L",04", L",05", L",06", L",07", L",08", L",09", L",0a", L",0b", L",0c", L",0d", L",0e", L",0f",
    		                   L",10", L",11", L",12", L",13", L",14", L",15", L",16", L",17", L",18", L",19", L",1a", L",1b", L",1c", L",1d", L",1e", L",1f",
    		                   L",20", L",21", L",22", L",23", L",24", L",25", L",26", L",27", L",28", L",29", L",2a", L",2b", L",2c", L",2d", L",2e", L",2f",
    		                   L",30", L",31", L",32", L",33", L",34", L",35", L",36", L",37", L",38", L",39", L",3a", L",3b", L",3c", L",3d", L",3e", L",3f",
    		                   L",40", L",41", L",42", L",43", L",44", L",45", L",46", L",47", L",48", L",49", L",4a", L",4b", L",4c", L",4d", L",4e", L",4f",
    		                   L",50", L",51", L",52", L",53", L",54", L",55", L",56", L",57", L",58", L",59", L",5a", L",5b", L",5c", L",5d", L",5e", L",5f",
    		                   L",60", L",61", L",62", L",63", L",64", L",65", L",66", L",67", L",68", L",69", L",6a", L",6b", L",6c", L",6d", L",6e", L",6f",
    		                   L",70", L",71", L",72", L",73", L",74", L",75", L",76", L",77", L",78", L",79", L",7a", L",7b", L",7c", L",7d", L",7e", L",7f",
    		                   L",80", L",81", L",82", L",83", L",84", L",85", L",86", L",87", L",88", L",89", L",8a", L",8b", L",8c", L",8d", L",8e", L",8f",
    		                   L",90", L",91", L",92", L",93", L",94", L",95", L",96", L",97", L",98", L",99", L",9a", L",9b", L",9c", L",9d", L",9e", L",9f",
    		                   L",a0", L",a1", L",a2", L",a3", L",a4", L",a5", L",a6", L",a7", L",a8", L",a9", L",aa", L",ab", L",ac", L",ad", L",ae", L",af",
    		                   L",b0", L",b1", L",b2", L",b3", L",b4", L",b5", L",b6", L",b7", L",b8", L",b9", L",ba", L",bb", L",bc", L",bd", L",be", L",bf",
    		                   L",c0", L",c1", L",c2", L",c3", L",c4", L",c5", L",c6", L",c7", L",c8", L",c9", L",ca", L",cb", L",cc", L",cd", L",ce", L",cf",
    		                   L",d0", L",d1", L",d2", L",d3", L",d4", L",d5", L",d6", L",d7", L",d8", L",d9", L",da", L",db", L",dc", L",dd", L",de", L",df",
    		                   L",e0", L",e1", L",e2", L",e3", L",e4", L",e5", L",e6", L",e7", L",e8", L",e9", L",ea", L",eb", L",ec", L",ed", L",ee", L",ef",
    		                   L",f0", L",f1", L",f2", L",f3", L",f4", L",f5", L",f6", L",f7", L",f8", L",f9", L",fa", L",fb", L",fc", L",fd", L",fe", L",ff"};
    
    const	LPCWSTR	szHKey[8] = {L"HKCR",
    		             L"HKCU",
    		             L"HKLM",
    		             L"HKU",
    		             L"HKPD",
    		             L"HKCC",
    		             L"HKDD",
    		             L"HKLS"};
    
    const	LPCWSTR	szHKEY[8] = {L"HKEY_CLASSES_ROOT",
    		             L"HKEY_CURRENT_USER",
    		             L"HKEY_LOCAL_MACHINE",
    		             L"HKEY_USERS",
    		             L"HKEY_PERFORMANCE_DATA",
    		             L"HKEY_CURRENT_CONFIG",
    		             L"HKEY_DYN_DATA",
    		             L"HKEY_CURRENT_USER_LOCAL_SETTINGS"};
    
    const	LPCWSTR	dwHKEY[8] = {sizeof("HKEY_CLASSES_ROOT"),
    		             sizeof("HKEY_CURRENT_USER"),
    		             sizeof("HKEY_LOCAL_MACHINE"),
    		             sizeof("HKEY_USERS"),
    		             sizeof("HKEY_PERFORMANCE_DATA"),
    		             sizeof("HKEY_CURRENT_CONFIG"),
    		             sizeof("HKEY_DYN_DATA"),
    		             sizeof("HKEY_CURRENT_USER_LOCAL_SETTINGS")};
    
    const	LPCWSTR	szType[12] = {L"NONE",
    		              L"SZ",
    		              L"EXPAND_SZ",
    		              L"BINARY",
    		              L"DWORD",		// alias DWORD_LITTLE_ENDIAN
    		              L"DWORD_BIG_ENDIAN",
    		              L"LINK",
    		              L"MULTI_SZ",
    		              L"RESOURCE_LIST",
    		              L"FULL_RESOURCE_DESCRIPTOR",
    		              L"RESOURCE_REQUIREMENTS_LIST",
    		              L"QWORD"};	// alias QWORD_LITTLE_ENDIAN
    
    DWORD	WINAPI	Registry(HANDLE hConsole, HANDLE hOutput, HKEY hkHKEY, DWORD dwKey)
    {
    	DWORD	dwIndex = (DWORD) hkHKEY ^ (DWORD) HKEY_CLASSES_ROOT;
    	DWORD	dwSubKeys, dwSubKey;
    	DWORD	dwValues, dwValue, dwType, dwData, dwBytes;
    #ifdef SANITY
    	DWORD	dwCount, dwChars;
    	LPCWSTR	lpCount;
    #endif
    	LPCWSTR	lpHKey = szHKey[dwIndex];
    	LPCWSTR	lpKey = dwKey < dwHKEY[dwIndex] ? NULL : szKey + dwHKEY[dwIndex];
    	LPCWSTR	lpSubKey = szKey + dwKey + 1;
    	LPCWSTR	lpData, lpEscape, lpLast, lpMulti;
    #ifdef SECURITY
    	LPCWSTR	lpSDDL;
    	DWORD	dwSD;
    #endif
    	DWORD	dwError;
    	BOOL	bOutput;
    	HKEY	hkKey;
    
    	dwError = RegOpenKeyEx(hkHKEY,
    	                       lpKey,
    	                       REG_OPTION_OPEN_LINK,
    	                       KEY_READ | KEY_WOW64_64KEY,
    	                       &hkKey);
    
    	if (dwError != ERROR_SUCCESS)
    		PrintConsole(hConsole,
    		             L"RegOpenKeyEx() returned error %lu for registry key \'%ls\'\n",
    		             dwError, szKey);
    	else
    	{
    #ifdef SECURITY
    		dwSD = sizeof(cbSD);
    
    		dwError = RegGetKeySecurity(hkHKEY,
    		                            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
    		                            cbSD,
    		                            &dwSD);
    
    		if (dwError != ERROR_SUCCESS)
    			PrintConsole(hConsole,
    			             L"RegGetKeySecurity() returned error %lu for registry key \'%ls\'\n",
    			             dwError, szKey);
    		else
    			if (!ConvertSecurityDescriptorToStringSecurityDescriptor(cbSD,
    			                                                         SDDL_REVISION_1,
    			                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
    			                                                         &lpSDDL,
    			                                                         (LPDWORD) NULL))
    				PrintConsole(hConsole,
    				             L"ConvertSecurityDescriptorToStringSecurityDescriptor() returned error %lu for registry key \'%ls\'\n",
    				             dwError = GetLastError(), szKey);
    			else
    			{
    				if (lpKey == NULL)
    					bOutput = PrintFormat(hOutput,
    					                      L"%ls,,,%%REG_KEYONLY%%; %ls\n",
    					                      lpHKey, lpSDDL);
    				else
    					bOutput = PrintFormat(hOutput,
    					                      L"%ls,\"%ls\",,%%REG_KEYONLY%%; %ls\n",
    					                      lpHKey, lpKey, lpSDDL))
    
    				if (!bOutput)
    					PrintConsole(hConsole,
    					             L"WriteFile() returned error %lu for registry key \'%ls\'\n",
    					             dwError = GetLastError(), szKey);
    
    				if (LocalFree(lpSDDL) != NULL)
    					PrintConsole(hConsole,
    					             L"LocalFree() returned error %lu\n",
    					             GetLastError());
    			}
    #endif
    		for (dwValues = 0;; dwValues++)
    		{
    		//	*szValue = L'\0';
    			dwValue = sizeof(szValue) / sizeof(*szValue);
    			dwData = sizeof(cbData);
    
    			dwError = RegEnumValue(hkKey,
    			                       dwValues,
    			                       szValue,
    			                       &dwValue,
    			                       (LPDWORD) NULL,
    			                       &dwType,
    			                       cbData,
    			                       &dwData);
    
    			if (dwError != ERROR_SUCCESS)
    			{
    				if (dwError == ERROR_NO_MORE_ITEMS)
    					break;
    
    				PrintConsole(hConsole,
    				             L"RegEnumValue() returned error %lu for registry key \'%ls\'\n",
    				             dwError, szKey);
    
    				if (dwError == ERROR_ACCESS_DENIED)
    					break;
    			}
    			else
    			{
    #ifdef SANITY
    				dwChars = wcslen(szValue);
    
    				if (dwValue < dwChars)
    					PrintConsole(hConsole,
    					             L"ERROR: size (%lu characters) of value name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu characters)!\n",
    					             dwValue, szValue, dwChars, szKey);
    				else if (dwValue > dwChars)
    					PrintConsole(hConsole,
    					             L"WARNING: size (%lu characters) of value name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu characters)\n",
    					             dwValue, szValue, dwChars, szKey);
    
    				if (dwData == 0)
    					PrintConsole(hConsole,
    					             L"WARNING: no value data for value name \'%ls\' in registry key \'%ls\'\n",
    					             szValue, szKey);
    				else
    					switch (dwType)
    					{
    					case REG_LINK:
    
    						if (dwData % sizeof(L'\0'))
    							PrintConsole(hConsole,
    							             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' not a multiple of WCHAR size!\n",
    							             dwData, szValue, szKey);
    						break;
    
    					case REG_DWORD_BIG_ENDIAN:
    					case REG_DWORD_LITTLE_ENDIAN:
    				//	case REG_DWORD:
    
    						if (dwData < sizeof(DWORD))
    							PrintConsole(hConsole,
    							             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than DWORD size!\n",
    							             dwData, szValue, szKey);
    						else if (dwData > sizeof(DWORD))
    							PrintConsole(hConsole,
    							             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than DWORD size\n",
    							             dwData, szValue, szKey);
    						break;
    
    					case REG_QWORD_LITTLE_ENDIAN:
    				//	case REG_QWORD:
    
    						if (dwData < sizeof(QWORD))
    							PrintConsole(hConsole,
    							             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than QWORD size!\n",
    							             dwData, szValue, szKey);
    						else if (dwData > sizeof(QWORD))
    							PrintConsole(hConsole,
    							             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than QWORD size\n",
    							             dwData, szValue, szKey);
    						break;
    
    					case REG_SZ:
    					case REG_EXPAND_SZ:
    
    						dwChars = wcslen((LPCWSTR) cbData);
    						dwBytes = (dwChars + 1) * sizeof(L'\0');
    
    						if (dwData < dwBytes)
    							PrintConsole(hConsole,
    							             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu + 1 characters = %lu bytes)\n",
    							             dwData, szValue, szKey, dwChars, dwBytes);
    						else if (dwData > dwBytes)
    							PrintConsole(hConsole,
    							             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu + 1 characters = %lu bytes)\n",
    							             dwData, szValue, szKey, dwChars, dwBytes);
    						break;
    
    					case REG_MULTI_SZ:
    
    						dwChars = 0;
    						dwCount = 1;
    						lpCount = (LPCWSTR) cbData;
    
    						while (*lpCount != L'\0')
    						{
    							dwChars += wcslen(lpCount);
    							dwCount++;
    							lpCount += wcslen(lpCount) + 1;
    						}
    
    						dwBytes = (dwChars + dwCount) * sizeof(L'\0');
    
    						if (dwData < dwBytes)
    							PrintConsole(hConsole,
    							             L"ERROR: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' smaller than sum of actual string lengths (%lu + %lu characters = %lu bytes)\n",
    							             dwData, szValue, szKey, dwChars, dwCount, dwBytes);
    						else if (dwData > dwBytes)
    							PrintConsole(hConsole,
    							             L"WARNING: size (%lu bytes) of value data for value name \'%ls\' in registry key \'%ls\' greater than sum of actual string lengths (%lu + %lu characters = %lu bytes)\n",
    							             dwData, szValue, szKey, dwChars, dwCount, dwBytes);
    						break;
    
    				//	case REG_NONE:
    				//	case REG_BINARY:
    				//	case REG_RESOURCE_LIST:
    				//	case REG_FULL_RESOURCE_DESCRIPTOR:
    				//	case REG_RESOURCE_REQUIREMENTS_LIST:
    					}
    #ifdef UNKNOWN
    				if (dwType > REG_QWORD)
    					PrintConsole(hConsole,
    					             L"WARNING: unknown data type (0x%08lx) for value name \'%ls\' in registry key \'%ls\'\n",
    					             dwType, szValue, szKey);
    #endif
    #endif // SANITY
    				if (lpKey == NULL)
    					bOutput = PrintFormat(hOutput, L"%ls,,", lpHKey);
    				else
    					bOutput = PrintFormat(hOutput, L"%ls,\"%ls\",", lpHKey, lpKey);
    
    				if (dwType > sizeof(szType) / sizeof(*szType))
    					if (dwValue == 0)
    						bOutput &= PrintFormat(hOutput, L",0x%08lx", dwType);
    					else
    						bOutput &= PrintFormat(hOutput, L"\"%ls\",0x%08lx", dwType);
    				else
    					if (dwValue == 0)
    						bOutput &= PrintFormat(hOutput, L",%%REG_%ls%%", szType[dwType]);
    					else
    						bOutput &= PrintFormat(hOutput, L"\"%ls\",%%REG_%ls%%", szValue, szType[dwType]);
    
    				if (dwData == 0)
    					bOutput &= PrintString(hOutput, L"\r\n", 3);
    				else
    					switch (dwType)
    					{
    					case REG_LINK:
    
    						if (dwData % sizeof(L'\0'))
    							goto DEFAULT;
    
    						bOutput &= PrintString(hOutput, L",\"", 2);
    						bOutput &= PrintString(hOutput, (LPCWSTR) cbData, dwData / sizeof(L'\0'));
    						bOutput &= PrintString(hOutput, L"\"\r\n", 3);
    
    						break;
    
    					case REG_SZ:
    					case REG_EXPAND_SZ:
    
    						if (dwData % sizeof(L'\0'))
    							goto DEFAULT;
    
    						if (*(LPCWSTR) cbData == L'\0')
    							bOutput &= PrintString(hOutput, L",\"\"\r\n", 5);
    						else
    						{
    							lpData = (LPCWSTR) cbData;
    							((LPWSTR) lpData)[dwData / sizeof(L'\0')] = L'\0';
    							dwData = wcslen(lpData);
    
    							bOutput &= PrintString(hOutput, L",\"", 2);
    
    							for (lpEscape = InfEscape(lpData); lpEscape != NULL; lpData = lpEscape, lpEscape = InfEscape(lpData + 1))
    								bOutput &= PrintString(hOutput, lpData, lpEscape + 1 - lpData);
    
    							bOutput &= PrintString(hOutput, lpData, (LPCWSTR) cbData + dwData - lpData);
    							bOutput &= PrintString(hOutput, L"\"\r\n", 3);
    						}
    
    						break;
    
    					case REG_MULTI_SZ:
    
    						if (dwData % sizeof(L'\0'))
    							goto DEFAULT;
    
    						if ((dwData == sizeof(L'\0'))
    						 && (*(LPCWSTR) cbData == L'\0'))
    							bOutput &= PrintString(hOutput, L";\r\n", 3);
    						else
    						{
    							lpData = (LPCWSTR) cbData;
    							dwData /= sizeof(L'\0');
    							lpLast = lpData + dwData;
    
    							if ((dwData > 1)
    							 && (lpData[dwData - 1] == L'\0')
    							 && (lpData[dwData - 2] == L'\0'))
    								lpLast--;
    							else
    								*(LPWSTR) lpLast = L'\0';
    
    							do
    								if (*lpData == L'\0')
    									bOutput &= PrintString(hOutput, L";\"\"", 3);
    								else
    								{
    									dwData = wcslen(lpData);
    
    									bOutput &= PrintString(hOutput, L",\"", 2);
    
    									for (lpEscape = InfEscape(lpMulti = lpData), lpData += dwData;
    									     lpEscape != NULL; lpMulti = lpEscape,
    									     lpEscape = InfEscape(lpEscape + 1))
    										bOutput &= PrintString(hOutput, lpMulti, lpEscape + 1 - lpMulti);
    
    									bOutput &= PrintString(hOutput, lpMulti, lpData - lpMulti);
    									bOutput &= PrintString(hOutput, L"\"", 1);
    								}
    							while (++lpData < lpLast);
    
    							bOutput &= PrintString(hOutput, L"\r\n", 2);
    						}
    
    						break;
    
    					case REG_DWORD_BIG_ENDIAN:
    
    						if (dwData != sizeof(DWORD))
    							goto DEFAULT;
    #if 0
    						bOutput &= PrintFormat(hOutput, L",%lu\r\n", _byteswap_ulong(*(LPDWORD) cbData));
    #else
    						bOutput &= PrintFormat(hOutput, L",%lu ; 0x%08lx\r\n", _byteswap_ulong(*(LPDWORD) cbData), *(LPDWORD) cbData);
    #endif
    						break;
    
    					case REG_DWORD_LITTLE_ENDIAN:
    				//	case REG_DWORD:
    
    						if (dwData != sizeof(DWORD))
    							goto DEFAULT;
    #if 0
    						bOutput &= PrintFormat(hOutput, L",%lu\r\n", *(LPDWORD) cbData);
    #else
    						bOutput &= PrintFormat(hOutput, L",%lu ; 0x%08lx\r\n", *(LPDWORD) cbData, *(LPDWORD) cbData);
    #endif
    						break;
    
    					case REG_QWORD_LITTLE_ENDIAN:
    				//	case REG_QWORD:
    
    						if (dwData != sizeof(QWORD))
    							goto DEFAULT;
    #if 0
    						bOutput &= PrintFormat(hOutput, L",%I64u\r\n", *(LPQWORD) cbData);
    #else
    						bOutput &= PrintFormat(hOutput, L",%I64u ; 0x%016I64x\r\n", *(LPQWORD) cbData, *(LPQWORD) cbData);
    #endif
    						break;
    
    				//	case REG_NONE:
    				//	case REG_BINARY:
    				//	case REG_RESOURCE_LIST:
    				//	case REG_FULL_RESOURCE_DESCRIPTOR:
    				//	case REG_RESOURCE_REQUIREMENTS_LIST:
    					default:
    					DEFAULT:
    						for (dwBytes = 0; dwBytes < dwData; dwBytes++)
    #if 0
    							bOutput &= PrintFormat(hOutput, L",%02x", cbData[dwBytes]);
    #else
    							bOutput &= PrintString(hOutput, szBytes[cbData[dwBytes]], 3);
    #endif
    						bOutput &= PrintString(hOutput, L"\r\n", 2);
    					}
    
    				if (!bOutput)
    					PrintConsole(hConsole,
    					             L"WriteFile() returned error %lu for value \'%ls\' of registry key \'%ls\'\n",
    					             dwError = GetLastError(), szValue, szKey);
    			}
    		}
    
    		for (dwSubKeys = 0;; dwSubKeys++)
    		{
    			dwSubKey = sizeof(szKey) / sizeof(*szKey) - dwKey - 1;
    #if 0
    			dwError = RegEnumKey(hkKey,
    			                     dwSubKeys,
    			                     lpSubKey,
    			                     dwSubKey);
    #else
    			dwError = RegEnumKeyEx(hkKey,
    			                       dwSubKeys,
    			                       lpSubKey,
    			                       &dwSubKey,
    			                       (LPDWORD) NULL,
    			                       (LPWSTR) NULL,
    			                       (LPDWORD) NULL,
    			                       (LPFILETIME) NULL);
    #endif
    			if (dwError != ERROR_SUCCESS)
    			{
    				if (dwError == ERROR_NO_MORE_ITEMS)
    					break;
    
    				PrintConsole(hConsole,
    #if 0
    				             L"RegEnumKey() returned error %lu for registry key \'%ls\'\n",
    #else
    				             L"RegEnumKeyEx() returned error %lu for registry key \'%ls\'\n",
    #endif
    				             dwError, szKey);
    
    				if (dwError == ERROR_ACCESS_DENIED)
    					break;
    			}
    			else
    			{
    #ifdef SANITY
    				dwChars = wcslen(lpSubKey);
    
    				if (dwChars > dwSubKey)
    					PrintConsole(hConsole,
    					             L"ERROR: size (%lu characters) of subkey name \'%ls\' in registry key \'%ls\' smaller than actual string length (%lu characters)\n",
    					             dwSubKey, lpSubKey, szKey, dwChars);
    				else if (dwChars < dwSubKey)
    					PrintConsole(hConsole,
    					             L"WARNING: size (%lu characters) of subkey name \'%ls\' in registry key \'%ls\' greater than actual string length (%lu characters)\n",
    					             dwSubKey, lpSubKey, szKey, dwChars);
    #endif
    				szKey[dwKey] = L'\\';
    
    				dwError = Registry(hConsole, hOutput, hkHKEY, dwKey + 1 + dwSubKey);
    
    				szKey[dwKey] = L'\0';
    			}
    		}
    #ifndef SECURITY
    		if ((dwValues == 0) && (dwSubKeys == 0))
    		{
    			if (lpKey == NULL)
    				bOutput = PrintFormat(hOutput,
    				                      L"%ls,,,%%REG_KEYONLY%%\r\n",
    				                      lpHKey);
    			else
    				bOutput = PrintFormat(hOutput,
    				                      L"%ls,\"%ls\",,%%REG_KEYONLY%%\r\n",
    				                      lpHKey, lpKey);
    
    			if (!bOutput)
    				PrintConsole(hConsole,
    				             L"WriteFile() returned error %lu for empty registry key \'%ls\'\n",
    				             dwError = GetLastError(), szKey);
    		}
    #endif
    		dwValue = RegCloseKey(hkKey);
    
    		if (dwValue != ERROR_SUCCESS)
    			PrintConsole(hConsole,
    			             L"RegCloseKey() returned error %lu for registry key \'%ls\'\n",
    			             dwValue, szKey);
    	}
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	SYSTEMTIME st;
    
    	INT	nArgument = 1;
    	INT	nArguments;
    	LPWSTR	*lpArguments;
    	WCHAR	szComputer[MAX_COMPUTERNAME_LENGTH + 1] = L"<unknown>";
    	DWORD	dwComputer = sizeof(szComputer) / sizeof(*szComputer);
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwIndex;
    	HKEY	hkHKEY;
    	HANDLE	hOutput;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"No arguments: at least one predefined registry key name must be given!\n");
    			else
    			{
    				hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    				if (hOutput == INVALID_HANDLE_VALUE)
    					PrintConsole(hConsole,
    					             L"GetStdHandle() returned error %lu\n",
    					             dwError = GetLastError());
    				else
    				{
    					if (!FlushFileBuffers(hOutput))
    						PrintConsole(hConsole,
    						             L"FlushFileBuffers() returned error %lu: standard output is not redirected to a file!\n",
    						             dwError = GetLastError());
    					else
    					{
    						dwError = ERROR_SUCCESS;
    
    						if (!GetComputerName(szComputer, &dwComputer))
    							PrintConsole(hConsole,
    							             L"GetComputerName() returned error %lu\n",
    							             dwError = GetLastError());
    
    						GetSystemTime(&st);
    
    						if (!PrintFormat(hOutput,
    						                 L"\xfeff"	// UTF-16LE BOM
    						                 L"; Registry of \'%ls\'\r\n"
    						                 L"\r\n"
    						                 L"[Version]\r\n"
    						                 L"DriverVer = %02hu/%02hu/%04hu,%02hu.%02hu.%02hu.%03hu ; UTC\r\n"
    						                 L"Provider  = \"Stefan Kanthak\"\r\n"
    						                 L"Signature = \"$Windows NT$\"\r\n"
    						                 L"\r\n"
    						                 L"[Strings]\r\n"
    						                 L"REG_SZ                         = 0x00000000\r\n"
    						                 L"REG_BINARY                     = 0x00000001\r\n"
    						                 L"REG_KEYONLY                    = 0x00000010\r\n"
    						                 L"REG_MULTI_SZ                   = 0x00010000\r\n"
    						                 L"REG_DWORD                      = 0x00010001\r\n"
    						                 L"REG_EXPAND_SZ                  = 0x00020000\r\n"
    						                 L"REG_NONE                       = 0x00020001\r\n"
    						                 L"REG_COMPATIBLE                 = 0x00030001 ; same as REG_BINARY\r\n"
    						                 L"REG_DWORD_LITTLE_ENDIAN        = 0x00040001 ; same as REG_DWORD\r\n"
    						                 L"REG_DWORD_BIG_ENDIAN           = 0x00050001\r\n"
    						                 L"REG_LINK                       = 0x00060000\r\n"
    						                 L"REG_RESOURCE_LIST              = 0x00080001\r\n"
    						                 L"REG_FULL_RESOURCE_DESCRIPTOR   = 0x00090001\r\n"
    						                 L"REG_RESOURCE_REQUIREMENTS_LIST = 0x000a0001\r\n"
    						                 L"REG_QWORD                      = 0x000b0001\r\n"
    						                 L"REG_QWORD_LITTLE_ENDIAN        = 0x000b0001 ; same as REG_QWORD\r\n"
    						                 L"\r\n"
    						                 L"[DefaultInstall.NT]\r\n"
    						                 L";AddReg = AddReg.HKU,AddReg.HKLM;AddReg.HKCU,AddReg.HKCR\r\n",
    						                 szComputer,
    						                 st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds))
    							PrintConsole(hConsole,
    							             L"WriteFile() returned error %lu\n",
    							             dwError = GetLastError());
    
    						do
    						{
    							hkHKEY = HKEY_CLASSES_ROOT;
    
    							do
    							{
    								dwIndex = (DWORD) hkHKEY ^ (DWORD) HKEY_CLASSES_ROOT;
    
    								if ((wcscmp(szHKey[dwIndex], lpArguments[nArgument]) == 0)
    								 || (wcscmp(szHKEY[dwIndex], lpArguments[nArgument]) == 0))
    								{
    									memcpy(szKey, szHKEY[dwIndex], dwHKEY[dwIndex] * sizeof(*szKey));
    
    									if (!PrintFormat(hOutput,
    									                 L"\r\n"
    									                 L"[AddReg.%ls]\r\n",
    									                 szHKey[dwIndex]))
    										PrintConsole(hConsole,
    										             L"WriteFile() returned error %lu\n",
    										             dwError = GetLastError());
    
    									Registry(hConsole, hOutput, hkHKEY, dwHKEY[dwIndex] - 1);
    
    									break;
    								}
    							} while (++(DWORD) hkHKEY <= (DWORD) HKEY_CURRENT_USER_LOCAL_SETTINGS);
    
    							if ((DWORD) hkHKEY > (DWORD) HKEY_CURRENT_USER_LOCAL_SETTINGS)
    								PrintConsole(hConsole,
    								             L"Argument \'%ls\' is not a predefined registry key name!\n",
    								             lpArguments[nArgument]);
    						} while (++nArgument < nArguments);
    
    						if (!PrintString(hOutput,
    						                 L"\r\n"
    						                 L"; EOF\r\n", 9))
    							PrintConsole(hConsole,
    							             L"WriteFile() returned error %lu\n",
    							             dwError = GetLastError());
    					}
    
    					if (!CloseHandle(hOutput))
    						PrintConsole(hConsole,
    						             L"CloseHandle() returned error %lu\n",
    						             GetLastError());
    				}
    			}
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
    Note: the output format allows to detect Registry values without data, REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ values with empty strings, REG_LINK, REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ values with (invalid) odd size, and REG_DWORD_BIG_ENDIAN, REG_DWORD_LITTLE_ENDIAN alias REG_DWORD as well as REG_QWORD_LITTLE_ENDIAN alias REG_QWORD values with sizes not matching their data type.

    Note: with the preprocessor macro SANITY defined, several consistency and sanity checks regarding the size of key and value names as well as value data and value data types are performed.

    Note: with the preprocessor macro SECURITY defined, the security descriptors of enumerated keys are printed in Security Descriptor Definition Language notation.

  2. Run the following four command lines to compile the source file REGISTRY.C created in step 1., link the compiled object file REGISTRY.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:ADVAPI32.LIB /DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Registry INF Dumper.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE REGISTRY.C
    ERASE REGISTRY.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    REGISTRY.C
    REGISTRY.C(113) : warning C4213: nonstandard extension used : cast on l-value
    REGISTRY.C(573) : warning C4090: 'function' : different 'const' qualifiers
    REGISTRY.C(761) : warning C4213: nonstandard extension used : cast on l-value
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …
  3. Finally execute the console application Registry INF Dumper.com built in step 2. to dump the HKCC branch to the file HKCC.inf and display it afterwards:

    VER
    ".\Registry INF Dumper.com" HKCC 1>HKCC.inf
    NET.EXE HelpMsg %ERRORLEVEL%
    TYPE HKCC.inf
    Microsoft Windows [Version 6.1.7601]
    
    The operation completed successfully.
    
    ; Registry of 'AMNESIAC'
    
    [Version]
    DriverVer = 04/27/2022,08.15.00.815 ; UTC
    Provider  = "Stefan Kanthak"
    Signature = "$Windows NT$"
    
    [Strings]
    REG_SZ                         = 0x00000000
    REG_BINARY                     = 0x00000001
    REG_KEYONLY                    = 0x00000010
    REG_MULTI_SZ                   = 0x00010000
    REG_DWORD                      = 0x00010001
    REG_EXPAND_SZ                  = 0x00020000
    REG_NONE                       = 0x00020001
    REG_COMPATIBLE                 = 0x00030001 ; same as REG_BINARY
    REG_DWORD_LITTLE_ENDIAN        = 0x00040001 ; same as REG_DWORD
    REG_DWORD_BIG_ENDIAN           = 0x00050001
    REG_LINK                       = 0x00060000
    REG_RESOURCE_LIST              = 0x00080001
    REG_FULL_RESOURCE_DESCRIPTOR   = 0x00090001
    REG_RESOURCE_REQUIREMENTS_LIST = 0x000a0001
    REG_QWORD                      = 0x000b0001
    REG_QWORD_LITTLE_ENDIAN        = 0x000b0001 ; same as REG_QWORD
    
    [DefaultInstall.NT]
    ;AddReg = AddReg.HKU,AddReg.HKLM;AddReg.HKCU,AddReg.HKCR
    
    [AddReg.HKCC]
    HKCC,"Software\Fonts","LogPixels",%REG_DWORD%,96 ; 0x00000060
    HKCC,"System\CurrentControlSet\Control\Print\Printers",,%REG_KEYONLY%;
    HKCC,"System\CurrentControlSet\Control\VIDEO",,%REG_KEYONLY%;
    HKCC,"System\CurrentControlSet\Enum\IDE\DISKFUJITSU_MHZ2320BJ_G2____________________0000001E\5&2223391E&0&0.0.0","CSConfigFlags",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\Enum\USBSTOR\DISK&VEN_GENERIC&PROD_FLASH_DISK&REV_8.07\4E0B595B&0","CSConfigFlags",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\TSDDD\DEVICE0","Attach.ToDesktop",%REG_DWORD%,1 ; 0x00000001
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","Attach.ToDesktop",%REG_DWORD%,1 ; 0x00000001
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.BitsPerPel",%REG_DWORD%,32 ; 0x00000020
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.XResolution",%REG_DWORD%,1920 ; 0x00000780
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.YResolution",%REG_DWORD%,1200 ; 0x000004b0
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.VRefresh",%REG_DWORD%,1 ; 0x00000001
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.Flags",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.XPanning",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.YPanning",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.Orientation",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","DefaultSettings.FixedOutput",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","Attach.RelativeX",%REG_DWORD%,0 ; 0x00000000
    HKCC,"System\CurrentControlSet\SERVICES\VGASAVE\DEVICE0","Attach.RelativeY",%REG_DWORD%,0 ; 0x00000000
    
    ; EOF

Security Descriptor Inspector

Purpose
Implementation and Build Details
Source and Build Instructions

Purpose

Find directories and files on NTFS filesystems that have a security descriptor which contains unknown security principals, i.e. user or group accounts that don’t exist on the local machine.

Implementation and Build Details

Security Descriptor Inspector is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source and Build Instructions

Perform the following 2 (plus 2 optional) simple steps to build the console application Security Descriptor Inspector from the source presented hereafter.
  1. Create the text file SECURITY.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define _CRT_SECURE_NO_WARNINGS
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    #include <sddl.h>
    #include <lmcons.h>
    #include <aclapi.h>
    
    #ifndef LABEL_SECURITY_INFORMATION
    #define LABEL_SECURITY_INFORMATION			0x00000010UL
    #endif
    
    #ifndef ATTRIBUTE_SECURITY_INFORMATION
    #define ATTRIBUTE_SECURITY_INFORMATION			0x00000020UL
    #endif
    
    #ifndef SCOPE_SECURITY_INFORMATION
    #define SCOPE_SECURITY_INFORMATION			0x00000040UL
    #endif
    
    #ifndef PROCESS_TRUST_LABEL_SECURITY_INFORMATION
    #define PROCESS_TRUST_LABEL_SECURITY_INFORMATION	0x00000080UL
    #endif
    
    #ifndef BACKUP_SECURITY_INFORMATION
    #define BACKUP_SECURITY_INFORMATION			0x00010000UL
    #endif
    
    #ifndef SYSTEM_MANDATORY_LABEL_ACE_TYPE
    #define SYSTEM_MANDATORY_LABEL_ACE_TYPE		0x11
    
    typedef	struct	_SYSTEM_MANDATORY_LABEL_ACE
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	DWORD		SidStart;
    } SYSTEM_MANDATORY_LABEL_ACE;
    #endif
    
    #ifndef SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE
    #define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE	0x12
    
    typedef	struct	_SYSTEM_RESOURCE_ATTRIBUTE_ACE
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	DWORD		SidStart;
    } SYSTEM_RESOURCE_ATTRIBUTE_ACE;
    #endif
    
    #ifndef SYSTEM_SCOPED_POLICY_ID_ACE_TYPE
    #define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE	0x13
    
    typedef	struct	_SYSTEM_SCOPED_POLICY_ID_ACE
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	DWORD		SidStart;
    } SYSTEM_SCOPED_POLICY_ID_ACE;
    #endif
    
    #ifndef SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE
    #define SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE	0x14
    
    typedef	struct	_SYSTEM_PROCESS_TRUST_LABEL_ACE
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	DWORD		SidStart;
    } SYSTEM_PROCESS_TRUST_LABEL_ACE;
    #endif
    
    #ifndef SYSTEM_ACCESS_FILTER_ACE_TYPE
    #define SYSTEM_ACCESS_FILTER_ACE_TYPE		0x15
    
    typedef	struct	_SYSTEM_ACCESS_FILTER_ACE
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	DWORD		SidStart;
    } SYSTEM_ACCESS_FILTER_ACE;
    #endif
    
    #define memcpy	__movsb
    #define wmemcpy	__movsw
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    __declspec(safebuffers)
    SID_NAME_USE	CheckSID(HANDLE hConsole, SID *sid, LPWSTR *lpStringSID)
    {
    	SID_NAME_USE	snu = 0;
    
    	DWORD	dwError = ERROR_SUCCESS;
    	WCHAR	szAccount[UNLEN + 1];
    	DWORD	dwAccount = sizeof(szAccount) / sizeof(*szAccount);
    	WCHAR	szDomain[GNLEN + 1];
    	DWORD	dwDomain = sizeof(szDomain) / sizeof(*szDomain);
    
    	if (!ConvertSidToStringSid(sid, lpStringSID))
    		PrintConsole(hConsole,
    		             L"ConvertSidToStringSid() returned error %lu\n",
    		             GetLastError());
    
    	if (!LookupAccountSid((LPCWSTR) NULL,
    	                      sid,
    	                      szAccount, &dwAccount,
    	                      szDomain, &dwDomain,
    	                      &snu))
    	{
    		dwError = GetLastError();
    
    		if (dwError != ERROR_NONE_MAPPED)
    			PrintConsole(hConsole,
    			             L"LookupAccountSid() returned error %lu for \'%ls\'\n",
    			             dwError, *lpStringSID);
    	}
    
    	return snu;
    }
    
    __declspec(safebuffers)
    DWORD	WINAPI	Security(HANDLE hConsole, WCHAR szPathName[])
    {
    	SECURITY_DESCRIPTOR	*lpSD;
    	SID	*lpOwner, *lpGroup, *lpTrustee;
    	ACL	*lpDACL, *lpSACL;
    	ACE_HEADER	*lpACE;
    	WORD	wACE;
    	LPWSTR	lpStringSID;
    	DWORD	dwError;
    
    	dwError = GetNamedSecurityInfo(szPathName,
    	                               SE_FILE_OBJECT,
    #if 1
    	                               OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
    #else
    	                               OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
    #endif
    	                               &lpOwner,
    	                               &lpGroup,
    	                               &lpDACL,
    	                               &lpSACL,
    	                               &lpSD);
    
    	if (dwError != ERROR_SUCCESS)
    		PrintConsole(hConsole,
    		             L"GetNamedSecurityInfo() returned error %lu for \'%ls\'\n",
    		             dwError, szPathName);
    	else
    		if (!IsValidSecurityDescriptor(lpSD))
    			PrintConsole(hConsole,
    			             L"IsValidSecurityDescriptor() returned FALSE for security descriptor of \'%ls\'\n",
    			             szPathName);
    		else
    		{
    			if (lpOwner == NULL)
    				PrintConsole(hConsole,
    				             L"No owner in security descriptor of \'%ls\'\n",
    				             szPathName);
    			else
    				if (!IsValidSid(lpOwner))
    					PrintConsole(hConsole,
    					             L"IsValidSid() returned FALSE for owner of \'%ls\'\n",
    					             szPathName);
    				else
    				{
    					if (!CheckSID(hConsole, lpOwner, &lpStringSID))
    						PrintConsole(hConsole,
    						             L"Unknown owner \'%ls\' in security descriptor of \'%ls\'\n",
    						             lpStringSID, szPathName);
    
    					if (LocalFree(lpStringSID) != NULL)
    						PrintConsole(hConsole,
    						             L"LocalFree() returned error %lu\n",
    						             GetLastError());
    				}
    
    			if (lpGroup == NULL)
    				PrintConsole(hConsole,
    				             L"No group in security descriptor of \'%ls\'\n",
    				             szPathName);
    			else
    				if (!IsValidSid(lpGroup))
    					PrintConsole(hConsole,
    					             L"IsValidSid() returned FALSE for group of \'%ls\'\n",
    					             szPathName);
    				else
    				{
    					if (!CheckSID(hConsole, lpGroup, &lpStringSID))
    						PrintConsole(hConsole,
    						             L"Unknown group \'%ls\' in security descriptor of \'%ls\'\n",
    						             lpStringSID, szPathName);
    
    					if (LocalFree(lpStringSID) != NULL)
    						PrintConsole(hConsole,
    						             L"LocalFree() returned error %lu\n",
    						             GetLastError());
    				}
    
    			if (lpDACL == NULL)
    				PrintConsole(hConsole,
    				             L"No DACL in security descriptor of \'%ls\'\n",
    				             szPathName);
    			else
    				if (!IsValidAcl(lpDACL))
    					PrintConsole(hConsole,
    					             L"IsValidAcl() returned FALSE for DACL of \'%ls\'\n",
    					             szPathName);
    				else
    					if (lpDACL->AceCount == 0)
    						PrintConsole(hConsole,
    						             L"Empty DACL in security descriptor of \'%ls\'\n",
    						             szPathName);
    					else
    						for (lpACE = (ACE_HEADER *) (lpDACL + 1),
    						     wACE = 0; wACE < lpDACL->AceCount; wACE++,
    						     lpACE = (ACE_HEADER *) ((BYTE *) lpACE + lpACE->AceSize))
    						{
    							switch (lpACE->AceType)
    							{
    							case ACCESS_ALLOWED_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((ACCESS_ALLOWED_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case ACCESS_DENIED_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((ACCESS_DENIED_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
    
    								continue;
    
    							case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    
    								if (((((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((ACCESS_ALLOWED_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case ACCESS_DENIED_OBJECT_ACE_TYPE:
    
    								if (((((ACCESS_DENIED_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((ACCESS_DENIED_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((ACCESS_DENIED_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((ACCESS_DENIED_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((ACCESS_DENIED_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((ACCESS_DENIED_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((ACCESS_ALLOWED_CALLBACK_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case ACCESS_DENIED_CALLBACK_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((ACCESS_DENIED_CALLBACK_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
    
    								if (((((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
    
    								if (((((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((ACCESS_DENIED_CALLBACK_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							default:
    								PrintConsole(hConsole,
    								             L"Unknown ACE type %u in DACL of \'%ls\'\n",
    								             lpACE->AceType, szPathName);
    								continue;
    							}
    
    							if (!IsValidSid(lpTrustee))
    								PrintConsole(hConsole,
    								             L"IsValidSid() returned FALSE for trustee in DACL of \'%ls\'\n",
    								             szPathName);
    							else
    							{
    								if (!CheckSID(hConsole, lpTrustee, &lpStringSID))
    									PrintConsole(hConsole,
    									             L"Unknown trustee \'%ls\' in DACL of \'%ls\'\n",
    									             lpStringSID, szPathName);
    
    								if (LocalFree(lpStringSID) != NULL)
    									PrintConsole(hConsole,
    									             L"LocalFree() returned error %lu\n",
    									             GetLastError());
    							}
    						}
    
    			if (lpSACL == NULL)
    				PrintConsole(hConsole,
    				             L"No SACL in security descriptor of \'%ls\'\n",
    				             szPathName);
    			else
    				if (!IsValidAcl(lpSACL))
    					PrintConsole(hConsole,
    					             L"IsValidAcl() returned FALSE for SACL of \'%ls\'\n",
    					             szPathName);
    				else
    					if (lpSACL->AceCount == 0)
    						PrintConsole(hConsole,
    						             L"Empty SACL in security descriptor of \'%ls\'\n",
    						             szPathName);
    					else
    						for (lpACE = (ACE_HEADER *) (lpSACL + 1),
    						     wACE = 0; wACE < lpSACL->AceCount; wACE++,
    						     lpACE = (ACE_HEADER *) ((BYTE *) lpACE + lpACE->AceSize))
    						{
    							switch (lpACE->AceType)
    							{
    							case SYSTEM_AUDIT_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_AUDIT_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_ALARM_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_ALARM_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    
    								if (((((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((SYSTEM_AUDIT_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case SYSTEM_ALARM_OBJECT_ACE_TYPE:
    
    								if (((((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((SYSTEM_ALARM_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_AUDIT_CALLBACK_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_ALARM_CALLBACK_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_ALARM_CALLBACK_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
    
    								if (((((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE:
    
    								if (((((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    								 == ((((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) == ACE_INHERITED_OBJECT_TYPE_PRESENT))
    									if ((((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->Flags & ACE_OBJECT_TYPE_PRESENT) == ACE_OBJECT_TYPE_PRESENT)
    										lpTrustee = (SID *) &(((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->SidStart);
    									else
    										lpTrustee = (SID *) &(((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->ObjectType);
    								else
    									lpTrustee = (SID *) &(((SYSTEM_ALARM_CALLBACK_OBJECT_ACE *) lpACE)->InheritedObjectType);
    								break;
    
    							case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_MANDATORY_LABEL_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_RESOURCE_ATTRIBUTE_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_SCOPED_POLICY_ID_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_PROCESS_TRUST_LABEL_ACE *) lpACE)->SidStart);
    
    								break;
    
    							case SYSTEM_ACCESS_FILTER_ACE_TYPE:
    
    								lpTrustee = (SID *) &(((SYSTEM_ACCESS_FILTER_ACE *) lpACE)->SidStart);
    
    								break;
    
    							default:
    								PrintConsole(hConsole,
    								             L"Unknown ACE type %u in SACL of \'%ls\'\n",
    								             lpACE->AceType, szPathName);
    								continue;
    							}
    
    							if (!IsValidSid(lpTrustee))
    								PrintConsole(hConsole,
    								             L"IsValidSid() returned FALSE for trustee in SACL of \'%ls\'\n",
    								             szPathName);
    							else
    							{
    								if (!CheckSID(hConsole, lpTrustee, &lpStringSID))
    									PrintConsole(hConsole,
    									             L"Unknown trustee \'%ls\' in SACL of \'%ls\'\n",
    									             lpStringSID, szPathName);
    
    								if (LocalFree(lpStringSID) != NULL)
    									PrintConsole(hConsole,
    									             L"LocalFree() returned error %lu\n",
    									             GetLastError());
    							}
    						}
    
    		if (LocalFree(lpSD) != NULL)
    			PrintConsole(hConsole,
    			             L"LocalFree() returned error %lu\n",
    			             GetLastError());
    	}
    
    	return dwError;
    }
    
    __declspec(safebuffers)
    DWORD	WINAPI	Traverse(HANDLE hConsole, WCHAR szPathName[32768])
    {
    	WIN32_FIND_DATA	wfd;
    
    	HANDLE	hPathName;
    	DWORD	dwPathName;
    	DWORD	dwError = Security(hConsole, szPathName);
    
    	dwPathName = wcslen(szPathName);
    #if 0
    	wcscat(szPathName, L"\\*");
    #elif 0
    	wmemcpy(szPathName + dwPathName, L"\\*", sizeof("\\*"));
    #elif 0
    	memcpy(szPathName + dwPathName, L"\\*", sizeof(L"\\*"));
    #else
    	szPathName[dwPathName + 0] = L'\\';
    	szPathName[dwPathName + 1] = L'*';
    	szPathName[dwPathName + 2] = L'\0';
    #endif
    	hPathName = FindFirstFile(szPathName, &wfd);
    
    	if (hPathName != INVALID_HANDLE_VALUE)
    	{
    		do
    		{
    #if 0
    			if ((wcscmp(wfd.cFileName, L".") == 0)
    			 || (wcscmp(wfd.cFileName, L"..") == 0))
    				continue;
    #elif 0
    			if ((wmemcmp(wfd.cFileName, L".", sizeof(".")) == 0)
    			 || (wmemcmp(wfd.cFileName, L"..", sizeof("..")) == 0))
    				continue;
    #elif 0
    			if ((memcmp(wfd.cFileName, L".", sizeof(L".")) == 0)
    			 || (memcmp(wfd.cFileName, L"..", sizeof(L"..")) == 0))
    				continue;
    #else
    			if ((wfd.cFileName[0] == L'.')
    			 && (wfd.cFileName[1] == L'\0'))
    				continue;
    
    			if ((wfd.cFileName[0] == L'.')
    			 && (wfd.cFileName[1] == L'.')
    			 && (wfd.cFileName[2] == L'\0'))
    				continue;
    #endif
    			wcscpy(szPathName + dwPathName + 1, wfd.cFileName);
    
    			if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
    				dwError = Security(hConsole, szPathName);
    			else if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
    				dwError = Traverse(hConsole, szPathName);
    		}
    		while (FindNextFile(hPathName, &wfd));
    
    		dwError = GetLastError();
    
    		if (dwError == ERROR_NO_MORE_FILES)
    			dwError = ERROR_SUCCESS;
    		else
    			PrintConsole(hConsole,
    			             L"FindNextFile() returned error %lu for path \'%ls\'\n",
    			             dwError, szPathName);
    
    		if (!FindClose(hPathName))
    			PrintConsole(hConsole,
    			             L"FindClose() returned error %lu for path \'%ls\'\n",
    			             GetLastError(), szPathName);
    	}
    	else
    	{
    		dwError = GetLastError();
    
    		if (dwError == ERROR_FILE_NOT_FOUND)
    			dwError = ERROR_SUCCESS;
    		else
    			PrintConsole(hConsole,
    			             L"FindFirstFile() returned error %lu for path \'%ls\'\n",
    			             dwError, szPathName);
    	}
    
    //	szPathName[dwPathName] = L'\0';
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	WIN32_FIND_DATA	wfd;
    
    	LPWSTR	*lpArguments;
    	INT	nArguments;
    	INT	nArgument = 1;
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwArgument;
    	WCHAR	szArgument[32768];
    	LPWSTR	lpArgument;
    	HANDLE	hArgument;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"No arguments: at least one directory or file name must be given!\n");
    			else
    				do
    				{
    					wcscpy(szArgument, lpArguments[nArgument]);
    
    					dwArgument = GetFileAttributes(szArgument);
    
    					if (dwArgument == INVALID_FILE_ATTRIBUTES)
    					{
    						hArgument = FindFirstFile(szArgument, &wfd);
    
    						if (hArgument == INVALID_HANDLE_VALUE)
    							PrintConsole(hConsole,
    							             L"FindFirstFile() returned error %lu for argument \'%ls\'\n",
    							             dwError = GetLastError(), szArgument);
    						else
    						{
    							dwArgument = 0;
    							lpArgument = NULL;
    
    							do
    								if (szArgument[dwArgument] == L'\\')
    									lpArgument = szArgument + dwArgument;
    							while (szArgument[dwArgument++] != L'\0');
    
    							if (dwArgument > MAX_PATH)
    								PrintConsole(hConsole,
    								             L"Argument \'%ls\' exceeds MAX_PATH!\n",
    								             szArgument);
    
    							if (lpArgument != NULL)
    								lpArgument++;
    							else
    								lpArgument = szArgument + 2 * (szArgument[1] == L':');
    
    							dwArgument = 0;
    
    							do
    							{
    #if 0
    								if ((wcscmp(wfd.cFileName, L".") == 0)
    								 || (wcscmp(wfd.cFileName, L"..") == 0))
    									continue;
    #elif 0
    								if ((wmemcmp(wfd.cFileName, L".", sizeof(".")) == 0)
    								 || (wmemcmp(wfd.cFileName, L"..", sizeof("..")) == 0))
    									continue;
    #elif 0
    								if ((memcmp(wfd.cFileName, L".", sizeof(L".")) == 0)
    								 || (memcmp(wfd.cFileName, L"..", sizeof(L"..")) == 0))
    									continue;
    #else
    								if ((wfd.cFileName[0] == L'.')
    								 && (wfd.cFileName[1] == L'\0'))
    									continue;
    
    								if ((wfd.cFileName[0] == L'.')
    								 && (wfd.cFileName[1] == L'.')
    								 && (wfd.cFileName[2] == L'\0'))
    									continue;
    #endif
    								dwArgument++;
    
    								wcscpy(lpArgument, wfd.cFileName);
    
    								if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
    									dwError = Security(hConsole, szArgument);
    								else
    									dwError = Traverse(hConsole, szArgument);
    							}
    							while (FindNextFile(hArgument, &wfd));
    
    							dwError = GetLastError();
    
    							if (dwError == ERROR_NO_MORE_FILES)
    								dwError = ERROR_SUCCESS;
    							else
    								PrintConsole(hConsole,
    								             L"FindNextFile() returned error %lu for argument \'%ls\'\n",
    								             dwError, lpArguments[nArgument]);
    
    							if (dwArgument == 0)
    								PrintConsole(hConsole,
    								             L"No match for argument \'%ls\'!\n",
    								             lpArguments[nArgument]);
    
    							if (!FindClose(hArgument))
    								PrintConsole(hConsole,
    								             L"FindClose() returned error %lu for argument \'%ls\'\n",
    								             GetLastError(), lpArguments[nArgument]);
    						}
    					}
    					else if ((dwArgument & FILE_ATTRIBUTE_DIRECTORY) == 0)
    						dwError = Security(hConsole, szArgument);
    					else
    						dwError = Traverse(hConsole, szArgument);
    				}
    				while (++nArgument < nArguments);
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
    Note: the console application Security Descriptor Inspector.com supports long pathnames with the \\?\ prefix.
  2. Run the following four command lines to compile the source file SECURITY.C created in step 1., link the compiled object file SECURITY.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gs69632 /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:ADVAPI32.LIB /DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Security Descriptor Inspector.com" /RELEASE /STACK:1048576,65536 /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE SECURITY.C
    ERASE SECURITY.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    SECURITY.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …
  3. Create the text file SECURITY.XML with the following content next to the console application Security Descriptor Inspector.com built in step 2.:

    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
    <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
        <assemblyIdentity name="eSKamation.Tidbits.Security Descriptor Inspector" processorArchitecture="*" type="win32" version="0.8.1.5" />
        <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
            <application xmlns="urn:schemas-microsoft-com:asm.v3">
                <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
                <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
                <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
                <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
                <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
                <windowsSettings>
                    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
                </windowsSettings>
            </application>
        </compatibility>
        <description>Security Descriptor Inspector</description>
        <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
            <security>
                <requestedPrivileges>
                    <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
                </requestedPrivileges>
            </security>
        </trustInfo>
    </assembly>
  4. Embed the application manifest SECURITY.XML created in step 3. in the console application Security Descriptor Inspector.com built in step 2.:

    MT.EXE /MANIFEST SECURITY.XML /OUTPUTRESOURCE:"Security Descriptor Inspector.com"
    Note: the Manifest Tool MT.exe is shipped with the Windows Software Development Kit.
    Microsoft (R) Manifest Tool version 6.1.7716.0
    Copyright (c) Microsoft Corporation 2009.
    All rights reserved.
    Note: on Windows 10 1607 alias Anniversary Update and newer versions of Windows NT, the console application Security Descriptor Inspector.com supports long pathnames when the following Registry. entry is present:
    REGEDIT4
    
    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
    "LongPathsEnabled"=dword:00000001
    Note: without this Registry entry and on older versions of Windows NT, the console application Security Descriptor Inspector.com supports long pathnames with the \\?\ prefix.

Really Known SIDs Enumerator

Purpose
Background Information
Implementation and Build Details
Source, Build Instructions and Output

Purpose

Enumerate (most of) the Security Identifiers really known on a machine, translate them to their (fully qualified) account name plus type and print these 3 pieces.

Background Information

The TechNet articles What are Security Identifiers?, How Security Identifiers Work and Security Identifiers Technical Overview provide an overview.

Implementation and Build Details

Really Known SIDs Enumerator is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows XP and newer versions of Windows NT as well as Windows PE 1.5 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source, Build Instructions and Output

Perform the following 3 simple steps to build the console application Really Known SIDs Enumerator from the source presented hereafter and execute it.
  1. Create the text file SIDEREAL.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <sddl.h>
    #include <lmcons.h>
    
    #ifndef SECURITY_LOCAL_LOGON_RID
    #define SECURITY_LOCAL_LOGON_RID				1UL
    #endif
    
    #ifndef SECURITY_CREATOR_OWNER_RIGHTS_RID
    #define SECURITY_CREATOR_OWNER_RIGHTS_RID			4UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS
    #define DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS	498UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_READONLY_CONTROLLERS
    #define DOMAIN_GROUP_RID_READONLY_CONTROLLERS			521UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS
    #define DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS			522UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_PROTECTED_USERS
    #define DOMAIN_GROUP_RID_PROTECTED_USERS			525UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_KEY_ADMINS
    #define DOMAIN_GROUP_RID_KEY_ADMINS				526UL
    #endif
    
    #ifndef DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS
    #define DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS			527UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_IUSERS
    #define DOMAIN_ALIAS_RID_IUSERS					568UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_CRYPTO_OPERATORS
    #define DOMAIN_ALIAS_RID_CRYPTO_OPERATORS			569UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP
    #define DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP		571UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP
    #define DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP		572UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP
    #define DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP		573UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP
    #define DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP		574UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS
    #define DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS		575UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS
    #define DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS			576UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_RDS_MANAGEMENT_SERVERS
    #define DOMAIN_ALIAS_RID_RDS_MANAGEMENT_SERVERS			577UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_HYPER_V_ADMINS
    #define DOMAIN_ALIAS_RID_HYPER_V_ADMINS				578UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_ACCESS_CONTROL_ASSISTANCE_OPS
    #define DOMAIN_ALIAS_RID_ACCESS_CONTROL_ASSISTANCE_OPS		579UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS
    #define DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS		580UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_DEFAULT_ACCOUNT
    #define DOMAIN_ALIAS_RID_DEFAULT_ACCOUNT			581UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_STORAGE_REPLICA_ADMINS
    #define DOMAIN_ALIAS_RID_STORAGE_REPLICA_ADMINS			582UL
    #endif
    
    #ifndef DOMAIN_ALIAS_RID_DEVICE_OWNERS
    #define DOMAIN_ALIAS_RID_DEVICE_OWNERS				583UL
    #endif
    
    #ifndef SECURITY_IUSER_RID
    #define SECURITY_IUSER_RID					17UL
    #endif
    
    #ifndef SECURITY_ENTERPRISE_READONLY_CONTROLLERS_RID
    #define SECURITY_ENTERPRISE_READONLY_CONTROLLERS_RID		22UL
    #endif
    
    #ifndef SECURITY_WRITE_RESTRICTED_CODE_RID
    #define SECURITY_WRITE_RESTRICTED_CODE_RID			33UL
    #endif
    
    #ifndef SECURITY_CRED_TYPE_BASE_RID
    #define SECURITY_CRED_TYPE_BASE_RID				65UL
    #endif
    
    #ifndef SECURITY_CRED_TYPE_THIS_ORG_CERT_RID
    #define SECURITY_CRED_TYPE_THIS_ORG_CERT_RID			1UL
    #endif
    
    #ifndef SECURITY_SERVICE_ID_BASE_RID
    #define SECURITY_SERVICE_ID_BASE_RID				80UL
    #endif
    
    #ifndef SECURITY_TRUSTED_INSTALLER_RID1
    #define SECURITY_TRUSTED_INSTALLER_RID1				956008885UL
    #define SECURITY_TRUSTED_INSTALLER_RID2				3418522649UL
    #define SECURITY_TRUSTED_INSTALLER_RID3				1831038044UL
    #define SECURITY_TRUSTED_INSTALLER_RID4				1853292631UL
    #define SECURITY_TRUSTED_INSTALLER_RID5				2271478464UL
    #endif
    
    #ifndef SECURITY_RESERVED_ID_BASE_RID
    #define SECURITY_RESERVED_ID_BASE_RID				81UL
    #endif
    
    #ifndef SECURITY_APPPOOL_ID_BASE_RID
    #define SECURITY_APPPOOL_ID_BASE_RID				82UL
    #endif
    
    #ifndef SECURITY_VIRTUALSERVER_ID_BASE_RID
    #define SECURITY_VIRTUALSERVER_ID_BASE_RID			83UL
    #endif
    
    #ifndef SECURITY_USERMODEDRIVERHOST_ID_BASE_RID
    #define SECURITY_USERMODEDRIVERHOST_ID_BASE_RID			84UL
    #endif
    
    #ifndef SECURITY_CLOUD_INFRASTRUCTURE_SERVICES_ID_BASE_RID
    #define SECURITY_CLOUD_INFRASTRUCTURE_SERVICES_ID_BASE_RID	85UL
    #endif
    
    #ifndef SECURITY_WMIHOST_ID_BASE_RID
    #define SECURITY_WMIHOST_ID_BASE_RID				86UL
    #endif
    
    #ifndef SECURITY_TASK_ID_BASE_RID
    #define SECURITY_TASK_ID_BASE_RID				87UL
    #endif
    
    #ifndef SECURITY_NFS_ID_BASE_RID
    #define SECURITY_NFS_ID_BASE_RID				88UL
    #endif
    
    #ifndef SECURITY_COM_ID_BASE_RID
    #define SECURITY_COM_ID_BASE_RID				89UL
    #endif
    
    #ifndef SECURITY_WINDOW_MANAGER_BASE_RID
    #define SECURITY_WINDOW_MANAGER_BASE_RID			90UL
    #endif
    
    #ifndef SECURITY_RDV_GFX_BASE_RID
    #define SECURITY_RDV_GFX_BASE_RID				91UL
    #endif
    
    #ifndef SECURITY_DASHOST_ID_BASE_RID
    #define SECURITY_DASHOST_ID_BASE_RID				92UL
    #endif
    
    #ifndef SECURITY_USERMANAGER_ID_BASE_RID
    #define SECURITY_USERMANAGER_ID_BASE_RID			93UL
    #endif
    
    #ifndef SECURITY_WINRM_ID_BASE_RID
    #define SECURITY_WINRM_ID_BASE_RID				94UL
    #endif
    
    #ifndef SECURITY_WINDOWSMOBILE_ID_BASE_RID
    #define SECURITY_WINDOWSMOBILE_ID_BASE_RID			112UL
    #endif
    
    #ifndef SECURITY_LOCAL_ACCOUNT_RID
    #define SECURITY_LOCAL_ACCOUNT_RID				113UL
    #endif
    
    #ifndef SECURITY_LOCAL_ACCOUNT_AND_ADMIN_RID
    #define SECURITY_LOCAL_ACCOUNT_AND_ADMIN_RID			114UL
    #endif
    
    #ifndef SECURITY_SITESERVER_AUTHORITY
    #define SECURITY_SITESERVER_AUTHORITY		{0, 0, 0, 0, 0, 6}
    #endif
    
    #ifndef SECURITY_INTERNETSITE_AUTHORITY
    #define SECURITY_INTERNETSITE_AUTHORITY		{0, 0, 0, 0, 0, 7}
    #endif
    
    #ifndef SECURITY_EXCHANGE_AUTHORITY
    #define SECURITY_EXCHANGE_AUTHORITY		{0, 0, 0, 0, 0, 8}
    #endif
    
    #ifndef SECURITY_PASSPORT_AUTHORITY
    #define SECURITY_PASSPORT_AUTHORITY		{0, 0, 0, 0, 0, 10}
    #endif
    
    #ifndef SECURITY_APP_PACKAGE_AUTHORITY
    #define SECURITY_APP_PACKAGE_AUTHORITY		{0, 0, 0, 0, 0, 15}
    #define SECURITY_APP_PACKAGE_BASE_RID				2UL
    #define SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE			1UL
    
    #define SECURITY_CAPABILITY_BASE_RID				3UL
    #define SECURITY_CAPABILITY_INTERNET_CLIENT			1UL
    #define SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER		2UL
    #define SECURITY_CAPABILITY_PRIVATE_NETWORK_CLIENT_SERVER	3UL
    #define SECURITY_CAPABILITY_PICTURES_LIBRARY			4UL
    #define SECURITY_CAPABILITY_VIDEOS_LIBRARY			5UL
    #define SECURITY_CAPABILITY_MUSIC_LIBRARY			6UL
    #define SECURITY_CAPABILITY_DOCUMENTS_LIBRARY			7UL
    #define SECURITY_CAPABILITY_ENTERPRISE_AUTHENTICATION		8UL
    #define SECURITY_CAPABILITY_SHARED_USER_CERTIFICATES		9UL
    #define SECURITY_CAPABILITY_REMOVABLE_STORAGE			10UL
    #define SECURITY_CAPABILITY_APPOINTMENTS			11UL
    #define SECURITY_CAPABILITY_CONTACTS				12UL
    
    #define SECURITY_CAPABILITY_APP_RID				1024UL
    
    #define SECURITY_CAPABILITY_INTERNET_EXPLORER			4096UL
    #endif
    
    #ifndef SECURITY_MANDATORY_LABEL_AUTHORITY
    #define SECURITY_MANDATORY_LABEL_AUTHORITY	{0, 0, 0, 0, 0, 16}
    #define SECURITY_MANDATORY_UNTRUSTED_RID			0UL
    #define SECURITY_MANDATORY_LOW_RID				4096UL
    #define SECURITY_MANDATORY_MEDIUM_RID				8192UL
    #define SECURITY_MANDATORY_MEDIUM_PLUS_RID			(SECURITY_MANDATORY_MEDIUM_RID + 256UL)
    #define SECURITY_MANDATORY_HIGH_RID				12288UL
    #define SECURITY_MANDATORY_SYSTEM_RID				16384UL
    #define SECURITY_MANDATORY_PROTECTED_PROCESS_RID		20480UL
    #endif
    
    #ifndef SECURITY_MANDATORY_SECURE_PROCESS_RID
    #define SECURITY_MANDATORY_SECURE_PROCESS_RID			28672UL
    #endif
    
    #ifndef SECURITY_SCOPED_POLICY_ID_AUTHORITY
    #define SECURITY_SCOPED_POLICY_ID_AUTHORITY	{0, 0, 0, 0, 0, 17}
    #endif
    
    #ifndef SECURITY_AUTHENTICATION_AUTHORITY
    #define SECURITY_AUTHENTICATION_AUTHORITY	{0, 0, 0, 0, 0, 18}
    #define SECURITY_AUTHENTICATION_AUTHORITY_ASSERTED_RID		1UL
    #define SECURITY_AUTHENTICATION_SERVICE_ASSERTED_RID		2UL
    #define SECURITY_AUTHENTICATION_FRESH_KEY_AUTH_RID		3UL
    #define SECURITY_AUTHENTICATION_KEY_TRUST_RID			4UL
    #define SECURITY_AUTHENTICATION_KEY_PROPERTY_MFA_RID		5UL
    #define SECURITY_AUTHENTICATION_KEY_PROPERTY_ATTESTATION_RID	6UL
    #endif
    
    #ifndef SECURITY_PROCESS_TRUST_AUTHORITY
    #define SECURITY_PROCESS_TRUST_AUTHORITY	{0, 0, 0, 0, 0, 19}
    #define SECURITY_PROCESS_PROTECTION_TYPE_NONE_RID		0UL
    #define SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID		512UL
    #define SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID		1024UL
    #define SECURITY_PROCESS_PROTECTION_LEVEL_NONE_RID		0UL
    #define SECURITY_PROCESS_PROTECTION_LEVEL_AUTHENTICODE_RID	1024UL
    #define SECURITY_PROCESS_PROTECTION_LEVEL_APP_RID		2048UL
    #define SECURITY_PROCESS_PROTECTION_LEVEL_WINDOWS_RID		4096UL
    #define SECURITY_PROCESS_PROTECTION_LEVEL_WINTCB_RID		8192UL
    #endif
    
    struct	_SID2
    {
    	BYTE				Revision;
    	BYTE				SubAuthorityCount;
    	SID_IDENTIFIER_AUTHORITY	IdentifierAuthority;
    	DWORD				SubAuthority[2];
    }
    const	sid2[] =
    {
    	// S-1-0 =
    	{SID_REVISION, 0, SECURITY_NULL_SID_AUTHORITY, {0, 0}},
    	// S-1-0-0 = 'NULL SID'
    	{SID_REVISION, 1, SECURITY_NULL_SID_AUTHORITY, {SECURITY_NULL_RID, 0}},
    
    	// S-1-1 =
    	{SID_REVISION, 0, SECURITY_WORLD_SID_AUTHORITY, {0, 0}},
    	// S-1-1-0 = 'Everyone'
    	{SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, {SECURITY_WORLD_RID, 0}},
    
    	// S-1-2 =
    	{SID_REVISION, 0, SECURITY_LOCAL_SID_AUTHORITY, {0, 0}},
    	// S-1-2-0 = 'LOCAL'
    	{SID_REVISION, 1, SECURITY_LOCAL_SID_AUTHORITY, {SECURITY_LOCAL_RID, 0}},
    	// S-1-2-1 = 'CONSOLE LOGON'
    	{SID_REVISION, 1, SECURITY_LOCAL_SID_AUTHORITY, {SECURITY_LOCAL_LOGON_RID, 0}},
    
    	// S-1-3 =
    	{SID_REVISION, 0, SECURITY_CREATOR_SID_AUTHORITY, {0, 0}},
    	// S-1-3-0 = 'CREATOR OWNER'
    	{SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_OWNER_RID, 0}},
    	// S-1-3-1 = 'CREATOR GROUP'
    	{SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_GROUP_RID, 0}},
    	// S-1-3-2 = 'CREATOR OWNER SERVER'
    	{SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_OWNER_SERVER_RID, 0}},
    	// S-1-3-3 = 'CREATOR GROUP SERVER'
    	{SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_GROUP_SERVER_RID, 0}},
    	// S-1-3-4 = 'OWNER RIGHTS'
    	{SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, {SECURITY_CREATOR_OWNER_RIGHTS_RID, 0}},
    
    	// S-1-4 =
    	{SID_REVISION, 0, SECURITY_NON_UNIQUE_AUTHORITY, {0, 0}},
    	// S-1-4-0 =
    	{SID_REVISION, 1, SECURITY_NON_UNIQUE_AUTHORITY, {0, 0}},
    
    	// S-1-5 = 'NT Pseudo Domain\NT Pseudo Domain'
    	{SID_REVISION, 0, SECURITY_NT_AUTHORITY, {0, 0}},
    	// S-1-5-1 = 'NT AUTHORITY\DIALUP'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_DIALUP_RID, 0}},
    	// S-1-5-2 = 'NT AUTHORITY\NETWORK'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_NETWORK_RID, 0}},
    	// S-1-5-3 = 'NT AUTHORITY\BATCH'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_BATCH_RID, 0}},
    	// S-1-5-4 = 'NT AUTHORITY\INTERACTIVE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_INTERACTIVE_RID, 0}},
    
    	// S-1-5-5 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_LOGON_IDS_RID, 0}},
    	// S-1-5-5-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_LOGON_IDS_RID, 0}},
    
    	// S-1-5-6 = 'NT AUTHORITY\SERVICE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_SERVICE_RID, 0}},
    	// S-1-5-7 = 'NT AUTHORITY\ANONYMOUS LOGON'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_ANONYMOUS_LOGON_RID, 0}},
    	// S-1-5-8 = 'NT AUTHORITY\PROXY'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_PROXY_RID, 0}},
    	// S-1-5-9 = 'NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_ENTERPRISE_CONTROLLERS_RID, 0}},
    	// S-1-5-10 = 'NT AUTHORITY\SELF'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_PRINCIPAL_SELF_RID, 0}},
    	// S-1-5-11 = 'NT AUTHORITY\Authenticated Users'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_AUTHENTICATED_USER_RID, 0}},
    	// S-1-5-12 = 'NT AUTHORITY\RESTRICTED'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_RESTRICTED_CODE_RID, 0}},
    	// S-1-5-13 = 'NT AUTHORITY\TERMINAL SERVER USER'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_TERMINAL_SERVER_RID, 0}},
    	// S-1-5-14 = 'NT AUTHORITY\REMOTE INTERACTIVE LOGON'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_REMOTE_LOGON_RID, 0}},
    	// S-1-5-15 = 'NT AUTHORITY\This Organization'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_THIS_ORGANIZATION_RID, 0}},
    	// S-1-5-16 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {16, 0}},
    	// S-1-5-17 = 'NT AUTHORITY\IUSR'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_IUSER_RID, 0}},
    	// S-1-5-18 = 'NT AUTHORITY\SYSTEM'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_LOCAL_SYSTEM_RID, 0}},
    	// S-1-5-19 = 'NT AUTHORITY\LOCAL SERVICE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_LOCAL_SERVICE_RID, 0}},
    	// S-1-5-20 = 'NT AUTHORITY\NETWORK SERVICE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_NETWORK_SERVICE_RID, 0}},
    
    	// S-1-5-21 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_NT_NON_UNIQUE, 0}},
    	// S-1-5-21-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_NT_NON_UNIQUE, 0}},
    	// S-1-5-21-1 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_NT_NON_UNIQUE, 1}},
    
    	// S-1-5-22 = 'NT AUTHORITY\ENTERPRISE READ-ONLY DOMAIN CONTROLLERS BETA'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_ENTERPRISE_READONLY_CONTROLLERS_RID, 0}},
    
    	// S-1-5-32 = 'BUILTIN\BUILTIN'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, 0}},
    	// S-1-5-32-498 = 'BUILTIN\Enterprise Read-Only Domain Controllers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS}},
    	// S-1-5-32-500 = 'BUILTIN\Administrator'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_USER_RID_ADMIN}},
    	// S-1-5-32-501 = 'BUILTIN\User'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_USER_RID_GUEST}},
    	// S-1-5-32-502 = 'BUILTIN\Kerberos Ticket Granting Ticket'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_USER_RID_KRBTGT}},
    	// S-1-5-32-512 = 'BUILTIN\Domain Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ADMINS}},
    	// S-1-5-32-513 = 'BUILTIN\Domain Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_USERS}},
    	// S-1-5-32-514 = 'BUILTIN\Domain Guests'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_GUESTS}},
    	// S-1-5-32-515 = 'BUILTIN\Domain Computers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_COMPUTERS}},
    	// S-1-5-32-516 = 'BUILTIN\Domain Controllers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_CONTROLLERS}},
    	// S-1-5-32-517 = 'BUILTIN\Certificate Server Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_CERT_ADMINS}},
    	// S-1-5-32-518 = 'BUILTIN\Schema Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_SCHEMA_ADMINS}},
    	// S-1-5-32-519 = 'BUILTIN\Enterprise Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS}},
    	// S-1-5-32-520 = 'BUILTIN\Group Policy Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_POLICY_ADMINS}},
    	// S-1-5-32-521 = 'BUILTIN\Read-Only Domain Controllers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_READONLY_CONTROLLERS}},
    	// S-1-5-32-522 = 'BUILTIN\Cloneable Controllers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_CLONEABLE_CONTROLLERS}},
    	// S-1-5-32-525 = 'BUILTIN\Protected Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_PROTECTED_USERS}},
    	// S-1-5-32-526 = 'BUILTIN\Key Admins'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_KEY_ADMINS}},
    	// S-1-5-32-527 = 'BUILTIN\Enterprise Key Admins'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ENTERPRISE_KEY_ADMINS}},
    	// S-1-5-32-544 = 'BUILTIN\Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS}},
    	// S-1-5-32-545 = 'BUILTIN\Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS}},
    	// S-1-5-32-546 = 'BUILTIN\Guests'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS}},
    	// S-1-5-32-547 = 'BUILTIN\Power Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS}},
    	// S-1-5-32-548 = 'BUILTIN\Account Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCOUNT_OPS}},
    	// S-1-5-32-549 = 'BUILTIN\System Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS}},
    	// S-1-5-32-550 = 'BUILTIN\Print Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PRINT_OPS}},
    	// S-1-5-32-551 = 'BUILTIN\Backup Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS}},
    	// S-1-5-32-552 = 'BUILTIN\Replicator'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REPLICATOR}},
    	// S-1-5-32-553 = 'BUILTIN\RAS Servers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RAS_SERVERS}},
    	// S-1-5-32-554 = 'BUILTIN\Pre-Windows 2000 Compatible Access'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS}},
    	// S-1-5-32-555 = 'BUILTIN\Remote Desktop Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS}},
    	// S-1-5-32-556 = 'BUILTIN\Network Configuration Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS}},
    	// S-1-5-32-557 = 'BUILTIN\Incoming Forest Trust Builders'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS}},
    	// S-1-5-32-558 = 'BUILTIN\Performance Monitor Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_MONITORING_USERS}},
    	// S-1-5-32-559 = 'BUILTIN\Performance Log Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_LOGGING_USERS}},
    	// S-1-5-32-560 = 'BUILTIN\Windows Authorization Access Group'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS}},
    	// S-1-5-32-561 = 'BUILTIN\Terminal Server License Servers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS}},
    	// S-1-5-32-562 = 'BUILTIN\Distributed COM Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_DCOM_USERS}},
    	// S-1-5-32-568 = 'BUILTIN\IIS IUSRS'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_IUSERS}},
    	// S-1-5-32-569 = 'BUILTIN\Cryptographic Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_CRYPTO_OPERATORS}},
    	// S-1-5-32-571 = 'BUILTIN\Allowed RODC Password Replication Group'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP}},
    	// S-1-5-32-572 = 'BUILTIN\Denied RODC Password Replication Group'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP}},
    	// S-1-5-32-573 = 'BUILTIN\Event Log Readers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP}},
    	// S-1-5-32-574 = 'BUILTIN\Certificate Service DCOM Access'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP}},
    	// S-1-5-32-575 = 'BUILTIN\RDS Remote Access Servers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_REMOTE_ACCESS_SERVERS}},
    	// S-1-5-32-576 = 'BUILTIN\RDS Endpoint Servers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_ENDPOINT_SERVERS}},
    	// S-1-5-32-577 = 'BUILTIN\RDS Management Servers'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RDS_MANAGEMENT_SERVERS}},
    	// S-1-5-32-578 = 'BUILTIN\Hyper-V Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_HYPER_V_ADMINS}},
    	// S-1-5-32-579 = 'BUILTIN\Access Control Assistance Operators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCESS_CONTROL_ASSISTANCE_OPS}},
    	// S-1-5-32-580 = 'BUILTIN\Remote Management Users'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_MANAGEMENT_USERS}},
    	// S-1-5-32-581 = 'BUILTIN\System Managed Accounts Group'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_DEFAULT_ACCOUNT}},
    	// S-1-5-32-582 = 'BUILTIN\Storage Replica Administrators'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_STORAGE_REPLICA_ADMINS}},
    	// S-1-5-32-583 = 'BUILTIN\Device Owners'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_DEVICE_OWNERS}},
    
    	// S-1-5-33 = 'NT AUTHORITY\WRITE RESTRICTED'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_WRITE_RESTRICTED_CODE_RID, 0}},
    
    	// S-1-5-64 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_PACKAGE_BASE_RID, 0}},
    	// S-1-5-64-10 = 'NT AUTHORITY\NTLM Authentication'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_NTLM_RID}},
    	// S-1-5-64-14 = 'NT AUTHORITY\SChannel Authentication'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_SCHANNEL_RID}},
    	// S-1-5-64-21 = 'NT AUTHORITY\Digest Authentication'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_DIGEST_RID}},
    
    	// S-1-5-65 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_CRED_TYPE_BASE_RID, 0}},
    	// S-1-5-65-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_CRED_TYPE_BASE_RID, 0}},
    	// S-1-5-65-1 = 'NT AUTHORITY\This Organization Certificate'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_CRED_TYPE_BASE_RID, SECURITY_CRED_TYPE_THIS_ORG_CERT_RID}},
    
    	// S-1-5-80 = 'NT SERVICE\NT SERVICE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_SERVICE_ID_BASE_RID, 0}},
    	// S-1-5-80-0 = 'NT SERVICE\ALL SERVICES'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_SERVICE_ID_BASE_RID, 0}},
    	// S-1-5-80-1 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_SERVICE_ID_BASE_RID, 1}},
    
    	// S-1-5-81 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_RESERVED_ID_BASE_RID, 0}},
    	// S-1-5-81-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_RESERVED_ID_BASE_RID, 0}},
    
    	// S-1-5-82 = 'IIS APPPOOL\IIS APPPOOL'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_APPPOOL_ID_BASE_RID, 0}},
    	// S-1-5-82-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_APPPOOL_ID_BASE_RID, 0}},
    
    	// S-1-5-83 = 'NT VIRTUAL MACHINE\NT VIRTUAL MACHINE'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_VIRTUALSERVER_ID_BASE_RID, 0}},
    	// S-1-5-83-0 = 'NT VIRTUAL MACHINE\Virtual Machines'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_VIRTUALSERVER_ID_BASE_RID, 0}},
    
    	// S-1-5-84 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_USERMODEDRIVERHOST_ID_BASE_RID, 0}},
    	// S-1-5-84-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_USERMODEDRIVERHOST_ID_BASE_RID, 0}},
    
    	// S-1-5-85 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_CLOUD_INFRASTRUCTURE_SERVICES_ID_BASE_RID, 0}},
    	// S-1-5-85-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_CLOUD_INFRASTRUCTURE_SERVICES_ID_BASE_RID, 0}},
    
    	// S-1-5-86 = 'WMI\WMI'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_WMIHOST_ID_BASE_RID, 0}},
    	// S-1-5-86-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_WMIHOST_ID_BASE_RID, 0}},
    
    	// S-1-5-87 = 'NT TASK\NT TASK'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_TASK_ID_BASE_RID, 0}},
    	// S-1-5-87-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_TASK_ID_BASE_RID, 0}},
    
    	// S-1-5-88 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_NFS_ID_BASE_RID, 0}},
    	// S-1-5-88-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_NFS_ID_BASE_RID, 0}},
    
    	// S-1-5-89 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_COM_ID_BASE_RID, 0}},
    	// S-1-5-89-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_COM_ID_BASE_RID, 0}},
    
    	// S-1-5-90 = 'Window Manager\Window Manager'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_WINDOW_MANAGER_BASE_RID, 0}},
    	// S-1-5-90-0 = 'Window Manager\Window Manager Group'
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_WINDOW_MANAGER_BASE_RID, 0}},
    
    	// S-1-5-91 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_RDV_GFX_BASE_RID, 0}},
    	// S-1-5-91-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_RDV_GFX_BASE_RID, 0}},
    
    	// S-1-5-92 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_DASHOST_ID_BASE_RID, 0}},
    	// S-1-5-92-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_DASHOST_ID_BASE_RID, 0}},
    
    	// S-1-5-93 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_USERMANAGER_ID_BASE_RID, 0}},
    	// S-1-5-93-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_USERMANAGER_ID_BASE_RID, 0}},
    
    	// S-1-5-94 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_WINRM_ID_BASE_RID, 0}},
    	// S-1-5-94-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_WINRM_ID_BASE_RID, 0}},
    
    	// S-1-5-95 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {95, 0}},
    	// S-1-5-95-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {95, 0}},
    
    	// S-1-5-96 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {96, 0}},
    	// S-1-5-96-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {96, 0}},
    
    	// S-1-5-112 =
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_WINDOWSMOBILE_ID_BASE_RID, 0}},
    	// S-1-5-112-0 =
    	{SID_REVISION, 2, SECURITY_NT_AUTHORITY, {SECURITY_WINDOWSMOBILE_ID_BASE_RID, 0}},
    
    	// S-1-5-113 = 'NT AUTHORITY\Local Account'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_LOCAL_ACCOUNT_RID, 0}},
    	// S-1-5-114 = 'NT AUTHORITY\Local Account and Member of Administrators Group'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_LOCAL_ACCOUNT_AND_ADMIN_RID, 0}},
    	// S-1-5-1000 = 'NT AUTHORITY\Other Organization'
    	{SID_REVISION, 1, SECURITY_NT_AUTHORITY, {SECURITY_OTHER_ORGANIZATION_RID, 0}},
    
    	// S-1-6 =
    	{SID_REVISION, 0, SECURITY_SITESERVER_AUTHORITY, {0, 0}},
    	// S-1-6-0 =
    	{SID_REVISION, 1, SECURITY_SITESERVER_AUTHORITY, {0, 0}},
    	// S-1-6-0-0 =
    	{SID_REVISION, 2, SECURITY_SITESERVER_AUTHORITY, {0, 0}},
    	// S-1-6-0-1 =
    	{SID_REVISION, 2, SECURITY_SITESERVER_AUTHORITY, {0, 1}},
    
    	// S-1-7 = 'Internet$\Internet$'
    	{SID_REVISION, 0, SECURITY_INTERNETSITE_AUTHORITY, {0, 0}},
    	// S-1-7-0 =
    	{SID_REVISION, 1, SECURITY_INTERNETSITE_AUTHORITY, {0, 0}},
    	// S-1-7-0-0 =
    	{SID_REVISION, 2, SECURITY_INTERNETSITE_AUTHORITY, {0, 0}},
    	// S-1-7-0-1 =
    	{SID_REVISION, 2, SECURITY_INTERNETSITE_AUTHORITY, {0, 1}},
    
    	// S-1-8 =
    	{SID_REVISION, 0, SECURITY_EXCHANGE_AUTHORITY, {0, 0}},
    	// S-1-8-0 =
    	{SID_REVISION, 1, SECURITY_EXCHANGE_AUTHORITY, {0, 0}},
    	// S-1-8-0-0 =
    	{SID_REVISION, 2, SECURITY_EXCHANGE_AUTHORITY, {0, 0}},
    	// S-1-8-0-1 =
    	{SID_REVISION, 2, SECURITY_EXCHANGE_AUTHORITY, {0, 1}},
    
    	// S-1-9 =
    	{SID_REVISION, 0, SECURITY_RESOURCE_MANAGER_AUTHORITY, {0, 0}},
    	// S-1-9-0 =
    	{SID_REVISION, 1, SECURITY_RESOURCE_MANAGER_AUTHORITY, {0, 0}},
    	// S-1-9-0-0 =
    	{SID_REVISION, 2, SECURITY_RESOURCE_MANAGER_AUTHORITY, {0, 0}},
    	// S-1-9-0-1 =
    	{SID_REVISION, 2, SECURITY_RESOURCE_MANAGER_AUTHORITY, {0, 1}},
    
    	// S-1-10 =
    	{SID_REVISION, 0, SECURITY_PASSPORT_AUTHORITY, {0, 0}},
    	// S-1-10-0 =
    	{SID_REVISION, 1, SECURITY_PASSPORT_AUTHORITY, {0, 0}},
    	// S-1-10-0-0 =
    	{SID_REVISION, 2, SECURITY_PASSPORT_AUTHORITY, {0, 0}},
    	// S-1-10-0-1 =
    	{SID_REVISION, 2, SECURITY_PASSPORT_AUTHORITY, {0, 1}},
    
    	// S-1-15 =
    	{SID_REVISION, 0, SECURITY_APP_PACKAGE_AUTHORITY, {0, 0}},
    	// S-1-15-0 =
    	{SID_REVISION, 1, SECURITY_APP_PACKAGE_AUTHORITY, {0, 0}},
    	// S-1-15-1 = 'APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES'
    	{SID_REVISION, 1, SECURITY_APP_PACKAGE_AUTHORITY, {1, 0}},
    	// S-1-15-2 =
    	{SID_REVISION, 1, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_APP_PACKAGE_BASE_RID, 0}},
    	// S-1-15-2-0 =
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_APP_PACKAGE_BASE_RID, 0}},
    	// S-1-15-2-1 =
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_APP_PACKAGE_BASE_RID, SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE}},
    	// S-1-15-3 =
    	{SID_REVISION, 1, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, 0}},
    	// S-1-15-3-0 =
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, 0}},
    	// S-1-15-3-1 = 'APPLICATION PACKAGE AUTHORITY\Your Internet connection'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_INTERNET_CLIENT}},
    	// S-1-15-3-2 = 'APPLICATION PACKAGE AUTHORITY\Your Internet connection, including incoming connections from the Internet'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER}},
    	// S-1-15-3-3 = 'APPLICATION PACKAGE AUTHORITY\Your home or work networks'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_PRIVATE_NETWORK_CLIENT_SERVER}},
    	// S-1-15-3-4 = 'APPLICATION PACKAGE AUTHORITY\Your pictures library'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_PICTURES_LIBRARY}},
    	// S-1-15-3-5 = 'APPLICATION PACKAGE AUTHORITY\Your videos library'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_VIDEOS_LIBRARY}},
    	// S-1-15-3-6 = 'APPLICATION PACKAGE AUTHORITY\Your music library'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_MUSIC_LIBRARY}},
    	// S-1-15-3-7 = 'APPLICATION PACKAGE AUTHORITY\Your documents library'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_DOCUMENTS_LIBRARY}},
    	// S-1-15-3-8 = 'APPLICATION PACKAGE AUTHORITY\Your Windows credentials'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_ENTERPRISE_AUTHENTICATION}},
    	// S-1-15-3-9 = 'APPLICATION PACKAGE AUTHORITY\Software and hardware certificates or a smart card'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_SHARED_USER_CERTIFICATES}},
    	// S-1-15-3-10 = 'APPLICATION PACKAGE AUTHORITY\Removable storage'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_REMOVABLE_STORAGE}},
    	// S-1-15-3-11 = 'APPLICATION PACKAGE AUTHORITY\Your Appointments'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_APPOINTMENTS}},
    	// S-1-15-3-12 = 'APPLICATION PACKAGE AUTHORITY\Your Contacts'
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_CONTACTS}},
    	// S-1-15-3-1024 =
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_APP_RID}},
    	// S-1-15-3-4096 =
    	{SID_REVISION, 2, SECURITY_APP_PACKAGE_AUTHORITY, {SECURITY_CAPABILITY_BASE_RID, SECURITY_CAPABILITY_INTERNET_EXPLORER}},
    
    	// S-1-16 = 'Mandatory Label\Mandatory Label'
    	{SID_REVISION, 0, SECURITY_MANDATORY_LABEL_AUTHORITY, {0, 0}},
    	// S-1-16-0 = 'Mandatory Label\Untrusted Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_UNTRUSTED_RID, 0}},
    	// S-1-16-4096 = 'Mandatory Label\Low Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_LOW_RID, 0}},
    	// S-1-16-8192 = 'Mandatory Label\Medium Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_MEDIUM_RID, 0}},
    	// S-1-16-8448 = 'Mandatory Label\Medium Plus Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_MEDIUM_PLUS_RID, 0}},
    	// S-1-16-12288 = 'Mandatory Label\High Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_HIGH_RID, 0}},
    	// S-1-16-16384 = 'Mandatory Label\System Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_SYSTEM_RID, 0}},
    	// S-1-16-20480 = 'Mandatory Label\Protected Process Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_PROTECTED_PROCESS_RID, 0}},
    	// S-1-16-24576 =
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {24576, 0}},
    	// S-1-16-28672 = 'Mandatory Label\Secure Process Mandatory Level'
    	{SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, {SECURITY_MANDATORY_SECURE_PROCESS_RID, 0}},
    
    	// S-1-17 =
    	{SID_REVISION, 0, SECURITY_SCOPED_POLICY_ID_AUTHORITY, {0, 0}},
    	// S-1-17-0 =
    	{SID_REVISION, 1, SECURITY_SCOPED_POLICY_ID_AUTHORITY, {0, 0}},
    
    	// S-1-18 =
    	{SID_REVISION, 0, SECURITY_AUTHENTICATION_AUTHORITY, {0, 0}},
    	// S-1-18-0 =
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {0, 0}},
    	// S-1-18-1 = 'Authentication Authority Asserted Identity'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_AUTHORITY_ASSERTED_RID, 0}},
    	// S-1-18-2 = 'Service Asserted Identity'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_SERVICE_ASSERTED_RID, 0}},
    	// S-1-18-3 = 'Fresh Public Key Identity'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_FRESH_KEY_AUTH_RID, 0}},
    	// S-1-18-4 = 'Key Trust Identity'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_KEY_TRUST_RID, 0}},
    	// S-1-18-5 = 'Key Property Multi-factor Authentication'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_KEY_PROPERTY_MFA_RID, 0}},
    	// S-1-18-6 = 'Key Property Attestation'
    	{SID_REVISION, 1, SECURITY_AUTHENTICATION_AUTHORITY, {SECURITY_AUTHENTICATION_KEY_PROPERTY_ATTESTATION_RID, 0}},
    
    	// S-1-19 =
    	{SID_REVISION, 0, SECURITY_PROCESS_TRUST_AUTHORITY, {0, 0}},
    	// S-1-19-0 =
    	{SID_REVISION, 1, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_NONE_RID, 0}},
    	// S-1-19-0-0 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_NONE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_NONE_RID}},
    	// S-1-19-512 =
    	{SID_REVISION, 1, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, 0}},
    	// S-1-19-512-0 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_NONE_RID}},
    	// S-1-19-512-1024 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_AUTHENTICODE_RID}},
    	// S-1-19-512-2048 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_APP_RID}},
    	// S-1-19-512-4096 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_WINDOWS_RID}},
    	// S-1-19-512-8192 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID, SECURITY_PROCESS_PROTECTION_LEVEL_WINTCB_RID}},
    	// S-1-19-1024 =
    	{SID_REVISION, 1, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, 0}},
    	// S-1-19-1024-0 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, SECURITY_PROCESS_PROTECTION_LEVEL_NONE_RID}},
    	// S-1-19-1024-1024 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, SECURITY_PROCESS_PROTECTION_LEVEL_AUTHENTICODE_RID}},
    	// S-1-19-1024-2048 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, SECURITY_PROCESS_PROTECTION_LEVEL_APP_RID}},
    	// S-1-19-1024-4096 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, SECURITY_PROCESS_PROTECTION_LEVEL_WINDOWS_RID}},
    	// S-1-19-1024-8192 =
    	{SID_REVISION, 2, SECURITY_PROCESS_TRUST_AUTHORITY, {SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID, SECURITY_PROCESS_PROTECTION_LEVEL_WINTCB_RID}}
    };
    
    struct	_SID6
    {
    	BYTE				Revision;
    	BYTE				SubAuthorityCount;
    	SID_IDENTIFIER_AUTHORITY	IdentifierAuthority;
    	DWORD				SubAuthority[6];
    }
    const	sid6[] =
    {
    	// S-1-5-21-0-0-0-496 =
    	{SID_REVISION, 5, SECURITY_NT_AUTHORITY, {SECURITY_NT_NON_UNIQUE, 0, 0, 0, 496, 0}},
    	// S-1-5-21-0-0-0-497 =
    	{SID_REVISION, 5, SECURITY_NT_AUTHORITY, {SECURITY_NT_NON_UNIQUE, 0, 0, 0, 497, 0}},
    
    	// S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464 = 'NT SERVICE\TrustedInstaller'
    	{SID_REVISION, 6, SECURITY_NT_AUTHORITY, {SECURITY_SERVICE_ID_BASE_RID, SECURITY_TRUSTED_INSTALLER_RID1, SECURITY_TRUSTED_INSTALLER_RID2, SECURITY_TRUSTED_INSTALLER_RID3, SECURITY_TRUSTED_INSTALLER_RID4, SECURITY_TRUSTED_INSTALLER_RID5}},
    	// S-1-5-84-0-0-0-0-0 = 'NT AUTHORITY\USER MODE DRIVERS'
    	{SID_REVISION, 6, SECURITY_NT_AUTHORITY, {SECURITY_USERMODEDRIVERHOST_ID_BASE_RID, 0, 0, 0, 0, 0}},
    	// S-1-5-86-615999462-62705297-2911207457-59056572-3668589837 = 'WMI\Network Service'
    	{SID_REVISION, 6, SECURITY_NT_AUTHORITY, {SECURITY_WMIHOST_ID_BASE_RID, 615999462, 62705297, 2911207457, 59056572, 3668589837}},
    	// S-1-5-86-1544737700-199408000-2549878335-3519669259-381336952 = 'WMI\Local Service'
    	{SID_REVISION, 6, SECURITY_NT_AUTHORITY, {SECURITY_WMIHOST_ID_BASE_RID, 1544737700, 199408000, 2549878335, 3519669259, 381336952}}
    };
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    const	LPCWSTR	szSNU[] = {NULL,
    		           L"user",
    		           L"group",
    		           L"domain",
    		           L"alias",
    		           L"well-known group",
    		           L"deleted account",
    		           L"invalid",
    		           L"unknown",
    		           L"computer",
    		           L"label",
    		           L"logon session"};
    
    __declspec(safebuffers)
    SID_NAME_USE	WINAPI	CheckSID(HANDLE hConsole, SID *sid)
    {
    	LPWSTR	lpStringSID;
    	DWORD	dwError = ERROR_SUCCESS;
    	WCHAR	szAccount[UNLEN + 1];
    	DWORD	dwAccount = sizeof(szAccount) / sizeof(*szAccount);
    	WCHAR	szDomain[GNLEN + 1];
    	DWORD	dwDomain = sizeof(szDomain) / sizeof(*szDomain);
    
    	SID_NAME_USE	snu = 0;
    
    	if (!ConvertSidToStringSid(sid, &lpStringSID))
    		PrintConsole(hConsole,
    		             L"ConvertSidToStringSid() returned error %lu\n",
    		             dwError = GetLastError());
    	else
    	{
    		if (!LookupAccountSid((LPCWSTR) NULL,
    		                      sid,
    		                      szAccount, &dwAccount,
    		                      szDomain, &dwDomain,
    		                      &snu))
    		{
    			dwError = GetLastError();
    
    			if (dwError != ERROR_NONE_MAPPED)
    				PrintConsole(hConsole,
    				             L"LookupAccountSid() returned error %lu for security identifier \'%ls\'\n",
    				             dwError, lpStringSID);
    		}
    		else
    			if (*szDomain == L'\0')
    				PrintConsole(hConsole,
    				             L"%ls: %ls \'%ls\'\n",
    				             lpStringSID, szSNU[snu], szAccount);
    			else if (*szAccount == L'\0')
    				PrintConsole(hConsole,
    				             L"%ls: %ls \'%ls\'\n",
    				             lpStringSID, szSNU[snu], szDomain);
    			else
    				PrintConsole(hConsole,
    				             L"%ls: %ls \'%ls\\%ls\'\n",
    				             lpStringSID, szSNU[snu], szDomain, szAccount);
    
    		if (LocalFree(lpStringSID) != NULL)
    			PrintConsole(hConsole,
    			             L"LocalFree() returned error %lu\n",
    			             GetLastError());
    	}
    
    //	SetLastError(dwError);
    
    	return snu;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	SID_NAME_USE	snu;
    
    	WCHAR	szAccount[UNLEN + 1];
    	DWORD	dwAccount = sizeof(szAccount) / sizeof(*szAccount);
    	WCHAR	szDomain[GNLEN + 1];
    	DWORD	dwDomain = sizeof(szDomain) / sizeof(*szDomain);
    	BYTE	sid[SECURITY_MAX_SID_SIZE];
    	BOOL	bSid;
    	DWORD	dwSid;
    	DWORD	dw;
    	DWORD	*lpRid;
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		for (dw = 0; dw < sizeof(sid2) / sizeof(*sid2); dw++)
    			CheckSID(hConsole, (SID *) &sid2[dw]);
    
    		for (dw = 0; dw < sizeof(sid6) / sizeof(*sid6); dw++)
    			CheckSID(hConsole, (SID *) &sid6[dw]);
    
    		for (dw = 0; dw < 128; dw++)
    		{
    			dwSid = sizeof(sid);
    
    			if (!CreateWellKnownSid((WELL_KNOWN_SID_TYPE) dw,
    			                        (SID *) NULL,
    			                        (SID *) sid,
    			                        &dwSid))
    			{
    				dwError = GetLastError();
    
    				if (dwError != ERROR_INVALID_PARAMETER)
    					PrintConsole(hConsole,
    					             L"CreateWellKnownSid() returned error %lu\n",
    					             dwError);
    			}
    			else
    			{
    				bSid = FALSE;
    
    				if (dwSid <= sizeof(*sid2))
    				{
    					for (dwSid = 0; dwSid < sizeof(sid2) / sizeof(*sid2); dwSid++)
    						if (bSid = EqualSid((SID *) sid, (SID *) &sid2[dwSid]))
    							break;
    				}
    				else if (dwSid == sizeof(*sid6))
    				{
    					for (dwSid = 0; dwSid < sizeof(sid6) / sizeof(*sid6); dwSid++)
    						if (bSid = EqualSid((SID *) sid, (SID *) &sid6[dwSid]))
    							break;
    				}
    				else
    					continue;
    
    				if (!bSid)
    					CheckSID(hConsole, (SID *) sid);
    			}
    		}
    
    		if (!GetComputerName(szAccount, &dwAccount))
    			PrintConsole(hConsole,
    			             L"GetComputerName() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			dwSid = sizeof(sid);
    
    			if (!LookupAccountName((LPCWSTR) NULL,
    			                       szAccount,
    			                       (SID *) sid, &dwSid,
    			                       szDomain, &dwDomain,
    			                       &snu))
    				PrintConsole(hConsole,
    				             L"LookupAccountName() returned error %lu for \'%ls\'\n",
    				             dwError = GetLastError(), szAccount);
    			else
    			{
    				CheckSID(hConsole, (SID *) sid);
    
    				lpRid = GetSidSubAuthority((SID *) sid, GetSidSubAuthorityCount((SID *) sid)[0]++);
    
    				for (lpRid[0] = FOREST_USER_RID_MAX - 1; lpRid[0] <= DOMAIN_USER_RID_MAX + 1; lpRid[0]++)
    					CheckSID(hConsole, (SID *) sid);
    			}
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file SIDEREAL.C created in step 1., link the compiled object file SIDEREAL.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:ADVAPI32.LIB /DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.1 /OUT:"Really Known SIDs Enumerator.com" /RELEASE /SUBSYSTEM:CONSOLE,5.1 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE SIDEREAL.C
    ERASE SIDEREAL.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    SIDEREAL.C
    SIDEREAL.C(957) : warning C4706: assignment within conditional expression
    SIDEREAL.C(963) : warning C4706: assignment within conditional expression
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …
  3. Finally execute the console application SIDEREAL.COM built in step 2.:

    VER
    .\SIDEREAL.COM
    Microsoft Windows [Version 10.0.19044]
    
    S-1-0-0: well-known group 'NULL SID'
    S-1-1-0: well-known group 'Everyone'
    S-1-2-0: well-known group 'LOCAL'
    S-1-2-1: well-known group 'CONSOLE LOGON'
    S-1-3-0: well-known group 'CREATOR OWNER'
    S-1-3-1: well-known group 'CREATOR GROUP'
    S-1-3-2: well-known group 'CREATOR OWNER SERVER'
    S-1-3-3: well-known group 'CREATOR GROUP SERVER'
    S-1-3-4: well-known group 'OWNER RIGHTS'
    S-1-5: domain 'NT Pseudo Domain\NT Pseudo Domain'
    S-1-5-1: well-known group 'NT AUTHORITY\DIALUP'
    S-1-5-2: well-known group 'NT AUTHORITY\NETWORK'
    S-1-5-3: well-known group 'NT AUTHORITY\BATCH'
    S-1-5-4: well-known group 'NT AUTHORITY\INTERACTIVE'
    S-1-5-6: well-known group 'NT AUTHORITY\SERVICE'
    S-1-5-7: well-known group 'NT AUTHORITY\ANONYMOUS LOGON'
    S-1-5-8: well-known group 'NT AUTHORITY\PROXY'
    S-1-5-9: well-known group 'NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS'
    S-1-5-10: well-known group 'NT AUTHORITY\SELF'
    S-1-5-11: well-known group 'NT AUTHORITY\Authenticated Users'
    S-1-5-12: well-known group 'NT AUTHORITY\RESTRICTED'
    S-1-5-13: well-known group 'NT AUTHORITY\TERMINAL SERVER USER'
    S-1-5-14: well-known group 'NT AUTHORITY\REMOTE INTERACTIVE LOGON'
    S-1-5-15: well-known group 'NT AUTHORITY\This Organization'
    S-1-5-17: well-known group 'NT AUTHORITY\IUSR'
    S-1-5-18: well-known group 'NT AUTHORITY\SYSTEM'
    S-1-5-19: well-known group 'NT AUTHORITY\LOCAL SERVICE'
    S-1-5-20: well-known group 'NT AUTHORITY\NETWORK SERVICE'
    S-1-5-22: well-known group 'NT AUTHORITY\ENTERPRISE READ-ONLY DOMAIN CONTROLLERS BETA'
    S-1-5-32: domain 'BUILTIN\BUILTIN'
    S-1-5-32-544: alias 'BUILTIN\Administrators'
    S-1-5-32-545: alias 'BUILTIN\Users'
    S-1-5-32-546: alias 'BUILTIN\Guests'
    S-1-5-32-547: alias 'BUILTIN\Power Users'
    S-1-5-32-551: alias 'BUILTIN\Backup Operators'
    S-1-5-32-552: alias 'BUILTIN\Replicator'
    S-1-5-32-555: alias 'BUILTIN\Remote Desktop Users'
    S-1-5-32-556: alias 'BUILTIN\Network Configuration Operators'
    S-1-5-32-558: alias 'BUILTIN\Performance Monitor Users'
    S-1-5-32-559: alias 'BUILTIN\Performance Log Users'
    S-1-5-32-562: alias 'BUILTIN\Distributed COM Users'
    S-1-5-32-568: alias 'BUILTIN\IIS_IUSRS'
    S-1-5-32-569: alias 'BUILTIN\Cryptographic Operators'
    S-1-5-32-573: alias 'BUILTIN\Event Log Readers'
    S-1-5-32-578: alias 'BUILTIN\Hyper-V Administrators'
    S-1-5-32-579: alias 'BUILTIN\Access Control Assistance Operators'
    S-1-5-32-580: alias 'BUILTIN\Remote Management Users'
    S-1-5-32-581: alias 'BUILTIN\System Managed Accounts Group'
    S-1-5-32-583: alias 'BUILTIN\Device Owners'
    S-1-5-33: well-known group 'NT AUTHORITY\WRITE RESTRICTED'
    S-1-5-64-10: well-known group 'NT AUTHORITY\NTLM Authentication'
    S-1-5-64-14: well-known group 'NT AUTHORITY\SChannel Authentication'
    S-1-5-64-21: well-known group 'NT AUTHORITY\Digest Authentication'
    S-1-5-65-1: well-known group 'NT AUTHORITY\This Organization Certificate'
    S-1-5-80: domain 'NT SERVICE\NT SERVICE'
    S-1-5-80-0: well-known group 'NT SERVICE\ALL SERVICES'
    S-1-5-87: domain 'NT TASK\NT TASK'
    S-1-5-90: domain 'Window Manager\Window Manager'
    S-1-5-90-0: well-known group 'Window Manager\Window Manager Group'
    S-1-5-96: domain 'Font Driver Host\Font Driver Host'
    S-1-5-113: well-known group 'NT AUTHORITY\Local account'
    S-1-5-114: well-known group 'NT AUTHORITY\Local account and member of Administrators group'
    S-1-5-1000: well-known group 'NT AUTHORITY\Other Organization'
    S-1-7: domain 'Internet$\Internet$'
    S-1-15-2-1: well-known group 'APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES'
    S-1-15-3-1: well-known group 'APPLICATION PACKAGE AUTHORITY\Your Internet connection'
    S-1-15-3-2: well-known group 'APPLICATION PACKAGE AUTHORITY\Your Internet connection, including incoming connections from the Internet'
    S-1-15-3-3: well-known group 'APPLICATION PACKAGE AUTHORITY\Your home or work networks'
    S-1-15-3-4: well-known group 'APPLICATION PACKAGE AUTHORITY\Your pictures library'
    S-1-15-3-5: well-known group 'APPLICATION PACKAGE AUTHORITY\Your videos library'
    S-1-15-3-6: well-known group 'APPLICATION PACKAGE AUTHORITY\Your music library'
    S-1-15-3-7: well-known group 'APPLICATION PACKAGE AUTHORITY\Your documents library'
    S-1-15-3-8: well-known group 'APPLICATION PACKAGE AUTHORITY\Your Windows credentials'
    S-1-15-3-9: well-known group 'APPLICATION PACKAGE AUTHORITY\Software and hardware certificates or a smart card'
    S-1-15-3-10: well-known group 'APPLICATION PACKAGE AUTHORITY\Removable storage'
    S-1-15-3-11: well-known group 'APPLICATION PACKAGE AUTHORITY\Your Appointments'
    S-1-15-3-12: well-known group 'APPLICATION PACKAGE AUTHORITY\Your Contacts'
    S-1-16: domain 'Mandatory Label\Mandatory Label'
    S-1-16-0: label 'Mandatory Label\Untrusted Mandatory Level'
    S-1-16-4096: label 'Mandatory Label\Low Mandatory Level'
    S-1-16-8192: label 'Mandatory Label\Medium Mandatory Level'
    S-1-16-8448: label 'Mandatory Label\Medium Plus Mandatory Level'
    S-1-16-12288: label 'Mandatory Label\High Mandatory Level'
    S-1-16-16384: label 'Mandatory Label\System Mandatory Level'
    S-1-16-20480: label 'Mandatory Label\Protected Process Mandatory Level'
    S-1-18-1: well-known group 'Authentication authority asserted identity'
    S-1-18-2: well-known group 'Service asserted identity'
    S-1-18-3: well-known group 'Fresh public key identity'
    S-1-18-4: well-known group 'Key trust identity'
    S-1-18-5: well-known group 'Key property multi-factor authentication'
    S-1-18-6: well-known group 'Key property attestation'
    S-1-5-21-0-0-0-496: well-known group 'NT AUTHORITY\Compound Identity Present'
    S-1-5-21-0-0-0-497: well-known group 'NT AUTHORITY\Claims Valid'
    S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464: well-known group 'NT SERVICE\TrustedInstaller'
    S-1-5-84-0-0-0-0-0: well-known group 'NT AUTHORITY\USER MODE DRIVERS'
    S-1-5-21-820728443-44925810-1835867902: domain 'AMNESIAC'
    S-1-5-21-820728443-44925810-1835867902-500: user 'AMNESIAC\Administrator'
    S-1-5-21-820728443-44925810-1835867902-501: user 'AMNESIAC\Guest'
    S-1-5-21-820728443-44925810-1835867902-503: user 'AMNESIAC\DefaultAccount'
    S-1-5-21-820728443-44925810-1835867902-504: user 'AMNESIAC\WDAGUtilityAccount'
    S-1-5-21-820728443-44925810-1835867902-513: group 'AMNESIAC\None'
    S-1-5-21-820728443-44925810-1835867902-1000: user 'AMNESIAC\Stefan'

Privilege Twiddler

Purpose
Implementation and Build Details
Source and Build Instructions

Purpose

Enable, disable or remove privileges of the calling process.

Implementation and Build Details

Privilege Twiddler is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: due to the design and implementation of Windows’ (classic alias legacy) console, the Win32 function WriteConsole() can only write to a console, not to a file nor a pipe, i.e. redirection of standard error or standard output is not supported!

The MSDN article Console Handles provides background information.

Source and Build Instructions

Perform the following 2 simple steps to build the console application Privilege Twiddler from the source presented hereafter.
  1. Create the text file TWIDDLER.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <tlhelp32.h>
    #include <shellapi.h>
    
    #define SE_PRIVILEGE_DISABLED	0UL
    
    #define SE_MIN_WELL_KNOWN_PRIVILEGE			2UL
    #define SE_CREATE_TOKEN_PRIVILEGE			2UL	// "SeCreateTokenPrivilege"
    #define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE			3UL	// "SeAssignPrimaryTokenPrivilege"
    #define SE_LOCK_MEMORY_PRIVILEGE			4UL	// "SeLockMemoryPrivilege"
    #define SE_INCREASE_QUOTA_PRIVILEGE			5UL	// "SeIncreaseQuotaPrivilege"
    //      SE_UNSOLICITED_INPUT_PRIVILEGE			6UL	// "SeUnsolicitedInputPrivilege"
    #define SE_MACHINE_ACCOUNT_PRIVILEGE			6UL	// "SeMachineAccountPrivilege"
    #define SE_TCB_PRIVILEGE				7UL	// "SeTcbPrivilege"
    #define SE_SECURITY_PRIVILEGE				8UL	// "SeSecurityPrivilege"
    #define SE_TAKE_OWNERSHIP_PRIVILEGE			9UL	// "SeTakeOwnershipPrivilege"
    #define SE_LOAD_DRIVER_PRIVILEGE			10UL	// "SeLoadDriverPrivilege"
    #define SE_SYSTEM_PROFILE_PRIVILEGE			11UL	// "SeSystemProfilePrivilege"
    #define SE_SYSTEMTIME_PRIVILEGE				12UL	// "SeSystemtimePrivilege"
    #define SE_PROF_SINGLE_PROCESS_PRIVILEGE		13UL	// "SeProfileSingleProcessPrivilege"
    #define SE_INC_BASE_PRIORITY_PRIVILEGE			14UL	// "SeIncreaseBasePriorityPrivilege"
    #define SE_CREATE_PAGEFILE_PRIVILEGE			15UL	// "SeCreatePagefilePrivilege"
    #define SE_CREATE_PERMANENT_PRIVILEGE			16UL	// "SeCreatePermanentPrivilege"
    #define SE_BACKUP_PRIVILEGE				17UL	// "SeBackupPrivilege"
    #define SE_RESTORE_PRIVILEGE				18UL	// "SeRestorePrivilege"
    #define SE_SHUTDOWN_PRIVILEGE				19UL	// "SeShutdownPrivilege"
    #define SE_DEBUG_PRIVILEGE				20UL	// "SeDebugPrivilege"
    #define SE_AUDIT_PRIVILEGE				21UL	// "SeAuditPrivilege"
    #define SE_SYSTEM_ENVIRONMENT_PRIVILEGE			22UL	// "SeSystemEnvironmentPrivilege"
    #define SE_CHANGE_NOTIFY_PRIVILEGE			23UL	// "SeChangeNotifyPrivilege"
    #define SE_REMOTE_SHUTDOWN_PRIVILEGE			24UL	// "SeRemoteShutdownPrivilege"
    #define SE_UNDOCK_PRIVILEGE				25UL	// "SeUndockPrivilege"
    #define SE_SYNC_AGENT_PRIVILEGE				26UL	// "SeSyncAgentPrivilege"
    #define SE_ENABLE_DELEGATION_PRIVILEGE			27UL	// "SeEnableDelegationPrivilege"
    #define SE_MANAGE_VOLUME_PRIVILEGE			28UL	// "SeManageVolumePrivilege"
    #define SE_IMPERSONATE_PRIVILEGE			29UL	// "SeImpersonatePrivilege"
    #define SE_CREATE_GLOBAL_PRIVILEGE			30UL	// "SeCreateGlobalPrivilege"
    #define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE		31UL	// "SeTrustedCredManAccessPrivilege"
    #define SE_RELABEL_PRIVILEGE				32UL	// "SeRelabelPrivilege"
    #define SE_INCREASE_WORKING_SET_PRIVILEGE		33UL	// "SeIncreaseWorkingSetPrivilege"
    #define SE_TIME_ZONE_PRIVILEGE				34UL	// "SeTimeZonePrivilege"
    #define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE		35UL	// "SeCreateSymbolicLinkPrivilege"
    #define SE_DELEGATE_SESSION_USER_IMPERSONATE_PRIVILEGE	36UL	// "SeDelegateSessionUserImpersonatePrivilege"
    #define SE_MAX_WELL_KNOWN_PRIVILEGE			36UL
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	INT	nArgument = 1;
    	INT	nArguments;
    	LPWSTR	*lpArguments;
    	LPCWSTR	lpPrivilege;
    	WCHAR	szPrivilege[sizeof("SeDelegateSessionUserImpersonatePrivilege")];
    	DWORD	dwPrivilege;
    	DWORD	dwCurrentProcessId = GetCurrentProcessId();
    	DWORD	dwParentProcessId = 0;
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwTP;
    
    	TOKEN_PRIVILEGES *lpTP;
    
    	PROCESSENTRY32	pe /* = {sizeof(pe)} */;
    
    	HANDLE	hSnapshot;
    	HANDLE	hParent;
    	HANDLE	hToken;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"At least one privilege to enable, disable or remove must be given by its name!\n");
    			else
    			{
    				dwTP = sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES) * (nArguments - 1 - ANYSIZE_ARRAY);
    				lpTP = (TOKEN_PRIVILEGES *) LocalAlloc(LPTR, dwTP);
    
    				if (lpTP == NULL)
    					PrintConsole(hConsole,
    					             L"LocalAlloc() returned error %lu\n",
    					             dwError = GetLastError());
    				else
    				{
    					lpTP->PrivilegeCount = nArguments - 1;
    
    					do
    					{
    						lpPrivilege = NULL;
    
    						if (wcslen(lpArguments[nArgument]) > sizeof("/DISABLE:Se*Privilege"))
    							if (memcmp(lpArguments[nArgument], L"/DISABLE:", sizeof(L"/DISABLE:") - sizeof(L"")) == 0)
    							{
    								lpPrivilege = lpArguments[nArgument] + sizeof("/DISABLE");
    							//	lpTP->Privileges[nArgument - 1].Attributes = SE_PRIVILEGE_DISABLED;
    							}
    							else if (memcmp(lpArguments[nArgument], L"/ENABLE:", sizeof(L"/ENABLE:") - sizeof(L"")) == 0)
    							{
    								lpPrivilege = lpArguments[nArgument] + sizeof("/ENABLE");
    								lpTP->Privileges[nArgument - 1].Attributes = SE_PRIVILEGE_ENABLED;
    							}
    							else if (memcmp(lpArguments[nArgument], L"/REMOVE:", sizeof(L"/REMOVE:") - sizeof(L"")) == 0)
    							{
    								lpPrivilege = lpArguments[nArgument] + sizeof("/REMOVE");
    								lpTP->Privileges[nArgument - 1].Attributes = SE_PRIVILEGE_REMOVED;
    							}
    
    						if (lpPrivilege == NULL)
    						{
    							PrintConsole(hConsole,
    							             L"Invalid argument \'%ls\'!\n",
    							             lpArguments[nArgument]);
    
    							lpTP->PrivilegeCount = 0;
    						}
    						else
    							if (!LookupPrivilegeValue((LPCWSTR) NULL,
    							                          lpPrivilege,
    							                          &lpTP->Privileges[nArgument - 1].Luid))
    							{
    								PrintConsole(hConsole,
    								             L"LookupPrivilegeValue() returned error %lu for \'%ls\'\n",
    								             dwError = GetLastError(), lpPrivilege);
    
    								lpTP->PrivilegeCount = 0;
    							}
    					} while (++nArgument < nArguments);
    
    					if (lpTP->PrivilegeCount != 0)
    					{
    						hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    
    						if (hSnapshot == INVALID_HANDLE_VALUE)
    							PrintConsole(hConsole,
    							             L"CreateToolhelp32Snapshot() returned error %lu\n",
    							             dwError = GetLastError());
    						else
    						{
    							pe.dwSize = sizeof(pe);
    
    							if (!Process32First(hSnapshot, &pe))
    								PrintConsole(hConsole,
    								             L"Process32First() returned error %lu\n",
    								             dwError = GetLastError());
    							else
    							{
    								do
    									if (pe.th32ProcessID == dwCurrentProcessId)
    										dwParentProcessId = pe.th32ParentProcessID;
    								while (Process32Next(hSnapshot, &pe));
    
    								dwError = GetLastError();
    
    								if (dwError == ERROR_NO_MORE_FILES)
    									dwError = ERROR_SUCCESS;
    								else
    									PrintConsole(hConsole,
    									             L"Process32Next() returned error %lu\n",
    									             dwError);
    							}
    
    							if (!CloseHandle(hSnapshot))
    								PrintConsole(hConsole,
    								             L"CloseHandle() returned error %lu\n",
    								             GetLastError());
    
    							if (dwParentProcessId == 0)
    							{
    								PrintConsole(hConsole,
    								             L"Parent process of process %lu not found!\n",
    								             dwCurrentProcessId);
    
    								dwError = ERROR_PROCESS_ABORTED;
    							}
    							else
    							{
    								hParent = OpenProcess(PROCESS_QUERY_INFORMATION,
    								                      FALSE,
    								                      dwParentProcessId);
    
    								if (hParent == NULL)
    									PrintConsole(hConsole,
    									             L"OpenProcess() returned error %lu\n",
    									             dwError = GetLastError());
    								else
    								{
    									if (!OpenProcessToken(hParent,
    									                      TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    									                      &hToken))
    										PrintConsole(hConsole,
    										             L"OpenProcessToken() returned error %lu\n",
    										             dwError = GetLastError());
    									else
    									{
    										if (!AdjustTokenPrivileges(hToken,
    										                           FALSE,
    										                           lpTP,
    										                           dwTP,
    										                           lpTP,
    										                           &dwTP))
    											PrintConsole(hConsole,
    											             L"AdjustTokenPrivileges() returned error %lu\n",
    											             dwError = GetLastError());
    										else
    										{
    											dwError = GetLastError();
    
    											if (lpTP->PrivilegeCount == 0)
    												if (dwError == ERROR_NOT_ALL_ASSIGNED)
    													PrintConsole(hConsole,
    													             L"Not all privileges assigned, no privilege toggled in parent process %lu\n",
    													             dwParentProcessId);
    												else
    													PrintConsole(hConsole,
    													             L"No privilege toggled in parent process %lu\n",
    													             dwParentProcessId);
    											else
    											{
    												if (dwError == ERROR_NOT_ALL_ASSIGNED)
    													PrintConsole(hConsole,
    													             L"Not all privileges assigned, %lu privilege(s) toggled in parent process %lu:\n",
    													             lpTP->PrivilegeCount, dwParentProcessId);
    												else
    													PrintConsole(hConsole,
    													             L"%lu privilege(s) toggled in parent process %lu:\n",
    													             lpTP->PrivilegeCount, dwParentProcessId);
    
    												for (dwTP = 0; dwTP < lpTP->PrivilegeCount; dwTP++)
    												{
    													dwPrivilege = sizeof(szPrivilege) / sizeof(*szPrivilege);
    
    													if (!LookupPrivilegeName((LPCWSTR) NULL,
    													                         &lpTP->Privileges[dwTP].Luid,
    													                         szPrivilege,
    													                         &dwPrivilege))
    														PrintConsole(hConsole,
    														             L"LookupPrivilegeName() returned error %lu\n",
    														             dwError = GetLastError());
    													else
    														PrintConsole(hConsole,
    														             L"\'%ls\'%lc",
    														             szPrivilege, dwTP != lpTP->PrivilegeCount ? L' ' : L'\n');
    												}
    											}
    										}
    
    										if (!CloseHandle(hToken))
    											PrintConsole(hConsole,
    											             L"CloseHandle() returned error %lu\n",
    											             GetLastError());
    									}
    
    									if (!CloseHandle(hParent))
    										PrintConsole(hConsole,
    										             L"CloseHandle() returned error %lu\n",
    										             GetLastError());
    								}
    							}
    						}
    					}
    
    					if (LocalFree(lpTP) != NULL)
    						PrintConsole(hConsole,
    						             L"LocalFree() returned error %lu\n",
    						             GetLastError());
    				}
    			}
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file TWIDDLER.C created in step 1., link the compiled object file TWIDDLER.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:ADVAPI32.LIB /DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Privilege Twiddler.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE TWIDDLER.C
    ERASE TWIDDLER.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    TWIDDLER.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

UU Encoder

Purpose
Background Information
Implementation and Build Details
Source and Build Instructions

Purpose

UU encode (binary) files for transmission via electronic mail.

Background Information

The MSDN article UUENCODE Attachment Format documents the message format.

Implementation and Build Details

UU Encoder is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: UU Encoder is a so-called filter, it reads from standard input, writes to standard output and prints error messages on standard error.

Source and Build Instructions

Perform the following 2 simple steps to build the console application UU Encoder from the source presented hereafter.
  1. Create the text file UUENCODE.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    #define memcpy	__movsb
    
    __declspec(safebuffers)
    BOOL	PrintFormat(HANDLE hFile, LPCSTR lpFormat, ...)
    {
    	CHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwFile;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	dwBuffer *= sizeof(*szBuffer);
    
    	if (!WriteFile(hFile, szBuffer, dwBuffer, &dwFile, (LPOVERLAPPED) NULL))
    		return FALSE;
    
    	return dwFile == dwBuffer;
    }
    
    const	CHAR	szU2U[64] = "`!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
    
    __declspec(noreturn)
    VOID	WINAPI	mainCRTStartup(VOID)
    {
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwCount;
    	DWORD	dwInOut;
    	DWORD	dwInput;
    	BYTE	cbInput[45];
    	BYTE	cbOutput[sizeof(cbInput) / 3 * 4 + sizeof("\r\n")];
    	DWORD	dwOutput;
    	HANDLE	hOutput;
    	HANDLE	hInput;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hError == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    		if (hOutput == INVALID_HANDLE_VALUE)
    			PrintFormat(hError,
    			            "GetStdHandle(%ld) returned error %lu\r\n",
    			            STD_OUTPUT_HANDLE, dwError = GetLastError());
    		else
    		{
    			hInput = GetStdHandle(STD_INPUT_HANDLE);
    
    			if (hInput == INVALID_HANDLE_VALUE)
    				PrintFormat(hError,
    				            "GetStdHandle(%ld) returned error %lu\r\n",
    				            STD_INPUT_HANDLE, dwError = GetLastError());
    			else
    			{
    				memcpy(cbOutput, "\r\nbegin 644 -\r\n", dwOutput = sizeof("\r\nbegin 644 -\r\n") - 1);
    
    				for (;;)
    				{
    					if (!WriteFile(hOutput, cbOutput, dwOutput, &dwCount, (LPOVERLAPPED) NULL))
    						PrintFormat(hError,
    						            "WriteFile() returned error %lu\r\n",
    						            dwError = GetLastError());
    					else if (dwCount != dwOutput)
    						PrintFormat(hError,
    						            "WriteFile() failed, %lu of %lu characters written\r\n",
    						            dwCount, dwOutput, dwError = ERROR_WRITE_FAULT);
    					else
    						if (!ReadFile(hInput, cbInput, sizeof(cbInput), &dwInput, (LPOVERLAPPED) NULL)
    						 && (GetLastError() != ERROR_BROKEN_PIPE))
    							PrintFormat(hError,
    							            "ReadFile() returned error %lu\r\n",
    							            dwError = GetLastError());
    						else if (dwInput == 0)
    						{
    							memcpy(cbOutput, "`\r\nend\r\n", dwOutput = sizeof("`\r\nend\r\n") - 1);
    
    							if (!WriteFile(hOutput, cbOutput, dwOutput, &dwCount, (LPOVERLAPPED) NULL))
    								PrintFormat(hError,
    								            "WriteFile() returned error %lu\r\n",
    								            dwError = GetLastError());
    							else if (dwCount != dwOutput)
    								PrintFormat(hError,
    								            "WriteFile() failed, %lu of %lu characters written\r\n",
    								            dwCount, dwOutput, dwError = ERROR_WRITE_FAULT);
    						//	else
    						//		dwError = ERROR_SUCCESS;
    						}
    						else
    						{
    							dwOutput = 0;
    #if 0
    							cbOutput[dwOutput++] = szU2U[dwInput];
    #else
    							cbOutput[dwOutput++] = (BYTE) (' ' + dwInput);
    #endif
    #if 0
    							while (dwInput % 3 != 0)
    								cbInput[dwInput++] = '\0';
    #else
    							switch (dwInput % 3)
    							{
    							case 1:
    								cbInput[dwInput++] = '\0';
    							case 2:
    								cbInput[dwInput++] = '\0';
    							}
    #endif
    							dwCount = dwInput;
    							dwInput = 0;
    
    							do
    							{
    #if 0
    								dwInOut = cbInput[dwInput++];
    								dwInOut <<= 8;
    								dwInOut |= cbInput[dwInput++];
    								dwInOut <<= 8;
    								dwInOut |= cbInput[dwInput++];
    
    								cbOutput[dwOutput++] = szU2U[(dwInOut >> 18) & 63];
    								cbOutput[dwOutput++] = szU2U[(dwInOut >> 12) & 63];
    								cbOutput[dwOutput++] = szU2U[(dwInOut >> 6) & 63];
    								cbOutput[dwOutput++] = szU2U[dwInOut & 63];
    #else
    								dwInOut = _byteswap_ulong(*(DWORD *) (cbInput + dwInput));
    								dwInput += 3;
    
    								*(DWORD *) (cbOutput + dwOutput) = szU2U[dwInOut >> 26]
    								                                 | szU2U[(dwInOut >> 20) & 63] * 256
    								                                 | szU2U[(dwInOut >> 14) & 63] * 65536
    								                                 | szU2U[(dwInOut >> 8) & 63] * 16777216;
    								dwOutput += 4;
    #endif
    							}
    							while (dwInput < dwCount);
    
    							cbOutput[dwOutput++] = '\r';
    							cbOutput[dwOutput++] = '\n';
    							continue;
    						}
    
    					break;
    				}
    
    				if (!CloseHandle(hInput))
    					PrintFormat(hError,
    					            "CloseHandle() returned error %lu\r\n",
    					            GetLastError());
    			}
    
    			if (!CloseHandle(hOutput))
    				PrintFormat(hError,
    				            "CloseHandle() returned error %lu\r\n",
    				            GetLastError());
    		}
    
    		if (!CloseHandle(hError))
    			PrintFormat(hError,
    			            "CloseHandle() returned error %lu\r\n",
    			            GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file UUENCODE.C created in step 1., link the compiled object file UUENCODE.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:mainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"UU Encoder.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE UUENCODE.C
    ERASE UUENCODE.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    UUENCODE.C
    UUENCODE.C(51) : warning C4295: 'szU2U' : array is too small to include a terminating null character
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Base64 Encoder

Purpose
Background Information
Implementation and Build Details
Source and Build Instructions

Purpose

Encode (binary) files for transmission via electronic mail.

Background Information

Base64 encoding is specified in RFC 3548 4648 titled The Base16, Base32, and Base64 Data Encodings.

MIME is specified in RFC 1341 RFCs 1521 and 1522 2045, 2046, 2047, 2048 and 2049, all titled Multipurpose Internet Mail Extensions (MIME).

Implementation and Build Details

Base64 Encoder is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Note: Base64 Encoder is a so-called filter, it reads from standard input, writes to standard output and prints error messages on standard error.

Source and Build Instructions

Perform the following 2 simple steps to build the console application Base64 Encoder from the source presented hereafter.
  1. Create the text file 64ENCODE.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(safebuffers)
    BOOL	PrintFormat(HANDLE hFile, LPCSTR lpFormat, ...)
    {
    	CHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwFile;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	dwBuffer *= sizeof(*szBuffer);
    
    	if (!WriteFile(hFile, szBuffer, dwBuffer, &dwFile, (LPOVERLAPPED) NULL))
    		return FALSE;
    
    	return dwFile == dwBuffer;
    }
    
    const	CHAR	szBase64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    		               "abcdefghijklmnopqrstuvwxyz"
    		               "0123456789+/";
    
    __declspec(noreturn)
    VOID	WINAPI	mainCRTStartup(VOID)
    {
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwCount;
    	DWORD	dwInOut;
    	DWORD	dwInput;
    	BYTE	cbInput[57];
    	BYTE	cbOutput[sizeof(cbInput) / 3 * 4 + sizeof("\r\n") - 1];
    	DWORD	dwOutput;
    	HANDLE	hOutput;
    	HANDLE	hInput;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hError == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    
    		if (hOutput == INVALID_HANDLE_VALUE)
    			PrintFormat(hError,
    			            "GetStdHandle(%s) returned error %lu\r\n",
    			            "STD_OUTPUT_HANDLE", dwError = GetLastError());
    		else
    		{
    			hInput = GetStdHandle(STD_INPUT_HANDLE);
    
    			if (hInput == INVALID_HANDLE_VALUE)
    				PrintFormat(hError,
    				            "GetStdHandle(%s) returned error %lu\r\n",
    				            "STD_INPUT_HANDLE", dwError = GetLastError());
    			else
    			{
    				for (;;)
    				{
    					if (!ReadFile(hInput, cbInput, sizeof(cbInput), &dwInput, (LPOVERLAPPED) NULL)
    					 && (GetLastError() != ERROR_BROKEN_PIPE))
    						PrintFormat(hError,
    						            "ReadFile() returned error %lu\r\n",
    						            dwError = GetLastError());
    					else if (dwInput != 0)
    					{
    						if (dwInput < sizeof(cbInput))
    							cbInput[dwInput] = '\0';
    
    						dwCount = dwInput;
    						dwInput = 0;
    						dwOutput = 0;
    
    						do
    						{
    							dwInOut = _byteswap_ulong(*(DWORD *) (cbInput + dwInput));
    							dwInput += 3;
    
    							cbOutput[dwOutput++] = szBase64[dwInOut >> 26];
    							cbOutput[dwOutput++] = szBase64[(dwInOut >> 20) & 63];
    							cbOutput[dwOutput++] = szBase64[(dwInOut >> 14) & 63];
    							cbOutput[dwOutput++] = szBase64[(dwInOut >> 8) & 63];
    						}
    						while (dwInput < dwCount);
    
    						for (dwInOut = dwOutput; dwInput > dwCount; dwCount++)
    							cbOutput[--dwInOut] = '=';
    
    						cbOutput[dwOutput++] = '\r';
    						cbOutput[dwOutput++] = '\n';
    
    						if (!WriteFile(hOutput, cbOutput, dwOutput, &dwCount, (LPOVERLAPPED) NULL))
    							PrintFormat(hError,
    							            "WriteFile() returned error %lu\r\n",
    							            dwError = GetLastError());
    						else if (dwCount != dwOutput)
    							PrintFormat(hError,
    							            "WriteFile() failed, %lu of %lu characters written\r\n",
    							            dwCount, dwOutput, dwError = ERROR_WRITE_FAULT);
    						else
    							continue;
    					}
    				//	else
    				//		dwError = ERROR_SUCCESS;
    					break;
    				}
    
    				if (!CloseHandle(hInput))
    					PrintFormat(hError,
    					            "CloseHandle() returned error %lu\r\n",
    					            GetLastError());
    			}
    
    			if (!CloseHandle(hOutput))
    				PrintFormat(hError,
    				            "CloseHandle() returned error %lu\r\n",
    				            GetLastError());
    		}
    
    		if (!CloseHandle(hError))
    			PrintFormat(hError,
    			            "CloseHandle() returned error %lu\r\n",
    			            GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file 64ENCODE.C created in step 1., link the compiled object file 64ENCODE.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:mainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"Base64 Encoder.com" /RELEASE /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE 64ENCODE.C
    ERASE 64ENCODE.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    64ENCODE.C
    64ENCODE.C(51) : warning C4295: 'szBase64' : array is too small to include a terminating null character
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

8dot3 File and Directory Name Changer

Purpose
Background Information
Implementation and Build Details
Source and Build Instructions

Purpose

Remove superfluous long names of files and directories on VFAT filesystems and superfluous short (8.3) names of files and directories on NTFS filesystems, i.e. long respectively short (8.3) names which differ only in case from their corresponding short (8.3) or long name.

Background Information

Since Windows is insensitive to the case of file and directory names, long names which differ only in case from their corresponding short (8.3) name can generally be renamed to that short (8.3) name and the superfluous long or short (8.3) name thereby (implicitly) removed.

The TechNet article The FAT File System documents the format of directory entries and the layout of directories: while a (mandatory) short (8.3) name occupies just a single directory entry, a(n optional) long name occupies at least 1 and up to 14 additional directory entries.

The TechNet article The NTFS File System documents that short (8.3) names are needed only for legacy DOS applications.

Note: 64-bit editions of Windows NT don’t support DOS applications, thus short (8.3) names can be removed completely there.

For additional information see the MSKB articles How to Disable the 8.3 Name Creation on NTFS Partitions, How Windows Generates 8.3 File Names from Long File Names and Short (8.3) File Names May Change When Copied.

Implementation and Build Details

8dot3 File and Directory Name Changer is a pure Win32 console application, written in ANSI C, built with the Platform SDK for Windows Server 2003 R2 Microsoft Visual C++ Compiler 2010 SP1 from update 2519277, but without the MSVCRT libraries, for use on Windows 2000 and newer versions of Windows NT as well as Windows PE 1.0 and newer versions.

Source and Build Instructions

Perform the following 2 simple steps to build the console application 8dot3 File and Directory Name Changer from the source presented hereafter.
  1. Create the text file 8DOT3FIX.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2022, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    // * The software is provided "as is" without any warranty, neither express
    //   nor implied.
    // * In no event will the author be held liable for any damage(s) arising
    //   from the use of the software.
    // * Redistribution of the software is allowed only in unmodified form.
    // * Permission is granted to use the software solely for personal private
    //   and non-commercial purposes.
    // * An individuals use of the software in his or her capacity or function
    //   as an agent, (independent) contractor, employee, member or officer of
    //   a business, corporation or organization (commercial or non-commercial)
    //   does not qualify as personal private and non-commercial purpose.
    // * Without written approval from the author the software must not be used
    //   for a business, for commercial, corporate, governmental, military or
    //   organizational purposes of any kind, or in a commercial, corporate,
    //   governmental, military or organizational environment of any kind.
    
    #define _CRT_SECURE_NO_WARNINGS
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    
    #define memcpy	__movsb
    #define wmemcpy	__movsw
    
    __declspec(safebuffers)
    BOOL	PrintConsole(HANDLE hConsole, LPCWSTR lpFormat, ...)
    {
    	WCHAR	szBuffer[1025];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    
    	va_list	vaInserts;
    	va_start(vaInserts, lpFormat);
    
    	dwBuffer = wvsprintf(szBuffer, lpFormat, vaInserts);
    
    	va_end(vaInserts);
    
    	if (dwBuffer == 0)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwBuffer;
    }
    
    __declspec(safebuffers)
    DWORD	WINAPI	Traverse(HANDLE hConsole, WCHAR szPathName[32768], WCHAR sz8Dot3Name[32768])
    {
    	WIN32_FIND_DATA	wfd;
    
    	DWORD	dwError;
    	DWORD	dwPathName = wcslen(szPathName);
    	HANDLE	hPathName;
    #if 0
    	wcscat(szPathName, L"\\*");
    #elif 0
    	wmemcpy(szPathName + dwPathName, L"\\*", sizeof("\\*"));
    #elif 0
    	memcpy(szPathName + dwPathName, L"\\*", sizeof(L"\\*"));
    #else
    	szPathName[dwPathName + 0] = L'\\';
    	szPathName[dwPathName + 1] = L'*';
    	szPathName[dwPathName + 2] = L'\0';
    #endif
    	hPathName = FindFirstFile(szPathName, &wfd);
    
    	if (hPathName != INVALID_HANDLE_VALUE)
    	{
    		wmemcpy(sz8Dot3Name, szPathName, dwPathName + 1);
    
    		do
    		{
    #if 0
    			if ((wcscmp(wfd.cFileName, L".") == 0)
    			 || (wcscmp(wfd.cFileName, L"..") == 0))
    				continue;
    #elif 0
    			if ((wmemcmp(wfd.cFileName, L".", sizeof(".")) == 0)
    			 || (wmemcmp(wfd.cFileName, L"..", sizeof("..")) == 0))
    				continue;
    #elif 0
    			if ((memcmp(wfd.cFileName, L".", sizeof(L".")) == 0)
    			 || (memcmp(wfd.cFileName, L"..", sizeof(L"..")) == 0))
    				continue;
    #else
    			if ((wfd.cFileName[0] == L'.')
    			 && (wfd.cFileName[1] == L'\0'))
    				continue;
    
    			if ((wfd.cFileName[0] == L'.')
    			 && (wfd.cFileName[1] == L'.')
    			 && (wfd.cFileName[2] == L'\0'))
    				continue;
    #endif
    			wcscpy(szPathName + dwPathName + 1, wfd.cFileName);
    
    			if ((wcscmp(wfd.cFileName, wfd.cAlternateFileName) != 0)
    			 && (lstrcmpi(wfd.cFileName, wfd.cAlternateFileName) == 0))
    			{
    				wcscpy(sz8Dot3Name + dwPathName + 1, wfd.cAlternateFileName);
    
    				if (!MoveFile(szPathName, sz8Dot3Name))
    					PrintConsole(hConsole,
    					             L"MoveFile(\"%ls\", \"%ls\") returned error %lu\n",
    					             szPathName, sz8Dot3Name, dwError = GetLastError());
    			}
    
    			if (((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    			 && ((wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0))
    				dwError = Traverse(hConsole, szPathName, sz8Dot3Name);
    		}
    		while (FindNextFile(hPathName, &wfd));
    
    		dwError = GetLastError();
    
    		if (dwError == ERROR_NO_MORE_FILES)
    			dwError = ERROR_SUCCESS;
    		else
    			PrintConsole(hConsole,
    			             L"FindNextFile() returned error %lu for path \'%ls\'\n",
    			             dwError, szPathName);
    
    		if (!FindClose(hPathName))
    			PrintConsole(hConsole,
    			             L"FindClose() returned error %lu for path \'%ls\'\n",
    			             GetLastError(), szPathName);
    	}
    	else
    	{
    		dwError = GetLastError();
    
    		if (dwError == ERROR_FILE_NOT_FOUND)
    			dwError = ERROR_SUCCESS;
    		else
    			PrintConsole(hConsole,
    			             L"FindFirstFile() returned error %lu for path \'%ls\'\n",
    			             dwError, szPathName);
    	}
    
    //	szPathName[dwPathName] = L'\0';
    
    	return dwError;
    }
    
    __declspec(noreturn)
    VOID	WINAPI	wmainCRTStartup(VOID)
    {
    	WIN32_FIND_DATA	wfd;
    
    	LPWSTR	*lpArguments;
    	INT	nArguments;
    	INT	nArgument = 1;
    	WCHAR	sz8Dot3Name[32768];
    	DWORD	dwError = ERROR_BAD_ARGUMENTS;
    	DWORD	dwArgument;
    	WCHAR	szArgument[32768];
    	LPWSTR	lpArgument;
    	HANDLE	hArgument;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		lpArguments = CommandLineToArgvW(GetCommandLine(), &nArguments);
    
    		if (lpArguments == NULL)
    			PrintConsole(hConsole,
    			             L"CommandLineToArgv() returned error %lu\n",
    			             dwError = GetLastError());
    		else
    		{
    			if (nArguments < 2)
    				PrintConsole(hConsole,
    				             L"No arguments: at least one (wildcard) directory or file name must be given!\n");
    			else
    				do
    				{
    					wcscpy(szArgument, lpArguments[nArgument]);
    
    					hArgument = FindFirstFile(szArgument, &wfd);
    
    					if (hArgument == INVALID_HANDLE_VALUE)
    						PrintConsole(hConsole,
    						             L"FindFirstFile() returned error %lu for argument \'%ls\'\n",
    						             dwError = GetLastError(), szArgument);
    					else
    					{
    						wcscpy(sz8Dot3Name, szArgument);
    
    						dwArgument = 0;
    						lpArgument = NULL;
    
    						do
    							if (szArgument[dwArgument] == L'\\')
    								lpArgument = szArgument + dwArgument;
    						while (szArgument[dwArgument++] != L'\0');
    
    						if (dwArgument > MAX_PATH)
    							PrintConsole(hConsole,
    							             L"Argument \'%ls\' exceeds MAX_PATH!\n",
    							             szArgument);
    
    						if (lpArgument != NULL)
    							lpArgument++;
    						else
    							lpArgument = szArgument + 2 * (szArgument[1] == L':');
    
    						dwArgument = 0;
    
    						do
    						{
    #if 0
    							if ((wcscmp(wfd.cFileName, L".") == 0)
    							 || (wcscmp(wfd.cFileName, L"..") == 0))
    								continue;
    #elif 0
    							if ((wmemcmp(wfd.cFileName, L".", sizeof(".")) == 0)
    							 || (wmemcmp(wfd.cFileName, L"..", sizeof("..")) == 0))
    								continue;
    #elif 0
    							if ((memcmp(wfd.cFileName, L".", sizeof(L".")) == 0)
    							 || (memcmp(wfd.cFileName, L"..", sizeof(L"..")) == 0))
    								continue;
    #else
    							if ((wfd.cFileName[0] == L'.')
    							 && (wfd.cFileName[1] == L'\0'))
    								continue;
    
    							if ((wfd.cFileName[0] == L'.')
    							 && (wfd.cFileName[1] == L'.')
    							 && (wfd.cFileName[2] == L'\0'))
    								continue;
    #endif
    							dwArgument++;
    
    							wcscpy(lpArgument, wfd.cFileName);
    
    							if ((wcscmp(wfd.cFileName, wfd.cAlternateFileName) != 0)
    							 && (lstrcmpi(wfd.cFileName, wfd.cAlternateFileName) == 0))
    							{
    								wcscpy(lpArgument - szArgument + sz8Dot3Name, wfd.cAlternateFileName);
    
    								if (!MoveFile(szArgument, sz8Dot3Name))
    									PrintConsole(hConsole,
    									             L"MoveFile(\"%ls\", \"%ls\") returned error %lu\n",
    									             szArgument, sz8Dot3Name, dwError = GetLastError());
    							}
    
    							if (((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    							 && ((wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0))
    								dwError = Traverse(hConsole, szArgument, sz8Dot3Name);
    						}
    						while (FindNextFile(hArgument, &wfd));
    
    						dwError = GetLastError();
    
    						if (dwError == ERROR_NO_MORE_FILES)
    							dwError = ERROR_SUCCESS;
    						else
    							PrintConsole(hConsole,
    							             L"FindNextFile() returned error %lu for argument \'%ls\'\n",
    							             dwError, lpArguments[nArgument]);
    
    						if (dwArgument == 0)
    							PrintConsole(hConsole,
    							             L"No match for argument \'%ls\'!\n",
    							             lpArguments[nArgument]);
    
    						if (!FindClose(hArgument))
    							PrintConsole(hConsole,
    							             L"FindClose() returned error %lu for argument \'%ls\'\n",
    							             GetLastError(), lpArguments[nArgument]);
    					}
    				}
    				while (++nArgument < nArguments);
    
    			if (LocalFree(lpArguments) != NULL)
    				PrintConsole(hConsole,
    				             L"LocalFree() returned error %lu\n",
    				             GetLastError());
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Run the following four command lines to compile the source file 8DOT3FIX.C created in step 1., link the compiled object file 8DOT3FIX.OBJ and cleanup afterwards:

    SET CL=/GA /GF /GS /Gs135168 /Gy /O1 /Oi /Os /Oy /W4 /Zl
    SET LINK=/DEFAULTLIB:KERNEL32.LIB /DEFAULTLIB:SHELL32.LIB /DEFAULTLIB:USER32.LIB /DYNAMICBASE /ENTRY:wmainCRTStartup /LARGEADDRESSAWARE /NOCOFFGRPINFO /NXCOMPAT /OSVERSION:5.0 /OUT:"8dot3 File and Directory Name Changer.com" /RELEASE /STACK:1048576,131072 /SUBSYSTEM:CONSOLE,5.0 /SWAPRUN:CD,NET /VERSION:0.815
    CL.EXE 8DOT3FIX.C
    ERASE 8DOT3FIX.OBJ
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    8DOT3FIX.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    …

Contact

If you miss anything here, have additions, comments, corrections, criticism or questions, want to give feedback, hints or tipps, report broken links, bugs, deficiencies, errors, inaccuracies, misrepresentations, omissions, shortcomings, vulnerabilities or weaknesses, …: don’t hesitate to contact me and feel free to ask, comment, criticise, flame, notify or report!

Use the X.509 certificate to send S/MIME encrypted mail.

Note: email in weird format and without a proper sender name is likely to be discarded!

I dislike HTML (and even weirder formats too) in email, I prefer to receive plain text.
I also expect to see your full (real) name as sender, not your nickname.
I abhor top posts and expect inline quotes in replies.

Terms and Conditions

By using this site, you signify your agreement to these terms and conditions. If you do not agree to these terms and conditions, do not use this site!

Data Protection Declaration

This web page records no (personal) data and stores no cookies in the web browser.

The web service is operated and provided by

Telekom Deutschland GmbH
Business Center
D-64306 Darmstadt
Germany
<‍hosting‍@‍telekom‍.‍de‍>
+49 800 5252033

The web service provider stores a session cookie in the web browser and records every visit of this web site with the following data in an access log on their server(s):


Copyright © 1995–2022 • Stefan Kanthak • <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>