Skip to content

Instantly share code, notes, and snippets.

@hasherezade
Last active March 6, 2024 02:32
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save hasherezade/2860d94910c5c5fb776edadf57f0bef6 to your computer and use it in GitHub Desktop.
Save hasherezade/2860d94910c5c5fb776edadf57f0bef6 to your computer and use it in GitHub Desktop.
AES 128 - encrypt/decrypt using Windows Crypto API
#include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
#define AES_KEY_SIZE 16
#define IN_CHUNK_SIZE (AES_KEY_SIZE * 10) // a buffer must be a multiple of the key size
#define OUT_CHUNK_SIZE (IN_CHUNK_SIZE * 2) // an output buffer (for encryption) must be twice as big
//params: <input file> <output file> <is decrypt mode> <key>
int wmain(int argc, wchar_t *argv[])
{
if (argc < 4) {
printf("params: <input file> <output file> <is decrypt mode> [*key]\n");
system("pause");
return 0;
}
wchar_t *filename = argv[1];
wchar_t *filename2 = argv[2];
wchar_t default_key[] = L"3igcZhRdWq96m3GUmTAiv9";
wchar_t *key_str = default_key;
BOOL isDecrypt = FALSE;
if (argv[3][0] > '0') {
printf("Decrypt mode\n");
isDecrypt = TRUE;
}
if (argc >= 5) {
key_str = argv[4];
}
const size_t len = lstrlenW(key_str);
const size_t key_size = len * sizeof(key_str[0]); // size in bytes
printf("Key: %S\n", key_str);
printf("Key len: %#x\n", len);
printf("Key size: %#x\n", key_size);
printf("Input File: %S\n", filename);
printf("Output File: %S\n", filename2);
printf("----\n");
HANDLE hInpFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hInpFile == INVALID_HANDLE_VALUE) {
printf("Cannot open input file!\n");
system("pause");
return (-1);
}
HANDLE hOutFile = CreateFileW(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOutFile == INVALID_HANDLE_VALUE) {
printf("Cannot open output file!\n");
system("pause");
return (-1);
}
if (isDecrypt) {
printf("DECRYPTING\n");
}
else {
printf("ENCRYPTING\n");
}
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();
printf("CryptAcquireContext failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
dwStatus = GetLastError();
printf("CryptCreateHash failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
if (!CryptHashData(hHash, (BYTE*)key_str, key_size, 0)) {
DWORD err = GetLastError();
printf("CryptHashData Failed : %#x\n", err);
system("pause");
return (-1);
}
printf("[+] CryptHashData Success\n");
HCRYPTKEY hKey;
if (!CryptDeriveKey(hProv, CALG_AES_128, hHash, 0, &hKey)) {
dwStatus = GetLastError();
printf("CryptDeriveKey failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
printf("[+] CryptDeriveKey Success\n");
const size_t chunk_size = isDecrypt ? IN_CHUNK_SIZE: OUT_CHUNK_SIZE;
BYTE *chunk = new BYTE[chunk_size];
DWORD out_len = 0;
BOOL isFinal = FALSE;
DWORD readTotalSize = 0;
DWORD inputSize = GetFileSize(hInpFile, NULL);
while (bResult = ReadFile(hInpFile, chunk, IN_CHUNK_SIZE, &out_len, NULL)) {
if (0 == out_len) {
break;
}
readTotalSize += out_len;
if (readTotalSize >= inputSize) {
isFinal = TRUE;
printf("Final chunk set, len: %d = %x\n", out_len, out_len);
}
if (isDecrypt) {
if (!CryptDecrypt(hKey, NULL, isFinal, 0, chunk, &out_len)) {
printf("[-] CryptDecrypt failed: %x\n", GetLastError());
break;
}
}
else {
if (!CryptEncrypt(hKey, NULL, isFinal, 0, chunk, &out_len, chunk_size)) {
printf("[-] CryptEncrypt failed: %x\n", GetLastError());
break;
}
}
DWORD written = 0;
if (!WriteFile(hOutFile, chunk, out_len, &written, NULL)) {
printf("writing failed!\n");
break;
}
memset(chunk, 0, chunk_size);
}
delete[]chunk; chunk = NULL;
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
CryptReleaseContext(hProv, 0);
CloseHandle(hInpFile);
CloseHandle(hOutFile);
printf("Finished. Processed %#x bytes.\n", readTotalSize);
return 0;
}
@shekohex
Copy link

it didn't work for me !
i get the empty file with 0kb size

@0x410c
Copy link

0x410c commented Nov 28, 2017

Gives Error 234, which boils down to,
If the buffer allocated for pbData is not large enough to hold the data, GetLastError returns ERROR_MORE_DATA and stores the required buffer size, in bytes, in the DWORD value pointed to by pdwDataLen.

either delete this gist or correct it.
nice work on malwares though. 💃

@hasherezade
Copy link
Author

Wow, I didn't knew that someone is using it. It's my old, long-forgotten note. I will fix it.

@hasherezade
Copy link
Author

@0x410c - can you elaborate on the error that you was getting? does it persist in the new version?

@g4hsean
Copy link

g4hsean commented Apr 13, 2018

You are using a block size of 128 bytes when it is really 128 bits or 16 bytes. Not sure how your implementation impacts the security of the encrypted data but I am sure there must be issues.

@400lbhacker
Copy link

yo this is a really nice source. I got this to work on both visual studio 6.0 (1998) and visual studio 2017 /w 10 sdk...
I have tried many MANY sources/tutorials/demos/samples/ even MSDN documentation and none of it worked, this worked and not only did it work does so in only 142 lines of code! I had the original AES Rijndael code its someting like 1379 lines and had to manually change strings to bytes to hex and do the padding.. but this is very very clean. once again thankyou so much Your other sources are pretty beast as well (NTDDK is new for me) and I am learning.

Copy link

ghost commented Feb 28, 2019

Excuse me, @hasherezade . I'm very new to coding anything. I tried your code, and then found this:
[error: 'CALG_SHA_256' was not declared in this scope]
How do you fix this in the code? Again, very new to coding. Thx

@Hanan-Natan
Copy link

In CryptDecrypt you're using NULL in the second parameter but according to the documentation it should be 0. (Compiling with gcc gives a warning about it...)

@n0tdUck
Copy link

n0tdUck commented Aug 22, 2020

What should I put at "is decrypt mode" param?

@hasherezade
Copy link
Author

What should I put at "is decrypt mode" param?

"is decrypt mode" is a flag that switches between decryption and encryption. if it is set to 0, the supplied file will be encrypted, otherwise it will be decrypted.

@Cirn09
Copy link

Cirn09 commented Aug 21, 2021

Wrong order for releasing
right order:

    CryptDestroyHash(hHash);
    CryptDestroyKey(hKey);
    CryptReleaseContext(hProv, 0);

ref: https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program-encrypting-a-file

@YonPog
Copy link

YonPog commented Nov 21, 2021

Very nice source and example, but I noticed a flaw - The user-supplied key is treated as a wchar_t, and its length is calculated appropriately by lstrlenW, but CryptHashData treats its argument as a byte array and treats the len parameter as the number of bytes (https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-crypthashdata), thus hashes only half of the key.
For example, if the user typed abcd, the actual data in memory is a\x00b\x00c\x00d\x00 with length 4 from lstrlenW, but CryptHashData gets passed 4 as the number of bytes, and will only hash a\x00b\x00.
This weakens the security of the encryption, as it halves the number of bits needed to bruteforce the user-supplied key (exponential factor).

@hasherezade
Copy link
Author

@YonPog - you are right, thank you for noticing it! fixed

@Petern15
Copy link

It works fine but it didn't work with RSA key pair. Any ideas?

@Artorios
Copy link

Artorios commented Aug 29, 2023

maybe it will be useful to someone: an example of decryption in python:

import hashlib
from Crypto.Cipher import AES

def hash_md5(data):
    md5_obj = hashlib.md5()
    md5_obj.update(data)
    return md5_obj.digest()

def mscrypt_derive_key_md5(password):
    buf1 = bytearray([0x36] * 64)
    buf2 = bytearray([0x5C] * 64)

    hash_obj = hashlib.md5()
    hash_obj.update(password)
    hash_result = hash_obj.digest()

    buf1[:len(hash_result)] = [buf1[i] ^ hash_result[i] for i in range(len(hash_result))]
    buf2[:len(hash_result)] = [buf2[i] ^ hash_result[i] for i in range(len(hash_result))]

    hash_buf1 = hash_md5(buf1)
    hash_buf2 = hash_md5(buf2)
    
    return hash_buf1 + hash_buf2

input_file = r'D:\enc.dat'
output_file = r'D:\dec.dat'

with open(input_file, 'rb') as f:
    encrypted_data = f.read()

base_password = "3igcZhRdWq96m3GUmTAiv9"
my_derived_key = mscrypt_derive_key_md5(base_password.encode('utf-8'))

print("My Derived Key:\t\t", my_derived_key.hex())

cipher = AES.new(my_derived_key, AES.MODE_CBC, iv=b'\x00'*16)
decrypted_data = cipher.decrypt(encrypted_data)

with open(output_file, 'wb') as f:
    f.write(decrypted_data)

P.S in example used md5 hash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment