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

