| VB 源码 | VC 源码 | ASP源码 | JSP源码 | PHP源码 | CGI源码 | FLASH源码 | 素材模板 | C 源程序 | 站长工具 | 站长教程 |

系统安全

木马病毒
黑客技术
系统安全
防火墙
安全防范

本类阅读TOP10

·Windows 2000 密码破解不完全指南
·修改系统DLL文件实现禁用来防止木马病毒!
·浅谈Win2000密码的必然破解
·NTFS文件加密初探
·目前实用的几种入侵方法 以及工具防范
·防范非法用户入侵Win 2000/XP系统七招
·守护进程的概念和建立方法
·Windows 2000密码破解不完全指南
·虚拟服务器实现方法
·虚拟文件系统 (VFS) 简介

站内搜索

再谈进程与端口的映射

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

  首先,它检测当前用户是否拥有管理员权限(通过读取当前进程的令牌可知当前用户是否具有管理权限,请参考
相关历程),如果没有,打印一句提示后退出,然后设置当前进程的令牌,接着,用ZwOpenSection函数打开内核对象
\Device\PhysicalMemory,这个对象用于对系统物理内存的访问.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)
那么,打开内核对象\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字符串,函数原型见ntddk.h  
  InitializeObjectAttributes( &attributes, &physmemString,
          OBJ_CASE_INSENSITIVE, NULL, NULL ); //初始化OBJECT_ATTRIBUTES结构
  status = ZwOpenSection(pHandle, SECTION_MAP_READ, &attributes ); //打开内核对象\Device\PhysicalMemory,获得句柄
  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的分页机制>>)

  程序打开打开内核对象\Device\PhysicalMemory后,继续用函数ZwOpenFile打开内核对象\Device\Tcp和Device\Udp,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)
   
那么,打开内核对象\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函数获得系统当前所以进程的所建立的句柄及其相关信息,函数的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
  IN OUT PVOID SystemInformation,
  IN ULONG SystemInformationLength,
  OUT PULONG ReturnLength OPTIONAL
  };
(这个函数结构Microsoft没有公开,参见Gary Nebbett<<Windows NT/2000 Native API Reference>>)

第一个参数是一个枚举常数,设置要查询的系统信息类型,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<<Windows NT/2000 Native API Reference>>)
第二个参数输出查询的结果
第三个参数设置缓冲区的长度
第四个参数返回函数正确执行需要的缓冲区的大小
代码如下:
#define SystemHandleInformation 16
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;
}

因为如果一个进程打开了端口,那么它肯定会建立类型为\Device\Tcp和\Device\Udp的内核对象,所以,我们在当前进程中打开
上述的两个内核对象,在打开的同时保存了打开的句柄,这样,我们可以在上面获得的句柄列表中的当前进程中查找对象句柄的
数值和我们保存的两个打开的内核对象的句柄数值相同的句柄,并得到其句柄所指向的内核对象的地址.代码如下:
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下,\Device\Tcp和\Device\Udp的句柄类型值为0x16,在Windows 2000下这个值为0x1A)的核心地址用上面所描
述的办法得到其PTE内容,得到其WriteTable值,如果与内核对象\Device\Tcp和\Device\Udp相等,那么这个句柄就有可能打开
了一个端口,再对这个句柄进行确认,就可以了.确认的代码如下:
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为进程打开的句柄,并且经过比较为\Device\Tcp或\Device\Udp类型
//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 = %4d\n",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 = %4d\n",dwProcessesID,procName,openport);
        }
      }
    }
  }
  CloseHandle(hEven);
  CloseHandle(DupHandle);
}

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

参考:

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

关于我们:

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





相关文章
  • 电脑初学者必备之Windows进程大全
  • 解析Svchost.exe和Explorer.exe两大系统进程
  • 揭开SVCHOST.exe进程之谜
  • Win2000系统进程详解
  • 隐藏进程的简单办法
  • WinNT & Win2K下实现进程的完全隐藏
  • 再谈进程与端口的映射
  • windows进程中的内存结构
  • 隐藏任意进程,目录/文件,注册表,端口
  • 相关软件

  • 一个单进程的应用类  
  • 开始管理和切换进程  
  • 列举操作系统中所有的进程  
  • 列举所有的进程基类  
  • 这是一个弹出的进程窗口  
  • Windows 进程管理  
  • 依次启动不同进程  
  • 显示出系统中的所有进程和每个服务进程  
  • 列举系统中所有进程  
  • 建立NT服务进程的基类  

  • 下载首页关于我们广告服务联系方式常见问题隐私声明法律条款本站声明下载帮助发布软件站点地图谷歌卫星地图