Jump to content
Nytro

Process Injection: Writing the payload

Recommended Posts

Process Injection: Writing the payload

Posted on July 12, 2018 by odzhan

Introduction

The purpose of this post is to discuss a Position Independent Code (PIC) written in C that will be used for demonstrating process injection on the Windows operating system. The payload will simply execute an instance of the calculator, and is not intended to be malicious in any way. In follow up posts, I’ll discuss a number of injection methods to help the reader understand how they work. Below is a screenshot of the PROPagate method in action, that I’ll hopefully discuss in a follow up post.

propagate_hello.png?w=640

Function prototypes

Most of the injection methods require a PIC to run successfully in a remote process space, unless of course one wants to load a Dynamic-link Library. (DLL) In the case of loading a DLL, one only needs to execute the LoadLibrary API providing the path of a DLL as parameter. Traditionally, PICs have been written in C or assembly, but there are problems when using pure assembly code. Consider that Windows can run on multiple architectures (e.g x86,amd64,arm), with multiple calling conventions (e.g stdcall,fastcall). What if corrections or changes to the code are required? For those reasons, it makes more sense to use a High-level Language (HLL) like C.

Some of the methods that will be discussed have individual requirements. Some of the callback functions executed will be passed a number of parameters, and due to calling convention will expect those parameters be removed upon returning to the caller.

Thread procedure

Creating a new thread in a remote process can be performed using one of the following API.

  • CreateRemoteThread
  • RtlCreateUserThread
  • NtCreateThreadEx
  • ZwCreateThreadEx

Each API expects a pointer to a ThreadProc callback function. The following is defined in the Windows SDK. This is also perfect for LoadLibrary that only expects one parameter.

DWORD WINAPI ThreadProc(
  _In_ LPVOID lpParameter);

Asynchronous Procedure Call

It’s also possible to attach a PIC to an existing thread by using one of the following API. The thread must be alertable for this to work, and there’s no convenient way to determine if a thread is alertable or not.

  • QueueUserAPC
  • NtQueueApcThread
  • NtQueueApcThreadEx
  • ZwQueueApcThread
  • ZwQueueApcThreadEx
  • RtlQueueApcWow64Thread
VOID CALLBACK APCProc(
  _In_ ULONG_PTR dwParam);

WindowProc callback function

Some of you will know of the Extra Window Memory (EWM) injection method? The well-known method involves replacing the “CTray” object that is used to control the behavior of the “Shell_TrayWnd” class registered by explorer.exe. Some versions of Windows permit updating this object via SetWindowLongPtr, therefore allowing code execution without the creation of a new thread, but more on that later!

The following API can be used to update the window’s callback function.

  • SetWindowLong (32-bit)
  • GetWindowLong (32-bit)
  • SetWindowLongPtr (32 and 64-bit)
  • GetWindowLongPtr (32 and 64-bit)
LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam);

Subclass callback function

The PROPagate method, is named after the APIs GetProp and SetProp used to change the address of a callback function for a subclassed window. As of July 2018, this is a relatively new technique that’s similar to the EWM injection or shatter attacks described in 2002.

The following API (but not all) may be of interest.

  • GetProp
  • SetProp
  • EnumProps
typedef LRESULT ( CALLBACK *SUBCLASSPROC)(
   HWND      hWnd,
   UINT      uMsg,
   WPARAM    wParam,
   LPARAM    lParam,
   UINT_PTR  uIdSubclass,
   DWORD_PTR dwRefData);

Dynamic-link Library (DLL)

Although not a PIC, process injection can sometimes involve loading a DLL. Compiling the payload into a DLL is therefore an option.

__declspec(dllexport)
BOOL WINAPI DllMain(HINSTANCE hInstance, 
  DWORD fdwReason, LPVOID lpvReserved);

Compiling

To generate the appropriate payload using the correct parameters and return type, we use the define directive. If we need to compile the payload for another function prototype, it would be relatively easy to amend this code.

