开发者社区> xumaojun> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

DLL的远程注入技术

简介:
+关注继续查看

DLL的远程注入技术是目前Win32病毒广泛使用的一种技术。使用这种技术的病毒体通常位于一个DLL中,在系统启动的时候,一个EXE程序会将这个DLL加载至某些系统进程(如Explorer.exe)中运行。这样一来,普通的进程管理器就很难发现这种病毒了,而且即使发现了也很难清除,因为只要病毒寄生的进程不终止运行,那么这个DLL就不会在内存中卸载,用户也就无法在资源管理器中删除这个DLL文件,真可谓一箭双雕哉。

记得2003年QQ尾巴病毒肆虐的时候,就已经有些尾巴病毒的变种在使用这种技术了。到了2004年初,我曾经尝试着仿真了一个QQ尾巴病毒,但独是跳过了DLL的远程加载技术。直到最近在学校论坛上看到了几位朋友在探讨这一技术,便忍不住将这一尘封已久的技术从我的记忆中拣了出来,以满足广大的技术爱好者们。

必备知识

在阅读本文之前,你需要了解以下几个API函数:

OpenProcess - 用于打开要寄生的目标进程。
VirtualAllocEx/VirtualFreeEx - 用于在目标进程中分配/释放内存空间。
WriteProcessMemory - 用于在目标进程中写入要加载的DLL名称。
CreateRemoteThread - 远程加载DLL的核心内容,用于控制目标进程调用API函数。
LoadLibrary - 目标进程通过调用此函数来加载病毒DLL。

在此我只给出了简要的函数说明,关于函数的详细功能和介绍请参阅MSDN。

示例程序

我将在以下的篇幅中用一个简单的示例Virus.exe来实现这一技术。这个示例的界面如下图:

首先运行Target.exe,这个文件是一个用Win32 Application向导生成的“Hello, World”程序,用来作为寄生的目标进程。

然后在界面的编辑控件中输入进程的名称“Target.exe”,单击“注入DLL”按钮,这时候Virus.exe就会将当前目录下的DLL.dll注入至Target.exe进程中。

在注入DLL.dll之后,你也可以单击“卸载DLL”来将已经注入的DLL卸载。

点这里下载示例程序

模拟的病毒体DLL.dll

这是一个简单的Win32 DLL程序,它仅由一个入口函数DllMain组成:

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
switch ( fdwReason )
    {
case DLL_PROCESS_ATTACH:
        {
            MessageBox( NULL, _T("DLL已进入目标进程。"), _T("信息"), MB_ICONINFORMATION );
        }
break;
case DLL_PROCESS_DETACH:
        {
            MessageBox( NULL, _T("DLL已从目标进程卸载。"), _T("信息"), MB_ICONINFORMATION );
        }
break;
    }
return TRUE;
}

如你所见,这里我在DLL被加载和卸载的时候调用了MessageBox,这是用来显示我的远程注入/卸载工作是否成功完成。而对于一个真正的病毒体来说,它往往就是处理DLL_PROCESS_ATTACH事件,在其中加入了启动病毒代码的部分:

case DLL_PROCESS_ATTACH:
    {
        StartVirus();
    }
break;

注入!

现在要开始我们的注入工作了。首先,我们需要找到目标进程:

DWORD FindTarget( LPCTSTR lpszProcess )
{
    DWORD dwRet = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof( PROCESSENTRY32 );
    Process32First( hSnapshot, &pe32 );
do
    {
if ( lstrcmpi( pe32.szExeFile, lpszProcess ) == 0 )
        {
            dwRet = pe32.th32ProcessID;
break;
        }
    } while ( Process32Next( hSnapshot, &pe32 ) );
    CloseHandle( hSnapshot );
return dwRet;
}

这里我使用了Tool Help函数库,当然如果你是NT系统的话,也可以选择PSAPI函数库。这段代码的目的就是通过给定的进程名称来在当前系统中查找相应的进程,并返回该进程的ID。得到进程ID后,就可以调用OpenProcess来打开目标进程了:

// 打开目标进程
HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessID );

