securityos/components/system/Files/FileEntry/exeIcons.ts

104 lines
2.8 KiB
TypeScript
Raw Normal View History

2024-09-06 15:32:35 +00:00
import type {
IconGroupEntry,
IconGroupItem,
ResourceEntry,
} from "resedit/dist/resource";
const RESERVED = 0;
const ICON_TYPE = {
CUR: 2,
ICO: 1,
};
const createIconHeader = (iconCount: number): Uint8Array =>
Uint8Array.from([
RESERVED,
RESERVED,
...new Uint8Array(Uint16Array.from([ICON_TYPE.ICO]).buffer),
...new Uint8Array(Uint16Array.from([iconCount]).buffer),
]);
const createIconDirEntry = (
{ bitCount, colors, dataSize, height, planes, width }: IconGroupItem,
offset: number
): Uint8Array =>
Uint8Array.from([
width,
bitCount === 32 && height === width * 2 ? width : height,
colors,
RESERVED,
...new Uint8Array(Uint16Array.from([planes]).buffer),
...new Uint8Array(Uint16Array.from([bitCount]).buffer),
...new Uint8Array(Uint32Array.from([dataSize]).buffer),
...new Uint8Array(Uint32Array.from([offset]).buffer),
]);
const ICONDIR_LENGTH = 6;
const ICONDIRENTRY_LENGTH = 16;
const RC_ICON = 3;
export const extractExeIcon = async (
exeData: Buffer
): Promise<Buffer | undefined> => {
const ResEdit = await import("resedit");
let iconGroupEntry: IconGroupEntry;
let entries: ResourceEntry[];
try {
({ entries } = ResEdit.NtExecutableResource.from(
ResEdit.NtExecutable.from(exeData, {
ignoreCert: true,
}),
true
));
[iconGroupEntry] = ResEdit.Resource.IconGroupEntry.fromEntries(entries);
} catch (error) {
if (
(error as Error).message.includes(
"Binary with symbols is not supported now"
)
) {
const { unarchive } = await import("utils/zipFunctions");
try {
const { "/.rsrc/ICON/1.ico": icon } =
(await unarchive("data.exe", exeData)) || {};
return Buffer.from(icon);
} catch {
// Ignore error extracting EXE
}
}
return undefined;
}
if (!iconGroupEntry?.icons) return undefined;
const iconDataOffset =
ICONDIR_LENGTH + ICONDIRENTRY_LENGTH * iconGroupEntry.icons.length;
let currentIconOffset = iconDataOffset;
const iconData = iconGroupEntry.icons.map(({ iconID }) =>
entries.find(({ id, type }) => type === RC_ICON && id === iconID)
);
const iconHeader = iconGroupEntry.icons.reduce(
(accHeader, iconBitmapInfo, index) => {
currentIconOffset += index ? iconData[index - 1]?.bin.byteLength ?? 0 : 0;
return Buffer.concat([
accHeader,
createIconDirEntry(iconBitmapInfo, currentIconOffset),
]);
},
createIconHeader(iconGroupEntry.icons.length)
);
return Buffer.from(
iconData.reduce(
(accIcon, iconItem) =>
Buffer.concat([accIcon, Buffer.from((iconItem as ResourceEntry).bin)]),
iconHeader
)
);
};