delphi - COM Elevation Moniker no puede elevar el servidor en Vista/Windows 7
winapi windows-7 (1)
Un error que está cometiendo es que está pasando la variable HInstance
global de HInstance
donde CoGetObject()
espera un HWND
lugar. Un identificador de HINSTANCE
no es un identificador de HWND
válido. Necesita utilizar un HWND
real, como la propiedad Handle
de un TForm
, o bien, especifique 0
para que el Moniker de elevación elija una ventana adecuada para usted.
En cuanto al valor de retorno ERROR_ELEVATION_REQUIRED
, todo lo que puedo decir es que su registro COM probablemente esté incompleto en alguna parte. Por favor, muestre los detalles de registro completos que en realidad están siendo almacenados en el Registro (no lo que su código cree que está almacenando, lo que el Registro en realidad está almacenando).
Se debe llamar a CoInitializeSecurity()
cuando el proceso del servidor comienza a ejecutarse.
Creé un servidor COM local que requiere elevación y debería crearse una instancia desde un proceso no elevado.
Utilizando el artículo de MSDN sobre el moniker de elevación COM , configuré la clase del servidor siguiendo los requisitos especificados. El servidor se registró correctamente en la colmena HKLM.
El ejemplo de código:
procedure CoCreateInstanceAsAdmin(const Handle: HWND;
const ClassID, IID: TGuid; PInterface: PPointer);
var
rBindOpts: TBindOpts3;
sMonikerName: WideString;
iRes: HRESULT;
begin
ZeroMemory(@rBindOpts, Sizeof(TBindOpts3));
rBindOpts.cbStruct := Sizeof(TBindOpts3);
rBindOpts.hwnd := Handle;
rBindOpts.dwClassContext := CLSCTX_LOCAL_SERVER;
sMonikerName := ''Elevation:Administrator!new:'' + GUIDToString(ClassID);
iRes := CoGetObject(PWideChar(sMonikerName), @rBindOpts, IID, PInterface);
OleCheck(iRes);
end;
class function CoIMyServer.Create: IMyServer;
begin
CoCreateInstanceAsAdmin(HInstance, CLASS_IMyServer, IMyServer, @Result);
end;
Cuando se trata de CoGetObject(PWideChar(sMonikerName), @rBindOpts, IID, PInterface)
obtengo la pantalla UAC y confirmo que CoGetObject(PWideChar(sMonikerName), @rBindOpts, IID, PInterface)
el servidor como administrador. Sin embargo, OleCheck(iRes)
devuelve: "La operación solicitada requiere elevación" error.
De ese artículo que he leído sobre "elevación sobre el hombro (OTS)".
¿Es esta la única forma de que mi instancia de servidor esté disponible para el proceso no elevado? Si es así, ¿cuándo debe CoInitializeSecurity
en el servidor?
Complete los detalles de registro
HKLM/SOFTWARE/Wow6432Node/Classes/CLSID
{MyServer CLSID}
(Default) = IMyServer Object
LocalizedString = @C:/Program Files (x86)/MyServer/MyServer.exe,-15500
Elevation
Enabled = 0x000001 (1)
LocalServer32
(Default) = C:/PROGRA~2/MyServer/MYSERVER.EXE
ProgID
(Default) = uMyServer.IMyServer
TypeLib
(Default) = {TypeLib GUID}
Version
(Default) = 1.0
HKLM/SOFTWARE/Wow6432Node/Classes/Interface
{GUID of IID_IMyServer}
(Default) = IMyServer
ProxyStubClsid32
(Default) = {Some GUID}
TypeLib
(Default) = {TypeLib GUID}
Version = 1.0
Arriba están las únicas entradas que existen en mi registro después de registrar el servidor.
Detalles adicionales
Intenté sin éxito llamar a CoInitializeSecurity()
implícitamente + estableciendo permisos de almuerzo según lo recomendado usando el siguiente código:
function GetSecurityDescriptor(const lpszSDDL: LPWSTR; out pSD: PSecurityDescriptor): Boolean;
begin
Result := ConvertStringSecurityDescriptorToSecurityDescriptorW(lpszSDDL, SDDL_REVISION_1,
pSD, nil);
end;
function GetLaunchActPermissionsWithIL(out pSD: PSecurityDescriptor): Boolean;
var
lpszSDDL: LPWSTR;
begin
// Allow World Local Launch/Activation permissions. Label the SD for LOW IL Execute UP
lpszSDDL := ''O:BAG:BAD:(A;;0xb;;;WD)S:(ML;;NX;;;LW)'';
Result := GetSecurityDescriptor(lpszSDDL, pSD);
end;
function GetAccessPermissionsForLUAServer(out pSD: PSecurityDescriptor): Boolean;
var
lpszSDDL: LPWSTR;
begin
// Local call permissions to IU, SY
lpszSDDL := ''O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)'';
Result := GetSecurityDescriptor(lpszSDDL, pSD);
end;
function SetAccessPermissions(hAppKey: HKEY; pSD: PSECURITY_DESCRIPTOR): Boolean;
var
dwLen: DWORD;
iRes: LONG;
begin
dwLen := GetSecurityDescriptorLength(pSD);
iRes := RegSetValueExA(hAppKey, ''AccessPermission'', 0, REG_BINARY, pSD, dwLen);
Result := iRes = ERROR_SUCCESS;
end;
function SetLaunchActPermissions(hAppKey: HKEY; pSD: PSECURITY_DESCRIPTOR): Boolean;
var
dwLen: DWORD;
iRes: LONG;
begin
dwLen := GetSecurityDescriptorLength(pSD);
iRes := RegSetValueExA(hAppKey, ''LaunchPermission'', 0, REG_BINARY, pSD, dwLen);
Result := iRes = ERROR_SUCCESS;
end;
procedure Initialize;
var
pSD: PSecurityDescriptor;
sSubKey: WideString;
hAppKey: HKEY;
begin
sSubKey := ''AppID/{GUID}'';
RegOpenKeyW(HKEY_CLASSES_ROOT, PWideChar(sSubKey), hAppKey);
if GetAccessPermissionsForLUAServer(pSD) then
if not SetAccessPermissions(hAppKey, pSD) then
raise Exception.Create(Format(''Access permissions aren''''t set. System error: %d'',
[GetLastError()]));
pSD := nil;
if GetLaunchActPermissionsWithIL(pSD) then
if not SetLaunchActPermissions(hAppKey, pSD) then
raise Exception.Create(Format(''Launch permissions aren''''t set. System error: %d'',
[GetLastError()]));
end;
initialization
TAutoObjectFactory.Create(ComServer, TMyServer, Class_IMyServer,
ciMultiInstance, tmApartment);
Initialize;
Como un GUID de AppID intenté usar el mismo GUID de CLSID de mi interfaz de servidor y un nuevo GUID generado: el resultado era el mismo. AccessPermission
valores de AccessPermission
y LaunchPermission
aparecieron en el lugar especificado después del registro del servidor.
También probado:
- Especificación de
ROTFlags = 1
en la clave AppId - Construyendo el servidor como una aplicación de 64 bits
Claves / valores de registro adicionales que creé manualmente:
[HKEY_LOCAL_MACHINE/SOFTWARE/Classes/AppID/MyServer.exe]
@="MyServer"
"AppID"="{My GUID}"
[HKEY_LOCAL_MACHINE/SOFTWARE/Classes/AppID/{My GUID}]
@="MyServer"
"ROTFlags"=dword:00000001
[HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{My GUID}]
@="MyServer Object"
"AppID"="{My GUID}"