1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
#include "rar.hpp"
#ifdef _WIN_ALL
typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE
#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16
#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00
#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01
#endif
class CryptLoader
{
private:
HMODULE hCrypt;
bool LoadCalled;
public:
CryptLoader()
{
hCrypt=NULL;
pCryptProtectMemory=NULL;
pCryptUnprotectMemory=NULL;
LoadCalled=false;
}
~CryptLoader()
{
if (hCrypt!=NULL)
FreeLibrary(hCrypt);
hCrypt=NULL;
pCryptProtectMemory=NULL;
pCryptUnprotectMemory=NULL;
};
void Load()
{
if (!LoadCalled)
{
hCrypt = LoadLibraryW(L"Crypt32.dll");
if (hCrypt != NULL)
{
pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory");
pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory");
}
LoadCalled=true;
}
}
CRYPTPROTECTMEMORY pCryptProtectMemory;
CRYPTUNPROTECTMEMORY pCryptUnprotectMemory;
};
// We need to call FreeLibrary when RAR is exiting.
CryptLoader GlobalCryptLoader;
#endif
SecPassword::SecPassword()
{
CrossProcess=false;
Set(L"");
}
SecPassword::~SecPassword()
{
Clean();
}
void SecPassword::Clean()
{
PasswordSet=false;
cleandata(Password,sizeof(Password));
}
// When we call memset in end of function to clean local variables
// for security reason, compiler optimizer can remove such call.
// So we use our own function for this purpose.
void cleandata(void *data,size_t size)
{
#if defined(_WIN_ALL) && defined(_MSC_VER)
SecureZeroMemory(data,size);
#else
// 'volatile' is required. Otherwise optimizers can remove this function
// if cleaning local variables, which are not used after that.
volatile byte *d = (volatile byte *)data;
for (size_t i=0;i<size;i++)
d[i]=0;
#endif
}
// We got a complain from user that it is possible to create WinRAR dump
// with "Create dump file" command in Windows Task Manager and then easily
// locate Unicode password string in the dump. It is unsecure if several
// people share the same computer and somebody left WinRAR copy with entered
// password. So we decided to obfuscate the password to make it more difficult
// to find it in dump.
void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode)
{
// Source string can be shorter than destination as in case when we process
// -p<pwd> parameter, so we need to take into account both sizes.
memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst));
SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess);
}
void SecPassword::Get(wchar *Psw,size_t MaxSize)
{
if (PasswordSet)
{
Process(Password,ASIZE(Password),Psw,MaxSize,false);
Psw[MaxSize-1]=0;
}
else
*Psw=0;
}
void SecPassword::Set(const wchar *Psw)
{
if (*Psw==0)
{
PasswordSet=false;
memset(Password,0,sizeof(Password));
}
else
{
PasswordSet=true;
Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true);
}
}
size_t SecPassword::Length()
{
wchar Plain[MAXPASSWORD];
Get(Plain,ASIZE(Plain));
size_t Length=wcslen(Plain);
cleandata(Plain,ASIZE(Plain));
return Length;
}
bool SecPassword::operator == (SecPassword &psw)
{
// We cannot compare encoded data directly, because there is no guarantee
// than encryption function will always produce the same result for same
// data (salt?) and because we do not clean the rest of password buffer
// after trailing zero before encoding password. So we decode first.
wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD];
Get(Plain1,ASIZE(Plain1));
psw.Get(Plain2,ASIZE(Plain2));
bool Result=wcscmp(Plain1,Plain2)==0;
cleandata(Plain1,ASIZE(Plain1));
cleandata(Plain2,ASIZE(Plain2));
return Result;
}
void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
{
#ifdef _WIN_ALL
// Try to utilize the secure Crypt[Un]ProtectMemory if possible.
if (GlobalCryptLoader.pCryptProtectMemory==NULL)
GlobalCryptLoader.Load();
size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE;
DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS;
if (Encode)
{
if (GlobalCryptLoader.pCryptProtectMemory!=NULL)
{
if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags))
{
ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed");
ErrHandler.SysErrMsg();
ErrHandler.Exit(RARX_FATAL);
}
return;
}
}
else
{
if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL)
{
if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags))
{
ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed");
ErrHandler.SysErrMsg();
ErrHandler.Exit(RARX_FATAL);
}
return;
}
}
#endif
// CryptProtectMemory is not available, so only slightly obfuscate data.
uint Key;
#ifdef _WIN_ALL
Key=GetCurrentProcessId();
#elif defined(_UNIX)
Key=getpid();
#else
Key=0; // Just an arbitrary value.
#endif
for (size_t I=0;I<DataSize;I++)
*((byte *)Data+I)^=Key+I+75;
}
|