如何获取加密数据的大小?
我有一个棘手的问题,希望我能很好地解释它, 我想从 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 are string 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正确的大小,它总是在输出中给出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;
I have tricky question and I hope I can explain it well,
I want to read value from windows registry which is saved by another program that I don’t have its source, but I already know the type of this value and it’s like this:
_MyData = record
byteType: Byte;
encData: PByte;
end;
byteType indicates the type of this data as integer (1,2,3…) you can forget about this parameter, while encData is an encrypted data using the windows crypt32.dll function (CryptProtectData)
I use the next code to read the value from registry:
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 are string Consts.
So, now I have the encrypted data in myData.encData and now I want decrypt it by passing it the CryptUnprotectData function which has this signature:
function CryptUnprotectData(pDataIn: PDATA_BLOB; ppszDataDescr: PLPWSTR; pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
First I need to put the encrypted data in a variable of type DATA_BLOB which has this structure:
_CRYPTOAPI_BLOB = record
cbData: DWORD;
pbData: PBYTE;
end;
DATA_BLOB = _CRYPTOAPI_BLOB;
PDATA_BLOB = ^DATA_BLOB;
pbData is the pointer to the encrypted data (I read it from registry), and cbData is the size of the encrypted data, and here is my problem I have the pointer to encrypted data (I already read it from registry ) in myData.encData which is PByte, but I don’t know how to get the size of this data? and if i don't give the function CryptUnprotectData the right size it always gives nil in the outpout, Any idea how to do this?
Thanks for your help.
Edit:the solution, thanks to 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;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
数据的大小是
reg.GetDataSize
- SizeOf(Byte)但这现在是您唯一的问题,
您的 _MyData 结构有 8 个字节长。因此,当您调用
任何超过 8 个字节的键值时,就会发生缓冲区溢出。即使您读取的密钥短于 8 个字节,EncData 也会包含垃圾。
我宁愿这样做:
我将
packed
关键字添加到记录定义中,因为我相信这是最可能的声明方式......但是,这一切都取决于以下规范:应用程序写入值。我还将 EncData 声明为Array[0..MaxInt] of byte
。这使得声明 _MyData 类型的变量成为一个非常糟糕的主意,但这是我所知道的允许记录中的变量数组而不强制禁用范围检查的唯一方法。如果在您的项目中始终使用 Range Checking = False 运行并不需要担心(或者您不介意在代码中需要的地方打开/关闭它),您可以将其声明为Array[0..0] of字节
。我知道它应该有效,但我不知道该方法的具体细节,因为我从未真正使用过它。编辑(被接受后):
实际上,将 EncData 声明为
字节数组
与将其声明为 PByte 一样糟糕。字节数组
是一个引用类型(实际上是一个 4 字节指针)。如果您尝试在代码中访问 EncByte[0],您很可能会收到反病毒病毒。它起作用的唯一原因是您只使用@myData.encData
(事实上记录是使用 GetMem 分配的)。但是像这样使用,您可以像这样声明 EncData :这仍然有效,因为在您的示例中,您并不真正关心声明的 EncData 类型,您只关心它所在的内存地址。
The size of the data is
reg.GetDataSize
- SizeOf(Byte)But that is now your only problem,
Your _MyData structure is 8 bytes long. So when you call
for any key value longer than 8 bytes, you have some buffer overflow happening. Even if you read a key shorter than 8 bytes, EncData will contain garbage.
I'd rather go this way:
I added the
packed
keyword to the record definition as I would believe it's the most likely way it should be declared... But then, it's all up to the specifications of the application writting the values. I also declared EncData asArray[0..MaxInt] of byte
. That's makes it a VERY bad idea to declare a variable of type _MyData, but is the only way I know of allowing a variable array in a record without forcing to disable range checking. If always running with Range Checking = False in your project is not a concerned (Or you don't mind switching it on/off where needed in your code), you could declare it asArray[0..0] of byte
. I know it should work, I don't know the specifics of that method as I never really used it.EDIT (After being accepted):
Actually, declaring EncData as
Array of byte
is just as bad as declaring it as PByte.Array of byte
is a reference type (effectively a 4 bytes pointer). If you try to access EncByte[0] in your code, you'll most likely get an AV. The only reason it works is that you only use@myData.encData
(and the fact the record is allocated using GetMem instead). But used like this, you could declare EncData like this :and this would still work, as, in your example, you don't really care about the type EncData is declared, you just care about the memory address where it is.
我觉得很奇怪,第二个字段是“PByte”值。它存储一个指针吗?只要进程正在运行,指针就有效,除非它在程序运行时仅存储临时值(如果是这样,那就是奇怪的选择)。或者它存储了该值指向的任意长度的缓冲区?如果它首先存储类型值,然后存储缓冲区,则缓冲区大小只是 ValueSize - SizeOf(Byte),除非记录未打包并且直接从内存“转储”,否则可能会有一些填充字节。
It looks strange to me the second field is a "PByte" value. Is it storing a pointer? A pointer is valid as long as a process is running, unless it is storing just a temporary value while the program is running (if so, strange choice). Or is it storing an arbitrary length buffer pointed to by that value? If it's storing first the type value, then the buffer, the buffer size is simply ValueSize - SizeOf(Byte), unless the record is not packed and it is "dumped" directly from memory, then there could be some filler bytes.