HOOK -- DLL的远程注入技术详解(1)

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,
Worl
d”
程序,用来作为寄生的目标进程。
 
 
 
然后在界面的编辑控件中输入进程的名称
“Target.exe”

单击

注入
DLL”
按钮,
这时候
V
irus.exe
就会将当前目录下的
DLL.dll
注入至
Target.exe
进程中。
 
 
 
在注入
DLL.dll
之后,你也可以单击

卸载
DLL”
来将已经注入的
DLL
卸载。
 
 
 
模拟的病毒体
DLL.dll
 
 
 
这是一个简单的
Win32
DLL
程序,它仅由一个入口函数
DllMain
组成:
 
BOOL
WINAPI
DllMain(
HINSTANCE
hinstDLL,
DWORD
fdwReason,
LPVOID
lpvRe
served
)
 
{switch
(
fdwReason
)
 
 
{
 
 
 
case
DLL_PROCESS_ATTACH:
 
 
 
{
 
 
 
 
MessageBox(
NULL,
_T("DLL
已进入目标进程。
"),
_T("
信息
"),
MB_ICONINFOR
MATION
);
 
 
 
}
 
 
 
break;
 
 
 
case
DLL_PROCESS_DETACH:
 
 
 
{
 
 
 
 
MessageBox(
NULL,
_T("DLL
已从目标进程卸载。
"),
_T("
信息
"),
MB_ICONINFO
RMATION
);
 
 
 
}
 
 
 
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_V
M_OPERATION
|
PROCESS_VM_WRITE,
FALSE,
dwProcessID
);
 
 
 
现在有必要说一下
OpenProcess
第一个参数所指定的三种权限。

Win32
系统下,

个进程都拥有自己的
4G
虚拟地址空间,
各个进程之间都相互独立。
如果一个进程需要完成
跨进程的工作的话,那么它必须拥有目标进程的相应操作权限。在这里,
PROCESS_CRE
ATE_THREAD
表示我可以通过返回的进程句柄在该进程中创建新的线程,也就是调用
Cr
eateRemoteThread
的权限;
同理,
PROCESS_VM_OPERATION
则表示在该进程中分配
/
释放内存的权限,也就是调用
VirtualAllocEx/VirtualFreeEx
的权限;
PROCESS_VM_WRI
TE
表示可以向该进程的地址空间写入数据,也就是调用
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
名称,最后再调用
CreateRemot
eThread
。这段代码就成了这样:
 
//
向目标进程地址空间写入
DLL
名称
 
DWORD
dwSize,
dwWritten;
 
dwSize
=
lstrlenA(
lpszDll
)
+
1;
 
LPVOID
lpBuf
=
VirtualAllocEx(
hProcess,
NULL,
dwSize,
MEM_COMMIT,
PAGE_R
EADWRITE
);
 
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

FreeLi
brary
。出于篇幅考虑,我略去了与注入部分相似或相同的代码:
 
//
使目标进程调用
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_ROUTIN
E)pFunc,
(LPVOID)dwHandle,
0,
&dwID
);
 
//
等待
FreeLibrary
卸载完毕
 
WaitForSingleObject(
hThread,
INFINITE
);
 
CloseHandle(
hThread
);
 
CloseHandle(
hProcess
);
 
 
 
用这个方法可以卸载一个进程中的
DLL
模块,当然包括那些非病毒体的
DLL
。所以,
这段代码还是谨慎使用为好。
 
 
 
在完成卸载之后,如果没有别的程序加载这个
DLL
,你就可以将它删除了。
 
 
 
 
HOOK --
实现
HOOK
其他进程的
Messagebox
、、
 
思路方法如下
:
 
1 DLL
的编写
(
实现
IAT hook)

 
2 DLL
注入工具
(
远程线程技术和简单的
MFC
控件知识
)

 
3
简单的测试程序、
 
 
1
编写
dll

 
之前有篇文章
HOOK -- IAT HOOK
本进程
MessageBox
、这里部分代码是相同的、毕竟均

IAT HOOK
嘛、、
 
DLL
与跟
exe
有个
main
或者
WinMain
入口函数一样也有一个入口函数
DllMain

不过很多
仅仅包含资源信息的
DLL
是没有
DllMain
函数的、其原型如下
 
BOOL WINAPI DllMain(  HINSTANCE hInstance,  ULONG ulReason,  LPVOID
Reserved);
 
何时调用
Dllmain

 
DllMain
的第二个参数
fdwReason
指明了系统调用
Dll
的原因、
 
 
 
DLL_PROCESS_ATTACH
、当一个
DLL
文件被映射到进程的地址空间时、用此参数掉
dllmain
 
                                               
当同一
DLL
再次映射时不会再调
DllMain
函数只增加
dll

数、
 
DLL_PROCESS_DETACH
、当
DLL
被从进程的地址空间解除映射时
FreeLibrary
 
                                               
进程结束而解除
DLL
映射
(
若是使用的
TerminateProcess

不调
)
 
 
 
DLL_THREAD_ATTACH
、当进程创建一线程时
,

DLL_PROCESS_ATTACH
区别
 
                            
无论何时创建线程均会用此参数调用
Dllmain
 
 
 
DLL_THREAD_DETACH  
如果线程调用了
ExitThread
来结束线程(线程函数返回时,系
统也会自动调用
ExitThread
),若是
TerminateThread
也不会调用
DllMain.
 
 
 
 
这里选择如下方式编写
DllMain
函数
 
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID
lpvReserved)
 
{
 
         If  ( fdwReason= =DLL_PROCESS_ATTACH)   //
加载
dll
时候调用
DllMain     
 
            _beginthread(ThreadProc,0,NULL); //
创建线程
      
 
         return TRUE;
 
}
 
就是说当
dll
被加载的时候
(LoadLirary)
、在
DllMain
中实现创建
ThreadProc
线程函数、、

ThreadProc
函数里边是我们
IAT HOOK
的核心代码、、
 
ThreadProc
函数

功能是
 
一个应用程序定义的函数作为一个线程的起始地址服务

 

Hook Messagebox
需写一个自己的
Messagebox
如下
:
 
int __stdcall HookMBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT
uType)
 
{
 
         return MessageBox(NULL,"
哈哈
! HOOK

MessageBox

","HOOK",MB_OK); //

明一下
Hook
成功
 
}
 
在线程函数中完成我们的
ITA HOOK
的代码
:
 
1  
获取本进程的模块基址
  HANDLE pBegin = GetModuleHandle(NULL);
 
2  
初始化
PE
结构得到
IAT
的地址
 
        
PBYTE  pBegin2 = (PBYTE)pBegin;
 
         PIMAGE_DOS_HEADER   DOS = PIMAGE_DOS_HEADER(pBegin2);
 
 
原文地址:https://www.cnblogs.com/chenjian/p/3508027.html