9X
環(huán)境中Windows提供了想光的API函數(shù)用于隱藏系統(tǒng)進程。但是到了
2000
以上系統(tǒng),已經(jīng)無法真正的做到對于進程的隱藏,除非編寫底層驅(qū)動。但是我們可以通過一些變通的辦法來達到隱藏進程的目的,其中一個就是遠程注入。簡單的說就是先編寫一個?API的DLL,然后將這個DLL庫注入到一個系統(tǒng)進程中,作為它的一個線程去執(zhí)行。
要實現(xiàn)DLL注入,首先需要打開目標進程。
hRemoteProcess?
=?
OpenProcess
(
PROCESS_CREATE_THREAD?
|?
//允許遠程創(chuàng)建線程
?PROCESS_VM_OPERATION
|?
//允許遠程VM操作
?PROCESS_VM_WRITE
,
//允許遠程VM寫
?FALSE
,?
dwRemoteProcessId?
)
由于我們后面需要寫入遠程進程的內(nèi)存地址空間并建立遠程線程,所以需要申請足夠的權限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
如果進程打不開,以后的操作就別想了。進程打開后,就可以建立遠線程了,不過別急,先想想這個遠線程的線程函數(shù)是什么?我們的目的是注入一個DLL。而且我們知道用LoadLibrary可以加載一個DLL到本進程的地址空間。于是,自然會想到如果可以在目標進程中調(diào)用LoadLibrary,不就可以把?DLL加載到目標進程的地址空間了嗎?對!就是這樣。遠線程就在這兒用了一次,建立的遠線程的線程函數(shù)就是LoadLibrary,而參數(shù)就是要注入的?DLL的文件名。
(
這里需要自己想一想,注意到了嗎,線程函數(shù)ThreadProc和LoadLibrary函數(shù)非常相似,返回值,參數(shù)個數(shù)都一樣
)?
還有一個問題,LoadLibrary這個函數(shù)的地址在哪兒?也許你會說,這個簡單,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指向的是你的進程里的數(shù)據(jù),到了目標進程,這個指針都不知道指向哪兒去了,同樣pfnStartAddr這個地址上的代碼到了目標進程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,問題總是可以解決的,Windows有些很強大的API函數(shù),他們可以在目標進程里分配內(nèi)存,可以將你的進程中的數(shù)據(jù)拷貝到目標進程中。因此?pszLibFileRemote的問題可以解決了。
char?
*
pszLibFileName
=
"my.dll"
;
//注意,這個一定要是全路徑文件名,除非它在系統(tǒng)目錄里;原因大家自己想想。
//計算DLL路徑名需要的內(nèi)存空間
int?
cb?
=?(
1?
+?
lstrlenA
(
pszLibFileName
))?*?
sizeof
(
char
);
//使用VirtualAllocEx函數(shù)在遠程進程的內(nèi)存地址空間分配DLL文件名緩沖區(qū)
pszLibFileRemote?
=?(
char?
*)?
VirtualAllocEx
(?
hRemoteProcess
,?
NULL
,?
cb
,?
MEM_COMMIT
,?
PAGE_READWRITE
);
//使用WriteProcessMemory函數(shù)將DLL的路徑名復制到遠程進程的內(nèi)存空間
iReturnCode?
=?
WriteProcessMemory
(
hRemoteProcess
,?
pszLibFileRemote
,?(
PVOID
)?
pszLibFileName
,?
cb
,?
NULL
);
????
OK,現(xiàn)在目標進程也認識pszLibFileRemote了,但是pfnStartAddr好像不好辦,我怎么可能知道LoadLibraryA在目標進程中的地址呢?其實Windows為我們解決了這個問題,LoadLibraryA這個函數(shù)是在Kernel32
.
dll這個核心DLL里的,而這個?DLL很特殊,不管對于哪個進程,Windows總是把它加載到相同的地址上去。因此你的進程中LoadLibraryA的地址和目標進程中?LoadLibraryA的地址是相同的
(
其實,這個DLL里的所有函數(shù)都是如此
)
。至此,DLL注入結(jié)束了。
?
但是目前還有一個問題,上面的方法是無法將DLL注入到系統(tǒng)進程中去的,原因是進程級別不夠。那么我們就要提升注入程序的進程級別。使用下面的函數(shù):
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"
;
//注意,這個一定要是全路徑文件名,除非它在系統(tǒng)目錄里
?
hRemoteProcess?
=?
OpenProcess
(
PROCESS_CREATE_THREAD
????????
|
PROCESS_VM_OPERATION
????????
|
PROCESS_VM_WRITE
,
????????
FALSE
,
0x3E0
);
//0x3E0是進程的id,測試時是explorer的進程id,可以用spy++去查找。
?//計算DLL路徑名需要的內(nèi)存空間
?
int?
cb?
=?(
1?
+?
lstrlenA
(
pszLibFileName
))?*?
sizeof
(
char
);
?
//使用VirtualAllocEx函數(shù)在遠程進程的內(nèi)存地址空間分配DLL文件名緩沖區(qū)
?
pszLibFileRemote?
=?(
char?
*)?
VirtualAllocEx
(
hRemoteProcess
,?
NULL
,?
cb
,
????????????
MEM_COMMIT
,?
PAGE_READWRITE
);
?
//使用WriteProcessMemory函數(shù)將DLL的路徑名復制到遠程進程的內(nèi)存空間
?
iReturnCode?
=?
WriteProcessMemory
(
hRemoteProcess
,
????
pszLibFileRemote
,?(
PVOID
)?
pszLibFileName
,?
cb
,?
NULL
);
?
//計算LoadLibraryW的入口地址
?
PTHREAD_START_ROUTINE?pfnStartAddr?
=?(
PTHREAD_START_ROUTINE
)
??????????
GetProcAddress
(
GetModuleHandle
(
TEXT
(
"Kernel32"
)),
??????????
"LoadLibraryA"
);
?
//啟動遠程線程LoadLibraryW,通過遠程線程調(diào)用用戶的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盤根目錄下面運行注入程序。我們可以發(fā)現(xiàn)彈出了一個標示了被注入進程id的對話框。
如上,只要我們再dll中編寫我們需要的代碼,就可以隱秘的在電腦里執(zhí)行我們需要的事情。