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;
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
packedkeyword 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 byteis just as bad as declaring it as PByte.Array of byteis 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.