WriteFile с Windows Sockets возвращает ошибку неверного параметра

Я боролся с Windows Sockets в течение двух дней, не имея возможности просто использовать запись в сокет, как в Linux. Я хочу написать свой собственный шелл-код, и я играю, как перенаправить stdout, stdin на дескриптор сокета (вот откуда мои игры). Я использую Windows 7 x64, при необходимости собери 7601. Вот мой код

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int main (int argc,char ** argv)
{   
    // boring code starts
    if (argc < 2)
    {
        printf("Usage getstdhandle <ip> <port> ");
    }

    WSADATA wsadata;
    int result = WSAStartup (MAKEWORD(2,2),&wsadata);
    if (result != NO_ERROR)
    {
        printf("error with wsastartup");
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons (atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr (argv[1]);

    SOCKET soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (soc == INVALID_SOCKET)
    {
        printf("Error with creating socket");
    }

    if (connect(soc,(struct sockaddr *)&server,sizeof(server)) == SOCKET_ERROR)
    {
        printf("Problem with connecting");
    }

     // boring code ends

    const char * buf = "Tekscik \n"; // know it's not really good in new C standards
    const char buf[] = "Test\n";  // not working also, shouldn't have any influence

    bool isSent = WriteFile((HANDLE)soc,(LPCVOID)buf,(DWORD)10,NULL,NULL);
    DWORD ret = GetLastError();
    printf("%.08x",ret);

    closesocket(soc);
    WSACleanup();
    return 0;
}

Вот как я запускаю свою программу

C:\Users\Domin568\Desktop>getstdhandle 192.168.56.1 5555
Tekscik
00000057     <---- Error code in hex

На моей второй машине я просто запускаю nc, чтобы прослушать эти данные следующим образом:

15:14|domin568[21] ~ $ nc -l -v -p 5555
Connection from 192.168.56.101:50328
15:15|domin568[22] ~ $ 

Я получаю код ошибки 0x57:

ERROR_INVALID_PARAMETER 87 (0x57) Параметр неверен.

Локальный сетевой трафик (любые отправленные данные, чистые рукопожатия соединений и FIN)

Что могло быть причиной этого? Я знаю, что это не лучший способ отправки данных, но MSDN утверждает, что это возможно.


person Domin568    schedule 12.05.2017    source источник
comment
используйте OVERLAPPED ov = {}; BOOL isSent = WriteFile((HANDLE)soc,(LPCVOID)buf,(DWORD)10, 0, &ov); в этом случае будет работать   -  person RbMm    schedule 12.05.2017
comment
Обработка ошибок нарушена, вызовите GetLastError () только в случае сбоя функции. Если позвонить, когда он не вышел из строя, то получится произвольный номер.   -  person Hans Passant    schedule 12.05.2017


Ответы (2)


из документации WriteFile

lpOverlapped [вход, выход, необязательно]

Указатель на _2 _ является обязательной, если параметр hFile был открыт с помощью FILE_FLAG_OVERLAPPED, в противном случае этот параметр может иметь значение NULL.

но являются socket функция создать файл (socket это дескриптор файла, он указывает на _ 5_ структура) с FILE_FLAG_OVERLAPPED? это недокументировано, и вы не можете это контролировать. поэтому вам нужно использовать _7 _ в качестве параметра для WriteFile < / а>.

при использовании WSASocket у нас уже есть опция dwFlags, и мы можем устанавливать или не устанавливать WSA_FLAG_OVERLAPPED (что эквивалентно FILE_FLAG_OVERLAPPED для CreateFile)

почему OVERLAPPED требуется, когда мы используем hFile, открытый с флагом FILE_FLAG_OVERLAPPED (без флага FO_SYNCHRONOUS_IO в _ 16_) ?

WriteFile вызов _ 18_

искать

PLARGE_INTEGER ByteOffset [in, optional] параметр _20 _ - но это необязательно, только если мы открываем файл как синхронный. если файл открыт в режиме асинхронного ввода-вывода - этот параметр является обязательным.

из исходного кода wrk

if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
}
else if (!ARGUMENT_PRESENT( ByteOffset ) && !(fileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT))) {

        //
        // The file is not open for synchronous I/O operations, but the
        // caller did not specify a ByteOffset parameter.  This is an error
        // situation, so cleanup and return with the appropriate status.
        //

        if (eventObject) {
            ObDereferenceObject( eventObject );
        }
        ObDereferenceObject( fileObject );
        return STATUS_INVALID_PARAMETER;

    }

это как раз ваш случай, когда вы звоните

WriteFile((HANDLE)soc,(LPCVOID)buf,(DWORD)10,NULL,NULL);

NtWriteFile вернусь к вам STATUS_INVALID_PARAMETER который преобразовал в ошибку win32 ERROR_INVALID_PARAMETER

но следующий код будет работать нормально.

OVERLAPPED ov = {};
BOOL isSent = WriteFile((HANDLE)soc,(LPCVOID)buf,(DWORD)10, 0, &ov);

потому что в этом случае ByteOffset будет указывать на &ov.Offset

однако для отправки данных в сокет гораздо лучше использовать _ 29_

