/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Based on: * binfmt PE executable helper, by Ilya Konstantinov */ #include #include #include #include #define LOG_ENABLED 0 #define LOG(fmt, ...) \ if (LOG_ENABLED) { \ fprintf(stderr, fmt "\n" \ __VA_OPT__(,) __VA_ARGS__ ); \ } /* Read a big-endian, 16-bit value at the given offset from the given file. */ uint16_t fread_uint16(FILE* f, unsigned long offset) { if (fseek(f, offset, SEEK_SET) != 0) { LOG("Failed to seek to %lu: %d", offset, errno); exit(EXIT_FAILURE); } unsigned char bytes[2]; if (fread(&bytes, sizeof(bytes), 1, f) != 1) { LOG("Failed to read %zd bytes at %lu: %d", sizeof(bytes), offset, errno); exit(EXIT_FAILURE); } return bytes[0] | bytes[1] << 8; } /* Read a big-endian, 32-bit value at the given offset from the given file. */ uint32_t fread_uint32(FILE* f, unsigned long offset) { if (fseek(f, offset, SEEK_SET) != 0) { LOG("Failed to seek to %lu: %d", offset, errno); exit(EXIT_FAILURE); } unsigned char bytes[4]; if (fread(&bytes, sizeof(bytes), 1, f) != 1) { LOG("Failed to read %zd bytes at %lu: %d", sizeof(bytes), offset, errno); exit(EXIT_FAILURE); } return bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; } int main(int argc, char **argv) { if (argc < 2) exit(EXIT_FAILURE); FILE* image = fopen(argv[1], "r"); if (image == NULL) exit(EXIT_FAILURE); /* * Assume the given file is at least an MS-DOS binary (it starts * with the "MZ" magic). Just need to disambiguate between a * CLR-managed code binary vs. a traditional native binary. */ /* Offset to the PE structured is at fixed offset 0x3C */ const uint32_t pe_offset = fread_uint32(image, 0x3C); /* * PE structure should start with the "PE signature" ("PE\0\0", or * 0x00004550 in big-endian). */ const uint32_t pe_signature = fread_uint32(image, pe_offset); if (pe_signature != 0x004550) { LOG("Wrong PE_signature (got %#08x)", pe_signature); exit(EXIT_FAILURE); } /* * The PE format is stored after the 4-byte signature and 20-byte * COFF header. There are two formats PE32 or PE32+ that might * contain a CLR-managed binary. All other formats are native. */ const uint16_t pe_format = fread_uint16(image, pe_offset + 4 + 20); /* * Compute the offset of the "CLR Runtime Header" (the 15th) entry * in the Header Data Directories section. This offset is * computed relative to the pe_offset. */ size_t datadir_clr_offset = 0; if (pe_format == 0x10b) { LOG("PE32 format"); /* * +4 bytes for pe_signature * +20 bytes for COFF header * +28 bytes for PE32 "standard fields" header * +68 bytes for "Windows-specific fields" header * +(8 * 14) bytes for the 15th entry ("CLR Runtime") * in the Data Directories Header */ datadir_clr_offset = 4 + 20 + 28 + 68 + (8 * 14); } else if (pe_format == 0x20b) { LOG("PE32+ format"); /* * +4 bytes for pe_signature * +20 bytes for COFF header * +24 bytes for PE32 "standard fields" header * +88 bytes for "Windows-specific fields" header * +(8 * 14) bytes for the 15th entry ("CLR Runtime") * in the Data Directories Header */ datadir_clr_offset = 4 + 20 + 24 + 88 + (8 * 14); } else { /* * This file may be in some other PE format, none of those * format support CLR-managed binaries, so we are done. */ LOG("Not a PE32/PE32+ binary: %#04x", pe_format); exit(EXIT_FAILURE); } LOG("CLR Directory offset %#08zx", datadir_clr_offset); /* * The CLR Data Directory entry (like all entries in the Data * Directory) consists of a 4-byte virtual address and a 4-byte * size. (In both PE32 and PE32+ formats.) */ const uint32_t rva = fread_uint32(image, pe_offset + datadir_clr_offset); const uint32_t rsz = fread_uint32(image, pe_offset + datadir_clr_offset + 4); LOG("CLR Data rva=%#0x size=%#0x", rva, rsz); /* Empty CLR Data means its a native PE executable. */ if ((rva == 0) || (rsz == 0)) { LOG("No CLR content, not a Mono binary."); exit(EXIT_FAILURE); } /* * At this point we assume the binary is a CLR one. Technically, garbage * files could get this far, but hopefully the Mono runtime is the best * place to decode/explain such broken files (vs. the wine runtime). */ LOG("All checks passed."); fclose(image); exit(EXIT_SUCCESS); }