现在有必要说一下OpenProcess第一个参数所指定的三种权限。在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都相互独立。如果一个进程需要完成跨进程的工作的话,那么它必须拥有目标进程的相应操作权限。在这里,PROCESS_CREATE_THREAD表示我可以通过返回的进程句柄在该进程中创建新的线程,也就是调用CreateRemoteThread的权限;同理,PROCESS_VM_OPERATION则表示在该进程中分配/释放内存的权限,也就是调用VirtualAllocEx/VirtualFreeEx的权限;PROCESS_VM_WRITE表示可以向该进程的地址空间写入数据,也就是调用WriteProcessMemory的权限。

至此目标进程已经打开,那么我们该如何来将DLL注入其中呢?在这之前,我请你看一行代码,是如何在本进程内显式加载DLL的:

HMODULE hDll = LoadLibrary( "DLL.dll" );

那么,如果能控制目标进程调用LoadLibrary,不就可以完成DLL的远程注入了么?的确是这样,我们可以通过CreateRemoteThread将LoadLibrary作为目标进程的一个线程来启动,这样就可以完成“控制目标进程调用LoadLibrary”的工作了。到这里,也许你会想当然地写下类似这样的代码:

DWORD dwID;
LPVOID pFunc = LoadLibraryA;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)"DLL.dll", 0, &dwID );

不过结果肯定会让你大失所望——注入DLL失败!

嗯嗯,那么现在让我们来分析一下失败的原因吧。我是前说过,在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。在这里,我们当作参数传入的字符串"DLL.dll"其实是一个数值,它表示这个字符串位于Virus.exe地址空间之中的地址,而这个地址在传给Target.exe之后,它指向的东西就失去了有效性。举个例子来说,譬如A、B两栋大楼,我住在A楼的401;那么B楼的401住的是谁我当然不能确定——也就是401这个门牌号在B楼失去了有效性,而且如果我想要入住B楼的话,我就必须请B楼的楼长为我在B楼中安排新的住处(当然这个新的住处是否401也就不一定了)。

由此看来,我就需要做这么一系列略显繁杂的手续——首先在Target.exe目标进程中分配一段内存空间,然后向这段空间写入我要加载的DLL名称,最后再调用CreateRemoteThread。这段代码就成了这样:

// 向目标进程地址空间写入DLL名称
DWORD dwSize, dwWritten;
dwSize = lstrlenA( lpszDll ) + 1;
LPVOID lpBuf = VirtualAllocEx( hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE );
if ( NULL == lpBuf )
{
    CloseHandle( hProcess );
// 失败处理
}
if ( WriteProcessMemory( hProcess, lpBuf, (LPVOID)lpszDll, dwSize, &dwWritten ) )
{
// 要写入字节数与实际写入字节数不相等,仍属失败
if ( dwWritten != dwSize )
    {
        VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
        CloseHandle( hProcess );
// 失败处理
    }
}
else
{
    CloseHandle( hProcess );
// 失败处理
}
// 使目标进程调用LoadLibrary,加载DLL
DWORD dwID;
LPVOID pFunc = LoadLibraryA;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );

需要说的有两点,一是由于我要在目标进程中为ANSI字符串来分配内存空间,所以这里凡是和目标进程相关的部分,都明确使用了后缀为“A”的API函数——当然,如果要使用Unicode字符串的话,可以换作后缀是“W”的API;第二,在这里LoadLibrary的指针我是取的本进程的LoadLibraryA的地址,这是因为LoadLibraryA/LoadLibraryW位于kernel32.dll之中,而Win32下每个应用程序都会把kernel32.dll加载到进程地址空间中一个固定的地址,所以这里的函数地址在Target.exe中也是有效的。

在调用LoadLibrary完毕之后,我们就可以做收尾工作了:

// 等待LoadLibrary加载完毕
WaitForSingleObject( hThread, INFINITE );
// 释放目标进程中申请的空间
VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
CloseHandle( hThread );
CloseHandle( hProcess );

在此解释一下WaitForSingleObject一句。由于我们是通过CreateRemoteThread在目标进程中另外开辟了一个LoadLibrary的线程,所以我们必须等待这个线程运行完毕才能够释放那段先前申请的内存。

