From 580e9eb07a32c15efb3ca7faf9c4d3768e1f48da Mon Sep 17 00:00:00 2001 From: meowmycks Date: Sat, 12 Apr 2025 23:46:41 -0400 Subject: [PATCH] Upload files to "/" --- spoof.asm | 136 +++++++++++++++++++++++++++++++++++++++++ syscalls.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 spoof.asm create mode 100644 syscalls.cpp diff --git a/spoof.asm b/spoof.asm new file mode 100644 index 0000000..06cf709 --- /dev/null +++ b/spoof.asm @@ -0,0 +1,136 @@ +.code + +; A function can be called like so +; +; Spoof(arg1, arg2, arg3, arg4, ¶m, function, (PVOID)0); +; +; Param is a struct containing some necessary information for the call to have fake frames added. +; The 6th argument is a pointer to the function to execute +; The 7th argument specifies the number of args to pass to the stack. It has to be at an 8 byte size. + +Spoof PROC + pop rax ; Real return address in rax + + mov r10, rdi ; Store OG rdi in r10 + mov r11, rsi ; Store OG rsi in r11 + + mov rdi, qword ptr [rsp + 32] ; Storing struct in rdi + mov rsi, qword ptr [rsp + 40] ; Storing function to call + + ; --------------------------------------------------------------------- + ; Storing our original registers + ; --------------------------------------------------------------------- + + mov qword ptr [rdi + 24], r10 ; Storing OG rdi into param + mov qword ptr [rdi + 88], r11 ; Storing OG rsi into param + mov qword ptr [rdi + 96], r12 ; Storing OG r12 into param + mov qword ptr [rdi + 104], r13 ; Storing OG r13 into param + mov qword ptr [rdi + 112], r14 ; Storing OG r14 into param + mov qword ptr [rdi + 120], r15 ; Storing OG r15 into param + + mov r12, rax ; OG code used r12 for ret addr + + ; --------------------------------------------------------------------- + ; Prepping to move stack args + ; --------------------------------------------------------------------- + + xor r11, r11 ; r11 = # of args pushed + mov r13, qword ptr [rsp + 30h] ; r13 = total args to push + + mov r14, 200h ; Initial offset + add r14, 8 + add r14, qword ptr [rdi + 56] ; Add RUTS stack size + add r14, qword ptr [rdi + 48] ; Add BTIT stack size + add r14, qword ptr [rdi + 32] ; Add gadget frame size + sub r14, 20h ; Adjust for first stack arg + + mov r10, rsp + add r10, 30h ; Stack args base address + +looping_label: + xor r15, r15 + cmp r11, r13 + je finish_label + + ; --------------------------------------------------------------------- + ; Calculate target stack position + ; --------------------------------------------------------------------- + sub r14, 8 + mov r15, rsp + sub r15, r14 + + ; --------------------------------------------------------------------- + ; Move stack argument + ; --------------------------------------------------------------------- + add r10, 8 + push qword ptr [r10] + pop qword ptr [r15] + + ; --------------------------------------------------------------------- + ; Increment counter and loop + ; --------------------------------------------------------------------- + add r11, 1 + jmp looping_label + +finish_label: + + ; ---------------------------------------------------------------------- + ; Create working space and setup fake frames + ; ---------------------------------------------------------------------- + sub rsp, 200h + push 0 + + ; RtlUserThreadStart frame + sub rsp, qword ptr [rdi + 56] + mov r11, qword ptr [rdi + 64] + mov qword ptr [rsp], r11 + + ; BaseThreadInitThunk frame + sub rsp, qword ptr [rdi + 32] + mov r11, qword ptr [rdi + 40] + mov qword ptr [rsp], r11 + + ; Gadget frame -- `jmp QWORD PTR [rbx]` + sub rsp, qword ptr [rdi + 48] + mov r11, qword ptr [rdi + 80] + mov qword ptr [rsp], r11 + + ; ---------------------------------------------------------------------- + ; Prepare for function call and fixup + ; ---------------------------------------------------------------------- + mov r11, rsi ; Function to call + mov qword ptr [rdi + 8], r12 ; Store real return address + mov qword ptr [rdi + 16], rbx ; Store original RBX + lea rbx, fixup_label ; Get fixup address + mov qword ptr [rdi], rbx ; Store fixup in struct + mov rbx, rdi ; Param struct pointer + + ; Prepare syscall (if needed) + mov r10, rcx + mov rax, qword ptr [rdi + 72] + + jmp r11 ; Jump to target function + +fixup_label: + mov rcx, rbx ; Restore param struct + + ; Cleanup stack frames + add rsp, 200h + add rsp, qword ptr [rbx + 48] + add rsp, qword ptr [rbx + 32] + add rsp, qword ptr [rbx + 56] + + ; Restore original registers + mov rbx, qword ptr [rcx + 16] + mov rdi, qword ptr [rcx + 24] + mov rsi, qword ptr [rcx + 88] + mov r12, qword ptr [rcx + 96] + mov r13, qword ptr [rcx + 104] + mov r14, qword ptr [rcx + 112] + mov r15, qword ptr [rcx + 120] + + jmp qword ptr [rcx + 8] ; Jump to original return address + +Spoof ENDP + +END diff --git a/syscalls.cpp b/syscalls.cpp new file mode 100644 index 0000000..e4e466e --- /dev/null +++ b/syscalls.cpp @@ -0,0 +1,170 @@ +#include + +// Super reliable way to find the base address of a given module +PBYTE FindModuleBase(const CHAR* moduleName) { + // Retrieve the loader data from the Process Environment Block (PEB) + PPEB_LDR_DATA loaderData = NtCurrentTeb()->ProcessEnvironmentBlock->Ldr; + PLIST_ENTRY moduleListHead = (PLIST_ENTRY)&loaderData->Reserved2[1]; //Reserved2[1] == InLoadOrderModuleList + PLIST_ENTRY currentEntry = moduleListHead->Blink; + + // Iterate through the loaded modules backwards + while (currentEntry != moduleListHead) { + // Get the module entry + PLDR_DATA_TABLE_ENTRY_MODIFIED moduleEntry = CONTAINING_RECORD(currentEntry, LDR_DATA_TABLE_ENTRY_MODIFIED, InLoadOrderLinks); + currentEntry = currentEntry->Blink; + + // Get the base address of the module + PBYTE moduleBase = (PBYTE)moduleEntry->OriginalBase; + + // Access the NT headers of the module + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBase; + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(moduleBase + dosHeader->e_lfanew); + + // Check if the module has an export directory + DWORD exportDirectoryRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (!exportDirectoryRVA) continue; // No export table? skip + + // Access the export directory + PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(moduleBase + exportDirectoryRVA); + if (!exportDirectory->NumberOfNames) continue; // No symbols? skip + + // Extract the DLL name from the module entry + char dllName[MAX_PATH]; // Buffer to store the DLL name + snprintf(dllName, sizeof(dllName), "%wZ", moduleEntry->BaseDllName); // Extract BaseDllName + + // Compare the decoded name with the current module name + if (strcmp(dllName, moduleName) == 0) return moduleBase; // Found the module, return its base address + } + + // module not found + return nullptr; +} + +// Resolve System Service Number (SSN), Address, and Offset for a System Call Name +SyscallEntry SSNLookup(PCHAR syscall) { + SyscallEntry entry = { 0 }; + + // Load the Export Address Table + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(hNtdll + ((PIMAGE_DOS_HEADER)hNtdll)->e_lfanew); + DWORD exportDirRVA = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (!exportDirRVA) return { 0 }; // No export table + + PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(hNtdll + exportDirRVA); + + PDWORD pFunctions = (PDWORD)(hNtdll + pExportDir->AddressOfFunctions); + PDWORD pNames = (PDWORD)(hNtdll + pExportDir->AddressOfNames); + PWORD pNameOrdinals = (PWORD)(hNtdll + pExportDir->AddressOfNameOrdinals); + + // Load the Exception Directory + DWORD exceptTableRVA = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; + if (!exceptTableRVA) return { 0 }; // No exception directory + + PIMAGE_RUNTIME_FUNCTION_ENTRY pRuntimeFuncTable = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(hNtdll + exceptTableRVA); + + INT64 ssn = 0; + PBYTE address = 0; + + // Search export address table + for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { + PCHAR pFunctionName = (PCHAR)(hNtdll + pNames[i]); + + // Search runtime function table + for (INT64 i = 0; pRuntimeFuncTable[i].BeginAddress; i++) { + for (INT64 j = 0; j < pExportDir->NumberOfFunctions; j++) { + if (pFunctions[pNameOrdinals[j]] == pRuntimeFuncTable[i].BeginAddress) { + PCHAR api = (PCHAR)(hNtdll + pNames[j]); + PCHAR s1 = api; + PCHAR s2 = syscall; + + // Compare the syscall names + while (*s1 && (*s1 == *s2)) s1++, s2++; + INT64 cmp = (INT64)*(PBYTE)s1 - *(PBYTE)s2; + if (!cmp) { + address = (hNtdll + pRuntimeFuncTable[i].BeginAddress); + + // Locate `syscall; ret` sequence + for (INT64 offset = 0; offset < 0x100; offset++) {// Scan up to 256 bytes + if (address[offset] == 0x0F && address[offset + 1] == 0x05 && address[offset + 2] == 0xC3) { + + // Populate the SyscallEntry struct + entry.SSN = ssn; + entry.Address = address; + entry.Syscall = (PVOID)(address + offset); + return entry; + } + } + } + // If this is a syscall, increase the SSN value + if (*(USHORT*)api == 'wZ') ssn++; + } + } + } + } + return { 0 }; // Didn't find it +} + +// Collect all instances of a given ROP gadget in a given module +std::vector CollectGadgets(const PBYTE gadget, SIZE_T gadgetSize, PBYTE hModule) { + std::vector gadgets; + if (!hModule || !gadget || gadgetSize == 0) return gadgets; // Validate input + + PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(hModule); + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return gadgets; // Validate DOS header + + PIMAGE_NT_HEADERS pNtHeaders = reinterpret_cast(reinterpret_cast(hModule) + pDosHeader->e_lfanew); + if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) return gadgets; // Validate NT headers + + PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); + UINT_PTR moduleBase = reinterpret_cast(hModule); + + // Loop through each section in the module + for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++, pSectionHeader++) { + // Check if the section is executable code + if ((pSectionHeader->Characteristics & IMAGE_SCN_CNT_CODE) && + (pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { + + PBYTE sectionBase = reinterpret_cast(moduleBase + pSectionHeader->VirtualAddress); + PBYTE sectionEnd = sectionBase + pSectionHeader->Misc.VirtualSize; + + // Search within the section for the gadget pattern + for (PBYTE currentBytes = sectionBase; currentBytes <= sectionEnd - gadgetSize; ++currentBytes) { + if (!memcmp(currentBytes, gadget, gadgetSize)) { + gadgets.emplace_back(EncodePointer(reinterpret_cast(currentBytes))); // Construct each encoded address inside the vector + } + } + } + } + //printf("Found %u gadgets\n", gadgets.size()); + return gadgets; +} + +// Choose a random gadget +PVOID GoGoGadget(std::vector gadgets) { + if (gadgets.empty()) return nullptr; // Return nullptr if the vector is empty + + // Randomly select and decode a gadget address + static std::mt19937 rng(static_cast(std::time(nullptr))); + std::uniform_int_distribution dist(0, gadgets.size() - 1); + return DecodePointer((gadgets)[dist(rng)]); +} + +// Checks the bytes immediately before each gadget +VOID CheckGadgetPreBytes(const std::vector& gadgets, SIZE_T gadgetSize, SIZE_T lookbackSize) { + for (const auto& encodedGadget : gadgets) { + PBYTE gadgetAddress = reinterpret_cast(DecodePointer(encodedGadget)); // Decode the pointer + + // Ensure we can read preceding bytes safely + PBYTE precedingBytes = gadgetAddress - lookbackSize; + if (precedingBytes >= gadgetAddress) { + printf("Skipping address %p (out of range)\n", gadgetAddress); + continue; // Prevent underflow if the address is too low in memory + } + + // Print address and bytes + printf("Address: %p -> ", gadgetAddress); + for (SIZE_T i = 0; i < lookbackSize + gadgetSize + 8; i++) { // Include gadget bytes and 4 bytes ahead of gadget too + printf("%02X ", precedingBytes[i]); + } + printf("\n"); + } +} \ No newline at end of file