I want to use lsasrv!LsaProtectMemory() inside the LSASS process to encrypt a block of memory and return the ciphertext. It's part of the LsapLsasrvIfTable interface in lsasrv.dll, but unless I'm mistaken can only be accessed by another LSA extension using the lsasrv!QueryLsaInterface() function. The following text is some basic information about the internal structures.
LsapLsasrvIfTable:
dq offset LsaProtectMemory
dq offset LsaUnprotectMemory
dq offset LsaIFreeReturnBuffer
dq offset LsapSubProv_FreeRoutine(_RTL_BALANCED_NODE *,void *)
dq offset LsaICallPackage
dq offset LsaIfGetAuthDataForUser(_UNICODE_STRING *,ulong,_UNICODE_STRING *,uchar * *,ulong *,_UNICODE_STRING *)
dq offset LsaConvertAuthDataToToken
dq offset LsaIQueryPackageAttrInLogonSession
dq offset LsaINotifyPasswordChanged
dq offset LsaINoMoreWin2KDomain
dq offset LsaIAuditDPAPIEvent
dq offset LsaIQueryInformationPolicyTrusted
dq offset LsaIFree_LSAPR_POLICY_INFORMATION
dq offset LsapGetCredentialKey(_LUID *,_GUID *,_CREDENTIAL_KEY * *)
dq offset LsapIsDomainUserDPAPI(void *)
dq offset LsapDPAPIPasswordChangeForGMSA
dq offset LsapAllocateULong
dq offset LsapDecryptDPAPIMasterKey(_LUID *,LSAI_DPAPI_KEY_TYPE,uchar *,ulong,uchar *,ulong,uchar * *,ulong *)
Support for LSA extensions first appeared in Windows 7, but for some reason still aren't properly documented. If we look inside LSASS.EXE, there's three initial functions of interest:
* lsass!LsapInitializeExtensionConfig() initializes two LIST_ENTRY structures: gLsapIfList, gLsapExtensionList
* lsass!LsapLoadRequiredExtensions() reads and loads DLL from HKLM\System\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv
* lsass!LsapLoadLsaInterfaces() reads and loads DLL from HKLM\System\CurrentControlSet\Control\LsaExtensionConfig\Interfaces
Two are currently installed on my system. One for the Local Security Authority (LSA) and the other for Encrypting File System (EFS).
reg query "HKLM\System\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv"
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv
Extensions REG_MULTI_SZ lsasrv.dll\0efslsaext.dll
These DLL both export InitializeLsaExtension(). Internally is allocation of what I defined LSAP_EXTENSION_ENTRY allocated on the heap. Some of the fields are self-explanatory.
enum LSA_INTERFACE_TYPE {
LsasrvIfTable = 1001,
DpapiIfTable = 1002,
};
enum LSA_EXTENSION_INIT_STAGE {
LsaExtensionLoad = 1, // After InitializeLsaExtension(0) inside LsapLoadExtension()
LsaExtensionNotify = 2, // After InitializeLsaExtension(1) inside LsapNotifyExtensionsLoadComplete()
LsaExtensionStart = 3 // After InitializeLsaExtension(2) inside LsapStartExtensions()
};
typedef NTSTATUS (WINAPI *PINITIALIZELSAEXTENSION)(LSA_EXTENSION_INIT_STAGE);
typedef NTSTATUS (WINAPI *PQUERYLSAINTERFACE)(LSA_INTERFACE_TYPE, PVOID*);
typedef struct _LSAP_EXTENSION_ENTRY {
LIST_ENTRY Links;
LIST_ENTRY InterfaceLinks; // LSAP_INTERFACE_ENTRY.ExtentionLinks
UNICODE_STRING DllName; // name of extension. e.g. lsasrv.dll
PVOID DllBase; // set by LoadLibraryExW();
LSA_EXTENSION_INIT_STAGE Stage;
DWORD Win32Error;
NTSTATUS NtStatus;
PINITIALIZELSAEXTENSION InitializeLsaExtension; // set by GetProcAddress(hExtension, "InitializeLsaExtension");
PQUERYLSAINTERFACE QueryLsaInterface; // set by GetProcAddress(hExtension, "QueryLsaInterface");
} LSAP_EXTENSION_ENTRY, *PLSAP_EXTENSION_ENTRY;
Once an entry is created, it's inserted into the global list lsass!gLsapExtensionList.
Here we see two entries. 1001 and 1002 are unique to these interfaces and are actually LSA_INTERFACE_TYPE.
reg query "HKLM\System\CurrentControlSet\Control\LsaExtensionConfig\Interfaces"
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LsaExtensionConfig\Interfaces\1001
Extension REG_SZ lsasrv.dll
Name REG_SZ LsaLsasrvInterface
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LsaExtensionConfig\Interfaces\1002
Extension REG_SZ dpapisrv.dll
Name REG_SZ LsaDpapiInterface
Internally is allocation of what I call LSAP_INTERFACE_ENTRY, which is allocated on the heap.
typedef struct _LSAP_INTERFACE_ENTRY {
LIST_ENTRY Links;
LSA_INTERFACE_TYPE Type; // This is a unique ID used when calling QueryLsaInterface.
PLSAP_EXTENSION_ENTRY Extension; // extension this interface belongs to
LIST_ENTRY ExtensionLinks; // linked LSA_ENTENSION_ENTRY object
PVOID FunctionTable; // points to array of functions
} LSAP_INTERFACE_ENTRY, *PLSAP_INTERFACE_ENTRY;
For the LSASRV interface, we have something like the following:
NTSTATUS
QueryLsaInterface(LSA_INTERFACE_TYPE Type, PVOID *FunctionTable) {
if (Type != LsasrvIfTable)
return STATUS_NOT_SUPPORTED;
*FunctionTable = &LsapLsasrvIfTable;
return STATUS_SUCCESS;
}
Based on the internals of LSA extensions, it's not available via RPC, but perhaps there's another way?