You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C语言中以二进制格式将.exe文件下载到缓冲区的问题

问题分析

你当前代码的核心问题是错误地打印了缓冲区的内存地址,而非缓冲区里的二进制数据。printf("0x%2x", lpOutBuffer);中的lpOutBuffer是指针变量,这里打印的是它指向的内存地址,不是下载到的字节内容,所以才会输出类似0x85c070这样的地址值,而非期望的字节十六进制。

另外,你的代码还有几个潜在问题:

  • 每次循环都重新分配lpOutBuffer,但前一次分配的内存没有及时释放,会造成内存泄漏
  • 分配内存时加了+1,但exe是二进制文件,不需要额外的终止符,这个操作多余且可能浪费内存
  • 没有将下载的二进制数据保存到文件,也没有验证IMAGE_DOS_HEADER的e_magic字段
修正后的代码示例

以下是修正后的完整逻辑,包含二进制数据打印、文件保存以及DOS头验证:

#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <stdlib.h>
#include <imagehlp.h>

#pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "imagehlp.lib")

int main() {
    HINTERNET hSession = WinHttpOpen(L"WinHTTP Downloader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) {
        printf("WinHttpOpen failed: %d\n", GetLastError());
        return 1;
    }

    HINTERNET hConnect = WinHttpConnect(hSession, L"your-server-url.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
    if (!hConnect) {
        printf("WinHttpConnect failed: %d\n", GetLastError());
        WinHttpCloseHandle(hSession);
        return 1;
    }

    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/path/to/your/file.exe", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
    if (!hRequest) {
        printf("WinHttpOpenRequest failed: %d\n", GetLastError());
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
        printf("WinHttpSendRequest failed: %d\n", GetLastError());
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    if (!WinHttpReceiveResponse(hRequest, NULL)) {
        printf("WinHttpReceiveResponse failed: %d\n", GetLastError());
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 准备保存文件的句柄
    HANDLE hFile = CreateFile(L"downloaded.exe", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile failed: %d\n", GetLastError());
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    BOOL bQueryData, bReadData;
    DWORD outBufSize = 0;
    LPBYTE lpOutBuffer; // 用LPBYTE而非LPSTR,明确是二进制字节
    DWORD bytesDownloaded = 0;
    DWORD bytesWritten = 0;
    BOOL dosHeaderChecked = FALSE;

    do {
        bQueryData = WinHttpQueryDataAvailable(hRequest, &outBufSize);
        if (!bQueryData) {
            printf("WinHttpQueryDataAvailable failed: %d\n", GetLastError());
            break;
        }
        printf("WinHttpQueryDataAvailable Success | Bytes available : %d\n", outBufSize);

        if (outBufSize == 0) break;

        lpOutBuffer = malloc(outBufSize);
        if (!lpOutBuffer) {
            printf("Out of memory for lpOutBuffer\n");
            break;
        }

        bReadData = WinHttpReadData(hRequest, lpOutBuffer, outBufSize, &bytesDownloaded);
        if (bReadData && bytesDownloaded > 0) {
            printf("WinHttpReadData Success | Bytes downloaded : %d\n", bytesDownloaded);
            
            // 打印二进制数据(按期望格式)
            for (DWORD i = 0; i < bytesDownloaded; i++) {
                printf("0x%02x", lpOutBuffer[i]);
                if (i != bytesDownloaded - 1) {
                    printf(", ");
                }
            }
            printf("\n");

            // 保存到文件
            if (!WriteFile(hFile, lpOutBuffer, bytesDownloaded, &bytesWritten, NULL) || bytesWritten != bytesDownloaded) {
                printf("WriteFile failed: %d\n", GetLastError());
                free(lpOutBuffer);
                break;
            }

            // 验证IMAGE_DOS_HEADER的e_magic(仅第一次下载数据时验证)
            if (!dosHeaderChecked && bytesDownloaded >= sizeof(IMAGE_DOS_HEADER)) {
                IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)lpOutBuffer;
                if (pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) { // IMAGE_DOS_SIGNATURE即0x5A4D(MZ)
                    printf("Successfully verified IMAGE_DOS_HEADER e_magic (0x%04x)\n", pDosHeader->e_magic);
                    dosHeaderChecked = TRUE;
                } else {
                    printf("IMAGE_DOS_HEADER e_magic verification failed (got 0x%04x)\n", pDosHeader->e_magic);
                }
            }
        } else {
            printf("WinHttpReadData failed: %d\n", GetLastError());
        }

        free(lpOutBuffer); // 每次循环后释放内存,避免泄漏
    } while (outBufSize > 0);

    // 清理资源
    CloseHandle(hFile);
    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);

    return 0;
}
关键修正点说明
  1. 缓冲区类型修正:将LPSTR改为LPBYTE,明确表示处理的是二进制字节数据,避免字符编码相关问题。
  2. 打印逻辑修正:循环遍历缓冲区的每个字节,用printf("0x%02x", lpOutBuffer[i])打印每个字节的十六进制值,符合你期望的格式。
  3. 内存泄漏修复:每次循环结束后立即释放当前分配的lpOutBuffer,避免内存泄漏。
  4. 二进制文件保存:添加CreateFile和WriteFile逻辑,将下载的二进制数据保存为exe文件。
  5. DOS头验证:当下载的数据长度足够包含IMAGE_DOS_HEADER时,检查e_magic字段是否等于IMAGE_DOS_SIGNATURE(即0x5A4D,对应ASCII的"MZ"),验证下载的文件是否为有效的PE文件。
  6. 多余内存分配修正:去掉了malloc(outBufSize + 1)中的+1,因为二进制文件不需要字符串终止符。

内容的提问来源于stack exchange,提问作者RenardoSharp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 06:21:29