-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
64bit run_pe load 64bit payload not working on windows 11 24H2 #59
Comments
Starting from Windows 11 24H2, Microsoft has implemented a new Control Flow Guard or CFG system, which should limit the places where the application can execute code. You can read more info on MSDN. Hackers from the UnknownCheats forum analyzed the PE loader in Ntdll and found a function that initializes this mechanism for the PE image — RtlpInsertOrRemoveScpCfgFunctionTable. It seems like there is even a working code that patches RtlpInsertOrRemoveScpCfgFunctionTable function according to a hard-coded offset. const auto NtdllBase = reinterpret_cast<PBYTE>(GetModuleHandleW(L"ntdll.dll"));
const BYTE Patch[4] =
{
0x48, 0x31, 0xC0, // xor rax, rax
0xC3 // ret
};
// Patching RtlpInsertOrRemoveScpCfgFunctionTable function of Ntdll using hard-coded offset
WriteProcessMemory(pi.hProcess, NtdllBase + 0x7BE0, Patch, sizeof(Patch), nullptr); Original post — https://www.unknowncheats.me/forum/4239032-post15.html Perhaps zeroing IMAGE_LOAD_CONFIG_DIRECTORY of the payload image before copying it to another process can also help. |
So, I did get an Ntdll sample from Windows 11 24H2. The NTSTATUS __stdcall RtlpInsertOrRemoveScpCfgFunctionTable(PVOID ImageBase, __int64 Reserved, bool insertOrRemove)
{
NTSTATUS result; // eax
char *pageBase; // rax
__int64 offsetToRtlTable; // rcx
ULONG_PTR ImageExtensionLength; // [rsp+30h] [rbp-38h] BYREF
PVOID DynamicTable; // [rsp+38h] [rbp-30h] BYREF
MEMORY_IMAGE_EXTENSION_INFORMATION info; // [rsp+40h] [rbp-28h] BYREF
ImageExtensionLength = 0i64;
memset(&info, 0, sizeof(info));
result = ZwQueryVirtualMemory(
(HANDLE)0xFFFFFFFFFFFFFFFFi64,
ImageBase,
MemoryImageExtensionInformation,
&info,
0x18ui64,
&ImageExtensionLength);
if ( result == -1073741637 )
return 279;
if ( result >= 0 )
{
if ( !info.PageSize )
return 279;
pageBase = (char *)ImageBase + *(_QWORD *)&info.PageOffset;
offsetToRtlTable = *(unsigned int *)((char *)ImageBase + *(_QWORD *)&info.PageOffset + 20);
if ( !(_DWORD)offsetToRtlTable )
return 279;
if ( insertOrRemove )
{
result = RtlAddGrowableFunctionTable(
&DynamicTable,
(PRUNTIME_FUNCTION)&pageBase[offsetToRtlTable],
1u,
1u,
(ULONG_PTR)ImageBase + *(_QWORD *)&info.PageOffset,
(ULONG_PTR)&pageBase[info.PageSize]);
if ( result >= 0 )
return 0;
}
else
{
RtlDeleteFunctionTable((PRUNTIME_FUNCTION)&pageBase[offsetToRtlTable]);
return 0;
}
}
return result;
}
#ifdef _WIN64 // Dynamic function tables is only for 64-bits images
auto hNtdll = GetModuleHandleW(L"ntdll.dll"); // Found Ntdll base address
if (hNtdll) {
// Stub for functions
const BYTE patch[4] =
{
0x48, 0x31, 0xC0, // xor rax, rax
0xC3 // ret
};
FARPROC NtManageHotPatch = GetProcAddress(hNtdll, "NtManageHotPatch");
FARPROC RtlAddGrowableFunctionTable = GetProcAddress(hNtdll, "RtlAddGrowableFunctionTable");
FARPROC RtlDeleteFunctionTable = GetProcAddress(hNtdll, "RtlDeleteFunctionTable");
if (NtManageHotPatch && RtlAddGrowableFunctionTable && RtlDeleteFunctionTable) {
// Wanna some patches?
WriteProcessMemory(pi.hProcess, NtManageHotPatch, patch, sizeof(patch), nullptr);
WriteProcessMemory(pi.hProcess, RtlAddGrowableFunctionTable, patch, sizeof(patch), nullptr);
WriteProcessMemory(pi.hProcess, RtlDeleteFunctionTable, patch, sizeof(patch), nullptr);
// Apply changes to cache
FlushInstructionCache(pi.hProcess, NtManageHotPatch, sizeof(patch));
FlushInstructionCache(pi.hProcess, RtlAddGrowableFunctionTable, sizeof(patch));
FlushInstructionCache(pi.hProcess, RtlDeleteFunctionTable, sizeof(patch));
}
}
#endif I could do a pull request with this, but I don't have Windows 11 24H2 to test this code. |
Thank you for finding all those details @NotCapengeR ! It is definitely very useful! I will try to get Windows 11 24H2 and test it whenever I get some free time. |
@NotCapengeR I have, too, realized, that Windows 24H2 broke RunPE, so I stumbled upon your suggestion. I tried it (with |
As I said, I don't have Windows 11 24H2, so I cannot test my code. If it doesn't working, you need to debug and find a function that is causing the error (btw, what is this error?). But it seems to me that a patch of these functions is enough for everything to work, perhaps a few more functions need a patch too |
Any updates for this issue, did anybody tried patch ntdll method? I have tried some other PE Loaders, some of them has same error. |
Load 32bit payload ok.
64bit and 32bit are ok on before windows 11 24H2
The text was updated successfully, but these errors were encountered: