计算机病毒的惯用技术--注入技术

来源:互联网 发布:淘宝网店需要什么员工 编辑:程序博客网 时间:2024/04/28 03:39

本文是小生阅读《计算机病毒揭秘与对抗》的一篇笔记。这里还请大家支持原作者的书籍


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


DLL注入技术之远程线程注入DLL


一个进程要加载一个DLL通常要使用LoadLibrary函数,然而要加载LoadLibrary函数通常要在本进程内调用,无法令其他进程加载指定DLL,我们如何解决这个问题呢?

微软提供了一个API  CreateRemoteThread来实现本地进程促使其他进程开启并运行一个线程,即Windows提供的远程线程机制。所以想让其他进程执行LoadLibrary函数加载

DLL,那么只需令远程进程的LoadLibrary函数作为远程线程的线程函数,把要加载的DLL完整路径作为线程函数的参数即可。怎样获取远程进程中的LoadLibrary地址呢?

LoadLibrary函数由Kernel32.dll提供,而Kernel32.dll是系统基本库,几乎所有的程序都要加载这个模块。这样也就保证了系统中绝大数程序都可以调用LoadLibrary函数。而且

对于WinNT架构系统,2k / xp等,所有进程加载Kernel32.dll模块基址都是固定并且一致的,这就意味着这些系统的所有进程中所引入的LoadLibrary函数的地址是一致的。这样

就可以把本进程中的LoadLibrary函数的地址传到CreateRemoteThread远程线程函数的起始地址。因为在远程进程执行LoadLibrary,所以其参数也必须位于远程进程中。

我们知道这个参数是一个包含要加载的动态链接库的完整路径字符串的首地址。下一个问题就是远程进程中怎么有我们要的字符串呢?可以使用系统提供好的API把需要的信息

写入远程进程中去。通常是OpenProcess、VirtualAllocEx、WriteProcessMemory、VirtualFreeEx函数来实现。

上面扯了这么多,下面来看核心的实现代码:

/* 获取本进程LoadLibrary函数的地址 */
pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(        GetModuleHandle(L"Kernel32"),  // 模块句柄        "LoadLibraryW");   // 函数名


/* 向远程进程空间写入我们需要的参数字符串 */
// 1. 获取目标进程的句柄    if( !(hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid)) )        return FALSE;    // 2. 计算DLL路径名需要的字节数    dwSize = wcslen(lpszLibName)*sizeof(WCHAR) + sizeof(WCHAR);    // 3. 在远程线程中为路径名分配空间    pszLibFileRemote = (PSTR)VirtualAllocEx(        hProcess,     // 进程句柄        NULL,      // 想申请的地址        dwSize,       // 申请的大小        MEM_COMMIT,     // 申请的内存类型        PAGE_READWRITE );  // 申请的内存分页类型    if( !pszLibFileRemote )    {        if( hProcess )            CloseHandle(hProcess);        return FALSE;    }    // 4. 将DLL的路径名复制到远程进程的内存空间    if( !WriteProcessMemory(        hProcess,         // 进程句柄        (PVOID)pszLibFileRemote,         // 写入的目的进程的内存地址        (PVOID)lpszLibName,        // dll名        dwSize,         // 写入的字节数        NULL           // 传出值,返回写入的字节数        ) )    {         int n = GetLastError();         return FALSE;           }

/* 释放分配的内存 */
VirtualFreeEx(                hProcess,  // 进程句柄                (PVOID)pszLibFileRemote,  // 需要释放目标进程的地址                0,    // 释放的大小 MEM_RELEASE时必须为0                MEM_RELEASE // 释放类型 MEM_RELEASE 彻底释放                );


/* 远程线程注入 */
// 6. 创建远程线程,并通过远程线程调用用户的DLL文件    hThread = CreateRemoteThread(        hProcess,   // 进程句柄        NULL,         // 安全类型        0,    // 栈大小        pfnThreadRtn,   // 线程回调函数地址        (PVOID)pszLibFileRemote,  // 线程回调函数参数        0,  // 创建标志        NULL   // 传出值, 线程ID        );    int n = GetLastError();    if( hThread==NULL )        return FALSE;    // 7. 等待远程线程终止    WaitForSingleObject(hThread, INFINITE);    // 8. 释放相关资源并关闭句柄

到这里这里远程线程注入DLL的知识就结束了。


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

DLL注入技术之全局消息钩子注入DLL


关于钩子的安装和卸载这里不错过多的赘述,在星号密码查看器一文中有相关实例。

全局消息钩子不同于局部消息钩子,全局钩子的回调函数必须在一个DLL中,当安装好指定类型的全局钩子后,系统中凡是触发响应消息的进程都会加载这个DLL,然后调用

DLL模块中的回调函数。因为钩子回调函数位于DLL中,所以通常把安装钩子的函数也放到DLL中。因为DLL不能直接运行,必须被一个EXE加载后才可以运行。所以安装全局

钩子需要两个工程(dll && exe)。

全局钩子执行原理:

当在系统中安装一个全局键盘钩子后,无论任何一个进程的窗口收到按键消息都会将全局钩子所在DLL加载到该进程空间,并且调用该DLL模块中的钩子回调函数。


仅将钩子回调函数写在DLL中的问题分析:


不再需要安装与卸载钩子函数,导出钩子回调函数。

注意一:在钩子回调函数中需要调用CallNextHookEx将钩子继续向下传递,而该函数的第一个参数即为钩子句柄。此时安装钩子功能代码位于exe中,那么其返回的钩子句柄如何传递给DLL?

方案一:可以在DLL中导出一个变量,在exe中获取这个变量的地址,并将自身获得的钩子句柄为其赋值,这样DLL就有了钩子句柄了。

方案一遗留问题:这样在安装钩子的exe进程中的dll模块得到钩子句柄,其他进程加载了该DLL后如何得到钩子句柄?

方案一遗留问题解决:可以使用共享内存的办法,将DLL中保存钩子句柄的变量放在共享内存中,这样所有进程对该DLL的这个变量都是共享的。代码:
/* 将保存钩子句柄的变量存放在单独的数据段SharedSection中 */#pragma data_seg("SharedSection")/* 保存钩子句柄 */HHOOK g_hHook = NULL;#pragma data_seg()/* 设置该数据段的属性为可读/写/共享 */#pragma comment(linker, "/section:SharedSection, RWS")

准备工作做好后,在exe代码中我们就可以按方案一的解决方法来操作了:
/* 获取dll导出的用于保存钩子句柄的变量地址 */FARPROC hHook = GetProcAddress(/*DLL模块句柄,LoadLibrary函数返回*/, /*导出的变量名*/);/* 将钩子句柄赋值给DLL导出的变量 */*((DWORD*)hHook) = (DWORD)/*安装钩子返回的钩子句柄*/

0 0