diff --git a/ZuneModCore/Mods/VideoSyncMod.cs b/ZuneModCore/Mods/VideoSyncMod.cs index ba79f86..6ce177f 100644 --- a/ZuneModCore/Mods/VideoSyncMod.cs +++ b/ZuneModCore/Mods/VideoSyncMod.cs @@ -2,7 +2,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; using System.Threading.Tasks; +using ZuneModCore.Win32; namespace ZuneModCore.Mods { @@ -21,34 +24,54 @@ namespace ZuneModCore.Mods public override AbstractUIElementGroup? OptionsUI => null; +#pragma warning disable CA1416 // Validate platform compatibility public override Task Apply() { + // Make a backup of the file + File.Copy(WMVCORE_PATH, Path.Combine(StorageDirectory, "WMVCORE.original.dll"), true); + + // Get the working WMVCORE.dll + string sourcePath = Path.Combine( + Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)!, "Resources\\WMVCORE.dll"); + + // Get the original WMVCORE.dll + FileInfo wmvDllInfo = new(WMVCORE_PATH); + try { - // Make a backup of the file - File.Copy(WMVCORE_PATH, Path.Combine(StorageDirectory, "WMVCORE.original.dll"), true); + // Activate necessary admin privileges to make changes without NTFS perms + TokenManipulator.AddPrivilege("SeRestorePrivilege"); // Necessary to set Owner Permissions + TokenManipulator.AddPrivilege("SeBackupPrivilege"); // Necessary to bypass Traverse Checking + TokenManipulator.AddPrivilege("SeTakeOwnershipPrivilege"); // Necessary to override FilePermissions - // NOTE: This is quite dangerous to do blindly, so let's not do this - // Kill processes that are using WMVCORE.dll - //foreach (System.Diagnostics.Process proc in FileUtil.WhoIsLocking(WMVCORE_PATH)) - //{ - // proc.Kill(); - //} + // Get access control + FileSecurity security = wmvDllInfo.GetAccessControl(); + SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User; + if (cu == null) + return Task.FromResult("Failed to set permissions on WMVCORE.dll, current user was null"); - // Get the working WMVCORE.dll - string sourcePath = Path.Combine( - Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)!, "Resources\\WMVCORE.dll"); + // Set owner to current user + security.SetOwner(cu); + security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow)); + + // Update the Access Control on the original WMVCORE.dll + wmvDllInfo.SetAccessControl(security); // Copy the pre-Anniversary Update WMVCORE.dll File.Copy(sourcePath, WMVCORE_PATH, true); return Task.FromResult(null); } + catch (IOException) + { + return Task.FromResult($"Unable to replace '{wmvDllInfo.FullName}'. Verify that Zune and Windows Media Player are not running and try again."); + } catch (Exception ex) { return Task.FromResult(ex.Message)!; } } +#pragma warning restore CA1416 // Validate platform compatibility public override Task Reset() { diff --git a/ZuneModCore/Win32/TokenManipulator.cs b/ZuneModCore/Win32/TokenManipulator.cs new file mode 100644 index 0000000..a7fd6d8 --- /dev/null +++ b/ZuneModCore/Win32/TokenManipulator.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.InteropServices; + +namespace ZuneModCore.Win32 +{ + public class TokenManipulator + { + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, + ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); + [DllImport("kernel32.dll", ExactSpelling = true)] + internal static extern IntPtr GetCurrentProcess(); + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr + phtok); + [DllImport("advapi32.dll", SetLastError = true)] + internal static extern bool LookupPrivilegeValue(string host, string name, + ref long pluid); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TokPriv1Luid + { + public int Count; + public long Luid; + public int Attr; + } + internal const int SE_PRIVILEGE_DISABLED = 0x00000000; + internal const int SE_PRIVILEGE_ENABLED = 0x00000002; + internal const int TOKEN_QUERY = 0x00000008; + internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; + public static bool AddPrivilege(string privilege) + { + try + { + bool retVal; + TokPriv1Luid tp; + IntPtr hproc = GetCurrentProcess(); + IntPtr htok = IntPtr.Zero; + retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); + tp.Count = 1; + tp.Luid = 0; + tp.Attr = SE_PRIVILEGE_ENABLED; + retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); + retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); + return retVal; + } + catch + { + throw; + } + } + public static bool RemovePrivilege(string privilege) + { + try + { + bool retVal; + TokPriv1Luid tp; + IntPtr hproc = GetCurrentProcess(); + IntPtr htok = IntPtr.Zero; + retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); + tp.Count = 1; + tp.Luid = 0; + tp.Attr = SE_PRIVILEGE_DISABLED; + retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); + retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); + return retVal; + } + catch + { + throw; + } + } + } +} diff --git a/ZuneModCore/ZuneModCore.csproj b/ZuneModCore/ZuneModCore.csproj index f97362e..7d92783 100644 --- a/ZuneModCore/ZuneModCore.csproj +++ b/ZuneModCore/ZuneModCore.csproj @@ -9,6 +9,7 @@ +