Синий экран при перезаписи пакетов на уровне DATAGRAM_DATA в WFP

Я пытался изменить исходящие пакеты DNS через уровень DATAGRAM_DATA в WFP, однако я получаю ошибки синего экрана при перезаписи IP-адреса назначения в исходящем пакете. Что я делаю не так?

Я признаю, что нашел параметры для FwpsInjectTransportSendAsync немного запутанными и не был уверен, что именно вставить в аргумент sendParams, хотя я думаю, что то, что у меня есть, выглядит правильно.

RtlIpv4StringToAddressExW(
L"1.1.1.1", // hard-coding the new (rewritten) dns server for now
FALSE,
&sin4.sin_addr,
&sin4.sin_port);

RtlIpv4StringToAddressExW(
    L"8.8.8.8",        // hard-coding the original dns server for now
    FALSE,
    &origSin4.sin_addr,
    &origSin4.sin_port);
if ((Direction == FWP_DIRECTION_OUTBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == 53) && (RemoteAddress == origSin4.sin_addr.S_un.S_addr))
{

    UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
    UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;
    UINT64 endpointHandle = inMetaValues->transportEndpointHandle;

    PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB((PNET_BUFFER_LIST)layerData);
    NdisRetreatNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, 0, NULL);

    PNET_BUFFER_LIST NetBufferList = NULL;
    NTSTATUS Status = FwpsAllocateCloneNetBufferList(layerData, NULL, NULL, 0, &NetBufferList);
    if (!NT_SUCCESS(Status))
    {
        return;
    }

    NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);

    if (!NetBufferList)
    {
        return;
    }

    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);

    PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);

    // Rewriting the dest ip
    IpHeader->DestinationAddress = sin4.sin_addr.S_un.S_addr;

    // Updating the IP checksum
    UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));

    // not 100% sure the sendParams argument is setup correctly, the docs are slightly unclear
    FWPS_TRANSPORT_SEND_PARAMS sendParams = {
        .remoteAddress = (UCHAR*)IpHeader->DestinationAddress,
        .remoteScopeId = inMetaValues->remoteScopeId,
        .controlData = inMetaValues->controlData,
        .controlDataLength = inMetaValues->controlDataLength,
        .headerIncludeHeader = inMetaValues->headerIncludeHeader,
        .headerIncludeHeaderLength = inMetaValues->headerIncludeHeaderLength
    };

    Status = FwpsInjectTransportSendAsync(g_InjectionHandle, NULL, endpointHandle, 0, &sendParams, AF_INET, inMetaValues->compartmentId, NetBufferList, DriverDatagramDataInjectComplete, NULL);
    if (!NT_SUCCESS(Status))
    {
        FwpsFreeCloneNetBufferList(NetBufferList, 0);
    }

    classifyOut->actionType = FWP_ACTION_BLOCK;
    classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
}

person horseyguy    schedule 01.08.2020    source источник
comment
первое, что вам нужно сделать - посмотреть memory.dmp. что такое код ошибки, где, стек вызовов. может быть, вы просто просматриваете инструкции в своем коде, которые вызывают ошибку. может быть (скажем, пул (куча) поврежден) ошибкой в ​​произвольном контексте. стабильны и повторяемы (всегда в одном и том же месте) или в случайном месте/времени. это вопрос, а не просто синий экран   -  person RbMm    schedule 02.08.2020
comment
@RbMm, о, это такой простой / маленький код, я подумал, что проблема может быть очевидна для тех, у кого есть опыт работы с WFP. Как мне найти и просмотреть файл memory.dmp?   -  person horseyguy    schedule 02.08.2020
comment
нет, ядро дамп памяти всегда полезен и значительно упрощает задачу. также - перед перезагрузкой системы - дамп находится внутри pagefile.sys - если у вас произошел сбой на виртуальной машине - вы можете просто открыть pagefile.sys внутри windbg после сбоя и до перезагрузки   -  person RbMm    schedule 02.08.2020


Ответы (1)


Две вещи выделяются для меня, обе в sendParams.

Во-первых, remoteAddress неверно. Ему нужен указатель на адрес, поэтому он должен быть (UCHAR*)&IpHeader->DestinationAddress.

