/// <summary> /// Takes an unmanaged memory address and reads the unmanaged memory given by its pointer, /// with memory address validation for added protection /// </summary> /// <typeparam name="T">the type of structure to return</typeparam> /// <param name="memAddress">the unamanaged memory address to read</param> /// <returns>the requested structure T</returns> public static T ReadStructureSafe <T>(IntPtr memAddress) { if (UnmanagedMemoryHelper.IsValidMemoryRange(memAddress, RdMarshal.SizeOf(typeof(T)))) { return((T)RdMarshal.PtrToStructure(memAddress, typeof(T))); } throw new ArgumentException("Bad data pointer - unable to read structure data."); }
/// <summary> /// frees all unmanaged memory assoicated with a DISPPARAMS structure /// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221416(v=vs.85).aspx /// </summary> /// <param name="pDispParams"></param> private static void UnprepareDispatchArgs(ComTypes.DISPPARAMS pDispParams) { if (pDispParams.rgvarg != IntPtr.Zero) { // free the array of COM VARIANTs var variantStructSize = RdMarshal.SizeOf(typeof(VARIANT)); var variantArgsArrayOffset = pDispParams.rgvarg; var argIndex = 0; while (argIndex < pDispParams.cArgs) { VariantClear(variantArgsArrayOffset); variantArgsArrayOffset += variantStructSize; argIndex++; } RdMarshal.FreeHGlobal(pDispParams.rgvarg); } }
/// <summary> /// Validate a memory address range /// </summary> /// <param name="memOffset">the input memory address to check</param> /// <param name="size">the minimum size of data we are expecting to be available next to memOffset</param> /// <param name="checkIsExecutable">optionally check if the memory address points to EXECUTABLE memory</param> public static bool IsValidMemoryRange(IntPtr memOffset, int size, bool checkIsExecutable = false) { if (memOffset == IntPtr.Zero) { return(false); } var memInfo = new MEMORY_BASIC_INFORMATION(); var sizeOfMemInfo = new IntPtr(RdMarshal.SizeOf(memInfo)); // most of the time, a bad pointer will fail here if (VirtualQuery(memOffset, out memInfo, sizeOfMemInfo) != sizeOfMemInfo) { return(false); } // check the memory area is not a guard page, or otherwise inaccessible if ((memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_NOACCESS)) || (memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_GUARD))) { return(false); } // We've confirmed the base memory address is valid, and is accessible. // Finally just check the full address RANGE is also valid (i.e. the end point of the structure we're reading) var validMemAddressEnd = memInfo.BaseAddress.ToInt64() + memInfo.RegionSize.ToInt64(); var endOfStructPtr = memOffset.ToInt64() + size; if (endOfStructPtr > validMemAddressEnd) { return(false); } if (checkIsExecutable) { // We've been asked to check if the memory address is marked as containing executable code return(memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE) || memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_READ) || memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_READWRITE) || memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_WRITECOPY)); } return(true); }
/// <summary> /// Convert input args into a contiguous array of real COM VARIANTs for the DISPPARAMS struct used by IDispatch::Invoke /// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221416(v=vs.85).aspx /// </summary> /// <param name="args">An array of arguments to wrap</param> /// <returns><see cref="ComTypes.DISPPARAMS"/> structure ready to pass to IDispatch::Invoke</returns> private static ComTypes.DISPPARAMS PrepareDispatchArgs(object[] args) { var pDispParams = new ComTypes.DISPPARAMS(); if ((args != null) && (args.Length != 0)) { var variantStructSize = RdMarshal.SizeOf(typeof(VARIANT)); pDispParams.cArgs = args.Length; var argsVariantLength = variantStructSize * pDispParams.cArgs; var variantArgsArray = RdMarshal.AllocHGlobal(argsVariantLength); // In IDispatch::Invoke, arguments are passed in reverse order var variantArgsArrayOffset = variantArgsArray + argsVariantLength; foreach (var arg in args) { variantArgsArrayOffset -= variantStructSize; RdMarshal.GetNativeVariantForObject(arg, variantArgsArrayOffset); } pDispParams.rgvarg = variantArgsArray; } return(pDispParams); }