COM Elevation Moniker 在 Vista/Windows 7 下无法提升服务器
我创建了一个需要提升的本地 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:结果是相同的。 服务器注册后,AccessPermission
和 LaunchPermission
值出现在指定位置。
还尝试过:
- 在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您犯的一个错误是您正在传递 RTL 的全局
HInstance
变量,而CoGetObject()
需要一个HWND
来代替。HINSTANCE
句柄不是有效的HWND
句柄。您需要使用实际的HWND
,例如TForm
的Handle
属性,或者指定0
让Elevation Moniker 为您选择合适的窗口。至于
ERROR_ELEVATION_REQUIRED
返回值,我只能说你的COM注册可能在某个地方不完整。请显示实际存储在注册表中的完整注册详细信息(不是您的代码认为它存储的内容 - 注册表实际存储的内容)。CoInitializeSecurity()
应在服务器进程开始运行时调用。One mistake you are making is you are passing the RTL's global
HInstance
variable whereCoGetObject()
expects anHWND
instead. AnHINSTANCE
handle is not a validHWND
handle. You need to use an actualHWND
such as theHandle
property of aTForm
, or else specify0
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.