I am trying to hook the function enumobjects in Ishellfolder .
I am doing it because I want to display the user non existing files in explorer.
I was success to hook FindNextFile and FindFirstFile but unfortunately this function not always call by explorer according to this question Which APIs are used by explorer.exe in Windows 7 to list files?
Now I try to hook IShellFolder::EnumObjects so I hook
MyCoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
And inside this function I have the following code:
if (IsEqualCLSID(rclsid, (REFGUID) __uuidof (IShellFolder)) ||
IsEqualCLSID(rclsid, (REFGUID) __uuidof (IShellFolder2)) ||
IsEqualCLSID(rclsid, (REFGUID) CLSID_ShellDesktop) ||
IsEqualCLSID(rclsid, (REFGUID) IID_IShellFolder) )
{
PDEBUG(L"IID_IShellFolder.2");
IShellFolderCast *shellFolder = (IShellFolderCast *) *ppv;
orig_EnumObjects = (type_EnumObjects) GetInterfaceMethod(shellFolder->lpVtbl, 4);
if (!Mhook_SetHook((void **) &orig_EnumObjects, MyEnumObjects))
{
PDEBUG(L". CoCreateInstance. Failed to set EnumObjects!");
}else
{
PDEBUG(L". CoCreateInstance. success to set EnumObjects!");
}
}
but it never go inside that if
anyone know why?
The following lays out how the windows API enumerates files in a directory. Look here.
[EDIT]
Missed the intent of your question on my first entry. You want to know how to trap an event when iShellFolder is accessed? You have probably already Looked Here?. It has some example code, and discusses topics around what I think may be useful.
Just change to
if (IsEqualCLSID(rclsid, (REFGUID) CLSID_ShellFSFolder) )
and now it works
Related
I need to get something from a program. And with the help of ollydbg and IDA, I found the "thing" I can get is in a function called sub_HEXHEX.
Now I know how to hook a function from Dll like DrawTextA or other one.
I need to get function address with
HMODULE hmod = LoadLibrary(L"User32.dll");
GetProcAddress(hmod, "DrawTextA")
But when I need to hook this sub_HEXHEX, I confused. I can get that exe's HANDLE, I know the function's Address (that 0x00HEXHEX), but there's no GetProcAddress I can use. I tried use HANDLE + 0x00HEXHEX as function's address, but I think im wrong with 'offset' things.
Here is what I did
DWORD dwPid = GetCurrentProcessId();
hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
OldA2ECB0 = (sub_A2ECB0)((HMODULE)hProcess + 0xA2ECB0);
pfOldA2ECB0 = (FARPROC)OldA2ECB0;
And sub_A2ECB0
typedef int(*sub_A2ECB0)(LPCSTR param1, int param2);
But pfOldA2ECB0 will be NULL.
My knowledge is poor with C++ and Win32 (English, too), so its toooooo difficult for me.
From what I understand, and I do believe I understand correctly what you want is internal hooking.
I can show you how to do this in 3 simple steps.
These are the things you must understand:
The function shown in IDA is not exported, thus you cannot use GetProcAddress()
You must be within the context of the remote process to hook internal functions
You cannot use libraries like Detours, you must have your own method/function for hooking
These are the steps you must do:
Be withing the context of the remote process, simplest method: inject your dll.
Get the address offset from IDA, in your case is 00A2ECB0.
Simply apply the hook once inside the remote process:
//0xE8 instruction is a relative call instruction
SetCompleteHook(0xE8,0x00A2ECB0,&YOUR_OWN_FUNCTION);
Helper:
void SetCompleteHook(BYTE head,DWORD offset,...)
{
DWORD OldProtect;
VirtualProtect((void*)offset,5,PAGE_EXECUTE_READWRITE,&OldProtect);
if(head != 0xFF)
{
*(BYTE*)(offset) = head;
}
DWORD* function = &offset+1;
*(DWORD*)(offset+1) = (*function)-(offset+5);
VirtualProtect((void*)offset,5,OldProtect,&OldProtect);
}
And that's it really, it's as simple and straightforward as possible in your scenario.
You can go further of course and perform much more complex internal hooking, but for your learning purpose this is a massive step ahead and starting point.
Enjoy!
I have this project where I hook some Windows functions (GetOpenFileNameA, GetOpenFileNameW, GetSaveFileNameA, GetSaveFileNameW) with MHook library. This is the code I use to install the hooks.
for(size_t i = 0; i < FunctionsCount; ++i)
{
HMODULE hModule = GetModuleHandleA(Function[i].ModuleName);
//[1]
if( !hModule )
return FALSE;
*Function[i].Original = GetProcAddress(hModule, Function[i].Name);
if(*Function[i].Original == NULL)
return FALSE;
if(!Mhook_SetHook(Function[i].Original, Function[i].Hooked))
return FALSE;
}
It is called from DllMain on DLL_PROCESS_ATTACH reason.
Now, when I inject my Dll using the CreateRemoteThread approach it works pretty well, but when I want to set up the system-wide hooks using LoadAppInit_DLLs mechanism my hooks doesn't works. After hours debugging I found that the reason is that my Dll is loaded BEFORE comdlg32.dll (which is the module where these functions are), and then the statement [1] returns false, then my Dll is not loaded.
The solution I've so far is to call LoadLibrary if [1] returns false.
HMODULE hModule = GetModuleHandleA(Function[i].ModuleName);
//[2]
if( !hModule )
{
LoadLibraryA(Function[i].ModuleName);
hModule = GetModuleHandleA(Function[i].ModuleName);
}
I've found many site where is said this is evil and I agree (even when works fine). Also if a process doesn't use common dialogs at all I'm hooking functions that will never be called.
If anybody could help, maybe a workaround or an explanation of another way to set-up global hooks it will be appreciated. Thanks in advance
You need to hook LoadLibraryXXX functions and after successful their execution check whether your module has been loaded (calling GetModuleHandle) and hook it if it is loaded.
Also it is a good idea to pin hooked dlls so they are not unloaded anymore.
Im currently working with Visual Studio 2010 C++ Custom Action Project
I have a custom action like this:
extern "C" UINT __stdcall RegProductName(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
char szLocalPath[MAX_PATH];
hr = WcaInitialize(hInstall, "RegProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
strcpy(szLocalPath, Orc_Get_Product_Name());
MsiSetProperty(hInstall, "ProductName", szLocalPath);
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
It does not throw up any errors but I'm not sure if i'm doing what I want correctly.
In this line:
strcpy(szLocalPath, Orc_Get_Product_Name());
I'm getting the Product name from the function and copying it to szLocalPath. However I want to use this variable many times in other functions and also in my wix project.
Is this the correct way?
MsiSetProperty(hInstall, "ProductName", szLocalPath);
Many thanks
I can see no issue here but potential buffer overflow in strcpy. If your function Orc_Get_Product_Name() returns path longer than MAX_PATH. However, you'd better use TCHAR type instead of plain char to support Unicode/ANSI builds, and you should really build with Unicode enabled.
And I'm sure you want to assign the result of MsiSetProperty to hr variable.
Additionally, it looks strange that you copy Product Name into variable called Local Path.
And I can't see where the label LExit is used.
The function ExitOnFailure does not seem to return control: if WcaInitialize was unsuccessful, you should simply return ERROR_INSTALL_FAILURE or another error code back to MSI engine (rather than terminate the process where your custom action is running).
I'm trying to write a Windows Explorer thumbnail handler for our custom file type. I've got this working fine for the preview pane, but am having trouble getting it to work for the thumbnails.
Windows doesn't even seem to be trying to call the DllGetClassObject entry point.
Before I continue, note that I'm using Windows 7 and unmanaged C++.
I've registered the following values in the registry:
HKCR\CLSID\<my guid>
HKCR\CLSID\<my guid>\InprocServer32 (default value = path to my DLL)
HKCR\CLSID\<my guid>\InprocServer32\ThreadingModel (value = "Apartment")
HKCR\.<my ext>\shellex\{E357FCCD-A995-4576-B01F-234630154E96} (value = my guid)
I've also tried using the Win SDK sample, and that doesn't work. And also the sample project in this article (http://www.codemonkeycodes.com/2010/01/11/ithumbnailprovider-re-visited/), and that doesn't work.
I'm new to shell programming, so not really sure the best way of debugging this. I've tried attaching the debugger to explorer.exe, but that doesn't seem to work (breakpoints get disabled, and none of my OutputDebugStrings get displayed in the output window). Note that I tried setting the "DesktopProcess" in the registry as described in the WinSDK docs for debugging the shell, but I'm still only seeing one explorer.exe in the task manager - so that "may" be why I can't debug it??
Any help with this would be greatly appreciated!
Regards,
Dan.
I stumbled across this since you mentioned my blog ( codemonkeycodes.com ).
What problem are you having with my sample? Did you register you DLL using regsvr32? What version of Windows 7 are you on, 32 or 64?
Update:
I can't say what is or isn't working for you. I just downloaded the sample from my site, followed the directions and change the function
STDMETHODIMP CThumbnailProvider::GetThumbnail... to look like
{
*phbmp = NULL;
*pdwAlpha = WTSAT_UNKNOWN;
ULONG_PTR token;
GdiplusStartupInput input;
if (Ok == GdiplusStartup(&token, &input, NULL))
{
//gcImage.LogBuffer();
Bitmap * pBitmap = new Bitmap(188, 141);
if( pBitmap )
{
Color color(0, 0, 0);
pBitmap->GetHBITMAP(color, phbmp);
}
}
GdiplusShutdown(token);
if( *phbmp != NULL )
return NOERROR;
return E_NOTIMPL;
}
I registered the DLL and then created a new file with the proper extension, and tada, I had a nice black thumbnail.
I wish I could help you. Maybe you want to email me your code?
I've exactly the same problem. I cant make SDK or any sample works. I need COM sample because I must call Microsoft.Jet.OLEDB.4.0 which works only on 32 bits system.
I couldnt make this work: link
This works if AnyCPU is specified when compiling. Cant make it works for x86: link
This was nice under XP works like a charm: link
This show Adobe had problems with thumbnail An MS with Office 2007 (32 bits): link
I would like a process to always run at the user level. Either when it is launched by the installer (custom, not msi), which runs as at the administrator level, or when a user logs on. Looking around, I'm not sure this is possible.
The easiest way is to have 2 processes. One is normal user and it launches elevated/admin process. Then admin process can use IPC to ask normal user process to do things.
If you have no normal user process, then Raymond Chen documents:
Going from an unelevated process to an elevated process is easy. You can run a process with elevation by passing the runas verb to ShellExecute or ShellExecuteEx.
Going the other way is trickier. For one thing, it's really hard to munge your token to remove the elevation nature properly. And for another thing, even if you could do it, it's not the right thing to do, because the unelevated user may be different from the elevated user.
The solution here is to go back to Explorer and ask Explorer to launch the program for you. Since Explorer is running as the original unelevated user, the program (in this case, the Web browser) will run as Bob. This is also important in the case that the handler for the file you want to open runs as an in-process extension rather than as a separate process, for in that case, the attempt to unelevate would be pointless since no new process was created in the first place. (And if the handler for the file tries to communicate with an existing unelevated copy of itself, things may fail because of UIPI.)
Okay, I know that Little Programs are not supposed to have motivation, but I couldn't help myself. Enough jabber. Let's write code. (Remember that Little Programs do little or no error checking, because that's the way they roll.)
#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>
void FindDesktopFolderView(REFIID riid, void **ppv)
{
CComPtr<IShellWindows> spShellWindows;
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
CComPtr<IShellBrowser> spBrowser;
CComQIPtr<IServiceProvider>(spdisp)->
QueryService(SID_STopLevelBrowser,
IID_PPV_ARGS(&spBrowser));
CComPtr<IShellView> spView;
spBrowser->QueryActiveShellView(&spView);
spView->QueryInterface(riid, ppv);
}
void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
CComPtr<IShellView> spsv;
FindDesktopFolderView(IID_PPV_ARGS(&spsv));
CComPtr<IDispatch> spdispView;
spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
spdispView->QueryInterface(riid, ppv);
}
The GetDesktopAutomationObject function locates the desktop folder view then asks for the dispatch object for the view. We then return that dispatch object in the form requested by the caller. This dispatch object is a ShellFolderView, and the C++ interface for that is IShellFolderViewDual, so most callres are going to ask for that interface, but if you are a masochist, you can skip the dual interface and talk directly to IDispatch.
void ShellExecuteFromExplorer(
PCWSTR pszFile,
PCWSTR pszParameters = nullptr,
PCWSTR pszDirectory = nullptr,
PCWSTR pszOperation = nullptr,
int nShowCmd = SW_SHOWNORMAL)
{
CComPtr<IShellFolderViewDual> spFolderView;
GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
CComPtr<IDispatch> spdispShell;
spFolderView->get_Application(&spdispShell);
CComQIPtr<IShellDispatch2>(spdispShell)
->ShellExecute(CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(pszDirectory ? pszDirectory : L""),
CComVariant(pszOperation ? pszOperation : L""),
CComVariant(nShowCmd));
}
The ShellExecuteFromExplorer function starts by getting the desktop folder automation object. We use the desktop not because it's particularly meaningful but because we know that it's always going to be there.
As with the desktop folder view, the ShellFolderView object is not interesting to us for itself. It's interesting to us because the object resides in the process that is hosting the desktop view (which is the main Explorer process). From the ShellFolderView, we ask for the Application property so that we can get to the main Shell.Application object, which has the IShellDispatch interface (and its extensions IShellDispatch2 through IShellDispatch6) as its C++ interfaces. And it is the IShellDispatch2::ShellExecute method that is what we really want.
And we call IShellDispatch2::ShellExecute with the appropriate parameters. Note that the parameters to IShellDispatch2::ShellExecute are in a different order from the parameters to ShellExecute!
Okay, let's put this inside a little program.
int __cdecl wmain(int argc, wchar_t **argv)
{
if (argc < 2) return 0;
CCoInitialize init;
ShellExecuteFromExplorer(
argv[1],
argc >= 3 ? argv[2] : L"",
argc >= 4 ? argv[3] : L"",
argc >= 5 ? argv[4] : L"",
argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);
return 0;
}
The program takes a mandatory command line argument which is the thing to execute, be it a program or a document or a URL. Optional parameters are the parameters to the thing being executed, the current directory to use, the operation to perform, and how the window should be opened.
Open an elevated command prompt, and then run this program in various ways.
scratch http://www.msn.com/
Open an unelevated Web page in the user's default Web browser.
scratch cmd.exe "" C:\Users "" 3
Open an unelevated command prompt at C:\Users, maximized.
scratch C:\Path\To\Image.bmp "" "" edit
Edit a bitmap in an unelevated image editor.
Everybody is always looking for going around the other way. Anyhoo, this code project should help.
Assuming you know which user you want to run as, and have their password, the CreateProcessWithLogonW Function will do that.
There are many hacky ways to do this (Use the taskscheduler, inject into explorer.exe etc)
The only way to get the correct user (The one that started your program before UAC elevation (This might not be the same user as the shell/"login"/"session owner")) is to have the installer run two instances of itself, one "outer" instance that is not elevated, it mostly just starts the other instance by starting itself with ShellExecute[Ex] with the runas verb. When it comes time to start the medium/low level process, the elevated instance by some form of IPC tells the outer instance to start the new process.
It is a pain in the neck to implement, I would recommend just not having a run checkbox at the end of your installer.
similar to what Bill said, you can also use CreateProcessAsUser() API to do that.
First use LogonUser() and get an access token for the user that the process needs to run as . Here if the user belongs to administrators group (then you will get a split token if you pass the LOGON_FLAG as LOGON32_LOGON_INTERACTIVE). So if you need the elevated administrator token pass the flag as LOGON32_LOGON_BATCH.
With the token obtained above, you can call CreateProcessAsUser() passing the commandline and parameters.