我想使用CryptUnprotectData
& CryptProtectData
中的Crypt32.dll
。
我的代码是:
unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type TForm1 = class(TForm)
btn1: TButton;
btn2: TButton;
edt1: TEdit;
procedure btn1Click(Sender: TObject);
procedure btn2Click(Sender: TObject); private
{ Private declarations } public
{ Public declarations } end;
var Form1: TForm1; const CRYPTPROTECT_LOCAL_MACHINE = 4 ;
type TLargeByteArray = array [0..Pred(MaxInt)] of byte; PLargeByteArray = ^TLargeByteArray;
_CRYPTOAPI_BLOB = packed record
cbData: DWORD;
pbData: PByte; end; TCryptoApiBlob = _CRYPTOAPI_BLOB; PCrypyoApiBlob = ^TCryptoApiBlob; CRYPT_INTEGER_BLOB =
_CRYPTOAPI_BLOB; PCRYPT_INTEGER_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_UINT_BLOB = _CRYPTOAPI_BLOB; PCRYPT_UINT_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_OBJID_BLOB = _CRYPTOAPI_BLOB; PCRYPT_OBJID_BLOB = ^CRYPT_INTEGER_BLOB; CERT_NAME_BLOB =
_CRYPTOAPI_BLOB; PCERT_NAME_BLOB = ^CRYPT_INTEGER_BLOB; CERT_RDN_VALUE_BLOB = _CRYPTOAPI_BLOB; PCERT_RDN_VALUE_BLOB = ^CRYPT_INTEGER_BLOB; CERT_BLOB = _CRYPTOAPI_BLOB; PCERT_BLOB = ^CRYPT_INTEGER_BLOB; CRL_BLOB =
_CRYPTOAPI_BLOB; PCRL_BLOB = ^CRYPT_INTEGER_BLOB; DATA_BLOB = _CRYPTOAPI_BLOB; PDATA_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_DATA_BLOB = _CRYPTOAPI_BLOB; PCRYPT_DATA_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_HASH_BLOB =
_CRYPTOAPI_BLOB; PCRYPT_HASH_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_DIGEST_BLOB = _CRYPTOAPI_BLOB; PCRYPT_DIGEST_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_DER_BLOB = _CRYPTOAPI_BLOB; PCRYPT_DER_BLOB = ^CRYPT_INTEGER_BLOB; CRYPT_ATTR_BLOB =
_CRYPTOAPI_BLOB; PCRYPT_ATTR_BLOB = ^CRYPT_INTEGER_BLOB;
type _CRYPTPROTECT_PROMPTSTRUCT = packed record
cbSize: DWORD;
dwPromptFlags: DWORD;
hwndApp: HWND;
szPrompt: LPCWSTR; end; TCryptProtectPromptStruct = _CRYPTPROTECT_PROMPTSTRUCT; PCryptProtectPromptStruct = ^TCryptProtectPromptStruct; CRYPTPROTECT_PROMPTSTRUCT =
_CRYPTPROTECT_PROMPTSTRUCT; PCRYPTPROTECT_PROMPTSTRUCT = ^_CRYPTPROTECT_PROMPTSTRUCT;
function CryptProtectData(pDataIn: PDATA_BLOB; szDataDescr: LPCWSTR {PWideChar}; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
function CryptUnprotectData(pDataIn: PDATA_BLOB; var ppszDataDescr: LPWSTR; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
implementation {$R *.DFM}
procedure FreeDataBlob(var Data: DATA_BLOB); begin if Assigned(Data.pbData) then
LocalFree(HLOCAL(Data.pbData)); FillChar(Data, SizeOf(DATA_BLOB), 0); end;
function GetDataBlobText(Data: DATA_BLOB): string; begin if (Data.cbData > 0) and Assigned(Data.pbData) then
SetString(Result, PChar(Data.pbData), Data.cbData) else
SetLength(Result, 0); end;
function SetDataBlobText(Text: string; var Data: DATA_BLOB): boolean; begin FillChar(Data, SizeOf(DATA_BLOB), 0); if (Length(Text) > 0) then begin
Data.pbData := Pointer(LocalAlloc(LPTR, Succ(Length(Text))));
if Assigned(Data.pbData) then
begin
StrPCopy(PChar(Data.pbData), Text);
Data.cbData := Length(Text);
Result := True;
end
else
Result := False; end else
Result := True; end;
{============================================ }
function EncryptPassword(Password: string): string; var DataIn: DATA_BLOB; dwFlags: DWORD; DataOut: PDATA_BLOB; I: Integer; P: PByte; begin Result := ''; DataIn.cbData := Length(Password); DataIn.pbData := Pointer(PChar(Password)); dwFlags := CRYPTPROTECT_LOCAL_MACHINE; if CryptProtectData(@DataIn, 'Password', nil, nil, nil, dwFlags, DataOut) then begin
P := DataOut.pbData;
I := DataOut.cbData;
Result := IntToHex(I, 8);
while (I > 0) do
begin
Dec(I);
Result := Result + IntToHex(P^, 2);
Inc(P);
end;
LocalFree(Cardinal(DataOut.pbData)); end; end;
function DecryptPassword(Password: string): string; var DataIn: DATA_BLOB; dwFlags: DWORD; DataOut: PDATA_BLOB; I, J: Integer; P: PByte; DataDescr: LPWSTR; begin Result := ''; if (Length(Password) > 0) then begin
DataIn.cbData := StrToIntDef('$' + Copy(Password, 1, 8), 0);
if (DataIn.cbData > 0) then
begin
GetMem(DataIn.pbData, DataIn.cbData);
I := DataIn.cbData;
J := 9;
P := DataIn.pbData;
while (I > 0) and (J < Length(Password)) do
begin
Dec(I);
P^ := StrToInt('$' + Copy(Password, J, 2));
Inc(P);
Inc(J, 2);
end;
dwFlags := CRYPTPROTECT_LOCAL_MACHINE;
if CryptUnprotectData(@DataIn, DataDescr, nil, nil, nil, dwFlags, DataOut) then
begin
Result := Copy(string(DataOut.pbData), 0, DataOut.cbData);
LocalFree(Cardinal(DataOut.pbData));
end;
end; end; end;
procedure TForm1.btn1Click(Sender: TObject); var DataIn: DATA_BLOB; DataOut: DATA_BLOB; DataCheck: DATA_BLOB; lpwszDesc: PWideChar; begin FillChar(DataIn, SizeOf(DATA_BLOB), 0); FillChar(DataOut, SizeOf(DATA_BLOB), 0); FillChar(DataCheck, SizeOf(DATA_BLOB), 0); if SetDataBlobText('Hello world this is a test!', DataIn) then begin
try
if CryptProtectData(@DataIn, PWideChar(WideString('Hello Test')), nil, nil, nil, 0, @DataOut) then
begin
MessageBox(0, PChar(GetDataBlobText(DataOut)), PChar(Format('%d bytes returned', [DataOut.cbData])), MB_OK or MB_ICONINFORMATION);
try
if CryptUnprotectData(@DataOut, lpwszDesc, nil, nil, nil, 0, @DataCheck) then
begin
try
MessageBox(0, PChar(GetDataBlobText(DataCheck)), PChar(string(WideString(lpwszDesc))), MB_OK or MB_ICONINFORMATION);
finally
LocalFree(HLOCAL(lpwszDesc));
FreeDataBlob(DataCheck);
end;
end;
finally
FreeDataBlob(DataIn);
end;
end;
finally
FreeDataBlob(DataIn);
end; end;
end;
procedure TForm1.btn2Click(Sender: TObject); begin ShowMessage(DecryptPassword(edt1.Text)); end;
end.
但我在2个按钮中有错误,无法获得真正的字符串。
btn1错误:
---------------------------
Project1
---------------------------
Access violation at address 76F2E23E in module 'ntdll.dll'. Read of address 22481A56.
---------------------------
OK
---------------------------
btn2解密后显示null并显示此错误:
---------------------------
Project1
---------------------------
Access violation at address 00000000. Read of address 00000000.
---------------------------
OK
---------------------------
有什么问题?
答案 0 :(得分:3)
您的代码已格式化,几乎无法读取和分析。如果它的格式更好,那么我认为你会得到更全面的答案。这是我能看到的:
CryptUnprotectData
的第二个参数不应该是var参数。通过使其成为var
参数,您可以强制自己传递它。由于您不想使用它,您应该将其声明为指向PWideChar
的指针,以便您可以选择不使用它。btn1Click
中,您未向lpwszDesc
分配任何内容。你还有什么把它传递给LocalFree
。WideString
。只需将string
(已经是UTF-16)投射到PWideChar
。SizeOf(Char)
为2。所以你对错误的cbData
的处理。DecryptPassword
传递了一个指向CryptUnprotectData
的单位指针。DecryptPassword
泄漏了使用GetMem
分配的内存。DecryptPassword
正在尝试做什么,但它明显被打破了。我无法修复它,因为我不知道你的目标是什么。但是,我确定还有更多问题。我有一些一般性的建议。问题中的代码太多了。你应该尽可能地删除。你应该尽可能小SSCCE。这应该是一个简单的控制台应用程序代码应格式化为可读,并且最好不要求助于水平滚动。这将对您有所帮助。
重点是您正在搜索错误。如果您将代码缩减到尽可能简单,那么检查的代码就更少了。如果代码可见并整齐排列,则更容易检查。
尽管获得正确的具体细节,知道如何使代码可读和简洁的一般原则在这里更为重要。
所以,只是为了告诉你我的意思,这里是你原来的帖子转换成了一个SSCCE,并修复了一些错误:
program SO17823083;
{$APPTYPE CONSOLE}
uses
System.SysUtils, Winapi.Windows;
const
CRYPTPROTECT_LOCAL_MACHINE = 4;
type
TLargeByteArray = array [0 .. Pred(MaxInt)] of byte;
PLargeByteArray = ^TLargeByteArray;
_CRYPTOAPI_BLOB = record
cbData: DWORD;
pbData: PByte;
end;
DATA_BLOB = _CRYPTOAPI_BLOB;
PDATA_BLOB = ^DATA_BLOB;
type
_CRYPTPROTECT_PROMPTSTRUCT = record
cbSize: DWORD;
dwPromptFlags: DWORD;
hwndApp: HWND;
szPrompt: PWideChar;
end;
CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT;
PCRYPTPROTECT_PROMPTSTRUCT = ^CRYPTPROTECT_PROMPTSTRUCT;
function CryptProtectData(pDataIn: PDATA_BLOB;
szDataDescr: PWideChar; pOptionalEntropy: PDATA_BLOB;
pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD;
pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
function CryptUnprotectData(pDataIn: PDATA_BLOB; ppszDataDescr: PPWideChar;
pOptionalEntropy: PDATA_BLOB; pReserved: Pointer;
pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD;
pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
procedure FreeDataBlob(var Data: DATA_BLOB);
begin
if Assigned(Data.pbData) then
LocalFree(HLOCAL(Data.pbData));
FillChar(Data, SizeOf(DATA_BLOB), 0);
end;
function GetDataBlobText(Data: DATA_BLOB): string;
begin
SetString(Result, PChar(Data.pbData), Data.cbData div SizeOf(Char))
end;
function SetDataBlobText(const Text: string; var Data: DATA_BLOB): boolean;
begin
FillChar(Data, SizeOf(DATA_BLOB), 0);
if Length(Text) > 0 then
begin
Data.cbData := SizeOf(Char)*Length(Text);
Data.pbData := Pointer(LocalAlloc(LPTR, Data.cbData));
if Assigned(Data.pbData) then
begin
Move(Pointer(Text)^, Data.pbData^, Data.cbData);
Result := True;
end
else
Result := False;
end
else
Result := True;
end;
function DecryptPassword(Password: string): string;
var
DataIn: DATA_BLOB;
dwFlags: DWORD;
DataOut: DATA_BLOB;
I, J: Integer;
P: PByte;
begin
Result := '';
if (Length(Password) > 0) then
begin
DataIn.cbData := StrToIntDef('$' + Copy(Password, 1, 8), 0);
if (DataIn.cbData > 0) then
begin
GetMem(DataIn.pbData, DataIn.cbData);
I := DataIn.cbData;
J := 9;
P := DataIn.pbData;
while (I > 0) and (J < Length(Password)) do
begin
Dec(I);
P^ := StrToInt('$' + Copy(Password, J, 2));
Inc(P);
Inc(J, 2);
end;
dwFlags := CRYPTPROTECT_LOCAL_MACHINE;
if CryptUnprotectData(@DataIn, nil, nil, nil, nil, dwFlags, @DataOut)
then
begin
Result := GetDataBlobText(DataOut);
LocalFree(Cardinal(DataOut.pbData));
end;
FreeMem(DataIn.pbData);
end;
end;
end;
procedure Test1;
var
DataIn: DATA_BLOB;
DataOut: DATA_BLOB;
DataCheck: DATA_BLOB;
begin
if SetDataBlobText('Hello world this is a test!', DataIn) then
begin
try
if CryptProtectData(@DataIn, PChar('Hello Test'), nil,
nil, nil, 0, @DataOut) then
begin
Writeln(GetDataBlobText(DataOut));
Writeln(Format('%d bytes returned', [DataOut.cbData]));
try
if CryptUnprotectData(@DataOut, nil, nil, nil, nil, 0,
@DataCheck) then
begin
try
Writeln(GetDataBlobText(DataCheck));
finally
FreeDataBlob(DataCheck);
end;
end;
finally
FreeDataBlob(DataIn);
end;
end;
finally
FreeDataBlob(DataIn);
end;
end;
end;
procedure Test2;
begin
Writeln(DecryptPassword('1111'));
end;
begin
try
Test1;
Test2;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
我使用Delphi IDE的代码格式化功能以可读的方式放置代码。我将其转换为控制台应用程序,以便您拥有一个包含整个程序的文件。
此版本至少会运行而不会引发访问冲突。现在,由你决定让它真正做你想做的事。
答案 1 :(得分:2)
首先使用Jedi Apilib而不是自己转换(JwaWinCrypt),因为它们经过验证和时间测试。
在我的blog上是一小段示例代码。