必威注册电子书籍教程中心网文荟萃客户留言繁體中文
设为首页
加入收藏
联系必威注册
您当前的位置:92动力网 -> 教程中心 -> 网络冲浪 -> 网络技术 -> 教程内容 退出登录 用户管理
栏目导航
· 初学上网· 网页浏览
· 病毒快报· 防范措施
· 局域网专栏· 网络安全
· 系统安全· 黑客天空
· 网关代理· 网络技术
· 服务器· 网络其他
热门教程
· 如何在Visual Basic...
· 使用Visual Basic操...
· 使用VB6.0设计Activ...
· VB动态调用外部函数...
· 建立一个程序员自己...
· 如何编写高质量的VB...
· [图文] 为更新到Visual Bas...
· 再谈在VB中调用VC++...
· 用VB语言编程实现JP...
· 用DTS实现SQL数据库...
相关教程

再谈进程与端口的映射
作者:Admin聽聽来源:ASP教程聽聽发布时间:2004-6-29 18:05:02聽聽发布人:siforgern

减小字体 增大字体


关于进程与端口映射的文章已经有很多了,我把我对fport的分析也写出来,让大家知道fport是如何工作的.
fport.exe是由foundstone team出品的免费软件,可以列出系统中所有开放的端口都是由那些进程打开的.而下
面所描述的方法是基于fport v1.33的,如果和你机器上的fport有出入,请检查fport版本.

聽 首先,它检测当前用户是否拥有管理员权限(通过读取当前进程的令牌可知当前用户是否具有管理权限,请参考
相关历程),如果没有,打印一句提示后退出,然后设置当前进程的令牌,接着,用ZwOpenSection函数打开内核对象
DevicePhysicalMemory,这个对象用于对系统物理内存的访问.ZwOpenSection函数的原型如下:

NTSYSAPI
NTSTSTUS
NTAPI
ZwOpenSection(
聽 Out PHANDLE sectionHandle;
聽 IN ACCESS_MASK DesiredAccess;
聽 IN POBJECT_ATTRIBUTES ObjectAttributes
聽 };
(见ntddk.h)

第一个参数得到函数执行成功后的句柄
第二个参数DesiredAccess为一个常数,可以是下列值:
聽 #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)
第三个参数是一个结构,包含要打开的对象类型等信息,结构定义如下:
聽 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)
对于这个结构的初始化用一个宏完成:
聽 #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)
那么,打开内核对象DevicePhysicalMemory的语句如下:
WCHARPhysmemName[] =L"\Device\PhysicalMemory";
void *pMapPhysicalMemory;
HANDLEpHandle;

boolOpenPhysicalMemory()
{
聽 NTSTATUSstatus;
聽 UNICODE_STRINGphysmemString;
聽 OBJECT_ATTRIBUTES attributes;
聽 RtlInitUnicodeString( &physmemString, PhysmemName ); //初始化Unicode字符串,函数原型见ntddk.h
聽 InitializeObjectAttributes( &attributes, &physmemString,
聽OBJ_CASE_INSENSITIVE, NULL, NULL ); //初始化OBJECT_ATTRIBUTES结构
聽 status = ZwOpenSection(pHandle, SECTION_MAP_READ, &attributes ); //打开内核对象DevicePhysicalMemory,获得句柄
聽 if( !NT_SUCCESS( status ))
聽 return false;
聽 pMapPhysicalMemory=MapViewOfFile(pHandle,FILE_MAP_READ,
聽0,0x30000,0x1000);
聽 //从内存地址0x30000开始映射0x1000个字节
聽 if( GetLastError()!=0)
聽 return false;
聽 return true;
}

聽 为什么要从0x30000开始映射呢,是这样,我们知道,在Windows NT/2000下,系统分为内核模式和用户模式,也就是我们
所说的Ring0和Ring3,在Windows NT/2000下,我们所能够看到的进程都运行在Ring3下,一般情况下,系统进程(也就是System
进程)的页目录(PDE)所在物理地址地址为0x30000,或者说,系统中最小的页目录所在的物理地址为0x30000.而页目录(PDE)由
1024项组成,每项均指向一页表(PTE),每一页表也由1024个页组成,而每页的大小为4K,1024*4=4096(0x1000),所以,上面从物
理地址0x30000开始映射了0x1000个字节.(具体描述见WebCrazy的文章<<小议Windows NT/2000的分页机制>>)

