快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫"启动"或者"Startup"。位于该目录下的PE文件会在开机后进行自启动。由于不同版本的Windows启动目录的路径都是不同的,所以我们用一个SHGetSpecialFolderPath来获取该路径。该API位于ShlObj.dll中。我们用隐式载入方式获取。先看MSDN:
该API有四个参数。
1. HWND窗口句柄作为保留一般为NULL。
2. pszPath是输出参数。是API返回的启动目录的路径
3. csidl是一个常量,需要指明为CSIDL_STARTUP表明我们要获取自启动目录路径
4. 这是一个布尔值,指明如果目录不存在,是否要创建该目录
具体看一下csidl的其他数值参考MSDN: https://docs.microsoft.com/zh-cn/windows/desktop/shell/csidl
给出一个使用实例:
#include
#include
#include
#include
#include
#include
LPCTSTR GetFileNameFromPath(LPCTSTR szPathName) {LPCTSTR pFind = strrchr(szPathName, '\\');if (NULL != pFind) ++pFind;return(pFind);
}// szSrcPathName指针指向源文件路径
BOOL RapidStartup(LPCTSTR szSrcPathName) {TCHAR szPathName[MAX_PATH] = { 0 };BOOL bRet = SHGetSpecialFolderPath(NULL, szPathName, CSIDL_STARTUP, TRUE);if (FALSE == bRet)return(FALSE);LPCTSTR pFileName = GetFileNameFromPath(szSrcPathName);if (!pFileName)return(FALSE);StringCchPrintf(szPathName, _countof(szPathName), "%s\\%s", szPathName, pFileName);if (!CopyFile(szSrcPathName, szPathName, FALSE))return(FALSE);return(TRUE);
}int _tmain() {if (RapidStartup("C:\\Users\\Administrator\\Desktop\\hello.exe"))printf("successful!\n");elseprintf("failed!\n");system("pause");return(0);
}
注册表自启动也是一种非常常见的方式。关于注册表的详细介绍可以参见MSDN: https://docs.microsoft.com/zh-cn/windows/desktop/SysInfo/about-the-registry
这里简要介绍一下注册表。注册表是Windows操作系统中的一个重要数据库。其中包含了许多与系统相关的配置信息。Windows提供了一个编辑器方便我们编辑。可以按Win+R输入regedit来打开。或者直接去系统目录下寻找。
上面是注册表的基本布局。注册表相当复杂,这里仅仅讲关于自启动的配置的内容。设置自启动的键值项有很多,这里仅仅介绍两种通用的。其他的我也没了解感兴趣可以自己去百度。
1. HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run2. HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
可以看到上面两项唯一的区别就是根键(简称键)不同,子键是一样的。HKEY_CURRENT_USER与HKEY_LOCAL_MACHINE的区别是影响范围。前者影响只当前用户而后者是系统配置影响所有用户。只要里面添加入新的键值对就可以实现自启动。我们具体来看下:
进行如上图的写入操作后就可以实现开机自启动。可以看到我是在HKEY_CURRENT_USER根键下也就是说这个设置只会影响我目前的登陆账户。你可以利用编辑器手动写入也可以编写程序来实现。而这里就是要讲如何编写程序实现。
想要编写程序操作注册表就必须使用系统API函数,关于注册表的API位于advapi.dll动态库中。而关于注册表的API数不胜数,具体可以MSDN查询。我们这里只使用到如下API:
RegOpenKeyEx: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regopenkeyexa
RegCreateKeyEx: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regcreatekeyexa
RegSetKeyValue: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regsetkeyvaluea
RegDeleteKeyValue: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regdeletekeyvaluea
RegCloseKey: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regclosekey
注册表的API看上去相当繁琐,实际上大部分参数都不尽相同。这里仅给出MSDN中关于API的详细介绍,考虑到篇幅就不做累述。可以结合百度与MSDN的介绍来理解。接下去贴出代码:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#define DATA "C:\\Users\\Administrator\\Desktop\\hello.exe"
#define SUBKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
#define HKEYMACHINE HKEY_LOCAL_MACHINE
#define HKEYCURRENTUSER HKEY_CURRENT_USERBOOL RegistryStopAutoStartup(BOOL Is_HKEY_LOCAL_MACHINE, LPCTSTR pszValueName) {HKEY hKeyFirst = NULL, hKey = NULL;if (!pszValueName) {_tprintf("字符串不能为空!\n");return(FALSE);}if (Is_HKEY_LOCAL_MACHINE)hKeyFirst = HKEY_LOCAL_MACHINE;elsehKeyFirst = HKEY_CURRENT_USER;if (ERROR_SUCCESS != RegDeleteKeyValue(hKeyFirst, SUBKEY, pszValueName)) {_tprintf("%s错误代码: %d\n", "RegDeleteKeyValue", GetLastError());return(FALSE);}return(TRUE);
}BOOL RegistryAutoStartup(BOOL Is_HKEY_LOCAL_MACHINE, LPCTSTR pszKeyValue, LPCTSTR pszKeyValueData) {HKEY hKey = NULL, hKeyFirst = NULL;if (NULL == pszKeyValue || NULL == pszKeyValueData) {_tprintf("字符串不能为空!\n");return(FALSE);}if (Is_HKEY_LOCAL_MACHINE)hKeyFirst = HKEY_LOCAL_MACHINE;elsehKeyFirst = HKEY_CURRENT_USER;if (ERROR_SUCCESS != RegCreateKeyEx(hKeyFirst, SUBKEY, 0, NULL, 0, REG_OPTION_NON_VOLATILE, NULL, &hKey, NULL)) {if (0 != GetLastError())return(FALSE);else if (ERROR_SUCCESS != RegOpenKeyEx(hKeyFirst, SUBKEY, 0, KEY_ALL_ACCESS, &hKey)) {_tprintf("%s错误代码: %d\n", "RegOpenKeyEx", GetLastError());return(FALSE);}}if (ERROR_SUCCESS != RegSetKeyValue(hKeyFirst, SUBKEY, pszKeyValue, REG_SZ, pszKeyValueData, _tcslen(pszKeyValueData))) {_tprintf("%s错误代码: %d\n", "RegSetKeyValue", GetLastError());return(FALSE);}RegCloseKey(hKey);return(TRUE);
}int _tmain() {// 在注册表中写入键值RegistryAutoStartup(FALSE, "hello", DATA);// 删除注册表键值system("pause");RegistryStopAutoStartup(FALSE, "hello");system("pause");return(0);
}
注册表中还有两个值得注意的地方:
1. 操作HKEY_LOCAL_MACHINE根键内容时需要管理员权限否则会失败,而HKEY_CURRENT_USER只需要默认权限就足够了。
2. 在64位Windows下为了支持32位的程序,还存在一个WOW64层。具体参见: http://tech.163.com/06/0208/10/29ED63CH0009159F.html 这里就不再累述。我只想说的是由于32位程序的存在而为了使32位程序能够像64位程序一样使用相同方式操纵注册表而不必分开。Windows为我们设置了重定向。即假设我们为32位程序设置自启动,并且我们修改的是:
HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
实际上会被重定位到:
HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run
即SOFTWARE下WOW6432Node中。在这个项下的内容都是用于32位程序。Windows会自动为我们设置重定向,如果不想要重定向的话可以添加KEY_WOW64_64KEY标志。
系统服务实现自启动是一种更加隐蔽的方式,但是实现起来相对复杂一点。由于系统服务是出于SESSION0,自从Vista之后系统服务就与普通应用程序隔离了。也就是说处于SESSION0中的系统服务程序不能与应用程序交互以及共享UI。类似于MessageBox或者创建对话框之类的是没有办法显示的。本质上系统服务程序是控制台程序,但是执行一些系统的操作是可以的。当然想要获取图形界面也是可以的,具体参见: https://docs.microsoft.com/zh-cn/windows/desktop/Services/interactive-services
按Win+R后输入services.msc可以打开服务窗口:
可以对服务进行启动,停止,暂停,恢复这四种操作并且可以设置手动和自动,禁止等启动方式。在一项服务上右击选择属性就能看到如下对话框:
当然,这可以选择手动操作。但这里介绍如何用Win32 API来操作。我们要使用OpenSCManager, OpenService, CreateService, StartService, DeleteService, ControlService, CloseServiceHandle来操作。具体参见MSDN:
OpenSCManager: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-openscmanagera
CreateService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-createservicea
OpenService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-openservicea
StartService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-startservicea
DeleteService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-deleteservice
ControlService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-controlservice
CloseServiceHandle: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-closeservicehandle
下面贴出实例代码:
#include
#include
#include
#include
#pragma comment(lib, "Shlwapi.lib")BOOL ServiceCreateOrStart(BOOL IsCreate, LPCTSTR pszServicePath) {SC_HANDLE shSCM = NULL;TCHAR szServiceName[MAX_PATH] = { 0 };SC_HANDLE shCS = NULL;if (!pszServicePath)return(FALSE);_tcscpy_s(szServiceName, _countof(szServiceName), pszServicePath);PathStripPath(szServiceName);shSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);if (!shSCM) {_tprintf("OpenSCManager %d", GetLastError());return(FALSE);}if (IsCreate)shCS = CreateService(shSCM, szServiceName, szServiceName, SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START,SERVICE_ERROR_NORMAL, pszServicePath, NULL, NULL, NULL, NULL, NULL);else {shCS = OpenService(shSCM, szServiceName, SERVICE_ALL_ACCESS);if (NULL == shCS) {_tprintf("OpenService %d", GetLastError());CloseServiceHandle(shSCM);return(FALSE);}if (!StartService(shCS, 0, NULL)) {_tprintf("StartService %d", GetLastError());CloseServiceHandle(shSCM);CloseServiceHandle(shCS);return(FALSE);}}CloseServiceHandle(shSCM);CloseServiceHandle(shCS);return(TRUE);
}BOOL DelOrStopService(BOOL IsDel, LPCTSTR pszServiceName) {SC_HANDLE shSCM = NULL, shSC = NULL;SERVICE_STATUS ss = {0};TCHAR szServiceName[MAX_PATH] = {0};BOOL bRet = FALSE;if (!pszServiceName)return(FALSE);_tcscpy_s(szServiceName, _countof(szServiceName), pszServiceName);PathStripPath(szServiceName);if (NULL == (shSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS))) {_tprintf("OpenSCManager: %d\n", GetLastError());return(FALSE);}if (NULL == (shSC = OpenService(shSCM, szServiceName, SERVICE_ALL_ACCESS))) {_tprintf("OpenService: %d\n", GetLastError());CloseServiceHandle(shSCM);return(FALSE);}if (IsDel)bRet = DeleteService(shSC);elsebRet = ControlService(shSC, SERVICE_CONTROL_STOP, &ss);CloseServiceHandle(shSCM);CloseServiceHandle(shSC);return(bRet);
}int _tmain() {ServiceCreateOrStart(TRUE, "C:\\Users\\Administrator\\Desktop\\SERVICEEXE.exe");DelOrStopService(TRUE, "C:\\Users\\Administrator\\Desktop\\SERVICEEXE.exe");system("pause");return(0);
}
值得注意的是操纵系统服务需要管理员权限。上面这段代码实现了系统服务的创建,启动,关闭,删除等功能。
系统服务程序的编写也和普通程序不一样。其流程类似于下面的图:
接下去我们贴出实例代码:
#include
#include
TCHAR szServiceName[MAX_PATH] = "ServiceTest.exe";
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = 0;void MyFunc() {HANDLE hFile = NULL;TCHAR szText[] = "If you see this, it means service is created successfully : D";DWORD dwSize = 0;hFile = CreateFile("C:\\Users\\Administrator\\Desktop\\ttt.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);WriteFile(hFile, szText, _countof(szText), &dwSize, NULL);CloseHandle(hFile);
}BOOL SetServiceStatus(DWORD dwServiceCode) {SERVICE_STATUS ss = {0};ss.dwCurrentState = dwServiceCode;ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;ss.dwCOntrolsAccepted= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN |SERVICE_ACCEPT_PAUSE_CONTINUE;ss.dwWin32ExitCode = 0;ss.dwWaitHint = 3000;return(SetServiceStatus(g_ServiceStatusHandle, &ss));
}void __stdcall LphandlerFunction(DWORD dwControl) {switch (dwControl) {case SERVICE_CONTROL_STOP:SetServiceStatus(SERVICE_STOP_PENDING);SetServiceStatus(SERVICE_STOPPED);break;case SERVICE_CONTROL_PAUSE:SetServiceStatus(SERVICE_PAUSE_PENDING);SetServiceStatus(SERVICE_PAUSED);break;case SERVICE_CONTROL_CONTINUE:SetServiceStatus(SERVICE_CONTINUE_PENDING);SetServiceStatus(SERVICE_RUNNING);break;default:break;}
}void __stdcall LpserviceMainFunctiona(DWORD dwNumServicesArgs,LPSTR* lpServiceArgVectors
) {g_ServiceStatusHandle = RegisterServiceCtrlHandler(szServiceName, LphandlerFunction);if (0 == g_ServiceStatusHandle)return;SetServiceStatus(SERVICE_START_PENDING);SetServiceStatus(SERVICE_RUNNING);while (TRUE) {Sleep(5000);MyFunc();}
}int _tmain() {SERVICE_TABLE_ENTRY stDispatchTable[] = {{szServiceName, LpserviceMainFunctiona},{NULL, NULL}};StartServiceCtrlDispatcher(stDispatchTable);return(0);
}
这就是服务程序。注意要把想实现的功能用死循环包裹起来不然重启会导致功能无法运行。
(未完)