好了,现在你可以尝试着整理这些代码并编译运行。运行Target.exe,然后开启一个有模块查看功能的进程查看工具(在这里我使用我的July)来查看Target.exe的模块,你会发现在注入DLL之前,Target.exe中并没有DLL.dll的存在:

在调用了注入代码之后,DLL.dll就位于Target.exe的模块列表之中了:

矛盾相生

记得2004年初我将QQ尾巴病毒成功仿真后,有很多网友询问我如何才能杀毒,不过我都没有回答——因为当时我研究的重点并非病毒的寄生特性。这一寄生特性直到今天可以说我才仿真完毕,那么,我就将解毒的方法也一并公开吧。

和DLL的注入过程类似,只不过在这里使用了两个API:GetModuleHandle和FreeLibrary。出于篇幅考虑,我略去了与注入部分相似或相同的代码:

// 使目标进程调用GetModuleHandle,获得DLL在目标进程中的句柄
DWORD dwHandle, dwID;
LPVOID pFunc = GetModuleHandleA;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );
// 等待GetModuleHandle运行完毕
WaitForSingleObject( hThread, INFINITE );
// 获得GetModuleHandle的返回值
GetExitCodeThread( hThread, &dwHandle );
// 释放目标进程中申请的空间
VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
CloseHandle( hThread );
// 使目标进程调用FreeLibrary,卸载DLL
pFunc = FreeLibrary;
hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwHandle, 0, &dwID );
// 等待FreeLibrary卸载完毕
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
CloseHandle( hProcess );

用这个方法可以卸载一个进程中的DLL模块,当然包括那些非病毒体的DLL。所以,这段代码还是谨慎使用为好。

在完成卸载之后,如果没有别的程序加载这个DLL,你就可以将它删除了。

到此为止,整个的技术细节我就讲完了。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
硬核!!教你如何通过脚本自动部署虚拟机并安装操作系统
硬核!!教你如何通过脚本自动部署虚拟机并安装操作系统
169 0
【云吞铺子】CDN域名接入流程
好玩有趣的云计算知识系列视频!
2556 0
远程线程注入技术 屏蔽ctrl+alt+del
在NT/2000中怎么禁用Ctrl+Alt+Delete?(不能用gina,键盘驱动) 在Windows2000中Ctrl-Alt-Delete组合键的处理如下:Winlogon初始化的时候,在系统中注册了CTRL+ALT+DEL Secure Attention Sequence(SAS)热键,并且在WinSta0 Windows 系统中创建三个桌面。
1474 0
远程注入DLL(简单描述)
远程注入DLL(简单描述) 远程注入DLL 建议在阅读本文前先阅读取得句柄的令牌 OpenProcessToken()函数一文! 没错,现在讨论的就是传说中的远程注入技术,目前一种非常流行的隐藏技术,因为它没有进程,是通过远程插入线程调用DLL文件实现的!在这里先说一下.
871 0
远程注入DLL(简单描述)
远程注入DLL(简单描述) 远程注入DLL 建议在阅读本文前先阅读取得句柄的令牌 OpenProcessToken()函数一文! 没错,现在讨论的就是传说中的远程注入技术,目前一种非常流行的隐藏技术,因为它没有进程,是通过远程插入线程调用DLL文件实现的!在这里先说一下.
895 0
.NET 世界中的远程脚本
以前在开发ASP程序时,根据客户的要求,不能使用刷新页面要对数据进行更新。就使用当时比较新的方法(Microsoft Remote Scripting)。还记得那个 Rs.asp文件吗?呵呵,我们来回顾一下老的调用方法:   有三个文件:    Rs.
718 0
一起谈.NET技术,URI和URL及URN的区别
  对于URL,大家都比较熟悉,其他两个词就比较陌生了。URI、URL和URN是识别、定位和命名互联网上的资源的标准途径。1989年Tim Berners-Lee发明了互联网(World Wide Web)。
888 0
+关注
xumaojun
乐于学习与分析
1079
文章
95
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载