我想从 Windows注册表中读取值,这是由另一个我没有源代码的程序保存的,但是我已经知道了这个值的类型,它是这样的:
_MyData = record
byteType: Byte;
encData: PByte;
end;
byteType表示此数据的类型为整数(1,2,3 …),您可以忘记此参数,而encData是使用windows crypt32.dll函数(CryptProtectData)的加密数据
我使用下一个代码从注册表中读取值:
procedure TForm1.Button2Click(Sender: TObject);
var
myData: _MyData;
reg: TRegistry;
valueSize: Integer;
begin
reg := TRegistry.Create;
try
if reg.OpenKey(KEY_PATH,false) then
Begin
valueSize := reg.GetDataSize(VALUE_NAME);
reg.ReadBinaryData(VALUE_NAME, myData, valueSize);
End;
finally
reg.Free;
end;
end;
// KEY_PATH,VALUE_NAME是字符串Consts.
所以,现在我在myData.encData中有加密数据,现在我想通过传递具有此签名的CryptUnprotectData函数来解密它:
function CryptUnprotectData(pDataIn: PDATA_BLOB; ppszDataDescr: PLPWSTR; pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
首先,我需要将加密数据放在DATA_BLOB类型的变量中,该变量具有以下结构:
_CRYPTOAPI_BLOB = record
cbData: DWORD;
pbData: PBYTE;
end;
DATA_BLOB = _CRYPTOAPI_BLOB;
PDATA_BLOB = ^DATA_BLOB;
pbData是加密数据的指针(我从注册表中读取),cbData是加密数据的大小,这是我的问题我在myData.encData中有指向加密数据的指针(我已经从注册表中读取)这是PByte,但我不知道如何获得这些数据的大小?如果我没有给CryptUnprotectData函数正确的大小,它总是在outpout中给出nil,任何想法如何做到这一点?
谢谢你的帮助.
编辑:解决方案,感谢Ken Bourassa
_MyData = packed record
byteType: Byte;
encData: array of byte;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
myData: ^_MyData;
reg: TRegistry;
valueSize: Integer;
dataIn, dataOut: DATA_BLOB;
begin
reg := TRegistry.Create;
try
if reg.OpenKey(KEY_PATH,false) then
Begin
valueSize := reg.GetDataSize(VALUE_NAME);
GetMem(myData, ValueSize);
try
reg.ReadBinaryData(VALUE_NAME, myData^, valueSize);
dataOut.cbData := 0;
dataOut.pbData := nil;
dataIn.cbData := Valuesize - SizeOf(Byte);
dataIn.pbData := @myData.encData;
CryptUnprotectData(@dataIn,nil,nil,nil,nil,CRYPTPROTECT_UI_FORBIDDEN,@dataOut);
//yes, it works, Thank you very much Ken Bourassa
finally
FreeMem(myData);
End;
End;
finally
reg.Free;
end;
end;
数据的大小是reg.GetDataSize – SizeOf(Byte)
但现在这是你唯一的问题,
您的_MyData结构长度为8个字节.所以当你打电话
reg.ReadBinaryData(VALUE_NAME, myData, valueSize);
对于任何超过8个字节的键值,您会发生一些缓冲区溢出.即使您读取的密钥短于8个字节,EncData也会包含垃圾.
我宁愿这样走:
_MyData = packed record
byteType: Byte;
encData: array[0..MaxInt] of byte;
end
procedure TForm1.Button2Click(Sender: TObject);
var
myData: ^_MyData;
reg: TRegistry;
valueSize: Integer;
begin
reg := TRegistry.Create;
try
if reg.OpenKey(KEY_PATH,false) then
Begin
valueSize := reg.GetDataSize(VALUE_NAME);
GetMem(MyData, ValueSize);
try
reg.ReadBinaryData(VALUE_NAME, myData^, valueSize);
//Do what is needed with MyData. The size of MyData.EncData = Valuesize - SizeOf(Byte)
finally
FreeMem(MyData);
end;
End;
finally
reg.Free;
end;
end;
我将packed关键字添加到记录定义中,因为我认为它是最有可能被声明的方式……但是,这完全取决于写入值的应用程序的规范.我还将EncData声明为byte的字节[0..MaxInt].这使得声明_MyData类型的变量成为一个非常糟糕的主意,但这是我知道在记录中允许变量数组而不强制禁用范围检查的唯一方法.如果在项目中始终使用Range Checking = False运行并不关心(或者您不介意在代码中需要的地方打开/关闭它),您可以将其声明为字节的Array [0..0].我知道它应该工作,我不知道该方法的细节,因为我从未真正使用它.
编辑(被接受后):
实际上,将EncData声明为字节数组与将其声明为PByte一样糟糕.字节数组是引用类型(实际上是4字节指针).如果您尝试在代码中访问EncByte [0],则很可能会获得AV.它工作的唯一原因是你只使用@ myData.encData(以及使用GetMem分配记录的事实).但是像这样使用,你可以像这样声明EncData:
_MyData = packed record byteType: Byte; encData: Record end; end
这仍然有效,因为在你的例子中,你并不真正关心EncData类型的声明,你只关心它所在的内存地址.
