galaxy brain 2: bit of restructure (Bootstrap)

This commit is contained in:
Mino 2020-03-11 11:52:51 +09:00
parent 2438a89867
commit 350d2961c1
19 changed files with 345 additions and 136 deletions

View file

@ -5,16 +5,13 @@ using CoreHook.BinaryInjection.RemoteInjection;
using CoreHook.BinaryInjection.RemoteInjection.Configuration;
using CoreHook.IPC.Platform;
namespace Dalamud.Injector
namespace Dalamud.Bootstrap
{
public sealed partial class DalamudLauncher
public sealed class Bootstrapper
{
private readonly DalamudLauncherOptions m_options;
}
private readonly BootstrapperOptions m_options;
public sealed partial class DalamudLauncher
{
public DalamudLauncher(DalamudLauncherOptions options)
public Bootstrapper(BootstrapperOptions options)
{
m_options = options;
}
@ -46,10 +43,17 @@ namespace Dalamud.Injector
// Acquire the process handle and read the command line
using var process = Process.Open(pid);
var commandLines = process.ReadCommandLine();
// ....
var arguments = process.ReadCommandLine();
// TODO:
// .... if arg1 exists
// DecodeSqexArg(arguments[1]);
// args = ParseArgument()
// FindArguments(args, "T")
// RemoveArgs(args, "T")
// AddArgs(args, "T", newTick)
// str = ToString()
// EncodeSqexArg(str, newKey)
process.Terminate();
@ -90,7 +94,7 @@ namespace Dalamud.Injector
// Could not inject Dalamud for whatever reason; it could be process is not actually running, insufficient os privilege, or whatever the thing SE put in their game;
// Therefore there's not much we can do on this side; You have to trobleshoot by yourself somehow.
throw new DalamudLauncherException(pid, message, ex);
throw new BootstrapException(pid, message, ex);
}
}
}

View file

