在重启过程中替换windows保护的系统文件
作者:ClassyK 日期:2006-01-25
这最先缘起写一个3721的卸载软件,当时把系统文件都删除了,可是搜索依然会定位到3721,譬如当我在地址栏输入"好的"然后按回车,网页先定位去microsoft的搜索引擎,可是很快,被3721的引擎给替换了。变成了如下地址hxxp://cns.3721.com/cns.dll?coagent=dh&fw=dh&name=%BA%C3%B5%C4&pid=1000026&cnspid=401742_1006
在网上查阅了一下资料,这问题的引起在于广东电信将http://auto.search.msn.com重定位到hxxp://cns.3721.com,估计是跟3721有合作协议吧。现在的问题,让我们把系统里原来的搜索引擎替换为google,后来检查发现,原来的搜索引擎竟然是固化在一个系统文件shdoclc.dll里头,这文件被explorer使用。要替换此地址,唯有修改文件了。
但是修改远没想象中那么简单,最开始用了MoveFileEx,希望能在重启时替换此文件,但这函数只能应付普通的文件,对被windows保护的文件无能为力。只能另开道路了。在网上找了好多的资料,虽然有些是关于无提示替换系统文件的,但是都不够满意,或者实现非常复杂。我要做的,是安安静静的替换掉此文件。
最后灵机一动,答案就出来了。临时替换掉windows shell,这个文件通常就是explorer了。可以通过注册表获得当前的shell是啥。(不过我下面的代码,并没有这样去考虑这么多。)
附各部分代码如下(只适用于win 2000/XP/2003,不适合win9x):
//C++代码 替换系统文件、Shell
void CRemove3721Dlg::ReplaceSystemFile(void) { CString strShdoclc = "\\shdoclc.dll"; // 要被替换的文件名 WCHAR wszSearch[]= L"http://auto.search.msn.com/response.asp?MT=%1&srch=%2&prov=%3&utf8"; //原搜索引擎 WCHAR wszReplace[]= L"http://www.google.com/search?inlang=zh-CN&q=%1&lr=lang_zh-CN"; //替换后的引擎 char szWindows[MAX_PATH]; GetWindowsDirectory( szWindows, sizeof szWindows); CString strTempPath = szWindows; strTempPath += "\\TEMP"; // 通常指向C:\windows\Temp ,不使用GetTempPath得到的路径,为movefileex作准备 // MoveFileEx在重启时的替换只能是同分区(同一卷)的文件! char szSystem[MAX_PATH]; GetSystemDirectory( szSystem, sizeof szSystem); CString strSystemDll = szSystem; strSystemDll += strShdoclc; // --------- 拷贝shdoclc.dll到临时目录 char szTempFile1[MAX_PATH]; GetTempFileName( strTempPath, "_@", 0, szTempFile1); CopyFile( strSystemDll, szTempFile1, FALSE); //CopyFile( strSystemDll, "C:\\shdoclc.dll.bak", TRUE); //备份,如果已存在,就不再替换 // --------- 创建文件映像 HANDLE hProFile = CreateFile( szTempFile1, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE hFileMap = CreateFileMapping( hProFile, NULL, PAGE_READWRITE, 0, 0, 0); if( hFileMap == NULL || hFileMap == INVALID_HANDLE_VALUE) { MessageBox( "CreateFileMapping Error", 0, MB_OK|MB_ICONERROR); return; } LPVOID lpBuf = MapViewOfFile( hFileMap, FILE_MAP_WRITE, 0, 0, 0); // --------- 查找 DWORD dwLength = GetFileSize( hProFile, 0) / 2; wchar_t *wpFind = NULL; for( DWORD i=0; i<dwLength; i++) { wpFind = wcsstr(&((WCHAR*)lpBuf)[i], wszSearch); if( wpFind != 0) break; } if( wpFind == 0) //查找失败... 原文件已经被修改过 { UnmapViewOfFile( lpBuf); CloseHandle( hFileMap); CloseHandle( hProFile); return; } // ---------- 替换 wcscpy( wpFind, wszReplace); UnmapViewOfFile( lpBuf); CloseHandle( hFileMap); CloseHandle( hProFile); // 获取windows文件保护目录 // 以下获取各目录极为重要,如果漏掉将被windows替换回来 CString strSFCDll = szSystem; strSFCDll += "\\dllcache"; strSFCDll += strShdoclc; // 获取servicepack备份目录 char szSP[MAX_PATH]; memset( szSP, 0, sizeof szSP); DWORD dwTemp1 = REG_SZ; DWORD dwTemp2= sizeof szSP; SHGetValue( HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", "ServicePackSourcePath", &dwTemp1, szSP, &dwTemp2); CString strSPDll = szSP; if( strSPDll.GetLength() != 0) { strSPDll += "\\i386"; strSPDll += strShdoclc; } // OK,现在这种方法不需要关闭文件保护,因为我们在文件保护前就替换了文件 // 建立bat文件, 此文件用于替换文件 CString strBat = strTempPath; strBat += "\\[email protected]"; CFile fBat( strBat, CFile::modeCreate | CFile::modeWrite); CString strBuf; strBuf.Format( "Copy %s %s /y\n", szTempFile1, strSystemDll); fBat.Write( strBuf, strBuf.GetLength()); strBuf.Format( "Copy %s %s /y\n", szTempFile1, strSFCDll); fBat.Write( strBuf, strBuf.GetLength()); if( strSPDll.GetLength() != 0) { strBuf.Format( "Copy %s %s /y\n", szTempFile1, strSPDll); fBat.Write( strBuf, strBuf.GetLength()); } strBuf.Format( "Del %s \n", szTempFile1); fBat.Write( strBuf, strBuf.GetLength()); strBuf.Format( "Del %s \n", strBat); fBat.Write( strBuf, strBuf.GetLength()); fBat.Close(); // 用自身去替换系统shell,将bat文件名作参数传入 // 这里没有去获取原来的shell,并非完美 char szModule[MAX_PATH]; GetModuleFileName( NULL, szModule, MAX_PATH); strcat( szModule, " "); strcat( szModule, strBat); SHSetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "Shell", REG_SZ, szModule, strlen( szModule)); }
//接下来要做的就是重启系统,这段代码没什么可提的了
//C++代码 <重启系统: 如果只是替换此文件,只需要注销即可>
void CRemove3721Dlg::RestartMyComputer(void) { HANDLE hToken; TOKEN_PRIVILEGES tkp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0); ExitWindowsEx(EWX_REBOOT, 0); }
//现在,让我们看看启动的时候做些什么
//C++代码 <重启过程中完成文件替换>
BOOL CRemove3721App::InitInstance() { InitCommonControls(); CWinApp::InitInstance(); AfxEnableControlContainer(); //========== 以上是向导生成的代码 =========== int nArgs; LPWSTR *szArg = CommandLineToArgvW( GetCommandLineW(), &nArgs); if( nArgs == 2) { CString strBat(szArg[1]); //利用构造函数完成unicode到multibyte的转换 GlobalFree(szArg); //创建一个自定义desktop,防止出现dos窗口 LPSTR lpDeskName = "ClassyK_Replace_Windows_File"; // 随便给一个名字 HDESK hDesk = CreateDesktop( lpDeskName, NULL, NULL, 0, GENERIC_ALL, NULL); //以下启动开始创建的bat文件来进行替换 STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.lpDesktop = lpDeskName; si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW; si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); CreateProcess( NULL, strBat.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); WaitForSingleObject( pi.hProcess, INFINITE ); // 等待进程完成,否则可能因为shdoclc.dll正使用而无法完成替换。 CloseDesktop( hDesk); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); // 重置shell为explorer CString strShell = "Explorer.exe"; SHSetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "Shell", REG_SZ, strShell, strShell.GetLength()); // 运行原来的windows shell WinExec( strShell, SW_SHOW); return FALSE; // 直接返回,退出本程序。 } //========== 以下是向导生成的代码 =========== CRemove3721Dlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { } return FALSE; }