-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathIconChanger.cs
242 lines (223 loc) · 10.4 KB
/
IconChanger.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace rtlo_attack
{
// stolen from https://github.com/NYAN-x-CAT/AsyncRAT-C-Sharp/blob/master/AsyncRAT-C%23/Server/Helper/IconInjector.cs
public class IconChanger
{
// Basically, you can change icons with the UpdateResource api call.
// When you make the call you say "I'm updating an icon", and you send the icon data.
// The main problem is that ICO files store the icons in one set of structures, and exe/dll files store them in
// another set of structures. So you have to translate between the two -- you can't just load the ICO file as
// bytes and send them with the UpdateResource api call.
[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);
}
// The first structure in an ICO file lets us know how many images are in the file.
[StructLayout(LayoutKind.Sequential)]
private struct ICONDIR
{
// Reserved, must be 0
public ushort Reserved;
// Resource type, 1 for icons.
public ushort Type;
// How many images.
public ushort Count;
// The native structure has an array of ICONDIRENTRYs as a final field.
}
// Each ICONDIRENTRY describes one icon stored in the ico file. The offset says where the icon image data
// starts in the file. The other fields give the information required to turn that image data into a valid
// bitmap.
[StructLayout(LayoutKind.Sequential)]
private struct ICONDIRENTRY
{
/// <summary>
/// The width, in pixels, of the image.
/// </summary>
public byte Width;
/// <summary>
/// The height, in pixels, of the image.
/// </summary>
public byte Height;
/// <summary>
/// The number of colors in the image; (0 if >= 8bpp)
/// </summary>
public byte ColorCount;
/// <summary>
/// Reserved (must be 0).
/// </summary>
public byte Reserved;
/// <summary>
/// Color planes.
/// </summary>
public ushort Planes;
/// <summary>
/// Bits per pixel.
/// </summary>
public ushort BitCount;
/// <summary>
/// The length, in bytes, of the pixel data.
/// </summary>
public int BytesInRes;
/// <summary>
/// The offset in the file where the pixel data starts.
/// </summary>
public int ImageOffset;
}
// Each image is stored in the file as an ICONIMAGE structure:
//typdef struct
//{
// BITMAPINFOHEADER icHeader; // DIB header
// RGBQUAD icColors[1]; // Color table
// BYTE icXOR[1]; // DIB bits for XOR mask
// BYTE icAND[1]; // DIB bits for AND mask
//} ICONIMAGE, *LPICONIMAGE;
[StructLayout(LayoutKind.Sequential)]
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;
}
// The icon in an exe/dll file is stored in a very similar structure:
[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;
}
public static void InjectIcon(string exeFileName, string iconFileName)
{
InjectIcon(exeFileName, iconFileName, 1, 1);
}
public static void InjectIcon(string exeFileName, string iconFileName, uint iconGroupID, uint iconBaseID)
{
const uint RT_ICON = 3u;
const uint RT_GROUP_ICON = 14u;
IconFile iconFile = IconFile.FromFile(iconFileName);
var hUpdate = NativeMethods.BeginUpdateResource(exeFileName, false);
var data = iconFile.CreateIconGroupData(iconBaseID);
NativeMethods.UpdateResource(hUpdate, new IntPtr(RT_GROUP_ICON), new IntPtr(iconGroupID), 0, data,
data.Length);
for (int i = 0; i <= iconFile.ImageCount - 1; i++)
{
var image = iconFile.ImageData(i);
NativeMethods.UpdateResource(hUpdate, new IntPtr(RT_ICON), new IntPtr(iconBaseID + i), 0, image,
image.Length);
}
NativeMethods.EndUpdateResource(hUpdate, false);
}
private class IconFile
{
private ICONDIR iconDir = new ICONDIR();
private ICONDIRENTRY[] iconEntry;
private byte[][] iconImage;
public int ImageCount
{
get { return iconDir.Count; }
}
public byte[] ImageData(int index)
{
return iconImage[index];
}
public static IconFile FromFile(string filename)
{
IconFile instance = new IconFile();
// Read all the bytes from the file.
byte[] fileBytes = System.IO.File.ReadAllBytes(filename);
// First struct is an ICONDIR
// Pin the bytes from the file in memory so that we can read them.
// If we didn't pin them then they could move around (e.g. when the
// garbage collector compacts the heap)
GCHandle pinnedBytes = GCHandle.Alloc(fileBytes, GCHandleType.Pinned);
// Read the ICONDIR
instance.iconDir = (ICONDIR)Marshal.PtrToStructure(pinnedBytes.AddrOfPinnedObject(), typeof(ICONDIR));
// which tells us how many images are in the ico file. For each image, there's a ICONDIRENTRY, and associated pixel data.
instance.iconEntry = new ICONDIRENTRY[instance.iconDir.Count];
instance.iconImage = new byte[instance.iconDir.Count][];
// The first ICONDIRENTRY will be immediately after the ICONDIR, so the offset to it is the size of ICONDIR
int offset = Marshal.SizeOf(instance.iconDir);
// After reading an ICONDIRENTRY we step forward by the size of an ICONDIRENTRY
var iconDirEntryType = typeof(ICONDIRENTRY);
var size = Marshal.SizeOf(iconDirEntryType);
for (int i = 0; i <= instance.iconDir.Count - 1; i++)
{
// Grab the structure.
var entry =
(ICONDIRENTRY)
Marshal.PtrToStructure(new IntPtr(pinnedBytes.AddrOfPinnedObject().ToInt64() + offset),
iconDirEntryType);
instance.iconEntry[i] = entry;
// Grab the associated pixel data.
instance.iconImage[i] = new byte[entry.BytesInRes];
Buffer.BlockCopy(fileBytes, entry.ImageOffset, instance.iconImage[i], 0, entry.BytesInRes);
offset += size;
}
pinnedBytes.Free();
return instance;
}
public byte[] CreateIconGroupData(uint iconBaseID)
{
// This will store the memory version of the icon.
int sizeOfIconGroupData = Marshal.SizeOf(typeof(ICONDIR)) +
Marshal.SizeOf(typeof(GRPICONDIRENTRY)) * ImageCount;
byte[] data = new byte[sizeOfIconGroupData];
var pinnedData = GCHandle.Alloc(data, GCHandleType.Pinned);
Marshal.StructureToPtr(iconDir, pinnedData.AddrOfPinnedObject(), false);
var offset = Marshal.SizeOf(iconDir);
for (int i = 0; i <= ImageCount - 1; i++)
{
GRPICONDIRENTRY grpEntry = new GRPICONDIRENTRY();
BITMAPINFOHEADER bitmapheader = new BITMAPINFOHEADER();
var pinnedBitmapInfoHeader = GCHandle.Alloc(bitmapheader, GCHandleType.Pinned);
Marshal.Copy(ImageData(i), 0, pinnedBitmapInfoHeader.AddrOfPinnedObject(),
Marshal.SizeOf(typeof(BITMAPINFOHEADER)));
pinnedBitmapInfoHeader.Free();
grpEntry.Width = iconEntry[i].Width;
grpEntry.Height = iconEntry[i].Height;
grpEntry.ColorCount = iconEntry[i].ColorCount;
grpEntry.Reserved = iconEntry[i].Reserved;
grpEntry.Planes = bitmapheader.Planes;
grpEntry.BitCount = bitmapheader.BitCount;
grpEntry.BytesInRes = iconEntry[i].BytesInRes;
grpEntry.ID = Convert.ToUInt16(iconBaseID + i);
Marshal.StructureToPtr(grpEntry, new IntPtr(pinnedData.AddrOfPinnedObject().ToInt64() + offset),
false);
offset += Marshal.SizeOf(typeof(GRPICONDIRENTRY));
}
pinnedData.Free();
return data;
}
}
}
}