1、环境:
Windows 10 X64
NtBuildNumber:19044
2、原理
通过给\Device\Nsi设备发送IOCTL_NSI_GETALLPARAM进行查询;
R3层使用GetTcpTable和GetUdpTable分别检索TCP和UDP连接表从而获取本地网络连接信息,这两个函数通过windbg查看其函数调用链:GetTcpTable/GetUdpTable —>(lphlpapi.dll)GetTcpTableInternl/GetUdpTableInternal —>(nsi.dll)NsiAllocateAndGetTable.
该函数会查询两次,第一次获取连接数量,根据返回的连接数分配合适大小的内存,在第二查询时会将连接信息填充到buffer中,主要的内容就是对nsi.dll中NsiAllocateAndGetTable的逆向,并在r0实现。
3、实现过程
首先是控制码(使用IDA看nsi.dll,NsiAllocateAndGetTable函数,调用NtDeviceIoControlFile的地方第六个参数就是):
#define IOCTL_NSI_GETALLPARAM 0x12001B
其次是传入NtDeviceIoControlFile的通用结构体buffer,0x70字节大小,下面是我的定义
typedef struct _NSI_PARAM
{
ULONG_PTR UnknownParam1; //0
ULONG_PTR UnknownParam2; //0
ULONG_PTR UnknownParam3; //NPI_MODULEID指针
ULONG_PTR UnknownParam4; //硬编码
ULONG_PTR UnknownParam5; //硬编码
ULONG_PTR UnknownParam6; //结构体1数组指针
ULONG_PTR UnknownParam7; //结构体1大小
ULONG_PTR UnknownParam8; //0
ULONG_PTR UnknownParam9; //0
ULONG_PTR UnknownParam10; //结构体2数组指针
ULONG_PTR UnknownParam11; //结构体2大小
ULONG_PTR UnknownParam12; //结构体3数组指针
ULONG_PTR UnknownParam13; //结构体3数组指针
ULONG_PTR ConnCount; //连接数
}NSI_PARAM, *PNSI_PARAM;
查询TCP时需要传入3个buffer,查询UDP传入两个,并且相关硬编码有所差异,NPI_MODULEID由上层函数GetTcpTableInternal传入,可以在ida中直接提取,查询TCP时,NPI_MODULEID为NPI_MS_TCP_MODULEID。
UCHAR NPI_MS_TCP_MODULEID[24] = {
0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x03,0x4a,0x00,0xeb,0x1a,0x9b,0xd4,0x11,
0x91,0x23,0x00,0x50,0x04,0x77,0x59,0xbc,
};
第一次查询时,只需要填写NSI_PARAM结构中的3,4,5三个域,查询成功ConnCount会得到TCP连接数,之后就是第二次查询,查询前需要分配内存。但是buffer大小需要在返回的连接数上加2(我也不清楚为啥加)
4、源码
DriverEntry.h
#pragma once
#include<ntifs.h>
typedef struct _NSI_STATUS_ENTRY
{
ULONG_PTR dwState;
ULONG_PTR Unknown1;
}NSI_STATUS_ENTRY, * PNSI_STATUS_ENTRY;
typedef struct _INTERNAL_UDP_TABLE_ENTRY
{
USHORT Unknown1;
USHORT Port;
ULONG dwIP;
UCHAR Unknown2[0x14];
}INTERNAL_UDP_TABLE_ENTRY, * PINTERNAL_UDP_TABLE_ENTRY;
typedef struct _INTERNAL_TCP_TABLE_SUBENTRY
{
USHORT Unknown1;
USHORT Port;
ULONG dwIP;
UCHAR Unknown2[20];
}INTERNAL_TCP_TABLE_SUBENTRY, * PINTERNAL_TCP_TABLE_SUBENTRY;
typedef struct _INTERNAL_TCP_TABLE_ENTRY
{
INTERNAL_TCP_TABLE_SUBENTRY localEntry;
INTERNAL_TCP_TABLE_SUBENTRY remoteEntry;
}INTERNAL_TCP_TABLE_ENTRY, * PINTERNAL_TCP_TABLE_ENTRY;
typedef struct _NSI_PROCESSID_INFO
{
ULONG dwUdpProId;
ULONG UnknownParam2;
ULONG UnknownParam3;
ULONG dwTcpProId;
ULONG UnknownParam5;
ULONG UnknownParam6;
ULONG UnknownParam7;
ULONG UnknownParam8;
}NSI_PROCESSID_INFO, * PNSI_PROCESSID_INFO;
typedef struct _NSI_PARAM
{
ULONG_PTR UnknownParam1; //0
ULONG_PTR UnknownParam2; //0
ULONG_PTR UnknownParam3; //NPI_MODULEID指针
ULONG_PTR UnknownParam4; //硬编码
ULONG_PTR UnknownParam5; //硬编码
ULONG_PTR lpMem; //结构体1数组指针
ULONG_PTR UnknownParam7; //结构体1大小
ULONG_PTR UnknownParam8; //0
ULONG_PTR UnknownParam9; //0
ULONG_PTR lpStatus; //结构体2数组指针
ULONG_PTR UnknownParam11; //结构体2大小
ULONG_PTR UnknownParam12; //结构体3数组指针
ULONG_PTR UnknownParam13; //结构体3数组指针
ULONG_PTR ConnCount; //连接数
}NSI_PARAM, * PNSI_PARAM;
#define IOCTL_NSI_GETALLPARAM 0x12001B
DriverEntry.c
#include<ntifs.h>
#include<string.h>
#include<ntstrsafe.h>
#include"DriverEntry.h"
#define HTONS(a) (((0xFF&a)<<8) + ((0xFF00&a)>>8))
static long htonl(long value)
{
return (((value & 0xff000000) >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) | ((value & 0x000000ff) << 24));
}
PCHAR GetIP(unsigned int ipAddr) {
static char pIp[20];
unsigned int nIpAddr = htonl(ipAddr);
RtlStringCbPrintfA(pIp, sizeof(pIp), "%d.%d.%d.%d",
(nIpAddr >> 24) & 0xFF,
(nIpAddr >> 16) & 0xFF,
(nIpAddr >> 8) & 0xFF,
nIpAddr & 0xFF);
return pIp;
}
VOID MyUnload(PDRIVER_OBJECT pDriverObject)
{
UNREFERENCED_PARAMETER(pDriverObject);
DbgPrint("Unload!\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath) {
PIRP pIrp = NULL;
KEVENT Event;
NTSTATUS Statu;
IO_STATUS_BLOCK StatusBlock;
NSI_PARAM param = { 0, };
PIO_STACK_LOCATION StackLocation = NULL;
UCHAR NPI_MS_TCP_MODULEID[24] = {
0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x03,0x4a,0x00,0xeb,0x1a,0x9b,0xd4,0x11,
0x91,0x23,0x00,0x50,0x04,0x77,0x59,0xbc,
};
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = MyUnload;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\Nsi");
PFILE_OBJECT FileObj;
PDEVICE_OBJECT DevObj;
Statu = IoGetDeviceObjectPointer(
&DeviceName,
FILE_ALL_ACCESS,
&FileObj, &DevObj
);
param.UnknownParam3 = (ULONG_PTR)&NPI_MS_TCP_MODULEID;
param.UnknownParam4 = 0x3;//udp 0x1
param.UnknownParam5 = 0x100000001;
pIrp = IoBuildDeviceIoControlRequest(
IOCTL_NSI_GETALLPARAM,
DevObj,
¶m, 0x70,
¶m, 0x70,
FALSE,
&Event,
&StatusBlock
);
if (pIrp==NULL) {
DbgPrint("IoBuildDeviceIoControlRequest failed");
return STATUS_SUCCESS;
}
StackLocation = IoGetNextIrpStackLocation(pIrp);
StackLocation->FileObject = FileObj;
StackLocation->DeviceObject = DevObj;
pIrp->RequestorMode = KernelMode;
Statu = IoCallDriver(DevObj, pIrp);
Statu = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
0
);
ULONG_PTR Count = param.ConnCount + 2;
PINTERNAL_TCP_TABLE_ENTRY pBuf1 = (PINTERNAL_TCP_TABLE_ENTRY)ExAllocatePool(NonPagedPool, 0x38 * Count);
PNSI_STATUS_ENTRY pBuf2 = (PNSI_STATUS_ENTRY)ExAllocatePool(NonPagedPool, 0x10 * Count);
PNSI_PROCESSID_INFO pBuf3 = (PNSI_PROCESSID_INFO)ExAllocatePool(NonPagedPool, 0x20 * Count);
RtlZeroMemory(pBuf1, 0x38 * Count);
RtlZeroMemory(pBuf2, 0x10 * Count);
RtlZeroMemory(pBuf3, 0x20 * Count);
param.UnknownParam3 = (ULONG_PTR)&NPI_MS_TCP_MODULEID;
param.UnknownParam4 = 0x3;
param.UnknownParam5 = 0x100000001;
param.lpMem = (ULONG_PTR)pBuf1;
param.UnknownParam7 = 0x38;
param.lpStatus = (ULONG_PTR)pBuf2;
param.UnknownParam11 = 0x10;
param.UnknownParam12 = (ULONG_PTR)pBuf3;
param.UnknownParam13 = 0x20;
param.ConnCount = Count - 2;
pIrp = IoBuildDeviceIoControlRequest(
IOCTL_NSI_GETALLPARAM,
DevObj,
¶m, 0x70,
¶m, 0x70,
FALSE,
&Event,
&StatusBlock
);
StackLocation = IoGetNextIrpStackLocation(pIrp);
StackLocation->FileObject = FileObj;
StackLocation->DeviceObject = DevObj;
pIrp->RequestorMode = KernelMode;
Statu = IoCallDriver(DevObj, pIrp);
Statu = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
0
);
DbgPrint("TCP PORT");
char localIp[20] = { 0 };
char remoteIp[20] = { 0 };
for (ULONG i = 0; i < param.ConnCount; i++)
{
if (pBuf1[i].localEntry.dwIP != 0) {
strncpy(localIp, GetIP(pBuf1[i].localEntry.dwIP), 20);
strncpy(remoteIp, GetIP(pBuf1[i].remoteEntry.dwIP), 20);
DbgPrint("pid:%d \tlocalIp:%s\t\tPort:%d\t\tremoteIp:%s\t\tPort:%d", pBuf3[i].dwTcpProId, localIp, HTONS(pBuf1[i].localEntry.Port), remoteIp, HTONS(pBuf1[i].remoteEntry.Port));
}
}
UCHAR NPI_MS_UDP_MODULEID[24] =
{
0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x02,0x4a,0x00,0xeb,0x1a,0x9b,0xd4,0x11,
0x91,0x23,0x00,0x50,0x04,0x77,0x59,0xbc,
};
memset(¶m, 0x00, 0x70);
param.UnknownParam3 = (ULONG_PTR)NPI_MS_UDP_MODULEID;
param.UnknownParam4 = 0x1;
param.UnknownParam5 = 0x100000001;
pIrp = IoBuildDeviceIoControlRequest(
IOCTL_NSI_GETALLPARAM,
DevObj,
¶m, 0x70,
¶m, 0x70,
FALSE,
&Event,
&StatusBlock
);
StackLocation = IoGetNextIrpStackLocation(pIrp);
Statu = IoCallDriver(DevObj, pIrp);
Statu = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
0
);
Count = param.ConnCount + 2;
PINTERNAL_UDP_TABLE_ENTRY pBuf4 = (PINTERNAL_UDP_TABLE_ENTRY)ExAllocatePool(NonPagedPool, 0x1c * Count);
PNSI_PROCESSID_INFO pBuf5 = (PNSI_PROCESSID_INFO)ExAllocatePool(NonPagedPool, 0x20 * Count);
RtlZeroMemory(pBuf4, 0x1c * Count);
RtlZeroMemory(pBuf5, 0x20 * Count);
param.UnknownParam3 = (ULONG_PTR)NPI_MS_UDP_MODULEID;
param.UnknownParam4 = 0x1;
param.UnknownParam5 = 0x100000001;
param.lpMem = (ULONG_PTR)pBuf4;
param.UnknownParam7 = 0x1c;
param.UnknownParam12 = (ULONG_PTR)pBuf5;
param.UnknownParam13 = 0x20;
param.ConnCount = Count - 2;
pIrp = IoBuildDeviceIoControlRequest(
IOCTL_NSI_GETALLPARAM,
DevObj,
¶m, 0x70,
¶m, 0x70,
FALSE,
&Event,
&StatusBlock
);
StackLocation = IoGetNextIrpStackLocation(pIrp);
Statu = IoCallDriver(DevObj, pIrp);
Statu = KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
0
);
DbgPrint("UDP PORT");
char UDPlocalIp[20] = { 0 };
for (ULONG i = 0; i < param.ConnCount; i++)
{
if (pBuf4[i].dwIP!= 0) {
strncpy(UDPlocalIp, GetIP(pBuf4[i].dwIP), 20);
DbgPrint("pid:%d\tip:%s\tport:%d\n", pBuf5[i].dwUdpProId, UDPlocalIp, HTONS(pBuf4[i].Port));
}
}
ExFreePool(pBuf1);
ExFreePool(pBuf2);
ExFreePool(pBuf3);
ExFreePool(pBuf4);
ExFreePool(pBuf5);
ObDereferenceObject(FileObj);
return STATUS_SUCCESS;
}