You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With the current fancycode you can load DLLs but starting Calc.exe or Notepad.exe from memory fails always.
Calling the entry point hangs forever or crashes.
At least I know that adapting the PEB and the Loader Table is missing in fancycode.
Here is the missing code:
// The following 3 structs have been verified for 32 bit and 64 bit compilation
// using the macro "offsetof()" with the relative offset information from Geoff Campbell.
#pragma pack(push, 8)
// Original name: LDR_DATA_TABLE_ENTRY
// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/ldr_data_table_entry.htm
struct OS_LDR_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderModuleList; // LDR_DATA_TABLE_ENTRY by load order
LIST_ENTRY InMemoryOrderModuleList; // LDR_DATA_TABLE_ENTRY by memory location
LIST_ENTRY InInitializationOrderModuleList; // LDR_DATA_TABLE_ENTRY by initialization order
BYTE* DllBase; // base address to which the module was loaded
BYTE* EntryPoint; // DllMain()
ULONG SizeOfImage; // size in memory
UNICODE_STRING FullDllName; // full path
UNICODE_STRING BaseDllName; // only file name
ULONG Flags; // LDRP_IMAGE_DLL, etc...
USHORT LoadCount; // reference count (not used anymore since Windows 8)
USHORT TlsIndex; // Thread Local Storage slot for this module
union // 3C
{
LIST_ENTRY HashLinks; // since Windows 8 sizeof(LIST_ENTRY) = 8 bytes
PVOID SectionPointer; // NtClose(SectionPointer) when the DLL is unloaded
};
ULONG TimeDateStamp; // 44: Linker timestamp
// The rest is not required here
};
// Original name: PEB_LDR_DATA
// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
struct OS_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized; // Size = 1 !!
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
BOOLEAN ShutdownInProgress;
HANDLE ShutdownThreadId;
};
// Original name: PEB
// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
struct OS_PEB
{
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BYTE BitField;
HANDLE Mutant;
PVOID ImageBaseAddress;
OS_LDR_DATA* Ldr;
RTL_USER_PROCESS_PARAMETERS* ProcessParameters;
// The rest is not required here
};
#pragma pack(pop)
#define LDRP_IMAGE_DLL 0x00000004 // This module is an image DLL (not a Data DLL or EXE)
typedef OS_PEB* (NTAPI* tRtlGetCurrentPeb)();
// define global variable
static tRtlGetCurrentPeb gf_RtlGetCurrentPeb = NULL;
// returns API error
DWORD MEMORYMODULE::CallEntryPoint()
{
// Get the .NET Metadata directory (former name: COM descriptor)
IMAGE_DATA_DIRECTORY* pk_ClrDir = &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
if (pk_ClrDict->Size > 0)
{
IMAGE_COR20_HEADER* pk_ClrHeader = (IMAGE_COR20_HEADER*)(codeBase + pk_ClrDir->VirtualAddress);
// Since Windows XP the Windows loader does NOT call the entry point of a managed EXE anymore.
// Microsoft did this by purpose. If a virus exists in the entry code of a .NET assembly it will never be executed.
// Theoretically a .NET executable is started with the parameterless function _CorExeMain() in Mscoree.dll.
// But MsCoree.dll creates a filemapping from the real EXE file on disk.
// If the file exists only as image in memory _CorExeMain() immediately terminates the process.
// On the other hand loading a .NET DLL is ultra simple in managed code.
// Simply use Assembly.Load(Byte[])
// If you want to load managed assemblies into an unmanged process read these articles:
// https://www.codeproject.com/Articles/607352/Injecting-NET-Assemblies-Into-Unmanaged-Processes
// https://www.experts-exchange.com/questions/28909682/C-Loading-Managed-Assembly-From-Memory-in-Unmanaged-Process.html
// https://modexp.wordpress.com/2019/05/10/dotnet-loader-shellcode/
TRACE(L"ERROR: Loading managed executables is not implemented.");
return ERROR_INVALID_FUNCTION;
}
if (isDLL)
{
// The entry point for a DLL is optional
if (headers->OptionalHeader.AddressOfEntryPoint == 0)
return 0;
DllEntryProc f_DllEntry = (DllEntryProc)(codeBase + headers->OptionalHeader.AddressOfEntryPoint);
TRACE(L"Send DLL_PROCESS_ATTACH to DLL entry point %p", f_DllEntry);
if (!f_DllEntry((HINSTANCE)codeBase, DLL_PROCESS_ATTACH, 0))
return ERROR_DLL_INIT_FAILED;
initialized = TRUE;
return 0;
}
else // EXE
{
// An EXE without entry point does not make sense. The Windows Loader crashes if EntryPoint == 0.
if (headers->OptionalHeader.AddressOfEntryPoint == 0)
return ERROR_BAD_FORMAT;
if (!gf_RtlGetCurrentPeb)
{
HMODULE h_NtDll = GetModuleHandleA("ntdll.dll");
if (!h_NtDll)
return GetLastError();
if (!(gf_RtlGetCurrentPeb = (tRtlGetCurrentPeb)GetProcAddress(h_NtDll, "RtlGetCurrentPeb")))
return GetLastError();
}
ExeEntryProc f_ExeEntry = (ExeEntryProc)(codeBase + headers->OptionalHeader.AddressOfEntryPoint);
OS_PEB* pk_PEB = gf_RtlGetCurrentPeb();
pk_PEB->ImageBaseAddress = codeBase;
LIST_ENTRY *pk_Head = &pk_PEB->Ldr->InLoadOrderModuleList;
LIST_ENTRY *pk_Current = pk_Head;
// Iterate the modules of the current process.
// The ModuleList is cyclic, which means that it has no end.
// The last element points back to the first.
while ((pk_Current = pk_Current->Flink) != pk_Head)
{
OS_LDR_TABLE_ENTRY* pk_Module = CONTAINING_RECORD(pk_Current, OS_LDR_TABLE_ENTRY, InLoadOrderModuleList);
// Search for the EXE in the module list (normally the first entry)
if ((pk_Module->Flags & LDRP_IMAGE_DLL) == 0)
{
pk_Module->DllBase = codeBase;
pk_Module->SizeOfImage = AlignValueUp(headers->OptionalHeader.SizeOfImage, pageSize);
pk_Module->TimeDateStamp = headers->FileHeader.TimeDateStamp;
pk_Module->EntryPoint = (BYTE*)f_ExeEntry;
break;
}
}
TRACE(L"Call EXE main() at entry point %p", f_ExeEntry);
// This function never returns.
// When the user closes the main window, Windows kills our process with ExitProcess().
// Therefore it is nonsense to run this code in an extra thread as I have seen in Github forks of fancycode.
f_ExeEntry();
// This "return" is just for the compiler not to complain. It will never execute.
return 0;
}
}
Without the above code you can neither start Calc.exe nor Notepad.exe.
It fails on ALL operating systems.
With the above code you can start Calc.exe and Notepad.exe from memory.
But not on all operating systems.
1.)
If an EXE file does not have a relocation table it is very probable that you cannot start it.
Without relocation table the EXE MUST run at the predefined base address (mostly 40000).
If this address area is already occupied by your starter process there is no way to start this EXE.
In this case recompile the EXE that you want to start.
Under "Linker Options" --> "Adavanced" --> "Fixed Base Address" enter "Generate a relocation section"
This will add the linker commandline option /FIXED:NO
After that the EXE will run at any base address.
2.)
The Calc.exe on Windows 10 is not the real calculator anymore.
It is only a launcher of 26 kB size which starts Calculator.exe in an AppContainer and then exits.
3.)
With the above code I got the following results:
Windows XP 32 bit: Starting a 32 bit Calc.exe and Notepad.exe work perfectly (ONLY with the above code fix).
Windows 7 64 bit: Starting a 32 bit Calc.exe and Notepad.exe work perfectly (ONLY with the above code fix).
Windows 7 64 bit: Starting a 64 bit Calc.exe crashes immediately and Notepad.exe crashes when you chose a font.
Windows 10 64 bit: Starting a 32 + 64 bit Calc.exe works (Calculator.exe is launched in AppContainer)
Windows 10 64 bit: Starting a 32 + 64 bit Notepad.exe does not work
4.)
But I can start my own MFC compiled GUI application either 32 bit or 64 bit perfectly on ALL operating systems.
And even without the above PEB fix!
SUMMARY:
My own EXE (with GUI) can be started, but Microsoft's EXE's not always.
That's weird. I gave up getting this to work.
If anybody can explain me that, please post a comment.
BTW: I have no antivirus installed and Windows Defender is disabled.
Don't forget to implement the Activation Context! #100
P.D.
There is many code out there using the process hollowing technique.
But it has exactly the same problems.
Notepad.exe can be started on XP but not on Windows 10.
Elmue
The text was updated successfully, but these errors were encountered:
Well, I tried to have a universal DLL/EXE loader which works as the Microsoft loader does.
This means that it should load any DLL/EXE without limitations.
This is sadly not the case. Anything is still missing.
Sometimes you want to load third party DLLs/EXEs and it really sucks when they crash.
There are open source projects which do far more complicated things and they never crash.
For example I use EasyHook which is a great project. It works without any problem.
With the current fancycode you can load DLLs but starting Calc.exe or Notepad.exe from memory fails always.
Calling the entry point hangs forever or crashes.
At least I know that adapting the PEB and the Loader Table is missing in fancycode.
Here is the missing code:
Without the above code you can neither start Calc.exe nor Notepad.exe.
It fails on ALL operating systems.
With the above code you can start Calc.exe and Notepad.exe from memory.
But not on all operating systems.
1.)
If an EXE file does not have a relocation table it is very probable that you cannot start it.
Without relocation table the EXE MUST run at the predefined base address (mostly 40000).
If this address area is already occupied by your starter process there is no way to start this EXE.
In this case recompile the EXE that you want to start.
Under "Linker Options" --> "Adavanced" --> "Fixed Base Address" enter "Generate a relocation section"
This will add the linker commandline option /FIXED:NO
After that the EXE will run at any base address.
2.)
The Calc.exe on Windows 10 is not the real calculator anymore.
It is only a launcher of 26 kB size which starts Calculator.exe in an AppContainer and then exits.
3.)
With the above code I got the following results:
Windows XP 32 bit: Starting a 32 bit Calc.exe and Notepad.exe work perfectly (ONLY with the above code fix).
Windows 7 64 bit: Starting a 32 bit Calc.exe and Notepad.exe work perfectly (ONLY with the above code fix).
Windows 7 64 bit: Starting a 64 bit Calc.exe crashes immediately and Notepad.exe crashes when you chose a font.
Windows 10 64 bit: Starting a 32 + 64 bit Calc.exe works (Calculator.exe is launched in AppContainer)
Windows 10 64 bit: Starting a 32 + 64 bit Notepad.exe does not work
4.)
But I can start my own MFC compiled GUI application either 32 bit or 64 bit perfectly on ALL operating systems.
And even without the above PEB fix!
SUMMARY:
My own EXE (with GUI) can be started, but Microsoft's EXE's not always.
That's weird. I gave up getting this to work.
If anybody can explain me that, please post a comment.
BTW: I have no antivirus installed and Windows Defender is disabled.
Don't forget to implement the Activation Context!
#100
and fix the execution bug:
#101
P.D.
There is many code out there using the process hollowing technique.
But it has exactly the same problems.
Notepad.exe can be started on XP but not on Windows 10.
Elmue
The text was updated successfully, but these errors were encountered: