mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-18 15:45:39 +01:00
Check imported dlls in CI
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
@@ -4,9 +4,11 @@
|
||||
import { execFile as execFileCb } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join, basename } from 'node:path';
|
||||
import fastGlob from 'fast-glob';
|
||||
import { gte } from 'semver';
|
||||
import { Format, NtExecutable } from 'pe-library';
|
||||
|
||||
// Note: because we don't run under electron - this is a path to binary
|
||||
import ELECTRON_BINARY from 'electron';
|
||||
@@ -14,6 +16,8 @@ import ELECTRON_BINARY from 'electron';
|
||||
import { drop } from '../util/drop.std.js';
|
||||
import packageJson from '../util/packageJson.node.js';
|
||||
|
||||
const { ImageDosHeader, ImageNtHeaders, ImageDirectoryEntry } = Format;
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
|
||||
// See https://en.wikipedia.org/wiki/Darwin_(operating_system)#Darwin_20_onwards
|
||||
@@ -86,6 +90,164 @@ async function macosVersionCheck(file: string) {
|
||||
);
|
||||
}
|
||||
|
||||
// See: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format?redirectedfrom=MSDN
|
||||
// See: https://0xrick.github.io/win-internals/pe6/
|
||||
const EMPTY_IMPORT_ENTRY = Buffer.alloc(4 * 5);
|
||||
const EMPTY_DELAY_IMPORT_ENTRY = Buffer.alloc(4 * 8);
|
||||
|
||||
const DLL_TABLES = new Map([
|
||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table
|
||||
[ImageDirectoryEntry.Import, { empty: EMPTY_IMPORT_ENTRY, nameOffset: 12 }],
|
||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-delay-load-directory-table
|
||||
[
|
||||
ImageDirectoryEntry.DelayImport,
|
||||
{ empty: EMPTY_DELAY_IMPORT_ENTRY, nameOffset: 4 },
|
||||
],
|
||||
]);
|
||||
|
||||
const ALLOWED_DLLS = new Set([
|
||||
'advapi32.dll',
|
||||
'api-ms-win-core-handle-l1-1-0.dll',
|
||||
'api-ms-win-core-realtime-l1-1-1.dll',
|
||||
'api-ms-win-core-synch-l1-2-0.dll',
|
||||
'api-ms-win-core-winrt-error-l1-1-1.dll',
|
||||
'api-ms-win-core-winrt-l1-1-0.dll',
|
||||
'api-ms-win-core-winrt-string-l1-1-0.dll',
|
||||
'api-ms-win-power-base-l1-1-0.dll',
|
||||
'api-ms-win-shcore-scaling-l1-1-1.dll',
|
||||
'avrt.dll',
|
||||
'bcrypt.dll',
|
||||
'bcryptprimitives.dll',
|
||||
'bthprops.cpl',
|
||||
'cfgmgr32.dll',
|
||||
'comctl32.dll',
|
||||
'comdlg32.dll',
|
||||
'crypt32.dll',
|
||||
'd3d11.dll',
|
||||
'd3d12.dll',
|
||||
'dbghelp.dll',
|
||||
'dcomp.dll',
|
||||
'dhcpcsvc.dll',
|
||||
'dwmapi.dll',
|
||||
'dwrite.dll',
|
||||
'dxgi.dll',
|
||||
'ffmpeg.dll',
|
||||
'fontsub.dll',
|
||||
'gdi32.dll',
|
||||
'hid.dll',
|
||||
'iphlpapi.dll',
|
||||
'kernel32.dll',
|
||||
'mf.dll',
|
||||
'mfplat.dll',
|
||||
'mfreadwrite.dll',
|
||||
'mmdevapi.dll',
|
||||
'msdmo.dll',
|
||||
'ncrypt.dll',
|
||||
'node.exe',
|
||||
'ntdll.dll',
|
||||
'ole32.dll',
|
||||
'oleacc.dll',
|
||||
'oleaut32.dll',
|
||||
'pdh.dll',
|
||||
'powrprof.dll',
|
||||
'propsys.dll',
|
||||
'psapi.dll',
|
||||
'rpcrt4.dll',
|
||||
'secur32.dll',
|
||||
'setupapi.dll',
|
||||
'shell32.dll',
|
||||
'shlwapi.dll',
|
||||
'uiautomationcore.dll',
|
||||
'urlmon.dll',
|
||||
'user32.dll',
|
||||
'userenv.dll',
|
||||
'uxtheme.dll',
|
||||
'version.dll',
|
||||
'winhttp.dll',
|
||||
'winmm.dll',
|
||||
'winspool.drv',
|
||||
'wintrust.dll',
|
||||
'winusb.dll',
|
||||
'ws2_32.dll',
|
||||
'wtsapi32.dll',
|
||||
]);
|
||||
|
||||
async function windowsDllImportCheck(file: string): Promise<void> {
|
||||
console.log(`${file}: checking...`);
|
||||
|
||||
const fileData = await readFile(file);
|
||||
const dosHeader = ImageDosHeader.from(fileData);
|
||||
const ntHeaders = ImageNtHeaders.from(fileData, dosHeader.newHeaderAddress);
|
||||
|
||||
const ntExecutable = NtExecutable.from(fileData, {
|
||||
ignoreCert: true,
|
||||
});
|
||||
|
||||
function cstr(data: Buffer<ArrayBuffer>, offset: number): string {
|
||||
for (let end = offset; end < data.length; end += 1) {
|
||||
if (data[end] === 0) {
|
||||
return data.subarray(offset, end).toString();
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid cstring');
|
||||
}
|
||||
|
||||
const imports = new Set<string>();
|
||||
for (const [entryType, { empty, nameOffset }] of DLL_TABLES) {
|
||||
const section = ntExecutable.getSectionByEntry(entryType);
|
||||
const imageDirectoryEntry =
|
||||
ntHeaders.optionalHeaderDataDirectory.get(entryType);
|
||||
|
||||
if (section?.data == null || imageDirectoryEntry == null) {
|
||||
console.warn(`${file}: no ${entryType} directory entry`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// section contains the directory entry, but at offset determined by the
|
||||
// image directory entry
|
||||
const entryData = Buffer.from(section.data).subarray(
|
||||
imageDirectoryEntry.virtualAddress - section.info.virtualAddress
|
||||
);
|
||||
|
||||
for (let i = 0; i < entryData.byteLength; i += empty.byteLength) {
|
||||
const entry = entryData.subarray(i, i + empty.byteLength);
|
||||
|
||||
// Empty descriptor indicates end of the array
|
||||
if (entry.equals(empty)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const name = entry.readInt32LE(nameOffset);
|
||||
|
||||
if (name <= 0) {
|
||||
continue;
|
||||
}
|
||||
imports.add(
|
||||
cstr(
|
||||
fileData,
|
||||
// `name` is offest relative to loaded section, translate it back
|
||||
// to the file offset
|
||||
name - section.info.virtualAddress + section.info.pointerToRawData
|
||||
).toLowerCase()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let disallowed = 0;
|
||||
for (const name of imports) {
|
||||
if (ALLOWED_DLLS.has(name)) {
|
||||
console.log(` Allowed: ${name}`);
|
||||
} else {
|
||||
console.error(` Disallowed: ${name}`);
|
||||
disallowed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (disallowed !== 0) {
|
||||
throw new Error(`${basename(file)} contains disallowed dll imports`);
|
||||
}
|
||||
}
|
||||
|
||||
function padGlibcVersion(version: string) {
|
||||
if (/^\d+\.\d+$/.test(version)) {
|
||||
return `${version}.0`;
|
||||
@@ -166,13 +328,14 @@ async function main() {
|
||||
}
|
||||
)),
|
||||
];
|
||||
if (process.platform === 'darwin') {
|
||||
for (const file of BINARY_FILES) {
|
||||
for (const file of BINARY_FILES) {
|
||||
if (process.platform === 'darwin') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await macosVersionCheck(file);
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
for (const file of BINARY_FILES) {
|
||||
} else if (process.platform === 'win32') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await windowsDllImportCheck(file);
|
||||
} else if (process.platform === 'linux') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await linuxVersionCheck(file);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user