【问题标题】:WriteFile with Windows Sockets returns invalid parameter error带有 Windows 套接字的 WriteFile 返回无效参数错误
【发布时间】:2017-05-12 13:49:57
【问题描述】:

两天来,我一直在为 Windows Sockets 苦苦挣扎,无法像在 Linux 中那样使用 write to socket。我想编写自己的 shellcode,并且正在研究如何将 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) 参数不正确。

Local network traffic(任何数据发送,纯连接握手和FIN)

这可能是什么原因?我知道这不是发送数据的好方法,但 MSDN 说它应该是可能的。

【问题讨论】:

  • 在这种情况下使用OVERLAPPED ov = {}; BOOL isSent = WriteFile((HANDLE)soc,(LPCVOID)buf,(DWORD)10, 0, &amp;ov); 会起作用
  • 错误处理被破坏,只有在函数失败时才调用 GetLastError()。如果你在它没有失败时调用它,那么你会得到一个任意数字。

标签: c windows sockets writefile


【解决方案1】:

来自WriteFile 文档

lpOverlapped [输入、输出、可选]

一个指向OVERLAPPED结构的指针是必需的如果 参数是用 FILE_FLAG_OVERLAPPED 打开的,否则这个 参数可以为NULL。

但是socket 函数创建文件(socket 这是文件句柄,它指向FILE_OBJECT 结构)与FILE_FLAG_OVERLAPPED 吗?这是未记录的,您无法控制。所以你需要使用OVERLAPPED 作为WriteFile 的参数。

如果使用WSASocket,我们这里已经有dwFlags选项,可以设置或不设置WSA_FLAG_OVERLAPPED(相当于FILE_FLAG_OVERLAPPED em> 为CreateFile)

为什么 OVERLAPPED 结构是必需 当我们使用带有 FILE_FLAG_OVERLAPPED 标志打开的 hFile(没有 FO_SYNCHRONOUS_IO 标志在FILE_OBJECT) ?

WriteFile 致电ZwWriteFile

寻找

PLARGE_INTEGER ByteOffset [in, optional] ZwWriteFile 的参数 - 但仅当我们以同步方式打开文件时它是可选的。如果文件以异步 i/o 模式打开 - 此参数是强制

来自 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 将指向&amp;ov.Offset

但是,如果要向套接字发送数据,最好使用WSASend

【讨论】:

  • 如果在官方 MS 文档中记录在某处就好了。
  • @AndrewHenle - 我现在编辑自己的帖子,在文档WriteFile 中确实提到了这一点 - 如果打开 hFile 参数,则需要指向 OVERLAPPED 结构的指针FILE_FLAG_OVERLAPPED,否则该参数可以为NULL。
  • 非常感谢!我的代码现在可以工作了,您的解释已经完成,现在我明白问题所在了:)
  • @HarryJohnston - 完全是 FILE_FLAGGED_OVERLAPPED当然没有。但我检查(Windows 10 上的最低限度,可以检查和其他系统)。 socket 内部调用 WSASocketWSA_FLAG_OVERLAPPED 这当然是二进制不同的标志,但它具有 same 效果 - ZwCreateFile 在没有 FILE_SYNCHRONOUS_IO_NONALERTFILE_SYNCHRONOUS_IO_NALERT 标志的情况下调用。结果文件对象中没有FO_SYNCHRONOUS_IO
  • 我认为这里的结果是,你真的,真的应该使用 WSA 函数,使用 WriteFile 并假设句柄将是异步的,但取决于未记录的行为。
【解决方案2】:

MSDN 说应该可以。

真的吗? socket handles documentation states

套接字句柄可以选择是 Windows 套接字 2 中的文件句柄。 来自 Winsock 提供程序的套接字句柄可以与其他 非 Winsock 函数,例如 ReadFile、WriteFile、ReadFileEx 和 写文件。

注意“可选”这个词。

Per this discussion:

使用 SocketHandle 的 WriteFile 失败

...

不确定您如何阅读该页面并认为这是一个好主意 使用带有 ReadFile 等的套接字句柄。

让我们从这句话开始“一个套接字句柄可以选择是一个 Windows Sockets 2 中的文件句柄"。可以读取两种不同的 方式:

  1. 您可以选择使用任何套接字句柄作为 ReadFile 和朋友的文件句柄

  2. 套接字提供程序可以选择提供与 ReadFile 以及套接字函数一起使用的双重用途套接字。

很遗憾,该文章中没有足够的信息 最终决定#1 或#2 是否正确。然而事实 ReadFile 返回 ERROR_INVALID_PARAMETER(根据 Mike Danes 的 评论)我将假设#2是正确的解释 并且您的网络套接字也不是文件句柄。 (注意 通常,MSDN 文档是从角度编写的 实现 API 的团队,而不是 API 的使用者。如果 你从这个角度来看,#2 是更可能的解释—— 我同意这很烦人,因为 API 的使用者更多 可能将句子理解为#1)

【讨论】:

  • socket 是文件句柄。总是。如果它不是文件句柄 - 错误是 ERROR_INVALID_HANDLE 但不是 ERROR_INVALID_PARAMETER
  • WriteFile 可以并且将与套接字一起使用,如果使用OVERLAPPED
  • 尽管如此,微软决定可以在不记录事实的情况下将套接字设为异步的原因可能是因为它从来没有要求首先是一个有效的句柄地方。 :-)
猜你喜欢
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 1970-01-01
  • 2010-10-04
  • 2014-02-26
  • 1970-01-01
  • 2018-06-29
相关资源
最近更新 更多