在没有管理员权限的情况下扫描 COM dll 注册表
Scanning COM dll registry without admin privileges
我正在尝试创建一个程序来扫描 COM dll 并创建一个包含必要注册表项的 reg 文件,而无需调用 regsvr32 并且无需具有管理权限。
为此,我使用 RegOverridePredefKey 重新映射 HKEY_CURRENT_USER.
子项内的 windows 注册表配置单元
https://msdn.microsoft.com/pt-br/library/windows/desktop/ms724901(v=vs.85).aspx
正在重新映射。我知道,因为我可以,例如,调用 windows api 函数在 HKEY_LOCAL_MACHINE 下创建一个虚假的注册表项,它实际上出现在我的 HKEY_CURRENT_USER 子项下。无需管理员权限。
这里是 remaping 代码:
if (reg::remapRegistry(HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, basekey + "hkcr"))
{
if (reg::remapRegistry(HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, basekey + "hklm"))
{
if (reg::remapRegistry(HKEY_USERS, HKEY_CURRENT_USER, basekey + "hku"))
{
if (reg::remapRegistry(HKEY_CURRENT_USER, HKEY_CURRENT_USER, basekey + "hkcu"))
{
reg::createKey(HKEY_LOCAL_MACHINE, "Software\Classes");
reg::createKey(HKEY_CURRENT_USER, "Software\Classes");
ret = scan(args);
reg::cancelRemapedRegistry(HKEY_CURRENT_USER);
}
reg::cancelRemapedRegistry(HKEY_USERS);
}
reg::cancelRemapedRegistry(HKEY_LOCAL_MACHINE);
}
reg::cancelRemapedRegistry(HKEY_CLASSES_ROOT);
}
我的扫描函数的 args 参数只是一个包含我的 dll 路径的结构。 reg:: 函数只是 windows api.
的包装器
这是我的扫描函数:
int scan(const Args &args)
{
typedef HRESULT (__stdcall *pDllRegisterServer) (void);
try
{
HMODULE hDLL = LoadLibrary(args.dll.c_str());
if (hDLL == NULL)
{
LOGERROR("Cannot load dll");
return 1;
}
pDllRegisterServer DllRegisterServer = (pDllRegisterServer) GetProcAddress(hDLL, "DllRegisterServer");
if (DllRegisterServer == NULL)
{
LOGERROR("Cannot find function DllRegisterServer in dll");
FreeLibrary(hDLL);
return 2;
}
HRESULT res = DllRegisterServer();
DWORD err = GetLastError();
switch (res)
{
case S_OK:
LOGINFO("successfully called DllRegisterServer");
return 0;
case SELFREG_E_TYPELIB:
LOGERROR("DllRegisterServer error SELFREG_E_TYPELIB");
return 3;
case SELFREG_E_CLASS:
LOGERROR("DllRegisterServer error SELFREG_E_CLASS");
return 4;
case E_OUTOFMEMORY:
LOGERROR("DllRegisterServer error E_OUTOFMEMORY");
return 5;
case E_UNEXPECTED:
LOGERROR("DllRegisterServer error E_UNEXPECTED");
return 6;
default:
LOGERROR("DllRegisterServer really unexpected error");
}
return 7;
}
catch (...)
{
LOGERROR("unknown error loading dll or function");
return -1;
}
}
DllRegisterServer 正在返回 S_OK,但只创建了一半的注册表项。我在 HKEY_CURRENT_USER\tempkey\hkcr 下重新映射的 HKEY_CLASSES_ROOT 包含所有预期的键。但是 HKEY_CURRENT_USER\tempkey\hklm(重新映射 HKEY_LOCAL_MACHINE)仅包含我自己创建的空键 "Software\Classes"。它缺少 DllRegisterServer 应该创建的 "TypeLib" 和 "Interface" 键。
这不是重新映射或权限问题,因为我可以在没有管理员权限的情况下在 HKEY_LOCAL_MACHINE 下创建空的 "Software\Classes"。
奇怪的是,在另一台机器上测试 DllRegisterServer 失败 SELFREG_E_TYPELIB。
我还尝试调用 LoadTypeLib() 和 RegisterTypeLib(),但我收到错误代码“0x8002801c 访问 OLE 注册表时出错”。
如果我随后以管理员身份重新启动 visual studio 并再次 运行 程序,我将获得所有预期的注册表项,包括 HKLM 下的注册表项。
关于它为什么失败以及如何让它工作的任何想法?
我知道无需管理员身份也可以获取所有注册表项,因为 wix 的收获工具 heat.exe 可以成功做到这一点。我已经检查了他们的源代码,我相信我已经在做他们所做的一切。而且我不想让 wix 工具集作为我的构建环境的要求只是为了做到这一点,还因为我需要将它们的文件格式解析为 *.reg 文件。
我找到了解决方案:我们必须在重新映射之前调用函数 OaEnablePerUserTLibRegistration()。
https://msdn.microsoft.com/en-us/library/windows/desktop/cc713570(v=vs.85).aspx
调用该函数后,来自 COM dll 的回调 DllRegisterServer() 将尝试在 HKCR 而不是 HKLM 下创建类型库键。
我正在尝试创建一个程序来扫描 COM dll 并创建一个包含必要注册表项的 reg 文件,而无需调用 regsvr32 并且无需具有管理权限。
为此,我使用 RegOverridePredefKey 重新映射 HKEY_CURRENT_USER.
子项内的 windows 注册表配置单元https://msdn.microsoft.com/pt-br/library/windows/desktop/ms724901(v=vs.85).aspx
正在重新映射。我知道,因为我可以,例如,调用 windows api 函数在 HKEY_LOCAL_MACHINE 下创建一个虚假的注册表项,它实际上出现在我的 HKEY_CURRENT_USER 子项下。无需管理员权限。
这里是 remaping 代码:
if (reg::remapRegistry(HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, basekey + "hkcr"))
{
if (reg::remapRegistry(HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, basekey + "hklm"))
{
if (reg::remapRegistry(HKEY_USERS, HKEY_CURRENT_USER, basekey + "hku"))
{
if (reg::remapRegistry(HKEY_CURRENT_USER, HKEY_CURRENT_USER, basekey + "hkcu"))
{
reg::createKey(HKEY_LOCAL_MACHINE, "Software\Classes");
reg::createKey(HKEY_CURRENT_USER, "Software\Classes");
ret = scan(args);
reg::cancelRemapedRegistry(HKEY_CURRENT_USER);
}
reg::cancelRemapedRegistry(HKEY_USERS);
}
reg::cancelRemapedRegistry(HKEY_LOCAL_MACHINE);
}
reg::cancelRemapedRegistry(HKEY_CLASSES_ROOT);
}
我的扫描函数的 args 参数只是一个包含我的 dll 路径的结构。 reg:: 函数只是 windows api.
的包装器这是我的扫描函数:
int scan(const Args &args)
{
typedef HRESULT (__stdcall *pDllRegisterServer) (void);
try
{
HMODULE hDLL = LoadLibrary(args.dll.c_str());
if (hDLL == NULL)
{
LOGERROR("Cannot load dll");
return 1;
}
pDllRegisterServer DllRegisterServer = (pDllRegisterServer) GetProcAddress(hDLL, "DllRegisterServer");
if (DllRegisterServer == NULL)
{
LOGERROR("Cannot find function DllRegisterServer in dll");
FreeLibrary(hDLL);
return 2;
}
HRESULT res = DllRegisterServer();
DWORD err = GetLastError();
switch (res)
{
case S_OK:
LOGINFO("successfully called DllRegisterServer");
return 0;
case SELFREG_E_TYPELIB:
LOGERROR("DllRegisterServer error SELFREG_E_TYPELIB");
return 3;
case SELFREG_E_CLASS:
LOGERROR("DllRegisterServer error SELFREG_E_CLASS");
return 4;
case E_OUTOFMEMORY:
LOGERROR("DllRegisterServer error E_OUTOFMEMORY");
return 5;
case E_UNEXPECTED:
LOGERROR("DllRegisterServer error E_UNEXPECTED");
return 6;
default:
LOGERROR("DllRegisterServer really unexpected error");
}
return 7;
}
catch (...)
{
LOGERROR("unknown error loading dll or function");
return -1;
}
}
DllRegisterServer 正在返回 S_OK,但只创建了一半的注册表项。我在 HKEY_CURRENT_USER\tempkey\hkcr 下重新映射的 HKEY_CLASSES_ROOT 包含所有预期的键。但是 HKEY_CURRENT_USER\tempkey\hklm(重新映射 HKEY_LOCAL_MACHINE)仅包含我自己创建的空键 "Software\Classes"。它缺少 DllRegisterServer 应该创建的 "TypeLib" 和 "Interface" 键。
这不是重新映射或权限问题,因为我可以在没有管理员权限的情况下在 HKEY_LOCAL_MACHINE 下创建空的 "Software\Classes"。
奇怪的是,在另一台机器上测试 DllRegisterServer 失败 SELFREG_E_TYPELIB。
我还尝试调用 LoadTypeLib() 和 RegisterTypeLib(),但我收到错误代码“0x8002801c 访问 OLE 注册表时出错”。
如果我随后以管理员身份重新启动 visual studio 并再次 运行 程序,我将获得所有预期的注册表项,包括 HKLM 下的注册表项。
关于它为什么失败以及如何让它工作的任何想法?
我知道无需管理员身份也可以获取所有注册表项,因为 wix 的收获工具 heat.exe 可以成功做到这一点。我已经检查了他们的源代码,我相信我已经在做他们所做的一切。而且我不想让 wix 工具集作为我的构建环境的要求只是为了做到这一点,还因为我需要将它们的文件格式解析为 *.reg 文件。
我找到了解决方案:我们必须在重新映射之前调用函数 OaEnablePerUserTLibRegistration()。
https://msdn.microsoft.com/en-us/library/windows/desktop/cc713570(v=vs.85).aspx
调用该函数后,来自 COM dll 的回调 DllRegisterServer() 将尝试在 HKCR 而不是 HKLM 下创建类型库键。