📄 protectdata.c
字号:
/*
* Copyright 2005 Kees Cook <kees@outflux.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* The Win32 CryptProtectData and CryptUnprotectData functions are meant
* to provide a mechanism for encrypting data on a machine where other users
* of the system can't be trusted. It is used in many examples as a way
* to store username and password information to the registry, but store
* it not in the clear.
*
* The encryption is symmetric, but the method is unknown. However, since
* it is keyed to the machine and the user, it is unlikely that the values
* would be portable. Since programs must first call CryptProtectData to
* get a cipher text, the underlying system doesn't have to exactly
* match the real Windows version. However, attempts have been made to
* at least try to look like the Windows version, including guesses at the
* purpose of various portions of the "opaque data blob" that is used.
*
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "wincrypt.h"
#include "winreg.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
#define CRYPT32_PROTECTDATA_PROV PROV_RSA_FULL
#define CRYPT32_PROTECTDATA_HASH_CALG CALG_MD5
#define CRYPT32_PROTECTDATA_KEY_CALG CALG_RC2
#define CRYPT32_PROTECTDATA_SALT_LEN 16
static const BYTE crypt32_protectdata_secret[] = {
'I','\'','m',' ','h','u','n','t','i','n','g',' ',
'w','a','b','b','i','t','s',0
};
/*
* The data format returned by the real Windows CryptProtectData seems
* to be something like this:
DWORD count0; - how many "info0_*[16]" blocks follow (was always 1)
BYTE info0_0[16]; - unknown information
...
DWORD count1; - how many "info1_*[16]" blocks follow (was always 1)
BYTE info1_0[16]; - unknown information
...
DWORD null0; - NULL "end of records"?
DWORD str_len; - length of WCHAR string including term
WCHAR str[str_len]; - The "dataDescription" value
DWORD unknown0; - unknown value (seems large, but only WORD large)
DWORD unknown1; - unknown value (seems small, less than a BYTE)
DWORD data_len; - length of data (was 16 in samples)
BYTE data[data_len]; - unknown data (fingerprint?)
DWORD null1; - NULL ?
DWORD unknown2; - unknown value (seems large, but only WORD large)
DWORD unknown3; - unknown value (seems small, less than a BYTE)
DWORD salt_len; - length of salt(?) data
BYTE salt[salt_len]; - salt(?) for symmetric encryption
DWORD cipher_len; - length of cipher(?) data - was close to plain len
BYTE cipher[cipher_len]; - cipher text?
DWORD crc_len; - length of fingerprint(?) data - was 20 byte==160b SHA1
BYTE crc[crc_len]; - fingerprint of record?
* The data structures used in Wine are modelled after this guess.
*/
struct protect_data_t
{
DWORD count0;
DATA_BLOB info0; /* using this to hold crypt_magic_str */
DWORD count1;
DATA_BLOB info1;
DWORD null0;
WCHAR * szDataDescr; /* serialized differently than the DATA_BLOBs */
DWORD unknown0; /* perhaps the HASH alg const should go here? */
DWORD unknown1;
DATA_BLOB data0;
DWORD null1;
DWORD unknown2; /* perhaps the KEY alg const should go here? */
DWORD unknown3;
DATA_BLOB salt;
DATA_BLOB cipher;
DATA_BLOB fingerprint;
};
/* this is used to check if an incoming structure was built by Wine */
static const char crypt_magic_str[] = "Wine Crypt32 ok";
/* debugging tool to print strings of hex chars */
static const char *
hex_str(const unsigned char *p, int n)
{
const char * ptr;
char report[80];
int r=-1;
report[0]='\0';
ptr = wine_dbg_sprintf("%s","");
while (--n >= 0)
{
if (r++ % 20 == 19)
{
ptr = wine_dbg_sprintf("%s%s",ptr,report);
report[0]='\0';
}
sprintf(report+strlen(report),"%s%02x", r ? "," : "", *p++);
}
return wine_dbg_sprintf("%s%s",ptr,report);
}
#define TRACE_DATA_BLOB(blob) do { \
TRACE("%s cbData: %u\n", #blob ,(unsigned int)((blob)->cbData)); \
TRACE("%s pbData @ %p:%s\n", #blob ,(blob)->pbData, \
hex_str((blob)->pbData, (blob)->cbData)); \
} while (0)
static
void serialize_dword(DWORD value,BYTE ** ptr)
{
/*TRACE("called\n");*/
memcpy(*ptr,&value,sizeof(DWORD));
*ptr+=sizeof(DWORD);
}
static
void serialize_string(const BYTE *str, BYTE **ptr, DWORD len, DWORD width,
BOOL prepend_len)
{
/*TRACE("called %ux%u\n",(unsigned int)len,(unsigned int)width);*/
if (prepend_len)
{
serialize_dword(len,ptr);
}
memcpy(*ptr,str,len*width);
*ptr+=len*width;
}
static
BOOL unserialize_dword(const BYTE *ptr, DWORD *index, DWORD size, DWORD *value)
{
/*TRACE("called\n");*/
if (!ptr || !index || !value) return FALSE;
if (*index+sizeof(DWORD)>size)
{
return FALSE;
}
memcpy(value,&(ptr[*index]),sizeof(DWORD));
*index+=sizeof(DWORD);
return TRUE;
}
static
BOOL unserialize_string(const BYTE *ptr, DWORD *index, DWORD size,
DWORD len, DWORD width, BOOL inline_len,
BYTE ** data, DWORD * stored)
{
/*TRACE("called\n");*/
if (!ptr || !data) return FALSE;
if (inline_len) {
if (!unserialize_dword(ptr,index,size,&len))
return FALSE;
}
if (*index+len*width>size)
{
return FALSE;
}
if (!(*data = CryptMemAlloc( len*width)))
{
return FALSE;
}
memcpy(*data,&(ptr[*index]),len*width);
if (stored)
{
*stored = len;
}
*index+=len*width;
return TRUE;
}
static
BOOL serialize(const struct protect_data_t *pInfo, DATA_BLOB *pSerial)
{
BYTE * ptr;
DWORD dwStrLen;
DWORD dwStruct;
TRACE("called\n");
if (!pInfo || !pInfo->szDataDescr || !pSerial ||
!pInfo->info0.pbData || !pInfo->info1.pbData ||
!pInfo->data0.pbData || !pInfo->salt.pbData ||
!pInfo->cipher.pbData || !pInfo->fingerprint.pbData)
{
return FALSE;
}
if (pInfo->info0.cbData!=16)
{
ERR("protect_data_t info0 not 16 bytes long\n");
}
if (pInfo->info1.cbData!=16)
{
ERR("protect_data_t info1 not 16 bytes long\n");
}
dwStrLen=lstrlenW(pInfo->szDataDescr);
pSerial->cbData=0;
pSerial->cbData+=sizeof(DWORD)*8; /* 8 raw DWORDs */
pSerial->cbData+=sizeof(DWORD)*4; /* 4 BLOBs with size */
pSerial->cbData+=pInfo->info0.cbData;
pSerial->cbData+=pInfo->info1.cbData;
pSerial->cbData+=(dwStrLen+1)*sizeof(WCHAR) + 4; /* str, null, size */
pSerial->cbData+=pInfo->data0.cbData;
pSerial->cbData+=pInfo->salt.cbData;
pSerial->cbData+=pInfo->cipher.cbData;
pSerial->cbData+=pInfo->fingerprint.cbData;
/* save the actual structure size */
dwStruct = pSerial->cbData;
/* There may be a 256 byte minimum, but I can't prove it. */
/*if (pSerial->cbData<256) pSerial->cbData=256;*/
pSerial->pbData=LocalAlloc(LPTR,pSerial->cbData);
if (!pSerial->pbData) return FALSE;
ptr=pSerial->pbData;
/* count0 */
serialize_dword(pInfo->count0,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* info0 */
serialize_string(pInfo->info0.pbData,&ptr,
pInfo->info0.cbData,sizeof(BYTE),FALSE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* count1 */
serialize_dword(pInfo->count1,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* info1 */
serialize_string(pInfo->info1.pbData,&ptr,
pInfo->info1.cbData,sizeof(BYTE),FALSE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* null0 */
serialize_dword(pInfo->null0,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* szDataDescr */
serialize_string((BYTE*)pInfo->szDataDescr,&ptr,
(dwStrLen+1)*sizeof(WCHAR),sizeof(BYTE),TRUE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* unknown0 */
serialize_dword(pInfo->unknown0,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* unknown1 */
serialize_dword(pInfo->unknown1,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* data0 */
serialize_string(pInfo->data0.pbData,&ptr,
pInfo->data0.cbData,sizeof(BYTE),TRUE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* null1 */
serialize_dword(pInfo->null1,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* unknown2 */
serialize_dword(pInfo->unknown2,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* unknown3 */
serialize_dword(pInfo->unknown3,&ptr);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* salt */
serialize_string(pInfo->salt.pbData,&ptr,
pInfo->salt.cbData,sizeof(BYTE),TRUE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* cipher */
serialize_string(pInfo->cipher.pbData,&ptr,
pInfo->cipher.cbData,sizeof(BYTE),TRUE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
/* fingerprint */
serialize_string(pInfo->fingerprint.pbData,&ptr,
pInfo->fingerprint.cbData,sizeof(BYTE),TRUE);
/*TRACE("used %u\n",ptr-pSerial->pbData);*/
if (ptr - pSerial->pbData != dwStruct)
{
ERR("struct size changed!? %u != expected %u\n",
ptr - pSerial->pbData, (unsigned int)dwStruct);
LocalFree(pSerial->pbData);
pSerial->pbData=NULL;
pSerial->cbData=0;
return FALSE;
}
return TRUE;
}
static
BOOL unserialize(const DATA_BLOB *pSerial, struct protect_data_t *pInfo)
{
BYTE * ptr;
DWORD index;
DWORD size;
BOOL status=TRUE;
TRACE("called\n");
if (!pInfo || !pSerial || !pSerial->pbData)
return FALSE;
index=0;
ptr=pSerial->pbData;
size=pSerial->cbData;
/* count0 */
if (!unserialize_dword(ptr,&index,size,&pInfo->count0))
{
ERR("reading count0 failed!\n");
return FALSE;
}
/* info0 */
if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
&pInfo->info0.pbData, &pInfo->info0.cbData))
{
ERR("reading info0 failed!\n");
return FALSE;
}
/* count1 */
if (!unserialize_dword(ptr,&index,size,&pInfo->count1))
{
ERR("reading count1 failed!\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -