基本概念
我们知道,注册表是一个集中管理硬件、软件配置信息的数据库,其中存放着各种参数,直接控制着windows的启动、硬件驱动程序的装载以及应用程序的运行,并记录了各种硬件和软件的配置信息。注册表注入,顾名思义,就是将我们的想要进行的操作写入到注册表的配置信息里,通过系统加载我们的动态库完成注入。
注入原理
当应用程序被加载时,会将User32.dll映射到进程空间内,这时会触发User32动态库的DllMain函数中的DLL_PROCESS_ATTACH,进而查看注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows目录下的AppInit_DLLs是否存在需要加载的动态库路径,同时查看LoadAppInit_DLLs的值是否为1。如果为1,那么将根据路径加载所有的动态库。至此,就完成了注册表的注入。
实现过程
①调用RegOpenKeyEx函数,传入主键HKEY_LOCAL_MACHINE和子键的路径,打开注册表。
LSTATUS APIENTRY RegOpenKeyExW(
_In_ HKEY hKey, //主键
_In_opt_ LPCWSTR lpSubKey, //子键
_In_opt_ DWORD ulOptions, //选项
_In_ REGSAM samDesired, //权限
_Out_ PHKEY phkResult //返回值句柄
);
②调用RegSetValueEx设置AppInit_DLLs的值为我们的动态库路径,LoadAppInit_DLLs的值为1,表示我们希望在User32.dll被加载时加载我们的动态库。
LSTATUS APIENTRY RegSetValueExW(
_In_ HKEY hKey, //返回的句柄值
_In_opt_ LPCWSTR lpValueName, //键名
_Reserved_ DWORD Reserved,
_In_ DWORD dwType, //键值类型
_In_ CONST BYTE* lpData, //存储键值的缓冲区
_In_ DWORD cbData //键值大小
);
③注册表位置:
64位:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows
32位:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
参考代码
int main()
{
HKEY Key;
LPCWSTR KeyValue1 = _T("AppInit_DLLs"); //需要设置的注册表项
LPCWSTR KeyValue2 = _T("LoadAppInit_DLLs"); //需要设置的注册表项
LPCWSTR SubKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\"); //子键路径
TCHAR DllPath[] = _T("C:\\Users\\Admin\\Desktop\\Game\\Dll.dll"); //动态库路径
DWORD LoadAppInitValue = 1; //设置值为1
int nSize = sizeof(DllPath); //动态库路径大小
//打开注册表
LSTATUS ReturnValue = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_ALL_ACCESS, &Key);
if (ReturnValue != ERROR_SUCCESS)
{
printf("Open Key Failed!\n");
return -1;
}
//设置Dll路径
ReturnValue = RegSetValueEx(Key, KeyValue1, 0, REG_SZ, (const BYTE*)DllPath, nSize + 1);
if (ReturnValue != ERROR_SUCCESS)
{
printf("Set DllPath Failed!\n");
return -1;
}
//设置启动进程时加载Dll
ReturnValue = RegSetValueEx(Key, KeyValue2, 0, REG_DWORD, (const BYTE*)&LoadAppInitValue, sizeof(DWORD));
if (ReturnValue != ERROR_SUCCESS)
{
printf("Set LoadAppInitValue Failed!\n");
return -1;
}
RegCloseKey(Key);
return 0;
}
局限性
上面说到,应用程序如果要加载AppInit_DLLs下的动态库时,前提是调用User32的DllMain函数,但是并不是所有的应用程序都会加载User32.dll。
与此同时,这种注入方法的目标比较单一,灵活性较差,即所有加载User32.dll的进程都会被注入,如果我们的Dll在某些进程中出现错误或崩溃,往往会产生无法控制的结果。
这也就导致了这种注入方法的局限性。