@ -1,11 +1,11 @@
using System;
using System.IO;
namespace Dalamud.Injector
namespace Dalamud.Bootstrap
{
public sealed class DalamudLauncherOptions
public sealed class BootstrapperOptions
{
public static DalamudLauncherOptions Default => new DalamudLauncherOptions
public static BootstrapperOptions Default => new BootstrapperOptions
{
RootDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Dalamud"),
BinaryDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Dalamud", "bin"),

View file

@ -1,15 +1,8 @@
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
namespace Dalamud.Injector.Crypto
namespace Dalamud.Bootstrap.Crypto
{
/// <summary>
/// A struct that implements Blowfish algorithm.
/// </summary>
/// <remarks>
///
/// </remarks>
internal unsafe partial struct BlowfishState
{
// References:
@ -369,12 +362,12 @@ namespace Dalamud.Injector.Crypto
}
}
internal sealed partial class BlowfishLe
internal sealed partial class Blowfish
{
private BlowfishState m_state;
}
internal sealed partial class BlowfishLe
internal sealed partial class Blowfish
{
/// <summary>
/// Initializes a new instance of the Blowfish class.
@ -384,12 +377,36 @@ namespace Dalamud.Injector.Crypto
/// </remarks>
/// <param name="key">A secret key used for blowfish. Key length must be between 32 and 448 bits.</param>
/// <exception cref="ArgumentException">Length of the key is either too short or too long.</exception>
public BlowfishLe(ReadOnlySpan<byte> key)
public Blowfish(ReadOnlySpan<byte> key)
{
m_state = new BlowfishState(key);
}
public unsafe void EncryptBlockUnsafe(byte* input, byte* output)
public void EncryptInPlace(Span<byte> buffer)
{
// TODO: this is shit
}
public void DecryptInPlace(Span<byte> buffer)
{
if (buffer.Length % 8 != 0)
{
throw new ArgumentException("TODO: buffer length", nameof(buffer));
}
unsafe
{
fixed (byte* pBuffer = buffer)
{
for (byte* it = pBuffer; it < pBuffer + buffer.Length; it += 8)
{
DecryptBlockInPlace(it, it);
}
}
}
}
private unsafe void EncryptBlockInPlace(byte* input, byte* output)
{
var inputBlock = (uint*)input;
var outputBlock = (uint*)output;
@ -417,7 +434,7 @@ namespace Dalamud.Injector.Crypto
outputBlock[1] = xr;
}
public unsafe void DecryptBlockUnsafe(byte* input, byte* output)
private unsafe void DecryptBlockInPlace(byte* input, byte* output)
{
var inputBlock = (uint*)input;
var outputBlock = (uint*)output;

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>preview</LangVersion>
<nullable>enable</nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\lib\CoreHook\src\CoreHook.BinaryInjection\CoreHook.BinaryInjection.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,21 @@
using System;
namespace Dalamud.Bootstrap
{
/// <summary>
/// An error that is thrown when bootstraping Dalamud failed.
/// </summary>
public class BootstrapException : Exception
{
internal BootstrapException() : base() { }
internal BootstrapException(string message) : base(message) { }
internal BootstrapException(string message, Exception innerException) : base(message, innerException) { }
}
public class NtException : BootstrapException
{
}
}

View file

@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Text;
namespace Dalamud.Bootstrap.SqexArg
{
internal sealed class ArgumentBuilder
{
private readonly Dictionary<string, string> m_dict;
public ArgumentBuilder()
{
m_dict = new Dictionary<string, string>();
}
public ArgumentBuilder Add(string key, string value)
{
m_dict.Add(key, value);
return this;
}
public ArgumentBuilder Clear()
{
m_dict.Clear();
return this;
}
private static void Write(StringBuilder buffer, string key, string value)
{
var escapedKey = EscapeValue(key);
var escapedvalue = EscapeValue(value);
buffer.Append($" /{escapedKey} ={escapedvalue}");
}
private static string EscapeValue(string value)
{
// a single space character is represented as dobule spaces
return value.Replace(" ", " ");
}
public override string ToString()
{
var buffer = new StringBuilder(256);
foreach (var kv in m_dict)
{
Write(buffer, kv.Key, kv.Value);
}
return buffer.ToString();
}
}
}

View file

@ -0,0 +1,47 @@
using System;
namespace Dalamud.Bootstrap.SqexArg
{
internal static class ArgumentDecoder
{
public static void Decode(ReadOnlySpan<char> argument, uint key)
{
// 1. strip //**sqex003 and **//
// 2. extract checksum
// 3. deduce upper nibble key
// 4.
// //**c**//
if (argument.Length <= 9)
{
throw new ArgumentException(nameof(argument));
}
if (!argument.StartsWith("//**") || !argument.EndsWith("**//"))
{
throw new ArgumentException(nameof(argument));
}
var payload = argument[4..^5];
var checksum = argument[^5];
// undo url safe
//payload.re
// stuff
}
private static void DecodeUrlSafeBase64(ReadOnlySpan<char> content)
{
var buffer = new byte[(content.Length / 3) * 4];
if (!Convert.TryFromBase64Chars(payload, buffer, out var _))
{
// TODO
}
}
}
}

View file

@ -1,10 +1,10 @@
using Dalamud.Injector.Windows;
using Dalamud.Bootstrap.Windows;
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Dalamud.Injector
namespace Dalamud.Bootstrap
{
/// <summary>
/// TODO
@ -12,10 +12,7 @@ namespace Dalamud.Injector
internal sealed partial class Process : IDisposable
{
private SafeProcessHandle m_handle;
}
internal sealed partial class Process
{
/// <summary>
/// Creates a process object that can be used to manipulate process's internal state.
/// </summary>
@ -35,6 +32,7 @@ namespace Dalamud.Injector
{
const PROCESS_ACCESS_RIGHT access = PROCESS_ACCESS_RIGHT.PROCESS_VM_OPERATION
| PROCESS_ACCESS_RIGHT.PROCESS_VM_READ
// | PROCESS_ACCESS_RIGHT.PROCESS_VM_WRITE // we don't need it for now
| PROCESS_ACCESS_RIGHT.PROCESS_QUERY_LIMITED_INFORMATION
| PROCESS_ACCESS_RIGHT.PROCESS_QUERY_INFORMATION
| PROCESS_ACCESS_RIGHT.PROCESS_CREATE_THREAD
@ -77,7 +75,7 @@ namespace Dalamud.Injector
throw new Win32Exception();
}
// this is okay as Span<byte> can't really be longer than int.Max
// this is okay as the length of the span can't be longer than int.Max
return (int)bytesRead;
}
}
@ -108,13 +106,6 @@ namespace Dalamud.Injector
return buffer;
}
private ref T ReadMemoryValue<T>(IntPtr address, Span<byte> buffer) where T : unmanaged
{
ReadMemoryExact(address, buffer);
return ref MemoryMarshal.AsRef<T>(buffer);
}
private T ReadMemoryValue<T>(IntPtr address) where T : unmanaged
{
unsafe
@ -123,7 +114,7 @@ namespace Dalamud.Injector
Span<byte> buffer = stackalloc byte[sizeof(T)];
ReadMemoryExact(address, buffer);
// I reckon this is still far better than allocating on the heap when T is small enough.
// this is still far better than allocating the temporary buffer on the heap when sizeof(T) is small enough.
return MemoryMarshal.Read<T>(buffer);
}
}
@ -153,7 +144,7 @@ namespace Dalamud.Injector
var peb = ReadMemoryValue<PEB>(pebAddr);
var procParam = ReadMemoryValue<RTL_USER_PROCESS_PARAMETERS>(peb.ProcessParameters);
// Read the command line (utf16-like string)
// Read the command line (which is utf16-like string)
var commandLine = ReadMemoryExact(procParam.CommandLine.Buffer, procParam.CommandLine.Length);
return ParseCommandLine(commandLine);
}
@ -168,6 +159,8 @@ namespace Dalamud.Injector
fixed (byte* pCommandLine = commandLine)
{
// TODO: maybe explain why we can't just call .Split(' ')
// https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
argv = Win32.CommandLineToArgvW(pCommandLine, &argc);
}

View file

@ -2,7 +2,7 @@ using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
namespace Dalamud.Injector.Windows
namespace Dalamud.Bootstrap.Windows
{
internal static unsafe class Win32
{

View file

@ -4,12 +4,11 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>preview</LangVersion>
<nullable>enable</nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.7.82" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\lib\CoreHook\src\CoreHook.BinaryInjection\CoreHook.BinaryInjection.csproj" />
<ProjectReference Include="..\Dalamud.Bootstrap\Dalamud.Bootstrap.csproj" />
</ItemGroup>
</Project>

View file

@ -1,29 +0,0 @@
using System;
namespace Dalamud.Injector
{
/// <summary>
/// An error that is thrown when injecting Dalamud into the process failed.
/// </summary>
public sealed partial class DalamudLauncherException : Exception
{
/// <summary>
/// A target process id that was attempted to.
/// </summary>
public uint ProcessId { get; }
}
public sealed partial class DalamudLauncherException
{
public DalamudLauncherException() : base() { }
public DalamudLauncherException(string message) : base(message) { }
public DalamudLauncherException(string message, Exception inner) : base(message, inner) { }
public DalamudLauncherException(uint pid, string message, Exception inner) : base(message, inner)
{
ProcessId = pid;
}
}
}

View file

@ -0,0 +1,33 @@
using CommandLine;
namespace Dalamud.Injector
{
[Verb("inject")]
internal sealed class InjectOptions
{
[Option('p', "pid", Required = true, HelpText = "A target process id to inject.")]
public uint Pid { get; set; }
[Option("root", Required = false)]
public string? RootDirectory { get; set; }
[Option("bin", Required = false)]
public string? BinaryDirectory { get; set; }
}
[Verb("launch")]
internal sealed class LaunchOptions
{
[Value(0)]
public string ExecutablePath { get; set; } = null!;
[Value(1, Required = false)]
public string? CommandLine { get; set; }
[Option("root", Required = false)]
public string? RootDirectory { get; set; }
[Option("bin", Required = false)]
public string? BinaryDirectory { get; set; }
}
}

View file

@ -0,0 +1,52 @@
using System;
using System.IO;
using CommandLine;
using Dalamud.Bootstrap;
using static System.Environment;
namespace Dalamud.Injector
{
internal static class Program
{
private static void Main(string[] args)
{
Parser.Default.ParseArguments<InjectOptions, LaunchOptions>(args)
.WithParsed<InjectOptions>(Inject)
.WithParsed<LaunchOptions>(Launch);
}
private static void Inject(InjectOptions options)
{
var binDirectory = options.BinaryDirectory ?? GetDefaultBinaryDirectory();
var rootDirectory = options.RootDirectory ?? GetDefaultRootDirectory();
var boot = new Bootstrapper(new BootstrapperOptions
{
BinaryDirectory = binDirectory,
RootDirectory = rootDirectory,
});
// ..
boot.Relaunch(options.Pid);
}
private static void Launch(LaunchOptions options)
{
}
private static string GetDefaultBinaryDirectory()
{
var root = GetDefaultRootDirectory();
return Path.Combine(root, "bin");
}
private static string GetDefaultRootDirectory()
{
var localApp = Environment.GetFolderPath(SpecialFolder.LocalApplicationData);
return Path.Combine(localApp, "Dalamud");
}
}
}

View file

@ -1,18 +0,0 @@
using System;
using CommandLine;
namespace Dalamud.Injector
{
[Verb("inject")]
internal sealed class InjectOptions
{
[Option('p', "pid", Required = true, HelpText = "A target process id to inject.")]
public uint Pid { get; set; }
[Option("root", Required = true, HelpText = "")]
public string RootDirectory { get; set; } = null!;
[Option("bin", Required = true, HelpText = "")]
public string BinaryDirectory { get; set; } = null!;
}
}

View file

@ -1,20 +0,0 @@
using System;
using CommandLine;
using Dalamud.Injector.Windows;
namespace Dalamud.Injector
{
internal static class Program
{
private static void Main(string[] args)
{
var shit = Process.Open(12732);
var cmd = shit.ReadCommandLine();
/*Parser.Default.ParseArguments<InjectOptions>(args)
.WithParsed<InjectOptions>(opt =>
{
});*/
}
}
}

View file

@ -1,24 +0,0 @@
using System;
namespace Dalamud.Injector.Windows
{
/// <summary>
/// An exception that is thrown when the function call to ntdll failed.
/// </summary>
internal sealed class NtException : Exception
{
private const string DefaultMessage = "TODO: NtStatus failed message goes here";
public NtStatus Status { get; }
public NtException(NtStatus status) : base(DefaultMessage)
{
Status = status;
}
public NtException(NtStatus status, string message) : base(message)
{
Status = status;
}
}
}

View file

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>preview</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,14 @@
using System;
using Xunit;
namespace Dalamud.Testing
{
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
}

View file

@ -39,6 +39,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreHook.IPC", "lib\CoreHoo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreHook.Memory", "lib\CoreHook\src\CoreHook.Memory\CoreHook.Memory.csproj", "{ED4FFE13-5F83-42B9-8847-E7C4D7A988E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Bootstrap", "Dalamud.Bootstrap\Dalamud.Bootstrap.csproj", "{19032128-E336-460F-B6E4-EAF6055589E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Testing", "Dalamud.Testing\Dalamud.Testing.csproj", "{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -217,6 +221,30 @@ Global
{ED4FFE13-5F83-42B9-8847-E7C4D7A988E8}.Release|x64.Build.0 = Release|Any CPU
{ED4FFE13-5F83-42B9-8847-E7C4D7A988E8}.Release|x86.ActiveCfg = Release|Any CPU
{ED4FFE13-5F83-42B9-8847-E7C4D7A988E8}.Release|x86.Build.0 = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|x64.ActiveCfg = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|x64.Build.0 = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|x86.ActiveCfg = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Debug|x86.Build.0 = Debug|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|Any CPU.Build.0 = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|x64.ActiveCfg = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|x64.Build.0 = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|x86.ActiveCfg = Release|Any CPU
{19032128-E336-460F-B6E4-EAF6055589E5}.Release|x86.Build.0 = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|x64.ActiveCfg = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|x64.Build.0 = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|x86.ActiveCfg = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Debug|x86.Build.0 = Debug|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|Any CPU.Build.0 = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|x64.ActiveCfg = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|x64.Build.0 = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|x86.ActiveCfg = Release|Any CPU
{0A99A6B3-12E2-4197-A6F3-9211B6D4D824}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE