Skip to content

Commit b91b240

Browse files
committed
fix compatibility with latest MSVCRT
it now calls VirtualProtect on it's own module, and that will fail when mapped in kernel memory. We now stub the VirtualProtect import to return success when called with a kernel address
1 parent dc24b88 commit b91b240

File tree

3 files changed

+64
-11
lines changed

3 files changed

+64
-11
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ All-in-one kernel-based DLL injector
3737
- (Currently) it does not clean any traces of the vulnerable driver
3838
- Reboot before loading any "decent" anti-cheat if you don't feel like being insta banned
3939
- The target process needs to have a thread that we can schedule APCs on (this is usually not an issue outside of very simple hello world programs that only have one thread)
40-
- You might get random DEP violations because memory above 0x7FFF'FFFFFFFF is technically not valid user-mode memory (at least as far as Windows APIs are concerned, your CPU doesn't care and will happily execute it, that's the whole idea behind this loader)
41-
- You will have to register an exception handler in your DLL that will catch the exception and return `EXCEPTION_CONTINUE_EXECUTION` whenever it encounters a DEP violation above 0x7FFF'FFFFFFFF
40+
- You might get random DEP violations because memory above 0x7FFF'FFFEFFFF is technically not valid user-mode memory (at least as far as Windows APIs are concerned, your CPU doesn't care and will happily execute it, that's the whole idea behind this loader)
41+
- You will have to register an exception handler in your DLL that will catch the exception and return `EXCEPTION_CONTINUE_EXECUTION` whenever it encounters a DEP violation above 0x7FFF'FFFEFFFF
4242

4343
## Usage
4444

src/shellcode_extractor/shellcode_extractor.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
#include <vector>
66
#include <iomanip>
77

8-
// purpose: extract shellcode from object file given symbol name and emit C++ code
9-
// usage: shellcode_extractor.exe <object_file> <symbol_name> [output_file]
10-
// reason for existence: Why manually copy it every time when you shellcode_extractorcan
8+
// purpose: extract shellcode from object file given symbol name and save to disk
9+
// reason for existence: Why manually copy it every time when you can
1110
// overengineer an automatic extractor/encoder?
1211
// references: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
1312

src/stage2/fumo_loader.cpp

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,41 @@ using fnDllMain = BOOL(WINAPI*)(HMODULE hModule, DWORD dwReason, LPVOID lpReserv
99

1010
typedef struct _MANUAL_MAPPING_DATA {
1111
PVOID ImageBase;
12+
PVOID VirtualProtectStub;
1213
fnLdrLoadDll LdrLoadDll;
1314
fnLdrGetProcedureAddress LdrGetProcedureAddress;
1415
fnRtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString;
1516
} MANUAL_MAPPING_DATA, *PMANUAL_MAPPING_DATA;
1617

17-
MANUAL_MAPPING_DATA GetManualMappingData(PVOID pImageBase) {
18+
MANUAL_MAPPING_DATA GetManualMappingData(PVOID pImageBase, PVOID pVirtualProtectStub) {
1819
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
1920

2021
MANUAL_MAPPING_DATA data = {0};
2122
data.ImageBase = pImageBase;
23+
data.VirtualProtectStub = pVirtualProtectStub;
2224
data.LdrLoadDll = (fnLdrLoadDll)GetProcAddress(hNtdll, "LdrLoadDll");
2325
data.LdrGetProcedureAddress = (fnLdrGetProcedureAddress)GetProcAddress(hNtdll, "LdrGetProcedureAddress");
2426
data.RtlAnsiStringToUnicodeString = (fnRtlAnsiStringToUnicodeString)GetProcAddress(hNtdll, "RtlAnsiStringToUnicodeString");
2527
return data;
2628
}
2729

30+
std::array<uint8_t, 34> virtual_protect_stub = {
31+
// check if lpAddress is above 0x7FFFFFFEFFFF
32+
0x48, 0x89, 0xC8, // mov rax, rcx
33+
0x48, 0xC1, 0xE8, 0x10, // shr rax, 0x10
34+
0x48, 0x3D, 0xFE, 0xFF, 0xFF, 0x7F, // cmp rax, 0x7ffffffe
35+
0x76, 0x06, // jbe .call_original
36+
// return STATUS_SUCCESS
37+
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 0x1
38+
0xC3, // ret
39+
// .call_original
40+
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax, 0x0 - original VirtualProtect
41+
0x48, 0xFF, 0xE0 // rex.W jmp rax
42+
};
43+
constexpr ULONG_PTR virtual_protect_stub_orig_addr_off = 23;
44+
45+
#define tolower(c) ((c >= 'A' && c <= 'Z') ? c + 32 : c)
46+
2847
DWORD Shellcode(PMANUAL_MAPPING_DATA pMmData) {
2948
// resolve imports
3049
auto nt_headers = (PIMAGE_NT_HEADERS)((ULONG_PTR)pMmData->ImageBase + ((PIMAGE_DOS_HEADER)pMmData->ImageBase)->e_lfanew);
@@ -37,6 +56,18 @@ DWORD Shellcode(PMANUAL_MAPPING_DATA pMmData) {
3756
while (module_name[module_name_length] != 0)
3857
module_name_length++;
3958

59+
BOOL is_kernel32 = FALSE;
60+
CHAR kernel32[] = {'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l'};
61+
if (module_name_length == sizeof(kernel32)) {
62+
is_kernel32 = TRUE;
63+
for (int i = 0; i < sizeof(kernel32); i++) {
64+
if (tolower(module_name[i]) != kernel32[i]) {
65+
is_kernel32 = FALSE;
66+
break;
67+
}
68+
}
69+
}
70+
4071
ANSI_STRING ansi_module_name = {0};
4172
ansi_module_name.Buffer = module_name;
4273
ansi_module_name.Length = module_name_length;
@@ -64,13 +95,31 @@ DWORD Shellcode(PMANUAL_MAPPING_DATA pMmData) {
6495
USHORT function_name_length = 0;
6596
while (function_name[function_name_length] != 0)
6697
function_name_length++;
67-
98+
99+
CHAR virtual_protect[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't'};
100+
BOOL is_virtual_protect = FALSE;
101+
if (is_kernel32 && function_name_length == sizeof(virtual_protect)) {
102+
is_virtual_protect = TRUE;
103+
for (int i = 0; i < sizeof(virtual_protect); i++) {
104+
if (function_name[i] != virtual_protect[i]) {
105+
is_virtual_protect = FALSE;
106+
break;
107+
}
108+
}
109+
}
110+
68111
ANSI_STRING ansi_function_name = {0};
69112
ansi_function_name.Buffer = function_name;
70113
ansi_function_name.Length = function_name_length;
71114
ansi_function_name.MaximumLength = function_name_length + 1;
72115

73116
pMmData->LdrGetProcedureAddress(module_handle, &ansi_function_name, 0, &function_address);
117+
118+
if (is_virtual_protect) {
119+
auto virtual_protect_stub = (PBYTE)pMmData->VirtualProtectStub;
120+
*(PVOID*)(virtual_protect_stub + virtual_protect_stub_orig_addr_off) = function_address;
121+
function_address = virtual_protect_stub;
122+
}
74123
}
75124

76125
first_thunk->u1.Function = (ULONG_PTR)function_address;
@@ -107,9 +156,10 @@ int MapImage(fumo::DriverInterface* pDriver, ULONG pid, PVOID pImage) {
107156
return fumo::error(ERR_STAGE2_INVALID_PE_HEADER, L"Invalid PE header");
108157

109158
ULONG size_of_shellcode = (ULONG)((SIZE_T)Shellcode_End - (SIZE_T)Shellcode);
159+
ULONG size_of_virtual_protect_stub = virtual_protect_stub.size();
110160
ULONG size_of_shellcode_data = sizeof(MANUAL_MAPPING_DATA);
111161
auto size_of_image = nt_headers->OptionalHeader.SizeOfImage;
112-
auto size_of_mapping = size_of_image + size_of_shellcode + size_of_shellcode_data;
162+
auto size_of_mapping = size_of_image + size_of_shellcode + size_of_virtual_protect_stub + size_of_shellcode_data;
113163

114164
auto kernel_image = pDriver->AllocateKernelMemory(size_of_mapping);
115165
if (!kernel_image)
@@ -155,10 +205,14 @@ int MapImage(fumo::DriverInterface* pDriver, ULONG pid, PVOID pImage) {
155205
auto shellcode_addr = (PVOID)((ULONG_PTR)kernel_image + size_of_image);
156206
memcpy(shellcode_addr, Shellcode, size_of_shellcode);
157207

208+
// write the virtual protect stub
209+
auto virtual_protect_stub_addr = (PVOID)((ULONG_PTR)shellcode_addr + size_of_shellcode);
210+
memcpy(virtual_protect_stub_addr, virtual_protect_stub.data(), size_of_virtual_protect_stub);
211+
158212
// write the manual mapping data
159-
auto ManualMappingData = GetManualMappingData(kernel_image);
160-
auto manual_mapping_data_addr = (PMANUAL_MAPPING_DATA)((ULONG_PTR)shellcode_addr + size_of_shellcode);
161-
memcpy(manual_mapping_data_addr, &ManualMappingData, size_of_shellcode_data);
213+
auto manual_mapping_data = GetManualMappingData(kernel_image, virtual_protect_stub_addr);
214+
auto manual_mapping_data_addr = (PMANUAL_MAPPING_DATA)((ULONG_PTR)virtual_protect_stub_addr + size_of_virtual_protect_stub);
215+
memcpy(manual_mapping_data_addr, &manual_mapping_data, size_of_shellcode_data);
162216

163217
if (!pDriver->ExposeKernelMemory(pid, kernel_image, size_of_mapping))
164218
return fumo::error(ERR_STAGE2_FAILED_TO_EXPOSE_MEMORY, L"Failed to expose kernel memory");

0 commit comments

Comments
 (0)