Sunday, July 18, 2021

Parsing HTTPSENDREQUESTEX'S LPINTERNET_BUFFERSA struct with Frida

 This post is about how to read the LPINTERNET_BUFFERSA struct used by the Windows API HttpSendRequestExA. The background behind this post is answering an StackOverflow question but with a more detailed explanation.

To perform this example, instead of writing an application I will use Internet Explorer and I will use FRIDA on it. That said, the definition of the struct used in HttpSendRequestExA/W is:

typedef struct _INTERNET_BUFFERSA {

    DWORD                     dwStructSize;

    struct _INTERNET_BUFFERSA *Next;

    LPCSTR                    lpcszHeader;

    DWORD                     dwHeadersLength;

    DWORD                     dwHeadersTotal;

    LPVOID                    lpvBuffer;

    DWORD                     dwBufferLength;

    DWORD                     dwBufferTotal;

    DWORD                     dwOffsetLow;

    DWORD                     dwOffsetHigh;

} INTERNET_BUFFERSA, *LPINTERNET_BUFFERSA;

There are two things to note here, the first one is that the first member is the structure size and the second member of the struct is a pointer to the next one. A second thing to take into account is that LPCSTR is going to be a LPCWSTR if HttpSendRequestExW is used instead.

That said, this structure is pretty simple to parse, there is only three tricky members here:

  • LPCSTR size varies depending on the Process.pointerSize()
  • LPVOID size varies depending on the Process.pointerSize()

Next is a pointer to the next structure, so it will also vary depending on the Process.pointerSize()

With this in mind for a 32-bit process where Process.pointerSize=4 the offsets are:

DWORD                     dwStructSize; // 0

struct _INTERNET_BUFFERSA *Next; // 4

LPCSTR                    lpcszHeader; // 8

DWORD                     dwHeadersLength; // 12

DWORD                     dwHeadersTotal; // 16

LPVOID                    lpvBuffer; // 20

DWORD                     dwBufferLength; // 24

DWORD                     dwBufferTotal; // 28

DWORD                     dwOffsetLow; // 32

DWORD                     dwOffsetHigh; // 36

If the calculations are correct, dwStructSize will be 40 for a 32-bit process or 0x28 in hex. The following script is able to intercept correctly the HttpSendRequestW structure for a 32-bit process:

Interceptor.attach(Module.getExportByName(null, "HttpSendRequestExW"), {

onEnter (args) {

let internetBufferStruct = args[1];

console.log("Struct size: " + internetBufferStruct.readPointer());

console.log("*Next: " + internetBufferStruct.add(Process.pointerSize).readPointer());  

console.log("lpcszHeader: " + internetBufferStruct.add(Process.pointerSize * 2).readPointer());  

console.log("dwHeadersLength: " + internetBufferStruct.add(12).readPointer());  

console.log("dwHeadersTotal: " + internetBufferStruct.add(16).readPointer());  

let dwBufferLength = parseInt(internetBufferStruct.add(24).readPointer());

console.log("lpvBuffer: " + internetBufferStruct.add(Process.pointerSize * 5).readCString(dwBufferLength));  

console.log("dwBufferLength: " + dwBufferLength);  

console.log("dwBufferTotal: " + internetBufferStruct.add(28).readPointer());  

console.log("dwOffsetLow: " + internetBufferStruct.add(32).readPointer());  

console.log("dwOffsetHigh: " + internetBufferStruct.add(36).readPointer());  

}

});

And this is the result in a 32-bit process where as predicted the first member dwStructSize is 40(0x28) as predicted:

For a 64-bit process the only changes required are the differences in offsets generated by every pointer member and adjust the other members accordingly.

2023

Every year I start writing about a wrap-up of my year but I never end up finishing it. Hope this year is different. I'm starting with th...