Token is a free software produced by foundstone team. It can be used to list all open ports in the system that are opened by those processes. the method described below is based on fport v1.33. If it is different from the fport on your machine, check the fport version.
First, it checks whether the current user has administrator permissions (read the token of the current process to check whether the current user has administrative permissions. Refer to the relevant process). If not, print a prompt and exit. Then set the token of the current process. Then, use the ZwOpenSection function to open the kernel object \ Device \ PhysicalMemory, which is used to access the physical memory of the system. the prototype of the ZwOpenSection function is as follows:
NTSYSAPI
NTSTSTUS
NTAPI
ZwOpenSection (
Out PHANDLE sectionHandle;
IN ACCESS_MASK DesiredAccess;
IN POBJECT_ATTRIBUTES ObjectAttributes
};
(See ntddk. h)
The first parameter gets the handle after the function is successfully executed.
The second parameter DesiredAccess is a constant, which can be the following values:
# Define SECTION_QUERY 0x0001
# Define SECTION_MAP_WRITE0x0002
# 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)
(See ntddk. h)
The third parameter is a structure that contains information such as the object type to be opened. The structure is defined as follows:
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;
(See ntdef. h)
Use a macro to initialize the structure:
# 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 ;}
(See ntdef. h)
Then, the statement to open the kernel object \ Device \ PhysicalMemory is as follows:
WCHAR PhysmemName [] = L "\ Device \ PhysicalMemory ";
Void * pMapPhysicalMemory;
HANDLE pHandle;
Bool OpenPhysicalMemory ()
{
NTSTATUS status;
UNICODE_STRING physmemString;
OBJECT_ATTRIBUTES attributes;
RtlInitUnicodeString (& physmemString, PhysmemName); // initialize the Unicode string. For the function prototype, see ntddk. h.
InitializeObjectAttributes (& attributes, & physmemString,
OBJ_CASE_INSENSITIVE, NULL, NULL); // initialize the OBJECT_ATTRIBUTES Structure
Status = ZwOpenSection (pHandle, SECTION_MAP_READ, & attributes); // open the kernel object \ Device \ PhysicalMemory and obtain the handle.
If (! NT_SUCCESS (status ))
Return false;
PMapPhysicalMemory = MapViewOfFile (pHandle, FILE_MAP_READ,
0, 0x1000, 0 x );
// Map 0 x bytes from the memory address 0x30000
If (GetLastError ()! = 0)
Return false;
Return true;
}
In Windows NT/2000, the system is divided into kernel mode and user mode, that is, Ring0 and Ring3, in Windows NT/2000, we can see that all processes are running in Ring3. Generally, the page Directory of the System process (that is, the System process) the physical address is 0x30000, or the physical address of the smallest page directory in the system is 0x30000. the page Directory is composed of 1024 items, each pointing to a page table (PTE), each page table also consists of 1024 pages, and each page size is 4 K, 1024*4 = 4096 (0x1000). Therefore, 0x1000 bytes are mapped from the physical address 0x30000.
After the program opens the kernel object \ Device \ PhysicalMemory, continue to use the ZwOpenFile function to open the kernel object \ Device \ Tcp, Device \ Udp, ZwOpenFile
The function prototype is as follows:
NTSYSAPI
NTSTATUS
NTAPI
ZwOpenFile (
Out phandle FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
In ulong internal access,
In ulong OpenOptions
);
The first parameter returns the handle of the opened object.
The second parameter DesiredAccess is a constant, which can be the following values:
# 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)
The third parameter is a structure that contains information such as the object type to be opened. The structure definition is described above.
The fourth parameter returns the property of the open object, which is a structure and defined as follows:
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
The fifth parameter, Volume access, is a constant and can be the following values:
# Define file_1__read 0x00000001 // winnt
# Define file_pai_write 0x00000002 // winnt
# Define FILE_SHARE_DELETE0x00000004 // winnt
The sixth parameter OpenOptions is also a constant, which can be the following values:
# Define FILE_DIRECTORY_FILE0x00000001
# Define FILE_WRITE_THROUGH 0x00000002
# Define FILE_SEQUENTIAL_ONLY 0x00000004
# Define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
# Define FILE_SYNCHRONOUS_IO_ALERT0x00000010
# Define FILE_SYNCHRONOUS_IO_NONALERT0x00000020
# Define FILE_NON_DIRECTORY_FILE 0x00000040
# Define FILE_CREATE_TREE_CONNECTION 0x00000080
# Define FILE_COMPLETE_IF_OPLOCKED0x00000100
# Define FILE_NO_EA_KNOWLEDGE 0x00000200
# Define FILE_OPEN_FOR_RECOVERY0x00000400
# 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_COMPRESSION0x00008000
# Define FILE_RESERVE_OPFILTER 0x00100000
# Define FILE_OPEN_REPARSE_POINT 0x00200000
# Define FILE_OPEN_NO_RECALL0x00400000
# Define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
# Define FILE_COPY_STRUCTURED_STORAGE0x00000041
# Define FILE_STRUCTURED_STORAGE 0x00000441
# Define FILE_VALID_OPTION_FLAGS 0x00ffffff
# Define FILE_VALID_PIPE_OPTION_FLAGS0x00000032
# Define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032
# Define FILE_VALID_SET_FLAGS 0x00000036
(See ntddk. h)
Then, the statements for opening the kernel objects \ Device \ Tcp and \ Device \ Udp are as follows:
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 );
If (! NT_SUCCESS (status ))
Return NULL;
}
Then, the program uses the ZwQuerySystemInformation function to obtain the handle created by the current process of the system and its related information. The prototype of the function is as follows:
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation (
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
In out pvoid SystemInformation,
In ulong SystemInformationLength,
Out pulong ReturnLength OPTIONAL
};
The first parameter is an enumeration constant and sets the type of system information to be queried. ZwQuerySystemInformation supports 54 system information queries. We need to use its 16th function to query SystemHandleInformation.
The SYSTEM_HANDLE_INFORMATION structure is defined as follows:
Typedef struct _ SYSTEM_HANDLE_INFORMATION {
ULONG ProcessID; // process ID
UCHAR ObjectTypeNumber; // object type
UCHAR Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
USHORT Handle; // The value of the object Handle.
PVOID Object; // The kernel Object address indicated by the Object handle
ACCESS_MASK GrantedAccess; // access to the object allowed when a handle is created
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
The second parameter outputs the query result.
The third parameter sets the buffer length.
The fourth parameter returns the size of the buffer required for proper function execution.
The Code is as follows:
# Define SystemHandleInformation 16
PULONG GetHandleList ()
{
ULONG cbBuffer = 0x1000; // set a small buffer space first
PULONG pBuffer = new ULONG [cbBuffer]; // allocates memory.
NTSTATUS Status;
Do
{
Status = ZwQuerySystemInformation (
SystemHandleInformation,
PBuffer, cbBuffer * sizeof * pBuffer, NULL );
If (Status = STATUS_INFO_LENGTH_MISMATCH)
{
// If the returned error message is that the buffer length is insufficient, allocate the memory again.
Delete [] pBuffer;
PBuffer = new ULONG [cbBuffer * = 2];
}
Else if (! NT_SUCCESS (Status ))
{
// If there are other error messages, return
Delete [] pBuffer;
Return false;
}
}
While (Status = STATUS_INFO_LENGTH_MISMATCH );
Return pBuffer;
}
If a process opens a port, it will certainly establish kernel objects of the \ Device \ Tcp and \ Device \ Udp types. Therefore, we open the above two kernel objects in the current process, and save the opened handle while opening. In this way, we can find the value of the object handle in the current process in the handle list obtained above and the value of the two opened kernel objects that we saved are the same, obtain the address of the kernel object pointed to by the handle. the Code is as follows:
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;
}
This kernel object address is a linear address. We need to convert this address to a physical address and obtain related data. in fport, the conversion is as follows: (For details, refer to the WebCrazy article <small discuss the paging mechanism of 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 );
// Remap the physical memory to obtain the physical address content of the PTE point to the current linear address.
}
Then, obtain the physical page pointed to by this address based on the linear address of the kernel object and obtain the page that reflects the content of the current kernel object. Its structure is as follows:
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;
(Note: I cannot guarantee the correctness of this structure, but we only use two values. For the program, this structure can work, ^ _ ^)
The Code is as follows:
ULONG CurrWriteTable;
ULONG NoCache;
Void GetMustPar (DWORD objAddress)
{
DWORD CurrAddress;
CurrAddress = objAddress & 0 xFFF;
PPTE pte = (PPTE) (VOID *) (DWORD) NewmapPhy + CurrAddress );
CurrWriteTable = pte-> WriteTable;
CurrNoCache = Pte-> NoCache;
}
Now we want to get everything we want. What we need to do is traverse the process and use every handle in every process (haha, not every handle, in Windows NT, the handle type values of \ Device \ Tcp and \ Device \ Udp are 0x16 and 0x1A in Windows 2000) use the method described above to get its PTE content and get its WriteTable value. If it is equal to the kernel object \ Device \ Tcp and \ Device \ Udp, this handle may open a port and confirm the handle. the confirmation code is as follows:
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 {
LONGUserDataLength;
PVOID UserData;
LONGOptionsLength;
PVOID Options;
LONGRemoteAddressLength;
PVOID RemoteAddress;
} TDI_CONNECTION_INFORMATION, * PTDI_CONNECTION_INFORMATION;
(For the above structure, see tdi. h)
Void GetOpenPort (DWORD dwProcessesID, USHORT Handle, int NoCache)
// DwProcessesID indicates the process ID
// Handle is the Handle opened by the process and is of the \ Device \ Tcp or \ Device \ Udp type.
// NoCache is a value in the PTE structure.
{
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 ))
// Perform TDI query to obtain the connection information
{
Openport = ntohs (u_short) TdiConnInfo. ReceivedTsdus );
Procname = GetProcName (dwProcessesID); // obtain the process name of the process ID
Printf ("PID = % 4d ProcessName = % 15 s PORT = % 4d \ n", dwProcessesID, procName, openport );
}
}
If (NoCache = 0x1)
{
TdiConnInformation. RemoteAddressLength = 3;
If (DeviceIoControl (DupHandle, 0x210012,
& TdiConnInformation, sizeof (TdiConnInformation ),
& TdiConnInfo, sizeof (TdiConnInfo ),
0, & overlap ))
// Perform TDI query to obtain the connection information
{
Openport = ntohs (u_short) TdiConnInfo. ReceivedTsdus );
Procname = GetProcName (dwProcessesID); // obtain the process name of the process ID
Printf ("PID = % 4d ProcessName = % 15 s PORT = % 4d \ n", dwProcessesID, procName, openport );
}
}
}
}
CloseHandle (hEven );
CloseHandle (DupHandle );
}
This is my analysis of fport.exe and its implementation code. The demo can be downloaded from whitecell.org.