Skip to content
Merged
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
40 changes: 32 additions & 8 deletions BedrockLauncher.Core/BedrockCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,29 @@ await ZipExtractor.ExtractWithProgressAsync(options.FileFullPath, options.Instal
}
return null;
}
private static async Task<Process> WaitForProcessAsync(string processName, DateTime startTime, TimeSpan timeout)
{
var stopwatch = Stopwatch.StartNew();

while (stopwatch.Elapsed < timeout)
{
var processes = Process.GetProcessesByName(processName);

if (processes.Length > 0)
{

return processes
.Where(p => p.StartTime > startTime)
.OrderBy(p => (p.StartTime - startTime).TotalMilliseconds)
.FirstOrDefault();
}


await Task.Delay(200);
}

return null;
}
/// <summary>
/// Launch the Minecraft game process based on the specified launch options
/// </summary>
Expand All @@ -253,14 +276,15 @@ public async Task<Process> LaunchGameAsync(LaunchOptions options)
if (options.MinecraftBuildType == MinecraftBuildTypeVersion.GDK)
{
options.Progress?.Report(LaunchState.Launching);
var info = new ProcessStartInfo();
info.FileName = Path.Combine(options.GameFolder, "Minecraft.Windows.exe");
info.Arguments = options.LaunchArgs;
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.WorkingDirectory = options.GameFolder;
process.StartInfo = info;
process.Start();
DateTime startTimestamp = DateTime.Now;

Process.Start(new ProcessStartInfo()
{
FileName = "explorer.exe",
Arguments = Path.Combine(options.GameFolder, "Minecraft.Windows.exe")
});

process = await WaitForProcessAsync("Minecraft.Windows", startTimestamp, TimeSpan.FromSeconds(10));
options.Progress?.Report(LaunchState.Launched);
}

Expand Down
4 changes: 2 additions & 2 deletions BedrockLauncher.Core/BedrockLauncher.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<Version>2.0.1-dev</Version>
<Version>2.0.2-dev</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<Version>2.0.1</Version>
<Version>2.0.2</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
176 changes: 176 additions & 0 deletions BedrockLauncher.Core/Utils/ExeLauncher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;

namespace BedrockLauncher.Core.Utils
{
public class ExeLauncher
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateProcessWithTokenW(
IntPtr hToken,
LogonFlags dwLogonFlags,
string lpApplicationName,
string lpCommandLine,
CreationFlags dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
SecurityImpersonationLevel ImpersonationLevel,
TokenType TokenType,
out IntPtr phNewToken);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}

private enum LogonFlags
{
LOGON_WITH_PROFILE = 0x00000001,
LOGON_NETCREDENTIALS_ONLY = 0x00000002
}

private enum CreationFlags
{
NORMAL_PRIORITY_CLASS = 0x00000020,
CREATE_NO_WINDOW = 0x08000000,
CREATE_UNICODE_ENVIRONMENT = 0x00000400
}

private enum TokenType
{
TokenPrimary = 1,
TokenImpersonation = 2
}

private enum SecurityImpersonationLevel
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}


public static Process LaunchWithLowPrivilege(string exePath, string arguments = "")
{
IntPtr userToken = IntPtr.Zero;
IntPtr duplicateToken = IntPtr.Zero;
PROCESS_INFORMATION procInfo = default;

try
{
if (!File.Exists(exePath))
throw new FileNotFoundException($" {exePath}");


using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
userToken = identity.Token;


if (!DuplicateTokenEx(
userToken,
0x02000000, // MAXIMUM_ALLOWED
IntPtr.Zero,
SecurityImpersonationLevel.SecurityIdentification,
TokenType.TokenPrimary,
out duplicateToken))
{
throw new Exception($"can't copy token: {Marshal.GetLastWin32Error()}");
}
}


STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = Marshal.SizeOf(startupInfo);
startupInfo.lpDesktop = "Winsta0\\Default";
startupInfo.dwFlags = 0x00000001; // STARTF_USESHOWWINDOW
startupInfo.wShowWindow = 1; // SW_SHOWNORMAL

string commandLine = $"\"{exePath}\" {arguments}";
string workingDir = Path.GetDirectoryName(exePath);


bool success = CreateProcessWithTokenW(
duplicateToken,
LogonFlags.LOGON_WITH_PROFILE,
null,
commandLine,
CreationFlags.NORMAL_PRIORITY_CLASS | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
IntPtr.Zero,
workingDir,
ref startupInfo,
out procInfo);

if (!success)
{
int error = Marshal.GetLastWin32Error();
throw new Exception($"failed to start {error}");
}


Process process = Process.GetProcessById(procInfo.dwProcessId);


if (procInfo.hProcess != IntPtr.Zero)
CloseHandle(procInfo.hProcess);
if (procInfo.hThread != IntPtr.Zero)
CloseHandle(procInfo.hThread);

return process;
}
catch (Exception ex)
{
return null;
}
finally
{

if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
}
}
}
}
10 changes: 6 additions & 4 deletions CoreTest/LaunchTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BedrockLauncher.Core;
using BedrockLauncher.Core.CoreOption;
using BedrockLauncher.Core.DependsComplete;
using BedrockLauncher.Core.Utils;

namespace CoreTest;

Expand All @@ -14,12 +15,13 @@ public void Test()
bedrockCore.InitAsync().Wait();
var launchOptions = new LaunchOptions()
{
GameFolder = Path.GetFullPath("D:\\Windows11\\newdesk\\Code\\bedrock_versions\\1.21.13101"),
GameFolder = Path.GetFullPath(""),
MinecraftBuildType = MinecraftBuildTypeVersion.GDK,
GameType = MinecraftGameTypeVersion.Release,
LaunchArgs = null
};
bedrockCore.LaunchGameAsync(launchOptions).Wait();
LaunchArgs = null
};
var process = bedrockCore.LaunchGameAsync(launchOptions).Result;
// ExeLauncher.LaunchWithLowPrivilege(@"E:\GlacieCrack\plugins\Glacie\bedrock_versions\1234\Minecraft.Windows.exe", "");
}
[TestMethod]
public void MsiTest()
Expand Down
Loading