創(chuàng)建時間:2002-02-05
文章屬性:原創(chuàng)
文章來源:
www.whitecell.org文章提交:
ILSY (masteruser_at_263.net)
Author : ilsy
Email : ilsy@whitecell.org
HomePage:
http://www.whitecell.org 日 期:2002-02-06
類 別:安全
關(guān)鍵字:進程 PDE PTE 分頁 內(nèi)核對象 線性地址 物理地址
關(guān)于進程與端口映射的文章已經(jīng)有很多了,我把我對fport的分析也寫出來,讓大家知道fport是如何工作的.
fport.exe是由foundstone team出品的免費軟件,可以列出系統(tǒng)中所有開放的端口都是由那些進程打開的.而下
面所描述的方法是基于fport v1.33的,如果和你機器上的fport有出入,請檢查fport版本.
首先,它檢測當(dāng)前用戶是否擁有管理員權(quán)限(通過讀取當(dāng)前進程的令牌可知當(dāng)前用戶是否具有管理權(quán)限,請參考
相關(guān)歷程),如果沒有,打印一句提示后退出,然后設(shè)置當(dāng)前進程的令牌,接著,用ZwOpenSection函數(shù)打開內(nèi)核對象
\Device\PhysicalMemory,這個對象用于對系統(tǒng)物理內(nèi)存的訪問.ZwOpenSection函數(shù)的原型如下:
NTSYSAPI
NTSTSTUS
NTAPI
ZwOpenSection(
Out PHANDLE sectionHandle;
IN ACCESS_MASK DesiredAccess;
IN POBJECT_ATTRIBUTES ObjectAttributes
};
(見ntddk.h)
第一個參數(shù)得到函數(shù)執(zhí)行成功后的句柄
第二個參數(shù)DesiredAccess為一個常數(shù),可以是下列值:
#define SECTION_QUERY 0x0001
#define SECTION_MAP_WRITE 0x0002
#define SECTION_MAP_READ 0x0004
#define SECTION_MAP_EXECUTE 0x0008
#define SECTION_EXTEND_SIZE 0x0010
#define SECTION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SECTION_QUERY|\
SECTION_MAP_WRITE | \
SECTION_MAP_READ | \
SECTION_MAP_EXECUTE | \
SECTION_EXTEND_SIZE)
(見ntddk.h)
第三個參數(shù)是一個結(jié)構(gòu),包含要打開的對象類型等信息,結(jié)構(gòu)定義如下:
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
(見ntdef.h)
對于這個結(jié)構(gòu)的初始化用一個宏完成:
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
(見ntdef.h)
那么,打開內(nèi)核對象\Device\PhysicalMemory的語句如下:
WCHAR PhysmemName[] = L"\\Device\\PhysicalMemory";
void * pMapPhysicalMemory;
HANDLE pHandle;
bool OpenPhysicalMemory()
{
NTSTATUS status;
UNICODE_STRING physmemString;
OBJECT_ATTRIBUTES attributes;
RtlInitUnicodeString( &physmemString, PhysmemName ); //初始化Unicode字符串,函數(shù)原型見ntddk.h
InitializeObjectAttributes( &attributes, &physmemString,
OBJ_CASE_INSENSITIVE, NULL, NULL ); //初始化OBJECT_ATTRIBUTES結(jié)構(gòu)
status = ZwOpenSection(pHandle, SECTION_MAP_READ, &attributes ); //打開內(nèi)核對象\Device\PhysicalMemory,獲得句柄
if( !NT_SUCCESS( status ))
return false;
pMapPhysicalMemory=MapViewOfFile(pHandle,FILE_MAP_READ,
0,0x30000,0x1000);
//從內(nèi)存地址0x30000開始映射0x1000個字節(jié)
if( GetLastError()!=0)
return false;
return true;
}
為什么要從0x30000開始映射呢,是這樣,我們知道,在Windows NT/2000下,系統(tǒng)分為內(nèi)核模式和用戶模式,也就是我們
所說的Ring0和Ring3,在Windows NT/2000下,我們所能夠看到的進程都運行在Ring3下,一般情況下,系統(tǒng)進程(也就是System
進程)的頁目錄(PDE)所在物理地址地址為0x30000,或者說,系統(tǒng)中最小的頁目錄所在的物理地址為0x30000.而頁目錄(PDE)由
1024項組成,每項均指向一頁表(PTE),每一頁表也由1024個頁組成,而每頁的大小為4K,1024*4=4096(0x1000),所以,上面從物
理地址0x30000開始映射了0x1000個字節(jié).(具體描述見WebCrazy的文章<<小議Windows NT/2000的分頁機制>>)
程序打開打開內(nèi)核對象\Device\PhysicalMemory后,繼續(xù)用函數(shù)ZwOpenFile打開內(nèi)核對象\Device\Tcp和Device\Udp,ZwOpenFile
函數(shù)的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwOpenFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG OpenOptions
);
(見ntddk.h)
第一個參數(shù)返回打開對象的句柄
第二個參數(shù)DesiredAccess為一個常數(shù),可以是下列值:
#define FILE_READ_DATA ( 0x0001 ) // file & pipe
#define FILE_LIST_DIRECTORY ( 0x0001 ) // directory
#define FILE_WRITE_DATA ( 0x0002 ) // file & pipe
#define FILE_ADD_FILE ( 0x0002 ) // directory
#define FILE_APPEND_DATA ( 0x0004 ) // file
#define FILE_ADD_SUBDIRECTORY ( 0x0004 ) // directory
#define FILE_CREATE_PIPE_INSTANCE ( 0x0004 ) // named pipe
#define FILE_READ_EA ( 0x0008 ) // file & directory
#define FILE_WRITE_EA ( 0x0010 ) // file & directory
#define FILE_EXECUTE ( 0x0020 ) // file
#define FILE_TRAVERSE ( 0x0020 ) // directory
#define FILE_DELETE_CHILD ( 0x0040 ) // directory
#define FILE_READ_ATTRIBUTES ( 0x0080 ) // all
#define FILE_WRITE_ATTRIBUTES ( 0x0100 ) // all
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\
FILE_READ_DATA |\
FILE_READ_ATTRIBUTES |\
FILE_READ_EA |\
SYNCHRONIZE)
#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\
FILE_WRITE_DATA |\
FILE_WRITE_ATTRIBUTES |\
FILE_WRITE_EA |\
FILE_APPEND_DATA |\
SYNCHRONIZE)
#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\
FILE_READ_ATTRIBUTES |\
FILE_EXECUTE |\
SYNCHRONIZE)
(見ntdef.h)
第三個參數(shù)是一個結(jié)構(gòu),包含要打開的對象類型等信息,結(jié)構(gòu)定義見上面所述
第四個參數(shù)返回打開對象的屬性,是一個結(jié)構(gòu),定義如下:
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
#if defined(_WIN64)
typedef struct _IO_STATUS_BLOCK32 {
NTSTATUS Status;
ULONG Information;
} IO_STATUS_BLOCK32, *PIO_STATUS_BLOCK32;
#endif
(見ntddk.h)
第五個參數(shù)ShareAccess是一個常數(shù),可以是下列值:
#define FILE_SHARE_READ 0x00000001 // winnt
#define FILE_SHARE_WRITE 0x00000002 // winnt
#define FILE_SHARE_DELETE 0x00000004 // winnt
(見ntddk.h)
第六個參數(shù)OpenOptions也是一個常數(shù),可以是下列的值:
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_WRITE_THROUGH 0x00000002
#define FILE_SEQUENTIAL_ONLY 0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_CREATE_TREE_CONNECTION 0x00000080
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
#define FILE_NO_EA_KNOWLEDGE 0x00000200
#define FILE_OPEN_FOR_RECOVERY 0x00000400
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_DELETE_ON_CLOSE 0x00001000
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
#define FILE_NO_COMPRESSION 0x00008000
#define FILE_RESERVE_OPFILTER 0x00100000
#define FILE_OPEN_REPARSE_POINT 0x00200000
#define FILE_OPEN_NO_RECALL 0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
#define FILE_COPY_STRUCTURED_STORAGE 0x00000041
#define FILE_STRUCTURED_STORAGE 0x00000441
#define FILE_VALID_OPTION_FLAGS 0x00ffffff
#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032
#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032
#define FILE_VALID_SET_FLAGS 0x00000036
(見ntddk.h)
那么,打開內(nèi)核對象\Device\Tcp和\Device\Udp的語句如下:
WCHAR physmemNameTcp[]=L"\\Device\\TCP";
WCHAR physmemNameUdp[]=L"\\Device\\UDP";
HANDLE pTcpHandle;
HANDLE pUdpHandle;
HANDLE OpenDeviceTcpUdp(WCHAR * deviceName)
{
NTSTATUS status;
UNICODE_STRING physmemString;
OBJECT_ATTRIBUTES attributes;
IO_STATUS_BLOCK iosb;
HANDLE pDeviceHandle;
RtlInitUnicodeString(&physmemString, deviceName);
if(GetLastError()!=0)
return NULL;
InitializeObjectAttributes( &attributes,&physmemString,
OBJ_CASE_INSENSITIVE,0, NULL );
status = ZwOpenFile ( &pDeviceHandle,0x100000, &attributes, &iosb, 3,0);
if( !NT_SUCCESS( status ))
return NULL;
}
接著,程序用ZwQuerySystemInformation函數(shù)獲得系統(tǒng)當(dāng)前所以進程的所建立的句柄及其相關(guān)信息,函數(shù)的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
};
(這個函數(shù)結(jié)構(gòu)Microsoft沒有公開,參見Gary Nebbett<<Windows NT/2000 Native API Reference>>)
第一個參數(shù)是一個枚舉常數(shù),設(shè)置要查詢的系統(tǒng)信息類型,ZwQuerySystemInformation支持54個系統(tǒng)信息的查詢,我們要用到的
是它的第16號功能,進行SystemHandleInformation查詢.
SYSTEM_HANDLE_INFORMATION結(jié)構(gòu)定義如下:
typedef struct _SYSTEM_HANDLE_INFORMATION{
ULONG ProcessID; //進程的標(biāo)識ID
UCHAR ObjectTypeNumber; //對象類型
UCHAR Flags; //0x01 = PROTECT_FROM_CLOSE,0x02 = INHERIT
USHORT Handle; //對象句柄的數(shù)值
PVOID Object; //對象句柄所指的內(nèi)核對象地址
ACCESS_MASK GrantedAccess; //創(chuàng)建句柄時所準(zhǔn)許的對象的訪問權(quán)
}SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
(這個函數(shù)結(jié)構(gòu)Microsoft沒有公開,參見Gary Nebbett<<Windows NT/2000 Native API Reference>>)
第二個參數(shù)輸出查詢的結(jié)果
第三個參數(shù)設(shè)置緩沖區(qū)的長度
第四個參數(shù)返回函數(shù)正確執(zhí)行需要的緩沖區(qū)的大小
代碼如下:
#define SystemHandleInformation 16
PULONG GetHandleList()
{
ULONG cbBuffer = 0x1000; //先設(shè)定一個較小的緩沖空間
PULONG pBuffer = new ULONG[cbBuffer]; //分配內(nèi)存
NTSTATUS Status;
do
{
Status = ZwQuerySystemInformation(
SystemHandleInformation,
pBuffer, cbBuffer * sizeof * pBuffer, NULL);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
//如果返回的錯誤信息為緩沖區(qū)長度不夠,那么重新分配內(nèi)存
delete [] pBuffer;
pBuffer = new ULONG[cbBuffer *= 2];
}
else if (!NT_SUCCESS(Status))
{
//如果是其他錯誤信息,返回
delete [] pBuffer;
return false;
}
}
while (Status == STATUS_INFO_LENGTH_MISMATCH);
return pBuffer;
}
因為如果一個進程打開了端口,那么它肯定會建立類型為\Device\Tcp和\Device\Udp的內(nèi)核對象,所以,我們在當(dāng)前進程中打開
上述的兩個內(nèi)核對象,在打開的同時保存了打開的句柄,這樣,我們可以在上面獲得的句柄列表中的當(dāng)前進程中查找對象句柄的
數(shù)值和我們保存的兩個打開的內(nèi)核對象的句柄數(shù)值相同的句柄,并得到其句柄所指向的內(nèi)核對象的地址.代碼如下:
DWORD TcpHandle;
DWORD UdpHandle;
DWORD GetTcpUdpObject(PULONG pBuffer,HANDLE pHandle,DWORD ProcessId)
{
DWORD objTYPE1,objTYPE2,HandleObject;
PSYSTEM_HANDLE_INFORMATION pProcesses = (PSYSTEM_HANDLE_INFORMATION)(pBuffer+1);
for (i=0;i< * pBuffer;i++)
{
if ((pProcesses[i].ProcessID) == ProcessId)
{
objTYPE1 = (DWORD)hDeviceTcpUdp;
objTYPE2 = (DWORD)pProcesses[i].Handle;
if(objTYPE1==objTYPE2)
{
HandleObject = (DWORD)pProcesses.Object;
return HandleObject;
}
}
return 0;
}
這個內(nèi)核對象地址是一個線性地址,我們需要把這個地址轉(zhuǎn)換為物理地址,并得到一些相關(guān)的數(shù)據(jù).在fport中,換算是這樣進行的:
(具體描述見WebCrazy的文章<<小議Windows NT/2000的分頁機制>>)
void * NewmapPhy;
void GetPTE(DWORD objAddress)
{
DWORD physmemBuff;
DWORD newAddress1,newAddress2,newAddress3,newAddress4;
DWORD * newAddress;
physmemBuff = (DWORD)pMapPhysicalMemory;
newAddress1 = physmemBuff+(objAddress>>0x16)*4;
newAddress = (DWORD *)newAddress1;
newAddress1 = * newAddress;
newAddress2 = objAddress & 0x3FF000;
newAddress3 = newAddress1 & 0x0FFFFF000;
newAddress4 = newAddress2 + newAddress3;
NewmapPhy = MapViewOfFile(ghPhysicalMemory,FILE_MAP_READ,0,newAddress4,0x1000);
//重新映射物理內(nèi)存,得到當(dāng)前線性地址所指向的PTE的物理地址內(nèi)容
}
然后在根據(jù)內(nèi)核對象的線性地址得到這個地址所指向的物理頁,得到體現(xiàn)當(dāng)前內(nèi)核對象內(nèi)容的頁,其結(jié)構(gòu)如下:
typedef struct {
ULONG Present;
ULONG WriteTable;
ULONG User;
ULONG WriteThru;
ULONG NoCache;
ULONG Accessed;
ULONG Dirty;
ULONG PageSize;
ULONG Global;
ULONG Available;
ULONG Pfn;
} PTE, *PPTE;
(注:我不能保證這個結(jié)構(gòu)的正確性,但我們只會用到其中的兩個值,對程序來說,這個結(jié)構(gòu)是可以工作的,^_^)
代碼如下:
ULONG CurrWriteTable;
ULONG NoCache;
void GetMustPar(DWORD objAddress)
{
DWORD CurrAddress;
CurrAddress = objAddress & 0xFFF;
PPTE pte = (PPTE)(VOID *)((DWORD)NewmapPhy+CurrAddress);
CurrWriteTable = pte->WriteTable;
CurrNoCache = Pte->NoCache;
}
好了,我們現(xiàn)在想要得到的都已經(jīng)得到了,下面需要做的是遍歷進程,用每一個進程中的每一個句柄(呵呵,不是每一個句柄,
在Windows NT下,\Device\Tcp和\Device\Udp的句柄類型值為0x16,在Windows 2000下這個值為0x1A)的核心地址用上面所描
述的辦法得到其PTE內(nèi)容,得到其WriteTable值,如果與內(nèi)核對象\Device\Tcp和\Device\Udp相等,那么這個句柄就有可能打開
了一個端口,再對這個句柄進行確認(rèn),就可以了.確認(rèn)的代碼如下:
typedef struct _TDI_CONNECTION_INFO {
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, *PTDI_CONNECTION_INFO;
typedef struct _TDI_CONNECTION_INFORMATION {
LONG UserDataLength;
PVOID UserData;
LONG OptionsLength;
PVOID Options;
LONG RemoteAddressLength;
PVOID RemoteAddress;
} TDI_CONNECTION_INFORMATION, *PTDI_CONNECTION_INFORMATION;
(以上結(jié)構(gòu)見tdi.h)
void GetOpenPort(DWORD dwProcessesID,USHORT Handle,int NoCache)
//dwProcessesID為進程標(biāo)識ID
//Handle為進程打開的句柄,并且經(jīng)過比較為\Device\Tcp或\Device\Udp類型
//NoCache為PTE結(jié)構(gòu)中的一個值
{
HANDLE hProc,DupHandle=NULL;
HANDLE hEven=NULL;
OVERLAPPED overlap;
u_short openport;
int i=0;
char procName[256]={0};
int portflag=0;
overlap.Internal = 0;
overlap.InternalHigh = 0;
overlap.Offset = 0;
overlap.OffsetHigh = 0;
hEven=CreateEvent(0,1,0,0);
overlap.hEvent = hEven;
hProc = OpenProcess(PROCESS_DUP_HANDLE,
0,
dwProcessesID);
if(hProc)
{
DuplicateHandle(hProc,
(HANDLE)Handle,
GetCurrentProcess(),
&DupHandle,
0,
FALSE,
2);
CloseHandle( hProc );
if(DupHandle)
{
TDI_CONNECTION_INFO TdiConnInfo={0};
TDI_CONNECTION_INFORMATION TdiConnInformation={0};
DWORD dwRetu=0;
if(NoCache==0x2)
{
TdiConnInformation.RemoteAddressLength= 4;
if(DeviceIoControl(DupHandle,0x210012,
&TdiConnInformation,sizeof(TdiConnInformation),
&TdiConnInfo,sizeof(TdiConnInfo),
0,&overlap))
//進行TDI查詢,得到連接的相關(guān)信息
{
openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
procname = GetProcName(dwProcessesID); //得到進程標(biāo)識ID的進程名稱
printf("PID = %4d ProcessName = %15s PORT = %4d\n",dwProcessesID,procName,openport);
}
}
if(NoCache==0x1)
{
TdiConnInformation.RemoteAddressLength= 3;
if(DeviceIoControl(DupHandle,0x210012,
&TdiConnInformation,sizeof(TdiConnInformation),
&TdiConnInfo,sizeof(TdiConnInfo),
0,&overlap))
//進行TDI查詢,得到連接的相關(guān)信息
{
openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
procname = GetProcName(dwProcessesID); //得到進程標(biāo)識ID的進程名稱
printf("PID = %4d ProcessName = %15s PORT = %4d\n",dwProcessesID,procName,openport);
}
}
}
}
CloseHandle(hEven);
CloseHandle(DupHandle);
}
以上是我對fport.exe的分析及其實現(xiàn)代碼,演示程序可以從whitecell.org下載,如果你發(fā)現(xiàn)有問題,請通知我,^_^
參考:
fport.exe
Gary Nebbett<<Windows NT/2000 Native API Reference>>
WebCrazy<<小議Windows NT/2000分頁機制>>
NTDDK
關(guān)于我們:
WSS(Whitecell Security Systems),一個非營利性民間技術(shù)組織,致力于各種系統(tǒng)安全技術(shù)的研究。堅持傳統(tǒng)的hacker精神,追求技術(shù)的精純。
WSS 主頁:
http://www.whitecell.org/ WSS 論壇:
http://www.whitecell.org/forum/
posted on 2008-04-19 14:09
ViskerWong 閱讀(386)
評論(0) 編輯 收藏 引用