内核层枚举网络端口 Windows 10、x64、R0

该文介绍了如何在Windows10环境下,通过逆向工程分析NSIDLL的NsiAllocateAndGetTable函数,利用IOCTL_NSI_GETALLPARAM控制码来查询TCP和UDP的本地网络连接信息。文章详细阐述了查询过程,包括参数设置、内存分配以及不同协议查询时的差异,并提供了源代码示例。
摘要由CSDN通过智能技术生成

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,
        &param, 0x70,
        &param, 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,
        &param, 0x70,
        &param, 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(&param, 0x00, 0x70);
    param.UnknownParam3 = (ULONG_PTR)NPI_MS_UDP_MODULEID;
    param.UnknownParam4 = 0x1;
    param.UnknownParam5 = 0x100000001;

    pIrp = IoBuildDeviceIoControlRequest(
        IOCTL_NSI_GETALLPARAM,
        DevObj,
        &param, 0x70,
        &param, 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,
        &param, 0x70,
        &param, 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;
}


5、效果展示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值