Nytro Posted July 13, 2018 Report Posted July 13, 2018 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. 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. 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. 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 GCC output 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/ Quote