Skip to content

Instantly share code, notes, and snippets.

@jun-lsh
Created February 20, 2024 14:19
Show Gist options
  • Select an option

  • Save jun-lsh/5e8e1a3558c3ae86cd3e443344c81278 to your computer and use it in GitHub Desktop.

Select an option

Save jun-lsh/5e8e1a3558c3ae86cd3e443344c81278 to your computer and use it in GitHub Desktop.
A Naive Implementation of the Magistr/Shoerec Payload
#include <Windows.h>
#include <ExDisp.h>
#include <atlbase.h>
#include <Shlwapi.h>
#include <atlalloc.h>
#include <ShlObj.h>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <Psapi.h>
#include <RestartManager.h>
#pragma comment(lib, "Rstrtmgr.lib")
#define DESKTOP_KEY L"SOFTWARE\\Microsoft\\Windows\\Shell\\Bags\\1\\Desktop"
class CCoInitialize {
public:
CCoInitialize() : m_hr(CoInitialize(NULL)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
HRESULT m_hr;
};
RM_UNIQUE_PROCESS GetExplorerApplication()
{
RM_UNIQUE_PROCESS result = { 0 };
DWORD bytesReturned = 0;
DWORD processIdSize = 4096;
std::vector<DWORD> processIds;
processIds.resize(1024);
// Get the list of process identifiers.
EnumProcesses(processIds.data(), processIdSize, &bytesReturned);
while (bytesReturned == processIdSize)
{
processIdSize += processIdSize;
processIds.resize(processIdSize / 4);
EnumProcesses(processIds.data(), processIdSize, &bytesReturned);
}
std::for_each(processIds.begin(), processIds.end(), [&result](DWORD processId) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess) {
std::wstring imageName;
imageName.resize(4096);
if (GetProcessImageFileName(hProcess, (LPWSTR)imageName.data(), 4096) > 0)
{
if (wcscmp(L"explorer.exe", PathFindFileName(imageName.data())) == 0)
{
//this is assmuing the user is not running elevated and won't see explorer processes in other sessions
FILETIME ftCreate, ftExit, ftKernel, ftUser;
if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser))
{
if (result.dwProcessId == 0)
{
result.dwProcessId = processId;
result.ProcessStartTime = ftCreate;
}
// we opt for the older process
else if (CompareFileTime(&result.ProcessStartTime, &ftCreate) > 0)
{
result.dwProcessId = processId;
result.ProcessStartTime = ftCreate;
}
}
}
}
CloseHandle(hProcess);
}
});
return result;
}
bool FindDesktopFolderView(REFIID riid, void** ppv)
{
// All open shell windows (explorer windows)
CComPtr<IShellWindows> spShellWindows;
// Class ID: CLSID, Interface ID: IID
//
// To get the explorer windows, we use CoCreateInstance, which creates an object of the class associated with the CLSID and IID
// To get all shell windows: hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**) &psw);
// CLSCTX_ALL means we're fine with all contexts
HRESULT hResult = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
if (hResult != S_OK) return false;
// CSIDLs are "constant special item ID list", a unique system-independent way to ID special folders
// In the docs, the example given is C:\Windows. Turns out, you can name it whatever the hell you want
// CSIDL_DESKTOP is the virtual folder that represents the desktop.
//CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
// Finds a window in the Shell collection, returns the handle and IDispatch interface
// https://learn.microsoft.com/en-us/windows/win32/api/exdisp/nf-exdisp-ishellwindows-findwindowsw
// we set swClass to SWC_DESKTOP so that we are only looking at... desktop windows
// lhwnd is the corresponding handle, but what we really want is spdisp, the IDispatch interface
hResult = spShellWindows->FindWindowSW (
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
if (hResult != S_OK) return false;
// IShellBrowser gives us some helpful methods, and the one we want is QueryActiveShellView. Quite self-explanatory
// https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellbrowser-queryactiveshellview
CComPtr<IShellBrowser> spBrowser;
// CComQIPtr is a smart pointer class and we use the QueryService method to get a reference to the browser
//
// "Objects that have access to the site chain of the browser can get a reference to the browser on IShellBrowser
// using IServiceProvider::QueryService, with Service IDs such as SID_STopLevelBrowser and SID_SCommDlgBrowser."
hResult = CComQIPtr<IServiceProvider>(spdisp)->
QueryService(SID_STopLevelBrowser, // GUID for the browser
IID_PPV_ARGS(&spBrowser));
if (hResult != S_OK) return false;
CComPtr<IShellView> spView;
hResult = spBrowser->QueryActiveShellView(&spView);
if (hResult != S_OK) return false;
// Lastly, we can apply QueryInterface to get our interface of choice from the Desktop object by passing in an RIID
hResult = spView->QueryInterface(riid, ppv);
if (hResult != S_OK) return false;
return true;
}
void RestartExplorer() {
DWORD dwSession = 0;
WCHAR szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
// create a Restart Manager session
DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
if (dwError == ERROR_SUCCESS) {
// isolate the explorer process
RM_UNIQUE_PROCESS rgApplications[1] = { GetExplorerApplication() };
dwError = RmRegisterResources(dwSession, 0, NULL, 1, rgApplications, 0, NULL);
DWORD dwReason;
UINT nProcInfoNeeded;
UINT nProcInfo = 10;
RM_PROCESS_INFO rgpi[10];
// get the current status of registered items
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
if (dwReason == RmRebootReasonNone) //now free to restart explorer
{
RmShutdown(dwSession, RmForceShutdown, NULL);
RmRestart(dwSession, 0, NULL);
}
}
RmEndSession(dwSession);
}
// out of laziness, we copy and paste and overload this function.
void RestartExplorer(HKEY hKey, DWORD target) {
DWORD dwSession = 0;
WCHAR szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
// create a Restart Manager session
DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
if (dwError == ERROR_SUCCESS) {
// isolate the explorer process
RM_UNIQUE_PROCESS rgApplications[1] = { GetExplorerApplication() };
dwError = RmRegisterResources(dwSession, 0, NULL, 1, rgApplications, 0, NULL);
DWORD dwReason;
UINT nProcInfoNeeded;
UINT nProcInfo = 10;
RM_PROCESS_INFO rgpi[10];
// get the current status of registered items
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
if (dwReason == RmRebootReasonNone) //now free to restart explorer
{
RmShutdown(dwSession, RmForceShutdown, NULL);
RegSetValueEx(hKey, L"FFlags", NULL, REG_DWORD, (const BYTE*)&target, sizeof(target));
RmRestart(dwSession, 0, NULL);
}
}
RmEndSession(dwSession);
}
int __cdecl wmain(int argc, wchar_t** argv)
{
FreeConsole();
// wtf is CCoInitialize?
// it ensures CoUnitialize is called after your CCom ptrs are destroyed
// https://devblogs.microsoft.com/oldnewthing/20040520-00/?p=39243
CCoInitialize init;
RestartExplorer();
Sleep(2000);
// Using our function, we get a reference to the desktop's IFolderView
CComPtr<IFolderView> spView;
while(!FindDesktopFolderView(IID_PPV_ARGS(&spView)));
// The IShellFolder object will let us get attributes to do with the files in the folder
CComPtr<IShellFolder> spFolder;
spView->GetFolder(IID_PPV_ARGS(&spFolder));
POINT cursor_pt;
int pushRad = 125;
DWORD desktopFflags{};
DWORD pcbData = sizeof(desktopFflags);
WCHAR targetFlag[11];
swprintf_s(targetFlag, 11, L"%d", 1075839520);
DWORD target = 1075839520;
HKEY hKey = NULL;
RegOpenKeyExW(HKEY_CURRENT_USER, DESKTOP_KEY, 0, KEY_WRITE, &hKey);
RegGetValue(HKEY_CURRENT_USER, DESKTOP_KEY, L"FFlags", RRF_RT_REG_DWORD, NULL, &desktopFflags, &pcbData);
wprintf(L"Regvalue: %d %d\n", desktopFflags, pcbData);
if (desktopFflags != target) {
wprintf(L"Changed the regkey!\n");
RestartExplorer(hKey, target);
RegCloseKey(hKey);
}
wprintf(L"Continuing...\n");
Sleep(2000);
while (true) {
if (GetCursorPos(&cursor_pt))
{
CComPtr<IEnumIDList> spEnum;
// Items lets us enumerate across... the items.
// SVGIO is a flag here that just says gimme everything!
// "Used with the IFolderView::Items, IFolderView::ItemCount, and IShellView::GetItemObject methods to restrict or control the items in their collections."
HRESULT hResult = spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));
if (hResult != S_OK) {
// for posterity
spView.Release();
spFolder.Release();
if (FindDesktopFolderView(IID_PPV_ARGS(&spView))) {
spView->GetFolder(IID_PPV_ARGS(&spFolder));
}
}
else {
// Now we iterate through the objects
for (CComHeapPtr<ITEMID_CHILD> spidl;
spEnum->Next(1, &spidl, nullptr) == S_OK;
spidl.Free()) {
POINT icon_pt;
spView->GetItemPosition(spidl, &icon_pt); // This gives us the coordinates of the icons!
// Check if our icon is within the cursor's forcefield radius
double distance = pow(((icon_pt.x + 38 - 1920) - cursor_pt.x), 2) + pow(((icon_pt.y + 54) - cursor_pt.y), 2);
// wprintf(L"Icon is %d %d away\n", (icon_pt.x - cursor_pt.x), (icon_pt.y - cursor_pt.y));
if (distance <= pushRad * pushRad) {
double multiplier = pushRad / sqrt((distance)) - 1;
icon_pt.x += (int)(((icon_pt.x + 38 - 1920) - cursor_pt.x) * multiplier);
icon_pt.y += (int)(((icon_pt.y + 54) - cursor_pt.y) * multiplier);
}
PCITEMID_CHILD apidl[1] = { spidl };
spView->SelectAndPositionItems(
1, apidl, &icon_pt, SVSI_POSITIONITEM
);
}
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment