windowswininetfrida

Frida - Windows native app - Read a structure exposed by Wininet.h


I hook the function HttpSendRequestExA of a native Windows application using Frida but I cannot achieve to read the structure INTERNET_BUFFERSA passed via the parameter lpBuffersIn of this function:

Signature of the function HttpSendRequestExA:

BOOLAPI HttpSendRequestExA(
HINTERNET           hRequest,
LPINTERNET_BUFFERSA lpBuffersIn,
LPINTERNET_BUFFERSA lpBuffersOut,
DWORD               dwFlags,
DWORD_PTR           dwContext
);

Structure INTERNET_BUFFERSA:

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;

This is the function of the hooking script:

  onEnter: function (log, args, state) {
  log('###### HttpSendRequestExA() ###################');
  log("lpBuffersIn  = " + args[1].readPointer());//I'm stuck here to reach the structure
  log('###############################################');       
 }

I have searched on Internet but I do not found how to read the pointer an associate it to the expected structure.

Thank you very much in advance for any help or hint :)


Solution

  • Since the architecture was not specified, the following answer is for a 32-bit process but the 64-bit changes are explained.

    The struct _INTERNET_BUFFERSA is presented as LPINTERNET_BUFFERSA which is a pointer. args[1] holds the pointer to the _INTERNET_BUFFERSA struct and when read with .readPointer what is returned is the value of dwStructSize which is 40 for a 32-bit process and 52 for a 64-bit process.

    The offsets for each member are:

    typedef struct _INTERNET_BUFFERSA {
    DWORD                     dwStructSize; // Offset: 0
    struct _INTERNET_BUFFERSA *Next; // Offset: 4
    LPCSTR                    lpcszHeader; // Offset -> 32-bit: 8, 64-bit:12
    DWORD                     dwHeadersLength; // Offset -> 32-bit: 12, 64-bit:20
    DWORD                     dwHeadersTotal; // Offset -> 32-bit: 16, 64-bit 24
    LPVOID                    lpvBuffer; // Offset -> 32-bit: 20, 64-bit 28
    DWORD                     dwBufferLength; // Offset -> 32-bit: 24, 64-bit 36
    DWORD                     dwBufferTotal; // Offset -> 32-bit: 28, 64-bit 40
    DWORD                     dwOffsetLow; // Offset -> 32-bit: 32, 64-bit 44
    DWORD                     dwOffsetHigh;// Offset -> 32-bit: 36, 64-bit 48
    } INTERNET_BUFFERSA, *LPINTERNET_BUFFERSA;
    

    With this in mind, the following FRIDA code will retrieve every struct member 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());  
        }
    });
    

    Process.pointerSize is 4 for 32-bit processes and 8 for 64-bit processes and this allows to resolve offsets correctly.