在Delphi xe10中尝试并且未能使用CryptProtectMemory / CryptUnprotectMemory

时间:2015-11-14 06:06:58

标签: windows delphi winapi

我尝试了下面的代码(和varients)没有任何成功,也没有找到如何从Delphi调用这些Windows函数的示例。任何线索都会非常感激。

CryptProtectMemory确实会产生一些可能加密的结果,但unprotect根本不会改变那个结果。我怀疑我做了一些愚蠢的事情,但我整天都找不到......

function WinMemEnc(PlnTxt: String): String;
var
  Enc: Pointer;
  j: Integer;
  EncSze: Cardinal;
  ws: String;
const
  CRYPTPROTECTMEMORY_SAME_PROCESS: Cardinal = 0;
  EncryptionBlockSize: Integer = 8;
begin
  if Length(PlnTxt) mod EncryptionBlockSize = 0 then
    j := Length(PlnTxt)
  else
    j := ((Length(PlnTxt) div 8) + 1) * 8;
    ws := StringofChar(' ', j);
    Move(PlnTxt[1], ws[1], j);
    Enc := Pointer(ws);
  EncSze := j * 2;
  if CryptProtectMemory(Enc, EncSze, CRYPTPROTECTMEMORY_SAME_PROCESS) then
  begin
    Setlength(Result, j);
    Move(Enc, Result[1], EncSze);
  end;
end;

function WinMemDcr(EncInp: String): String;
var
  Enc: Pointer;
  j: Integer;
  EncSze: Cardinal;
  ws: String;
const
  CRYPTPROTECTMEMORY_SAME_PROCESS: Cardinal = 0;
begin
  j := Length(EncInp);
  EncSze := j * 2;
  ws := EncInp;
  Enc := Pointer(ws);
  if CryptUnprotectMemory(Enc, EncSze, CRYPTPROTECTMEMORY_SAME_PROCESS) then
  begin
    Setlength(Result, j);
    Move(Enc, Result[1], EncSze);
  end;
end;

2 个答案:

答案 0 :(得分:0)

不测试它(纯粹来自代码的外观),我认为问题出在MOVE语句中:

Move(Enc, Result[1], EncSze);

您正在从指针位置移动数据 - 而不是从指针指向的数据移动数据。

你应该使用

Move(Enc^, Result[1], EncSze);

从指针指向POINTED TO的位置移动数据,而不是从指针本身移动数据。

澄清:Enc变量是 - 比如说 - 位于地址$ 12345678,您正在操作的数据位于地址$ 99999999

这意味着地址$ 12345678位于4个字节(99美元99美元99美元和99美元)。在地址$ 99999999处找到您正在操作的数据。

声明

Move(Enc, Result[1], EncSze);

因此将EncSize字节从地址$ 12345678移动到字符串变量Result的第一个字符。这是你不想要的,因为它只会移动$ 99的4个字节,然后是地址$ 1234567C以及之后的任何内容。

要从地址$ 99999999移动数据,您需要告诉编译器,您希望通过指针从POINTED TO位置移动数据,而不是从POINTER本身移动数据:

Move(Enc^, Result[1], EncSze);

但除此之外,我同意大卫的观点。您应该停止使用字符串作为非字符串数据的存储。它会在某个时刻咬你的**。改为使用字节数组(TBytes)。

答案 1 :(得分:0)

您在我的资源库EncryptionBlockSize := 8;中设置了CRYPTPROTECTMEMORY_BLOCK_SIZE = 16。 您还错误地将输入字符串的一半移动到ws,因为j保持字符串的长度,而Move()移动Count字节Unicode Char为2个字节。

如评论中所述,加密/解密对字节起作用,将加密存储在字符串中是一种潜在的灾难。

所以这是我建议在TBytes中加密/解密带有加密存储的字符串。

function MemEncrypt(const StrInp: String): TBytes;
begin
  Result := TEncoding.Unicode.GetBytes(StrInp);
  if Length(Result) mod CRYPTPROTECTMEMORY_BLOCK_SIZE <> 0 then
    SetLength(Result, ((Length(Result) div CRYPTPROTECTMEMORY_BLOCK_SIZE) + 1) * CRYPTPROTECTMEMORY_BLOCK_SIZE);
  if not CryptProtectMemory(Result, Length(Result), CRYPTPROTECTMEMORY_SAME_PROCESS) then
    raise Exception.Create('Error Message: '+IntToStr(GetLastError));
end;

function MemDecrypt(const EncInp: TBytes): String;
var
  EncTmp: TBytes;
begin
  EncTmp := Copy(EncInp);
  if CryptUnprotectMemory(EncTmp, Length(EncTmp), CRYPTPROTECTMEMORY_SAME_PROCESS) then
    result := TEncoding.Unicode.GetString(EncTmp)
  else
    raise Exception.Create('Error Message: '+IntToStr(GetLastError));
end;

在解密中,输入TBytes的副本用于保存加密数据。

最后是一个测试程序:

procedure TForm13.Button2Click(Sender: TObject);
const
  Txt = '1234567890123456789012345678901';
var
  s: string;
  b: TBytes;
begin
  s := Txt;
  Memo1.Lines.Add(s);

  b := MemEncrypt(Txt);

  s := MemDecrypt(b);
  Memo1.Lines.Add(s);
end;