引言:
这是开发PC游戏《海战世界》时用到的配置文件加密工具,游戏开发难免要用到配置文件,而其中某些数据开发者并不期望用户获知或是修改,通常会对一些配置文件进行加密。一般是用一个批量的加密工具,统一对配置文件进行加密。
考虑到加密、解密的效率及对安全性的需求,选择了最常用的XOR算法,以下是加密工具的源代码:(windows环境下基于XOR算法,带有图形界面的文件批量加密工具)
源代码:
由于XOR算法是直接可逆的,故源代码中略去了部分代码。
EncryptXML.h:
#pragma once
#include <vector>
#include <atlframe.h>
#include <atlwin.h>
#include <ShlObj.h>
#include "Resource.h"
using namespace std;
struct encrypt_context
{
unsigned char mask[1024];
unsigned int mask_len;
};
class CEncryptXML : public CDialogImpl<CEncryptXML>, public CUpdateUI<CEncryptXML>,
public CMessageFilter, public CIdleHandler
{
public:
enum { IDC = IDD_DIALOG1 };
BOOL PreTranslateMessgae(MSG* pMsg)
{
return CWindow::IsDialogMessage(pMsg);
}
BOOL OnIdle()
{
return FALSE;
}
BEGIN_UPDATE_UI_MAP(CEncryptXML)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CEncryptXML)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnBnBrowse)
COMMAND_HANDLER(IDOK, BN_CLICKED, OnBnOk)
COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnCancel)
END_MSG_MAP()
public:
//UI code
LRESULT OnInitDialog(UINT, WPARAM, LPRAM, BOOL&);
LRESULT OnBnBrowse(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnBnOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnBnCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
int ValidPath(const std::wstring& strPath);
//encrypt code.
void CollectFilesByDirectory(const TCHAR* firstroot, const TCHAR* root, const TCHAR* outdir);
BOOL CollectFilesBySingleFile(const TCHAR* fileName, std::wstring& dest);
void CollectDeleteDirectory(const TCHAR* firstroot, const TCHAR* root, const TCHAR* outdir);
void encryptdata(const encrypt_context& ctx, unsigned char* data, size_t pos, size_t len);
BOOL encryptfile(encrypt_context& ectx, const TCHAR* src_file, const TCHAR* dest_file);
BOOL EncryptXML(const TCHAR* fileName, const TCHAR* destPath);
void DoEncrypt(const TCHAR* fileName);
private:
std::vector <std::wstring> arSourceFileName; ///< 源文件列表
std::vector <std::wstring> arDestFileNamep; ///< 临时文件列表
std::vector <std::wstring> arEncryptFailedFileName; ///< 加密失败文件列表
std::vector <std::wstring> arDirectoryNeedToBeDeleted; ///< 待删除文件夹列表
};
EncryptXML.cpp:
#include "stdafx.h"
#include "EncryptXML.h"
#include "Resource.h"
#include "..\TCommonUtiliLib\common.h"
#include <WinBase.h>
#include <iostream>
#include <Windows.h>
CAppModule _Module;
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED)
{
SendMessage(hwnd, BFFM_SETSELECTION, 1, lpData);
return 0;
}
return 0;
}
std::wstring BrowseForInstallDestination(HWND m_hWnd, const std::wstring& initdir)
{
BROWSEINFO bi;
bi.hwndOwner = m_hWnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = NULL;
std::wstring title = L"请选择要加密的文件或文件夹:";
bi.lpszTitle = title.c_str();
//bi.ulFlags = 0;
bi.ulFlags = BIF_BROWSEINCLUDEFILES; ///< 最初的版本仅支持选定文件夹,后来需要对单个文件进行加密
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)initdir.c_str();
bi.Image = 0;
TCHAR installPath[1024];
PIDLIST_ABSOLUTE ret = ::SHBrowseForFolder(&bi);
if (ret == NULL)
{
ZeroMemory(installPath, sizeof installPath);
}
else
{
::SHGetPathFromIDList(ret, installPath);
}
return installPath;
}
void CEncryptXML::CollectFilesByDirectory(const TCHAR* firstroot, const TCHAR* root , const TCHAR* outdir)
{
WIN32_FIND_DATA fd;
THCAR fullname[MAX_PATH + 1];
size_t offset = ::wcslen(firstroot);
::_stprintf_s(fullname, MAX_PATH + 1, L"%s\\%s", root, L"*.*");
HANDLE hf = ::FindFirstFile(fullname, &fd);
BOOL b = hf != INVALID_HANDLE_VALUE;
while(b)
{
::_stprintf_s(fullname, MAX_PATH + 1, L"%s\\%s", root, fd.cFileName);
if (!(wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0))
{
if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CollectFilesByDirectory(firstroot, fullname, outdir);
}
else
{
TCHAR dummy[1024];
TCHAR ext[1024];
TCHAR output_filename[1024];
::_stprintf_s(output_filename, 1024, L"%s%s", outdir, fullname + offset);
::_wsplitpath_s(output_filename, dummy, 1024, dummy, 1024, dummy, 1024, ext, 1024);
///< 过滤后缀名,只对.xml文件加密
if(wcscmp(ext, L".xml") == 0)
{
arSourceFileName.push_back(fullname);
arDestFileName.push_back(output_filename);
}
}
}
b = ::FindNextFile(hf, &fd);
}
::FindClose(hf);
}
void CEncryptXML::encryptdata(const encrypt_context& ctx, unsigned char* data, size_t pos, size_t len)
{
size_t i = 0;
while(i < len)
{
*data++ ^= /*省略计算方法*/
i++;
}
}
BOOL CEncryptXML::encryptfile(encrypt_context& ectx, const TCHAR* src_file, const TCHAR* dest_file)
{
TCreatePath(dest_file);
FILE* pf;
FILE* pf2;
errno_t err = ::_wfopen_s(&pf, src_file, L"rb");
if(err != 0)
{
return FALSE;
}
err = ::_wfopen_s(&pf2, dest_file, L"wb");
if(err != 0)
{
fclose(pf);
return FALSE;
}
size_t pos = 0;
unsigned char buffer[4096];
while(!feof(pf))
{
size_t readed = ::fread_s(buffer, 4096, 1, 4096, pf);
encryptdata(ectx, buffer, pos, readed);
if(fwrite(buffer, 1, readed, pf2) != readed)
{
fclose(pf);
fclose(pf2);
return FALSE;
}
pos += readed;
}
fclose(pf);
fclose(pf2);
return TRUE;
}
BOOL CEncryptXML::EncryptXML(const TCHAR* fileName, const TCHAR* destPath)
{
encrypt_context enc_ctx;
__int64 ilen = TGetFileSize(fileName);
if(ilen <= 0)
return FALSE;
size_t len = (size_t)ilen;
enc_ctx.mask_len = 1024;
enc_ctx.mask[0] =/*省略计算方法*/
for (int i = 1; i < 1024; ++i)
enc_ctx.mask[i] = /*省略计算方法*/
return encryptfile(enc_ctx, fileName, destPath);
}
LRESULT CEncryptXML::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
// center the dialog on the screen
CenterWindow();
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return TRUE;
}
int CEncryptXML::ValidPath(const std::wstring& strPath)
{
std::wstring s = strPath;
if(s.empty())
return 0;
if(s.find_last_of(L"\\") != s.length() - 1)
s += L"\\";
int ret = TCreatePath2(s.c_str());
//xml files should be in a exist directory.
if(ret == ERROR_FILE_EXISTS || ret == ERROR_ALREADY_EXISTS)
return 1;
return 0;
}
LRESULT CEncryptXML::OnBnBrowse(WORD, WORD, HWND, BOOL&)
{
TCHAR intdir[1024];
ZeroMemory(intdir, sizeof intdir);
if(FALSE == SHGetSpecialFolderPath(m_hWnd, intdir, CSIDL_PROGRAM_FILES, TRUE))
{
wcscpy_s<1024>(intdir, L"D:\\");
}
std::wstring result = BrowseForInstallDestination(m_hWnd, intdir);
if(!result.empty())
{
TFixPath(result);
CEDIT edit = GetDlgItem(IDC_EDIT1);
edit.SetWindowText(result.c_str());
}
return 0;
}
LRESULT CEncryptXML::OnBnOk(WORD, WORD, HWND, BOOL&)
{
TCHAR dir[1024];
ZeroMemory(dir, sizeof dir);
CEdit edit = GetDlgItem(IDC_EDIT1);
edit.GetWindowtext(dir, 1022);
int ret = ValidPath(std::wstring(dir));
if(ret == 0)
{
MessageBox(L"选择的路径有误!", L"提示", MB_OK);
}
else
{
DoEncrypt(dir);
}
return 0;
}
LRESULT CEncryptXML::OnBnCancel(WORD, WORD, HWND, BOOL&)
{
DestoryWindow();
PostQuitMessage(0);
return 0;
}
void CEncryptXML::DoEncrypt(const TCHAR* fileName)
{
std::wstring dest = L"";
std::wstring errorStr = L"";
///< 选择的文件夹
if (PathIsDirectory(fileName))
{
dest = std::wstring(fileName) + L"\\temp";
CollectFilesByDirectory(fileName, fileName, dest.c_str());
}
///< 选的文件
else
{
if (!CollectFilesBySingleFile(fileName, dest))
return;
}
for (size_t i = 0; i < arSourceFileName.size(); ++i)
{
if (!EncryptXML(arSourceFileName[i].c_str(), arDestFileName[i].c_str()))
{
arEncryptFailedFileName.push_back(arSourceFileName[i]);
::DeleteFile(arDestFileName[i].c_str());
continue;
}
///< 文件为只读时,CopyFile会失败
if (!::CopyFile(arDestFileName[i].c_str(), arSourceFileName[i].c_str(), FALSE))
{
arEncryptFailedFileName.push_back(arSourceFileName[i]);
::DeleteFile(arDestFileName[i].c_str());
continue;
}
if (!::DeleteFile(arDestFileName[i].c_str()))
{
arEncryptFailedFileName.push_back(arSourceFileName[i]);
continue;
}
}
CollectDeleteDirectory(dest.c_str(), dest.c_str(), dest.c_str());
for (size_t i = 0; i <arDirectoryNeedToBeDeleted.size(); ++i)
{
if (!::RemoveDirectory(arDirectoryNeedToBeDeleted[i].c_str()))
{
errStr = arDirectoryNeedToBeDeleted[i] + L"删除失败!";
MessageBox(errorStr.c_str(), L"提示", MB_OK);
}
}
if (!::RemoveDirectory(dest.c_str()))
{
errorStr = dest + L"删除失败!";
MessageBox(errorStr.c_str(), L"提示", MB_OK);
}
if (arEncryptFailedFileName.empty())
{
MessageBox(L"加密成功!", L"提示", MB_OK);
}
else
{
std::wstring errorFileName = L"";
for (size_t i = 0; i < arEncryptFailedFileName.size(); ++i)
{
errorFileName += arEncryptFailedFileName[i];
errorFileName += '\n';
}
errorFileName += L"加密失败!";
MessageBox(errorFileName.c_str(), L"提示", MB_OK);
}
DestroyWindow();
PostQuitMessage(0);
}
BOOL CEncryptXML::CollectFilesBySingleFile(const TCHAR* fileName, std::wstring& dest)
{
std::wstring strFileName = fileName;
std::wstring::size_type pos;
std::wstring tempDest;
TCHAR dummy[1024];
TCHAR ext[1024];
pos = strFileName.find_last_of(L"\\");
if (pos == std::wstring::npos)
{
MessageBox(L"解析文件路径失败!", L"提示", MB_OK);
return FALSE;
}
tempDest = strFileName.substr(0, pos);
dest = tempDest + L"\\temp";
size_t offset = ::wcslen(tempDest.c_str());
TCHAR output_filename[1024];
::_stprintf_s(output_filename, 1024, L"%s%s", dest.c_str(), fileName + offset);
::_wsplitpath_s(output_filename, dummy, 1024, dummy, 1024, dummy, 1024, ext, 1024);
///< 过滤后缀名,只对.xml文件加密
if (wcscmp(ext, L".xml") != 0)
{
MessageBox(L"选择的文件不是xml文件,请重新选择。", L"提示", MB_OK);
return FALSE;
}
else
{
arSourceFileName.push_back(fileName);
arDestFileName.push_back(output_filename);
}
return TRUE;
}
void CEncryptXML::CollectDeleteDirectory(const TCHAR* firstroot, const TCHAR* root, const TCHAR* outdir)
{
WIN32_FIND_DATA fd;
TCHAR fullname[MAX_PATH + 1];
size_t offset = ::wcslen(firstroot);
::_stprintf_s(fullname, MAX_PATH + 1, L"%s\\%s", root, L"*.*");
HANDLE hf = ::FindFirstFile(fullname, &fd);
BOOL b = hf != INVALID_HANDLE_VALUE;
while(b)
{
::_stprintf_s(fullname, MAX_PATH + 1, L"%s\\%s", root, fd.cFileName);
if (!(wcscmp(fd.cFileName, L".") == 0 ||wcscmp(fd.cFileName, L"..") == 0))
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
arDirectoryNeedToBeDeleted.push_back(fullname);
CollectDeleteDirectory(firstroot, fullname, outdir);
}
}
b = ::FindNextFile(hf, &fd);
}
::FindClose(hf);
}
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CEncryptXML dlgMain;
if (dlgMain.Create(NULL) == NULL)
{
ATLTRACE(_T("Main dialog creation failed!\n"));
return 0;
}
dlgMain.ShowWindow(nCmdShow);
dlgMain.CenterWindow();
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPreInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(HRes));
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES);
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
int nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term();
::CoUninitialize();
return nRet;
}
最后:
实际使用的过程中,最常导致机密失败的原因便是加密文件选择了“只读”属性,去除只读属性即可。