WriteFile с Windows Sockets връща грешка в невалиден параметър

От два дни се боря с Windows Sockets, не мога просто да използвам запис в сокет, както е в Linux. Искам да напиша свой собствен шелкод и си играя как да пренасоча stdout, stdin към socket handle (оттам идват моите игри). Използвам Windows 7 x64, build 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 [in, out, optional]

Указател към OVERLAPPED структура е задължителна, ако параметърът hFile е отворен с FILE_FLAG_OVERLAPPED, в противен случай този параметър може да бъде NULL.

но са socket функция за създаване на файл (сокет това е манипулатор на файл, сочи към FILE_OBJECT структура) с FILE_FLAG_OVERLAPPED? това е недокументирано и не можете да го контролирате. така че трябва да използвате OVERLAPPED като параметър за WriteFile< /a>.

ако използвате WSASocket вече имаме опция dwFlags и можем да зададем или не WSA_FLAG_OVERLAPPED (което е еквивалентно на FILE_FLAG_OVERLAPPED за CreateFile)

защо OVERLAPPED структурата е задължителна, когато използваме hFile, отворен с FILE_FLAG_OVERLAPPED флаг (без FO_SYNCHRONOUS_IO флаг в FILE_OBJECT) ?

WriteFile обаждане < a href="https://msdn.microsoft.com/en-us/library/windows/hardware/ff567121(v=vs.85).aspx" rel="nofollow noreferrer">ZwWriteFile

потърсете

PLARGE_INTEGER ByteOffset [in, optional] параметър на ZwWriteFile - но не е задължително само ако отворим файл като синхронен. ако файлът е отворен в режим на асинхронен вход/изход - този параметър е задължителен.

от изходния код на 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

но за изпращане на данни към сокет много по-добре използвайте WSASend

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, трябва да се погледне) ще бъде срив. но със застъпена работа. гледам под дебъгер. източник на грешка точно в кодовия фрагмент, който поставям. без файлово отместване - 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 with SocketHandle Fails

...

Не съм сигурен как можете да прочетете тази страница и смятате, че е добра идея да използвате манипулатор на гнездо с 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