#ifdef XAPC // Asynchronous Procedure Call
VOID CALLBACK APCProc(ULONG_PTR dwParam)
#endif

#ifdef THREAD // Create remote thread
DWORD WINAPI ThreadProc(LPVOID lpParameter)
#endif

#ifdef SUBCLASS // Window subclass callback procedure
LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, 
  LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
#endif

#ifdef WINDOW // Window callback procedure
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, 
  WPARAM wParam, LPARAM lParam)
#endif

#ifdef DLL    // compile as Dynamic-link Library
#pragma warning( push ) 
#pragma warning( disable : 4100 )
__declspec(dllexport)
BOOL WINAPI DllMain(HINSTANCE hInstance, 
  DWORD fdwReason, LPVOID lpvReserved)
#endif

What if the payload is called multiple times? For each method discussed, I will address that question.

Declaring strings

If you declare a string in C, and compile the source into an executable, you might find string literals stored in a read-only section of memory. This is a problem for a PIC because we need all variables, including strings stored on the stack. To illustrate the problem, consider the following simple piece of code.

#include <stdio.h>

int main(void){
    char msg[]="Hello, World!\n";

    printf("%s", msg);
    return 0;
}

Although this assembly output of the MSVC compiler isn’t very readable, it shows the stack isn’t used for local storage of the string literal.

hello1.png?w=640

The solution might be declaring the string as a byte array, like the following.

#include <stdio.h>

int main(void){
    char msg[]={'H','e','l','l','o',',',' ','W','o','r','l','d','!','\n',0};

    printf("%s", msg);
    return 0;
}

The assembly output of this shows MSVC will use the local stack.

hello2.png?w=640&h=255

This works for MSVC, but unfortunately not for GCC that will still store the string in read-only section of the executable. What other options are there?

Use inline assembly

If I recall correctly, it was Z0MBiE/29a who once suggested in Solving Plain Strings Problem In HLL using macro based assembly with the Borland C compiler to store strings on the stack for the purpose of obfuscation. In Phrack #69, Shellcode the better way, or how to just use your compiler by fishstiqz suggests using an inline macro.

#define INLINE_STR(name, str) \
        const char * name;          \
        asm(                        \
            "call 1f\n"             \
            ".asciz \"" str "\"\n"  \
            "1:\n"                  \
            "pop %0\n"              \
            : "=r" (name)           \
);

The macro can be used with the following.

INLINE_STR(kernel32, "kernel32");
PVOID pKernel32 = scGetModuleBase(kernel32);

We’re depending on assembly code here, and that’s precisely what we should avoid. We also can’t inline 64-bit assembly, as fishstiqz points out. What else?

Declaration using arrays

After examining various options, and having taken the advice of others (@solardiz) declaring strings as 32-bit arrays solves the problem for both GCC and MSVC.

#include <stdio.h>

typedef unsigned int W;

int main(void){
    W msg[4];
    
    msg[0]=*(W*)"Hell";
    msg[1]=*(W*)"o, W";
    msg[2]=*(W*)"orld";
    msg[3]=*(W*)"!\n";

    printf("%s", (char*)msg);
    return 0;
}

As you can see from MSVC and GCC output, both place the string on the stack.

MSVC output

hello3.png?w=640

GCC output

hello4.png?w=640

Of course, it would make sense to automate this process, rather than try initialize manually 😉

Declaring function pointers

You don’t have to declare function pointers in this way, but for a shellcode, it makes the code much easier to read.

typedef HANDLE (WINAPI *OpenEvent_t)(
  _In_ DWORD   dwDesiredAccess,
  _In_ BOOL    bInheritHandle,
  _In_ LPCTSTR lpName);

typedef BOOL (WINAPI *SetEvent_t)(
  _In_ HANDLE hEvent);
  
typedef BOOL (WINAPI *CloseHandle_t)(
  _In_ HANDLE hOject);
  
typedef UINT (WINAPI *WinExec_t)(
  _In_ LPCSTR lpCmdLine, _In_ UINT uCmdShow);

