我有一个棘手的问题,我希望我能解释清楚, 我想从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;
答案 0 :(得分:4)
数据大小为reg.GetDataSize
- SizeOf(字节)
但现在这是你唯一的问题,
您的_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声明为Array[0..MaxInt] of byte
。这使得声明_MyData类型的变量成为一个非常糟糕的主意,但这是我知道在记录中允许变量数组而不强制禁用范围检查的唯一方法。如果在项目中始终使用Range Checking = False运行并不关心(或者您不介意在代码中需要的地方打开/关闭它),您可以将其声明为Array[0..0] of byte
。我知道它应该有效,我不知道该方法的细节,因为我从未真正使用它。
编辑(被接受后):
实际上,将EncData声明为Array of byte
与将其声明为PByte一样糟糕。 Array of byte
是一个引用类型(实际上是一个4字节的指针)。如果您尝试在代码中访问EncByte [0],则很可能会获得AV。它工作的唯一原因是你只使用@myData.encData
(以及使用GetMem分配记录的事实)。但是像这样使用,你可以像这样声明EncData:
_MyData = packed record
byteType: Byte;
encData: Record end;
end
并且这仍然有效,因为在您的示例中,您并不真正关心EncData类型的声明,您只关心内存地址。
答案 1 :(得分:3)
对我来说,第二个字段是“PByte”值看起来很奇怪。它存储指针吗?只要进程正在运行,指针就是有效的,除非它在程序运行时只存储一个临时值(如果是,奇怪的选择)。或者它是否存储该值指向的任意长度缓冲区?如果它首先存储类型值,那么缓冲区,缓冲区大小只是ValueSize - SizeOf(字节),除非记录没有打包并且它直接从内存“转储”,那么可能会有一些填充字节。