Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor all Win32 API references with CsWin32 PInvoke code generator (work in progress) #351

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/docs/usage/scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
::sidebar_position: 1
id: scripts
title: Usage on scripts
hide_title: true
---
## Scripts

This folder contains sample scripts demonstrating how to use `gsudo` to elevate privileges on a Windows machine. The provided scripts include:

1. **Script Self-Elevation:** A technique to ensure that the current script is running elevated, by detecting when it is not, and elevating itself. More details can be found in the [gsudo documentation on script self-elevation](https://gerardog.github.io/gsudo/docs/tips/script-self-elevation).

- [`self-elevate.cmd`](./self-elevate.cmd): Batch script version
- [`self-elevate-one-liner.cmd`](./self-elevate-one-liner.cmd): A one-liner version of the batch script.
- [`self-elevate.ps1`](./self-elevate.ps1): PowerShell version.
- [`self-elevate-without-gsudo.cmd`](./self-elevate-without-gsudo.cmd): Example without using `gsudo`.

2. **Many Elevations Using gsudo Cache:**

- [`many-elevations-using-gsudo-cache.cmd`](./many-elevations-using-gsudo-cache.cmd): Batch script version
- [`many-elevations-using-gsudo-cache.ps1`](./many-elevations-using-gsudo-cache.ps1): PowerShell version

3. **Don't show an UAC pop-up:** Perform an elevated admin task if and only if it can be done without an interactive UAC pop-up. (i.e. when already elevated or gsudo cache is active)
- [`silent-elevation-one-liner.cmd`](./silent-elevation-one-liner.cmd): A one-liner version.
- [`silent-elevation.cmd`](./silent-elevation.cmd): Verbose version .

These scripts are examples and should be used with caution. Always review and understand the code you are executing on your system.
13 changes: 7 additions & 6 deletions src/gsudo/Commands/CtrlCCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
using Windows.Win32;
using static gsudo.Native.ConsoleApi;

namespace gsudo.Commands
{
// Required for sending Ctrl-C / Ctrl-Break to the elevated process on VT & piped Mode.
class CtrlCCommand : ICommand
{
public int Pid { get; set; }
public uint Pid { get; set; }

public bool SendSigBreak { get; set; }

public Task<int> Execute()
{
FreeConsole();
PInvoke.FreeConsole();

if (AttachConsole(Pid))
if (PInvoke.AttachConsole(Pid))
{
if (SendSigBreak)
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_BREAK_EVENT, 0);
PInvoke.GenerateConsoleCtrlEvent((uint)CtrlTypes.CTRL_BREAK_EVENT, 0);
else
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
PInvoke.GenerateConsoleCtrlEvent((uint)CtrlTypes.CTRL_C_EVENT, 0);

FreeConsole();
PInvoke.FreeConsole();
}
else
{
Expand Down
7 changes: 4 additions & 3 deletions src/gsudo/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;

using Windows.Win32;

namespace gsudo.Commands
{
public class RunCommand : ICommand
Expand Down Expand Up @@ -62,7 +63,7 @@ public async Task<int> Execute()
NewWindow = InputArguments.NewWindow,
Wait = (!commandBuilder.IsWindowsApp && !InputArguments.NewWindow) || InputArguments.Wait,
Mode = elevationMode,
ConsoleProcessId = Process.GetCurrentProcess().Id,
ConsoleProcessId = (uint) Process.GetCurrentProcess().Id,
IntegrityLevel = InputArguments.GetIntegrityLevel(),
ConsoleWidth = consoleWidth,
ConsoleHeight = consoleHeight,
Expand Down Expand Up @@ -154,7 +155,7 @@ private static int RunWithoutService(ElevationRequest elevationRequest)
{
var sameIntegrity = (int)InputArguments.GetIntegrityLevel() == SecurityHelper.GetCurrentIntegrityLevel();
// No need to escalate. Run in-process
Native.ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true);
PInvoke.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true);

ConsoleHelper.SetPrompt(elevationRequest);

Expand Down
2 changes: 1 addition & 1 deletion src/gsudo/ElevationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ElevationRequest
public int ConsoleWidth { get; set; }
public int ConsoleHeight { get; set; }
public ConsoleMode Mode { get; set; }
public int ConsoleProcessId { get; set; }
public uint ConsoleProcessId { get; set; }
public int TargetProcessId { get; set; }
public bool KillCache { get; set; }
public IntegrityLevel IntegrityLevel { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions src/gsudo/Helpers/ArgumentsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Windows.Win32;

namespace gsudo.Helpers
{
Expand Down Expand Up @@ -40,8 +41,7 @@ public static IList<string> SplitArgs(string args)

internal static string GetRealCommandLine()
{
System.IntPtr ptr = ConsoleApi.GetCommandLine();
string commandLine = Marshal.PtrToStringAuto(ptr).TrimStart();
string commandLine = PInvoke.GetCommandLine().ToString().TrimStart();

if (commandLine[0] == '"')
return commandLine.Substring(commandLine.IndexOf('"', 1) + 1).TrimStart(' ');
Expand Down
2 changes: 1 addition & 1 deletion src/gsudo/Helpers/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private ICommand ParseVerb()
if (arg.In("gsudoctrlc"))
return new CtrlCCommand()
{
Pid = int.Parse(DeQueueArg(), CultureInfo.InvariantCulture),
Pid = uint.Parse(DeQueueArg(), CultureInfo.InvariantCulture),
SendSigBreak = bool.Parse(DeQueueArg())
};

Expand Down
57 changes: 33 additions & 24 deletions src/gsudo/Helpers/ConsoleHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,34 @@
using System.Runtime.InteropServices;
using System.Security;
using static gsudo.Native.ConsoleApi;

using Windows.Win32;
using Windows.Win32.System.Console;
using Microsoft.Win32.SafeHandles;
using Windows.Win32.Foundation;

namespace gsudo.Helpers
{
class ConsoleHelper
{
{
const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;

Check warning on line 16 in src/gsudo/Helpers/ConsoleHelper.cs

View workflow job for this annotation

GitHub Actions / Test

Unused field 'ATTACH_PARENT_PROCESS'. (https://docs.microsoft.com/visualstudio/code-quality/ca1823-avoid-unused-private-fields)

Check warning on line 16 in src/gsudo/Helpers/ConsoleHelper.cs

View workflow job for this annotation

GitHub Actions / Test

Unused field 'ATTACH_PARENT_PROCESS'. (https://docs.microsoft.com/visualstudio/code-quality/ca1823-avoid-unused-private-fields)

static ConsoleHelper()
{
IgnoreConsoleCancelKeyPress += IgnoreConsoleCancelKeyPressMethod; // ensure no garbage collection
}

public static bool EnableVT()
public static unsafe bool EnableVT()
{
var hStdOut = Native.ConsoleApi.GetStdHandle(Native.ConsoleApi.STD_OUTPUT_HANDLE);
if (!Native.ConsoleApi.GetConsoleMode(hStdOut, out uint outConsoleMode))
var hStdOut = PInvoke.GetStdHandle(STD_HANDLE.STD_OUTPUT_HANDLE);
CONSOLE_MODE outConsoleMode;
if (!PInvoke.GetConsoleMode(hStdOut, &outConsoleMode))
{
Logger.Instance.Log("Could not get console mode", LogLevel.Debug);
return false;
}

outConsoleMode |= Native.ConsoleApi.ENABLE_VIRTUAL_TERMINAL_PROCESSING;// | Native.ConsoleApi.DISABLE_NEWLINE_AUTO_RETURN;
if (!Native.ConsoleApi.SetConsoleMode(hStdOut, outConsoleMode))
outConsoleMode |= CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING;// | CONSOLE_MODE.DISABLE_NEWLINE_AUTO_RETURN;
if (!PInvoke.SetConsoleMode(hStdOut, outConsoleMode))
{
Logger.Instance.Log("Could not enable virtual terminal processing", LogLevel.Error);
return false;
Expand All @@ -34,58 +41,60 @@
return true;
}

internal static SetConsoleCtrlEventHandler IgnoreConsoleCancelKeyPress;
internal static PHANDLER_ROUTINE IgnoreConsoleCancelKeyPress;

private static bool IgnoreConsoleCancelKeyPressMethod(CtrlTypes ctrlType)
private static BOOL IgnoreConsoleCancelKeyPressMethod(uint ctrlType)
{
if (ctrlType.In(CtrlTypes.CTRL_C_EVENT, CtrlTypes.CTRL_BREAK_EVENT))
if (ctrlType == (uint)CtrlTypes.CTRL_C_EVENT || ctrlType == (uint)CtrlTypes.CTRL_BREAK_EVENT)
return true;

return false;
}

public static uint[] GetConsoleAttachedPids()
{
var processIds = new uint[1];
var num = ConsoleApi.GetConsoleProcessList(processIds, 1);
var processIds = new uint[1].AsSpan();
var num = PInvoke.GetConsoleProcessList(processIds);
if (num == 0) throw new System.ComponentModel.Win32Exception();

processIds = new uint[num];
processIds = new uint[num].AsSpan();

num = ConsoleApi.GetConsoleProcessList(processIds, num);
num = PInvoke.GetConsoleProcessList(processIds);
if (num == 0) throw new System.ComponentModel.Win32Exception();

//** weird workaround for .net 7.0 NativeAOT + git-bash **
if (processIds[0] == 0)
num = ConsoleApi.GetConsoleProcessList(processIds, num);
num = PInvoke.GetConsoleProcessList(processIds);
if (processIds[0] == 0)
num = ConsoleApi.GetConsoleProcessList(processIds, num);
num = PInvoke.GetConsoleProcessList(processIds);
//**************************************************
return processIds;
return processIds.ToArray();
}

public static void GetConsoleInfo(out int width, out int height, out int cursorLeftPos, out int cursorTopPos)
{
if (Console.IsOutputRedirected && Console.IsErrorRedirected)
{
var hConsole = Native.FileApi.CreateFile("CONOUT$",
FileApi.GENERIC_READ, 0, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
var hConsole = PInvoke.CreateFile("CONOUT$", FileApi.GENERIC_READ,
Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE.FILE_SHARE_NONE, null,
Windows.Win32.Storage.FileSystem.FILE_CREATION_DISPOSITION.OPEN_EXISTING,
0, null);

if (hConsole == Native.FileApi.INVALID_HANDLE_VALUE)
if (hConsole.IsInvalid)
throw new System.ComponentModel.Win32Exception();

var consoleScreenBufferInfoEx = new CONSOLE_SCREEN_BUFFER_INFO_EX();
consoleScreenBufferInfoEx.cbSize = Marshal.SizeOf<CONSOLE_SCREEN_BUFFER_INFO_EX>();
var consoleScreenBufferInfoEx = new CONSOLE_SCREEN_BUFFER_INFOEX();
consoleScreenBufferInfoEx.cbSize = (uint)Marshal.SizeOf<CONSOLE_SCREEN_BUFFER_INFOEX>();

if (!GetConsoleScreenBufferInfoEx(hConsole, ref consoleScreenBufferInfoEx))
if (!PInvoke.GetConsoleScreenBufferInfoEx(hConsole, ref consoleScreenBufferInfoEx))
throw new System.ComponentModel.Win32Exception();

width = consoleScreenBufferInfoEx.srWindow.Right - consoleScreenBufferInfoEx.srWindow.Left + 1;
height = consoleScreenBufferInfoEx.srWindow.Bottom - consoleScreenBufferInfoEx.srWindow.Top + 1;
cursorLeftPos = consoleScreenBufferInfoEx.dwCursorPosition.X;
cursorTopPos = consoleScreenBufferInfoEx.dwCursorPosition.Y;

FileApi.CloseHandle(hConsole);
hConsole.Close();
}
else
{
Expand Down
10 changes: 6 additions & 4 deletions src/gsudo/Helpers/ProcessFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Windows.Win32;
using Windows.Win32.System.Console;
using static gsudo.Native.ProcessApi;
using static gsudo.Native.TokensApi;

Expand Down Expand Up @@ -319,9 +321,9 @@ private static SafeProcessHandle CreateProcessWithToken(IntPtr newToken, string
if (Console.IsErrorRedirected | Console.IsInputRedirected | Console.IsOutputRedirected)
{
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
startupInfo.hStdOutput = ConsoleApi.GetStdHandle(ConsoleApi.STD_OUTPUT_HANDLE);
startupInfo.hStdInput = ConsoleApi.GetStdHandle(ConsoleApi.STD_INPUT_HANDLE);
startupInfo.hStdError = ConsoleApi.GetStdHandle(ConsoleApi.STD_ERROR_HANDLE);
startupInfo.hStdOutput = PInvoke.GetStdHandle(STD_HANDLE.STD_OUTPUT_HANDLE);
startupInfo.hStdInput = PInvoke.GetStdHandle(STD_HANDLE.STD_INPUT_HANDLE);
startupInfo.hStdError = PInvoke.GetStdHandle(STD_HANDLE.STD_ERROR_HANDLE);
}

PROCESS_INFORMATION processInformation;
Expand Down Expand Up @@ -360,7 +362,7 @@ internal static void CreateProcessForTokenReplacement(string lpApplicationName,
Logger.Instance.Log($"Creating target process: {lpApplicationName} {args}", LogLevel.Debug);
if (!ProcessApi.CreateProcess(null, command, ref pSec, ref tSec, false, dwCreationFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo))
{
throw new Win32Exception((int)ConsoleApi.GetLastError());
throw new Win32Exception();
}

var currentProcessHandle = ProcessApi.GetCurrentProcess();
Expand Down
2 changes: 1 addition & 1 deletion src/gsudo/Helpers/ProcessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public static AutoResetEvent GetProcessWaitHandle(IntPtr processHandle) =>
SafeWaitHandle = new SafeWaitHandle(processHandle, ownsHandle: false)
};

public static SafeProcessHandle GetSafeProcessHandle(this Process p) => new SafeProcessHandle(p.Handle, true);
public static SafeProcessHandle GetSafeProcessHandle(this Process p) => p.SafeHandle;

public static AutoResetEvent GetProcessWaitHandle(this SafeProcessHandle processHandle) =>
new AutoResetEvent(false)
Expand Down
49 changes: 26 additions & 23 deletions src/gsudo/Helpers/SymbolicLinkSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

using Windows.Win32;

namespace gsudo.Helpers
{
static class SymbolicLinkSupport
Expand Down Expand Up @@ -48,33 +49,35 @@ public static string ResolveSymbolicLink(string symLinkFullPath)
}
private static string GetFinalPathName(string path)
{
var h = Native.FileApi.CreateFile(path,
using (var h = PInvoke.CreateFile(path,
Native.FileApi.FILE_READ_EA,
FileShare.ReadWrite | FileShare.Delete,
IntPtr.Zero,
FileMode.Open,
Native.FileApi.FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero);
Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE.FILE_SHARE_READ | Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE.FILE_SHARE_WRITE | Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE.FILE_SHARE_DELETE,
null,
Windows.Win32.Storage.FileSystem.FILE_CREATION_DISPOSITION.OPEN_EXISTING,
Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS,
null))
{
if (h.IsInvalid)
return path;

if (h == Native.FileApi.INVALID_HANDLE_VALUE)
return path;
uint res;

try
{
var sb = new StringBuilder(1024);
var res = Native.FileApi.GetFinalPathNameByHandle(h, sb, 1024, 0);
Span<char> text = stackalloc char[1024]; // value gotten from GetWindowTextLength
unsafe
{
fixed (char* pText = text)
{
res = PInvoke.GetFinalPathNameByHandle(h, pText, 1024, 0);
}

if (res == 0)
{
Logger.Instance.Log($"{nameof(SymbolicLinkSupport)}.{nameof(GetFinalPathName)} failed with: {new Win32Exception()}", LogLevel.Debug);
return path; // Sad workaround: do not resolve the symlink.
}
if (res == 0)
{
Logger.Instance.Log($"{nameof(SymbolicLinkSupport)}.{nameof(GetFinalPathName)} failed with: {new Win32Exception()}", LogLevel.Debug);
return path; // Sad workaround: do not resolve the symlink.
}

return sb.ToString();
}
finally
{
Native.FileApi.CloseHandle(h);
return text.Slice(0, (int)res).ToString();
}
}
}
}
Expand Down
Loading
Loading