secure_memory.cpp
Top


#include <windows.h>
#include "secure_memory.hpp"
#include <stdlib.h>

static int key_counter=1;
struct encryption_key
{
unsigned char key_data[4];
encryption_key()
{
unsigned pid=GetCurrentProcessId();
((unsigned int *)key_data)[0]=(pid+key_counter) * 0x89273228 + 0x928474f;
key_counter++;
}

};


// Housekeeping data that we are storing securely in memory
struct secure_data_info
{
unsigned int checksum; // holds checksum of data1 & data2 buffers
unsigned int data_size; // size fo data we have saved
void *data1; // location of first copy
void *data2;
encryption_key key1; // encryption key for data stored at data1
encryption_key key2; // encryption key for data stored at data2
};

// This provides a simple but effective means of encrypting memory contents so that
// hackers cannot scan memory looking for specific values
void xor_block(void *src, void *dst, int size, encryption_key &key)
{
for (int i=0; i<size; i++)
((unsigned char *)dst)[i] = ((unsigned char *)src)[i] ^ (key.key_data[i % (sizeof(key.key_data))] + i);
}

// This will allocate memory at a random location in memory.
// This function should return a different address everytime it is called, and not
// be the same across two executions of your program
void *allocate_at_random_location(int size)
{
SYSTEMTIME t;
GetSystemTime(&t);
unsigned int pad_size=t.wMilliseconds + GetCurrentProcessId() * key_counter;
key_counter++;
pad_size=(pad_size & 0xfff)+4;
void *v=malloc(size + pad_size);
void *ret=(char *)v+pad_size;
*((void **)ret-1)=v;
return ret;
}

// Since allocate_at_random_location returns a pointer cannot be supplied directly to free, we use this function
// to free the memory
void free_randomly_allocated_memory(void *ptr)
{
void *start=*((void **)ptr-1);
free(start);
}

// Simple function to calculate the checksum on a buffer
// We use this function to verify our data has not changed
unsigned int calc_checksum(void *data, int size)
{
unsigned char a=0,b=0,c=0,d=0;
for (int i=0; i<size; i++)
{
a+=*((unsigned char *)data+i);
b+=a;
c+=b;
d+=c;
}
return a | (b<<8) | (c<<16) | (d<<24);
}

static secure_data_info *secure_store_ptr=0;
static encryption_key ptr_key;


// This will free up data allocated to store secure data, this is automatically
// invoked if store_secure_data is called more than once
void free_secure_data()
{
if (secure_store_ptr)
{
secure_data_info header;
xor_block(secure_store_ptr, &header, sizeof(secure_data_info), ptr_key);
free_randomly_allocated_memory(secure_store_ptr);
free_randomly_allocated_memory(header.data1);
free_randomly_allocated_memory(header.data2);
secure_store_ptr=0;
}
}


// store_secure_data will save a copy of sensitive data in memory at 2 locations
// both copies will be encrypted with different keys. The checksum for each copy
// is also saved. The location for both copies of the data is futher encrypted
// with a different key
// This function will also erase the buffer you supplied when it finishes as a saftey measure
void store_secure_data(void *data, int size)
{
free_secure_data();

secure_store_ptr=(secure_data_info *)allocate_at_random_location(sizeof(secure_data_info));

secure_data_info header;
void *new_data1=allocate_at_random_location(size);
void *new_data2=allocate_at_random_location(size);
xor_block(data, new_data1, size, header.key1);
xor_block(data, new_data2, size, header.key2);

header.checksum=calc_checksum(data, size);
header.data_size=size;
header.data1=new_data1;
header.data2=new_data2;

xor_block(&header, secure_store_ptr, sizeof(secure_data_info), ptr_key);

memset(data, 0, size); // clear memory so there is no longer a trace of it in memory
memset(&header, 0, sizeof(header));
}


// This function will crash the current program in a somewhat subtle manner
// It gets the address of the program entry point and runs the program again


// Eventually the program should crash or have a stack overflow
// We avoid showing a dialog box or quiting immediately to make it hard to
// locate this code
inline void subtle_crash_program()
{
IMAGE_DOS_HEADER *dh=(IMAGE_DOS_HEADER *)GetModuleHandle(0);
IMAGE_OPTIONAL_HEADER *oh=(IMAGE_OPTIONAL_HEADER *)((char *)dh+dh->e_lfanew+4+sizeof(IMAGE_FILE_HEADER));
unsigned long entry=oh->AddressOfEntryPoint + (unsigned int)dh;
_asm jmp [entry]
}

// This function copies previously stored secure data to the address specified
// The buffer specified by store_to should be the same size as the buffer supplied to store_secure_data
void get_secure_data(void *store_to)
{
secure_data_info header;
xor_block(secure_store_ptr, &header, sizeof(secure_data_info), ptr_key);

void *data1=malloc(header.data_size);
void *data2=malloc(header.data_size);

xor_block(header.data1, data1, header.data_size, header.key1);
xor_block(header.data2, data2, header.data_size, header.key2);


// check data checksums
unsigned checksum1=calc_checksum(data1, header.data_size);
unsigned checksum2=calc_checksum(data2, header.data_size);

if (checksum1!=checksum2 || checksum1!=header.checksum)
subtle_crash_program();

memcpy(store_to, data1, header.data_size);
memset(data1, 0, header.data_size);
memset(data2, 0, header.data_size);
memset(&header, 0, header.data_size);
free(data1);
free(data2);
}