应用程序启动后修改自身EXE文件或自删除EXE文件(附VC++6.0源码)
2013-08-20 10:46
399 查看
请点击查看原文,感谢提供的技术支持,支持原创!!!!
在CSDN论坛看到点击打开链接这么一个问题:如何为第三方工具加上使用限制次数?问题的答案很简单,重新做一个应用程序,将第三方程序打包进这个应用程序,启动应用程序时可以检查第三方工具的使用次数,检查通过,可运行第三方工具。至于如果检查使用次数,答案也是五花八门,写注册表、写配置文件、读取硬盘、网卡信息等,都是常规做法。我也思考了下,还可以这样:
也是重写应用程序,但将使用次数写入到应用程序文件里面,每使用一次,就更新一下应用程序文件里的计数。
这种方案想来有几个问题:1.如果运行应用程序前做了备份,则每当使用次数完后,还可以用备份应用程序运行;2.我们知道,一个应用程序运行后,是不能修改自身EXE文件的。因此这个方案也只是想想而已,并不实用。但是要实现这种方案还是需要一定的技术,我闲来无事,就写了一个DEMO。
每2个问题,如何在应用程序运行时候修改自身EXE文件?答案也很简单,应用程序运行时加载另一个应用程序,自己退出,让另一个应用程序来修改EXE文件。既然应用程序运行时可以修改本身EXE文件,那么删除自身的EXE文件也是可以的,因此这种方法还可以用于卸载软件中,卸载软件时,可以使用此方法删除卸载软件本身。
好了,我们把目标明确一下:
1.第三方软件为SourceExe.exe,为了演示,我们还是自己模拟第三方,自己写;
2.加载的应用程序为ExePackage.exe;
3.修改ExePackage.exe的程序为ExeHelper.exe。
将SourceExe.exe和ExeHelper.exe以资源形式加载进ExePackage.exe。启动ExePackage.exe时,ExePackage.exe并不显示界面,只将SourceExe.exe和ExeHelper.exe释放到临时目录,然后启动ExeHelper.exe,并告诉ExeHelper.exe一些必要的信息(DEMO中通过参数传递)。
ExeHelper.e
4000
xe启动时,首先等待ExePackage.exe结束,这样才能修改ExePackage.exe文件。然后从ExePackage.exe文件末尾读取16个字节,测试是否匹配我们指定的数据(以区别这些数据是EXE文件本身的数据,还是我们添加的数据),这16个字节的最后4个字节保存的是使用次数,这样我们就可得到使用次数了,使用一次后,再将数据更新到EXE文件,最后就可以启动SourceExe.exe了。
原理很简单,但用到的技术还是不少的:
1.子进程等待父进程结束再处理事务(应用程序的自删除);
2.父进程将句柄等信息传递给子进程;
3.将EXE中加载的资源释放为本地文件。
下面就开始上代码,代码很简单,就不一一解释了,只在需要说明的地方说明一下:
首先是SourceExe.exe的源码,作为第三方工具,代码很简单,就不多说了。
SourceExe.cpp:
然后是ExeHelper.exe的源码,仔细看,也不太难。
ExeHelper.cpp:
最后是ExePackage.exe的源码,ExePackage.cpp:
需要说明:
创建的是MFC对话框程序,因为方便导入SourceExe.exe和ExeHelper.exe作为资源。SourceExe.exe和ExeHelper.exe都是导入的EXE资源。ID可见代码或下图。代码中使用了FreeResFile函数来释放资源到本地文件。注释掉了启动对话框的代码,只将SourceExe.exe和ExeHelper.exe释放到临时目录,运行ExeHelper.exe后立即退出。SourceExe和ExeHelper编译后,会自动将生成的EXE复制到ExePackage\res目录中,因此SourceExe或ExeHelper重新编译后,都需重新编译ExePackage,重新将SourceExe.exe和ExeHelper.exe打包进ExePackage.exe。
运行的效果图如下:
当然,你也可做成非Console的界面,获取参数就需使用GetCommandLine()。
源码下载地址:应用程序启动后修改自身EXE文件或自删除EXE文件
在CSDN论坛看到点击打开链接这么一个问题:如何为第三方工具加上使用限制次数?问题的答案很简单,重新做一个应用程序,将第三方程序打包进这个应用程序,启动应用程序时可以检查第三方工具的使用次数,检查通过,可运行第三方工具。至于如果检查使用次数,答案也是五花八门,写注册表、写配置文件、读取硬盘、网卡信息等,都是常规做法。我也思考了下,还可以这样:
也是重写应用程序,但将使用次数写入到应用程序文件里面,每使用一次,就更新一下应用程序文件里的计数。
这种方案想来有几个问题:1.如果运行应用程序前做了备份,则每当使用次数完后,还可以用备份应用程序运行;2.我们知道,一个应用程序运行后,是不能修改自身EXE文件的。因此这个方案也只是想想而已,并不实用。但是要实现这种方案还是需要一定的技术,我闲来无事,就写了一个DEMO。
每2个问题,如何在应用程序运行时候修改自身EXE文件?答案也很简单,应用程序运行时加载另一个应用程序,自己退出,让另一个应用程序来修改EXE文件。既然应用程序运行时可以修改本身EXE文件,那么删除自身的EXE文件也是可以的,因此这种方法还可以用于卸载软件中,卸载软件时,可以使用此方法删除卸载软件本身。
好了,我们把目标明确一下:
1.第三方软件为SourceExe.exe,为了演示,我们还是自己模拟第三方,自己写;
2.加载的应用程序为ExePackage.exe;
3.修改ExePackage.exe的程序为ExeHelper.exe。
将SourceExe.exe和ExeHelper.exe以资源形式加载进ExePackage.exe。启动ExePackage.exe时,ExePackage.exe并不显示界面,只将SourceExe.exe和ExeHelper.exe释放到临时目录,然后启动ExeHelper.exe,并告诉ExeHelper.exe一些必要的信息(DEMO中通过参数传递)。
ExeHelper.e
4000
xe启动时,首先等待ExePackage.exe结束,这样才能修改ExePackage.exe文件。然后从ExePackage.exe文件末尾读取16个字节,测试是否匹配我们指定的数据(以区别这些数据是EXE文件本身的数据,还是我们添加的数据),这16个字节的最后4个字节保存的是使用次数,这样我们就可得到使用次数了,使用一次后,再将数据更新到EXE文件,最后就可以启动SourceExe.exe了。
原理很简单,但用到的技术还是不少的:
1.子进程等待父进程结束再处理事务(应用程序的自删除);
2.父进程将句柄等信息传递给子进程;
3.将EXE中加载的资源释放为本地文件。
下面就开始上代码,代码很简单,就不一一解释了,只在需要说明的地方说明一下:
首先是SourceExe.exe的源码,作为第三方工具,代码很简单,就不多说了。
SourceExe.cpp:
// SourceExe.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> #include <conio.h> using namespace std; int main(int argc, char* argv[]) { cout << "Hello world!" << endl; getch(); return 0; }
然后是ExeHelper.exe的源码,仔细看,也不太难。
ExeHelper.cpp:
// ExeHelper.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> #include <conio.h> #include <windows.h> using namespace std; BYTE g_byCompare[16] = {'1', '2', '0', '6', '8', '0', '4', '5', '1', 0, 0, 0, 0, 0, 0, 0}; DWORD dwDefaultTimes = 30; int main(int argc, char* argv[]) { if (NULL == argv[0] || 0 == strlen(argv[0]) || NULL == argv[1] || 0 == strlen(argv[1]) || NULL == argv[2] || 0 == strlen(argv[2]) || NULL == argv[3] || 0 == strlen(argv[3])) { cout << "argv error!" << endl; getch(); return 0; } cout << "argv[0]=" << argv[0] << endl; cout << "argv[1]=" << argv[1] << endl; cout << "argv[2]=" << argv[2] << endl; cout << "argv[3]=" << argv[3] << endl; // 保证父进程已退出,才能操作父进程的EXE文件 HANDLE handle=(HANDLE)(atoi(argv[1])); if (WAIT_FAILED == WaitForSingleObject(handle, INFINITE)) { cout << "WaitForSingleObject Error (" << GetLastError() << ")" << endl; getch(); return 0; } HANDLE hFile = CreateFile( argv[2], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); BYTE pBuffer[16] = {0}; DWORD dwFileSizeLow = 0; DWORD dwFileSizeHigh = 0; dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); if (dwFileSizeLow < 16) { cout << "The size of " << argv[2] << " is " << dwFileSizeLow << ", less than 16 bytes." << endl; getch(); return 0; } SetFilePointer(hFile, -16, NULL, FILE_END); DWORD dwBytesRead = 0; ReadFile(hFile, pBuffer, 16, &dwBytesRead, NULL); DWORD dwTimes = 0; memcpy(&dwTimes, pBuffer + 12, sizeof(DWORD)); // pBuffer的前12个字节应该与g_byCompare的前12个字节相同,后4个字节为使用次数 if (0 != memcmp(pBuffer, g_byCompare, 12)) { // pBuffer前12个字节不是g_byCompare的前12个字节,则认为是第一次启动 // 在EXE文件末尾添加上默认的30次使用次数 dwTimes = dwDefaultTimes; memcpy(pBuffer, g_byCompare, 16); memcpy(pBuffer + 12, &dwTimes, sizeof(DWORD)); SetFilePointer(hFile, 0, NULL, FILE_END); DWORD dwBytesWrite = 0; WriteFile(hFile, pBuffer, 16, &dwBytesWrite, NULL); } cout << "Can use " << dwTimes << " times" << endl; if (0 == dwTimes) { getch(); return 0; } cout << "Press any key to run" << endl; getch(); // 减少使用次数,并将使用次数再更新到EXE文件 dwTimes--; memcpy(pBuffer + 12, &dwTimes, sizeof(DWORD)); SetFilePointer(hFile, -16, NULL, FILE_END); DWORD dwBytesWrite = 0; WriteFile(hFile, pBuffer, 16, &dwBytesWrite, NULL); CloseHandle(hFile); STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi)); // 然后启动第三方EXE CreateProcess( argv[3], NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }
最后是ExePackage.exe的源码,ExePackage.cpp:
CExePackageApp theApp; ///////////////////////////////////////////////////////////////////////////// // CExePackageApp initialization /* 函数功能:释放资源文件 参数说明:DWORD dwResID :指定要释放的资源ID号,如IDR_EXE LPCTSTR lpszResType :指定释放的资源的资源类型 LPCTSTR lpszFilePathName :指定释放后的目标文件名 返回值:成功则返回TRUE,失败返回FALSE 使用方法:BOOL bRet = FreeResFile(IDR_SOURCE_EXE, _T("EXE"), _T("D:\\123.txt")); 其中,IDR_SOURCE_EXE为EXE类型的资源ID */ BOOL FreeResFile(DWORD dwResID, LPCTSTR lpszResType, LPCTSTR lpszFilePathName) { HMODULE hInstance = ::GetModuleHandle(NULL);//得到自身实例句柄 HRSRC hResID = ::FindResource(hInstance, MAKEINTRESOURCE(dwResID), lpszResType); //查找资源 HGLOBAL hRes = ::LoadResource(hInstance, hResID); //加载资源 LPVOID pRes = ::LockResource(hRes); //锁定资源 if (NULL == pRes) //锁定失败 { return FALSE; } DWORD dwResSize = ::SizeofResource(hInstance, hResID); //得到待释放资源文件大小 HANDLE hResFile = CreateFile( lpszFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hResFile) { return FALSE; } DWORD dwWritten = 0; //写入文件的大小 WriteFile(hResFile, pRes, dwResSize, &dwWritten, NULL); //写入文件 CloseHandle(hResFile); //关闭文件句柄 FreeResource(hRes); return (dwResSize == dwWritten); //若写入大小等于文件大小,返回成功,否则失败 } BOOL CExePackageApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif TCHAR szTmpDir[MAX_PATH] = {0}; GetTempPath(MAX_PATH, szTmpDir); TCHAR szSourceExeFilePathName[MAX_PATH] = {0}; _stprintf(szSourceExeFilePathName, _T("%sSourceExe.exe"), szTmpDir); TCHAR szExeHelperFilePathName[MAX_PATH] = {0}; _stprintf(szExeHelperFilePathName, _T("%sExeHelper.exe"), szTmpDir); DeleteFile(szSourceExeFilePathName); DeleteFile(szExeHelperFilePathName); FreeResFile(IDR_SOURCE_EXE, "EXE", szSourceExeFilePathName); FreeResFile(IDR_EXE_HELPER, "EXE", szExeHelperFilePathName); TCHAR szModuleName[MAX_PATH] = {0}; GetModuleFileName(NULL, szModuleName, MAX_PATH); STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi)); SECURITY_ATTRIBUTES sa; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // 得到此进程的句柄,传递给ExeHelper.exe,因此这儿不能关闭hParentProcess // 需要在子进程中关闭 HANDLE hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId()); TCHAR szCommandLine[MAX_PATH * 3] = {0}; // 注意此处的参数,调用CreateProcess时,并不会将EXE的文件名也当作参数 // 因此,需要在参数列表中加上EXE的文件名(ExeHelper.exe并不使用) _stprintf(szCommandLine, _T("\"%s\" %d \"%s\" \"%s\""), szExeHelperFilePathName, (DWORD)hParentProcess, szModuleName, szSourceExeFilePathName); CreateProcess( szExeHelperFilePathName, szCommandLine, &sa, NULL, TRUE, NULL, NULL, NULL, &si, &pi); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); // CExePackageDlg dlg; // m_pMainWnd = &dlg; // int nResponse = dlg.DoModal(); // if (nResponse == IDOK) // { // // TODO: Place code here to handle when the dialog is // // dismissed with OK // } // else if (nResponse == IDCANCEL) // { // // TODO: Place code here to handle when the dialog is // // dismissed with Cancel // } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; }
需要说明:
创建的是MFC对话框程序,因为方便导入SourceExe.exe和ExeHelper.exe作为资源。SourceExe.exe和ExeHelper.exe都是导入的EXE资源。ID可见代码或下图。代码中使用了FreeResFile函数来释放资源到本地文件。注释掉了启动对话框的代码,只将SourceExe.exe和ExeHelper.exe释放到临时目录,运行ExeHelper.exe后立即退出。SourceExe和ExeHelper编译后,会自动将生成的EXE复制到ExePackage\res目录中,因此SourceExe或ExeHelper重新编译后,都需重新编译ExePackage,重新将SourceExe.exe和ExeHelper.exe打包进ExePackage.exe。
运行的效果图如下:
当然,你也可做成非Console的界面,获取参数就需使用GetCommandLine()。
源码下载地址:应用程序启动后修改自身EXE文件或自删除EXE文件
相关文章推荐
- 应用程序启动后修改自身EXE文件或自删除EXE文件(附VC++6.0源码)
- 应用程序启动后修改自身EXE文件或自删除EXE文件(附VC++6.0源码)
- 显示隐藏文件的方法,移动硬盘里的autorun.inf文件夹如何删除,svchost.exe应用程序错误 0x58fc16e2,4。未成功启动Cygwin
- windows应用程序(C#)生成的EXE文件图标修改
- OBS源码阅读笔记--如何修改录播文件路径到exe所在目录
- 【DEBUG笔记】执行exe文件时报错“应用程序无法正常启动(0x000007b)”
- 删除应用程序自身的可执行文件
- 分别用VB、Delphi、VC++、ASM四种语言写的一个用UpdateResource修改EXE文件图标的多源码示例
- 在PROCESS没有结束前就将启动PROCESS的EXE文件删除
- 同时启动了多个word的exe,word退出的时候,会报警告“此文件正由另一个应用程序或用户使用” normal.dot
- 用UpdateResource修改EXE文件图标的多源码
- 改造独立部署(SCD)模式下.NET Core应用程序 dotnet的exe文件启动过程
- MFC应用程序图标修改后exe文件没有立即生效问题
- 删除应用程序自身的可执行文件
- linux中/etc/fstab文件删除或修改了,导致系统无法启动
- Codeblocks 等软件 修改源代码后 不能立即执行的解决办法||exe文件删除慢
- 删除应用程序自身的可执行文件
- 用UpdateResource修改EXE文件图标的多源码(已修正) 注:转帖请包函作者信息.(作者:菜新)
- 转载VS2008 编译出来的exe文件,运行遇到“应用程序配置不正确,程序无法启动”的问题
- 在电脑上找到这个路径:D:\jakarta-tomcat-6\conf\Catalina,将localhost目录直接删除掉,再一运行,就没事了! 因为配置文件中配置了启动程序,而webapps文件夹下却没有此应用程序,所以出现了上述错误。