Во-вторых, FwpsInjectTransportSendAsync() является асинхронным, поэтому любые параметры, которые вы ему передаете, должны оставаться действительными до тех пор, пока он не завершится, что может произойти после возврата вызывающей функции. Обычно вы выделяете некоторую структуру контекста, которая содержит sendParams и глубокие копии соответствующих членов (remoteAddress и controlData). Вы передаете это как контекст подпрограмме завершения, где вы освобождаете его.

person Luke    schedule 02.08.2020
comment
Спасибо, это отличная информация. Ради интереса, какие слои поддерживают потоки данных? Я пытаюсь связать контекст потока на уровне CONNECT_REDIRECT и получить к нему доступ на уровне DATAGRAM_DATA, но не могу получить дескриптор потока на уровне CONNECT_REDIRECT... - person horseyguy; 04.08.2020
comment
Проблема с CONNECT_REDIRECT заключается в том, что он вызывается до создания потока. Вместо этого вам придется связать свой контекст с потоком в AUTH_CONNECT или AUTH_FLOW_ESTABLISHED. - person Luke; 04.08.2020
comment
спасибо! могу я спросить, откуда вы все это знаете? я вообще не могу найти эту информацию в документах, у вас есть другой ресурс, на который вы можете дать ссылку? - person horseyguy; 05.08.2020
comment
Сочетание документации, примера кода, сообщений на форуме и опыта. Многое из этого на самом деле есть в документации, но она либо плохо организована, либо недостаточно ясна. - person Luke; 06.08.2020
comment
Нет, я работаю полный рабочий день и начал работать водителем WFP только в прошлом году или около того. Я могу ответить на любые вопросы SO, если я знаю ответ. - person Luke; 06.08.2020
comment
Я смог убедиться, что возвращаемое значение PIPV4_HEADER IpHeader NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0); равно NULL — есть идеи, почему это так? Мне пришлось изменить количество Retreat, так что это просто IpHeaderSize, поскольку исходящие пакеты получают транспортный заголовок - person horseyguy; 12.08.2020
comment
Это одна из тех вещей, которые не очевидны и фактически запутаны документацией. Вот ссылка на комментарий в образце кода, описывающий происходящее. - person Luke; 13.08.2020
comment
Вы должны помнить, что сетевой стек — это стек. На входящем пути пакеты проходят через уровень IP, затем транспортный уровень, затем уровень данных. На исходящем пути пакеты проходят через уровень данных, затем транспортный уровень, затем уровень IP. Таким образом, на входящем пути следующие слои могут видеть данные из предыдущих слоев. Но на исходящем пути предыдущие слои не могут видеть данные следующих слоев, потому что они еще не созданы. - person Luke; 13.08.2020
comment
Поэтому, если вы хотите получить доступ к IP-заголовку исходящей дейтаграммы UDP, вам придется сделать это на уровне OUTBOUND_IPPACKET. Хотя, если вы намерены просто переопределить заголовок IP, то FwpsInjectTransportSendAsync() создаст заголовок IP для вас. - person Luke; 13.08.2020
comment
са интересно! поэтому, если я хочу изменить адреса получателя/источника в заголовке IP, я МОГУ сделать это в DATAGRAM_DATA_LAYER? а затем использовать FwpsInjectNetwork (не InjectTransport) для его внедрения? или вы говорите, что если я хочу изменить адреса получателя/источника в заголовке IP, я должен сделать это на уровне OUTBOUND_IPPACKET? - person horseyguy; 13.08.2020
comment
Я думаю, вы можете сделать это в DATAGRAM_DATA, но я не уверен, так как никогда не делал этого сам. Если это возможно, я думаю, вам придется вручную вставить заголовок UDP в буфер, затем передать буфер FwpsConstructIpHeaderForTransportPacket() для создания заголовка IP (или создать его вручную), а затем ввести его с помощью FwpsInjectNetworkSendAsync(). - person Luke; 14.08.2020
comment
Боже, лол, я думал, что WFP был более приятным API, чем расширения ядра macos, но я беру все обратно! macos kext network/socket API намного лучше этого! хотя спасибо за информацию! :) - person horseyguy; 14.08.2020