Once the function is defined, you can declare it inside the main code, like so.

SetEvent_t          pSetEvent;
OpenEvent_t         pOpenEvent;
CloseHandle_t       pCloseHandle;
WinExec_t           pWinExec;

Resolving API addresses

Rather than cover the same ground, I recommend reading two posts about this task. Resolving API addresses in memory, and Fido, how it resolves GetProcAddress and LoadLibraryA. These both cover some good ways to resolve API dynamically using the Import Address Table (IAT) and Export Address Tables (EAT) of a Portable Executable (PE) file.

Traversing the Process Environment Block (PEB) modules is commonly found in shellcodes, and it’s required for a PIC to resolve address of API functions.

// search all modules in the PEB for API
LPVOID xGetProcAddress(LPVOID pszAPI) {
    PPEB                  peb;
    PPEB_LDR_DATA         ldr;
    PLDR_DATA_TABLE_ENTRY dte;
    LPVOID                api_adr=NULL;
    
  #if defined(_WIN64)
    peb = (PPEB) __readgsqword(0x60);
  #else
    peb = (PPEB) __readfsdword(0x30);
  #endif

    ldr = (PPEB_LDR_DATA)peb->Ldr;
    
    // for each DLL loaded
    dte=(PLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderModuleList.Flink;
    for (;dte->DllBase != NULL && api_adr == NULL; 
         dte=(PLDR_DATA_TABLE_ENTRY)dte->InLoadOrderLinks.Flink)
    {
      // search the export table for api
      api_adr=FindExport(dte->DllBase, (PCHAR)pszAPI);  
    }
    return api_adr;
}

Searching the Export Address Table (EAT)

One can certainly search the Import Address Table (IAT) for the address of API, but the following uses the Export Addres Table. This function doesn’t handle forward references. Note that we’re also searching by string, instead of hash.

// locate address of API in export address table
LPVOID FindExport(LPVOID base, PCHAR pszAPI){
    PIMAGE_DOS_HEADER       dos;
    PIMAGE_NT_HEADERS       nt;
    DWORD                   cnt, rva, dll_h;
    PIMAGE_DATA_DIRECTORY   dir;
    PIMAGE_EXPORT_DIRECTORY exp;
    PDWORD                  adr;
    PDWORD                  sym;
    PWORD                   ord;
    PCHAR                   api, dll;
    LPVOID                  api_adr=NULL;
    
    dos = (PIMAGE_DOS_HEADER)base;
    nt  = RVA2VA(PIMAGE_NT_HEADERS, base, dos->e_lfanew);
    dir = (PIMAGE_DATA_DIRECTORY)nt->OptionalHeader.DataDirectory;
    rva = dir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    
    // if no export table, return NULL
    if (rva==0) return NULL;
    
    exp = (PIMAGE_EXPORT_DIRECTORY) RVA2VA(ULONG_PTR, base, rva);
    cnt = exp->NumberOfNames;
    
    // if no api names, return NULL
    if (cnt==0) return NULL;
    
    adr = RVA2VA(PDWORD,base, exp->AddressOfFunctions);
    sym = RVA2VA(PDWORD,base, exp->AddressOfNames);
    ord = RVA2VA(PWORD, base, exp->AddressOfNameOrdinals);
    dll = RVA2VA(PCHAR, base, exp->Name);
    
    do {
      // calculate hash of api string
      api = RVA2VA(PCHAR, base, sym[cnt-1]);
      // add to DLL hash and compare
      if (!xstrcmp(pszAPI, api)){
        // return address of function
        api_adr = RVA2VA(LPVOID, base, adr[ord[cnt-1]]);
        return api_adr;
      }
    } while (--cnt && api_adr==0);
    return api_adr;
}

Summary

If we use C instead of assembly, writing a payload isn’t that difficult. The next posts on this subject will cover some injection methods individually. I’ll post source code shortly.

 

Sursa: https://modexp.wordpress.com/2018/07/12/process-injection-writing-payload/

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...