CryptDecrypt() Failing to decrypt some blocks C++ - c++

I'm currently working on a simple encryption/decryption system in C++ using the Windows API.
I believe I've been successful at getting CryptEncrypt() to work (AES_128) for encrypting a file.
But when I Use CryptDecrypt() to decrypt the file, the first 16 bytes are corrupted and then after 4000 bytes (which is the size of the chunks I'm pulling from ReadFile() and encrypting) is another chunk of corrupted bytes and so on. If I try to decrypt a file with a total length less than 4000 bytes, the decryption works perfectly.
I'm very confused about why this is happening. There are no errors at all.
Here is a snippet of my code (I have CryptEncrypt() and CryptDecrypt() right after each other to save me exporting the key and to make the testing faster):
DWORD bytesRead;
DWORD bytesWritten;
DWORD pointer = 0;
unsigned int blockSize = 4000;
void *fileBuffer = new unsigned char[4106];
bool EOF = false;
do
{
SetFilePointer(hFileOrginal,pointer,0,0);
ReadFile(hFileOrginal,fileBuffer,blockSize,&bytesRead,NULL);
if(bytesRead<blockSize)
{
EOF=true;
}
CryptEncrypt(aesKey,NULL,EOF,0,(BYTE *)fileBuffer,&bytesRead,(blockSize+16));
CryptDecrypt(aesKey,NULL,EOF,0,(BYTE *)fileBuffer,&bytesRead);
WriteFile(hTempFile,fileBuffer,bytesRead,&bytesWritten,NULL);
pointer +=bytesRead;
}
while(!EOF);
delete[] fileBuffer;
I would really appreciate any suggestions about whats going wrong.
EDIT: On a 4704 bytes file I got the following using breakpoints.
First ReadFile bytesread 4000
First CryptEncrypt bytesRead 4000
First CryptDecrypt bytesRead 4000
Second ReadFile bytesread 704
Second CryptEncrypt bytesread 720
Second CryptDecrupt bytesread 704
Everything seems good with that yet I still get a problem.
I'm using the enhanced crypto api (With verifycontext) with a generated a single AES key with the CRYPT_EXPORTABLE property

You are not doing any error handling at all. All of the API functions you are calling have return values and error codes, none of which you are checking.
You are also not managing bytesRead correctly. CryptEncrypt() modifies the variable you pass to it, which then affects your call to CreateDecrypt(), which also modifies it, and that then affects subsequent calls to SetFilePointer(), which you should not be calling in your loop to begin with. You are not validating that you have as many bytes as you are expecting, or that bytesRead ends up back at the original value that ReadFile() returned, so you may end up skipping bytes in the source file.
Try something more like this instead:
bool ReadFromFile(HANDLE hFile, void *Buffer, DWORD BufSize, DWORD *BytesRead)
{
if (BytesRead)
*BytesRead = 0;
LPBYTE pBuffer = (LPBYTE) Buffer;
DWORD dwRead;
while (BufSize > 0)
{
if (!ReadFile(hFile, pBuffer, BufSize, &dwRead, NULL))
return false;
if (dwRead == 0)
break;
pBuffer += dwRead;
BufSize -= dwRead;
if (BytesRead)
*BytesRead += dwRead;
}
return true;
}
bool WriteToFile(HANDLE hFile, void *Buffer, DWORD BufSize)
{
LPBYTE pBuffer = (LPBYTE) Buffer;
DWORD dwWritten;
while (BufSize > 0)
{
if (!WriteFile(hFile, pBuffer, BufSize, &dwWritten, NULL))
return false;
pBuffer += dwWritten;
BufSize -= dwWritten;
}
return true;
}
DWORD bytesRead;
const UINT blockSize = 4000;
LPBYTE fileBuffer = new BYTE[blockSize+16];
bool EOF;
if (SetFilePointer(hFileOrginal, 0, NULL, FILE_BEGIN) != 0)
{
errorCode = GetLastError();
...
}
else
{
do
{
if (!ReadFromFile(hFileOrginal, fileBuffer, blockSize, &bytesRead))
{
errorCode = GetLastError();
...
break;
}
EOF = (bytesRead < blockSize);
bytesEncrypted = bytesRead;
if (!CryptEncrypt(aesKey, NULL, EOF, 0, fileBuffer, &bytesEncrypted, blockSize+16))
{
errorCode = GetLastError();
...
break;
}
bytesDecrypted = bytesEncrypted;
if (!CryptDecrypt(aesKey, NULL, EOF, 0, fileBuffer, &bytesDecrypted))
{
errorCode = GetLastError();
...
break;
}
if (!WriteToFile(hTempFile, fileBuffer, bytesDecrypted))
{
errorCode = GetLastError();
...
break;
}
if (bytesDecrypted != bytesRead)
{
...
break;
}
}
while (!EOF);
}
delete[] fileBuffer;

Related

Converting char to wchar_t

I'm trying to modify this function https://stackoverflow.com/a/35658917/19324589
from CStringA (i have no idea what data type is it) to std::wstring
char buf[1024] to wstring buf
'original' function:
char buf[1024];
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // No data available, return
break;
if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
// Error, the child process might ended
break;
buf[dwRead] = 0;
strResult += buf;
my attempt:
when i modify buf from char to wstring, it stores only garbage after the ReadFile line.
std::wstring strResult;
// ...
std::wstring buf(1024, '\0');
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // No data available, return
break;
if (!::ReadFile(hPipeRead, buf.data(), min((buf.size() * sizeof(wchar_t)), dwAvail), &dwRead, NULL) || !dwRead)
// Error, the child process might ended
break;
strResult += buf;
I end up doing:
bool bProcessEnded = false;
std::string result;
for (; !bProcessEnded;)
{
// Give some timeslice (50 ms), so we won't waste 100% CPU.
bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if
// there is some data available over pipe.
for (;;)
{
char buf[1024];
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // No data available, return
break;
if (!::ReadFile(hPipeRead, buf, min((sizeof(buf) - 1), dwAvail), &dwRead, NULL) || !dwRead)
// Error, the child process might ended
break;
buf[dwRead] = 0;
result += buf;
}
} //for
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return to_wstring(result);
In my use case buf is returning a data like already connected to 127.0.0.1:5555
I would like to understand what i'm doing wrong?

How to solve NTE_BAD_DATA using CryptDecrypt at final step

I am working on a encryption algo with c++ using WINAPI. My encryption works flawless and my decryption too untill the last chunk to be decrypted with final = TRUE. I got the NTE_BAD_DATA error.
PS : I manualy check the buffer and the decryption works fine untill the last CryptDecrypt.
If someone have an idea, kindly help me :)
Here is my code :
PVOID test(PVOID buffer, DWORD* length, PCHAR key_str2,bool isdecrypt) {
CHAR default_key[] = "3igcZhRdWq96m3GUmTAiv9";
CHAR* key_str = default_key;
size_t len = lstrlenA(key_str);
DWORD dwStatus = 0;
BOOL bResult = FALSE;
wchar_t info[] = L"Microsoft Enhanced RSA and AES Cryptographic Provider";
HCRYPTPROV hProv;
if (!CryptAcquireContextW(&hProv, NULL, info, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
dwStatus = GetLastError();
CryptReleaseContext(hProv, 0);
return 0;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
dwStatus = GetLastError();
CryptReleaseContext(hProv, 0);
return 0;
}
if (!CryptHashData(hHash, (BYTE*)key_str, len, 0)) {
DWORD err = GetLastError();
return 0;
}
HCRYPTKEY hKey;
if (!CryptDeriveKey(hProv, CALG_AES_128, hHash, 0, &hKey)) {
dwStatus = GetLastError();
CryptReleaseContext(hProv, 0);
return 0;
}
const size_t chunk_size = CHUNK_SIZE;
BYTE chunk[chunk_size] = { 0 };
DWORD out_len = 0;
BOOL isFinal = FALSE;
DWORD readTotalSize = 0;
DWORD inputSize = *length;
PVOID newBuff = VirtualAlloc(0, inputSize, MEM_COMMIT, PAGE_READWRITE);
while (true)
{
if (readTotalSize + chunk_size >= inputSize)
{
isFinal = TRUE;
memcpy(chunk, PVOID((DWORD)buffer + readTotalSize), inputSize - readTotalSize);
out_len = inputSize - readTotalSize;
}
else {
memcpy(chunk, PVOID((DWORD)buffer + readTotalSize), chunk_size);
out_len = chunk_size;
}
if (isdecrypt) {
if (!CryptDecrypt(hKey, NULL, isFinal, 0, chunk, &out_len)) {
int a = GetLastError();
break;
}
}
else {
if (!CryptEncrypt(hKey, NULL, isFinal, 0, chunk, &out_len, chunk_size)) {
break;
}
}
if (readTotalSize + chunk_size >= inputSize) {
memcpy(PVOID((DWORD)newBuff + readTotalSize), chunk, inputSize - readTotalSize);
readTotalSize += inputSize - readTotalSize;
}
else {
memcpy(PVOID((DWORD)newBuff + readTotalSize), chunk, chunk_size);
readTotalSize += chunk_size;
}
if (isFinal)
break;
memset(chunk, 0, chunk_size);
}
CryptReleaseContext(hProv, 0);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
return newBuff;
}
You are not storing off the encryption the final block correctly.
The amount of data you pass into the encryption is in out_len, and the amount of data that is encrypted is put back into out_len. For a block cypher that doesn't matter for whole blocks, but your last block which is less than a whole block being passed into encryption probably creates a whole block of encrypted data.
This means that your encrypted data is probably a little larger than your plain text in the final block, but you make no effort to handle this so on when you decrypt you have an issue as you've dropped some of the encrypted data on the floor.
Use the value put into out_len by the encryption function to store off your data (as you overwrite your data stream you'll need to make sure there's space though)

Reset handle after ReadFile

I'm trying to open a file on windows and check that the magic bytes match a windows PE32. If I run the code below and return just before the ReadFile call in the function problemFunction the code works fine and it prints 5a4d at the end of the main function. However if I return after the ReadFile call in problemFunction then I exit in the dos->e_magic != PIMAGE_DOS_HEADER check.
#include <Windows.h>
#include <winternl.h>
void problemFunction(HANDLE *fh) {
DWORD fileSize = GetFileSize(fh, NULL);
if (!fileSize) { CloseHandle(fh); exit(1); }
BYTE* pByte = new BYTE[fileSize];
DWORD dw;
ReadFile(*fh, pByte, fileSize, &dw, NULL);
// could be wrong but i think i need to run SetFilePointer here but not sure on what to do.
return;
}
int main() {
const char* filepath = "C:\\windows\\file\\path\\to\\exe";
HANDLE fh = CreateFileA(filepath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(fh == INVALID_HANDLE_VALUE) { CloseHandle(fh); exit(1); }
problemFunction(&fh);
DWORD fileSize = GetFileSize(fh, NULL);
if (!fileSize) { CloseHandle(fh); exit(1); }
BYTE* pByte = new BYTE[fileSize];
DWORD dw;
ReadFile(fh, pByte, fileSize, &dw, NULL);
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pByte;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) { CloseHandle(fh); exit(1); }
// dos->e_magic should be 5a4d for MZ, windows PE
}
I assume i need to reset the file pointer after the problemFunction read call with something like
LONG reset = -sizeof(DWORD);
SetFilePointer(*fh, reset, NULL, FILE_END);
But i can't get it to work.
Thanks
There are a number of problems with your code.
problemFunction() is taking a HANDLE* pointer as input, but it is not dereferencing that pointer when passing it to GetFileSize() or CloseHandle(). But it is dereferencing the pointer when passing it to ReadFile().
You must be compiling your code with STRICT Type Checking turned off, otherwise your code would fail to compile. You should always compile with STRICT enabled.
HANDLE is a pointer type, so there is no need to pass it around by pointer, unless you are going to modify its value, which this code is not doing. So you should change problemFunction() to take a HANDLE as-is rather than taking a HANDLE* pointer.
Also, GetFileSize() does not return 0 on failure, like your code assumes. It actually returns INVALID_FILE_SIZE which is -1, ie 0XFFFFFFFF as a DWORD. This is clearly stated in the documentation:
If the function fails and lpFileSizeHigh is NULL, the return value is INVALID_FILE_SIZE. To get extended error information, call GetLastError.
But, most importantly, your 2nd call to ReadFile() inside of main() does not read what you are expecting because the 1st call to ReadFile() inside of problemFunction() has already read the data (and leaked it!), but you are not seeking the HANDLE back to the beginning of the file after that read so the 2nd call to ReadFile() can read it again. You are correct that you need to use SetFilePointer() for that.
With that said, try something more like this:
#include <Windows.h>
#include <winternl.h>
bool test(HANDLE fh) {
DWORD fileSize = GetFileSize(fh, NULL);
if (fileSize == INVALID_FILE_SIZE) {
return false;
}
BYTE* pByte = new BYTE[fileSize];
DWORD dw;
if (!ReadFile(fh, pByte, fileSize, &dw, NULL)) {
delete[] pByte;
return false;
}
// use pByte as needed...
delete[] pByte;
if (SetFilePointer(fh, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
return false;
}
return true;
}
int main() {
const char* filepath = "C:\\windows\\file\\path\\to\\exe";
HANDLE fh = CreateFileA(filepath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fh == INVALID_HANDLE_VALUE) {
return 1;
}
if (!test(fh)) {
CloseHandle(fh);
return 1;
}
DWORD fileSize = GetFileSize(fh, NULL);
if (fileSize == INVALID_FILE_SIZE) {
CloseHandle(fh);
return 1;
}
BYTE* pByte = new BYTE[fileSize];
DWORD dw;
if (!ReadFile(fh, pByte, fileSize, &dw, NULL) || dw < sizeof(IMAGE_DOS_HEADER)) {
CloseHandle(fh);
return 1;
}
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pByte;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
delete[] pByte;
CloseHandle(fh);
return 1;
}
...
delete[] pByte;
CloseHandle(fh);
return 0;
}

WinApi: Cant read registry

Im trying to read a registry using the winapi and c++.
The code runs, but the result is not the contents of the registry
After a hexdump is just 0xCD repeated over and over. (So, as if the data hasnt been modified by RegQueryValueEx, and is just the result of the malloc)
I tried running as admin too, with no luck.
This is the code im using:
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\Shell\\Bags\\1\\Desktop", 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
return;
//Read & save
DWORD BufferSize = TOTALBYTES;
DWORD cbData;
DWORD dwRet;
LPBYTE data = (LPBYTE)malloc(BufferSize);
cbData = BufferSize;
DWORD type = REG_BINARY;
dwRet = RegQueryValueEx(hKey, "IconLayouts", NULL, &type, data, &cbData);
while (dwRet == ERROR_MORE_DATA) {
BufferSize += BYTEINCREMENT;
data = (LPBYTE)realloc(data, BufferSize);
cbData = BufferSize;
dwRet = RegQueryValueEx(hKey, "IconLayouts", NULL, &type, data, &cbData);
}
if (dwRet == ERROR_SUCCESS)
{
//Write current registry to a file
std::ofstream currentRegistryFile(DIRECTORY + currentDesktop + ".bin");
if (!currentRegistryFile) {
log(currentDesktop + " file couldn't be opened.");
return;
}
for (int i = 0; i < cbData; i++)
currentRegistryFile << (data)[cbData];
}
else
log("Couldnt read registry");
//Close registry
RegCloseKey(hKey);
Your saving code is the problem. It’s actually accessing the array out of bounds:
for (int i = 0; i < cbData; i++)
currentRegistryFile << (data)[cbData];
Note you’re indexing data with constant value of cbData and not loop variable i. Change that.

Calling SetupDiEnumDeviceInfo causes a subsequent CreateFile to return ERROR_SHARING_VIOLATION

In the following code the call to SetupDiEnumDeviceInfo() causes the subsequent CreateFile to return ERROR_SHARING_VIOLATION instead of opening the file. I was able to pinpoint the line by commenting out the other pieces of code until I hit one line that would cause the CreateFile to fail.
String SerialATDT::getComPortId()
{
#if 1
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
String comPort = "";
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MODEM,
0, // Enumerator
0,
DIGCF_PRESENT );
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return "";
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
int offset = 0;
while ( SetupDiEnumDeviceInfo(hDevInfo, offset++, &DeviceInfoData) )
{
DWORD DataT;
#if 1
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (LPTSTR)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
// Look for identifying info in the name
if ( mComPortIdentifier.size() > 0 ) {
const char *temp = strstr(buffer, mComPortIdentifier.c_str());
if ( temp == 0 ) {
continue;
}
}
// Now find out the port number
DWORD nSize=0 ;
TCHAR buf[MAX_PATH];
if ( SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, buf, MAX_PATH, &nSize) )
{
HKEY devKey = SetupDiOpenDevRegKey(hDevInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
DWORD size = 0;
DWORD type;
RegQueryValueEx(devKey, TEXT("PortName"), NULL, NULL, NULL, & size);
BYTE* buff = new BYTE[size];
String result;
if( RegQueryValueEx(devKey, TEXT("PortName"), NULL, &type, buff, & size) == ERROR_SUCCESS ) {
comPort = (char*)buff;
if ( comPort.size() > 0 ) {
RegCloseKey(devKey);
break;
}
}
RegCloseKey(devKey);
delete [] buff;
}
#else
comPort = "COM44";
#endif
}
// Cleanup
SetupDiDestroyDeviceInfoList (hDevInfo);
if (buffer) {
LocalFree(buffer);
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS &&
GetLastError() != ERROR_INVALID_HANDLE )
{
TRACE_L("ATDT error after free %ld", GetLastError() );
// Insert error handling here.
return "";
}
return comPort;
#else
return "COM44";
#endif
}
bool SerialATDT::getComPort(HANDLE *hFile)
{
String comPort = getComPortId();
*hFile = INVALID_HANDLE_VALUE;
if ( comPort.size() > 0 ) {
String comPortStr;
comPortStr.Format("\\\\.\\%s", comPort.c_str());
*hFile = ::CreateFile( comPortStr.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL );
if ( *hFile == INVALID_HANDLE_VALUE ) {
TRACE_L("AT file open error %ld", GetLastError());
}
}
return *hFile != INVALID_HANDLE_VALUE;
}
I have been looking but have not found a reason why the DeviceInfoData needs to be cleared (nor have I found a method to do it). Has anybody run into this before?