COM Elevation Moniker 在 Vista/Windows 7 下无法提升服务器

发布于 2025-01-06 11:32:03 字数 4944 浏览 1 评论 0原文

我创建了一个需要提升的本地 COM 服务器,并且应该从非提升的进程内部实例化。

使用 MSDN 有关 COM 提升的文章moniker,我已经按照指定的要求配置了服务器类。服务器已在 HKLM 配置单元中成功注册。

代码示例:

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;

当涉及到 CoGetObject(PWideChar(sMonikerName), @rBindOpts, IID, PInterface) 时,我会看到 UAC 屏幕并确认以管理员身份运行服务器。但是,OleCheck(iRes) 返回:“请求的操作需要提升”错误。

来自该文章我读过有关“过肩(OTS)抬高”的内容。

这是让我的服务器实例可用于非提升进程的唯一方法吗?如果是这样,什么时候应该在服务器上调用CoInitializeSecurity


完整的注册详细信息

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

以上是注册服务器后我的注册表中存在的唯一条目。


其他详细信息

尝试隐式调用CoInitializeSecurity()但未成功+使用以下代码按照建议设置午餐权限:

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;  

作为AppID GUID,我尝试使用我的应用程序的相同CLSID GUID服务器接口和新生成的 GUID:结果是相同的。 服务器注册后,AccessPermissionLaunchPermission 值出现在指定位置。

还尝试过:

  • 在 AppId 项中指定 ROTFlags = 1
  • 将服务器构建为 64 位应用程序

我手动创建的其他注册表项/值:

[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}"

I’ve created a local COM server that requires elevation and should be instantiated from inside a non-elevated process.

Using MSDN's article on the COM elevation moniker, I’ve configured the server class following the specified requirements. The server was successfully registered in the HKLM hive.

The code sample:

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;

When it comes to CoGetObject(PWideChar(sMonikerName), @rBindOpts, IID, PInterface) I get the UAC screen and confirm running the server as admin. However, OleCheck(iRes) returns: "The requested operation requires elevation" error.

From that article I’ve read about "Over-The-Shoulder (OTS) Elevation".

Is this the only way to get my server instance available for the non-elevated process? If so, when should CoInitializeSecurity be called on the server?


Complete registration details

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

Above are the only entries that exist in my registry after registering the server.


Additional details

Tried without success calling CoInitializeSecurity() implicitly + setting lunch permissions as advised using the following code:

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;  

As a AppID GUID I tried to use both the same CLSID GUID of my server interface and a new generated GUID: result was the same.
AccessPermission and LaunchPermission values appeared at the specified place after server registering.

Also tried:

  • Specifying ROTFlags = 1 in the AppId key
  • Building the server as 64-bit application

Additional registry keys/values I created manually:

[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}"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风启觞 2025-01-13 11:32:03

您犯的一个错误是您正在传递 RTL 的全局 HInstance 变量,而 CoGetObject() 需要一个 HWND 来代替。 HINSTANCE 句柄不是有效的 HWND 句柄。您需要使用实际的 HWND,例如 TFormHandle 属性,或者指定 0 让Elevation Moniker 为您选择合适的窗口。

至于ERROR_ELEVATION_REQUIRED返回值,我只能说你的COM注册可能在某个地方不完整。请显示实际存储在注册表中的完整注册详细信息(不是您的代码认为它存储的内容 - 注册表实际存储的内容)。

CoInitializeSecurity() 应在服务器进程开始运行时调用。

One mistake you are making is you are passing the RTL's global HInstance variable where CoGetObject() expects an HWND instead. An HINSTANCE handle is not a valid HWND handle. You need to use an actual HWND such as the Handle property of a TForm, or else specify 0 to let the Elevation Moniker choose a suitable window for you.

As for the ERROR_ELEVATION_REQUIRED return value, all I can say is that your COM registration is likely incomplete somewhere. Please show the complete registration details that are actually being stored in the Registry (not what your code thinks it is storing - what the Registry is actually storing).

CoInitializeSecurity() should be called when the server process begins running.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文