Login


Encrypting Source Code

By Jonathan Wood on 1/22/2011
Language: C++
Technology: MFC
Platform: Windows
License: CPOL
Views: 24,067
Frameworks & Libraries » MFC » General » Encrypting Source Code

Demo Project Screenshot

Download Source Code Download Source Code

Introduction

Some applications must contain sensitive text data. For example, an application might contain a hard-coded password in order to be able to remotely access a database. Since savvy users can browse the contents of your executables, having sensitive data embedded in your application can pose a risk.

As developers, it's fairly common to write code that performs some sort of encryption on our data. But encrypting data that is in the executable image is another matter. This data can be viewed before the code ever runs!

We can encrypt the text defined in our application. But it can be a little awkward to cut and paste strings into an encryption program and then back into our source code.

The SourceEncrypt Project

To make it as easy as possible to encrypt data within your executable, I created a small utility program in MFC called SourceEncrypt.

SourceEncrypt will read a C/C++ source or header file and look for special directives. You place these special directives within comments so that the utility knows what changes to make.

Let's start with an example of how to write these special directives into your source code. Listing 1 defines three directives. The first is $EncryptBegin(), which tells the utility to process the lines that follow. This directive also specifies an encryption key to ensure a unique encryption value.

Listing 1: Source Code Before Processing

// Here's some sample source code
// $EncryptBegin("EncryptionKey")
// $EncryptString(strName, "Value of my string")
// $EncryptEnd()

// More source code...

The next directive is $EncryptString(), which defines a string to be encrypted. The first argument is the name of the string, and the second argument is the value of the string. You can specify this directive as many times as you need. The final directive is $EncryptEnd(). This directive tells the utility to stop processing lines.

Listing 2 shows the results after running the utility. The utility has inserted a string declaration with the name specified by the $EncryptString() directive. The value of the string is an encrypted version of the specified string value.

Listing 2: Source Code After Processing

// Here's some sample source code
// $EncryptBegin("EncryptionKey")
// $EncryptString(strName, "Value of my string")
static TCHAR strName[] = _T("kpOPmZiZnJSam6eUe3ahlqiuU4lIQYhRUqhvSVFCUlheR05a");
// $EncryptEnd()

// More source code...

Note that you can run the utility on the same file as many times as you need. The reason the $EncryptBegin() and $EncryptEnd() directives were required is because the program strips out any lines that don't start with a comment. This is what allows it to remove any previous lines generated by the utility before generating new ones.

Warning: Be sure not to forget the $EncryptEnd() directive. If you do forget, any lines that follow and do not start with a comment will be removed!

Accessing the Encrypted String

Now when you compile your executable, the encrypted string will be embedded in the executable image. So it won't be as easy for users to see the information when examining your application files.

However, a little more work is needed in order for your application to be able to use the encrypted string. You must decrypt the encrypted string. Listings 3 and 4 show the source code for my CEncrypt class.

The CEncrypt class is a very rudimentary encryption class. It just does a bit of manipulation to the text before base-64 encoding it. The important things is to make sure that the routine you use within your program to decrypt the strings is compatible with the routines used to encrypt them. And be sure to use the same encryption key in order for your code to be able to successfully decrypt encrypted strings.

Listing 3: The CEncrypt Header File

#pragma once

class CEncrypt
{
public:
    CEncrypt();
    CEncrypt(LPCTSTR pszKey);
    virtual ~CEncrypt(void);

    void SetKey(LPCTSTR pszKey)
    {
        m_sKey = pszKey;
    }

    CString Encrypt(const CString& s);
    CString Decrypt(const CString& s);

protected:
    void Scramble(BYTE *pBuff, int nCount);
    void Encode(BYTE *pBuff, int nCount);

    CString m_sKey;
};

Listing 4: The CEncrypt Source File

#include "StdAfx.h"
#include "Encrypt.h"
#include <atlenc.h>    // For base64 encoding

///////////////////////////////////////////////////////////////////
// CEncrypt
// Implements a very simple, multi-stage string encryption algorithm

CEncrypt::CEncrypt()
{
    m_sKey.Empty();
}

CEncrypt::CEncrypt(LPCTSTR pszKey)
{
    m_sKey = pszKey;
}

CEncrypt::~CEncrypt(void)
{
}