person RbMm    schedule 12.05.2017
comment
Было бы неплохо, если бы это было где-то задокументировано в официальной документации MS. - person Andrew Henle; 12.05.2017
comment
@AndrewHenle - сейчас я редактирую собственный пост, на самом деле об этом сказано в документации WriteFile - указатель на структуру OVERLAPPED необходим, если параметр hFile был открыт с FILE_FLAG_OVERLAPPED, в противном случае этот параметр может иметь значение NULL. - person RbMm; 12.05.2017
comment
Большое спасибо ! Мой код теперь работает, и ваше объяснение было полным, и теперь я понимаю, в чем проблема :) - person Domin568; 13.05.2017
comment
@HarryJohnston - с точно FILE_FLAGGED_OVERLAPPED, конечно, нет. но проверяю (как минимум на windows 10, могу проверить и на других системах). socket внутренний вызов WSASocket с WSA_FLAG_OVERLAPPED, это, конечно, разные двоичные флаги, но он имеет тот же эффект - ZwCreateFile вызывается без флагов FILE_SYNCHRONOUS_IO_NONALERT и FILE_SYNCHRONOUS_IO_NALERT. в результате нет FO_SYNCHRONOUS_IO в файловом объекте - person RbMm; 13.05.2017
comment
@RbMm, я думаю, вы правы, это также объясняет комментарий, на который ссылается ответ Эндрю. Это довольно удивительно. Спасибо за разъяснения. - person Harry Johnston; 13.05.2017
comment
Я думаю, что в результате вы действительно, действительно должны использовать функции WSA, используя WriteFile и предполагая, что дескриптор будет асинхронным, может работать, но зависит от недокументированного поведения. - person Harry Johnston; 13.05.2017
comment
@HarryJohnston - и использование lpNumberOfBytesWritten не помогает, я проверяю ту же ошибку в этом случае (недопустимый параметр) также на xp (и может быть win7, нужен просмотр) будет сбой. но с перекрытием работы. смотрю под отладчиком. источник ошибки точно во фрагменте кода, который я вставляю. нет fileoffset - person RbMm; 13.05.2017
comment
@HarryJohnston об использовании WSA функций полностью согласен. - person RbMm; 13.05.2017
comment
@HarryJohnston - что действительно неясно (для меня) с сокетами, которые A socket handle created by the WSASocket or the socket function is inheritable by default - для меня это как минимум бессмысленно - потому что дескриптор сокета имеет дополнительный контекст в пользовательском режиме и не может быть скопирован в другой процесс. во-вторых, это может привести к возникновению трудных для поиска ошибок. скажем, мы создаем дочерний процесс с унаследованными дескрипторами, дескрипторы сокетов скопированы в него (зачем ??). чем закрываем подключенную розетку. мы можем разорвать эту связь. но нет - потому что в дочернем процессе существует дополнительный дескриптор ... - person RbMm; 13.05.2017

MSDN утверждает, что это возможно.

Это правда? Сокет обрабатывает состояния документации :

Опционально дескриптор сокета может быть дескриптором файла в Windows Sockets 2. Дескриптор сокета от поставщика Winsock может использоваться с другими функциями, отличными от Winsock, такими как ReadFile, WriteFile, ReadFileEx и WriteFileEx.

Обратите внимание на слово «необязательно».

За это обсуждение:

WriteFile с ошибкой SocketHandle

...

Не уверен, как вы можете прочитать эту страницу и подумать, что использовать дескриптор сокета с ReadFile et al.

Начнем с предложения: «Дескриптор сокета может быть дескриптором файла в Windows Sockets 2». Это можно прочесть двумя разными способами:

  1. При желании вы можете использовать любой дескриптор сокета в качестве дескриптора файла для ReadFile и друзей.

  2. Провайдер сокетов может дополнительно предоставить сокеты двойного назначения, которые работают с ReadFile, а также с функциями сокетов.

К сожалению, в этой статье недостаточно информации, чтобы окончательно решить, правильный ли №1 или №2. Однако тот факт, что ReadFile возвращает ERROR_INVALID_PARAMETER (согласно комментарию Майка Дейнса), я собираюсь предположить, что # 2 - правильная интерпретация, и ваш сетевой сокет также не является дескриптором файла. (Обратите внимание, что часто документация MSDN написана скорее с точки зрения команды, которая реализовала API, чем с точки зрения потребителя API. Если вы посмотрите на это в этом свете, то № 2 является более вероятной интерпретацией - я соглашусь, что это раздражает, потому что потребитель API с большей вероятностью поймет предложение как №1)

person Andrew Henle    schedule 12.05.2017
comment
socket - дескриптор файла. всегда. если это не дескриптор файла - ошибка будет ERROR_INVALID_HANDLE, но не ERROR_INVALID_PARAMETER - person RbMm; 12.05.2017
comment
WriteFile может и будет работать с сокетами, если использовать OVERLAPPED - person RbMm; 12.05.2017
comment
Возможно, однако, причина, по которой Microsoft решила, что сокет можно сделать асинхронным без документирования этого факта, могла заключаться в том, что никогда не требовалось быть действительным дескриптором. :-) - person Harry Johnston; 13.05.2017