如何从 32 位进程读取 64 位注册表项?
Posted
技术标签:
【中文标题】如何从 32 位进程读取 64 位注册表项?【英文标题】:How can I read 64-bit registry key from a 32-bit process? 【发布时间】:2012-02-25 14:55:32 【问题描述】:我一直在使用来自HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography
的键MachineGuid
的值来唯一标识主机,但是对于在64 位计算机上运行的32 位进程,该值似乎丢失了。我猜它是在 Wow6432Node 下搜索,它确实不见了。根据this,您应该可以通过添加标志来获得正确的键,但下面的代码似乎仍然无法完成这项工作。我错过了什么?
const
KEY_WOW64_64KEY=$0100;
var
r:HKEY;
s:string;
i,l:integer;
begin
//use cryptography machineguid, keep a local copy of this in initialization?
l:=40;
if RegOpenKeyEx(HKEY_LOCAL_MACHINE,PChar('Software\Microsoft\Cryptography'),
0,KEY_QUERY_VALUE,r)=ERROR_SUCCESS then
begin
SetLength(s,l);
if RegQueryValue(r,'MachineGuid',PChar(s),l)=ERROR_SUCCESS then
begin
SetLength(s,l);
RegCloseKey(r);
end
else
begin
//try from-32-to-64
RegCloseKey(r);
if RegOpenKeyEx(HKEY_LOCAL_MACHINE,PChar('Software\Microsoft\Cryptography'),
0,KEY_QUERY_VALUE or KEY_WOW64_64KEY,r)=ERROR_SUCCESS then
begin
l:=40;
if RegQueryValue(r,'MachineGuid',PChar(s),l)=ERROR_SUCCESS then
SetLength(s,l)
else
l:=0;
RegCloseKey(r);
end;
end;
end;
【问题讨论】:
为什么不使用 TRegistry?你的逻辑也是一团糟。您希望调用单个函数来读取值。叫它两次。第一次通过 0。第二次通过 KEY_WOW64_64KEY。如果第一次失败,只有第二次调用。这是提取方法重构。 我以前用过TRegistry,不知道你可以通过带参数的构造函数添加KEY_WOW64_64KEY。 当然可以。看我的回答。编辑:看起来你现在找到了我的答案。另请注意,您可以随时修改Access
属性以在已创建的注册表对象中切换视图。
【参考方案1】:
我建议您使用 IsWow64Process()
函数来了解您何时是在 64 位操作系统上运行的 32 进程,然后仅在该特定条件下应用 KEY_WOW64_64KEY
标志。如果应用是 32 位操作系统上的 32 位进程,或 64 位操作系统上的 64 位进程,则不需要这些标志。
例如:
const
KEY_WOW64_64KEY = $0100;
var
key: HKEY;
str: string;
len: DWORD;
flag: REGSAM;
wow64: BOOL;
begin
flag := 0;
wow64 := 0;
IsWow64Process(GetCurrentProcess(), @wow64);
if wow64 <> 0 then flag := KEY_WOW64_64KEY;
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Cryptography', 0, KEY_QUERY_VALUE or flag, key) = ERROR_SUCCESS then
try
SetLength(str, 40);
len := Length(str) * SizeOf(Char);
if RegQueryValueEx(key, 'MachineGuid', nil, nil, PByte(Pointer(s)), @len) <> ERROR_SUCCESS then len := 0;
SetLength(str, len div SizeOf(Char));
finally
RegCloseKey(key);
end;
end;
【讨论】:
我认为你可以简单地应用该标志,它在 x86 操作系统上被忽略 没错。如果你总是想从原生视图读取,可以无条件使用KEY_WOW64_64KEY
仅在 XP 及更高版本上,即使在 32 位版本中也能识别 64 位系统的存在。如果您在 Win2k 或更早版本上指定标志,它将作为未知参数失败。在这些系统上,无论如何都需要动态加载 IsWow64Process()
以检测 WOW64 是否存在。【参考方案2】:
您的代码不必要地复杂,主要是因为您没有利用内置的TRegistry
类,该类使您免受低级注册表API 的所有复杂性的影响。例如,考虑以下代码:
type
TRegistryView = (rvDefault, rvRegistry64, rvRegistry32);
function RegistryViewAccessFlag(View: TRegistryView): LongWord;
begin
case View of
rvDefault:
Result := 0;
rvRegistry64:
Result := KEY_WOW64_64KEY;
rvRegistry32:
Result := KEY_WOW64_32KEY;
end;
end;
function ReadRegStr(const Root: HKEY; const Key, Name: string;
const View: TRegistryView=rvDefault): string;
var
Registry: TRegistry;
begin
Registry := TRegistry.Create(KEY_READ or RegistryViewAccessFlag(View));
try
Registry.RootKey := Root;
if not Registry.OpenKey(Key) then
raise ERegistryException.CreateFmt('Key not found: %s', [Key]);
if not Registry.ValueExists(Name) then
raise ERegistryException.CreateFmt('Name not found: %s\%s', [Key, Name]);
Result := Registry.ReadString(Name);//will raise exception in case of failure
finally
Registry.Free;
end;
end;
函数ReadRegStr
将从键Key
中返回名为Name
的字符串值,该值相对于根键Root
。如果出现错误,例如键或名称不存在,或者值的类型错误,则会引发异常。
View
参数是一个枚举,可让您轻松访问注册表的本机、32 位或 64 位视图。请注意,本机意味着正在运行的进程的本机。所以它将是 32 位进程的 32 位视图和 64 位进程的 64 位视图。此枚举反映了 .net 中的等效定义。
【讨论】:
不,仍然无法正常工作,ReadString 给出了空字符串(我的笔记本电脑有问题吗?): const KEY_WOW64_64KEY=$0100; var r:TRegistry; s:字符串;开始 r:=TRegistry.Create(KEY_READ 或 KEY_WOW64_64KEY); r.RootKey:=HKEY_LOCAL_MACHINE;如果 r.OpenKeyReadOnly('Software\Microsoft\Cryptography') 则 s:=r.ReadString('MachineGuid'); 我的代码中有一个错误,我刚刚修复了它。根密钥。但你已经明白了。您的代码应该可以工作。那把钥匙在吗?!我会在一个小时左右检查我的机器。晚餐时间到了!! 我已经测试了代码。我修复了另一个错误,因为我不太了解TRegistry
在不存在值时如何工作。但是如果你看到一个空字符串,那就有问题了。当我运行它们时,您的代码(和我的代码)返回正确的值。我确实想知道您使用的是哪个版本的Delphi。也许您使用的是 TRegistry
不尊重 KEY_WOW64_64KEY
的旧 Delphi。但这会让我感到惊讶。我对此有点怀疑,因为您正在定义KEY_WOW64_64KEY
,但我只是从 Windows.pas 得到它。那么,您使用的是什么 Delphi?
Delphi 7,我检查了Registry.pas,在OpenKey方法中为samDesired参数正确传递了FAccess值,但在OpenKeyReadOnly方法中被覆盖了! 在使用此注册表项时,我更进一步。如果该值不存在,我创建了它:不在 HKEY_LOCAL_MACHINE 中,这需要提升,但在 HKEY_CURRENT_USER 中。任何看到引入的密钥的人都不太可能意识到它是一个虚拟的。
function GetComputerGUID: String;
var
Reg: TRegistry;
oGuid: TGUID;
sGuid: String;
begin
Result := '';
// Attempt to retrieve the real key
Reg := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Cryptography') and Reg.ValueExists('MachineGuid') then
Result := Reg.ReadString('MachineGuid');
Reg.CloseKey;
finally
Reg.Free;
end;
// If retrieval fails, look for the surrogate
if Result = '' then begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey('SOFTWARE\Microsoft\Cryptography', True) then begin
if Reg.ValueExists('MachineGuid') then
Result := Reg.ReadString('MachineGuid')
else begin
// If the surrogate doesn't exist, create it
if CreateGUID(oGUID) = 0 then begin
sGuid := Lowercase(GUIDToString(oGUID));
Reg.WriteString('MachineGuid', Copy(sGuid, 2, Length(sGUID) - 2));
Result := Reg.ReadString('MachineGuid');
end;
end;
end;
Reg.CloseKey;
finally
Reg.Free;
end;
end;
if Result = '' then
raise Exception.Create('Unable to access registry value in GetComputerGUID');
end;
@Remy Lebeau 说的很好——不过是 TeamB;我应该适当地修改上面的代码。
【讨论】:
在调用ReadString()
之前,您无需检查ValueExists()
。如果值不存在,则返回一个空字符串,它不会像其他读取方法那样引发异常。【参考方案4】:
使用此路径调用 reg.exe C:\Windows\sysnative\reg.exe 例如:
C:\Windows\sysnative\reg.exe QUERY "HKLM\SOFTWARE\JavaSoft\JDK" /v CurrentVersion
来源:https://***.com/a/25103599
【讨论】:
以上是关于如何从 32 位进程读取 64 位注册表项?的主要内容,如果未能解决你的问题,请参考以下文章