// Encrypts the given string
// Note: Results will differ between Unicode and multibyte builds
CString CEncrypt::Encrypt(const CString& s)
{
    int nChars, nBytes;
    CStringA sTemp;
    BYTE *pBuff;

    nChars = s.GetLength();
    if (nChars == 0)
        return CString();

    // Copy data to BYTE buffer
    nBytes = nChars * sizeof(TCHAR);
    pBuff = new BYTE[nBytes];
    if (pBuff == NULL)
        return CString();
    memcpy(pBuff, (LPBYTE)(LPCTSTR)s, nBytes);

    // Encrypt data
    Encode(pBuff, nBytes);
    Scramble(pBuff, nBytes);
    // A little extra modification
    for (int i = 0; i < nBytes; i++)
        pBuff[i] += (i + nBytes);

    // Finally, base64 encode the encrypted data
    nChars = Base64EncodeGetRequiredLength(nBytes,
        ATL_BASE64_FLAG_NOPAD|ATL_BASE64_FLAG_NOCRLF);
    VERIFY(Base64Encode(pBuff, nBytes, sTemp.GetBufferSetLength(nChars), &nChars,
        ATL_BASE64_FLAG_NOPAD|ATL_BASE64_FLAG_NOCRLF));
    sTemp.ReleaseBuffer(nChars);

    delete [] pBuff;

#ifdef _UNICODE

    CString sResult;
    nChars = MultiByteToWideChar(CP_ACP, 0, sTemp, nChars,
        sResult.GetBufferSetLength(nChars), nChars);
    sResult.ReleaseBuffer(nChars);
    return sResult;

#else

    return sTemp;

#endif

}

// Decrypts the given string (encrypted with Encrypt)
// Note: Results will differ between Unicode and multibyte builds
CString CEncrypt::Decrypt(const CString& s)
{
    int nChars, nBytes;
    CStringA sTemp;
    BYTE *pBuff;

    // Get encrypted length
    nChars = s.GetLength();
    if (nChars == 0)
        return CString();

#ifdef _UNICODE

    nChars = WideCharToMultiByte(CP_ACP, 0, s, nChars,
        sTemp.GetBufferSetLength(nChars), nChars, NULL, NULL);
    sTemp.ReleaseBuffer(nChars);

#else

    sTemp = s;

#endif

    // Copy to byte array
    nBytes = Base64DecodeGetRequiredLength(nChars);
    pBuff = new BYTE[nBytes];
    if (pBuff == NULL)
        return CString();
    memcpy(pBuff, (LPBYTE)sTemp.GetBuffer(), nBytes);

    // Base64 decode the encrypted data
    VERIFY(Base64Decode(sTemp, sTemp.GetLength(), pBuff, &nBytes));

    // Decrypt data
    for (int i = 0; i < nBytes; i++)
        pBuff[i] -= (i + nBytes);
    Scramble(pBuff, nBytes);
    Encode(pBuff, nBytes);

    CString sResult;
    nChars = nBytes / sizeof(TCHAR);
    memcpy(sResult.GetBufferSetLength(nChars), pBuff, nBytes);
    sResult.ReleaseBuffer(nChars);

    delete [] pBuff;

    return sResult;
}

// Scrambles a string by separating even from odd characters
void CEncrypt::Scramble(BYTE *pBuff, int nCount)
{
    int i, nDiv;
    char c;

    nDiv = (nCount / 2);

    for (i = 0; i < nDiv; i += 2)
    {
        c = pBuff[i];
        pBuff[i] = pBuff[nCount - (i + 1)];
        pBuff[nCount - (i + 1)] = c;
    }
}

// XORs the given string against a password
void CEncrypt::Encode(BYTE *pBuff, int nLen)
{
    int i, nKeyLen;

    nKeyLen = m_sKey.GetLength();
    if (nKeyLen > 0)
    {
        for (i = 0; i < nLen; i++)
        {
            pBuff[i] ^= (char)m_sKey[i % nKeyLen];
        }
    }
}

Limitations

Of course, this approach isn't foolproof. For starters, my encryption code is nothing particularly secure. If you use this approach, I would recommend swapping my encryption routines for something a bit more robust.

In addition, even with the most powerful encryption routines, it's still possible for sophisticated users to step through your code with a debugger, where they can view any encrypted text after it has been decrypted.

Still, this approach does make it harder for casual users to see text you want to keep hidden. For medium security situations, it may be worthwhile.

Conclusion

That's about it. I didn't list the source code for the utility itself due to space limitations. The source code is included in the attached project.

The utility is very simple. It prompts you to select the file to modify. It also provides an option to specify if the file should be updated in place, or if the changes should be written to a second file.

Whether or not you can actually put this technique to use, I thought it was kind of interesting.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.