#include #include // extern "C" // UINT_PTR // NTAPI // MwGenericCall( // ULONG SyscallNumber, // ULONG ArgumentCount, // va_list ArgumentList // ); #pragma section(".text") __declspec(allocate(".text")) UCHAR MwSyscallCode[] = { // // ; // ; Save r12 to the shadow space. // ; This register will be held as a temporary // ; stack pointer for the syscall. // ; 0x4C, 0x89, 0x64, 0x24, 0x08, // mov qword ptr [rsp + 8], r12 // // ; // ; eax = SyscallNumber // ; r10 = ArgumentCount // ; r11 = ArgumentList // ; r12 = ArgumentList - sizeof(PVOID) // ; (space for return address) // ; // 0x8B, 0xC1, // mov eax, ecx 0x44, 0x8B, 0xD2, // mov r10d, edx 0x4D, 0x8B, 0xD8, // mov r11, r8 0x4D, 0x8D, 0x63, 0xF8, // lea r12, qword ptr [r11 - 8] // // ; // ; Assign first 4 arguments (rcx, rdx, r8, r9) // ; from the ArgumentList. // ; 0x41, 0x83, 0xFA, 0x01, // cmp r10d, 1 0x49, 0x0F, 0x43, 0x0B, // cmovae rcx, qword ptr [r11 + 0] // 0x41, 0x83, 0xFA, 0x02, // cmp r10d, 2 0x49, 0x0F, 0x43, 0x53, 0x08, // cmovae rdx, qword ptr [r11 + 8] // 0x41, 0x83, 0xFA, 0x03, // cmp r10d, 3 0x4D, 0x0F, 0x43, 0x43, 0x10, // cmovae r8, qword ptr [r11 + 16] // 0x41, 0x83, 0xFA, 0x04, // cmp r10d, 4 0x4D, 0x0F, 0x43, 0x4B, 0x18, // cmovae r9, qword ptr [r11 + 24] // // ; // ; The syscall handler expects the first // ; parameter in the r10 register. // ; 0x4C, 0x8B, 0xD1, // mov r10, rcx // // ; // ; Stack pivot, syscall, restore stack. // ; 0x4C, 0x87, 0xE4, // xchg r12, rsp 0x0F, 0x05, // syscall 0x4C, 0x87, 0xE4, // xchg r12, rsp // // ; // ; Restore r12 register from the shadow space. // ; 0x4C, 0x8B, 0x64, 0x24, 0x08, // mov r12, qword ptr [rsp + 8] // 0xC3, // retn }; // typedef UINT_PTR (NTAPI* GENERIC_CALL_FN)( _In_ ULONG SyscallNumber, _In_ ULONG ArgumentCount, _In_ va_list ArgumentList ); GENERIC_CALL_FN MwGenericCall = (GENERIC_CALL_FN)&MwSyscallCode; static const unsigned char ASM_MOV_EAX_IMM = 0xb8; // followed by DWORD static const unsigned char ASM_RET = 0xc3; static const unsigned char ASM_MOV_R10_RCX[] = { 0x4c, 0x8b, 0xD1 }; static const unsigned char ASM_SYSCALL[] = { 0x0f, 0x05 }; static const unsigned char ASM_SYSCALL_STUB[] = { 0x4c, 0x8b, 0xD1, // mov r10, rcx 0xb8 // mov eax, imm32 - follows DWORD }; NTSTATUS NTAPI MwDisasmGetSyscallNumber( VOID* FunctionAddress, ULONG* SyscallNumber ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; if ((*(PULONG)FunctionAddress) == (*(PULONG)ASM_SYSCALL_STUB)) { *SyscallNumber = *(ULONG*)((BYTE*)FunctionAddress + sizeof(ASM_SYSCALL_STUB)); Status = STATUS_SUCCESS; } return Status; } NTSTATUS NTAPI MwGetFunctionAddress( VOID* ImageBase, const CHAR* FunctionName, VOID** FunctionAddress, ULONG* SyscallNumber ) { TEB* Teb = NtCurrentTeb(); LIST_ENTRY* ModuleListHead = &Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList; LIST_ENTRY* ModuleListNextEntry; BYTE* NtdllBase; for ( ModuleListNextEntry = ModuleListHead; ModuleListNextEntry->Flink != ModuleListHead; ModuleListNextEntry = ModuleListNextEntry->Flink ) { LDR_DATA_TABLE_ENTRY* LdrEntry = (LDR_DATA_TABLE_ENTRY*)ModuleListNextEntry->Flink; if (!wcscmp(LdrEntry->BaseDllName.Buffer, L"ntdll.dll")) { NtdllBase = (BYTE*)LdrEntry->DllBase; break; } } IMAGE_DOS_HEADER* DosHeader = (IMAGE_DOS_HEADER*)NtdllBase; IMAGE_NT_HEADERS* NtHeader = (IMAGE_NT_HEADERS*)(NtdllBase + DosHeader->e_lfanew); IMAGE_DATA_DIRECTORY* ExportDataDirectory = (IMAGE_DATA_DIRECTORY*)&NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; IMAGE_EXPORT_DIRECTORY* ExportDirectory = (IMAGE_EXPORT_DIRECTORY*)(NtdllBase + ExportDataDirectory->VirtualAddress); DWORD* FunctionPtrRvaArray = (DWORD*)(NtdllBase + ExportDirectory->AddressOfFunctions); DWORD* FunctionNameRvaArray = (DWORD*)(NtdllBase + ExportDirectory->AddressOfNames); USHORT* FunctionOrdinalRvaArray = (USHORT*)(NtdllBase + ExportDirectory->AddressOfNameOrdinals); int FunctionNameRvaArraySize = ExportDirectory->NumberOfNames; CHAR* ExportFunctionName; VOID* ExportFunctionPtr; for (int i = 0; i < FunctionNameRvaArraySize; i++) { ExportFunctionName = (CHAR*)(NtdllBase + FunctionNameRvaArray[i]); ExportFunctionPtr = (VOID*)(NtdllBase + FunctionPtrRvaArray[FunctionOrdinalRvaArray[i]]); if (!strcmp(ExportFunctionName, FunctionName)) { MwDisasmGetSyscallNumber(ExportFunctionPtr, SyscallNumber); *FunctionAddress = ExportFunctionPtr; return STATUS_SUCCESS; } } return STATUS_UNSUCCESSFUL; } NTSTATUS NTAPI MwCallNtdll( const CHAR* FunctionName, ULONG ArgumentCount, ... ) { VOID* FunctionAddress; ULONG SyscallNumber; MwGetFunctionAddress(NULL, FunctionName, &FunctionAddress, &SyscallNumber); va_list ArgumentList; va_start(ArgumentList, ArgumentCount); UINT_PTR Result = MwGenericCall(SyscallNumber, ArgumentCount, ArgumentList); va_end(ArgumentList); return (NTSTATUS)Result; } // // Variadic macro rape. // #define MW_MACRO_EVAL(...) __VA_ARGS__ #define MW_MACRO_EXPAND(expr) expr #define MW_VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define MW_VA_NUM_ARGS(...) MW_MACRO_EXPAND(MW_VA_NUM_ARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)) #define MW_CALL_WITH_NUM_ARGS(FunctionName, ArgumentCount, ...) MwCallNtdll(FunctionName, ArgumentCount, __VA_ARGS__) #define MW_CALL_VA_STR(FunctionName, ...) MW_CALL_WITH_NUM_ARGS(FunctionName, MW_VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__) #define MW_CALL_VA(FunctionName, ...) MW_CALL_WITH_NUM_ARGS(#FunctionName, MW_VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__) #define MW_CALL_STR(FunctionName, Args) MW_CALL_VA_STR(FunctionName, MW_MACRO_EVAL Args) #define MW_CALL(FunctionName, Args) MW_CALL_VA_STR(#FunctionName, MW_MACRO_EVAL Args) #define NtCall MW_CALL void NtMain() { NTSTATUS Status; HANDLE SectionHandle; LARGE_INTEGER MaximumSize; MaximumSize.QuadPart = 4096; Status = NtCall(NtCreateSection, (&SectionHandle, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)); PVOID BaseAddress = NULL; SIZE_T ViewSize = 4096; Status = NtCall(NtMapViewOfSection, (SectionHandle, NtCurrentProcess(), &BaseAddress, 0, 4096, NULL, &ViewSize, ViewUnmap, 0, PAGE_READWRITE)); RtlCopyMemory(BaseAddress, "HELLO", 6); NtTestAlert(); }