【问题标题】:How to get the size of encrypted data?如何获取加密数据的大小?
【发布时间】:2011-05-22 18:25:00
【问题描述】:

我有一个棘手的问题,我希望我能解释清楚, 我想从 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 是字符串常量。

所以,现在我在 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;

【问题讨论】:

  • 你从注册表中究竟读到了什么,这不就是 CRYPTOAPI_BLOB 记录吗?如果不是,数据在写入之前是否被修改过(例如序列化、某种 bintohex 等)?
  • 我想知道你怎么没有reg.Create..
  • @Sertac Akyuz 是的,你是对的,这是紧急任务 :) 它应该是 reg := TRegistry.Create;

标签: delphi pointers cryptography encryption


【解决方案1】:

数据大小为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 声明为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 声明的类型,您只关心它所在的内存地址。

【讨论】:

  • 当您将 someting 声明为 array[0..0] 时,您只需获得一种访问任意长度缓冲区的第一个字节的方法。该技巧避免了声明一个非常大的数组(即 MaxInt...)的问题,并且通常告诉开发人员需要以某种特殊方式访问数据,而如果您声明一个巨大的数组,则可以很容易地访问应该访问的数据'在没有错误的情况下访问。
  • @ldsandon 我使用 encData: 字节数组,它给了我正确的结果,这有什么副作用吗?
  • 您正在以这种方式使用动态数组,Delphi 会自动处理它,一旦编译器“魔术”代码尝试处理它,以这种方式使用它可能会导致非常奇怪的行为。使用“array[0.0] of Byte”,您将使用 GetMem() 分配内存,就像 Ken 的回答一样,这只是声明 MyData 类型的另一种方式。我更喜欢这种方式,因为它降低了访问错误内存的风险,但 Ken 代码也可以。使用动态数组声明不是可行的方法,它可能会带来麻烦。
  • @Robin-Hood:你只是幸运的 Array of Byte 作品……阅读我的编辑了解更多详情。
【解决方案2】:

我觉得第二个字段是“PByte”值很奇怪。它是否存储指针?只要进程正在运行,指针就有效,除非它在程序运行时仅存储临时值(如果是这样,奇怪的选择)。还是存储该值指向的任意长度缓冲区?如果它首先存储类型值,然后存储缓冲区,则缓冲区大小只是 ValueSize - SizeOf(Byte),除非记录没有打包并且直接从内存中“转储”,否则可能会有一些填充字节。

【讨论】:

  • 关于您对肯回答的评论:我使用 encData: 字节数组,它给了我正确的结果,这有什么副作用吗?
猜你喜欢
  • 2011-04-30
  • 1970-01-01
  • 2014-02-21
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
相关资源
最近更新 更多