内核态与用户态是操作系统的两种运行级别,intel cpu提供Ring0-Ring3三种级别的运行模式。Ring0级别最高,Ring3最低。其中特权级0(Ring0)是留给操作系统代码,设备驱动程序代码使用的,它们工作于系统核心态;而特权极3(Ring3)则给普通的用户程序使用,它们工作在用户态。运行于处理器核心态的代码不受任何的限制,可以自由地访问任何有效地址,进行直接端口访问。而运行于用户态的代码则要受到处理器的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问(此时处理器状态和控制标志寄存器EFLAGS中的IOPL通常为0,指明当前可以进行直接I/O的最低特权级别是Ring0)。以上的讨论只限于保护模式操作系统,象DOS这种模式操作系统则没有这些概念,其中的所有代码都可被看作运行在核心态。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级) 内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。
在内核态下CPU可执行任何指令,在用户态下CPU只能执行非特权指令。当CPU处于内核态,可以随意进入用户态;而当CPU处于用户态时,用户从用户态切换到内核态只有在系统调用和中断两种情况下发生,一般程序一开始都是运行于用户态,当程序需要使用系统资源时,就必须通过调用软中断进入内核态。
Linux使用了Ring3级别运行用户态,Ring0作为内核态,没有使用Ring1和Ring2。Ring3状态不能访问Ring0的地址空间,包括代码和数据。Linux进程的4GB地址空间,3G-4G部分大家是共享的,是内核态的地址空间,这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据。用户运行一个程序,该程序所创建的进程开始是运行在用户态的,如果要执行文件操作,网络数据发送等操作,必须通过 write,send等系统调用,这些系统调用会调用内核中的代码来完成操作,这时,必须切换到Ring0,然后进入3GB-4GB中的内核地址空间去执行这些代码完成操作,完成后,切换回Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。
处理器模式从Ring3向Ring0的切换发生在控制权转移时,有以下两种情况:访问调用门的长转移指令CALL,访问中断门或陷阱门的INT指令。具体的转移细节由于涉及复杂的保护检查和堆栈切换,不再赘述,请参阅相关资料。现代的操作系统通常使用中断门来提供系统服务,通过执行一条陷入指令来完成模式切换,在INTEL X86上这条指令是INT,如在WIN9X下是INT30(保护模式回调),在LINUX下是INT80,在WINNT/2000下是INT2E。用户模式的服务程序(如系统DLL)通过执行一个INTXX来请求系统服务,然后处理器模式将切换到核心态,工作于核心态的相应的系统代码将服务于此次请求并将结果传给用户程序。
一,中断处理过程
硬件中断:来自时钟,外设
可编程中断:programmed interrupt,执行引起软件中断的指令。
例外中断:如页面错。
都由系统负责处理。当发生一个中断时,如果CPU正在比该中断级低的处理机运行级上运行,它就在解码下条指令之前,接受该中断,并提高处理机运行级。内核处理中断的操作顺序如下:
1,对于正在进行的进程,保存其当前寄存器上下文,并创建压入一个新的上下文层。
2,确定中断源,识别中断类型。如是时钟或磁盘的。
3,查找中断向量。当系统接受一个中断时,它从机器中得到一个数,系统把这个作为查表的偏移量。这个表通常成为中断向量(interrupt vector)。中断向量的内容包括各种中断源的中断处理程序的地址,以及中断处理程序取得参数的方式。
4,内核调用中断处理程序。
5,中断处理程序执行那个返回,恢复(弹出)前一上下文层。
二,软中断
软中断通知进程发生了异步事件。
系统有个进程表,每个进程在进程表中有有个进程表项,每个进程表项有个软中断信号字段,纪录发向一个进程的所有未处理的软中断信号。
当一个进程即将从核心态返回到用户态时,或它要进入或离开一个适当的低调度优先级时,内核要检查它是否收到了一个软中断信号。
内核仅当一个进程从核心态返回到用户态时才处理软中断信号。
三,系统调用
我们在C程序中调用系统调用好像是个一般的函数调用,当实际上调用系统调用会引起用户态到核心态的状态变化,这是怎么做到的呢?
原来,C编译程序采用一个预定义的函数库(C之程序库),其中的函数具有系统调用的名字,从而解决了在用户程序中请求系统调用的问题。这些库函数一般都执行一条指令,该指令将进程的运行方式变为核心态,然后,使内核开始为系统调用执行代码。我们称这个指令为操作系统陷入(operating system trap)。
系统调用的接口是一个中断处理程序的特例。
在处理操作系统陷入时,
1,内核根据系统调用号查系统调用入口表,找到相应的内核子程序的地址。
2,内核还要确定该系统调用所要求的参数个数。
3,从用户地址空间拷贝参数到U区(Unix V)。
4,保存当前上下文,执行系统调用代码。
核心态:当CPU正在运行内核代码时(内核代码是共享的)。
用户态:当CPU正在运行用户代码时。
用户模式:不可以访问内核空间(>=0x80000000)
内核模式:可以访问任何有效虚拟地址,包括内核空间。一个线程可以访问其他任何线程地址空间。
// 作者 beiyu http://beiyu.bokee.com
// 内核态实现进程和端口关联,在WINDOWS2000.xp,2003下可以用。
// 感谢Leven公布了他的代码,增加了区分tcp,udp,增加了操作系统的兼容性
// 可以在Windows 2000, xp, 2003下面正常使用,编译环境Win2000DDK
// 使用妳的sys loader加载,使用Dbgview查看
// 如果你有什么改进,请email我: beiyuly@gmail.com
//
//
#include <ntddk.h>
#include <string.h>
#define SystemHandleInformation 16
#define TCPUDP_FLAG 100
#define WIN2K_SOCKET_FLAG 0x1a //2k
#define WINXP_SOCKET_FLAG 0x1c //xp
#define WIN2K3_SOCKET_FLAG 0x1a //2k3
#define WIN2K_EPROCESS_NAMEOFFSET 0x1fc //2k
#define WINXP_EPROCESS_NAMEOFFSET 0x174 //xp
#define WIN2K3_EPROCESS_NAMEOFFSET 0x1fc //2k3
#define ObjectNameInformation 1
#define ObjectAllTypesInformation 3
/*
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING Name;
ULONG ObjectCount;
ULONG HandleCount;
ULONG Reserved1[4];
ULONG PeakObjectCount;
ULONG PeakHandleCount;
ULONG Reserved2[4];
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
UCHAR Unknown;
BOOLEAN MaintainHandleDatabase;
POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef struct _OBJECT_ALL_TYPES_INFORMATION {
ULONG NumberOfTypes;
OBJECT_TYPE_INFORMATION TypeInformation;
} OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION;
*/
#define ntohs(s) /
( ( ((s) >> 8) & 0x00FF ) | /
( ((s) << 8) & 0xFF00 ) )
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;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessID; //进程的标识ID
UCHAR ObjectTypeNumber; //对象类型
UCHAR Flags; //0x01 = PROTECT_FROM_CLOSE,0x02 = INHERIT
USHORT Handle; //对象句柄的数值
PVOID Object; //对象句柄所指的内核对象地址 WinNT4/Windows2000是0x1A xp中是0x1c 2003中是
ACCESS_MASK GrantedAccess; //创建句柄时所准许的对象的访问权
}SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
NTSYSAPI
NTSTATUS
NTAPI
NtDeviceIoControlFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength
);
NTSYSAPI
NTSTATUS
NTAPI
ZwQueryObject(
IN HANDLE ObjectHandle,
IN ULONG ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
NTSYSAPI
BOOLEAN
NTAPI
NtDuplicateObject(
IN HANDLE hSourceProcessHandle,
IN HANDLE hSourceHandle,
IN HANDLE hTargetProcessHandle,
OUT HANDLE * lpTargetHandle,
IN ULONG dwDesiredAccess,
IN BOOLEAN bInheritHandle,
IN ULONG dwOptions
);
NTSYSAPI
NTSTATUS
NTAPI
PsLookupProcessByProcessId(
IN ULONG ulProcId,
OUT PEPROCESS * pEProcess
);
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath);
void DriverUnload(IN PDRIVER_OBJECT DriverObject);
//几个全局变量,记录端口相关信息,最后列印出来
ULONG g_pid[1000];
ULONG g_port[1000];
ULONG g_handle[1000];
ULONG g_tcpudp[1000];
ULONG g_num =0 ;
ULONG g_tu[1000]; //g_tu=0 tcp, g_tu=1 udp
//获得所有句柄
ULONG GetHandleList()
{
ULONG n;
ULONG pBuffer;
NTSTATUS status;
DbgPrint("GetHandleList/n");
pBuffer =(ULONG)ExAllocatePool(PagedPool,0x1000);
status = ZwQuerySystemInformation(SystemHandleInformation,(PVOID)pBuffer,0x1000,&n);
ExFreePool((PVOID)pBuffer);
if(STATUS_INFO_LENGTH_MISMATCH == status)
{
pBuffer =(ULONG)ExAllocatePool(NonPagedPool,n);
ZwQuerySystemInformation(SystemHandleInformation,(PVOID)pBuffer,n,NULL);
return pBuffer;
}
else
return 0;
}
//根据句柄得到端口信息
void GetOpenPort(ULONG dwProcessesID,ULONG Handle,int NoCache,ULONG tcpudp)
{
HANDLE hProc,DupHandle=NULL;
USHORT openport;
ULONG i=0;
NTSTATUS status;
TDI_CONNECTION_INFO TdiConnInfo={0};
TDI_CONNECTION_INFORMATION TdiConnInformation={0};
ULONG dwRetu=0;
IO_STATUS_BLOCK IoStatusBlock={0};
CLIENT_ID id;
OBJECT_ATTRIBUTES objatt = {0};
POBJECT_NAME_INFORMATION ObjectName;
char ObjectNameBuf[512];
// char ObjectNameMBS[261];
ULONG ReturnLen;
ObjectName = (POBJECT_NAME_INFORMATION)ObjectNameBuf;
ObjectName->Name.MaximumLength = 500;
//DbgPrint("GetOpenPort/n");
id.UniqueProcess = (HANDLE)dwProcessesID;
id.UniqueThread = 0;
//打开对方进程
NtOpenProcess(&hProc,PROCESS_DUP_HANDLE,&objatt,&id);
//复制句柄
NtDuplicateObject(hProc,
(HANDLE)Handle,
(HANDLE)0xffffffff,
&DupHandle,
0,
FALSE,
2);
//根据object的数据得到端口信息
if(NoCache==0x2)
{
//取得句柄关联的对象的信息
ZwQueryObject(DupHandle, ObjectNameInformation, ObjectName, sizeof(ObjectNameBuf), &ReturnLen);
TdiConnInformation.RemoteAddressLength= 4;
status = NtDeviceIoControlFile((HANDLE)DupHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
0x210012, // Command code
&TdiConnInformation,
sizeof(TdiConnInformation),
&TdiConnInfo,
sizeof(TdiConnInfo));
//进行TDI查询,得到连接的相关信息
if(status == 0)
{
openport = ntohs((USHORT)TdiConnInfo.ReceivedTsdus);
if(openport == 0)
return;
for(i=0;i {
if(g_pid == dwProcessesID && g_port == openport)
if(tcpudp >= TCPUDP_FLAG && g_tcpudp >= TCPUDP_FLAG || tcpudp < TCPUDP_FLAG && g_tcpudp < TCPUDP_FLAG)
return;
}
g_pid = dwProcessesID;
g_port = openport;
g_handle = Handle;
g_tcpudp = tcpudp;
g_num++;
if (wcscmp(ObjectName->Name.Buffer, L"//Device//Tcp") == 0)
{
g_tu = 0;
}
if (wcscmp(ObjectName->Name.Buffer, L"//Device//Udp") == 0)
{
g_tu = 1;
}
}
}
if(NoCache==0x1)
{
ZwQueryObject(DupHandle, ObjectNameInformation, ObjectName, sizeof(ObjectNameBuf), &ReturnLen);
TdiConnInformation.RemoteAddressLength= 3;
status = NtDeviceIoControlFile((HANDLE)DupHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
0x210012, // Command code
&TdiConnInformation,
sizeof(TdiConnInformation),
&TdiConnInfo,
sizeof(TdiConnInfo));
//进行TDI查询,得到连接的相关信息
if(status == 0)
{
openport = ntohs((USHORT)TdiConnInfo.ReceivedTsdus);
if(openport == 0)
return;
for(i=0;i {
if(g_pid == dwProcessesID && g_port == openport)
if(tcpudp >= TCPUDP_FLAG && g_tcpudp >= TCPUDP_FLAG || tcpudp < TCPUDP_FLAG && g_tcpudp < TCPUDP_FLAG)
return;
}
g_pid = dwProcessesID;
g_port = openport;
g_handle = Handle;
g_tcpudp = tcpudp;
g_num++;
if (wcscmp(ObjectName->Name.Buffer, L"//Device//Tcp") == 0)
{
g_tu = 0;
}
if (wcscmp(ObjectName->Name.Buffer, L"//Device//Udp") == 0)
{
g_tu = 1;
}
}
}
}
void Start(ULONG pBuffer)
{
ULONG i;
//头4个字节是所有的句柄的数目UNONG 32位
//从第5个字节开始就是结构体了
PSYSTEM_HANDLE_INFORMATION pProcesses = (PSYSTEM_HANDLE_INFORMATION)(pBuffer+4);
ULONG nocache;
ULONG tcpudp;
PEPROCESS epro;
char *p;
ULONG uMajorVersion;
ULONG uMinorVersion;
ULONG uBuildNumber;
ULONG uOsVer;
DbgPrint("Start11/n");
PsGetVersion(&uMajorVersion, &uMinorVersion, &uBuildNumber, NULL);
if(uMajorVersion == 5)
{
if(uMinorVersion == 0)
{
DbgPrint("2k/n");
uOsVer = 0;//2k
}
else if(uMinorVersion == 1)
{
uOsVer = 1;//xp
DbgPrint("xp/n");
}
else if(uMinorVersion == 2)
{
uOsVer = 2;//2k3
DbgPrint("2k3/n");
}
else
{
uOsVer = 3;//nt
DbgPrint("NT/n");
}
}
else
{
uOsVer = 99;
DbgPrint("Unknow OS/n");
}
for (i=0;i<((ULONG)(*(ULONG*)pBuffer));i++)
{
//2000 xp 2003 三种操作系统
if(pProcesses.ObjectTypeNumber == WIN2K_SOCKET_FLAG
|| pProcesses.ObjectTypeNumber == WINXP_SOCKET_FLAG
|| pProcesses.ObjectTypeNumber == WIN2K3_SOCKET_FLAG)
{
//得到SYSTEM_HANDLE_INFORMATION.object的相关数据
//这里要密切注意内存情况,一不小心就蓝屏。因为句柄经常变化,有些可能已经被销毁了
nocache = (ULONG)pProcesses.Object;
if(!MmIsAddressValid((VOID*)nocache))
continue;
nocache = (ULONG)(*((ULONG*)(nocache)+4));
tcpudp = (ULONG)(*((ULONG*)(pProcesses.Object)+1));
if(!MmIsAddressValid((VOID*)tcpudp))
continue;
tcpudp = (ULONG)(*((ULONG*)(tcpudp)+1));
if(nocache == 2 || nocache == 1)
{
GetOpenPort(pProcesses.ProcessID,pProcesses.Handle,nocache,tcpudp);
}
}
}
for(i=0;i {
//根据PID得到进程名
PsLookupProcessByProcessId(g_pid,&epro);
if(uOsVer == 0)
{ //2k中进程名在EPROCESS结构中的位置
p = (char*)epro + WIN2K_EPROCESS_NAMEOFFSET;
//DbgPrint("2k/n");
}
if(uOsVer == 1)
{ //xp中进程名在EPROCESS结构中的位置
p = (char*)epro + WINXP_EPROCESS_NAMEOFFSET;
//DbgPrint("xp/n");
}
if(uOsVer == 2)
{ //2k3中进程名在EPROCESS结构中的位置
p = (char*)epro + WIN2K3_EPROCESS_NAMEOFFSET;
//DbgPrint("2k3/n");
}
if(uOsVer == 3)
{
p = (char*)epro + WIN2K_EPROCESS_NAMEOFFSET; //NT
//DbgPrint("nt/n");
}
if(uOsVer == 99)
{
//DbgPrint("Unknow OS/n");
break;
}
if(g_tu == 0)
DbgPrint("TCP:/tProcName=%s/tPID=%d/tport=%d/t%d/n",p,g_pid,g_port,g_tcpudp);
if(g_tu == 1)
DbgPrint("UDP:/tProcName=%s/tPID=%d/tport=%d/t%d/n",p,g_pid,g_port,g_tcpudp);
}
return;
}
//
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
ULONG pbuf;
DbgPrint("DriverEntry/n");
DriverObject->DriverUnload = DriverUnload;
pbuf = GetHandleList();
Start(pbuf);
return STATUS_SUCCESS;
}
void DriverUnload(IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
//ResumeDestFunction();
if(pDriverObject->DeviceObject != NULL)
{
IoDeleteDevice( pDriverObject->DeviceObject );
}
DbgPrint("DriverUnload/n");
}
参考文献:
1 Windows DDK
2 http://coffeeqiqi.blogchina.com
3 Leven-端口关联进程-在核心态的实现方法
4 Msdn
5 port/connection hiding http://dev.csdn.net/Develop/article/28/84294.shtm
6 在NT系列操作系统里让自己“消失”
7 http://www.rootkit.com
很多关于端口关联进程的方法都是在用户态实现的,如fport等,结合大虾们的方法,这里给出一种在核心态实现的方法,方法可能比较笨 ^_^
大概是
1.用ZwQuerySystemInformation列出系统所有的句柄
2.找出类型为SOCKET的句柄
3.根据SYSTEM_HANDLE_INFORMATION.object的一些数据,用NtDeviceIoControlFile得到端口相关信息
这种方法在用户态已经实现过了,正好是shotgun的方法加上isly的方法,这里主要是注意一些API的转换。
在WINDOWSXP下可以用,如果是2k,需要修改WINXP_SOCKET_FLAG和WINXP_EPROCESS_NAMEOFFSET,WINXP_SOCKET_FLAG是SOCKET句柄的类型值,2K下是0x1a,WINXP_EPROCESS_NAMEOFFSET是EPROCESS的进程名偏移值,2k下是0x1fc
代码如下
/* Lport.sys */
/* list ports with processes in kernel */
/* Writen by Leven */
/* 2004-6-29 */
#include <ntddk.h>
#include <string.h>
#include "Port.h"
#include "tdiinfo.h"
#include "tdistat.h"
#define SystemHandleInformation 16
#define TCPUDP_FLAG 100
#define WINXP_SOCKET_FLAG 0x1c
#define WINXP_EPROCESS_NAMEOFFSET 0x174
#define ntohs(s) /
( ( ((s) >> 8) & 0x00FF ) | /
( ((s) << 8) & 0xFF00 ) )
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;
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;
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
NTSYSAPI
NTSTATUS
NTAPI
NtDeviceIoControlFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength
);
NTSYSAPI
BOOLEAN
NTAPI
NtDuplicateObject(
IN HANDLE hSourceProcessHandle,
IN HANDLE hSourceHandle,
IN HANDLE hTargetProcessHandle,
OUT HANDLE * lpTargetHandle,
IN ULONG dwDesiredAccess,
IN BOOLEAN bInheritHandle,
IN ULONG dwOptions
);
NTSYSAPI
NTSTATUS
NTAPI
PsLookupProcessByProcessId(
IN ULONG ulProcId,
OUT PEPROCESS * pEProcess
);
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath);
void DriverUnload(IN PDRIVER_OBJECT DriverObject);
//几个全局变量,记录端口相关信息,最后列印出来
ULONG g_pid[1000];
ULONG g_port[1000];
ULONG g_handle[1000];
ULONG g_tcpudp[1000];
ULONG g_num =0 ;
//获得所有句柄
ULONG GetHandleList()
{
ULONG n;
ULONG pBuffer;
NTSTATUS status;
pBuffer =(ULONG)ExAllocatePool(PagedPool,0x1000);
status = ZwQuerySystemInformation(SystemHandleInformation,(PVOID)pBuffer,0x1000,&n);
ExFreePool((PVOID)pBuffer);
if(STATUS_INFO_LENGTH_MISMATCH == status)
{
pBuffer =(ULONG)ExAllocatePool(NonPagedPool,n);
ZwQuerySystemInformation(SystemHandleInformation,(PVOID)pBuffer,n,NULL);
return pBuffer;
}
else
return 0;
}
//根据句柄得到端口信息
void GetOpenPort(ULONG dwProcessesID,ULONG Handle,int NoCache,ULONG tcpudp)
{
HANDLE hProc,DupHandle=NULL;
USHORT openport;
ULONG i=0;
NTSTATUS status;
TDI_CONNECTION_INFO TdiConnInfo={0};
TDI_CONNECTION_INFORMATION TdiConnInformation={0};
ULONG dwRetu=0;
IO_STATUS_BLOCK IoStatusBlock={0};
CLIENT_ID id;
OBJECT_ATTRIBUTES objatt = {0};
id.UniqueProcess = (HANDLE)dwProcessesID;
id.UniqueThread = 0;
//打开对方进程
NtOpenProcess(&hProc,PROCESS_DUP_HANDLE,&objatt,&id);
//复制句柄
NtDuplicateObject(hProc,
(HANDLE)Handle,
(HANDLE)0xffffffff,
&DupHandle,
0,
FALSE,
2);
//根据object的数据得到端口信息
if(NoCache==0x2)
{
TdiConnInformation.RemoteAddressLength= 4;
status = NtDeviceIoControlFile((HANDLE)DupHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
0x210012, // Command code
&TdiConnInformation,
sizeof(TdiConnInformation),
&TdiConnInfo,
sizeof(TdiConnInfo));
//进行TDI查询,得到连接的相关信息
if(status == 0)
{
openport = ntohs((USHORT)TdiConnInfo.ReceivedTsdus);
if(openport == 0)
return;
for(i=0;i<g_num;i++)
{
if(g_pid[i] == dwProcessesID && g_port[i] == openport)
if(tcpudp >= TCPUDP_FLAG && g_tcpudp[i] >= TCPUDP_FLAG || tcpudp < TCPUDP_FLAG && g_tcpudp[i] < TCPUDP_FLAG)
return;
}
g_pid[i] = dwProcessesID;
g_port[i] = openport;
g_handle[i] = Handle;
g_tcpudp[i] = tcpudp;
g_num++;
}
}
if(NoCache==0x1)
{
TdiConnInformation.RemoteAddressLength= 3;
status = NtDeviceIoControlFile((HANDLE)DupHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
0x210012, // Command code
&TdiConnInformation,
sizeof(TdiConnInformation),
&TdiConnInfo,
sizeof(TdiConnInfo));
//进行TDI查询,得到连接的相关信息
if(status == 0)
{
openport = ntohs((USHORT)TdiConnInfo.ReceivedTsdus);
if(openport == 0)
return;
for(i=0;i<g_num;i++)
{
if(g_pid[i] == dwProcessesID && g_port[i] == openport)
if(tcpudp >= TCPUDP_FLAG && g_tcpudp[i] >= TCPUDP_FLAG || tcpudp < TCPUDP_FLAG && g_tcpudp[i] < TCPUDP_FLAG)
return;
}
g_pid[i] = dwProcessesID;
g_port[i] = openport;
g_handle[i] = Handle;
g_tcpudp[i] = tcpudp;
g_num++;
}
}
}
void Start(ULONG pBuffer)
{
ULONG i;
PSYSTEM_HANDLE_INFORMATION pProcesses = (PSYSTEM_HANDLE_INFORMATION)(pBuffer+4);
ULONG nocache;
ULONG tcpudp;
PEPROCESS epro;
char *p;
for (i=0;i<((ULONG)(*(ULONG*)pBuffer));i++)
{
if(pProcesses[i].ObjectTypeNumber == WINXP_SOCKET_FLAG)
{
//得到SYSTEM_HANDLE_INFORMATION.object的相关数据
//这里要密切注意内存情况,一不小心就蓝屏。因为句柄经常变化,有些可能已经被销毁了
nocache = (ULONG)pProcesses[i].Object;
if(!MmIsAddressValid((VOID*)nocache))
continue;
nocache = (ULONG)(*((ULONG*)(nocache)+4));
tcpudp = (ULONG)(*((ULONG*)(pProcesses[i].Object)+1));
if(!MmIsAddressValid((VOID*)tcpudp))
continue;
tcpudp = (ULONG)(*((ULONG*)(tcpudp)+1));
if(nocache == 2 || nocache == 1)
{
GetOpenPort(pProcesses[i].ProcessID,pProcesses[i].Handle,nocache,tcpudp);
}
}
}
for(i=0;i<g_num;i++)
{
//大于TCPUDP_FLAG是TCP,小于是UDP,后面有说明
if(g_tcpudp[i] >= TCPUDP_FLAG)
DbgPrint("TCP:/t");
if(g_tcpudp[i] < TCPUDP_FLAG)
DbgPrint("UDP:/t");
//根据PID得到进程名
PsLookupProcessByProcessId(g_pid[i],&epro);
p = (char*)epro + WINXP_EPROCESS_NAMEOFFSET;
DbgPrint("ProcName=%s/tPID=%d/tport=%d/t%d/n",p,g_pid[i],g_port[i],g_tcpudp[i]);
}
return;
}
//
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
ULONG pbuf;
DbgPrint("DriverEntry/n");
DriverObject->DriverUnload = DriverUnload;
pbuf = GetHandleList();
Start(pbuf);
return STATUS_SUCCESS;
}
void DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("DriverUnload/n");
}
///
///
有个问题没有解决,就是不能知道端口是TCP还是UDP,这里用了一个TCPUDP_FLAG临时解决,值为100,SYSTEM_HANDLE_INFORMATION.object的某个数据大于它就是TCP,小于就是UDP,这种方法不是很好,以后再找更好的方法