Skip to content

Commit b03eb6d

Browse files
feat(hook): impl install_GetUIObjectOf_hook
1 parent 1aaae47 commit b03eb6d

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

src/shell/contextmenu/hooks.cc

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,130 @@ void mb_shell::context_menu_hooks::install_SHCreateDefaultContextMenu_hook() {
267267
return res;
268268
});
269269
}
270+
271+
#pragma optimize("", off)
272+
extern "C" __declspec(dllexport) size_t
273+
functionToGetGetUIObjectOfVptr(IShellFolder2 *i) {
274+
return i->GetUIObjectOf(nullptr, 0, nullptr, IID_IContextMenu, nullptr,
275+
nullptr);
276+
}
277+
#pragma optimize("", on)
278+
void mb_shell::context_menu_hooks::install_GetUIObjectOf_hook() {
279+
// For OneCommander
280+
281+
auto proc = blook::Process::self();
282+
auto shell32 = proc->module("shell32.dll");
283+
auto SHELL32_SHCreateDefaultContextMenu =
284+
shell32.value()->exports("SHELL32_SHCreateDefaultContextMenu");
285+
286+
static std::atomic_bool close_next_create_window_exw_window = false;
287+
288+
IShellFolder *psfDesktop = NULL;
289+
IShellFolder2 *psf2Desktop = NULL;
290+
if (NOERROR != SHGetDesktopFolder(&psfDesktop)) {
291+
std::cerr << "Failed to get desktop shell folder" << std::endl;
292+
return;
293+
}
294+
psfDesktop->QueryInterface(IID_IShellFolder2, (void **)&psf2Desktop);
295+
psfDesktop->Release();
296+
297+
// patch 'call' in functionToGetGetUIObjectOfVptr to 'mov rax, ptr' to get
298+
// the real function called
299+
auto disasmFuncGetVptr =
300+
blook::Pointer((void *)&functionToGetGetUIObjectOfVptr)
301+
.range_size(0x50)
302+
.disassembly();
303+
304+
std::optional<size_t> stackSize;
305+
for (auto &inst : disasmFuncGetVptr) {
306+
if (inst->getMnemonic() == zasm::x86::Mnemonic::Sub && !stackSize)
307+
stackSize = inst->getOperand(1).get<zasm::Imm>().value<size_t>();
308+
309+
if (inst->getMnemonic() == zasm::x86::Mnemonic::Call) {
310+
inst.ptr()
311+
.reassembly([&](zasm::x86::Assembler a) {
312+
a.mov(zasm::x86::rax, inst->getOperand(0).get<zasm::Mem>());
313+
a.add(zasm::x86::rsp, *stackSize);
314+
a.ret();
315+
})
316+
.patch();
317+
break;
318+
}
319+
}
320+
321+
auto pGetUIObjectOf =
322+
blook::Pointer((void *)functionToGetGetUIObjectOfVptr(psf2Desktop));
323+
std::println("GetUIObjectOf ptr: {}", pGetUIObjectOf.data());
324+
325+
/**
326+
prototype:
327+
HRESULT GetUIObjectOf(
328+
[in] HWND hwndOwner,
329+
[in] UINT cidl,
330+
[in] PCUITEMID_CHILD_ARRAY apidl,
331+
[in] REFIID riid,
332+
[in, out] UINT *rgfReserved,
333+
[out] void **ppv
334+
);
335+
336+
https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellfolder-getuiobjectof
337+
*/
338+
static auto GetUIObjectOfHook = pGetUIObjectOf.as_function().inline_hook();
339+
GetUIObjectOfHook->install(+[](void *thiz, HWND hwndOwner, UINT cidl,
340+
PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
341+
UINT *rgfReserved, void **ppv) -> HRESULT {
342+
std::println("GetUIObjectOf called");
343+
auto res = GetUIObjectOfHook->call_trampoline<HRESULT>(
344+
thiz, hwndOwner, cidl, apidl, riid, rgfReserved, ppv);
345+
346+
// only proceed if requesting IContextMenu
347+
if (riid != IID_IContextMenu || !ppv || !*ppv) {
348+
return res;
349+
}
350+
351+
std::println("Upgrading context menu");
352+
IContextMenu *pdcm = (IContextMenu *)(*ppv);
353+
if (SUCCEEDED(res) && pdcm) {
354+
CComPtr<IContextMenu> pCM(pdcm);
355+
356+
HMENU hmenu = CreatePopupMenu();
357+
pCM->QueryContextMenu(hmenu, 0, 1, 0x7FFF,
358+
CMF_EXPLORE | CMF_CANRENAME);
359+
360+
CComPtr<IContextMenu2> pCM2 = NULL;
361+
if (SUCCEEDED(pCM->QueryInterface(&pCM2))) {
362+
POINT pt;
363+
GetCursorPos(&pt);
364+
entry::main_window_loop_hook.install(hwndOwner);
365+
block_js_reload.fetch_add(1);
366+
perf_counter perf("TrackPopupMenuEx");
367+
menu menu = menu::construct_with_hmenu(
368+
hmenu, hwndOwner, true,
369+
[=](int message, WPARAM wParam, LPARAM lParam) {
370+
pCM2->HandleMenuMsg(message, wParam, lParam);
371+
});
372+
perf.end("construct_with_hmenu");
373+
374+
auto selected_menu = track_popup_menu(menu, pt.x, pt.y);
375+
mb_shell::context_menu_hooks::block_js_reload.fetch_sub(1);
376+
377+
if (selected_menu) {
378+
CMINVOKECOMMANDINFOEX ici = {};
379+
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
380+
ici.hwnd = hwndOwner;
381+
ici.fMask = 0x100000; /* CMIC_MASK_UNICODE */
382+
ici.lpVerb = MAKEINTRESOURCEA(*selected_menu - 1);
383+
ici.lpVerbW = MAKEINTRESOURCEW(*selected_menu - 1);
384+
ici.nShow = SW_SHOWNORMAL;
385+
386+
pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
387+
}
388+
389+
close_next_create_window_exw_window = true;
390+
}
391+
}
392+
393+
return res;
394+
});
395+
std::println("GetUIObjectOf hook installed");
396+
}

src/shell/contextmenu/hooks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ struct context_menu_hooks {
77
static std::atomic_int block_js_reload;
88
static void install_NtUserTrackPopupMenuEx_hook();
99
static void install_SHCreateDefaultContextMenu_hook();
10+
static void install_GetUIObjectOf_hook();
1011
}; // namespace context_menu_hooks
1112
} // namespace mb_shell

0 commit comments

Comments
 (0)