IFPSTools.NET/uninno/Program.cs
2022-08-07 15:42:41 +01:00

226 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace uninno
{
internal class Program
{
private static void Usage()
{
Console.WriteLine("Usage: {0} compiledcode uninsdat --version <version>", Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location));
Console.WriteLine();
Console.WriteLine("uninno: Inno Setup Uninstaller Configuration Creator.");
Console.WriteLine("Creates an Inno Setup uninstaller configuration containing a compiled IFPS script.");
Console.WriteLine("This can be used with a signed Inno Setup uninstaller as a lolbin to execute custom scripts.");
Console.WriteLine();
Console.WriteLine("Additional arguments:");
Console.WriteLine("--platform 32|64 - Sets the uninstaller configuration platform (32-bit or 64-bit)");
Console.WriteLine("--appid <appid> - Sets the AppID in the configuration file");
Console.WriteLine("--appname <appname> - Sets the AppName in the configuration file");
Console.WriteLine("--fileversion <version> - File version number to use (default is 48). For a Unicode Inno Setup you might want to pass 1048.");
Console.WriteLine("--version <version> - [REQUIRED] Inno Setup version number, postfixed with 'u' for unicode: for example, 6.2.1u");
Console.WriteLine("--mimic <path> - Path to an Inno Setup uninstaller configuration to read the previous five arguments from");
Console.WriteLine("Example: --platform 32 --appid {8E14ADF3-1B18-4711-87BD-E3827D395466} --appname \"Microsoft Azure Storage Explorer\" --fileversion 1048 --version 6.0.5u");
Console.WriteLine("Example: --mimic=\"%localappdata%\\Programs\\Microsoft Azure Storage Explorer\\unins000.dat\"");
Console.WriteLine();
}
static void Main(string[] args)
{
if (args.Length < 2)
{
Usage();
return;
}
Dictionary<string, string> OptionalArgs = new Dictionary<string, string>();
int requiredArgsIndex = 0;
int optionalArgsIndex = 0;
if (args.Length > 2)
{
var optionalAtStart = args[0].StartsWith("--");
if (!optionalAtStart && !args[2].StartsWith("--"))
{
Usage();
return;
}
if (optionalAtStart)
{
for (int i = 0; i < args.Length; i++)
{
if (!args[i].StartsWith("--"))
{
requiredArgsIndex = i;
break;
}
if (i > args.Length - 3)
{
Usage();
return;
}
}
} else
{
optionalArgsIndex = 2;
}
var optionalArgsEnd = args.Length;
if (requiredArgsIndex > optionalArgsIndex) optionalArgsEnd = optionalArgsIndex;
for (int i = optionalArgsIndex; i < optionalArgsEnd; i++)
{
var arg = args[i].Substring(2);
switch (arg.ToLower())
{
case "platform":
case "appid":
case "appname":
case "fileversion":
case "version":
case "mimic":
OptionalArgs[arg] = args[i + 1];
i++;
break;
default:
Usage();
return;
}
}
}
var header = new LogHeader();
uint ExtraData = 0;
bool IsUnicode = false;
if (OptionalArgs.TryGetValue("mimic", out var mimic))
{
LogHeader mimicHeader;
try
{
using (var fs = File.OpenRead(mimic))
{
mimicHeader = LogHeader.LoadForMimic(fs);
var body = LogFile.ReadBody(fs);
using (var bodyReader = new BinaryReader(body, Encoding.UTF8, true))
{
while (body.Position < body.Length)
{
var type = bodyReader.Read<RecordType>();
var extra = bodyReader.Read<uint>();
var len = bodyReader.Read<uint>();
if (type != RecordType.CompiledCode)
{
body.Position += len;
continue;
}
ExtraData = extra;
IsUnicode = (extra & 0x80000000) != 0;
break;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error when loading {0}: {1}", mimic, ex);
Usage();
return;
}
header.Is64Bit = mimicHeader.Is64Bit;
header.AppId = mimicHeader.AppId;
header.AppName = mimicHeader.AppName;
header.Unmanaged.Version = mimicHeader.Unmanaged.Version;
}
else
{
if (OptionalArgs.TryGetValue("platform", out var platform))
{
header.Is64Bit = platform == "64";
if (platform != "32" && !header.Is64Bit)
{
Usage();
return;
}
}
header.AppId = header.AppName = "";
if (!OptionalArgs.TryGetValue("appid", out header.AppId)) header.AppId = "";
if (!OptionalArgs.TryGetValue("appname", out header.AppName)) header.AppName = "";
if (OptionalArgs.TryGetValue("fileversion", out var verString))
{
if (int.TryParse(verString, out var version)) header.Unmanaged.Version = version;
}
if (OptionalArgs.TryGetValue("version", out verString))
{
var versionSplit = verString.Split('.');
if (!uint.TryParse(versionSplit[0], out var one) || !uint.TryParse(versionSplit[1], out var two)) {
Usage();
return;
}
if (versionSplit[2].EndsWith("u"))
{
IsUnicode = true;
versionSplit[2] = versionSplit[2].Substring(0, versionSplit[2].Length - 1);
}
if (!uint.TryParse(versionSplit[2], out var three))
{
Usage();
return;
}
ExtraData = (one << 24) | (two << 16) | (three << 8) | (IsUnicode ? 0x80000000u : 0);
}
else
{
Usage();
return;
}
}
var file = new LogFile();
file.Header = header;
var ms = new MemoryStream();
{
// first: script
ms.WriteByte(0xfe);
var data = File.ReadAllBytes(args[requiredArgsIndex]);
var dataLen = data.Length;
// If Unicode Inno Setup, then the script length needs to be divisible by sizeof(char).
bool needsExtraByte = IsUnicode && (dataLen & 1) != 0;
if (needsExtraByte) dataLen++;
ms.Write(BitConverter.GetBytes(dataLen * (IsUnicode ? -1 : 1)), 0, sizeof(int));
ms.Write(data, 0, data.Length);
if (needsExtraByte) ms.WriteByte(0);
// second: leadbytes, size = 0x20
ms.WriteByte(0x20);
ms.Position += 0x20;
// 2-5: can be len=0
ms.WriteByte(0);
ms.WriteByte(0);
ms.WriteByte(0);
ms.WriteByte(0);
// 6: len=4, uint array count=0
ms.WriteByte(4);
ms.WriteByte(0);
ms.WriteByte(0);
ms.WriteByte(0);
ms.WriteByte(0);
}
file.Records.Add(new Record(RecordType.CompiledCode, ms.ToArray(), ExtraData));
ms = new MemoryStream();
var bw = new BinaryWriter(ms, Encoding.UTF8, true);
file.Save(bw);
ms.Position = 0;
using (var fs = File.OpenWrite(args[requiredArgsIndex + 1]))
ms.CopyTo(fs);
Console.WriteLine("Saved binary to {0}", args[requiredArgsIndex + 1]);
}
}
}