📄 29a-7.024
字号:
We will use class ObjectNameInformation and ObjectAllTypesInformation.
ObjectNameInfromation class will fill the buffer with OBJECT_NAME_INFORMATION
structure, ObjectAllTypesInformation class with OBJECT_ALL_TYPES_INFORMATION
structure then.
#define ObjectNameInformation 1
#define ObjectAllTypesInformation 3
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
Name determines the name of the handle.
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;
Name determines the name of type object which immediately follows
each OBJECT_TYPE_INFORMATION structure. The next OBJECT_TYPE_INFORMATION
structure follows this Name, starting on the first four-byte boundary.
ObjectTypeNumber from SYSTEM_HANDLE_INFORMATION structure is an index
to TypeInformation array.
Harder is to get the name of handle from other process. There are two
possibilities how to name it. First is to copy the handle via NtDuplicateObject
to our process and then to name it. This method will fail for some specific
types of handles. But it will fail only for few, so we can stay calm and use
this.
NtDuplicateObject(
IN HANDLE SourceProcessHandle,
IN HANDLE SourceHandle,
IN HANDLE TargetProcessHandle,
OUT PHANDLE TargetHandle OPTIONAL,
IN ACCESS_MASK DesiredAccess,
IN ULONG Attributes,
IN ULONG Options
);
SourceProcessHandle is a handle of process which owns SourceHandle
which is the handle we want to copy. TargetProcessHandle is handle of process
where to copy. This will be handle to our process in our case. TargetHandle
is the pointer on handle where to save a copy of original handle. DesiredAccess
should be set to PROCESS_QUERY_INFORMATION, Attribures and Options to 0.
Second naming method which works with any handle is to use system
driver. Source code for this is available in OpHandle project on my site
http://rootkit.host.sk.
=====[ 10. Ports ]==============================================================
The easiest way to enumarate open ports is to use functions called
AllocateAndGetTcpTableFromStack and AllocateAndGetUdpTableFromStack, and or
AllocateAndGetTcpExTableFromStack and AllocateAndGetUdpExTableFromStack from
iphlpapi.dll. The Ex functions are available since Windows XP.
typedef struct _MIB_TCPROW {
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
} MIB_TCPROW, *PMIB_TCPROW;
typedef struct _MIB_TCPTABLE {
DWORD dwNumEntries;
MIB_TCPROW table[ANY_SIZE];
} MIB_TCPTABLE, *PMIB_TCPTABLE;
typedef struct _MIB_UDPROW {
DWORD dwLocalAddr;
DWORD dwLocalPort;
} MIB_UDPROW, *PMIB_UDPROW;
typedef struct _MIB_UDPTABLE {
DWORD dwNumEntries;
MIB_UDPROW table[ANY_SIZE];
} MIB_UDPTABLE, *PMIB_UDPTABLE;
typedef struct _MIB_TCPROW_EX
{
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
DWORD dwProcessId;
} MIB_TCPROW_EX, *PMIB_TCPROW_EX;
typedef struct _MIB_TCPTABLE_EX
{
DWORD dwNumEntries;
MIB_TCPROW_EX table[ANY_SIZE];
} MIB_TCPTABLE_EX, *PMIB_TCPTABLE_EX;
typedef struct _MIB_UDPROW_EX
{
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwProcessId;
} MIB_UDPROW_EX, *PMIB_UDPROW_EX;
typedef struct _MIB_UDPTABLE_EX
{
DWORD dwNumEntries;
MIB_UDPROW_EX table[ANY_SIZE];
} MIB_UDPTABLE_EX, *PMIB_UDPTABLE_EX;
DWORD WINAPI AllocateAndGetTcpTableFromStack(
OUT PMIB_TCPTABLE *pTcpTable,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion;
);
DWORD WINAPI AllocateAndGetUdpTableFromStack(
OUT PMIB_UDPTABLE *pUdpTable,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion;
);
DWORD WINAPI AllocateAndGetTcpExTableFromStack(
OUT PMIB_TCPTABLE_EX *pTcpTableEx,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion;
);
DWORD WINAPI AllocateAndGetUdpExTableFromStack(
OUT PMIB_UDPTABLE_EX *pUdpTableEx,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion;
);
There is another way to do this stuff. When program creates a socket
and starts listening it surely has an open handle for it and for open port.
We can enumerate all open handles in the system and send them special buffer
via NtDeviceIoControlFile to find out whether the handle is for open port
or not. This will also give us information about the port. Because there are
a lot of open handles we will test only handles which type is File and name
is \Device\Tcp or \Device\Udp. Open ports have only this type and name.
When we look to the code of iphlpapi.dll functions above we find out
that these functions also calls NtDeviceIoControlFile and sends special buffer
to get a list of all open ports in the system. That mean only functions we
need to hook for hiding ports is NtDeviceIoControlFile.
NTSTATUS 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
);
Interesting agruments for us now are FileHandle which specify a handle
of device to communicate with, IoStatusBlock which points to a variable that
receives the final completion status and information about the requested
operation, IoControlCode that is a number specifying type of the device,
method, file access and a function. InputBuffer contains input data that are
InputBufferLength bytes long and similarly OutputBuffer and OutputbufferLength.
=====[ 10.1 Netstat, OpPorts on WinXP, FPort on WinXP ]=========================
Getting a list of all open ports is the first way which is used by e.g.
OpPorts and FPort on Windows XP and also Netstat.
Programs calls here NtDeviceIoControlFile twice with IoControlCode
0x000120003. OutputBuffer is filled after a second call. Name of FileHandle is
here alwats \Device\Tcp. InputBuffer differs for different types of call:
1) To get an array of MIB_TCPROW InputBuffer looks as follows:
first call:
0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
second call:
0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
2) To get an array of MIB_UDPROW:
first call:
0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
second call:
0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
3) To get an array of MIB_TCPROW_EX:
first call:
0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
second call:
0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
4) To get an array of MIB_UDPROW_EX:
first call:
0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
second call:
0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00
0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
You can see the buffers are different in few bytes only. We can lucidly
recapitulate these:
Calls we are interested in have InputBuffer[1] set to 0x04 and mainly
InputBuffer[17] on 0x01. Only after these input data we get filled OutputBuffer
with desiderative tables. If we want to get info about TCP ports we set
InputBuffer[0] on 0x00, or on 0x01 for information about UDP. If we want
extended output tables (MIB_TCPROW_EX or MIB_UDPROW_EX) we use Inputbuffer[16]
in second call set to 0x02.
If we find out the call with these parameters we can change the output
buffer. To get number of rows in output buffer simply divide Information from
IoStatusBlock by size of the row. Hiding of one row is easy then. Just rewrite
it with following rows and delete last row. Don't forget to change
OutputBufferLength and IoStatusBlock.
=====[ 10.2 OpPorts on Win2k and NT4, FPort on Win2k ]==========================
We use NtDeviceIoControlFile with IoControlCode 0x00210012 to determine
if the handle of File type and name \Device\Tcp or \Device\Udp is the handle of
open port.
So at first we compare IoControlCode and then a type and the name of
the handle. If it is still interesting then we compare the length of input
buffer which should be equal to the length of struct TDI_CONNECTION_IN. This
length is 0x18. OutputBuffer is TDI_CONNETION_OUT.
typedef struct _TDI_CONNETION_IN
{
ULONG UserDataLength,
PVOID UserData,
ULONG OptionsLength,
PVOID Options,
ULONG RemoteAddressLength,
PVOID RemoteAddress
} TDI_CONNETION_IN, *PTDI_CONNETION_IN;
typedef struct _TDI_CONNETION_OUT
{
ULONG State,
ULONG Event,
ULONG TransmittedTsdus,
ULONG ReceivedTsdus,
ULONG TransmissionErrors,
ULONG ReceiveErrors,
LARGE_INTEGER Throughput
LARGE_INTEGER Delay,
ULONG SendBufferSize,
ULONG ReceiveBufferSize,
ULONG Unreliable,
ULONG Unknown1[5],
USHORT Unknown2
} TDI_CONNETION_OUT, *PTDI_CONNETION_OUT;
Concrete implementation of how to determine the handle is open port
is available in source code of OpPorts on http://rootkit.host.sk. We are
interested in hiding specific port now. We already compared InputBufferLength
and IoControlCode. Now we have to compare RemoteAddressLength. This is always
3 or 4 for open port. The last we have to do is to compare ReceivedTsdus from
OutputBuffer which contains the port in network form and our list of ports we
want to hide. Differentiate between TCP and UDP is done according to the name
of the handle. By deleting OutputBuffer, changing IoStatusBlock and returning
the value of STATUS_INVALID_ADDRESS we will hide this port.
=====[ 11. Ending ]=============================================================
Concrete implementation of described techniques will be available with
the source code of Hander defender rootkit in version 1.0.0 on its homepage
http://rootkit.host.sk and on http://www.rootkit.com.
It is possible I will add some more information about invisibility on
Windows NT in the future. New versions of this document could also contain
improvement of described methods or new comments.
Special thanks to Ratter who give me a lot of knowhow which was
necessary to write this document and to code project Hacker defender.
Send all remarks to holy_father@phreaker.net or to the board on
http://rootkit.host.sk.
===================================[ End ]======================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -