在C#中使用UpdateResource?

Nik*_*vić 6 .net c# resources icons

我正在尝试以编程方式更改外部可执行文件的图标.我用谷歌搜索,并使用C++找到有关此问题的大量信息.基本上,我需要使用BeginUpdateResource,UpdateResource和EndUpdateResource.问题是 - 我不知道在C#中传递给UpdateResource的内容.

这是我到目前为止的代码:

class IconChanger
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName,
        [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage,
        IntPtr lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    public ICResult ChangeIcon(string exeFilePath, byte[] iconData)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null)
            return ICResult.FailBegin;

        // Get language identifier
        CultureInfo currentCulture = CultureInfo.CurrentCulture;
        int pid = ((ushort)currentCulture.LCID) & 0x3ff;
        int sid = ((ushort)currentCulture.LCID) >> 10;
        ushort languageID = (ushort)((((ushort)pid) << 10) | ((ushort)sid));

        // Get pointer to data
        GCHandle iconHandle = GCHandle.Alloc(iconData, GCHandleType.Pinned);

        // Replace the icon
        if (UpdateResource(handleExe, "#3", "#1", languageID, iconHandle.AddrOfPinnedObject(), (uint)iconData.Length))
        {
            if (EndUpdateResource(handleExe, false))
                return ICResult.Success;
            else
                return ICResult.FailEnd;
        }
        else
            return ICResult.FailUpdate;
    }
}
Run Code Online (Sandbox Code Playgroud)

关于lpType - 在C++中,您传递RT_ICON(或RT_GROUP_ICON).我应该在C#中传递什么价值?lpName参数也是同样的问题.我不确定语言标识符(我在Internet上发现了这个),因为我无法测试它.我也不确定我是否提供了合适的图标数据.目前,iconData包含.ico文件中的字节.

有人能指出我正确的方向吗?

非常感谢你.

Han*_*ant 7

只是一些指示,这很难做对.通过说谎lpType参数传递RT_ICON.将其从字符串更改为IntPtr并传递(IntPtr)3.

lpData参数非常棘手.您需要按资源编译器(rc.exe)编译的方式传递数据.我不知道它是否会破坏.ico文件的原始数据.唯一合理的尝试是将带有FileStream的.ico文件中的数据读入byte [],您似乎已经这样做了.我认为该功能实际上是为了将资源从一个二进制映像复制到另一个二进制映像.你的方法工作的几率不是零.

你也忽略了另一个潜在的问题,程序图标的资源ID不一定是1.通常不是,100往往是一个受欢迎的选择,但任何事情都有.EnumResourceNames需要使其可靠.规则是编号最小的ID设置文件的图标.我真的不确定这是否真的意味着资源编译器首先放置最小的数字,这可能是API可能没有做到的.

一个非常小的故障模式是UpdateResource只能更新编号的资源项,而不是命名的资源项.使用名称而不是数字并不罕见,但绝大多数图像都使用数字作为图标.

当然,如果没有UAC清单,这种情况的可能性为零.您正在攻击通常没有写入权限的文件.


Ern*_*iks 5

我设法使用 ResourceHacker 并以这篇文章为例,在纯 C# 中实现了此功能。只需使用常规 .ico 作为输入即可。在 ResourceHacker ( http://www.angusj.com/resourcehacker/ ) 中,您将看到图标标识符(在我的例子中为 1)和语言标识符(在我的例子中为 1043):

在此输入图像描述

我使用了这段代码:

internal class IconChanger
{

