基于fport软件谈进程与端口的映射‖fport是如何工作的.fport.
放的端口都是由那些进程打
的fport有出入,请检查fpor
文章已经有很多了,我把我对fpo
exe是由foundstone team出品的
开的.而下面所描述的方法是基
t版本.
| rt的分析也写出来,让大家知道
免费软件,可以列出系统中所有开
于fport v1.33的,如果和你机器上
|
首先,它检测当前用户
具有管理权限,请参考相关
接着,用ZwOpenSection函数
内存的访问.ZwOpenSection
| 是否拥有管理员权限(通过读取
历程),如果没有,打印一句提示
打开内核对象DevicePhysical
函数的原型如下:
| 当前进程的令牌可知当前用户是否
后退出,然后设置当前进程的令牌,
Memory,这个对象用于对系统物理
|
| Out PHANDLE sectionHandle; |
| IN ACCESS_MASK DesiredAccess; |
IN POBJECT_ATTRI
| BUTES ObjectAttributes
|
| 第二个参数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 (
SECTION_MAP_WR
SECTION_MAP_READ |
SECTION_EX
| STANDARD_RIGHTS_REQUIRED|SECTION_QUERY|
ITE |
SECTION_MAP_EXECUTE |
TEND_SIZE)
|
| 第三个参数是一个结构,包含要打开的对象类型等信息,结构定义如下: |
| typedef struct _OBJECT_ATTRIBUTES { |
| PUNICODE_STRING ObjectName; |
PVOID SecurityDescriptor
| ; ?/ Points to type SECURITY_DESCRIPTOR
|
PVOID Securi
ITY_QUALITY_OF_SERVICE
| tyQualityOfService; // Poin
| ts to type SECUR
|
typedef OBJECT_ATTRIBUTES *P
| OBJECT_ATTRIBUTES;
|
| #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; } |
| 那么,打开内核对象DevicePhysicalMemory的语句如下: |
WCHAR PhysmemName
| [] = L"\Device\Phys
| icalMemory";
|
| void * pMapPhysicalMemory; |
| bool OpenPhysicalMemory() |
| UNICODE_STRING physmemString; |
| OBJECT_ATTRIBUTES attributes; |
RtlInitUnicodeSt
,函数原型见ntddk.h
| ring( &physmemString, Physme
| mName ); //初始化Unicode字符串
|
InitializeObject
| Attributes( &attributes, &ph
| ysmemString,
|
OBJ_CASE_IN
OBJECT_ATTRIBUTES结构
| SENSITIVE, NULL, NULL ); //初始化
|
status = ZwOpenSection(pHand
核对象DevicePhysicalMemory,获得句
| le, SECTION_MAP_READ, &attributes ); //打开内
柄
|
| if( !NT_SUCCESS( status )) |
pMapPhysicalMemo
| ry=MapViewOfFile(pHandle,FIL
| E_MAP_READ,
|
| //从内存地址0x30000开始映射0x1000个字节 |
为什么要从0x30000开
模式和用户模式,也就是我
进程都运行在Ring3下,一般
地址为0x30000,或者说,系
1024项组成,每项均指向一
4K,1024*4=4096(0x1000),
见WebCrazy的文章《小议Wi
| 始映射呢,是这样,我们知道,在W
们所说的Ring0和Ring3,在Windo
情况下,系统进程(也就是System
统中最小的页目录所在的物理地
页表(PTE),每一页表也由1024个
所以,上面从物理地址0x30000开
ndows NT/2000的分页机制》)
| indows NT/2000下,系统分为内核
ws NT/2000下,我们所能够看到的
进程)的页目录(PDE)所在物理地址
址为0x30000.而页目录(PDE)由
页组成,而每页的大小为
始映射了0x1000个字节.(具体描述
|
程序打开打开内核对象DevicePhys
DeviceTcp和DeviceUdp,ZwOpenFile函
| icalMemory后,继续用函数ZwOpenFile打开内核对象
数的原型如下:
|
| IN ACCESS_MASK DesiredAccess, |
IN POBJECT_ATTRI
| BUTES ObjectAttributes,
|
| OUT PIO_STATUS_BLOCK IoStatusBlock, |
| 第二个参数DesiredAccess为一个常数,可以是下列值: |
#define FILE_REA
| D_DATA ( 0x0001 )
| // file & pipe
|
#define FILE_LIST_DIRECTORY
| ( 0x0001 ) // directory
|
#define FILE_WRI
| TE_DATA ( 0x0002 )
| // file & pipe
|
#define FILE_ADD_FILE
| ( 0x0002 ) // directory
|
#define FILE_APP
| END_DATA ( 0x0004 )
| // file
|
#define FILE_ADD
| _SUBDIRECTORY ( 0x0004 )
| // directory
|
#define FILE_CREATE_PIPE_INS
| TANCE ( 0x0004 ) // named pipe
|
#define FILE_REA
| D_EA ( 0x0008 )
| // file & directory
|
#define FILE_WRITE_EA
| ( 0x0010 ) // file & directory
|
#define FILE_EXECUTE
| ( 0x0020 ) // file
|
#define FILE_TRA
| VERSE ( 0x0020 )
| // directory
|
#define FILE_DEL
| ETE_CHILD ( 0x0040 )
| // directory
|
#define FILE_READ_ATTRIBUTES
| ( 0x0080 ) // all
|
#define FILE_WRITE_ATTRIBUTE
| S ( 0x0100 ) // all
|
#define FILE_ALL
| _ACCESS (STANDARD_RIGHTS_REQ
| UIRED | SYNCHRONIZE | 0x1FF)
|
#define FILE_GEN
FILE_READ_ATT
FILE_READ_EA
| ERIC_READ (STANDARD_
FILE_READ_DATA
RIBUTES |
|
| RIGHTS_READ |
|
SYNCHRONIZE)
|
#define FILE_GEN
FILE_WRITE_AT
FILE_WRITE_EA
ILE_APPEND_DATA
| ERIC_WRITE (STANDARD_
FILE_WRITE_DATA
TRIBUTES |
|
|
| RIGHTS_WRITE |
|
F
SYNCHRONIZE)
|
#define FILE_GEN
FILE_EXECUTE
SYNCHRONIZE)
| ERIC_EXECUTE (STANDARD_
FILE_READ_ATTRIBUTES
|
| RIGHTS_EXECUTE |
|
|
| 第三个参数是一个结构,包含要打开的对象类型等信息,结构定义见上面所述 |
| 第四个参数返回打开对象的属性,是一个结构,定义如下: |
| typedef struct _IO_STATUS_BLOCK { |
| } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; |
| typedef struct _IO_STATUS_BLOCK32 { |
} IO_STATUS_BLOC
| K32, *PIO_STATUS_BLOCK32;
|
| 第五个参数ShareAccess是一个常数,可以是下列值: |
#define FILE_SHARE_READ
| 0x00000001 // winnt
|
#define FILE_SHA
| RE_WRITE 0x00
| 000002 // winnt
|
#define FILE_SHA
| RE_DELETE 0x00
| 000004 // winnt
|
| 第六个参数OpenOptions也是一个常数,可以是下列的值: |
#define FILE_DIRECTORY_FILE
| 0x00000001
|
#define FILE_WRI
| TE_THROUGH
| 0x00000002
|
#define FILE_SEQUENTIAL_ONLY
| 0x00000004
|
#define FILE_NO_
| INTERMEDIATE_BUFFERING
| 0x00000008
|
#define FILE_SYN
| CHRONOUS_IO_ALERT
| 0x00000010
|
#define FILE_SYNCHRONOUS_IO_
| NONALERT 0x00000020
|
#define FILE_NON
| _DIRECTORY_FILE
| 0x00000040
|
#define FILE_CREATE_TREE_CON
| NECTION 0x00000080
|
#define FILE_COMPLETE_IF_OPL
| OCKED 0x00000100
|
#define FILE_NO_
| EA_KNOWLEDGE
| 0x00000200
|
#define FILE_OPEN_FOR_RECOVE
| RY 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_RES
| ERVE_OPFILTER
| 0x00100000
|
#define FILE_OPEN_REPARSE_PO
| INT 0x00200000
|
#define FILE_OPEN_NO_RECALL
| 0x00400000
|
#define FILE_OPEN_FOR_FREE_S
| PACE_QUERY 0x00800000
|
#define FILE_COPY_STRUCTURED
| _STORAGE 0x00000041
|
#define FILE_STR
| UCTURED_STORAGE
| 0x00000441
|
#define FILE_VALID_OPTION_FL
| AGS 0x00ffffff
|
#define FILE_VALID_PIPE_OPTI
| ON_FLAGS 0x00000032
|
#define FILE_VAL
| ID_MAILSLOT_OPTION_FLAGS
| 0x00000032
|
#define FILE_VALID_SET_FLAGS
| 0x00000036
|
那么,打开内核对象De
| viceTcp和DeviceUdp的语句
| 如下:
|
WCHAR physmemNameTcp[]=L"\Devic
| e\TCP";
|
WCHAR physmemNameUdp[]=L"\Devic
| e\UDP";
|
HANDLE OpenDeviceTcp
| Udp(WCHAR * deviceName)
|
| UNICODE_STRING physmemString; |
| OBJECT_ATTRIBUTES attributes; |
RtlInitUnicodeString(&physme
| mString, deviceName);
|
InitializeObjectAttributes(
| &attributes,&physmemString,
|
| OBJ_CASE_INSENSITIVE
| ,0, NULL );
|
status = ZwOpenFile ( &pDevi
| ceHandle,0x100000, &attributes, &iosb, 3,0);
|
| if( !NT_SUCCESS( status )) |
接着,程序用ZwQue
及其相关畔?函数的原型
| rySystemInformation函数获得
如下:
| 系统当前所以进程的所建立的句柄
|
| ZwQuerySystemInformation( |
IN SYSTEM_INFORMATION_CLASS
| SystemInformationClass,
|
| IN OUT PVOID SystemInformation, |
| IN ULONG SystemInformationLength, |
| OUT PULONG ReturnLength OPTIONAL |
| (这个函数结构Microsoft没有公开,参见Gary Nebbett< >) |
第一个参数是一个枚举常数,设置要
持54个系统信息的查询,我们要用到的是
.
| 查询的系统信息类型,ZwQuerySystemInformation支
它的第16号功能,进行SystemHandleInformation查询
|
| SYSTEM_HANDLE_INFORMATION结构定义如下: |
typedef struct _
| SYSTEM_HANDLE_INFORMATION{
|
| ULONG ProcessID; //进程的标识ID |
| UCHAR ObjectTypeNumber; //对象类型 |
UCHAR Flags;
| //0x01 = PROTEC
| T_FROM_CLOSE,0x02 = INHERIT
|
| PVOID Object; //对象句柄所指的内核对象地址 |
ACCESS_MASK
| GrantedAccess; //创建句
| 柄时所准许的对象的访问权
|
}SYSTEM_HANDLE_INFORMATION,
| * PSYSTEM_HANDLE_INFORMATION;
|
| (这个函数结构Microsoft没有公开,参见Gary Nebbett< >) |
| #define SystemHandleInformation 16 |
ULONG cbBuffer = 0x1000;
| //先设定一个较小的缓冲空间
|
PULONG pBuffer =
| new ULONG[cbBuffer]; //分配
| 内存
|
| Status = ZwQuerySystemInformation( |
| pBuffer, cbBuffer * sizeof *
| pBuffer, NULL);
|
if (Status =
| = STATUS_INFO_LENGTH_MISMATC
| H)
|
| //如果返回的错误信息为缓冲区长度不够,那么重新分配内存 |
pBuffer
| = new ULONG[cbBuffer *= 2];
|
| else if (!NT_SUCCESS(Status)) |
while (Status == STATUS_
| INFO_LENGTH_MISMATCH);
|
因为如果一个进程打开
核对象,所以,我们在当前进
这样,我们可以在上面获得
打开的内核对象的句柄数值
| 了端口,那么它肯定会建立类型
程中打开上述的两个内核对象,
的句柄列表中的当前进程中查找
相同的句柄,并得到其句柄所指
| 为DeviceTcp和DeviceUdp的内
在打开的同时保存了打开的句柄,
对象句柄的数值和我们保存的两个
向的内核对象的地址.代码如下:
|
DWORD GetTcpUdpObjec
| t(PULONG pBuffer,HANDLE pHan
| dle,DWORD ProcessId)
|
| DWORD objTYPE1,objTYPE2,HandleObject; |
PSYSTEM_HANDLE_INFORMATION p
DLE_INFORMATION)(pBuffer+1);
| Processes = (PSYSTEM_HAN
|
| for (i=0;i< * pBuffer;i++) |
if ((pProces
| ses[i].ProcessID) == Process
| Id)
|
| objTYPE1 = (DWORD)hDeviceTcpUdp; |
objTYPE2
| = (DWORD)pProcesses[i].Hand
| le;
|
Hand
| leObject = (DWORD)pProcesses
| .Object;
|
| 这个内核对象地址是一个线性地址,我们需要把这个地址转换为物理地址,并得到一些相关的数据.在fport中,换算是这样进行的:(具体描述见WebCrazy的文章< < 小议Windows NT/2000的分页机制>>) |
| void GetPTE(DWORD objAddress) |
DWORD newAddress1,newAddress
| 2,newAddress3,newAddress4;
|
physmemBuff = (DWORD)pMapPhy
| sicalMemory;
|
| newAddress1 = physmemBuff+(objAddress>>0x16)*4; |
| newAddress = (DWORD *)newAddress1; |
| newAddress1 = * newAddress; |
| newAddress2 = objAddress & 0x3FF000; |
newAddress3 = newAddress1 &
| 0x0FFFFF000;
|
newAddress4 = newAddress2 +
| newAddress3;
|
NewmapPhy = MapViewOfFile(gh
READ,0,newAddress4,0x1000);
| PhysicalMemory,FILE_MAP_
|
| //重新映射物理内存,得到当前线性地址所指向的PTE的物理地址内容 |
然后在根据内核对象的线性地址得到
容的页,其结构如下:
| 这个地址所指向的物理页,得到体现当前内核对象内
|
(注:我不能保证这个结
是可以工作的)
| 构的正确性,但我们只会用到其
| 中的两个值,对程序来说,这个结构
|
| void GetMustPar(DWORD objAddress) |
| CurrAddress = objAddress & 0xFFF; |
PPTE pte = (PPTE)(VOID *)((D
| WORD)NewmapPhy+CurrAddress);
|
| CurrWriteTable = pte->WriteTable; |
| CurrNoCache = Pte->NoCache; |
我们现在想要得到的都已经得到了,
句柄(呵呵,不是每一个句柄,在Windows N
0x16,在Windows 2000下这个值为0x1A)的
WriteTable值,如果与内核对象DeviceT
一个端口,再对这个句柄进行确认,就可以
| 下面需要做的是遍历进程,用每一个进程中的每一个
T下,DeviceTcp和DeviceUdp的句柄类型值为
核心地址用上面所描述的办法得到其PTE内容,得到其
cp和DeviceUdp相等,那么这个句柄就有可能打开了
了.确认的代码如下:
|
| typedef struct _TDI_CONNECTION_INFO { |
| ULONG TransmissionErrors; |
| LARGE_INTEGER Throughput; |
} TDI_CONNECTION_INFO, *PTDI_CON
| NECTION_INFO;
|
typedef struct _TDI_CONNECTION_I
| NFORMATION {
|
| LONG RemoteAddressLength; |
} TDI_CONNECTION_INFORMATION, *P
| TDI_CONNECTION_INFORMATION;
|
void GetOpenPort(DWORD dwProcess
| esID,USHORT Handle,int NoCache)
|
//Handle为进程打开的句柄,并且经
| 过比较为DeviceTcp或DeviceUdp类型
|
| HANDLE hProc,DupHandle=NULL; |
| overlap.InternalHigh = 0; |
| hEven=CreateEvent(0,1,0,0); |
hProc = OpenProc
| ess(PROCESS_DUP_HANDLE,
|
TDI_CONN
| ECTION_INFO TdiConnInfo={
| 0};
|
TDI_CONNECTION_INFOR
| MATION TdiConnInformation={0};
|
TdiC
| onnInformation.RemoteAddress
| Length= 4;
|
if(D
| eviceIoControl(DupHandle,0x2
| 10012,
|
| &TdiConnInformation,
| sizeof(TdiConnInformation),
|
&Tdi
| ConnInfo,sizeof(TdiConnInfo),
|
openport = n
| tohs((u_short)TdiConnInfo.ReceivedTsdus);
|
进程名称
| procname = GetProcName(dwPro
| cessesID); //得到进程标识ID的
|
printf("PID
%4dn",dwProcessesID,procName,openpo
| = %4d ProcessName = %15s PORT =
rt);
|
TdiC
| onnInformation.RemoteAddress
| Length= 3;
|
if(D
| eviceIoControl(DupHandle,0x2
| 10012,
|
| &TdiConnInformation,
| sizeof(TdiConnInformation),
|
&Tdi
| ConnInfo,sizeof(TdiConnInfo),
|
openport = n
| tohs((u_short)TdiConnInfo.ReceivedTsdus);
|
procname = G
进程名称
| etProcName(dwProcessesID); //得到进程标识ID的
|
printf("PID
%4dn",dwProcessesID,procName,openpo
| = %4d ProcessName = %15s PORT =
rt);
|
上一页> 下一页>