聽 程序打开打开内核对象DevicePhysicalMemory后,继续用函数ZwOpenFile打开内核对象DeviceTcp和DeviceUdp,ZwOpenFile
函数的原型如下:
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)

第一个参数返回打开对象的句柄
第二个参数DesiredAccess为一个常数,可以是下列值:
聽 #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)
第三个参数是一个结构,包含要打开的对象类型等信息,结构定义见上面所述
第四个参数返回打开对象的属性,是一个结构,定义如下:
聽 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)
第五个参数ShareAccess是一个常数,可以是下列值:
聽 #define FILE_SHARE_READ 0x00000001// winnt
聽 #define FILE_SHARE_WRITE0x00000002// winnt
聽 #define FILE_SHARE_DELETE 0x00000004// winnt
聽 (见ntddk.h)
第六个参数OpenOptions也是一个常数,可以是下列的值:
聽 #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)
聽
那么,打开内核对象DeviceTcp和DeviceUdp的语句如下:
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函数获得系统当前所以进程的所建立的句柄及其相关信息,函数的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
聽 IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
聽 IN OUT PVOID SystemInformation,
聽 IN ULONG SystemInformationLength,
聽 OUT PULONG ReturnLength OPTIONAL
聽 };
(这个函数结构Microsoft没有公开,参见Gary Nebbett<>)

第一个参数是一个枚举常数,设置要查询的系统信息类型,ZwQuerySystemInformation支持54个系统信息的查询,我们要用到的
是它的第16号功能,进行SystemHandleInformation查询.
SYSTEM_HANDLE_INFORMATION结构定义如下:
聽 typedef struct _SYSTEM_HANDLE_INFORMATION{
聽 ULONG ProcessID;//进程的标识ID
聽 UCHAR ObjectTypeNumber;//对象类型
聽 UCHAR Flags; //0x01 = PROTECT_FROM_CLOSE,0x02 = INHERIT
聽 USHORT Handle; //对象句柄的数值
聽 PVOIDObject;//对象句柄所指的内核对象地址
聽 ACCESS_MASK GrantedAccess;//创建句柄时所准许的对象的访问权
聽 }SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
聽 (这个函数结构Microsoft没有公开,参见Gary Nebbett<>)
第二个参数输出查询的结果
第三个参数设置缓冲区的长度
第四个参数返回函数正确执行需要的缓冲区的大小
代码如下:

PULONG GetHandleList()
{
聽 ULONG cbBuffer = 0x1000;//先设定一个较小的缓冲空间
聽 PULONG pBuffer = new ULONG[cbBuffer]; //分配内存
聽 NTSTATUS Status;

聽 do
聽 {
聽 Status = ZwQuerySystemInformation(
聽 SystemHandleInformation,
聽 pBuffer, cbBuffer * sizeof * pBuffer, NULL);

聽 if (Status == STATUS_INFO_LENGTH_MISMATCH)
聽 {
聽 //如果返回的错误信息为缓冲区长度不够,那么重新分配内存
聽 delete [] pBuffer;
聽 pBuffer = new ULONG[cbBuffer *= 2];
聽 }
聽 else if (!NT_SUCCESS(Status))
聽 {
聽 //如果是其他错误信息,返回
聽 delete [] pBuffer;
聽 return false;
聽 }
聽 }
聽 while (Status == STATUS_INFO_LENGTH_MISMATCH);
聽 return pBuffer;
}

因为如果一个进程打开了端口,那么它肯定会建立类型为DeviceTcp和DeviceUdp的内核对象,所以,我们在当前进程中打开
上述的两个内核对象,在打开的同时保存了打开的句柄,这样,我们可以在上面获得的句柄列表中的当前进程中查找对象句柄的
数值和我们保存的两个打开的内核对象的句柄数值相同的句柄,并得到其句柄所指向的内核对象的地址.代码如下:
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;
}

这个内核对象地址是一个线性地址,我们需要把这个地址转换为物理地址,并得到一些相关的数据.在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);
聽 //重新映射物理内存,得到当前线性地址所指向的PTE的物理地址内容
}

然后在根据内核对象的线性地址得到这个地址所指向的物理页,得到体现当前内核对象内容的页,其结构如下:
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;
(注:我不能保证这个结构的正确性,但我们只会用到其中的两个值,对程序来说,这个结构是可以工作的,^_^)
代码如下:
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;
}

好了,我们现在想要得到的都已经得到了,下面需要做的是遍历进程,用每一个进程中的每一个句柄(呵呵,不是每一个句柄,
在Windows NT下,DeviceTcp和DeviceUdp的句柄类型值为0x16,在Windows 2000下这个值为0x1A)的核心地址用上面所描
述的办法得到其PTE内容,得到其WriteTable值,如果与内核对象DeviceTcp和DeviceUdp相等,那么这个句柄就有可能打开
了一个端口,再对这个句柄进行确认,就可以了.确认的代码如下:
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;
(以上结构见tdi.h)

void GetOpenPort(DWORD dwProcessesID,USHORT Handle,int NoCache)
//dwProcessesID为进程标识ID
//Handle为进程打开的句柄,并且经过比较为DeviceTcp或DeviceUdp类型
//NoCache为PTE结构中的一个值
{
聽 HANDLE hProc,DupHandle=NULL;
聽 HANDLE hEven=NULL;
聽 OVERLAPPED overlap;
聽 u_short openport;
聽 int i=0;
聽 char procName[256]=;
聽 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=;
聽 TDI_CONNECTION_INFORMATION TdiConnInformation=;
聽 DWORD dwRetu=0;

聽 if(NoCache==0x2)
聽 {
聽 TdiConnInformation.RemoteAddressLength= 4;
聽 if(DeviceIoControl(DupHandle,0x210012,
聽 &TdiConnInformation,sizeof(TdiConnInformation),
聽 &TdiConnInfo,sizeof(TdiConnInfo),
聽 0,&overlap))
聽 //进行TDI查询,得到连接的相关信息
聽 {
聽 openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
聽 procname = GetProcName(dwProcessesID);//得到进程标识ID的进程名称
聽 printf("PID = %4d ProcessName = %15s PORT = %4dn",dwProcessesID,procName,openport);
聽 }
聽 }
聽 if(NoCache==0x1)
聽 {
聽 TdiConnInformation.RemoteAddressLength= 3;
聽 if(DeviceIoControl(DupHandle,0x210012,
聽 &TdiConnInformation,sizeof(TdiConnInformation),
聽 &TdiConnInfo,sizeof(TdiConnInfo),
聽 0,&overlap))
聽 //进行TDI查询,得到连接的相关信息
聽 {
聽 openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
聽 procname = GetProcName(dwProcessesID);//得到进程标识ID的进程名称
聽 printf("PID = %4d ProcessName = %15s PORT = %4dn",dwProcessesID,procName,openport);
聽 }
聽 }
聽 }
聽 }
聽 CloseHandle(hEven);
聽 CloseHandle(DupHandle);
}

以上是我对fport.exe的分析及其实现代码,演示程序可以从whitecell.org下载,如果你发现有问题,请通知我,^_^

参考:

fport.exe
Gary Nebbett<>
WebCrazy<<小议Windows NT/2000分页机制>>
NTDDK

关于我们:

WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。

WSS 主页:http://www.whitecell.org/
WSS 论坛:http://www.whitecell.org/forum/

(出处:viphot)
[] [返回上一页] [打 印] [收 藏]
上一篇教程:宽带网技术介绍
∷相关教程评论∷    (评论内容只代表网友观点,与本站立场无关!) [更多评论...]
关于本站 - 网站帮助 - 广告合作 - 下载声明 - 网站地图 - 管理登录
Copyright 2019 必威注册. All Rights Reserved .
浙ICP备05047688号