    #region IconReader
    public class Icons : List<Icon>
    {
        public byte[] ToGroupData(int startindex = 1)
        {
            using (var ms = new MemoryStream())
            using (var writer = new BinaryWriter(ms))
            {
                var i = 0;

                writer.Write((ushort)0);  //reserved, must be 0
                writer.Write((ushort)1);  // type is 1 for icons
                writer.Write((ushort)this.Count);  // number of icons in structure(1)

                foreach (var icon in this)
                {

                    writer.Write(icon.Width);
                    writer.Write(icon.Height);
                    writer.Write(icon.Colors);

                    writer.Write((byte)0); // reserved, must be 0
                    writer.Write(icon.ColorPlanes);

                    writer.Write(icon.BitsPerPixel);

                    writer.Write(icon.Size);

                    writer.Write((ushort)(startindex + i));

                    i++;

                }
                ms.Position = 0;

                return ms.ToArray();
            }
        }
    }

    public class Icon
    {

        public byte Width { get; set; }
        public byte Height { get; set; }
        public byte Colors { get; set; }

        public uint Size { get; set; }

        public uint Offset { get; set; }

        public ushort ColorPlanes { get; set; }

        public ushort BitsPerPixel { get; set; }

        public byte[] Data { get; set; }

    }

    public class IconReader
    {

        public Icons Icons = new Icons();

        public IconReader(Stream input)
        {
            using (BinaryReader reader = new BinaryReader(input))
            {
                reader.ReadUInt16(); // ignore. Should be 0
                var type = reader.ReadUInt16();
                if (type != 1)
                {
                    throw new Exception("Invalid type. The stream is not an icon file");
                }
                var num_of_images = reader.ReadUInt16();

                for (var i = 0; i < num_of_images; i++)
                {
                    var width = reader.ReadByte();
                    var height = reader.ReadByte();
                    var colors = reader.ReadByte();
                    reader.ReadByte(); // ignore. Should be 0

                    var color_planes = reader.ReadUInt16(); // should be 0 or 1

                    var bits_per_pixel = reader.ReadUInt16();

                    var size = reader.ReadUInt32();

                    var offset = reader.ReadUInt32();

                    this.Icons.Add(new Icon()
                    {
                        Colors = colors,
                        Height = height,
                        Width = width,
                        Offset = offset,
                        Size = size,
                        ColorPlanes = color_planes,
                        BitsPerPixel = bits_per_pixel
                    });
                }

                // now get the Data
                foreach (var icon in Icons)
                {
                    if (reader.BaseStream.Position < icon.Offset)
                    {
                        var dummy_bytes_to_read = (int)(icon.Offset - reader.BaseStream.Position);
                        reader.ReadBytes(dummy_bytes_to_read);
                    }

                    var data = reader.ReadBytes((int)icon.Size);

                    icon.Data = data;
                }

            }
        }

    }
    #endregion

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int UpdateResource(IntPtr hUpdate, uint lpType, ushort lpName, ushort wLanguage, byte[] lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    const uint RT_ICON = 3;
    const uint RT_GROUP_ICON = 14;

    public ICResult ChangeIcon(string exeFilePath, string iconFilePath)
    {
        using (FileStream fs = new FileStream(iconFilePath, FileMode.Open, FileAccess.Read))
        {
            var reader = new IconReader(fs);

            var iconChanger = new IconChanger();
            return iconChanger.ChangeIcon(exeFilePath, reader.Icons);
        }
    }

    public ICResult ChangeIcon(string exeFilePath, Icons icons)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null) return ICResult.FailBegin;

        ushort startindex = 1;
        ushort index = startindex;
        ICResult result = ICResult.Success;

        var ret = 1;

        foreach (var icon in icons)
        {
            // Replace the icon
            // todo :Improve the return value handling of UpdateResource
            ret = UpdateResource(handleExe, RT_ICON, index, 0, icon.Data, icon.Size);

            index++;
        }

        var groupdata = icons.ToGroupData();

        // todo :Improve the return value handling of UpdateResource
        ret = UpdateResource(handleExe, RT_GROUP_ICON, startindex, 0, groupdata, (uint)groupdata.Length);
        if (ret == 1)
        {
            if (EndUpdateResource(handleExe, false))
                result = ICResult.Success;
            else
                result = ICResult.FailEnd;
        }
        else
            result = ICResult.FailUpdate;

        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)