184 lines
5.3 KiB
C#
184 lines
5.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security;
|
|
|
|
namespace Server.Helper;
|
|
|
|
public static class IconInjector
|
|
{
|
|
[SuppressUnmanagedCodeSecurity]
|
|
private class NativeMethods
|
|
{
|
|
[DllImport("kernel32")]
|
|
public static extern IntPtr BeginUpdateResource(string fileName, [MarshalAs(UnmanagedType.Bool)] bool deleteExistingResources);
|
|
|
|
[DllImport("kernel32")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool UpdateResource(IntPtr hUpdate, IntPtr type, IntPtr name, short language, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] byte[] data, int dataSize);
|
|
|
|
[DllImport("kernel32")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool EndUpdateResource(IntPtr hUpdate, [MarshalAs(UnmanagedType.Bool)] bool discard);
|
|
}
|
|
|
|
private struct ICONDIR
|
|
{
|
|
public ushort Reserved;
|
|
|
|
public ushort Type;
|
|
|
|
public ushort Count;
|
|
}
|
|
|
|
private struct ICONDIRENTRY
|
|
{
|
|
public byte Width;
|
|
|
|
public byte Height;
|
|
|
|
public byte ColorCount;
|
|
|
|
public byte Reserved;
|
|
|
|
public ushort Planes;
|
|
|
|
public ushort BitCount;
|
|
|
|
public int BytesInRes;
|
|
|
|
public int ImageOffset;
|
|
}
|
|
|
|
private struct BITMAPINFOHEADER
|
|
{
|
|
public uint Size;
|
|
|
|
public int Width;
|
|
|
|
public int Height;
|
|
|
|
public ushort Planes;
|
|
|
|
public ushort BitCount;
|
|
|
|
public uint Compression;
|
|
|
|
public uint SizeImage;
|
|
|
|
public int XPelsPerMeter;
|
|
|
|
public int YPelsPerMeter;
|
|
|
|
public uint ClrUsed;
|
|
|
|
public uint ClrImportant;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
|
private struct GRPICONDIRENTRY
|
|
{
|
|
public byte Width;
|
|
|
|
public byte Height;
|
|
|
|
public byte ColorCount;
|
|
|
|
public byte Reserved;
|
|
|
|
public ushort Planes;
|
|
|
|
public ushort BitCount;
|
|
|
|
public int BytesInRes;
|
|
|
|
public ushort ID;
|
|
}
|
|
|
|
private class IconFile
|
|
{
|
|
private ICONDIR iconDir;
|
|
|
|
private ICONDIRENTRY[] iconEntry;
|
|
|
|
private byte[][] iconImage;
|
|
|
|
public int ImageCount => iconDir.Count;
|
|
|
|
public byte[] ImageData(int index)
|
|
{
|
|
return iconImage[index];
|
|
}
|
|
|
|
public static IconFile FromFile(string filename)
|
|
{
|
|
IconFile iconFile = new IconFile();
|
|
byte[] array = File.ReadAllBytes(filename);
|
|
GCHandle gCHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
|
iconFile.iconDir = (ICONDIR)Marshal.PtrToStructure(gCHandle.AddrOfPinnedObject(), typeof(ICONDIR));
|
|
iconFile.iconEntry = new ICONDIRENTRY[iconFile.iconDir.Count];
|
|
iconFile.iconImage = new byte[iconFile.iconDir.Count][];
|
|
int num = Marshal.SizeOf(iconFile.iconDir);
|
|
Type typeFromHandle = typeof(ICONDIRENTRY);
|
|
int num2 = Marshal.SizeOf(typeFromHandle);
|
|
for (int i = 0; i <= iconFile.iconDir.Count - 1; i++)
|
|
{
|
|
ICONDIRENTRY iCONDIRENTRY = (ICONDIRENTRY)Marshal.PtrToStructure(new IntPtr(gCHandle.AddrOfPinnedObject().ToInt64() + num), typeFromHandle);
|
|
iconFile.iconEntry[i] = iCONDIRENTRY;
|
|
iconFile.iconImage[i] = new byte[iCONDIRENTRY.BytesInRes];
|
|
Buffer.BlockCopy(array, iCONDIRENTRY.ImageOffset, iconFile.iconImage[i], 0, iCONDIRENTRY.BytesInRes);
|
|
num += num2;
|
|
}
|
|
gCHandle.Free();
|
|
return iconFile;
|
|
}
|
|
|
|
public byte[] CreateIconGroupData(uint iconBaseID)
|
|
{
|
|
byte[] array = new byte[Marshal.SizeOf(typeof(ICONDIR)) + Marshal.SizeOf(typeof(GRPICONDIRENTRY)) * ImageCount];
|
|
GCHandle gCHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
|
Marshal.StructureToPtr(iconDir, gCHandle.AddrOfPinnedObject(), fDeleteOld: false);
|
|
int num = Marshal.SizeOf(iconDir);
|
|
for (int i = 0; i <= ImageCount - 1; i++)
|
|
{
|
|
GRPICONDIRENTRY structure = default(GRPICONDIRENTRY);
|
|
BITMAPINFOHEADER bITMAPINFOHEADER = default(BITMAPINFOHEADER);
|
|
GCHandle gCHandle2 = GCHandle.Alloc(bITMAPINFOHEADER, GCHandleType.Pinned);
|
|
Marshal.Copy(ImageData(i), 0, gCHandle2.AddrOfPinnedObject(), Marshal.SizeOf(typeof(BITMAPINFOHEADER)));
|
|
gCHandle2.Free();
|
|
structure.Width = iconEntry[i].Width;
|
|
structure.Height = iconEntry[i].Height;
|
|
structure.ColorCount = iconEntry[i].ColorCount;
|
|
structure.Reserved = iconEntry[i].Reserved;
|
|
structure.Planes = bITMAPINFOHEADER.Planes;
|
|
structure.BitCount = bITMAPINFOHEADER.BitCount;
|
|
structure.BytesInRes = iconEntry[i].BytesInRes;
|
|
structure.ID = Convert.ToUInt16(iconBaseID + i);
|
|
Marshal.StructureToPtr(structure, new IntPtr(gCHandle.AddrOfPinnedObject().ToInt64() + num), fDeleteOld: false);
|
|
num += Marshal.SizeOf(typeof(GRPICONDIRENTRY));
|
|
}
|
|
gCHandle.Free();
|
|
return array;
|
|
}
|
|
}
|
|
|
|
public static void InjectIcon(string exeFileName, string iconFileName)
|
|
{
|
|
InjectIcon(exeFileName, iconFileName, 1u, 1u);
|
|
}
|
|
|
|
public static void InjectIcon(string exeFileName, string iconFileName, uint iconGroupID, uint iconBaseID)
|
|
{
|
|
IconFile iconFile = IconFile.FromFile(iconFileName);
|
|
IntPtr hUpdate = NativeMethods.BeginUpdateResource(exeFileName, deleteExistingResources: false);
|
|
byte[] array = iconFile.CreateIconGroupData(iconBaseID);
|
|
NativeMethods.UpdateResource(hUpdate, new IntPtr(14L), new IntPtr(iconGroupID), 0, array, array.Length);
|
|
for (int i = 0; i <= iconFile.ImageCount - 1; i++)
|
|
{
|
|
byte[] array2 = iconFile.ImageData(i);
|
|
NativeMethods.UpdateResource(hUpdate, new IntPtr(3L), new IntPtr(iconBaseID + i), 0, array2, array2.Length);
|
|
}
|
|
NativeMethods.EndUpdateResource(hUpdate, discard: false);
|
|
}
|
|
}
|