From 868e78d81b9b452437714e80682c1936c0aacb5f Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Fri, 30 May 2025 18:35:13 +0100 Subject: [PATCH 01/38] Remove redundant (and POSIX/MinGW-only) file size check --- src/getdata/getdata.c | 7 ------- src/getdata/getdata.h | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index 1455e89..d31a28a 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -171,13 +171,6 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) return ERR_UNKNOWN_DATA_REQUEST; } - // Get header data required for ANY XEX - // PE size - fseek(pe, 0, SEEK_SET); // If we don't do this, the size returned is wrong (?) - struct stat peStat; - fstat(fileno(pe), &peStat); - peData->size = peStat.st_size; - // Getting PE header offset before we go any further.. fseek(pe, 0x3C, SEEK_SET); peData->peHeaderOffset = get32BitFromPE(pe); diff --git a/src/getdata/getdata.h b/src/getdata/getdata.h index 26384ce..e898c71 100644 --- a/src/getdata/getdata.h +++ b/src/getdata/getdata.h @@ -1,7 +1,7 @@ // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // -// Copyright (c) 2024 Aiden Isik +// Copyright (c) 2024-25 Aiden Isik // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -18,7 +18,6 @@ #pragma once -#include #include "../common/common.h" #include "../common/datastorage.h" From 6c5274ea54fbe7ec0646d4df417b969ada848517 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 1 Jun 2025 15:49:47 +0100 Subject: [PATCH 02/38] Set up data structures/helper functions for PE import parsing, make byte-swapping read functions more efficient --- src/common/datastorage.c | 90 ++++++++++++++++------------------- src/common/datastorage.h | 31 ++++++++++-- src/getdata/getdata.c | 27 ++++++----- src/setdata/pagedescriptors.c | 8 ++-- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/common/datastorage.c b/src/common/datastorage.c index e5d807e..c434e35 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -28,84 +28,76 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment) return offset; // Offset already aligned } -// TODO: Combine all of these into a single function +uint32_t rvaToOffset(uint32_t rva, struct sections *sections) +{ + for(int32_t i = sections->count - 1; i >= 0; i--) + { + if(rva >= sections->section[i].rva) + { + return (rva - sections->section[i].rva) + sections->section[i].offset; + } + } +} + +uint32_t offsetToRVA(uint32_t offset, struct sections *sections) +{ + for(int32_t i = sections->count - 1; i >= 0; i--) + { + if(offset >= sections->section[i].offset) + { + return (offset - sections->section[i].offset) + sections->section[i].rva; + } + } +} uint32_t get32BitFromPE(FILE *pe) { - uint8_t store[4]; - fread(store, sizeof(uint8_t), 4, pe); - - uint32_t result = 0; - - for(int i = 0; i < 4; i++) - { - result |= store[i] << i * 8; - } + uint32_t result; + fread(&result, sizeof(uint32_t), 1, pe); // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM - result = __builtin_bswap32(result); -#endif - + return __builtin_bswap32(result); +#else return result; +#endif } uint16_t get16BitFromPE(FILE *pe) { - uint8_t store[2]; - fread(store, sizeof(uint8_t), 2, pe); - - uint16_t result = 0; - - for(int i = 0; i < 2; i++) - { - result |= store[i] << i * 8; - } + uint16_t result; + fread(&result, sizeof(uint16_t), 1, pe); // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM - result = htons(result); -#endif - + return __builtin_bswap16(result); +#else return result; +#endif } uint32_t get32BitFromXEX(FILE *xex) { - uint8_t store[4]; - fread(store, sizeof(uint8_t), 4, xex); - - uint32_t result = 0; - - for(int i = 0; i < 4; i++) - { - result |= store[i] << i * 8; - } + uint32_t result; + fread(&result, sizeof(uint32_t), 1, xex); // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM - result = __builtin_bswap32(result); -#endif - + return __builtin_bswap32(result); +#else return result; +#endif } uint16_t get16BitFromXEX(FILE *xex) { - uint8_t store[2]; - fread(store, sizeof(uint8_t), 2, xex); - - uint32_t result = 0; - - for(int i = 0; i < 2; i++) - { - result |= store[i] << i * 8; - } + uint16_t result; + fread(&result, sizeof(uint16_t), 1, xex); // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM - result = __builtin_bswap16(result); -#endif - + return __builtin_bswap16(result); +#else return result; +#endif } diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 0f2aa77..686716e 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -76,18 +76,39 @@ struct sections { uint16_t count; - struct sectionPerms *sectionPerms; + struct section *section; }; -struct sectionPerms +struct section { uint8_t permFlag; + uint32_t virtualSize; uint32_t rva; + uint32_t rawSize; + uint32_t offset; +}; + +struct peImport +{ + bool ordinal; + uint32_t iatAddr; + uint32_t branchStubAddr; +}; + +struct peImportTable +{ + char *name; + uint32_t importCount; + uint32_t branchStubCount; + struct peImport *imports; }; struct peImportInfo { - uint16_t count; + uint32_t tableCount; + uint32_t totalImportCount; + uint32_t totalBranchStubCount; + struct peImportTable *tables; }; struct peExportInfo @@ -210,7 +231,9 @@ struct optHeaders uint32_t getNextAligned(uint32_t offset, uint32_t alignment); -// TODO: combine these into a single function +uint32_t rvaToOffset(uint32_t rva, struct sections *sections); +uint32_t offsetToRVA(uint32_t offset, struct sections *sections); + uint32_t get32BitFromPE(FILE *pe); uint16_t get16BitFromPE(FILE *pe); uint32_t get32BitFromXEX(FILE *xex); diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index d31a28a..5119c85 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -120,7 +120,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false return true; // Checked enough, this is an Xbox 360 PE file } -int getSectionRwxFlags(FILE *pe, struct sections *sections) +int getSectionInfo(FILE *pe, struct sections *sections) { fseek(pe, 0x3C, SEEK_SET); uint32_t peOffset = get32BitFromPE(pe); @@ -128,29 +128,32 @@ int getSectionRwxFlags(FILE *pe, struct sections *sections) fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count sections->count = get16BitFromPE(pe); - sections->sectionPerms = calloc(sections->count, sizeof(struct sectionPerms)); // free() is called for this in setdata - if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} + sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata + if(sections->section == NULL) {return ERR_OUT_OF_MEM;} fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table for(uint16_t i = 0; i < sections->count; i++) { - fseek(pe, 0xC, SEEK_CUR); // Seek to RVA of section - sections->sectionPerms[i].rva = get32BitFromPE(pe); + fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section + sections->section[i].virtualSize = get32BitFromPE(pe); + sections->section[i].rva = get32BitFromPE(pe); + sections->section[i].rawSize = get32BitFromPE(pe); + sections->section[i].offset = get32BitFromPE(pe); - fseek(pe, 0x14, SEEK_CUR); // Now progress to characteristics, where we will check flags + fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags uint32_t characteristics = get32BitFromPE(pe); if(characteristics & PE_SECTION_FLAG_EXECUTE) { - sections->sectionPerms[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 + sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 } else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) { - sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; + sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; } else if(characteristics & PE_SECTION_FLAG_READ) { - sections->sectionPerms[i].permFlag = XEX_SECTION_RODATA | 0b10000; + sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; } else { @@ -209,7 +212,7 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) // Import tables fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); - peData->peImportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data + peData->peImportInfo.tableCount = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); @@ -217,8 +220,8 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) peData->tlsSize = get32BitFromPE(pe); if(peData->tlsAddr != 0 || peData->tlsSize != 0) {return ERR_UNSUPPORTED_STRUCTURE;} - // Page RWX flags - int ret = getSectionRwxFlags(pe, &(peData->sections)); + // Section info + int ret = getSectionInfo(pe, &(peData->sections)); if(ret != 0) {return ret;} return SUCCESS; diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 9fdc41d..5e57a10 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -23,11 +23,11 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3 uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; uint32_t currentOffset = page * pageSize; - for(int i = peData->sections.count - 1; i >= 0; i--) + for(int32_t i = peData->sections.count - 1; i >= 0; i--) { - if(currentOffset >= peData->sections.sectionPerms[i].rva) + if(currentOffset >= peData->sections.section[i].rva) { - return peData->sections.sectionPerms[i].permFlag; + return peData->sections.section[i].permFlag; } } @@ -76,5 +76,5 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se } } - free(peData->sections.sectionPerms); // Alloc'd in getdata + free(peData->sections.section); // Alloc'd in getdata } From 8ff9bb7582ef5dd5bd4c5d995c176a62ac8b5aee Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 1 Jun 2025 19:14:14 +0100 Subject: [PATCH 03/38] Start working on import data parsing from PE (untested) --- CMakeLists.txt | 3 + src/common/common.h | 1 + src/common/datastorage.c | 16 +++++ src/common/datastorage.h | 4 ++ src/getdata/getdata.c | 2 +- src/getdata/getimports.c | 131 +++++++++++++++++++++++++++++++++++++++ src/getdata/getimports.h | 24 +++++++ 7 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/getdata/getimports.c create mode 100644 src/getdata/getimports.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d2e3ea8..5029413 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.c) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.c) +list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.h) +list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.c) + list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.c) diff --git a/src/common/common.h b/src/common/common.h index 410446e..4b9e702 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -47,5 +47,6 @@ #define ERR_FILE_READ -4 #define ERR_OUT_OF_MEM -5 #define ERR_UNSUPPORTED_STRUCTURE -6 +#define ERR_INVALID_RVA_OR_OFFSET -7 void infoPrint(char *str); diff --git a/src/common/datastorage.c b/src/common/datastorage.c index c434e35..7f9d33f 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -30,6 +30,12 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment) uint32_t rvaToOffset(uint32_t rva, struct sections *sections) { + if((sections->count > 0) + && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) + { + return 0; // Not found (beyond end of PE) + } + for(int32_t i = sections->count - 1; i >= 0; i--) { if(rva >= sections->section[i].rva) @@ -37,10 +43,18 @@ uint32_t rvaToOffset(uint32_t rva, struct sections *sections) return (rva - sections->section[i].rva) + sections->section[i].offset; } } + + return 0; // Not found } uint32_t offsetToRVA(uint32_t offset, struct sections *sections) { + if((sections->count > 0) + && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) + { + return 0; // Not found (beyond end of PE) + } + for(int32_t i = sections->count - 1; i >= 0; i--) { if(offset >= sections->section[i].offset) @@ -48,6 +62,8 @@ uint32_t offsetToRVA(uint32_t offset, struct sections *sections) return (offset - sections->section[i].offset) + sections->section[i].rva; } } + + return 0; // Not found } uint32_t get32BitFromPE(FILE *pe) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 686716e..e23b4ca 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -67,6 +67,9 @@ #define PE_SECTION_FLAG_READ 0x40000000 #define PE_SECTION_FLAG_WRITE 0x80000000 +// PE import ordinal flag +#define PE_IMPORT_ORDINAL_FLAG 0x80000000 + // RWX flags (XEX) #define XEX_SECTION_CODE 0x1 #define XEX_SECTION_RWDATA 0x2 @@ -105,6 +108,7 @@ struct peImportTable struct peImportInfo { + uint32_t idtRVA; uint32_t tableCount; uint32_t totalImportCount; uint32_t totalBranchStubCount; diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index 5119c85..6815032 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -212,7 +212,7 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) // Import tables fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); - peData->peImportInfo.tableCount = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data + peData->peImportInfo.idtRVA = get32BitFromPE(pe); // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c new file mode 100644 index 0000000..faf934a --- /dev/null +++ b/src/getdata/getimports.c @@ -0,0 +1,131 @@ +// This file is part of SynthXEX, one component of the +// FreeChainXenon development toolchain +// +// Copyright (c) 2025 Aiden Isik +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "getimports.h" + +int getImports(FILE *pe, struct peData *peData) +{ + // If there is no IDT, just skip handling imports + if(peData->peImportInfo.idtRVA == 0) + { + return SUCCESS; + } + + // Seek to the IDT and read the first entry + uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); + if(idtOffset == 0) {return ERR_INVALID_RVA_OR_OFFSET;} + if(fseek(pe, idtOffset, SEEK_SET) != 0) {return ERR_FILE_READ;} + + uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); + if(currentIDT == NULL) {return ERR_OUT_OF_MEM;} + uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons + if(blankIDT == NULL) {free(currentIDT); return ERR_OUT_OF_MEM;} + if(fread(currentIDT, 5, sizeof(uint32_t), pe) < 5) {return ERR_FILE_READ;} + + // While the IDT is not blank, process it + for(uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) + { + // Allocate space for the current table data + peData->peImportInfo.tableCount = i; + peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); + if(peData->peImportInfo.tables == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} + +#ifdef BIG_ENDIAN_SYSTEM + // Byteswap the IDT fields + for(uint8_t i = 0; i < 5; i++) + { + currentIDT[i] = __builtin_bswap32(currentIDT[i]); + } +#endif + + // Retrieve the name pointed to by the table + uint32_t savedOffset = ftell(pe); + uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); + if(tableNameOffset == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + if(fseek(pe, tableNameOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + + // Allocating is expensive, go 16 bytes at a time to avoid calling realloc an excessive number of times + peData->peImportInfo.tables[i].name = calloc(16, sizeof(char)); + if(peData->peImportInfo.tables[i].name == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} + if(fread(peData->peImportInfo.tables[i].name, 15, sizeof(char), pe) < 15) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + peData->peImportInfo.tables[i].name[15] = '\0'; + + // Use strlen to determine whether the string ends short of the buffer. If it does, we have the name. + for(uint32_t j = 15; strlen(peData->peImportInfo.tables[i].name) != j; j += 16) + { + peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, ((j + 1) + 16) * sizeof(char)); + if(peData->peImportInfo.tables[i].name == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} + if(fread(&(peData->peImportInfo.tables[i].name[j]), 15, sizeof(char), pe) < 15) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + peData->peImportInfo.tables[i].name[j + 16] = '\0'; + } + + // Seek to the IAT and read the first entry + uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); + if(iatOffset == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + if(fseek(pe, iatOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + + uint32_t currentImport; + if(fread(¤tImport, 1, sizeof(uint32_t), pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + + // While the import is not blank, process it + for(int j = 0; currentImport != 0; j++) + { +#ifdef BIG_ENDIAN_SYSTEM + // Byteswap the import + currentImport = __builtin_bswap32(currentImport); +#endif + + // Allocate space for the current import + peData->peImportInfo.tables[i].importCount = j; + peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); + peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Make sure this is zeroed, we rely on it later + + // Check which type of import we have + if(!(currentImport & PE_IMPORT_ORDINAL_FLAG)) + { + free(currentIDT); + free(blankIDT); + return ERR_UNSUPPORTED_STRUCTURE; // We don't support import by name yet + } + else + { + peData->peImportInfo.tables[i].imports[j].ordinal = true; + } + + // Store the address of the current import entry in iatAddr + uint32_t currentImportRVA = offsetToRVA(ftell(pe), &(peData->sections)); + if(currentImportRVA == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; + + // Read the next import + if(fread(¤tImport, 1, sizeof(uint32_t), pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + } + + // Add table's import count to total and return to the next IDT entry to read + peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; + if(fseek(pe, savedOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + + // Read next IDT + if(fread(currentIDT, 5, sizeof(uint32_t), pe) < 5) {return ERR_FILE_READ;} + } + + free(currentIDT); + free(blankIDT); + + return SUCCESS; +} diff --git a/src/getdata/getimports.h b/src/getdata/getimports.h new file mode 100644 index 0000000..2c08bff --- /dev/null +++ b/src/getdata/getimports.h @@ -0,0 +1,24 @@ +// This file is part of SynthXEX, one component of the +// FreeChainXenon development toolchain +// +// Copyright (c) 2025 Aiden Isik +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#pragma once + +#include "../common/common.h" +#include "../common/datastorage.h" + +int getImports(FILE *pe, struct peData *peData); From f3e972a99f1419a483c812b838b2d8478d664a4e Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 1 Jun 2025 19:29:49 +0100 Subject: [PATCH 04/38] Bump version --- src/common/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/common.h b/src/common/common.h index 4b9e702..6242f00 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -26,7 +26,7 @@ // Program identifiers #define NAME "SynthXEX" -#define VERSION "v0.0.3" +#define VERSION "v0.0.4" #define COPYRIGHT "2024-25" #ifdef GIT_COMMIT From 4041036c65873ec0391b93243fc67b5271339ac2 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 1 Jun 2025 23:23:46 +0100 Subject: [PATCH 05/38] Fix what currently exists of PE import reading --- src/getdata/getimports.c | 44 ++++++++++++++++++++++++---------------- src/main.c | 17 ++++++++++++++-- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index faf934a..3a6f829 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -20,12 +20,15 @@ int getImports(FILE *pe, struct peData *peData) { + // Make sure the peImportInfo struct is blank, except the IDT RVA + memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t)); + // If there is no IDT, just skip handling imports if(peData->peImportInfo.idtRVA == 0) { return SUCCESS; } - + // Seek to the IDT and read the first entry uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); if(idtOffset == 0) {return ERR_INVALID_RVA_OR_OFFSET;} @@ -35,15 +38,16 @@ int getImports(FILE *pe, struct peData *peData) if(currentIDT == NULL) {return ERR_OUT_OF_MEM;} uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons if(blankIDT == NULL) {free(currentIDT); return ERR_OUT_OF_MEM;} - if(fread(currentIDT, 5, sizeof(uint32_t), pe) < 5) {return ERR_FILE_READ;} - + if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} + // While the IDT is not blank, process it for(uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) { // Allocate space for the current table data - peData->peImportInfo.tableCount = i; + peData->peImportInfo.tableCount++; peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); if(peData->peImportInfo.tables == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} + memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank #ifdef BIG_ENDIAN_SYSTEM // Byteswap the IDT fields @@ -60,27 +64,31 @@ int getImports(FILE *pe, struct peData *peData) if(fseek(pe, tableNameOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} // Allocating is expensive, go 16 bytes at a time to avoid calling realloc an excessive number of times - peData->peImportInfo.tables[i].name = calloc(16, sizeof(char)); - if(peData->peImportInfo.tables[i].name == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} - if(fread(peData->peImportInfo.tables[i].name, 15, sizeof(char), pe) < 15) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} - peData->peImportInfo.tables[i].name[15] = '\0'; - - // Use strlen to determine whether the string ends short of the buffer. If it does, we have the name. - for(uint32_t j = 15; strlen(peData->peImportInfo.tables[i].name) != j; j += 16) + for(uint32_t j = 0;; j += 16) { - peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, ((j + 1) + 16) * sizeof(char)); + peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); if(peData->peImportInfo.tables[i].name == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} - if(fread(&(peData->peImportInfo.tables[i].name[j]), 15, sizeof(char), pe) < 15) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} - peData->peImportInfo.tables[i].name[j + 16] = '\0'; + if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + + // Check for a null terminator + for(uint32_t k = j; k < j + 16; k++) + { + if(peData->peImportInfo.tables[i].name[k] == '\0') + { + goto doneGettingTableName; + } + } } + doneGettingTableName: + // Seek to the IAT and read the first entry uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); if(iatOffset == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} if(fseek(pe, iatOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} uint32_t currentImport; - if(fread(¤tImport, 1, sizeof(uint32_t), pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} // While the import is not blank, process it for(int j = 0; currentImport != 0; j++) @@ -91,7 +99,7 @@ int getImports(FILE *pe, struct peData *peData) #endif // Allocate space for the current import - peData->peImportInfo.tables[i].importCount = j; + peData->peImportInfo.tables[i].importCount++; peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Make sure this is zeroed, we rely on it later @@ -113,7 +121,7 @@ int getImports(FILE *pe, struct peData *peData) peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; // Read the next import - if(fread(¤tImport, 1, sizeof(uint32_t), pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} } // Add table's import count to total and return to the next IDT entry to read @@ -121,7 +129,7 @@ int getImports(FILE *pe, struct peData *peData) if(fseek(pe, savedOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} // Read next IDT - if(fread(currentIDT, 5, sizeof(uint32_t), pe) < 5) {return ERR_FILE_READ;} + if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} } free(currentIDT); diff --git a/src/main.c b/src/main.c index a45c249..4afecf6 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,7 @@ #include "common/datastorage.h" #include "pemapper/pemapper.h" #include "getdata/getdata.h" +#include "getdata/getimports.h" #include "setdata/populateheaders.h" #include "setdata/pagedescriptors.h" #include "setdata/optheaders.h" @@ -245,8 +246,8 @@ int main(int argc, char **argv) struct optHeaderEntries optHeaderEntries; struct optHeaders optHeaders; - // Make sure the import library size is initially zero - optHeaders.importLibraries.size = 0; + // Make sure the import library size is initially zero + optHeaders.importLibraries.size = 0; printf("%s Validating PE file...\n", PRINT_STEM); @@ -302,6 +303,18 @@ int main(int argc, char **argv) } printf("%s Got header data from PE!\n", PRINT_STEM); + + // Process imports + printf("%s Retrieving import data from PE...\n", PRINT_STEM); + ret = getImports(pe, &peData); + + if(ret != SUCCESS) + { + printf("TEMPORARY ERROR HANDLING: IMPORT DATA FAILED: %d\n", ret); + exit(-1); + } + + printf("%s Got import data from PE!\n", PRINT_STEM); // Determine the path we should save the basefile at and open it. printf("%s Creating basefile from PE...\n", PRINT_STEM); From 0005775f85f1189241e3f84b2d286dee841c2a44 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 2 Jun 2025 20:26:36 +0100 Subject: [PATCH 06/38] Add code for locating branch stubs for imported functions --- .gitignore | 4 ++ src/getdata/getimports.c | 138 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d1f6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +*~ +*# +\#* diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 3a6f829..5a2e096 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -18,6 +18,107 @@ #include "getimports.h" +int checkForBranchStub(FILE *pe, struct peData *peData) +{ + uint32_t expectedInstructions[] = + { + // lis r, + // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction + 0x3C000000, + + // lwz r, (r) + // Mask 0xFC000000 for instruction + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction + 0x80000000, + + // mtctr r + // Mask 0xFC1FFFFF for instruction (mtspr CTR) + // Mask 0x03E00000 and shift right 21 bits for register + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction + 0x7C0903A6, + + // bctr + // An assembler told me this, apparently IBM doesn't have documentation on their site for it + 0x4E800420 + }; + + // Read in the set of instructions to check + uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); + if(instructionBuffer == NULL) {return ERR_OUT_OF_MEM;} + if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) {free(instructionBuffer); return ERR_FILE_READ;} + +#ifdef LITTLE_ENDIAN_SYSTEM + // Byteswap the instructions + for(uint8_t i = 0; i < 4; i++) + { + instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); + } +#endif + + // Check if each instruction matches + uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; + + if(((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) + && (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) + { + if(((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) + && (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) + && (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) + { + if(((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) + && (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) + { + if(instructionBuffer[3] == expectedInstructions[3]) + { + // Found a branch stub, check if the address matches any IAT entries + uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) + | (instructionBuffer[1] & 0x0000FFFF); + + for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) + { + if(peData->peImportInfo.tables[i].importCount == peData->peImportInfo.tables[i].branchStubCount) + { + continue; // We already have all the branch stubs possible for this table, skip it + } + + for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) + { + if(peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) + { + continue; // We already have this branch stub, skip it + } + + if(peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) + { + // Found the branch stub for the current import, store the address + uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); + if(currentBranchStubRVA == 0) {free(instructionBuffer); return ERR_INVALID_RVA_OR_OFFSET;} + peData->peImportInfo.tables[i].imports[j].branchStubAddr = peData->baseAddr + currentBranchStubRVA; + peData->peImportInfo.tables[i].branchStubCount++; + peData->peImportInfo.totalBranchStubCount++; + + free(instructionBuffer); + return SUCCESS; + } + } + } + } + } + } + } + + free(instructionBuffer); + if(fseek(pe, -12, SEEK_CUR) != 0) {return ERR_FILE_READ;} + return SUCCESS; +} + int getImports(FILE *pe, struct peData *peData) { // Make sure the peImportInfo struct is blank, except the IDT RVA @@ -92,7 +193,7 @@ int getImports(FILE *pe, struct peData *peData) // While the import is not blank, process it for(int j = 0; currentImport != 0; j++) - { + { #ifdef BIG_ENDIAN_SYSTEM // Byteswap the import currentImport = __builtin_bswap32(currentImport); @@ -116,10 +217,10 @@ int getImports(FILE *pe, struct peData *peData) } // Store the address of the current import entry in iatAddr - uint32_t currentImportRVA = offsetToRVA(ftell(pe), &(peData->sections)); + uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); if(currentImportRVA == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; - + // Read the next import if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} } @@ -134,6 +235,37 @@ int getImports(FILE *pe, struct peData *peData) free(currentIDT); free(blankIDT); + + // Find the branch stubs + for(uint16_t i = 0; i < peData->sections.count; i++) + { + if(peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) + { + break; // We have all the branch stubs there could possibly be + } + + if(!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) + { + continue; // Skip the current section if it's not executable + } + + // Seek to the start of the current section to begin reading + if(fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + // While we're within the bounds of the current section, and there's at least 4 instructions space left + while(ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) + { + int ret = checkForBranchStub(pe, peData); + + if(ret != SUCCESS) + { + return ret; + } + } + } return SUCCESS; } From 24c68d7ff174649d8d4f326cecff0ea29c11877d Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 3 Jun 2025 01:27:05 +0100 Subject: [PATCH 07/38] Put better memory management routines in place (TODO: use them everywhere) --- src/common/common.h | 4 ++ src/common/datastorage.c | 110 ++++++++++++++++++++++++++++++++++ src/common/datastorage.h | 19 ++++++ src/getdata/getimports.c | 40 ++++++------- src/main.c | 73 ++++++++++++---------- src/pemapper/pemapper.c | 4 +- src/setdata/optheaders.c | 7 +++ src/setdata/pagedescriptors.c | 4 +- src/write/writexex.c | 11 +--- 9 files changed, 210 insertions(+), 62 deletions(-) diff --git a/src/common/common.h b/src/common/common.h index 6242f00..f1721f7 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -24,6 +24,10 @@ #include #include +// Forbid the use of the free function +#define freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse free +#pragma GCC poison free + // Program identifiers #define NAME "SynthXEX" #define VERSION "v0.0.4" diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 7f9d33f..ce8f972 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -18,6 +18,116 @@ #include "datastorage.h" +// Frees memory and sets the pointer to NULL +// If the pointer is currently NULL, do nothing (avoid double-free) +// Disable pointer type checking here to make this easier to use +void nullAndFree(void **ptr) +{ + if(*ptr != NULL) + { + freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); + *ptr = NULL; + } +} + +// These functions together handle freeing all of the main structs. +// They can also be called individually, and it doesn't matter if you call them twice. +// Each function checks if the struct going to be accessed is NULL, so it doesn't try +// to access unallocated data. +// Even if it doesn't access data right now, it's still there so it's not forgotten +// about if it becomes necessary with future additions. +void freeOffsetsStruct(struct offsets **offsets) +{ + if(*offsets != NULL) + { + nullAndFree((void**)&((*offsets)->optHeaders)); + nullAndFree((void**)offsets); + } +} + +void freeXexHeaderStruct(struct xexHeader **xexHeader) +{ + if(*xexHeader != NULL) + { + nullAndFree((void**)xexHeader); + } +} + +void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) +{ + if(*secInfoHeader != NULL) + { + // Ignore the unaligned pointer warning here, every field before descriptors is a multiple of 8. + // Also works for Clang. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" + nullAndFree((void**)&((*secInfoHeader)->descriptors)); + #pragma GCC diagnostic pop + + nullAndFree((void**)secInfoHeader); + } +} + +void freeSectionsStruct(struct sections *sections) +{ + nullAndFree((void**)&(sections->section)); +} + +void freePeImportInfoStruct(struct peImportInfo *peImportInfo) +{ + if(peImportInfo->tables != NULL) + { + // Free the imports within each table first, then the tables, + // otherwise we'll have a memory leak + for(uint32_t i = 0; i < peImportInfo->tableCount; i++) + { + nullAndFree((void**)&(peImportInfo->tables[i].name)); + nullAndFree((void**)&(peImportInfo->tables[i].imports)); + } + + nullAndFree((void**)&(peImportInfo->tables)); + } +} + + +void freePeDataStruct(struct peData **peData) +{ + if(*peData != NULL) + { + freeSectionsStruct(&((*peData)->sections)); + freePeImportInfoStruct(&((*peData)->peImportInfo)); + nullAndFree((void**)peData); + } +} + +void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) +{ + if(*optHeaderEntries != NULL) + { + nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); + nullAndFree((void**)optHeaderEntries); + } +} + +void freeOptHeadersStruct(struct optHeaders **optHeaders) +{ + if(*optHeaders != NULL) + { + nullAndFree((void**)optHeaders); + } +} + +void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, + struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders) +{ + freeOffsetsStruct(offsets); + freeXexHeaderStruct(xexHeader); + freeSecInfoHeaderStruct(secInfoHeader); + freePeDataStruct(peData); + freeOptHeaderEntriesStruct(optHeaderEntries); + freeOptHeadersStruct(optHeaders); +} + uint32_t getNextAligned(uint32_t offset, uint32_t alignment) { if(offset % alignment) // If offset not aligned diff --git a/src/common/datastorage.h b/src/common/datastorage.h index e23b4ca..32a0578 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -231,6 +231,25 @@ struct optHeaders struct tlsInfo tlsInfo; }; +// Functions used for easier memory management + +// Frees memory and sets the pointer to NULL +// If the pointer is currently NULL, do nothing (avoid double-free) +void nullAndFree(void **ptr); + +// These functions together handle freeing all of the main structs. +// They can also be called individually, and it doesn't matter if you call them twice. +void freeOffsetsStruct(struct offsets **offsets); +void freeXexHeaderStruct(struct xexHeader **xexHeader); +void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader); +void freeSectionsStruct(struct sections *sections); +void freePeImportInfoStruct(struct peImportInfo *peImportInfo); +void freePeDataStruct(struct peData **peData); +void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries); +void freeOptHeadersStruct(struct optHeaders **optHeaders); +void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, + struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders); + // Functions used for file data manipulation uint32_t getNextAligned(uint32_t offset, uint32_t alignment); diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 5a2e096..44829dc 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -52,7 +52,7 @@ int checkForBranchStub(FILE *pe, struct peData *peData) // Read in the set of instructions to check uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); if(instructionBuffer == NULL) {return ERR_OUT_OF_MEM;} - if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) {free(instructionBuffer); return ERR_FILE_READ;} + if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) {nullAndFree((void**)&instructionBuffer); return ERR_FILE_READ;} #ifdef LITTLE_ENDIAN_SYSTEM // Byteswap the instructions @@ -99,12 +99,12 @@ int checkForBranchStub(FILE *pe, struct peData *peData) { // Found the branch stub for the current import, store the address uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); - if(currentBranchStubRVA == 0) {free(instructionBuffer); return ERR_INVALID_RVA_OR_OFFSET;} + if(currentBranchStubRVA == 0) {nullAndFree((void**)&instructionBuffer); return ERR_INVALID_RVA_OR_OFFSET;} peData->peImportInfo.tables[i].imports[j].branchStubAddr = peData->baseAddr + currentBranchStubRVA; peData->peImportInfo.tables[i].branchStubCount++; peData->peImportInfo.totalBranchStubCount++; - free(instructionBuffer); + nullAndFree((void**)&instructionBuffer); return SUCCESS; } } @@ -114,7 +114,7 @@ int checkForBranchStub(FILE *pe, struct peData *peData) } } - free(instructionBuffer); + nullAndFree((void**)&instructionBuffer); if(fseek(pe, -12, SEEK_CUR) != 0) {return ERR_FILE_READ;} return SUCCESS; } @@ -138,7 +138,7 @@ int getImports(FILE *pe, struct peData *peData) uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); if(currentIDT == NULL) {return ERR_OUT_OF_MEM;} uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons - if(blankIDT == NULL) {free(currentIDT); return ERR_OUT_OF_MEM;} + if(blankIDT == NULL) {nullAndFree((void**)¤tIDT); return ERR_OUT_OF_MEM;} if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} // While the IDT is not blank, process it @@ -147,7 +147,7 @@ int getImports(FILE *pe, struct peData *peData) // Allocate space for the current table data peData->peImportInfo.tableCount++; peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); - if(peData->peImportInfo.tables == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} + if(peData->peImportInfo.tables == NULL) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_OUT_OF_MEM;} memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank #ifdef BIG_ENDIAN_SYSTEM @@ -161,15 +161,15 @@ int getImports(FILE *pe, struct peData *peData) // Retrieve the name pointed to by the table uint32_t savedOffset = ftell(pe); uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); - if(tableNameOffset == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} - if(fseek(pe, tableNameOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(tableNameOffset == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + if(fseek(pe, tableNameOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} // Allocating is expensive, go 16 bytes at a time to avoid calling realloc an excessive number of times for(uint32_t j = 0;; j += 16) { peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); - if(peData->peImportInfo.tables[i].name == NULL) {free(currentIDT); free(blankIDT); return ERR_OUT_OF_MEM;} - if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(peData->peImportInfo.tables[i].name == NULL) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_OUT_OF_MEM;} + if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} // Check for a null terminator for(uint32_t k = j; k < j + 16; k++) @@ -185,11 +185,11 @@ int getImports(FILE *pe, struct peData *peData) // Seek to the IAT and read the first entry uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); - if(iatOffset == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} - if(fseek(pe, iatOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(iatOffset == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + if(fseek(pe, iatOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} uint32_t currentImport; - if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} // While the import is not blank, process it for(int j = 0; currentImport != 0; j++) @@ -207,8 +207,8 @@ int getImports(FILE *pe, struct peData *peData) // Check which type of import we have if(!(currentImport & PE_IMPORT_ORDINAL_FLAG)) { - free(currentIDT); - free(blankIDT); + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); return ERR_UNSUPPORTED_STRUCTURE; // We don't support import by name yet } else @@ -218,23 +218,23 @@ int getImports(FILE *pe, struct peData *peData) // Store the address of the current import entry in iatAddr uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); - if(currentImportRVA == 0) {free(currentIDT); free(blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} + if(currentImportRVA == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; // Read the next import - if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} } // Add table's import count to total and return to the next IDT entry to read peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; - if(fseek(pe, savedOffset, SEEK_SET) != 0) {free(currentIDT); free(blankIDT); return ERR_FILE_READ;} + if(fseek(pe, savedOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} // Read next IDT if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} } - free(currentIDT); - free(blankIDT); + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); // Find the branch stubs for(uint16_t i = 0; i < peData->sections.count; i++) diff --git a/src/main.c b/src/main.c index 4afecf6..69b78d0 100644 --- a/src/main.c +++ b/src/main.c @@ -163,7 +163,7 @@ int main(int argc, char **argv) if(pePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - if(xexfilePath != NULL) {free(xexfilePath);} + if(xexfilePath != NULL) {nullAndFree((void**)&xexfilePath);} return -1; } @@ -177,7 +177,7 @@ int main(int argc, char **argv) if(xexfilePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - if(pePath != NULL) {free(pePath);} + if(pePath != NULL) {nullAndFree((void**)&pePath);} return -1; } @@ -193,7 +193,7 @@ int main(int argc, char **argv) { if(gotOutput) { - free(xexfilePath); + nullAndFree((void**)&xexfilePath); } printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); @@ -203,7 +203,7 @@ int main(int argc, char **argv) { if(gotInput) { - free(pePath); + nullAndFree((void**)&pePath); } printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); @@ -216,12 +216,12 @@ int main(int argc, char **argv) if(pe == NULL) { printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); - free(pePath); - free(xexfilePath); + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); return -1; } - free(pePath); + nullAndFree((void**)&pePath); FILE *xex = fopen(xexfilePath, "wb+"); @@ -229,7 +229,7 @@ int main(int argc, char **argv) { printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); fclose(pe); - free(xexfilePath); + nullAndFree((void**)&xexfilePath); return -1; } @@ -239,22 +239,32 @@ int main(int argc, char **argv) int ret; - struct offsets offsets; - struct xexHeader xexHeader; - struct secInfoHeader secInfoHeader; - struct peData peData; - struct optHeaderEntries optHeaderEntries; - struct optHeaders optHeaders; - - // Make sure the import library size is initially zero - optHeaders.importLibraries.size = 0; + struct offsets *offsets = calloc(1, sizeof(struct offsets)); + struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader)); + struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader)); + struct peData *peData = calloc(1, sizeof(struct peData)); + struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); + struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); + if(offsets == NULL + || xexHeader == NULL + || secInfoHeader == NULL + || peData == NULL + || optHeaderEntries == NULL + || optHeaders == NULL) + { + // Don't bother freeing right now, errors will have their own function soon + printf("%s ERROR: Out of memory\n", PRINT_STEM); + return -1; + } + printf("%s Validating PE file...\n", PRINT_STEM); if(!validatePE(pe, skipMachineCheck)) { printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); - free(xexfilePath); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); return -1; @@ -264,7 +274,7 @@ int main(int argc, char **argv) // Reading in header data from PE printf("%s Retrieving header data from PE...\n", PRINT_STEM); - ret = getHdrData(pe, &peData, 0); + ret = getHdrData(pe, peData, 0); if(ret == ERR_UNKNOWN_DATA_REQUEST) { @@ -306,7 +316,7 @@ int main(int argc, char **argv) // Process imports printf("%s Retrieving import data from PE...\n", PRINT_STEM); - ret = getImports(pe, &peData); + ret = getImports(pe, peData); if(ret != SUCCESS) { @@ -323,7 +333,7 @@ int main(int argc, char **argv) if(basefilePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - free(xexfilePath); + nullAndFree((void**)&xexfilePath); fclose(pe); fclose(xex); return -1; @@ -333,8 +343,8 @@ int main(int argc, char **argv) strcat(basefilePath, ".basefile"); FILE* basefile = fopen(basefilePath, "wb+"); - free(xexfilePath); // *Now* we're done with it. - free(basefilePath); + nullAndFree((void**)&xexfilePath); // *Now* we're done with it. + nullAndFree((void**)&basefilePath); if(basefile == NULL) { @@ -345,7 +355,7 @@ int main(int argc, char **argv) } // Map the PE into the basefile (RVAs become offsets) - ret = mapPEToBasefile(pe, basefile, &peData); + ret = mapPEToBasefile(pe, basefile, peData); fclose(pe); if(ret == ERR_OUT_OF_MEM) @@ -360,13 +370,13 @@ int main(int argc, char **argv) // Setting final XEX data structs printf("%s Building XEX header...\n", PRINT_STEM); - setXEXHeader(&xexHeader, &peData); + setXEXHeader(xexHeader, peData); printf("%s Building security header...\n", PRINT_STEM); - setSecInfoHeader(&secInfoHeader, &peData); + setSecInfoHeader(secInfoHeader, peData); printf("%s Setting page descriptors...\n", PRINT_STEM); - ret = setPageDescriptors(basefile, &peData, &secInfoHeader); + ret = setPageDescriptors(basefile, peData, secInfoHeader); if(ret == ERR_OUT_OF_MEM) { @@ -377,7 +387,7 @@ int main(int argc, char **argv) } printf("%s Building optional headers...\n", PRINT_STEM); - ret = setOptHeaders(&secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); if(ret == ERR_OUT_OF_MEM) { @@ -389,7 +399,7 @@ int main(int argc, char **argv) // Setting data positions... printf("%s Aligning data...\n", PRINT_STEM); - ret = placeStructs(&offsets, &xexHeader, &optHeaderEntries, &secInfoHeader, &optHeaders); + ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); if(ret == ERR_OUT_OF_MEM) { @@ -401,7 +411,7 @@ int main(int argc, char **argv) // Write out all of the XEX data to file printf("%s Writing data to XEX file...\n", PRINT_STEM); - ret = writeXEX(&xexHeader, &optHeaderEntries, &secInfoHeader, &optHeaders, &offsets, basefile, xex); + ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); if(ret == ERR_OUT_OF_MEM) { @@ -422,5 +432,8 @@ int main(int argc, char **argv) fclose(xex); printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); + + // Free everything left and exit + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index d5d099b..c3e07e2 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -81,8 +81,8 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) peData->size = ftell(basefile); // We're done with these, free them - free(buffer); - free(sectionInfo); + nullAndFree((void**)&buffer); + nullAndFree((void**)§ionInfo); return SUCCESS; } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 0ce9caa..afacec8 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -70,5 +70,12 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st // Fifth optional header (system flags) optHeaderEntries->optHeaderEntry[3].id = XEX_OPT_ID_SYS_FLAGS; + + // We're using the name of the first element of the struct for clarity, + // it can never be an unaligned access, so ignore that warning. + // Also works for Clang. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Waddress-of-packed-member" setSysFlags(&(optHeaderEntries->optHeaderEntry[3].dataOrOffset)); + #pragma GCC diagnostic pop } diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 5e57a10..9494c86 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -37,7 +37,7 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3 int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *secInfoHeader) { uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; - secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); // The free() for this is called after written to XEX + secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); if(secInfoHeader->descriptors == NULL) {return ERR_OUT_OF_MEM;} // Setting size/info data and calculating hashes for page descriptors @@ -76,5 +76,5 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se } } - free(peData->sections.section); // Alloc'd in getdata + freeSectionsStruct(&(peData->sections)); } diff --git a/src/write/writexex.c b/src/write/writexex.c index 1007bd7..88dc476 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -1,7 +1,7 @@ // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // -// Copyright (c) 2024 Aiden Isik +// Copyright (c) 2024-25 Aiden Isik // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -50,8 +50,6 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); } - free(optHeaderEntries->optHeaderEntry); // Alloc'd in setdata (optheaders). Now we're done with it. - // Page descriptors fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET); @@ -66,8 +64,6 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(secInfoHeader->descriptors[i].sha1, sizeof(uint8_t), 0x14, xex); } - free(secInfoHeader->descriptors); // calloc'd elsewhere, freeing now - // Basefile fseek(pe, 0x0, SEEK_SET); fseek(xex, offsets->basefile, SEEK_SET); @@ -82,7 +78,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(buffer, sizeof(uint8_t), readBufSize, xex); } - free(buffer); + nullAndFree((void**)&buffer); // Security Info #ifdef LITTLE_ENDIAN_SYSTEM @@ -148,7 +144,6 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex); } - - free(offsets->optHeaders); // Alloc'd in placer. + return SUCCESS; } From c0607ecc490ab63f4c2d6fa99b7a38506db87c34 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 3 Jun 2025 18:24:07 +0100 Subject: [PATCH 08/38] Improve error handling in main, other misc bug fixes --- src/common/datastorage.c | 5 + src/common/datastorage.h | 3 + src/main.c | 220 +++++++++++++++++++++++++--------- src/setdata/optheaders.c | 2 + src/setdata/pagedescriptors.c | 2 +- src/setdata/populateheaders.c | 8 +- src/setdata/populateheaders.h | 4 +- src/write/headerhash.c | 4 +- src/write/headerhash.h | 4 +- 9 files changed, 188 insertions(+), 64 deletions(-) diff --git a/src/common/datastorage.c b/src/common/datastorage.c index ce8f972..380d395 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -34,6 +34,11 @@ void nullAndFree(void **ptr) // They can also be called individually, and it doesn't matter if you call them twice. // Each function checks if the struct going to be accessed is NULL, so it doesn't try // to access unallocated data. +// +// DO NOT CALL THESE OUTWITH MAIN. Those which take double pointers will replace +// the pointer given to them with null ONLY in the function they're called in, not it's callers +// (including main), which WILL result in a double free. +// // Even if it doesn't access data right now, it's still there so it's not forgotten // about if it becomes necessary with future additions. void freeOffsetsStruct(struct offsets **offsets) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 32a0578..64e7c5e 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -239,6 +239,9 @@ void nullAndFree(void **ptr); // These functions together handle freeing all of the main structs. // They can also be called individually, and it doesn't matter if you call them twice. +// DO NOT CALL THESE OUTWITH MAIN. Those which take double pointers will replace +// the pointer given to them with null ONLY in the function they're called in, not it's callers +// (including main), which WILL result in a double free. void freeOffsetsStruct(struct offsets **offsets); void freeXexHeaderStruct(struct xexHeader **xexHeader); void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader); diff --git a/src/main.c b/src/main.c index 69b78d0..dd8d3a2 100644 --- a/src/main.c +++ b/src/main.c @@ -16,6 +16,9 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// TODO: Harden all functions against bad data, unallocated data (check for NULL), etc. +// Some functions are not robust as I wanted things up and running as quickly as possible. + #include "common/common.h" #include "common/datastorage.h" #include "pemapper/pemapper.h" @@ -246,15 +249,14 @@ int main(int argc, char **argv) struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); - if(offsets == NULL - || xexHeader == NULL - || secInfoHeader == NULL - || peData == NULL - || optHeaderEntries == NULL - || optHeaders == NULL) + if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL + || optHeaderEntries == NULL || optHeaders == NULL) { - // Don't bother freeing right now, errors will have their own function soon - printf("%s ERROR: Out of memory\n", PRINT_STEM); + printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); return -1; } @@ -275,38 +277,36 @@ int main(int argc, char **argv) // Reading in header data from PE printf("%s Retrieving header data from PE...\n", PRINT_STEM); ret = getHdrData(pe, peData, 0); - - if(ret == ERR_UNKNOWN_DATA_REQUEST) + + if(ret != SUCCESS) { - printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); - fclose(pe); - fclose(xex); - return -1; - } - else if(ret == ERR_FILE_READ) - { - printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); - fclose(pe); - fclose(xex); - return -1; - } - else if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - fclose(pe); - fclose(xex); - return -1; - } - else if(ret == ERR_MISSING_SECTION_FLAG) - { - printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); - fclose(pe); - fclose(xex); - return -1; - } - else if(ret == ERR_UNSUPPORTED_STRUCTURE) - { - printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + if(ret == ERR_UNKNOWN_DATA_REQUEST) + { + printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_FILE_READ) + { + printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_MISSING_SECTION_FLAG) + { + printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_UNSUPPORTED_STRUCTURE) + { + printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); return -1; @@ -320,8 +320,32 @@ int main(int argc, char **argv) if(ret != SUCCESS) { - printf("TEMPORARY ERROR HANDLING: IMPORT DATA FAILED: %d\n", ret); - exit(-1); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_FILE_READ) + { + printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_INVALID_RVA_OR_OFFSET) + { + printf("%s ERROR: Invalid RVA or offset encountered (malformed PE). Aborting.\n", PRINT_STEM); + } + else if(ret == ERR_UNSUPPORTED_STRUCTURE) // Does import by name actually need special treatment or can this go?? + { + printf("%s ERROR: Import by name detected. This is currently unsupported. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; } printf("%s Got import data from PE!\n", PRINT_STEM); @@ -333,6 +357,7 @@ int main(int argc, char **argv) if(basefilePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); nullAndFree((void**)&xexfilePath); fclose(pe); fclose(xex); @@ -349,6 +374,7 @@ int main(int argc, char **argv) if(basefile == NULL) { printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); return -1; @@ -358,9 +384,18 @@ int main(int argc, char **argv) ret = mapPEToBasefile(pe, basefile, peData); fclose(pe); - if(ret == ERR_OUT_OF_MEM) + if(ret != SUCCESS) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); fclose(xex); return -1; @@ -370,52 +405,115 @@ int main(int argc, char **argv) // Setting final XEX data structs printf("%s Building XEX header...\n", PRINT_STEM); - setXEXHeader(xexHeader, peData); + ret = setXEXHeader(xexHeader, peData); + + if(ret != SUCCESS) + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } printf("%s Building security header...\n", PRINT_STEM); - setSecInfoHeader(secInfoHeader, peData); + ret = setSecInfoHeader(secInfoHeader, peData); + + if(ret != SUCCESS) + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } printf("%s Setting page descriptors...\n", PRINT_STEM); ret = setPageDescriptors(basefile, peData, secInfoHeader); - if(ret == ERR_OUT_OF_MEM) + if(ret != SUCCESS) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); fclose(xex); return -1; } + + // Done with this now + freeSectionsStruct(&(peData->sections)); printf("%s Building optional headers...\n", PRINT_STEM); ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); - if(ret == ERR_OUT_OF_MEM) + if(ret != SUCCESS) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); fclose(xex); return -1; } + + // Done with this now + freePeImportInfoStruct(&(peData->peImportInfo)); // Setting data positions... printf("%s Aligning data...\n", PRINT_STEM); ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); - if(ret == ERR_OUT_OF_MEM) + if(ret != SUCCESS) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); fclose(xex); return -1; } - + + // We're done with this now + freePeDataStruct(&peData); + // Write out all of the XEX data to file printf("%s Writing data to XEX file...\n", PRINT_STEM); ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); - - if(ret == ERR_OUT_OF_MEM) + + if(ret != SUCCESS) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + } + else + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + } + + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); fclose(xex); return -1; @@ -425,7 +523,17 @@ int main(int argc, char **argv) // Final pass (sets & writes header hash) printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); - setHeaderSha1(xex); + ret = setHeaderSha1(xex); + + if(ret != SUCCESS) + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + printf("%s Header SHA1 written!\n", PRINT_STEM); fclose(basefile); diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index afacec8..2ca79ca 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -78,4 +78,6 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st #pragma GCC diagnostic ignored "-Waddress-of-packed-member" setSysFlags(&(optHeaderEntries->optHeaderEntry[3].dataOrOffset)); #pragma GCC diagnostic pop + + return SUCCESS; } diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 9494c86..57b2cb7 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -76,5 +76,5 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se } } - freeSectionsStruct(&(peData->sections)); + return SUCCESS; } diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index bbb68df..60715db 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -18,7 +18,7 @@ #include "populateheaders.h" -void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) +int setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) { // Writing data into XEX header. strcpy(xexHeader->magic, "XEX2"); // Magic @@ -41,9 +41,11 @@ void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) } xexHeader->optHeaderCount = 0x4; // Hard-coding until more optional headers supported, then maybe it can be determined dynamically. + + return SUCCESS; } -void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) +int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) { // Writing data into security info header (much of this is derived from info in PE) secInfoHeader->peSize = peData->size; @@ -63,4 +65,6 @@ void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type. secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages) secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void*)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) + + return SUCCESS; } diff --git a/src/setdata/populateheaders.h b/src/setdata/populateheaders.h index a6fb05f..0e95a59 100644 --- a/src/setdata/populateheaders.h +++ b/src/setdata/populateheaders.h @@ -21,5 +21,5 @@ #include "../common/common.h" #include "../common/datastorage.h" -void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData); -void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData); +int setXEXHeader(struct xexHeader *xexHeader, struct peData *peData); +int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData); diff --git a/src/write/headerhash.c b/src/write/headerhash.c index 220accf..47fbb94 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -51,7 +51,7 @@ void setImportsSha1(FILE *xex) // So, it would probably be more sensible to endian-swap all of the data back and determine which structure is where // to determine the hash, but reading the file we just created is easier. -void setHeaderSha1(FILE *xex) +int setHeaderSha1(FILE *xex) { fseek(xex, 0x8, SEEK_SET); // Basefile offset uint32_t basefileOffset = get32BitFromXEX(xex); @@ -85,4 +85,6 @@ void setHeaderSha1(FILE *xex) // Finally, write it out fseek(xex, secInfoOffset + 0x164, SEEK_SET); // 0x164 == offset in secinfo of header hash fwrite(headerHash, sizeof(uint8_t), 20, xex); + + return SUCCESS; } diff --git a/src/write/headerhash.h b/src/write/headerhash.h index 0f08a0a..45a7c5d 100644 --- a/src/write/headerhash.h +++ b/src/write/headerhash.h @@ -1,7 +1,7 @@ // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // -// Copyright (c) 2024 Aiden Isik +// Copyright (c) 2024-25 Aiden Isik // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -23,4 +23,4 @@ #include "../common/datastorage.h" void setImportsSha1(FILE *xex); -void setHeaderSha1(FILE *xex); +int setHeaderSha1(FILE *xex); From aee6624e027162fcff17be4930df4df691e94340 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 3 Jun 2025 18:43:37 +0100 Subject: [PATCH 09/38] Rename getdata to gethdrdata --- CMakeLists.txt | 5 ++--- src/getdata/{getdata.c => gethdrdata.c} | 2 +- src/getdata/{getdata.h => gethdrdata.h} | 0 src/main.c | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) rename src/getdata/{getdata.c => gethdrdata.c} (99%) rename src/getdata/{getdata.h => gethdrdata.h} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5029413..d0529df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,8 @@ list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/crypto.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.c) - +list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/gethdrdata.h) +list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/gethdrdata.c) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.c) diff --git a/src/getdata/getdata.c b/src/getdata/gethdrdata.c similarity index 99% rename from src/getdata/getdata.c rename to src/getdata/gethdrdata.c index 6815032..1a27f3b 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/gethdrdata.c @@ -16,7 +16,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#include "getdata.h" +#include "gethdrdata.h" // Validate PE. This isn't thorough, but it's enough to catch any non-PE/360 files. // I was considering merging this with getHdrData and mapPEToBasefile as we're diff --git a/src/getdata/getdata.h b/src/getdata/gethdrdata.h similarity index 100% rename from src/getdata/getdata.h rename to src/getdata/gethdrdata.h diff --git a/src/main.c b/src/main.c index dd8d3a2..ff18e4a 100644 --- a/src/main.c +++ b/src/main.c @@ -22,7 +22,7 @@ #include "common/common.h" #include "common/datastorage.h" #include "pemapper/pemapper.h" -#include "getdata/getdata.h" +#include "getdata/gethdrdata.h" #include "getdata/getimports.h" #include "setdata/populateheaders.h" #include "setdata/pagedescriptors.h" From 080df81ef677c507619ce0efad21df47210a6a12 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 8 Jun 2025 16:42:13 +0100 Subject: [PATCH 10/38] Add code to populate XEX import libraries header (breaks things, need to fix) --- src/common/common.h | 2 + src/common/datastorage.c | 17 +++ src/common/datastorage.h | 18 ++- src/setdata/optheaders.c | 280 ++++++++++++++++++++++++++++++++++++--- src/setdata/optheaders.h | 1 + src/write/writexex.c | 2 + 6 files changed, 303 insertions(+), 17 deletions(-) diff --git a/src/common/common.h b/src/common/common.h index f1721f7..e74a926 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -52,5 +52,7 @@ #define ERR_OUT_OF_MEM -5 #define ERR_UNSUPPORTED_STRUCTURE -6 #define ERR_INVALID_RVA_OR_OFFSET -7 +#define ERR_INVALID_IMPORT_NAME -8 +#define ERR_DATA_OVERFLOW -9 void infoPrint(char *str); diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 380d395..eea7883 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -114,10 +114,27 @@ void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) } } +void freeImportLibrariesStruct(struct importLibraries *importLibraries) +{ + nullAndFree((void**)&(importLibraries->nameTable)); + + if(importLibraries->importTables != NULL) + { + for(uint32_t i = 0; i < importLibraries->tableCount; i++) + { + nullAndFree((void**)&(importLibraries->importTables[i].addresses)); + } + + nullAndFree((void**)&(importLibraries->nameTable)); + } +} + void freeOptHeadersStruct(struct optHeaders **optHeaders) { if(*optHeaders != NULL) { + nullAndFree((void**)&((*optHeaders)->importLibraries.nameTable)); + nullAndFree((void**)&((*optHeaders)->importLibraries.importTables)); nullAndFree((void**)optHeaders); } } diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 64e7c5e..aee8ff2 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -198,10 +198,26 @@ struct __attribute__((packed)) basefileFormat uint32_t zeroSize; }; +struct __attribute__((packed)) importTable +{ + uint32_t size; + uint8_t sha1[0x14]; + uint32_t unknown; // Unique to each executable imported from, and always the same + uint32_t targetVer; + uint32_t minimumVer; + uint8_t padding; + uint8_t tableIndex; + uint16_t addressCount; + uint32_t *addresses; // IAT entry address followed by branch stub code address for functions, just IAT address for other symbols +}; + struct __attribute__((packed)) importLibraries { uint32_t size; - uint8_t *data; + uint32_t nameTableSize; + uint32_t tableCount; + char *nameTable; + struct importTable *importTables; }; struct __attribute__((packed)) tlsInfo diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 2ca79ca..d75ad70 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -1,7 +1,7 @@ // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // -// Copyright (c) 2024 Aiden Isik +// Copyright (c) 2024-25 Aiden Isik // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -36,6 +36,232 @@ void setTLSInfo(struct tlsInfo *tlsInfo) tlsInfo->rawDataSize = 0x0; } +int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportInfo *peImportInfo, struct secInfoHeader *secInfoHeader) +{ + // Set table count and allocate enough memory for all tables + importLibraries->tableCount = peImportInfo->tableCount; + secInfoHeader->importTableCount = peImportInfo->tableCount; + importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); + if(importLibraries->importTables == NULL) {return ERR_OUT_OF_MEM;} + + // Initialise the size of the import libraries to just the size of the header (- 2 * sizeof(void*) to exclude addresses for internal use only) + importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); + + // Space to store names for use after the loop + char *names[importLibraries->tableCount]; + + // Populate each table, then compute it's hash and store in the previous table + for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--) + { + // Set the table index field to the current index + importLibraries->importTables[i].tableIndex = i; + + // Extract the name, target, and minimum versions from the name string + // Major and minor version are always 2 and 0, respectively + // Strings are definitely null-terminated as otherwise they couldn't have been read in + const uint8_t majorVer = 2; + const uint8_t minorVer = 0; + char *targetBuildVerStr = NULL; + char *targetHotfixVerStr = NULL; + char *minimumBuildVerStr = NULL; + char *minimumHotfixVerStr = NULL; + uint16_t buildVer; + uint8_t hotfixVer; + char *strtoulRet; + + // Get the name + uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); + names[i] = strtok(peImportInfo->tables[i].name, "@"); + if(strlen(names[i]) == oldNameLen || strlen(names[i]) < 1) + {return ERR_INVALID_IMPORT_NAME;} // Encountered '\0', not '@' + + // Target versions first + targetBuildVerStr = strtok(NULL, "."); + if(targetBuildVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} // No more tokens + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) + {return ERR_INVALID_IMPORT_NAME;} // Encountered null terminator instead of '.' + + buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); + if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr) + {return ERR_INVALID_IMPORT_NAME;} // Encountered a non-number, or string was empty + + targetHotfixVerStr = strtok(NULL, "+"); + if(targetHotfixVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) + {return ERR_INVALID_IMPORT_NAME;} + + hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); + if(*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) + {return ERR_INVALID_IMPORT_NAME;} + + // Now pack these into the target version bitfield + importLibraries->importTables[i].targetVer = + ((majorVer & 0xF) << (32 - 4)) + | ((minorVer & 0xF) << (32 - 4 - 4)) + | (buildVer << (32 - 4 - 4 - 16)) + | hotfixVer; + + // Now onto the minimum versions, this works much the same + minimumBuildVerStr = strtok(NULL, "."); + if(minimumBuildVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} // No more tokens + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + + 1 + strlen(minimumBuildVerStr) == oldNameLen) + {return ERR_INVALID_IMPORT_NAME;} // Encountered null terminator instead of '.' + + buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); + if(*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) + {return ERR_INVALID_IMPORT_NAME;} // Encountered a non-number, or string was empty + + minimumHotfixVerStr = strtok(NULL, "\0"); + if(minimumHotfixVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} + + hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); + if(*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) + {return ERR_INVALID_IMPORT_NAME;} + + // Now pack these into the minimum version bitfield + importLibraries->importTables[i].minimumVer = + ((majorVer & 0xF) << (32 - 4)) + | ((minorVer & 0xF) << (32 - 4 - 4)) + | (buildVer << (32 - 4 - 4 - 16)) + | hotfixVer; + + // Hardcode a currently unknown value. TODO: find out how this is calculated. + if(strcmp(names[i], "xboxkrnl.exe") == 0) + { + importLibraries->importTables[i].unknown = 0x45DC17E0; + } + else if(strcmp(names[i], "xam.xex") == 0) + { + importLibraries->importTables[i].unknown = 0xFCA15C76; + } + else if(strcmp(names[i], "xbdm.xex") == 0) + { + importLibraries->importTables[i].unknown = 0xECEB8109; + } + else + { + return ERR_UNSUPPORTED_STRUCTURE; + } + + // Determine the number of addresses (2 for functions, 1 for everything else) + importLibraries->importTables[i].addressCount = + (peImportInfo->tables[i].branchStubCount * 2) + + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + + // Allocate enough memory for the addresses + importLibraries->importTables[i].addresses = calloc(importLibraries->importTables[i].addressCount, sizeof(uint32_t)); + if(importLibraries->importTables[i].addresses == NULL) {return ERR_OUT_OF_MEM;} + + // Populate the addresses + uint16_t currentAddr = 0; + + for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) + { + if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) + { + if(currentAddr >= importLibraries->importTables[i].addressCount) + { + return ERR_DATA_OVERFLOW; + } + + importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].branchStubAddr; + currentAddr++; + } + + if(currentAddr >= importLibraries->importTables[i].addressCount) + { + return ERR_DATA_OVERFLOW; + } + + importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; + currentAddr++; + } + + // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) + importLibraries->importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importLibraries->importTables[i].addressCount * sizeof(uint32_t))); + importLibraries->size += importLibraries->importTables[i].size; + + // Init sha1 hash + struct sha1_ctx shaContext; + sha1_init(&shaContext); + + // On little endian this ensures the byteswapped address count doesn't cause any trouble when using it. + // On big endian it's pointless but here so the code isn't too complex with differences between endianness. + uint16_t addressCount = importLibraries->importTables[i].addressCount; + + // If we're on a little endian system, swap everything into big endian for hashing +#ifdef LITTLE_ENDIAN_SYSTEM + importLibraries->importTables[i].size = __builtin_bswap32(importLibraries->importTables[i].size); + importLibraries->importTables[i].unknown = __builtin_bswap32(importLibraries->importTables[i].unknown); + importLibraries->importTables[i].targetVer = __builtin_bswap32(importLibraries->importTables[i].targetVer); + importLibraries->importTables[i].minimumVer = __builtin_bswap32(importLibraries->importTables[i].minimumVer); + importLibraries->importTables[i].addressCount = __builtin_bswap16(importLibraries->importTables[i].addressCount); + + // Byteswap the addresses + for(uint16_t j = 0; j < addressCount; j++) + { + importLibraries->importTables[i].addresses[j] = __builtin_bswap32(importLibraries->importTables[i].addresses[j]); + } +#endif + + // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*), (void*)&(importLibraries->importTables[i])); + sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)importLibraries->importTables[i].addresses); + + // If we're on a little endian system, swap everything back into little endian +#ifdef LITTLE_ENDIAN_SYSTEM + importLibraries->importTables[i].size = __builtin_bswap32(importLibraries->importTables[i].size); + importLibraries->importTables[i].unknown = __builtin_bswap32(importLibraries->importTables[i].unknown); + importLibraries->importTables[i].targetVer = __builtin_bswap32(importLibraries->importTables[i].targetVer); + importLibraries->importTables[i].minimumVer = __builtin_bswap32(importLibraries->importTables[i].minimumVer); + importLibraries->importTables[i].addressCount = __builtin_bswap16(importLibraries->importTables[i].addressCount); + + // Byteswap the addresses + for(uint16_t j = 0; j < addressCount; j++) + { + importLibraries->importTables[i].addresses[j] = __builtin_bswap32(importLibraries->importTables[i].addresses[j]); + } +#endif + + if(i != 0) + { + sha1_digest(&shaContext, 0x14, importLibraries->importTables[i - 1].sha1); + } + else + { + sha1_digest(&shaContext, 0x14, secInfoHeader->importTableSha1); + } + } + + // Allocate enough memory for the name table + uint32_t nameTableLen = 0; + + for(uint32_t i = 0; i < importLibraries->tableCount; i++) + { + nameTableLen += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); + } + + importLibraries->nameTable = calloc(nameTableLen, sizeof(char)); + if(importLibraries->nameTable == NULL) {return ERR_OUT_OF_MEM;} + + // Populate the name table + for(uint32_t i = 0; i < importLibraries->tableCount; i++) + { + if(i == 0) + { + strcpy(importLibraries->nameTable, names[i]); + } + else + { + uint32_t nameTableOffset = getNextAligned(strlen(importLibraries->nameTable) + 1, sizeof(uint32_t)); + strcpy(&(importLibraries->nameTable[nameTableOffset]), names[i]); + } + } + + return SUCCESS; +} + // STUB. TODO: Dynamically select, and/or allow user to select, flags void setSysFlags(uint32_t *flags) { @@ -48,36 +274,58 @@ void setSysFlags(uint32_t *flags) int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders) { - // TODO: Dynamically select optional headers to use, instead of hard-coding + bool importsPresent = (peData->peImportInfo.totalImportCount > 0) ? true : false; + optHeaderEntries->count = 4; - optHeaderEntries->optHeaderEntry = calloc(5, sizeof(struct optHeaderEntry)); - if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;} + + if(importsPresent) + { + optHeaderEntries->count++; + } - // First optional header (basefile format) + optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry)); + if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;} + + uint32_t currentHeader = 0; + + // NOTE: Make sure that these headers are handled IN ORDER OF ID. The loader will reject the XEX if they are not. + + // Basefile format (0x003FF) setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader); - optHeaderEntries->optHeaderEntry[0].id = XEX_OPT_ID_BASEFILE_FORMAT; + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_BASEFILE_FORMAT; + currentHeader++; - // Second optional header (entrypoint) - optHeaderEntries->optHeaderEntry[1].id = XEX_OPT_ID_ENTRYPOINT; - optHeaderEntries->optHeaderEntry[1].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint; + // Entrypoint (0x10100) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_ENTRYPOINT; + optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint; + currentHeader++; - // Third optional header (import libs) - //optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_IMPORT_LIBS; + // Import libraries (0x103FF) + if(importsPresent) + { + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; + int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); + if(ret != SUCCESS) {return ret;} + currentHeader++; + } - // Fourth optional header (tls info) - optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_TLS_INFO; + // TLS info (0x20104) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_TLS_INFO; setTLSInfo(&(optHeaders->tlsInfo)); + currentHeader++; - // Fifth optional header (system flags) - optHeaderEntries->optHeaderEntry[3].id = XEX_OPT_ID_SYS_FLAGS; + // System flags (0x30000) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_SYS_FLAGS; // We're using the name of the first element of the struct for clarity, // it can never be an unaligned access, so ignore that warning. // Also works for Clang. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - setSysFlags(&(optHeaderEntries->optHeaderEntry[3].dataOrOffset)); + setSysFlags(&(optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset)); #pragma GCC diagnostic pop + //currentHeader++; + return SUCCESS; } diff --git a/src/setdata/optheaders.h b/src/setdata/optheaders.h index d7ade80..2a9033b 100644 --- a/src/setdata/optheaders.h +++ b/src/setdata/optheaders.h @@ -19,6 +19,7 @@ #pragma once #include "../common/common.h" +#include "../common/crypto.h" #include "../common/datastorage.h" int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders); diff --git a/src/write/writexex.c b/src/write/writexex.c index 88dc476..c3f2ea7 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -117,6 +117,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr currentHeader++; } + /* if(optHeaders->importLibraries.size != 0) { fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); @@ -130,6 +131,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(optHeaders->importLibraries.data, sizeof(uint8_t), importLibsSize - 0x4, xex); currentHeader++; } + */ if(optHeaders->tlsInfo.slotCount != 0) { From 1b62027120f2dcd001789dd560a7c8578e4f366d Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 8 Jun 2025 19:01:22 +0100 Subject: [PATCH 11/38] Fix name table stuff --- src/setdata/optheaders.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index d75ad70..7370d2d 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -235,28 +235,21 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn } // Allocate enough memory for the name table - uint32_t nameTableLen = 0; + uint32_t nameOffsets[importLibraries->tableCount]; for(uint32_t i = 0; i < importLibraries->tableCount; i++) { - nameTableLen += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); + nameOffsets[i] = importLibraries->nameTableSize; + importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); } - importLibraries->nameTable = calloc(nameTableLen, sizeof(char)); + importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); if(importLibraries->nameTable == NULL) {return ERR_OUT_OF_MEM;} // Populate the name table for(uint32_t i = 0; i < importLibraries->tableCount; i++) { - if(i == 0) - { - strcpy(importLibraries->nameTable, names[i]); - } - else - { - uint32_t nameTableOffset = getNextAligned(strlen(importLibraries->nameTable) + 1, sizeof(uint32_t)); - strcpy(&(importLibraries->nameTable[nameTableOffset]), names[i]); - } + strcpy(&(importLibraries->nameTable[nameOffsets[i]]), names[i]); } return SUCCESS; From 81b649ca3252323ef5b2f23966052be1929c3b48 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 9 Jun 2025 13:47:58 +0100 Subject: [PATCH 12/38] Finish import table generation --- src/common/datastorage.c | 11 ++--------- src/main.c | 26 ++++++++++++------------ src/placer/placer.c | 10 ++++------ src/setdata/optheaders.c | 20 ++++++++++--------- src/setdata/populateheaders.c | 4 ++-- src/setdata/populateheaders.h | 2 +- src/write/writexex.c | 37 ++++++++++++++++++++++++++++++----- 7 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/common/datastorage.c b/src/common/datastorage.c index eea7883..f22a75e 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -62,13 +62,7 @@ void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) { if(*secInfoHeader != NULL) { - // Ignore the unaligned pointer warning here, every field before descriptors is a multiple of 8. - // Also works for Clang. - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" nullAndFree((void**)&((*secInfoHeader)->descriptors)); - #pragma GCC diagnostic pop - nullAndFree((void**)secInfoHeader); } } @@ -125,7 +119,7 @@ void freeImportLibrariesStruct(struct importLibraries *importLibraries) nullAndFree((void**)&(importLibraries->importTables[i].addresses)); } - nullAndFree((void**)&(importLibraries->nameTable)); + nullAndFree((void**)&(importLibraries->importTables)); } } @@ -133,8 +127,7 @@ void freeOptHeadersStruct(struct optHeaders **optHeaders) { if(*optHeaders != NULL) { - nullAndFree((void**)&((*optHeaders)->importLibraries.nameTable)); - nullAndFree((void**)&((*optHeaders)->importLibraries.importTables)); + freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); nullAndFree((void**)optHeaders); } } diff --git a/src/main.c b/src/main.c index ff18e4a..594d958 100644 --- a/src/main.c +++ b/src/main.c @@ -403,19 +403,7 @@ int main(int argc, char **argv) printf("%s Created basefile!\n", PRINT_STEM); - // Setting final XEX data structs - printf("%s Building XEX header...\n", PRINT_STEM); - ret = setXEXHeader(xexHeader, peData); - - if(ret != SUCCESS) - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - + // Setting final XEX data structs printf("%s Building security header...\n", PRINT_STEM); ret = setSecInfoHeader(secInfoHeader, peData); @@ -471,6 +459,18 @@ int main(int argc, char **argv) return -1; } + printf("%s Building XEX header...\n", PRINT_STEM); + ret = setXEXHeader(xexHeader, optHeaderEntries, peData); + + if(ret != SUCCESS) + { + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + // Done with this now freePeImportInfoStruct(&(peData->peImportInfo)); diff --git a/src/placer/placer.c b/src/placer/placer.c index baf4854..48eac61 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -62,7 +62,6 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea return SUCCESS; } -// Todo in future: implement a dynamic optional header selection mechanism instead of hard-coding the basic 5 int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct secInfoHeader *secInfoHeader, struct optHeaders *optHeaders) { // XEX Header @@ -75,14 +74,13 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry); // Security header - currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers etc + currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries offsets->secInfoHeader = currentOffset; xexHeader->secInfoOffset = currentOffset; currentOffset += (sizeof(struct secInfoHeader) - sizeof(void*)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); // Optional headers (minus imports) struct importLibIdcs importLibIdcs; - uint32_t importLibsIdx; // Entry in opt header entries of import libs int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs); if(ret != SUCCESS) @@ -90,7 +88,7 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op return ret; } - //currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports + currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports // PE basefile currentOffset = getNextAligned(currentOffset, 0x1000); // 4KiB alignment for basefile @@ -98,8 +96,8 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op xexHeader->peOffset = currentOffset; // Imports, the end of this header is aligned to the start of the basefile, so they are a special case - //offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; - //optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; + offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; + optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; return SUCCESS; } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 7370d2d..0a46f45 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -158,6 +158,14 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { + if(currentAddr >= importLibraries->importTables[i].addressCount) + { + return ERR_DATA_OVERFLOW; + } + + importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; + currentAddr++; + if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) { if(currentAddr >= importLibraries->importTables[i].addressCount) @@ -168,14 +176,6 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].branchStubAddr; currentAddr++; } - - if(currentAddr >= importLibraries->importTables[i].addressCount) - { - return ERR_DATA_OVERFLOW; - } - - importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; - currentAddr++; } // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) @@ -206,7 +206,8 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn #endif // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) - sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*), (void*)&(importLibraries->importTables[i])); + // +/- sizeof(uint32_t) to exclude table size from hash + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&(importLibraries->importTables[i]) + sizeof(uint32_t)); sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)importLibraries->importTables[i].addresses); // If we're on a little endian system, swap everything back into little endian @@ -243,6 +244,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); } + importLibraries->size += importLibraries->nameTableSize; importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); if(importLibraries->nameTable == NULL) {return ERR_OUT_OF_MEM;} diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 60715db..1021b8c 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -18,7 +18,7 @@ #include "populateheaders.h" -int setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) +int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData) { // Writing data into XEX header. strcpy(xexHeader->magic, "XEX2"); // Magic @@ -40,7 +40,7 @@ int setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions } - xexHeader->optHeaderCount = 0x4; // Hard-coding until more optional headers supported, then maybe it can be determined dynamically. + xexHeader->optHeaderCount = optHeaderEntries->count; return SUCCESS; } diff --git a/src/setdata/populateheaders.h b/src/setdata/populateheaders.h index 0e95a59..e98dab2 100644 --- a/src/setdata/populateheaders.h +++ b/src/setdata/populateheaders.h @@ -21,5 +21,5 @@ #include "../common/common.h" #include "../common/datastorage.h" -int setXEXHeader(struct xexHeader *xexHeader, struct peData *peData); +int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData); int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData); diff --git a/src/write/writexex.c b/src/write/writexex.c index c3f2ea7..3859bf5 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -117,21 +117,48 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr currentHeader++; } - /* if(optHeaders->importLibraries.size != 0) { fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); - uint32_t importLibsSize = optHeaders->importLibraries.size; // Need to use this value, so save it before we endian-swap + + // Write the main header first + // Save the values we need to use before byteswapping + uint32_t nameTableSize = optHeaders->importLibraries.nameTableSize; + uint32_t tableCount = optHeaders->importLibraries.tableCount; #ifdef LITTLE_ENDIAN_SYSTEM optHeaders->importLibraries.size = __builtin_bswap32(optHeaders->importLibraries.size); + optHeaders->importLibraries.nameTableSize = __builtin_bswap32(optHeaders->importLibraries.nameTableSize); + optHeaders->importLibraries.tableCount = __builtin_bswap32(optHeaders->importLibraries.tableCount); #endif - fwrite(&(optHeaders->importLibraries.size), sizeof(uint8_t), 0x4, xex); - fwrite(optHeaders->importLibraries.data, sizeof(uint8_t), importLibsSize - 0x4, xex); + fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void*)), xex); + fwrite(optHeaders->importLibraries.nameTable, sizeof(uint8_t), nameTableSize, xex); + + // Now write each import table + for(uint32_t i = 0; i < tableCount; i++) + { + uint16_t addressCount = optHeaders->importLibraries.importTables[i].addressCount; + +#ifdef LITTLE_ENDIAN_SYSTEM + optHeaders->importLibraries.importTables[i].size = __builtin_bswap32(optHeaders->importLibraries.importTables[i].size); + optHeaders->importLibraries.importTables[i].unknown = __builtin_bswap32(optHeaders->importLibraries.importTables[i].unknown); + optHeaders->importLibraries.importTables[i].targetVer = __builtin_bswap32(optHeaders->importLibraries.importTables[i].targetVer); + optHeaders->importLibraries.importTables[i].minimumVer = __builtin_bswap32(optHeaders->importLibraries.importTables[i].minimumVer); + optHeaders->importLibraries.importTables[i].addressCount = __builtin_bswap16(optHeaders->importLibraries.importTables[i].addressCount); + + for(uint16_t j = 0; j < addressCount; j++) + { + optHeaders->importLibraries.importTables[i].addresses[j] = __builtin_bswap32(optHeaders->importLibraries.importTables[i].addresses[j]); + } +#endif + + fwrite(&(optHeaders->importLibraries.importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); + fwrite(optHeaders->importLibraries.importTables[i].addresses, sizeof(uint32_t), addressCount, xex); + } + currentHeader++; } - */ if(optHeaders->tlsInfo.slotCount != 0) { From 32bdb848873cb05942a275044623bfcc1f547ff6 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 9 Jun 2025 14:57:06 +0100 Subject: [PATCH 13/38] Fix unaligned pointer accesses and buffer overflow bug in freeImportLibrariesStruct --- src/common/datastorage.c | 18 +++++++--- src/setdata/optheaders.c | 68 ++++++++++++++++++----------------- src/setdata/pagedescriptors.c | 11 +++--- src/write/writexex.c | 50 +++++++++++++++++--------- 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/common/datastorage.c b/src/common/datastorage.c index f22a75e..e55dc99 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -62,7 +62,9 @@ void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) { if(*secInfoHeader != NULL) { - nullAndFree((void**)&((*secInfoHeader)->descriptors)); + struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer + nullAndFree((void**)&descriptors); + //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways nullAndFree((void**)secInfoHeader); } } @@ -110,16 +112,22 @@ void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) void freeImportLibrariesStruct(struct importLibraries *importLibraries) { - nullAndFree((void**)&(importLibraries->nameTable)); + struct importTable *importTables = importLibraries->importTables; + char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers - if(importLibraries->importTables != NULL) + nullAndFree((void**)&nameTable); + + if(importTables != NULL) { for(uint32_t i = 0; i < importLibraries->tableCount; i++) { - nullAndFree((void**)&(importLibraries->importTables[i].addresses)); + uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer + nullAndFree((void**)&addresses); + importTables[i].addresses = addresses; } - nullAndFree((void**)&(importLibraries->importTables)); + nullAndFree((void**)&importTables); + importLibraries->importTables = importTables; } } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 0a46f45..642a015 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -1,3 +1,4 @@ + // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // @@ -43,6 +44,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn secInfoHeader->importTableCount = peImportInfo->tableCount; importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); if(importLibraries->importTables == NULL) {return ERR_OUT_OF_MEM;} + struct importTable *importTables = importLibraries->importTables; // Use this to avoid dereferencing an unaligned pointer // Initialise the size of the import libraries to just the size of the header (- 2 * sizeof(void*) to exclude addresses for internal use only) importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); @@ -54,7 +56,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--) { // Set the table index field to the current index - importLibraries->importTables[i].tableIndex = i; + importTables[i].tableIndex = i; // Extract the name, target, and minimum versions from the name string // Major and minor version are always 2 and 0, respectively @@ -95,7 +97,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn {return ERR_INVALID_IMPORT_NAME;} // Now pack these into the target version bitfield - importLibraries->importTables[i].targetVer = + importTables[i].targetVer = ((majorVer & 0xF) << (32 - 4)) | ((minorVer & 0xF) << (32 - 4 - 4)) | (buildVer << (32 - 4 - 4 - 16)) @@ -120,7 +122,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn {return ERR_INVALID_IMPORT_NAME;} // Now pack these into the minimum version bitfield - importLibraries->importTables[i].minimumVer = + importTables[i].minimumVer = ((majorVer & 0xF) << (32 - 4)) | ((minorVer & 0xF) << (32 - 4 - 4)) | (buildVer << (32 - 4 - 4 - 16)) @@ -129,15 +131,15 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn // Hardcode a currently unknown value. TODO: find out how this is calculated. if(strcmp(names[i], "xboxkrnl.exe") == 0) { - importLibraries->importTables[i].unknown = 0x45DC17E0; + importTables[i].unknown = 0x45DC17E0; } else if(strcmp(names[i], "xam.xex") == 0) { - importLibraries->importTables[i].unknown = 0xFCA15C76; + importTables[i].unknown = 0xFCA15C76; } else if(strcmp(names[i], "xbdm.xex") == 0) { - importLibraries->importTables[i].unknown = 0xECEB8109; + importTables[i].unknown = 0xECEB8109; } else { @@ -145,42 +147,43 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn } // Determine the number of addresses (2 for functions, 1 for everything else) - importLibraries->importTables[i].addressCount = + importTables[i].addressCount = (peImportInfo->tables[i].branchStubCount * 2) + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); // Allocate enough memory for the addresses - importLibraries->importTables[i].addresses = calloc(importLibraries->importTables[i].addressCount, sizeof(uint32_t)); - if(importLibraries->importTables[i].addresses == NULL) {return ERR_OUT_OF_MEM;} + importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); + if(importTables[i].addresses == NULL) {return ERR_OUT_OF_MEM;} + uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer // Populate the addresses uint16_t currentAddr = 0; for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { - if(currentAddr >= importLibraries->importTables[i].addressCount) + if(currentAddr >= importTables[i].addressCount) { return ERR_DATA_OVERFLOW; } - importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; + addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; currentAddr++; if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) { - if(currentAddr >= importLibraries->importTables[i].addressCount) + if(currentAddr >= importTables[i].addressCount) { return ERR_DATA_OVERFLOW; } - importLibraries->importTables[i].addresses[currentAddr] = peImportInfo->tables[i].imports[j].branchStubAddr; + addresses[currentAddr] = peImportInfo->tables[i].imports[j].branchStubAddr; currentAddr++; } } // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) - importLibraries->importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importLibraries->importTables[i].addressCount * sizeof(uint32_t))); - importLibraries->size += importLibraries->importTables[i].size; + importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); + importLibraries->size += importTables[i].size; // Init sha1 hash struct sha1_ctx shaContext; @@ -188,46 +191,46 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn // On little endian this ensures the byteswapped address count doesn't cause any trouble when using it. // On big endian it's pointless but here so the code isn't too complex with differences between endianness. - uint16_t addressCount = importLibraries->importTables[i].addressCount; + uint16_t addressCount = importTables[i].addressCount; // If we're on a little endian system, swap everything into big endian for hashing #ifdef LITTLE_ENDIAN_SYSTEM - importLibraries->importTables[i].size = __builtin_bswap32(importLibraries->importTables[i].size); - importLibraries->importTables[i].unknown = __builtin_bswap32(importLibraries->importTables[i].unknown); - importLibraries->importTables[i].targetVer = __builtin_bswap32(importLibraries->importTables[i].targetVer); - importLibraries->importTables[i].minimumVer = __builtin_bswap32(importLibraries->importTables[i].minimumVer); - importLibraries->importTables[i].addressCount = __builtin_bswap16(importLibraries->importTables[i].addressCount); + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); // Byteswap the addresses for(uint16_t j = 0; j < addressCount; j++) { - importLibraries->importTables[i].addresses[j] = __builtin_bswap32(importLibraries->importTables[i].addresses[j]); + addresses[j] = __builtin_bswap32(addresses[j]); } #endif // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) // +/- sizeof(uint32_t) to exclude table size from hash - sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&(importLibraries->importTables[i]) + sizeof(uint32_t)); - sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)importLibraries->importTables[i].addresses); + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&(importTables[i]) + sizeof(uint32_t)); + sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); // If we're on a little endian system, swap everything back into little endian #ifdef LITTLE_ENDIAN_SYSTEM - importLibraries->importTables[i].size = __builtin_bswap32(importLibraries->importTables[i].size); - importLibraries->importTables[i].unknown = __builtin_bswap32(importLibraries->importTables[i].unknown); - importLibraries->importTables[i].targetVer = __builtin_bswap32(importLibraries->importTables[i].targetVer); - importLibraries->importTables[i].minimumVer = __builtin_bswap32(importLibraries->importTables[i].minimumVer); - importLibraries->importTables[i].addressCount = __builtin_bswap16(importLibraries->importTables[i].addressCount); + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); // Byteswap the addresses for(uint16_t j = 0; j < addressCount; j++) { - importLibraries->importTables[i].addresses[j] = __builtin_bswap32(importLibraries->importTables[i].addresses[j]); + addresses[j] = __builtin_bswap32(addresses[j]); } #endif if(i != 0) { - sha1_digest(&shaContext, 0x14, importLibraries->importTables[i - 1].sha1); + sha1_digest(&shaContext, 0x14, importTables[i - 1].sha1); } else { @@ -247,11 +250,12 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importLibraries->size += importLibraries->nameTableSize; importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); if(importLibraries->nameTable == NULL) {return ERR_OUT_OF_MEM;} + char *nameTable = importLibraries->nameTable; // Use this to avoid dereferencing an unaligned pointer // Populate the name table for(uint32_t i = 0; i < importLibraries->tableCount; i++) { - strcpy(&(importLibraries->nameTable[nameOffsets[i]]), names[i]); + strcpy(&(nameTable[nameOffsets[i]]), names[i]); } return SUCCESS; diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 57b2cb7..5ab7a79 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -39,12 +39,13 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); if(secInfoHeader->descriptors == NULL) {return ERR_OUT_OF_MEM;} + struct pageDescriptor *descriptors = secInfoHeader->descriptors; // So we don't dereference an unaligned pointer // Setting size/info data and calculating hashes for page descriptors for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) { // Get page type (rwx) - secInfoHeader->descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); + descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); // Init sha1 hash struct sha1_ctx shaContext; @@ -56,19 +57,19 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se // For little endian systems, swap into big endian for hashing, then back (to keep struct endianness consistent) #ifdef LITTLE_ENDIAN_SYSTEM - secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif sha1_update(&shaContext, pageSize, page); - sha1_update(&shaContext, 0x18, (uint8_t*)&secInfoHeader->descriptors[i]); + sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); #ifdef LITTLE_ENDIAN_SYSTEM - secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif if(i != 0) { - sha1_digest(&shaContext, 0x14, secInfoHeader->descriptors[i - 1].sha1); + sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); } else { diff --git a/src/write/writexex.c b/src/write/writexex.c index 3859bf5..4f4bcd8 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -52,16 +52,19 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr // Page descriptors fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET); + + // So we don't try to dereference an unaligned pointer + struct pageDescriptor *descriptors = secInfoHeader->descriptors; for(int i = 0; i < secInfoHeader->pageDescCount; i++) { #ifdef LITTLE_ENDIAN_SYSTEM - secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif // Writing out current descriptor... - fwrite(&(secInfoHeader->descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex); - fwrite(secInfoHeader->descriptors[i].sha1, sizeof(uint8_t), 0x14, xex); + fwrite(&(descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex); + fwrite(descriptors[i].sha1, sizeof(uint8_t), 0x14, xex); } // Basefile @@ -122,6 +125,10 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); // Write the main header first + + // Use these to avoid dereferencing an unaligned pointer + char *nameTable = optHeaders->importLibraries.nameTable; + // Save the values we need to use before byteswapping uint32_t nameTableSize = optHeaders->importLibraries.nameTableSize; uint32_t tableCount = optHeaders->importLibraries.tableCount; @@ -133,28 +140,37 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr #endif fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void*)), xex); - fwrite(optHeaders->importLibraries.nameTable, sizeof(uint8_t), nameTableSize, xex); - - // Now write each import table - for(uint32_t i = 0; i < tableCount; i++) - { - uint16_t addressCount = optHeaders->importLibraries.importTables[i].addressCount; + fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex); #ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->importLibraries.importTables[i].size = __builtin_bswap32(optHeaders->importLibraries.importTables[i].size); - optHeaders->importLibraries.importTables[i].unknown = __builtin_bswap32(optHeaders->importLibraries.importTables[i].unknown); - optHeaders->importLibraries.importTables[i].targetVer = __builtin_bswap32(optHeaders->importLibraries.importTables[i].targetVer); - optHeaders->importLibraries.importTables[i].minimumVer = __builtin_bswap32(optHeaders->importLibraries.importTables[i].minimumVer); - optHeaders->importLibraries.importTables[i].addressCount = __builtin_bswap16(optHeaders->importLibraries.importTables[i].addressCount); + // Restore the table count (we require it to free the import libraries struct later) + optHeaders->importLibraries.tableCount = tableCount; +#endif + + // Now write each import table + // Use this to avoid dereferencing an unaligned pointer + struct importTable *importTables = optHeaders->importLibraries.importTables; + + for(uint32_t i = 0; i < tableCount; i++) + { + uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer + uint16_t addressCount = importTables[i].addressCount; + +#ifdef LITTLE_ENDIAN_SYSTEM + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); for(uint16_t j = 0; j < addressCount; j++) { - optHeaders->importLibraries.importTables[i].addresses[j] = __builtin_bswap32(optHeaders->importLibraries.importTables[i].addresses[j]); + addresses[j] = __builtin_bswap32(addresses[j]); } #endif - fwrite(&(optHeaders->importLibraries.importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); - fwrite(optHeaders->importLibraries.importTables[i].addresses, sizeof(uint32_t), addressCount, xex); + fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); + fwrite(addresses, sizeof(uint32_t), addressCount, xex); } currentHeader++; From d82fd383f05978b77d39ebf44c3153466288f5c3 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 9 Jun 2025 19:10:55 +0100 Subject: [PATCH 14/38] Rework executable type logic, add command line option to override it --- src/common/datastorage.h | 1 - src/getdata/getimports.c | 12 ----- src/main.c | 83 +++++++++++++++++++++++++---------- src/setdata/optheaders.c | 1 - src/setdata/populateheaders.c | 39 ++++++++++------ 5 files changed, 85 insertions(+), 51 deletions(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index aee8ff2..e1fd3f9 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -93,7 +93,6 @@ struct section struct peImport { - bool ordinal; uint32_t iatAddr; uint32_t branchStubAddr; }; diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 44829dc..9b08280 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -204,18 +204,6 @@ int getImports(FILE *pe, struct peData *peData) peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Make sure this is zeroed, we rely on it later - // Check which type of import we have - if(!(currentImport & PE_IMPORT_ORDINAL_FLAG)) - { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_UNSUPPORTED_STRUCTURE; // We don't support import by name yet - } - else - { - peData->peImportInfo.tables[i].imports[j].ordinal = true; - } - // Store the address of the current import entry in iatAddr uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); if(currentImportRVA == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} diff --git a/src/main.c b/src/main.c index 594d958..b9d263b 100644 --- a/src/main.c +++ b/src/main.c @@ -110,7 +110,8 @@ void dispHelp(char **argv) printf("-l,\t--libs,\t\t\tShow licensing information of libraries used\n"); printf("-s,\t--skip-machine-check,\tSkip the PE file machine ID check\n"); printf("-i,\t--input,\t\tSpecify input PE file path\n"); - printf("-o,\t--output,\t\tSpecify output XEX file path\n\n"); + printf("-o,\t--output,\t\tSpecify output XEX file path\n"); + printf("-t,\t--type,\t\t\tOverride automatic executable type detection\n\t\t\t\t(options: title, titledll, sysdll, dll)\n\n"); } int main(int argc, char **argv) @@ -123,9 +124,25 @@ int main(int argc, char **argv) {"skip-machine-check", no_argument, 0, 0}, {"input", required_argument, 0, 0}, {"output", required_argument, 0, 0}, + {"type", required_argument, 0, 0}, {0, 0, 0, 0} }; + struct offsets *offsets = calloc(1, sizeof(struct offsets)); + struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader)); + struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader)); + struct peData *peData = calloc(1, sizeof(struct peData)); + struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); + struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); + + if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL + || optHeaderEntries == NULL || optHeaders == NULL) + { + printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + return -1; + } + int optIndex = 0; int option = 0; @@ -136,21 +153,24 @@ int main(int argc, char **argv) char *pePath = NULL; char *xexfilePath = NULL; - while((option = getopt_long(argc, argv, "hvlsi:o:", longOptions, &optIndex)) != -1) + while((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) { if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) { dispHelp(argv); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) { dispVer(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) { dispLibs(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) @@ -166,11 +186,13 @@ int main(int argc, char **argv) if(pePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - if(xexfilePath != NULL) {nullAndFree((void**)&xexfilePath);} + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } strncpy(pePath, optarg, strlen(optarg) + 1); + pePath[strlen(optarg)] = '\0'; } else if(option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) { @@ -180,11 +202,45 @@ int main(int argc, char **argv) if(xexfilePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - if(pePath != NULL) {nullAndFree((void**)&pePath);} + nullAndFree((void**)&pePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } strncpy(xexfilePath, optarg, strlen(optarg) + 1); + xexfilePath[strlen(optarg)] = '\0'; + } + else if(option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) + { + if(strcmp(optarg, "title") == 0) + { + // Overriding type with "title" + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; + } + else if(strcmp(optarg, "titledll") == 0) + { + // Overriding type with "titledll" + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; + } + else if(strcmp(optarg, "sysdll") == 0) + { + // Overriding type with "sysdll" + xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; + printf("moduleFlags: 0x%.8X\n", xexHeader->moduleFlags); + } + else if(strcmp(optarg, "dll") == 0) + { + // Overriding type with "dll" + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; + } + else + { + printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", PRINT_STEM, optarg); + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + return -1; + } } } @@ -241,25 +297,6 @@ int main(int argc, char **argv) //free(xexfilePath); int ret; - - struct offsets *offsets = calloc(1, sizeof(struct offsets)); - struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader)); - struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader)); - struct peData *peData = calloc(1, sizeof(struct peData)); - struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); - struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); - - if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL - || optHeaderEntries == NULL || optHeaders == NULL) - { - printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - printf("%s Validating PE file...\n", PRINT_STEM); if(!validatePE(pe, skipMachineCheck)) diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 642a015..8e2d998 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -1,4 +1,3 @@ - // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 1021b8c..5ecaad1 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -1,3 +1,4 @@ + // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // @@ -21,23 +22,33 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData) { // Writing data into XEX header. - strcpy(xexHeader->magic, "XEX2"); // Magic + strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic // Module flags (type of executable) - xexHeader->moduleFlags = 0; - - if(peData->characteristics & PE_CHAR_FLAG_DLL) + // I'm not sure if this is determined properly, but it should be accurate most of the time + if(xexHeader->moduleFlags == 0) { - xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL - } - else - { - xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title - } - - if(peData->peExportInfo.count > 0) - { - xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions + printf("HERE!\n"); + if(peData->baseAddr < 0x90000000 && peData->peExportInfo.count > 0) + { + // Title DLL + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL | XEX_MOD_FLAG_TITLE; + } + else if(peData->baseAddr >= 0x90000000 && peData->peExportInfo.count > 0) + { + // System DLL + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL | XEX_MOD_FLAG_EXPORTS; + } + else if(peData->baseAddr >= 0x90000000) + { + // DLL + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; + } + else + { + // Just a regular title + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; + } } xexHeader->optHeaderCount = optHeaderEntries->count; From 1dab6189beefefbd9d6aed7a1b63b5c22195d607 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 9 Jun 2025 20:17:57 +0100 Subject: [PATCH 15/38] Remove some debugging stuff --- src/main.c | 1 - src/setdata/populateheaders.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main.c b/src/main.c index b9d263b..e6b0af8 100644 --- a/src/main.c +++ b/src/main.c @@ -226,7 +226,6 @@ int main(int argc, char **argv) { // Overriding type with "sysdll" xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; - printf("moduleFlags: 0x%.8X\n", xexHeader->moduleFlags); } else if(strcmp(optarg, "dll") == 0) { diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 5ecaad1..1b0ff74 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -1,4 +1,3 @@ - // This file is part of SynthXEX, one component of the // FreeChainXenon development toolchain // @@ -28,7 +27,6 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader // I'm not sure if this is determined properly, but it should be accurate most of the time if(xexHeader->moduleFlags == 0) { - printf("HERE!\n"); if(peData->baseAddr < 0x90000000 && peData->peExportInfo.count > 0) { // Title DLL From 75e2a58ade77c64926f1ae9631f703efa73fb630 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 9 Jun 2025 21:58:32 +0100 Subject: [PATCH 16/38] Use PE DLL flag to decide whether to use DLL or Title flag (more accurate?) --- src/setdata/populateheaders.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 1b0ff74..4f95228 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -37,7 +37,7 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader // System DLL xexHeader->moduleFlags = XEX_MOD_FLAG_DLL | XEX_MOD_FLAG_EXPORTS; } - else if(peData->baseAddr >= 0x90000000) + else if(peData->characteristics & PE_CHAR_FLAG_DLL) { // DLL xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; From 7b1d147cf01896eb490497227ce4efaee960fe60 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 10 Jun 2025 16:52:07 +0100 Subject: [PATCH 17/38] Fix bug where if there are no import tables, the location of the first optional header overwrites the basefile --- src/placer/placer.c | 7 +++++-- src/setdata/populateheaders.c | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/placer/placer.c b/src/placer/placer.c index 48eac61..cebf076 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -96,8 +96,11 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op xexHeader->peOffset = currentOffset; // Imports, the end of this header is aligned to the start of the basefile, so they are a special case - offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; - optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; + if(optHeaders->importLibraries.tableCount > 0) + { + offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; + optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; + } return SUCCESS; } diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 4f95228..ad0b0a9 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -59,15 +59,14 @@ int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) // Writing data into security info header (much of this is derived from info in PE) secInfoHeader->peSize = peData->size; - // Setting signature. Clearing first to ensure no memory contents are written to file, then adding identifier. - memset(secInfoHeader->signature, 0, sizeof(secInfoHeader->signature)); + // Setting signature (just a SynthXEX version identifier) strcpy(secInfoHeader->signature, VERSION_STRING); secInfoHeader->imageInfoSize = 0x174; // Image info size is always 0x174 secInfoHeader->imageFlags = (peData->pageSize == 0x1000 ? XEX_IMG_FLAG_4KIB_PAGES : 0) | XEX_IMG_FLAG_REGION_FREE; // If page size is 4KiB (small pages), set that flag secInfoHeader->baseAddr = peData->baseAddr; - memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID - memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key + //memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID (no longer need to use memset, as we use calloc for these structs now) + //memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key //secInfoHeader->exportTableAddr = TEMPEXPORTADDR; secInfoHeader->exportTableAddr = 0; secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE; From 3d57c01b9265ea8bfd969d6bd7c9f7bd8b8cdcf1 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 15 Jun 2025 12:36:09 +0100 Subject: [PATCH 18/38] Do not use base address to determine DLL type, revert to old method --- src/setdata/populateheaders.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index ad0b0a9..ad2f26e 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -24,28 +24,21 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic // Module flags (type of executable) - // I'm not sure if this is determined properly, but it should be accurate most of the time + // Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead if(xexHeader->moduleFlags == 0) { - if(peData->baseAddr < 0x90000000 && peData->peExportInfo.count > 0) + if(peData->characteristics & PE_CHAR_FLAG_DLL) { - // Title DLL - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL | XEX_MOD_FLAG_TITLE; - } - else if(peData->baseAddr >= 0x90000000 && peData->peExportInfo.count > 0) - { - // System DLL - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL | XEX_MOD_FLAG_EXPORTS; - } - else if(peData->characteristics & PE_CHAR_FLAG_DLL) - { - // DLL - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; + xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL } else { - // Just a regular title - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; + xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title + } + + if(peData->peExportInfo.count > 0) + { + xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions } } From 434ed63db9959e13059d84982b8c32e0c03cb38f Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 15 Jun 2025 22:29:47 +0100 Subject: [PATCH 19/38] Use UBSan as well --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d0529df..d29400a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,8 +77,8 @@ if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) # MinGW doesn't support ASAN if(NOT (MINGW)) - target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address) - target_link_options(synthxex PRIVATE -fsanitize=address) + target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address -fsanitize=undefined) + target_link_options(synthxex PRIVATE -lasan -lubsan -fsanitize=address -fsanitize=undefined) else() target_compile_options(synthxex PRIVATE -O0 -g) endif() From 1938c7136986b29c8391284bde196552c4a20538 Mon Sep 17 00:00:00 2001 From: Vali0004 Date: Sun, 15 Jun 2025 20:37:43 -0400 Subject: [PATCH 20/38] Add Nix support Signed-off-by: Aiden Isik --- .gitignore | 1 + flake.lock | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 35 ++++++++++++++++++++++++++++++ synthxex.nix | 22 +++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 synthxex.nix diff --git a/.gitignore b/.gitignore index 6d1f6c0..ffd84af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build +result *~ *# \#* diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cd3a4a3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1750032973, + "narHash": "sha256-UqiBHZueCKKB8oQrVFooBA0DHh8AUVwhqiLSdhSXyYA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5a0be37d950a180a4853647674c89ec286e538c1", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1e1564d --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + inputs = { + utils.url = "github:numtide/flake-utils"; + # to update: nix flake update nixpkgs + nixpkgs.url = "github:nixos/nixpkgs"; + }; + outputs = { self, utils, nixpkgs }: + (utils.lib.eachSystem [ "x86_64-linux" "ppc64" "ppc32" ] (system: + let + pkgsLut = { + x86_64-linux = nixpkgs.legacyPackages.${system}.extend self.overlay; + ppc32 = import nixpkgs { + crossSystem.config = "powerpc-none-eabi"; + system = "x86_64-linux"; + overlays = [ self.overlay ]; + }; + ppc64 = import nixpkgs { + crossSystem.config = "powerpc64-unknown-linux-gnuabielfv2"; + system = "x86_64-linux"; + overlays = [ self.overlay ]; + config.allowUnsupportedSystem = true; + }; + }; + pkgs = pkgsLut.${system}; + in { + packages = { + inherit (pkgs) synthxex; + }; + devShell = pkgs.synthxex; + })) // { + overlay = self: super: { + synthxex = self.callPackage ./synthxex.nix {}; + }; + }; +} \ No newline at end of file diff --git a/synthxex.nix b/synthxex.nix new file mode 100644 index 0000000..75fadf2 --- /dev/null +++ b/synthxex.nix @@ -0,0 +1,22 @@ +{ stdenv +, cmake +, lib +, ninja +, pkg-config +}: + +stdenv.mkDerivation { + name = "synthxex"; + allowSubstitutes = false; + src = ./.; + nativeBuildInputs = [ cmake pkg-config ninja ]; + + postUnpack = '' + chmod -R +w $sourceRoot + ''; + + installPhase = '' + mkdir -p $out/bin + cp -v synthxex $out/bin/synthxex + ''; +} \ No newline at end of file From 4e053b09ca2d728f1ef8dc825963ab7d1a7e40a7 Mon Sep 17 00:00:00 2001 From: Vali0004 Date: Sun, 15 Jun 2025 22:08:39 -0400 Subject: [PATCH 21/38] Refactor code style, and add hardening Signed-off-by: Aiden Isik --- CMakeLists.txt | 86 ++--- src/common/common.h | 4 +- src/common/datastorage.c | 201 ++++++----- src/common/datastorage.h | 2 +- src/getdata/gethdrdata.c | 162 ++++----- src/getdata/getimports.c | 418 +++++++++++----------- src/main.c | 631 ++++++++++++++-------------------- src/pemapper/pemapper.c | 94 ++--- src/placer/placer.c | 76 ++-- src/setdata/optheaders.c | 408 +++++++++++----------- src/setdata/pagedescriptors.c | 88 ++--- src/setdata/populateheaders.c | 24 +- src/write/headerhash.c | 142 ++++++-- src/write/writexex.c | 49 ++- 14 files changed, 1141 insertions(+), 1244 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d29400a..678229a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,81 +16,45 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -# This version of CMake is available on Debian bullseye backports. I think that's a reasonably old version that most people should have. cmake_minimum_required(VERSION 3.25.1) project(synthxex) -# Make sure we're not being compiled with MSVC (we don't support it) if(MSVC) - message(SEND_ERROR "Compiling with MSVC is not supported. Please use GCC or Clang via the -DCMAKE_C_COMPILER flag or CC environment variable. (Example: cmake -DCMAKE_C_COMPILER=\"gcc\" ..).") - return() + message(SEND_ERROR "Compiling with MSVC is not supported. Please use GCC or Clang via the -DCMAKE_C_COMPILER flag or CC environment variable. (Example: cmake -DCMAKE_C_COMPILER=\"gcc\" ..).") + return() endif() -# Setting sources... -set(allsources ${CMAKE_SOURCE_DIR}/src/main.c) +# Gather all source and header files +file(GLOB_RECURSE allsources + ${CMAKE_SOURCE_DIR}/src/*.c + ${CMAKE_SOURCE_DIR}/src/*.h + ${CMAKE_SOURCE_DIR}/include/getopt_port/*.c + ${CMAKE_SOURCE_DIR}/include/getopt_port/*.h + ${CMAKE_SOURCE_DIR}/include/nettle/*.c + ${CMAKE_SOURCE_DIR}/include/nettle/*.h +) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/common.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/crypto.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/gethdrdata.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/gethdrdata.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getimports.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/placer/placer.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/placer/placer.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/optheaders.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/pagedescriptors.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/populateheaders.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/optheaders.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/pagedescriptors.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/populateheaders.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/getopt_port/getopt.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/getopt_port/getopt.c) - -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/macros.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/nettle-types.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/nettle-write.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1.h) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1-compress.c) -list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/write-be32.c) - -# Setting compilation settings... +# Setting compilation settings add_executable(synthxex ${allsources}) target_include_directories(synthxex PRIVATE ${CMAKE_SOURCE_DIR}/include) -# If we're doing a debug build, compile with debugging and git commit hash if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) - # MinGW doesn't support ASAN - if(NOT (MINGW)) - target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address -fsanitize=undefined) - target_link_options(synthxex PRIVATE -lasan -lubsan -fsanitize=address -fsanitize=undefined) - else() - target_compile_options(synthxex PRIVATE -O0 -g) - endif() + if(NOT MINGW) + target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address -fsanitize=undefined) + target_link_options(synthxex PRIVATE -lasan -lubsan -fsanitize=address -fsanitize=undefined) + else() + target_compile_options(synthxex PRIVATE -O0 -g) + endif() - execute_process( - COMMAND git rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) - add_compile_definitions(GIT_COMMIT="${GIT_COMMIT}") + add_compile_definitions(GIT_COMMIT="${GIT_COMMIT}") endif() # Setting install target settings... diff --git a/src/common/common.h b/src/common/common.h index e74a926..e8dbdcc 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -31,10 +31,10 @@ // Program identifiers #define NAME "SynthXEX" #define VERSION "v0.0.4" -#define COPYRIGHT "2024-25" +#define COPYRIGHT "2024-2025" #ifdef GIT_COMMIT -#define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT +#define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT #else #define VERSION_STRING NAME " " VERSION #endif diff --git a/src/common/datastorage.c b/src/common/datastorage.c index e55dc99..b35b735 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -23,11 +23,13 @@ // Disable pointer type checking here to make this easier to use void nullAndFree(void **ptr) { - if(*ptr != NULL) - { - freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); - *ptr = NULL; - } + if (ptr == NULL) + return; + + if (*ptr != NULL) { + freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); + *ptr = NULL; + } } // These functions together handle freeing all of the main structs. @@ -43,30 +45,36 @@ void nullAndFree(void **ptr) // about if it becomes necessary with future additions. void freeOffsetsStruct(struct offsets **offsets) { - if(*offsets != NULL) - { - nullAndFree((void**)&((*offsets)->optHeaders)); - nullAndFree((void**)offsets); - } + if (offsets == NULL) + return; + + if (*offsets != NULL) { + nullAndFree((void**)&((*offsets)->optHeaders)); + nullAndFree((void**)offsets); + } } void freeXexHeaderStruct(struct xexHeader **xexHeader) { - if(*xexHeader != NULL) - { - nullAndFree((void**)xexHeader); - } + if (xexHeader == NULL) + return; + + if (*xexHeader != NULL) { + nullAndFree((void**)xexHeader); + } } void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) { - if(*secInfoHeader != NULL) - { - struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer - nullAndFree((void**)&descriptors); - //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways - nullAndFree((void**)secInfoHeader); - } + if (secInfoHeader == NULL) + return; + + if (*secInfoHeader != NULL) { + struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer + nullAndFree((void**)&descriptors); + //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways + nullAndFree((void**)secInfoHeader); + } } void freeSectionsStruct(struct sections *sections) @@ -76,72 +84,65 @@ void freeSectionsStruct(struct sections *sections) void freePeImportInfoStruct(struct peImportInfo *peImportInfo) { - if(peImportInfo->tables != NULL) - { - // Free the imports within each table first, then the tables, - // otherwise we'll have a memory leak - for(uint32_t i = 0; i < peImportInfo->tableCount; i++) - { - nullAndFree((void**)&(peImportInfo->tables[i].name)); - nullAndFree((void**)&(peImportInfo->tables[i].imports)); - } - - nullAndFree((void**)&(peImportInfo->tables)); + if (peImportInfo->tables != NULL) { + // Free the imports within each table first, then the tables, + // otherwise we'll have a memory leak + for (uint32_t i = 0; i < peImportInfo->tableCount; i++) { + nullAndFree((void**)&(peImportInfo->tables[i].name)); + nullAndFree((void**)&(peImportInfo->tables[i].imports)); } + + nullAndFree((void**)&(peImportInfo->tables)); + } } - + void freePeDataStruct(struct peData **peData) { - if(*peData != NULL) - { - freeSectionsStruct(&((*peData)->sections)); - freePeImportInfoStruct(&((*peData)->peImportInfo)); - nullAndFree((void**)peData); - } + if (*peData != NULL) { + freeSectionsStruct(&((*peData)->sections)); + freePeImportInfoStruct(&((*peData)->peImportInfo)); + nullAndFree((void**)peData); + } } void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) { - if(*optHeaderEntries != NULL) - { - nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); - nullAndFree((void**)optHeaderEntries); - } + if (*optHeaderEntries != NULL) { + nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); + nullAndFree((void**)optHeaderEntries); + } } void freeImportLibrariesStruct(struct importLibraries *importLibraries) { struct importTable *importTables = importLibraries->importTables; char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers - - nullAndFree((void**)&nameTable); - - if(importTables != NULL) - { - for(uint32_t i = 0; i < importLibraries->tableCount; i++) - { - uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer - nullAndFree((void**)&addresses); - importTables[i].addresses = addresses; - } - nullAndFree((void**)&importTables); - importLibraries->importTables = importTables; + nullAndFree((void**)&nameTable); + + if (importTables != NULL) { + for (uint32_t i = 0; i < importLibraries->tableCount; i++) { + uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer + nullAndFree((void**)&addresses); + importTables[i].addresses = addresses; } + + nullAndFree((void**)&importTables); + importLibraries->importTables = importTables; + } } void freeOptHeadersStruct(struct optHeaders **optHeaders) { - if(*optHeaders != NULL) - { - freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); - nullAndFree((void**)optHeaders); - } + if (*optHeaders != NULL) { + freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); + nullAndFree((void**)optHeaders); + } } void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, - struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders) + struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders) { freeOffsetsStruct(offsets); freeXexHeaderStruct(xexHeader); @@ -153,48 +154,35 @@ void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, uint32_t getNextAligned(uint32_t offset, uint32_t alignment) { - if(offset % alignment) // If offset not aligned - { - return offset + (alignment - (offset % alignment)); // Align - } + if (offset % alignment) // If offset not aligned + return offset + (alignment - (offset % alignment)); // Align return offset; // Offset already aligned } uint32_t rvaToOffset(uint32_t rva, struct sections *sections) { - if((sections->count > 0) - && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) - { - return 0; // Not found (beyond end of PE) - } - - for(int32_t i = sections->count - 1; i >= 0; i--) - { - if(rva >= sections->section[i].rva) - { - return (rva - sections->section[i].rva) + sections->section[i].offset; - } - } + if ((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) + return 0; // Not found (beyond end of PE) + + for (int32_t i = sections->count - 1; i >= 0; i--) { + if (rva >= sections->section[i].rva) + return (rva - sections->section[i].rva) + sections->section[i].offset; + } return 0; // Not found } uint32_t offsetToRVA(uint32_t offset, struct sections *sections) { - if((sections->count > 0) - && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) - { - return 0; // Not found (beyond end of PE) - } - - for(int32_t i = sections->count - 1; i >= 0; i--) - { - if(offset >= sections->section[i].offset) - { - return (offset - sections->section[i].offset) + sections->section[i].rva; - } - } + if ((sections->count > 0) + && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) + return 0; // Not found (beyond end of PE) + + for (int32_t i = sections->count - 1; i >= 0; i--) { + if (offset >= sections->section[i].offset) + return (offset - sections->section[i].offset) + sections->section[i].rva; + } return 0; // Not found } @@ -202,7 +190,10 @@ uint32_t offsetToRVA(uint32_t offset, struct sections *sections) uint32_t get32BitFromPE(FILE *pe) { uint32_t result; - fread(&result, sizeof(uint32_t), 1, pe); + if (fread(&result, sizeof(uint32_t), 1, pe) != 1) { + fprintf(stderr, "Error: Failed to read 32-bit value from PE\n"); + exit(EXIT_FAILURE); + } // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM @@ -215,9 +206,11 @@ uint32_t get32BitFromPE(FILE *pe) uint16_t get16BitFromPE(FILE *pe) { uint16_t result; - fread(&result, sizeof(uint16_t), 1, pe); + if (fread(&result, sizeof(uint16_t), 1, pe) != 1) { + fprintf(stderr, "Error: Failed to read 16-bit value from PE\n"); + exit(EXIT_FAILURE); + } - // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM return __builtin_bswap16(result); #else @@ -228,12 +221,14 @@ uint16_t get16BitFromPE(FILE *pe) uint32_t get32BitFromXEX(FILE *xex) { uint32_t result; - fread(&result, sizeof(uint32_t), 1, xex); + if (fread(&result, sizeof(uint32_t), 1, xex) != 1) { + fprintf(stderr, "Error: Failed to read 32-bit value from XEX\n"); + exit(EXIT_FAILURE); + } - // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM return __builtin_bswap32(result); -#else +#else return result; #endif } @@ -241,12 +236,14 @@ uint32_t get32BitFromXEX(FILE *xex) uint16_t get16BitFromXEX(FILE *xex) { uint16_t result; - fread(&result, sizeof(uint16_t), 1, xex); + if (fread(&result, sizeof(uint16_t), 1, xex) != 1) { + fprintf(stderr, "Error: Failed to read 16-bit value from XEX\n"); + exit(EXIT_FAILURE); + } - // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM return __builtin_bswap16(result); #else return result; #endif -} +} \ No newline at end of file diff --git a/src/common/datastorage.h b/src/common/datastorage.h index e1fd3f9..fdcaad9 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -266,7 +266,7 @@ void freePeDataStruct(struct peData **peData); void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries); void freeOptHeadersStruct(struct optHeaders **optHeaders); void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, - struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders); + struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders); // Functions used for file data manipulation diff --git a/src/getdata/gethdrdata.c b/src/getdata/gethdrdata.c index 1a27f3b..a2c6767 100644 --- a/src/getdata/gethdrdata.c +++ b/src/getdata/gethdrdata.c @@ -22,101 +22,83 @@ // I was considering merging this with getHdrData and mapPEToBasefile as we're // basically reading the same data twice, but I think it's beneficial to have a // dedicated place where we validate the input. -bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false +bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false { // Check if we have at least the size of a DOS header, so we don't overrun the PE fseek(pe, 0, SEEK_END); size_t finalOffset = ftell(pe); - if(finalOffset < 0x3C + 0x4) - { - return false; - } - + if (finalOffset < 0x3C + 0x4) + return false; + // Check magic fseek(pe, 0, SEEK_SET); uint16_t magic = get16BitFromPE(pe); - if(magic != 0x5A4D) // PE magic - { - return false; - } + if (magic != 0x5A4D) // PE magic + return false; // Check if pointer to PE header is valid fseek(pe, 0x3C, SEEK_SET); size_t peHeaderOffset = get32BitFromPE(pe); - - if(finalOffset < peHeaderOffset) - { - return false; - } + + if (finalOffset < peHeaderOffset) + return false; // Check if the file is big enough to get size of optional header, and therefore size of whole PE header - if(finalOffset < 0x14 + 0x2) - { - return false; - } + if (finalOffset < 0x14 + 0x2) + return false; // Check section count fseek(pe, peHeaderOffset + 0x6, SEEK_SET); + uint16_t sectionCount = get16BitFromPE(pe); - - if(sectionCount == 0) - { - return false; - } - + if (sectionCount == 0) + return false; + // Check if the file is large enough to contain the whole PE header fseek(pe, peHeaderOffset + 0x14, SEEK_SET); uint16_t sizeOfOptHdr = get16BitFromPE(pe); // 0x18 == size of COFF header, 0x28 == size of one entry in section table - if(finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28)) - { - return false; - } - + if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28)) + return false; + // Check machine ID fseek(pe, peHeaderOffset + 0x4, SEEK_SET); - uint16_t machineID = get16BitFromPE(pe); - if(machineID != 0x1F2 && !skipMachineCheck) // 0x1F2 == POWERPCBE - { - return false; - } + // 0x1F2 == POWERPCBE + uint16_t machineID = get16BitFromPE(pe); + if (machineID != 0x1F2 && !skipMachineCheck) + return false; // Check subsystem fseek(pe, peHeaderOffset + 0x5C, SEEK_SET); - uint16_t subsystem = get16BitFromPE(pe); - if(subsystem != 0xE) // 0xE == XBOX - { - return false; - } + uint16_t subsystem = get16BitFromPE(pe); + if (subsystem != 0xE) // 0xE == XBOX + return false; // Check page size/alignment fseek(pe, peHeaderOffset + 0x38, SEEK_SET); - uint32_t pageSize = get32BitFromPE(pe); - if(pageSize != 0x1000 && pageSize != 0x10000) // 4KiB and 64KiB are the only valid sizes - { - return false; - } + // 4KiB and 64KiB are the only valid sizes + uint32_t pageSize = get32BitFromPE(pe); + if (pageSize != 0x1000 && pageSize != 0x10000) + return false; // Check each raw offset + raw size in section table fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry - for(uint16_t i = 0; i < sectionCount; i++) - { - // If raw size + raw offset exceeds file size, PE is invalid - if(finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe)) - { - return false; - } + for (uint16_t i = 0; i < sectionCount; i++) { + // If raw size + raw offset exceeds file size, PE is invalid + if (finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe)) + return false; + + // Next entry + fseek(pe, 0x20, SEEK_CUR); + } - fseek(pe, 0x20, SEEK_CUR); // Next entry - } - return true; // Checked enough, this is an Xbox 360 PE file } @@ -127,53 +109,43 @@ int getSectionInfo(FILE *pe, struct sections *sections) fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count sections->count = get16BitFromPE(pe); - + sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata - if(sections->section == NULL) {return ERR_OUT_OF_MEM;} + if (sections->section == NULL) + return ERR_OUT_OF_MEM; + fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table - for(uint16_t i = 0; i < sections->count; i++) - { - fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section - sections->section[i].virtualSize = get32BitFromPE(pe); - sections->section[i].rva = get32BitFromPE(pe); - sections->section[i].rawSize = get32BitFromPE(pe); - sections->section[i].offset = get32BitFromPE(pe); + for (uint16_t i = 0; i < sections->count; i++) { + fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section + sections->section[i].virtualSize = get32BitFromPE(pe); + sections->section[i].rva = get32BitFromPE(pe); + sections->section[i].rawSize = get32BitFromPE(pe); + sections->section[i].offset = get32BitFromPE(pe); - fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags - uint32_t characteristics = get32BitFromPE(pe); + fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags + uint32_t characteristics = get32BitFromPE(pe); - if(characteristics & PE_SECTION_FLAG_EXECUTE) - { - sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 - } - else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) - { - sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; - } - else if(characteristics & PE_SECTION_FLAG_READ) - { - sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; - } - else - { - return ERR_MISSING_SECTION_FLAG; - } + if (characteristics & PE_SECTION_FLAG_EXECUTE) + sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 + else if (characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) + sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; + else if (characteristics & PE_SECTION_FLAG_READ) + sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; + else + return ERR_MISSING_SECTION_FLAG; + } - // Don't need to progress any more to get to beginning of next entry, as characteristics is last field - } - + // Don't need to progress any more to get to beginning of next entry, as characteristics is last field return SUCCESS; } int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) { // No flags supported at this time (will be used for getting additional info, for e.g. other optional headers) - if(flags) - { - return ERR_UNKNOWN_DATA_REQUEST; - } - + if (flags) + return ERR_UNKNOWN_DATA_REQUEST; + // Getting PE header offset before we go any further.. fseek(pe, 0x3C, SEEK_SET); peData->peHeaderOffset = get32BitFromPE(pe); @@ -184,7 +156,7 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) // Size of section table peData->sectionTableSize = peData->numberOfSections * 0x28; - + // Size of header // 0x18 == size of COFF header, get16BitFromPE value == size of optional header fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET); @@ -197,7 +169,7 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) // Entry point (RVA) fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); peData->entryPoint = get32BitFromPE(pe); - + // Base address fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET); peData->baseAddr = get32BitFromPE(pe); @@ -218,11 +190,13 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); peData->tlsAddr = get32BitFromPE(pe); peData->tlsSize = get32BitFromPE(pe); - if(peData->tlsAddr != 0 || peData->tlsSize != 0) {return ERR_UNSUPPORTED_STRUCTURE;} + if (peData->tlsAddr != 0 || peData->tlsSize != 0) + return ERR_UNSUPPORTED_STRUCTURE; // Section info int ret = getSectionInfo(pe, &(peData->sections)); - if(ret != 0) {return ret;} + if (ret != 0) + return ret; return SUCCESS; } diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 9b08280..35d65b2 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -20,240 +20,270 @@ int checkForBranchStub(FILE *pe, struct peData *peData) { - uint32_t expectedInstructions[] = - { - // lis r, - // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction - 0x3C000000, + uint32_t expectedInstructions[] = { + // lis r, + // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction + 0x3C000000, - // lwz r, (r) - // Mask 0xFC000000 for instruction - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction - 0x80000000, + // lwz r, (r) + // Mask 0xFC000000 for instruction + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction + 0x80000000, - // mtctr r - // Mask 0xFC1FFFFF for instruction (mtspr CTR) - // Mask 0x03E00000 and shift right 21 bits for register - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction - 0x7C0903A6, + // mtctr r + // Mask 0xFC1FFFFF for instruction (mtspr CTR) + // Mask 0x03E00000 and shift right 21 bits for register + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction + 0x7C0903A6, - // bctr - // An assembler told me this, apparently IBM doesn't have documentation on their site for it - 0x4E800420 - }; + // bctr + // An assembler told me this, apparently IBM doesn't have documentation on their site for it + 0x4E800420 + }; // Read in the set of instructions to check uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); - if(instructionBuffer == NULL) {return ERR_OUT_OF_MEM;} - if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) {nullAndFree((void**)&instructionBuffer); return ERR_FILE_READ;} + if (instructionBuffer == NULL) + return ERR_OUT_OF_MEM; + memset(instructionBuffer, 0, sizeof(uint32_t) * 4); + if (fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) { + nullAndFree((void**)&instructionBuffer); + return ERR_FILE_READ; + } #ifdef LITTLE_ENDIAN_SYSTEM // Byteswap the instructions - for(uint8_t i = 0; i < 4; i++) - { - instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); - } + for (uint8_t i = 0; i < 4; i++) + instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); #endif - + // Check if each instruction matches uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; - if(((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) - && (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) - { - if(((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) - && (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) - && (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) - { - if(((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) - && (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) - { - if(instructionBuffer[3] == expectedInstructions[3]) - { - // Found a branch stub, check if the address matches any IAT entries - uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) - | (instructionBuffer[1] & 0x0000FFFF); + if (((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && + (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) { + if (((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && + (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) && + (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) { + if (((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && + (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) { + if (instructionBuffer[3] == expectedInstructions[3]) { + uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | + (instructionBuffer[1] & 0x0000FFFF); - for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) - { - if(peData->peImportInfo.tables[i].importCount == peData->peImportInfo.tables[i].branchStubCount) - { - continue; // We already have all the branch stubs possible for this table, skip it - } + for (uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) { + if (peData->peImportInfo.tables[i].importCount == + peData->peImportInfo.tables[i].branchStubCount) { + continue; + } - for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) - { - if(peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) - { - continue; // We already have this branch stub, skip it - } + for (uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) { + if (peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) { + continue; + } - if(peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) - { - // Found the branch stub for the current import, store the address - uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); - if(currentBranchStubRVA == 0) {nullAndFree((void**)&instructionBuffer); return ERR_INVALID_RVA_OR_OFFSET;} - peData->peImportInfo.tables[i].imports[j].branchStubAddr = peData->baseAddr + currentBranchStubRVA; - peData->peImportInfo.tables[i].branchStubCount++; - peData->peImportInfo.totalBranchStubCount++; + if (peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) { + uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); + if (currentBranchStubRVA == 0) { + nullAndFree((void**)&instructionBuffer); + return ERR_INVALID_RVA_OR_OFFSET; + } - nullAndFree((void**)&instructionBuffer); - return SUCCESS; - } - } - } - } - } - } + peData->peImportInfo.tables[i].imports[j].branchStubAddr = + peData->baseAddr + currentBranchStubRVA; + peData->peImportInfo.tables[i].branchStubCount++; + peData->peImportInfo.totalBranchStubCount++; + + nullAndFree((void**)&instructionBuffer); + return SUCCESS; + } + } + } + } + } } + } nullAndFree((void**)&instructionBuffer); - if(fseek(pe, -12, SEEK_CUR) != 0) {return ERR_FILE_READ;} - return SUCCESS; + return fseek(pe, -12, SEEK_CUR) != 0 ? ERR_FILE_READ : SUCCESS; } int getImports(FILE *pe, struct peData *peData) { // Make sure the peImportInfo struct is blank, except the IDT RVA memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t)); - + // If there is no IDT, just skip handling imports - if(peData->peImportInfo.idtRVA == 0) - { - return SUCCESS; - } - + if (peData->peImportInfo.idtRVA == 0) + return SUCCESS; + // Seek to the IDT and read the first entry uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); - if(idtOffset == 0) {return ERR_INVALID_RVA_OR_OFFSET;} - if(fseek(pe, idtOffset, SEEK_SET) != 0) {return ERR_FILE_READ;} + if (idtOffset == 0) + return ERR_INVALID_RVA_OR_OFFSET; + if (fseek(pe, idtOffset, SEEK_SET) != 0) + return ERR_FILE_READ; uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); - if(currentIDT == NULL) {return ERR_OUT_OF_MEM;} - uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons - if(blankIDT == NULL) {nullAndFree((void**)¤tIDT); return ERR_OUT_OF_MEM;} - if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} - + if (currentIDT == NULL) + return ERR_OUT_OF_MEM; + + uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons + if (blankIDT == NULL) { + nullAndFree((void**)¤tIDT); + return ERR_OUT_OF_MEM; + } + + if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + // While the IDT is not blank, process it - for(uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) - { - // Allocate space for the current table data - peData->peImportInfo.tableCount++; - peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); - if(peData->peImportInfo.tables == NULL) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_OUT_OF_MEM;} - memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank - -#ifdef BIG_ENDIAN_SYSTEM - // Byteswap the IDT fields - for(uint8_t i = 0; i < 5; i++) - { - currentIDT[i] = __builtin_bswap32(currentIDT[i]); - } -#endif - - // Retrieve the name pointed to by the table - uint32_t savedOffset = ftell(pe); - uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); - if(tableNameOffset == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} - if(fseek(pe, tableNameOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - - // Allocating is expensive, go 16 bytes at a time to avoid calling realloc an excessive number of times - for(uint32_t j = 0;; j += 16) - { - peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); - if(peData->peImportInfo.tables[i].name == NULL) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_OUT_OF_MEM;} - if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - - // Check for a null terminator - for(uint32_t k = j; k < j + 16; k++) - { - if(peData->peImportInfo.tables[i].name[k] == '\0') - { - goto doneGettingTableName; - } - } - } - - doneGettingTableName: - - // Seek to the IAT and read the first entry - uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); - if(iatOffset == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} - if(fseek(pe, iatOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - - uint32_t currentImport; - if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - - // While the import is not blank, process it - for(int j = 0; currentImport != 0; j++) - { -#ifdef BIG_ENDIAN_SYSTEM - // Byteswap the import - currentImport = __builtin_bswap32(currentImport); -#endif - - // Allocate space for the current import - peData->peImportInfo.tables[i].importCount++; - peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); - peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Make sure this is zeroed, we rely on it later - - // Store the address of the current import entry in iatAddr - uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); - if(currentImportRVA == 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET;} - peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; - - // Read the next import - if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - } - - // Add table's import count to total and return to the next IDT entry to read - peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; - if(fseek(pe, savedOffset, SEEK_SET) != 0) {nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); return ERR_FILE_READ;} - - // Read next IDT - if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) {return ERR_FILE_READ;} + for (uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) { + // Allocate space for the current table data + peData->peImportInfo.tableCount++; + peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); + if (peData->peImportInfo.tables == NULL) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_OUT_OF_MEM; } - + memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank + +#ifdef BIG_ENDIAN_SYSTEM + // Byteswap the IDT fields + for (uint8_t j = 0; j < 5; j++) + currentIDT[j] = __builtin_bswap32(currentIDT[j]); +#endif + + // Retrieve the name pointed to by the table + uint32_t savedOffset = ftell(pe); + uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); + if (tableNameOffset == 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + if (fseek(pe, tableNameOffset, SEEK_SET) != 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Allocating is expensive, go 16 bytes at a time to avoid excessive realloc calls + for (uint32_t j = 0;; j += 16) { + peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); + if (peData->peImportInfo.tables[i].name == NULL) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_OUT_OF_MEM; + } + if (fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Check for null terminator + for (uint32_t k = j; k < j + 16; k++) + if (peData->peImportInfo.tables[i].name[k] == '\0') + goto doneGettingTableName; + } + + doneGettingTableName: + // Seek to the IAT and read the first entry + uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); + if (iatOffset == 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + if (fseek(pe, iatOffset, SEEK_SET) != 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + uint32_t currentImport; + if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // While the import is not blank, process it + for (int j = 0; currentImport != 0; j++) { +#ifdef BIG_ENDIAN_SYSTEM + // Byteswap the import + currentImport = __builtin_bswap32(currentImport); +#endif + // Allocate space for the current import + peData->peImportInfo.tables[i].importCount++; + peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); + peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later + + // Store the address of the current import entry in iatAddr + uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); + if (currentImportRVA == 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; + + // Read the next import + if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + } + + // Add table's import count to total and return to next IDT entry + peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; + if (fseek(pe, savedOffset, SEEK_SET) != 0) { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Read next IDT + if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) + return ERR_FILE_READ; + } + nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); - + // Find the branch stubs - for(uint16_t i = 0; i < peData->sections.count; i++) - { - if(peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) - { - break; // We have all the branch stubs there could possibly be - } + for (uint16_t i = 0; i < peData->sections.count; i++) { + if (peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) + break; // All branch stubs found - if(!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) - { - continue; // Skip the current section if it's not executable - } + if (!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) + continue; // Skip non-executable section - // Seek to the start of the current section to begin reading - if(fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + // Seek to the start of the current section + if (fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) + return ERR_FILE_READ; - // While we're within the bounds of the current section, and there's at least 4 instructions space left - while(ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) - { - int ret = checkForBranchStub(pe, peData); - - if(ret != SUCCESS) - { - return ret; - } - } + // While inside section and at least 4 instructions left (15 bytes) + while (ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) { + int ret = checkForBranchStub(pe, peData); + if (ret != SUCCESS) + return ret; } + } return SUCCESS; } + diff --git a/src/main.c b/src/main.c index e6b0af8..4e87d89 100644 --- a/src/main.c +++ b/src/main.c @@ -114,18 +114,50 @@ void dispHelp(char **argv) printf("-t,\t--type,\t\t\tOverride automatic executable type detection\n\t\t\t\t(options: title, titledll, sysdll, dll)\n\n"); } +void handleError(int ret) +{ + switch (ret) { + case ERR_UNKNOWN_DATA_REQUEST: + printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); + break; + case ERR_FILE_READ: + printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + break; + case ERR_OUT_OF_MEM: + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + break; + case ERR_MISSING_SECTION_FLAG: + printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); + break; + case ERR_UNSUPPORTED_STRUCTURE: + printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + break; + case ERR_INVALID_RVA_OR_OFFSET: + printf("%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); + break; + case ERR_INVALID_IMPORT_NAME: + printf("%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); + break; + case ERR_DATA_OVERFLOW: + printf("%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); + break; + default: + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + break; + } +} + int main(int argc, char **argv) { - static struct option longOptions[] = - { - {"help", no_argument, 0, 0}, - {"version", no_argument, 0, 0}, - {"libs", no_argument, 0, 0}, - {"skip-machine-check", no_argument, 0, 0}, - {"input", required_argument, 0, 0}, - {"output", required_argument, 0, 0}, - {"type", required_argument, 0, 0}, - {0, 0, 0, 0} + static struct option longOptions[] = { + { "help", no_argument, 0, 0 }, + { "version", no_argument, 0, 0 }, + { "libs", no_argument, 0, 0 }, + { "skip-machine-check", no_argument, 0, 0 }, + { "input", required_argument, 0, 0 }, + { "output", required_argument, 0, 0 }, + { "type", required_argument, 0, 0 }, + { 0, 0, 0, 0 } }; struct offsets *offsets = calloc(1, sizeof(struct offsets)); @@ -135,13 +167,14 @@ int main(int argc, char **argv) struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); - if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL - || optHeaderEntries == NULL || optHeaders == NULL) - { - printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } + if (offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || + peData == NULL || optHeaderEntries == NULL || optHeaders == NULL) + { + printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } int optIndex = 0; int option = 0; @@ -149,435 +182,275 @@ int main(int argc, char **argv) bool gotInput = false; bool gotOutput = false; bool skipMachineCheck = false; - + char *pePath = NULL; char *xexfilePath = NULL; - - while((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) - { - if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) - { - dispHelp(argv); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) - { - dispVer(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) - { - dispLibs(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) - { - printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); - skipMachineCheck = true; - } - else if(option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) - { - gotInput = true; - pePath = malloc(strlen(optarg) + 1); - if(pePath == NULL) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } - - strncpy(pePath, optarg, strlen(optarg) + 1); - pePath[strlen(optarg)] = '\0'; - } - else if(option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) - { - gotOutput = true; - xexfilePath = malloc(strlen(optarg) + 1); + while ((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) { + if (option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) { + dispHelp(argv); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } else if (option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) { + dispVer(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } else if (option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) { + dispLibs(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } else if (option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) { + printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); + skipMachineCheck = true; + } else if (option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) { + gotInput = true; + pePath = malloc(strlen(optarg) + 1); + if (pePath == NULL) { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + strcpy(pePath, optarg); + } else if (option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) { + gotOutput = true; + xexfilePath = malloc(strlen(optarg) + 1); + if (xexfilePath == NULL) { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&pePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + strcpy(xexfilePath, optarg); + } else if (option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) { + if (strcmp(optarg, "title") == 0) { + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; + } else if (strcmp(optarg, "titledll") == 0) { + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; + } else if (strcmp(optarg, "sysdll") == 0) { + xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; + } else if (strcmp(optarg, "dll") == 0) { + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; + } else { + printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", + PRINT_STEM, optarg); - if(xexfilePath == NULL) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&pePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } - - strncpy(xexfilePath, optarg, strlen(optarg) + 1); - xexfilePath[strlen(optarg)] = '\0'; - } - else if(option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) - { - if(strcmp(optarg, "title") == 0) - { - // Overriding type with "title" - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; - } - else if(strcmp(optarg, "titledll") == 0) - { - // Overriding type with "titledll" - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; - } - else if(strcmp(optarg, "sysdll") == 0) - { - // Overriding type with "sysdll" - xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; - } - else if(strcmp(optarg, "dll") == 0) - { - // Overriding type with "dll" - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; - } - else - { - printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", PRINT_STEM, optarg); - nullAndFree((void**)&pePath); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } - } + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } } + } printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT); printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]); - - // Check we got everything we need - if(!gotInput) - { - if(gotOutput) - { - nullAndFree((void**)&xexfilePath); - } - - printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); - return -1; - } - else if(!gotOutput) - { - if(gotInput) - { - nullAndFree((void**)&pePath); - } - printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); - return -1; - } - - // Opening the files now that they've been validated - FILE *pe = fopen(pePath, "rb"); - - if(pe == NULL) - { - printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); + if (!gotInput) { + if (gotOutput) + nullAndFree((void**)&xexfilePath); + printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); + return -1; + } else if (!gotOutput) { + if (gotInput) nullAndFree((void**)&pePath); - nullAndFree((void**)&xexfilePath); - return -1; - } + printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); + return -1; + } + FILE *pe = fopen(pePath, "rb"); + if (pe == NULL) { + printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); + return -1; + } nullAndFree((void**)&pePath); - + FILE *xex = fopen(xexfilePath, "wb+"); + if (xex == NULL) { + printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); + fclose(pe); + nullAndFree((void**)&xexfilePath); + return -1; + } - if(xex == NULL) - { - printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); - fclose(pe); - nullAndFree((void**)&xexfilePath); - return -1; - } + // Keep xexfilePath for later use + // Do NOT free xexfilePath here yet - // Don't free this yet. We need it to determine where we can put the mapped basefile. - // There *are* ways to get the file path from file pointer, but none of them are portable. - //free(xexfilePath); + int ret = 0; - int ret; printf("%s Validating PE file...\n", PRINT_STEM); - - if(!validatePE(pe, skipMachineCheck)) - { - printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - + if (!validatePE(pe, skipMachineCheck)) { + printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } printf("%s PE valid!\n", PRINT_STEM); - // Reading in header data from PE printf("%s Retrieving header data from PE...\n", PRINT_STEM); ret = getHdrData(pe, peData, 0); - - if(ret != SUCCESS) - { - if(ret == ERR_UNKNOWN_DATA_REQUEST) - { - printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_FILE_READ) - { - printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_MISSING_SECTION_FLAG) - { - printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_UNSUPPORTED_STRUCTURE) - { - printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - + if (ret != SUCCESS) { + handleError(ret); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } printf("%s Got header data from PE!\n", PRINT_STEM); - // Process imports printf("%s Retrieving import data from PE...\n", PRINT_STEM); ret = getImports(pe, peData); - - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_FILE_READ) - { - printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_INVALID_RVA_OR_OFFSET) - { - printf("%s ERROR: Invalid RVA or offset encountered (malformed PE). Aborting.\n", PRINT_STEM); - } - else if(ret == ERR_UNSUPPORTED_STRUCTURE) // Does import by name actually need special treatment or can this go?? - { - printf("%s ERROR: Import by name detected. This is currently unsupported. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - + if (ret != SUCCESS) { + handleError(ret); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } printf("%s Got import data from PE!\n", PRINT_STEM); - - // Determine the path we should save the basefile at and open it. + printf("%s Creating basefile from PE...\n", PRINT_STEM); char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char)); - - if(basefilePath == NULL) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - nullAndFree((void**)&xexfilePath); - fclose(pe); - fclose(xex); - return -1; - } - + if (!basefilePath) { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + nullAndFree((void**)&xexfilePath); + fclose(pe); + fclose(xex); + return -1; + } strcpy(basefilePath, xexfilePath); strcat(basefilePath, ".basefile"); - - FILE* basefile = fopen(basefilePath, "wb+"); - nullAndFree((void**)&xexfilePath); // *Now* we're done with it. + + FILE *basefile = fopen(basefilePath, "wb+"); + nullAndFree((void**)&xexfilePath); nullAndFree((void**)&basefilePath); - - if(basefile == NULL) - { - printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } + + if (!basefile) { + printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } // Map the PE into the basefile (RVAs become offsets) ret = mapPEToBasefile(pe, basefile, peData); fclose(pe); + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + printf("%s Created basefile!\n", PRINT_STEM); - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - printf("%s Created basefile!\n", PRINT_STEM); - - // Setting final XEX data structs + // Setting final XEX data structs printf("%s Building security header...\n", PRINT_STEM); ret = setSecInfoHeader(secInfoHeader, peData); + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } - if(ret != SUCCESS) - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - printf("%s Setting page descriptors...\n", PRINT_STEM); ret = setPageDescriptors(basefile, peData, secInfoHeader); - - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } // Done with this now freeSectionsStruct(&(peData->sections)); - + printf("%s Building optional headers...\n", PRINT_STEM); ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); - - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } printf("%s Building XEX header...\n", PRINT_STEM); ret = setXEXHeader(xexHeader, optHeaderEntries, peData); - - if(ret != SUCCESS) - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } // Done with this now freePeImportInfoStruct(&(peData->peImportInfo)); - - // Setting data positions... + + // Setting data positions printf("%s Aligning data...\n", PRINT_STEM); ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); - - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } // We're done with this now freePeDataStruct(&peData); // Write out all of the XEX data to file - printf("%s Writing data to XEX file...\n", PRINT_STEM); + printf("%s Writing XEX...\n", PRINT_STEM); ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); - - if(ret != SUCCESS) - { - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - } - else - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - } - - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - printf("%s Main data written to XEX file!\n", PRINT_STEM); + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } // Final pass (sets & writes header hash) printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); ret = setHeaderSha1(xex); - - if(ret != SUCCESS) - { - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } + if (ret != SUCCESS) { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } printf("%s Header SHA1 written!\n", PRINT_STEM); - - fclose(basefile); - fclose(xex); - - printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); - // Free everything left and exit + // Free files + fclose(xex); + fclose(basefile); + // Free structs freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + + printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); return SUCCESS; } diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index c3e07e2..b2b23c5 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -29,58 +29,70 @@ struct sectionInfo // Maps the PE file into the basefile (RVAs become offsets) int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { - // Retrieve data for each section struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo)); - fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET); // Seek to the first section in the section table at virtualSize + if (!sectionInfo) + return ERR_OUT_OF_MEM; + + if (fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) + return ERR_FILE_READ; + + for (uint16_t i = 0; i < peData->numberOfSections; i++) { + sectionInfo[i].virtualSize = get32BitFromPE(pe); + sectionInfo[i].rva = get32BitFromPE(pe); + sectionInfo[i].rawSize = get32BitFromPE(pe); + sectionInfo[i].offset = get32BitFromPE(pe); + + if (fseek(pe, 0x18, SEEK_CUR) != 0) + return ERR_FILE_READ; + } + + if (fseek(pe, 0, SEEK_SET) != 0) + return ERR_FILE_READ; - for(uint16_t i = 0; i < peData->numberOfSections; i++) - { - sectionInfo[i].virtualSize = get32BitFromPE(pe); - sectionInfo[i].rva = get32BitFromPE(pe); - sectionInfo[i].rawSize = get32BitFromPE(pe); - sectionInfo[i].offset = get32BitFromPE(pe); - fseek(pe, 0x18, SEEK_CUR); // Seek to next entry at virtualSize - } - - // Copy the PE header and section table to the basefile verbatim - fseek(pe, 0, SEEK_SET); uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize); - if(buffer == NULL) {return ERR_OUT_OF_MEM;} + if (!buffer) + return ERR_OUT_OF_MEM; - fread(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, pe); - fwrite(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, basefile); + size_t totalHeader = peData->headerSize + peData->sectionTableSize; - // Now map the sections and write them - for(uint16_t i = 0; i < peData->numberOfSections; i++) - { - buffer = realloc(buffer, sectionInfo[i].rawSize * sizeof(uint8_t)); - if(buffer == NULL) {return ERR_OUT_OF_MEM;} - - fseek(pe, sectionInfo[i].offset, SEEK_SET); - fread(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, pe); + if (fread(buffer, 1, totalHeader, pe) != totalHeader) + return ERR_FILE_READ; + if (fwrite(buffer, 1, totalHeader, basefile) != totalHeader) + return ERR_FILE_READ; - fseek(basefile, sectionInfo[i].rva, SEEK_SET); - fwrite(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, basefile); - } + for (uint16_t i = 0; i < peData->numberOfSections; i++) { + buffer = realloc(buffer, sectionInfo[i].rawSize); + if (!buffer) + return ERR_OUT_OF_MEM; + + if (fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0) + return ERR_FILE_READ; + if (fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize) + return ERR_FILE_READ; + + if (fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0) + return ERR_FILE_READ; + if (fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize) + return ERR_FILE_READ; + } - // Pad the rest of the final page with zeroes, we can achieve this by seeking - // to the end and placing a single zero there (unless the data runs all the way up to the end!) uint32_t currentOffset = ftell(basefile); - uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 0x1; + uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1; - if(nextAligned != currentOffset) - { - buffer = realloc(buffer, 1 * sizeof(uint8_t)); - if(buffer == NULL) {return ERR_OUT_OF_MEM;} - buffer[0] = 0; - fseek(basefile, nextAligned, SEEK_SET); - fwrite(buffer, sizeof(uint8_t), 1, basefile); - } + if (nextAligned != currentOffset) { + buffer = realloc(buffer, 1); + if (!buffer) + return ERR_OUT_OF_MEM; + buffer[0] = 0; + + if (fseek(basefile, nextAligned, SEEK_SET) != 0) + return ERR_FILE_READ; + if (fwrite(buffer, 1, 1, basefile) != 1) + return ERR_FILE_READ; + } - // Make sure to update the PE (basefile) size peData->size = ftell(basefile); - - // We're done with these, free them + nullAndFree((void**)&buffer); nullAndFree((void**)§ionInfo); diff --git a/src/placer/placer.c b/src/placer/placer.c index cebf076..bae4eba 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -27,37 +27,36 @@ struct importLibIdcs int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders, uint32_t *currentOffset, struct importLibIdcs *importLibIdcs) { - offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); // Calloc because 0 values will be used to determine if a header is not present. - if(offsets->optHeaders == NULL) {return ERR_OUT_OF_MEM;} - uint32_t sepHeader = 0; // Separate header iterator, i.e. one with it's data outwith the entries - - for(uint32_t i = 0; i < optHeaderEntries->count; i++) - { - *currentOffset = getNextAligned(*currentOffset, 0x8); - - switch(optHeaderEntries->optHeaderEntry[i].id) - { - case XEX_OPT_ID_BASEFILE_FORMAT: - optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; - offsets->optHeaders[sepHeader] = *currentOffset; - *currentOffset += sizeof(struct basefileFormat); - sepHeader++; - break; + // Calloc because 0 values will be used to determine if a header is not present. + offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); + if (offsets->optHeaders == NULL) + return ERR_OUT_OF_MEM; - case XEX_OPT_ID_IMPORT_LIBS: - importLibIdcs->header = sepHeader; - importLibIdcs->entry = i; - sepHeader++; - break; + // Separate header iterator, i.e. one with it's data outwith the entries + uint32_t sepHeader = 0; - case XEX_OPT_ID_TLS_INFO: - optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; - offsets->optHeaders[sepHeader] = *currentOffset; - *currentOffset += sizeof(struct tlsInfo); - sepHeader++; - break; - } + for (uint32_t i = 0; i < optHeaderEntries->count; i++) { + *currentOffset = getNextAligned(*currentOffset, 0x8); + switch(optHeaderEntries->optHeaderEntry[i].id) { + case XEX_OPT_ID_BASEFILE_FORMAT: + optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; + offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += sizeof(struct basefileFormat); + sepHeader++; + break; + case XEX_OPT_ID_IMPORT_LIBS: + importLibIdcs->header = sepHeader; + importLibIdcs->entry = i; + sepHeader++; + break; + case XEX_OPT_ID_TLS_INFO: + optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; + offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += sizeof(struct tlsInfo); + sepHeader++; + break; } + } return SUCCESS; } @@ -72,7 +71,7 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op // Optional header entries (no alignment, they immediately follow XEX header) offsets->optHeaderEntries = currentOffset; currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry); - + // Security header currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries offsets->secInfoHeader = currentOffset; @@ -82,25 +81,20 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op // Optional headers (minus imports) struct importLibIdcs importLibIdcs; int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs); + if (ret != SUCCESS) + return ret; - if(ret != SUCCESS) - { - return ret; - } - currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports - + // PE basefile currentOffset = getNextAligned(currentOffset, 0x1000); // 4KiB alignment for basefile offsets->basefile = currentOffset; xexHeader->peOffset = currentOffset; // Imports, the end of this header is aligned to the start of the basefile, so they are a special case - if(optHeaders->importLibraries.tableCount > 0) - { - offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; - optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; - } - + if (optHeaders->importLibraries.tableCount > 0) { + offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; + optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; + } return SUCCESS; } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 8e2d998..c399ed4 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -37,232 +37,211 @@ void setTLSInfo(struct tlsInfo *tlsInfo) } int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportInfo *peImportInfo, struct secInfoHeader *secInfoHeader) -{ - // Set table count and allocate enough memory for all tables +{ importLibraries->tableCount = peImportInfo->tableCount; secInfoHeader->importTableCount = peImportInfo->tableCount; + importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); - if(importLibraries->importTables == NULL) {return ERR_OUT_OF_MEM;} - struct importTable *importTables = importLibraries->importTables; // Use this to avoid dereferencing an unaligned pointer - - // Initialise the size of the import libraries to just the size of the header (- 2 * sizeof(void*) to exclude addresses for internal use only) + if (!importLibraries->importTables) + return ERR_OUT_OF_MEM; + + if (peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) { + fprintf(stderr, "ERROR: Invalid tableCount = %d\n", peImportInfo->tableCount); + return ERR_OUT_OF_MEM; + } + + struct importTable *importTables = importLibraries->importTables; importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); - // Space to store names for use after the loop - char *names[importLibraries->tableCount]; - - // Populate each table, then compute it's hash and store in the previous table - for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--) - { - // Set the table index field to the current index - importTables[i].tableIndex = i; - - // Extract the name, target, and minimum versions from the name string - // Major and minor version are always 2 and 0, respectively - // Strings are definitely null-terminated as otherwise they couldn't have been read in - const uint8_t majorVer = 2; - const uint8_t minorVer = 0; - char *targetBuildVerStr = NULL; - char *targetHotfixVerStr = NULL; - char *minimumBuildVerStr = NULL; - char *minimumHotfixVerStr = NULL; - uint16_t buildVer; - uint8_t hotfixVer; - char *strtoulRet; + int ret = ERR_INVALID_IMPORT_NAME; - // Get the name - uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); - names[i] = strtok(peImportInfo->tables[i].name, "@"); - if(strlen(names[i]) == oldNameLen || strlen(names[i]) < 1) - {return ERR_INVALID_IMPORT_NAME;} // Encountered '\0', not '@' + // Allocate name list + char **names = calloc(importLibraries->tableCount, sizeof(char *)); + if (!names) + goto cleanup_tables; - // Target versions first - targetBuildVerStr = strtok(NULL, "."); - if(targetBuildVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} // No more tokens - if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) - {return ERR_INVALID_IMPORT_NAME;} // Encountered null terminator instead of '.' + for (int64_t i = importLibraries->tableCount - 1; i >= 0; i--) { + importTables[i].tableIndex = i; - buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); - if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr) - {return ERR_INVALID_IMPORT_NAME;} // Encountered a non-number, or string was empty + const uint8_t majorVer = 2; + const uint8_t minorVer = 0; + char *targetBuildVerStr = NULL; + char *targetHotfixVerStr = NULL; + char *minimumBuildVerStr = NULL; + char *minimumHotfixVerStr = NULL; + uint16_t buildVer = 0; + uint8_t hotfixVer = 0; + char *strtoulRet = NULL; - targetHotfixVerStr = strtok(NULL, "+"); - if(targetHotfixVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} - if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) - {return ERR_INVALID_IMPORT_NAME;} - - hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); - if(*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) - {return ERR_INVALID_IMPORT_NAME;} - - // Now pack these into the target version bitfield - importTables[i].targetVer = - ((majorVer & 0xF) << (32 - 4)) - | ((minorVer & 0xF) << (32 - 4 - 4)) - | (buildVer << (32 - 4 - 4 - 16)) - | hotfixVer; - - // Now onto the minimum versions, this works much the same - minimumBuildVerStr = strtok(NULL, "."); - if(minimumBuildVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} // No more tokens - if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) - + 1 + strlen(minimumBuildVerStr) == oldNameLen) - {return ERR_INVALID_IMPORT_NAME;} // Encountered null terminator instead of '.' - - buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); - if(*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) - {return ERR_INVALID_IMPORT_NAME;} // Encountered a non-number, or string was empty - - minimumHotfixVerStr = strtok(NULL, "\0"); - if(minimumHotfixVerStr == NULL) {return ERR_INVALID_IMPORT_NAME;} - - hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); - if(*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) - {return ERR_INVALID_IMPORT_NAME;} - - // Now pack these into the minimum version bitfield - importTables[i].minimumVer = - ((majorVer & 0xF) << (32 - 4)) - | ((minorVer & 0xF) << (32 - 4 - 4)) - | (buildVer << (32 - 4 - 4 - 16)) - | hotfixVer; - - // Hardcode a currently unknown value. TODO: find out how this is calculated. - if(strcmp(names[i], "xboxkrnl.exe") == 0) - { - importTables[i].unknown = 0x45DC17E0; - } - else if(strcmp(names[i], "xam.xex") == 0) - { - importTables[i].unknown = 0xFCA15C76; - } - else if(strcmp(names[i], "xbdm.xex") == 0) - { - importTables[i].unknown = 0xECEB8109; - } - else - { - return ERR_UNSUPPORTED_STRUCTURE; - } - - // Determine the number of addresses (2 for functions, 1 for everything else) - importTables[i].addressCount = - (peImportInfo->tables[i].branchStubCount * 2) - + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); - - // Allocate enough memory for the addresses - importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); - if(importTables[i].addresses == NULL) {return ERR_OUT_OF_MEM;} - uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer - - // Populate the addresses - uint16_t currentAddr = 0; - - for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) - { - if(currentAddr >= importTables[i].addressCount) - { - return ERR_DATA_OVERFLOW; - } - - addresses[currentAddr] = peImportInfo->tables[i].imports[j].iatAddr; - currentAddr++; - - if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) - { - if(currentAddr >= importTables[i].addressCount) - { - return ERR_DATA_OVERFLOW; - } - - addresses[currentAddr] = peImportInfo->tables[i].imports[j].branchStubAddr; - currentAddr++; - } - } - - // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) - importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); - importLibraries->size += importTables[i].size; - - // Init sha1 hash - struct sha1_ctx shaContext; - sha1_init(&shaContext); - - // On little endian this ensures the byteswapped address count doesn't cause any trouble when using it. - // On big endian it's pointless but here so the code isn't too complex with differences between endianness. - uint16_t addressCount = importTables[i].addressCount; - - // If we're on a little endian system, swap everything into big endian for hashing -#ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - - // Byteswap the addresses - for(uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } -#endif - - // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) - // +/- sizeof(uint32_t) to exclude table size from hash - sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&(importTables[i]) + sizeof(uint32_t)); - sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); - - // If we're on a little endian system, swap everything back into little endian -#ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - - // Byteswap the addresses - for(uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } -#endif - - if(i != 0) - { - sha1_digest(&shaContext, 0x14, importTables[i - 1].sha1); - } - else - { - sha1_digest(&shaContext, 0x14, secInfoHeader->importTableSha1); - } + uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); + names[i] = strtok(peImportInfo->tables[i].name, "@"); + if (!peImportInfo->tables[i].name) { + fprintf(stderr, "ERROR: tables[%d].name is NULL\n", i); + goto cleanup_names_invalid; } - // Allocate enough memory for the name table - uint32_t nameOffsets[importLibraries->tableCount]; + targetBuildVerStr = strtok(NULL, "."); + if (!targetBuildVerStr) + goto cleanup_names_invalid; - for(uint32_t i = 0; i < importLibraries->tableCount; i++) - { - nameOffsets[i] = importLibraries->nameTableSize; - importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) + goto cleanup_names_invalid; + + buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); + if (*strtoulRet != 0 || strtoulRet == targetBuildVerStr) + goto cleanup_names_invalid; + + targetHotfixVerStr = strtok(NULL, "+"); + if (!targetHotfixVerStr) + goto cleanup_names_invalid; + + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) + goto cleanup_names_invalid; + + hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); + if (*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) + goto cleanup_names_invalid; + + importTables[i].targetVer = + ((majorVer & 0xF) << 28) | + ((minorVer & 0xF) << 24) | + (buildVer << 8) | + hotfixVer; + + minimumBuildVerStr = strtok(NULL, "."); + if (!minimumBuildVerStr) + goto cleanup_names_invalid; + + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + + 1 + strlen(minimumBuildVerStr) == oldNameLen) + goto cleanup_names_invalid; + + buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); + if (*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) + goto cleanup_names_invalid; + + minimumHotfixVerStr = strtok(NULL, "\0"); + if (!minimumHotfixVerStr) + goto cleanup_names_invalid; + + hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); + if (*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) + goto cleanup_names_invalid; + + importTables[i].minimumVer = + ((majorVer & 0xF) << 28) | + ((minorVer & 0xF) << 24); + if (strcmp(names[i], "xboxkrnl.exe") == 0) { + importTables[i].unknown = 0x45DC17E0; + } else if (strcmp(names[i], "xam.xex") == 0) { + importTables[i].unknown = 0xFCA15C76; + } else if (strcmp(names[i], "xbdm.xex") == 0) { + importTables[i].unknown = 0xECEB8109; + } else { + goto cleanup_names_invalid; } + importTables[i].addressCount = + (peImportInfo->tables[i].branchStubCount * 2) + + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + + importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); + if (!importTables[i].addresses) + goto cleanup_names_invalid; + + uint32_t *addresses = importTables[i].addresses; + uint16_t currentAddr = 0; + + for (uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { + if (currentAddr >= importTables[i].addressCount) + goto cleanup_names_invalid; + + addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr; + + if (peImportInfo->tables[i].imports[j].branchStubAddr != 0) { + if (currentAddr >= importTables[i].addressCount) + goto cleanup_names_invalid; + addresses[currentAddr++] = peImportInfo->tables[i].imports[j].branchStubAddr; + } + } + + importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); + importLibraries->size += importTables[i].size; + + struct sha1_ctx shaContext; + memset(&shaContext, 0, sizeof(shaContext)); + sha1_init(&shaContext); + + uint16_t addressCount = importTables[i].addressCount; + +#ifdef LITTLE_ENDIAN_SYSTEM + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + for (uint16_t j = 0; j < addressCount; j++) + addresses[j] = __builtin_bswap32(addresses[j]); +#endif + + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&importTables[i] + sizeof(uint32_t)); + sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); + +#ifdef LITTLE_ENDIAN_SYSTEM + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + for (uint16_t j = 0; j < addressCount; j++) + addresses[j] = __builtin_bswap32(addresses[j]); +#endif + + sha1_digest(&shaContext, 0x14, i != 0 ? importTables[i - 1].sha1 : secInfoHeader->importTableSha1); + } + + // Allocate offset table + uint32_t *nameOffsets = calloc(importLibraries->tableCount, sizeof(uint32_t)); + if (!nameOffsets) + goto cleanup_names; + + for (uint32_t i = 0; i < importLibraries->tableCount; i++) { + nameOffsets[i] = importLibraries->nameTableSize; + importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); + } + importLibraries->size += importLibraries->nameTableSize; importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); - if(importLibraries->nameTable == NULL) {return ERR_OUT_OF_MEM;} - char *nameTable = importLibraries->nameTable; // Use this to avoid dereferencing an unaligned pointer + if (!importLibraries->nameTable) + goto cleanup_offsets; - // Populate the name table - for(uint32_t i = 0; i < importLibraries->tableCount; i++) - { - strcpy(&(nameTable[nameOffsets[i]]), names[i]); - } + char *nameTable = importLibraries->nameTable; + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + strcpy(&(nameTable[nameOffsets[i]]), names[i]); + nullAndFree((void**)&nameOffsets); + nullAndFree((void**)&names); return SUCCESS; + +cleanup_offsets: + nullAndFree((void**)&nameOffsets); +cleanup_names: + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + nullAndFree((void**)&(importTables[i].addresses)); +cleanup_names_invalid: + nullAndFree((void**)&names); +cleanup_tables: + nullAndFree((void**)&(importLibraries->importTables)); + importLibraries->importTables = NULL; + return ret; } // STUB. TODO: Dynamically select, and/or allow user to select, flags void setSysFlags(uint32_t *flags) { + if (flags == NULL) + return; + *flags = XEX_SYS_GAMEPAD_DISCONNECT | XEX_SYS_INSECURE_SOCKETS | XEX_SYS_XAM_HOOKS | @@ -276,13 +255,12 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st optHeaderEntries->count = 4; - if(importsPresent) - { - optHeaderEntries->count++; - } - + if (importsPresent) + optHeaderEntries->count++; + optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry)); - if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;} + if (optHeaderEntries->optHeaderEntry == NULL) + return ERR_OUT_OF_MEM; uint32_t currentHeader = 0; @@ -299,13 +277,13 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st currentHeader++; // Import libraries (0x103FF) - if(importsPresent) - { - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; - int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); - if(ret != SUCCESS) {return ret;} - currentHeader++; - } + if (importsPresent) { + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; + int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); + if (ret != SUCCESS) + return ret; + currentHeader++; + } // TLS info (0x20104) optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_TLS_INFO; diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 5ab7a79..3e771bd 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -22,14 +22,10 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3 { uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; uint32_t currentOffset = page * pageSize; - - for(int32_t i = peData->sections.count - 1; i >= 0; i--) - { - if(currentOffset >= peData->sections.section[i].rva) - { - return peData->sections.section[i].permFlag; - } - } + + for (int32_t i = peData->sections.count - 1; i >= 0; i--) + if (currentOffset >= peData->sections.section[i].rva) + return peData->sections.section[i].permFlag; return XEX_SECTION_RODATA | 0b10000; // We're in the PE header, so RODATA } @@ -37,45 +33,51 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3 int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *secInfoHeader) { uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; + secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); - if(secInfoHeader->descriptors == NULL) {return ERR_OUT_OF_MEM;} - struct pageDescriptor *descriptors = secInfoHeader->descriptors; // So we don't dereference an unaligned pointer - - // Setting size/info data and calculating hashes for page descriptors - for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) - { - // Get page type (rwx) - descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); - - // Init sha1 hash - struct sha1_ctx shaContext; - sha1_init(&shaContext); - - fseek(pe, i * pageSize, SEEK_SET); - uint8_t page[pageSize]; - fread(page, sizeof(uint8_t), pageSize, pe); + if (!secInfoHeader->descriptors) + return ERR_OUT_OF_MEM; - // For little endian systems, swap into big endian for hashing, then back (to keep struct endianness consistent) -#ifdef LITTLE_ENDIAN_SYSTEM - descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); -#endif - - sha1_update(&shaContext, pageSize, page); - sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); + struct pageDescriptor *descriptors = secInfoHeader->descriptors; -#ifdef LITTLE_ENDIAN_SYSTEM - descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); -#endif - - if(i != 0) - { - sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); - } - else - { - sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); - } + for (int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) { + descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); + + struct sha1_ctx shaContext; + sha1_init(&shaContext); + + if (fseek(pe, i * pageSize, SEEK_SET) != 0) + return ERR_FILE_READ; + + uint8_t *page = malloc(pageSize); + if (!page) + return ERR_OUT_OF_MEM; + + if (fread(page, 1, pageSize, pe) != pageSize) { + nullAndFree((void**)&page); + return ERR_FILE_READ; } + #ifdef LITTLE_ENDIAN_SYSTEM + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); + #endif + + sha1_update(&shaContext, pageSize, page); + sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); + + #ifdef LITTLE_ENDIAN_SYSTEM + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); + #endif + + if (i != 0) { + sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); + } else { + sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); + } + + nullAndFree((void**)&page); + } + return SUCCESS; } + diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index ad2f26e..1252f7a 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -25,23 +25,15 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader // Module flags (type of executable) // Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead - if(xexHeader->moduleFlags == 0) - { - if(peData->characteristics & PE_CHAR_FLAG_DLL) - { - xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL - } - else - { - xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title - } + if (xexHeader->moduleFlags == 0) { + if (peData->characteristics & PE_CHAR_FLAG_DLL) + xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL + else + xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title + if (peData->peExportInfo.count > 0) + xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions + } - if(peData->peExportInfo.count > 0) - { - xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions - } - } - xexHeader->optHeaderCount = optHeaderEntries->count; return SUCCESS; diff --git a/src/write/headerhash.c b/src/write/headerhash.c index 47fbb94..a7f756e 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -21,70 +21,136 @@ // TEMPORARY, TO BE REPLACED ELSEWHERE WHEN IMPORT HEADER IS IMPLEMENTED void setImportsSha1(FILE *xex) { - fseek(xex, 0x0, SEEK_SET); - while(get32BitFromXEX(xex) != 0x103FF){} - fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET); - + if (fseek(xex, 0, SEEK_SET) != 0) + return; + + while (get32BitFromXEX(xex) != 0x103FF) { + if (feof(xex)) + return; + } + + if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET) != 0) + return; + uint8_t importCount[4]; - fseek(xex, 0x4, SEEK_CUR); - fread(importCount, sizeof(uint8_t), 4, xex); - fseek(xex, -0x8, SEEK_CUR); - - fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR); + memset(importCount, 0, sizeof(importCount)); + + if (fseek(xex, 0x4, SEEK_CUR) != 0) + return; + + if (fread(importCount, 1, 4, xex) != 4) + return; + + if (fseek(xex, -0x8, SEEK_CUR) != 0) + return; + + if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR) != 0) + return; + uint32_t size = get32BitFromXEX(xex) - 0x4; - - uint8_t data[size]; - fread(data, sizeof(uint8_t), size, xex); - + + uint8_t *data = malloc(size); + if (!data) + return; + + memset(data, 0, size); + + if (fread(data, 1, size, xex) != size) { + nullAndFree((void**)&data); + return; + } + struct sha1_ctx shaContext; sha1_init(&shaContext); sha1_update(&shaContext, size, data); + nullAndFree((void**)&data); + uint8_t sha1[20]; + memset(sha1, 0, sizeof(sha1)); sha1_digest(&shaContext, 20, sha1); - fseek(xex, 0x10, SEEK_SET); - fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET); - fwrite(importCount, sizeof(uint8_t), 4, xex); - fwrite(sha1, sizeof(uint8_t), 20, xex); + if (fseek(xex, 0x10, SEEK_SET) != 0) + return; + + if (fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET) != 0) + return; + + fwrite(importCount, 1, 4, xex); + fwrite(sha1, 1, 20, xex); } + // So, it would probably be more sensible to endian-swap all of the data back and determine which structure is where // to determine the hash, but reading the file we just created is easier. int setHeaderSha1(FILE *xex) { - fseek(xex, 0x8, SEEK_SET); // Basefile offset + if (fseek(xex, 0x8, SEEK_SET) != 0) + return ERR_FILE_READ; + uint32_t basefileOffset = get32BitFromXEX(xex); - - fseek(xex, 0x10, SEEK_SET); // Secinfo offset + + if (fseek(xex, 0x10, SEEK_SET) != 0) + return ERR_FILE_READ; + uint32_t secInfoOffset = get32BitFromXEX(xex); - uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; // 0x8 == image info offset in security info, 0x174 == length of that - uint32_t remainingSize = basefileOffset - endOfImageInfo; // How much data is between end of image info and basefile (we hash that too) + uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; + uint32_t remainingSize = basefileOffset - endOfImageInfo; - // Init sha1 hash struct sha1_ctx shaContext; + memset(&shaContext, 0, sizeof(shaContext)); sha1_init(&shaContext); - // Hash first part (remainder of headers is done first, then the start) - uint8_t remainderOfHeaders[remainingSize]; - fseek(xex, endOfImageInfo, SEEK_SET); - fread(remainderOfHeaders, sizeof(uint8_t), remainingSize, xex); + uint8_t *remainderOfHeaders = malloc(remainingSize); + if (!remainderOfHeaders) + return ERR_OUT_OF_MEM; + + memset(remainderOfHeaders, 0, remainingSize); + + if (fseek(xex, endOfImageInfo, SEEK_SET) != 0) { + nullAndFree((void**)&remainderOfHeaders); + return ERR_FILE_READ; + } + + if (fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) { + nullAndFree((void**)&remainderOfHeaders); + return ERR_FILE_READ; + } + sha1_update(&shaContext, remainingSize, remainderOfHeaders); - - uint8_t headersStart[secInfoOffset + 0x8]; // Hash from start up to image info (0x8 into security header) - fseek(xex, 0, SEEK_SET); - - fread(headersStart, sizeof(uint8_t), secInfoOffset + 0x8, xex); - sha1_update(&shaContext, secInfoOffset + 0x8, headersStart); - - // Get final hash + nullAndFree((void**)&remainderOfHeaders); + + uint32_t headersLen = secInfoOffset + 0x8; + uint8_t *headersStart = malloc(headersLen); + if (!headersStart) + return ERR_OUT_OF_MEM; + + memset(headersStart, 0, headersLen); + + if (fseek(xex, 0, SEEK_SET) != 0) { + nullAndFree((void**)&headersStart); + return ERR_FILE_READ; + } + + if (fread(headersStart, 1, headersLen, xex) != headersLen) { + nullAndFree((void**)&headersStart); + return ERR_FILE_READ; + } + + sha1_update(&shaContext, headersLen, headersStart); + nullAndFree((void**)&headersStart); + uint8_t headerHash[20]; + memset(headerHash, 0, sizeof(headerHash)); sha1_digest(&shaContext, 20, headerHash); - // Finally, write it out - fseek(xex, secInfoOffset + 0x164, SEEK_SET); // 0x164 == offset in secinfo of header hash - fwrite(headerHash, sizeof(uint8_t), 20, xex); + if (fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) + return ERR_FILE_READ; + + if (fwrite(headerHash, 1, 20, xex) != 20) + return ERR_FILE_READ; return SUCCESS; } + diff --git a/src/write/writexex.c b/src/write/writexex.c index 4f4bcd8..0f30b5f 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -77,8 +77,23 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize) { - fread(buffer, sizeof(uint8_t), readBufSize, pe); - fwrite(buffer, sizeof(uint8_t), readBufSize, xex); + size_t bytesToRead = (secInfoHeader->peSize - i < readBufSize) + ? secInfoHeader->peSize - i + : readBufSize; + + size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe); + if (readRet != bytesToRead) + { + fprintf(stderr, "Error: fread failed at offset %u (read %zu of %zu bytes)\n", i, readRet, bytesToRead); + break; + } + + size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex); + if (writeRet != readRet) + { + fprintf(stderr, "Error: fwrite failed at offset %u (wrote %zu of %zu bytes)\n", i, writeRet, readRet); + break; + } } nullAndFree((void**)&buffer); @@ -152,26 +167,26 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr struct importTable *importTables = optHeaders->importLibraries.importTables; for(uint32_t i = 0; i < tableCount; i++) - { - uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer - uint16_t addressCount = importTables[i].addressCount; + { + uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer + uint16_t addressCount = importTables[i].addressCount; #ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - for(uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } + for(uint16_t j = 0; j < addressCount; j++) + { + addresses[j] = __builtin_bswap32(addresses[j]); + } #endif - fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); - fwrite(addresses, sizeof(uint32_t), addressCount, xex); - } + fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); + fwrite(addresses, sizeof(uint32_t), addressCount, xex); + } currentHeader++; } From 1ff1e8f93f83706240bd5e22f0b0f834f8aa19d3 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 16 Jun 2025 15:31:35 +0100 Subject: [PATCH 22/38] Reformat all code to modified Allman/BSD style --- .gitignore | 1 + src/common/datastorage.c | 267 ++++++----- src/common/datastorage.h | 194 ++++---- src/getdata/gethdrdata.c | 299 ++++++------ src/getdata/getimports.c | 545 ++++++++++++---------- src/main.c | 833 +++++++++++++++++++--------------- src/pemapper/pemapper.c | 154 ++++--- src/placer/placer.c | 131 +++--- src/setdata/optheaders.c | 533 +++++++++++++--------- src/setdata/pagedescriptors.c | 103 +++-- src/setdata/populateheaders.c | 68 +-- src/write/headerhash.c | 219 +++++---- src/write/writexex.c | 317 ++++++------- 13 files changed, 2065 insertions(+), 1599 deletions(-) diff --git a/.gitignore b/.gitignore index ffd84af..d420d42 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ result *~ *# \#* +*.orig diff --git a/src/common/datastorage.c b/src/common/datastorage.c index b35b735..7728242 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -23,13 +23,14 @@ // Disable pointer type checking here to make this easier to use void nullAndFree(void **ptr) { - if (ptr == NULL) - return; - - if (*ptr != NULL) { - freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); - *ptr = NULL; - } + if (ptr != NULL) + { + if (*ptr != NULL) + { + freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); + *ptr = NULL; + } + } } // These functions together handle freeing all of the main structs. @@ -45,205 +46,235 @@ void nullAndFree(void **ptr) // about if it becomes necessary with future additions. void freeOffsetsStruct(struct offsets **offsets) { - if (offsets == NULL) - return; - - if (*offsets != NULL) { - nullAndFree((void**)&((*offsets)->optHeaders)); - nullAndFree((void**)offsets); - } + if (offsets != NULL) + { + if (*offsets != NULL) + { + nullAndFree((void**)&((*offsets)->optHeaders)); + nullAndFree((void**)offsets); + } + } } void freeXexHeaderStruct(struct xexHeader **xexHeader) { - if (xexHeader == NULL) - return; - - if (*xexHeader != NULL) { - nullAndFree((void**)xexHeader); - } + if (xexHeader != NULL) + { + if (*xexHeader != NULL) + { + nullAndFree((void**)xexHeader); + } + } } void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) { - if (secInfoHeader == NULL) - return; - - if (*secInfoHeader != NULL) { - struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer - nullAndFree((void**)&descriptors); - //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways - nullAndFree((void**)secInfoHeader); - } + if (secInfoHeader != NULL) + { + if (*secInfoHeader != NULL) + { + struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer + nullAndFree((void**)&descriptors); + //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways + nullAndFree((void**)secInfoHeader); + } + } } void freeSectionsStruct(struct sections *sections) { - nullAndFree((void**)&(sections->section)); + nullAndFree((void**)&(sections->section)); } void freePeImportInfoStruct(struct peImportInfo *peImportInfo) { - if (peImportInfo->tables != NULL) { - // Free the imports within each table first, then the tables, - // otherwise we'll have a memory leak - for (uint32_t i = 0; i < peImportInfo->tableCount; i++) { - nullAndFree((void**)&(peImportInfo->tables[i].name)); - nullAndFree((void**)&(peImportInfo->tables[i].imports)); - } + if (peImportInfo->tables != NULL) + { + // Free the imports within each table first, then the tables, + // otherwise we'll have a memory leak + for (uint32_t i = 0; i < peImportInfo->tableCount; i++) + { + nullAndFree((void**)&(peImportInfo->tables[i].name)); + nullAndFree((void**)&(peImportInfo->tables[i].imports)); + } - nullAndFree((void**)&(peImportInfo->tables)); - } + nullAndFree((void**)&(peImportInfo->tables)); + } } void freePeDataStruct(struct peData **peData) { - if (*peData != NULL) { - freeSectionsStruct(&((*peData)->sections)); - freePeImportInfoStruct(&((*peData)->peImportInfo)); - nullAndFree((void**)peData); - } + if (*peData != NULL) + { + freeSectionsStruct(&((*peData)->sections)); + freePeImportInfoStruct(&((*peData)->peImportInfo)); + nullAndFree((void**)peData); + } } void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) { - if (*optHeaderEntries != NULL) { - nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); - nullAndFree((void**)optHeaderEntries); - } + if (*optHeaderEntries != NULL) + { + nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); + nullAndFree((void**)optHeaderEntries); + } } void freeImportLibrariesStruct(struct importLibraries *importLibraries) { - struct importTable *importTables = importLibraries->importTables; - char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers + struct importTable *importTables = importLibraries->importTables; + char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers - nullAndFree((void**)&nameTable); + nullAndFree((void**)&nameTable); - if (importTables != NULL) { - for (uint32_t i = 0; i < importLibraries->tableCount; i++) { - uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer - nullAndFree((void**)&addresses); - importTables[i].addresses = addresses; + if (importTables != NULL) + { + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + { + uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer + nullAndFree((void**)&addresses); + importTables[i].addresses = addresses; + } + + nullAndFree((void**)&importTables); + importLibraries->importTables = importTables; } - - nullAndFree((void**)&importTables); - importLibraries->importTables = importTables; - } } void freeOptHeadersStruct(struct optHeaders **optHeaders) { - if (*optHeaders != NULL) { - freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); - nullAndFree((void**)optHeaders); - } + if (*optHeaders != NULL) + { + freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); + nullAndFree((void**)optHeaders); + } } void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders) { - freeOffsetsStruct(offsets); - freeXexHeaderStruct(xexHeader); - freeSecInfoHeaderStruct(secInfoHeader); - freePeDataStruct(peData); - freeOptHeaderEntriesStruct(optHeaderEntries); - freeOptHeadersStruct(optHeaders); + freeOffsetsStruct(offsets); + freeXexHeaderStruct(xexHeader); + freeSecInfoHeaderStruct(secInfoHeader); + freePeDataStruct(peData); + freeOptHeaderEntriesStruct(optHeaderEntries); + freeOptHeadersStruct(optHeaders); } uint32_t getNextAligned(uint32_t offset, uint32_t alignment) { - if (offset % alignment) // If offset not aligned - return offset + (alignment - (offset % alignment)); // Align + if (offset % alignment) // If offset not aligned + { + return offset + (alignment - (offset % alignment)); // Align + } - return offset; // Offset already aligned + return offset; // Offset already aligned } uint32_t rvaToOffset(uint32_t rva, struct sections *sections) { - if ((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) - return 0; // Not found (beyond end of PE) + if ((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) + { + return 0; // Not found (beyond end of PE) + } - for (int32_t i = sections->count - 1; i >= 0; i--) { - if (rva >= sections->section[i].rva) - return (rva - sections->section[i].rva) + sections->section[i].offset; - } + for (int32_t i = sections->count - 1; i >= 0; i--) + { + if (rva >= sections->section[i].rva) + { + return (rva - sections->section[i].rva) + sections->section[i].offset; + } + } - return 0; // Not found + return 0; // Not found } uint32_t offsetToRVA(uint32_t offset, struct sections *sections) { - if ((sections->count > 0) - && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) - return 0; // Not found (beyond end of PE) + if ((sections->count > 0) + && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) + { + return 0; // Not found (beyond end of PE) + } - for (int32_t i = sections->count - 1; i >= 0; i--) { - if (offset >= sections->section[i].offset) - return (offset - sections->section[i].offset) + sections->section[i].rva; - } + for (int32_t i = sections->count - 1; i >= 0; i--) + { + if (offset >= sections->section[i].offset) + { + return (offset - sections->section[i].offset) + sections->section[i].rva; + } + } - return 0; // Not found + return 0; // Not found } uint32_t get32BitFromPE(FILE *pe) { - uint32_t result; - if (fread(&result, sizeof(uint32_t), 1, pe) != 1) { - fprintf(stderr, "Error: Failed to read 32-bit value from PE\n"); - exit(EXIT_FAILURE); - } + uint32_t result; - // If system is big endian, swap endianness (PE is LE) + if (fread(&result, sizeof(uint32_t), 1, pe) != 1) + { + fprintf(stderr, "%s Error: Failed to read 32-bit value from PE\n", PRINT_STEM); + exit(EXIT_FAILURE); + } + + // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM - return __builtin_bswap32(result); + return __builtin_bswap32(result); #else - return result; + return result; #endif } uint16_t get16BitFromPE(FILE *pe) { - uint16_t result; - if (fread(&result, sizeof(uint16_t), 1, pe) != 1) { - fprintf(stderr, "Error: Failed to read 16-bit value from PE\n"); - exit(EXIT_FAILURE); - } + uint16_t result; + + if (fread(&result, sizeof(uint16_t), 1, pe) != 1) + { + fprintf(stderr, "%s Error: Failed to read 16-bit value from PE\n", PRINT_STEM); + exit(EXIT_FAILURE); + } #ifdef BIG_ENDIAN_SYSTEM - return __builtin_bswap16(result); + return __builtin_bswap16(result); #else - return result; + return result; #endif } uint32_t get32BitFromXEX(FILE *xex) { - uint32_t result; - if (fread(&result, sizeof(uint32_t), 1, xex) != 1) { - fprintf(stderr, "Error: Failed to read 32-bit value from XEX\n"); - exit(EXIT_FAILURE); - } + uint32_t result; + + if (fread(&result, sizeof(uint32_t), 1, xex) != 1) + { + fprintf(stderr, "%s Error: Failed to read 32-bit value from XEX\n", PRINT_STEM); + exit(EXIT_FAILURE); + } #ifdef LITTLE_ENDIAN_SYSTEM - return __builtin_bswap32(result); + return __builtin_bswap32(result); #else - return result; + return result; #endif } uint16_t get16BitFromXEX(FILE *xex) { - uint16_t result; - if (fread(&result, sizeof(uint16_t), 1, xex) != 1) { - fprintf(stderr, "Error: Failed to read 16-bit value from XEX\n"); - exit(EXIT_FAILURE); - } + uint16_t result; + + if (fread(&result, sizeof(uint16_t), 1, xex) != 1) + { + fprintf(stderr, "%s Error: Failed to read 16-bit value from XEX\n", PRINT_STEM); + exit(EXIT_FAILURE); + } #ifdef LITTLE_ENDIAN_SYSTEM - return __builtin_bswap16(result); + return __builtin_bswap16(result); #else - return result; + return result; #endif -} \ No newline at end of file +} diff --git a/src/common/datastorage.h b/src/common/datastorage.h index fdcaad9..c12817e 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -54,7 +54,7 @@ #define XEX_OPT_ID_TLS_INFO 0x20104 #define XEX_OPT_ID_SYS_FLAGS 0x30000 -// System flags +// System flags #define XEX_SYS_GAMEPAD_DISCONNECT 0x00000020 #define XEX_SYS_INSECURE_SOCKETS 0x00000040 #define XEX_SYS_XAM_HOOKS 0x00001000 @@ -78,172 +78,172 @@ // Page RWX flags struct sections { - uint16_t count; - struct section *section; + uint16_t count; + struct section *section; }; struct section { - uint8_t permFlag; - uint32_t virtualSize; - uint32_t rva; - uint32_t rawSize; - uint32_t offset; + uint8_t permFlag; + uint32_t virtualSize; + uint32_t rva; + uint32_t rawSize; + uint32_t offset; }; struct peImport { - uint32_t iatAddr; - uint32_t branchStubAddr; + uint32_t iatAddr; + uint32_t branchStubAddr; }; struct peImportTable { - char *name; - uint32_t importCount; - uint32_t branchStubCount; - struct peImport *imports; + char *name; + uint32_t importCount; + uint32_t branchStubCount; + struct peImport *imports; }; struct peImportInfo { - uint32_t idtRVA; - uint32_t tableCount; - uint32_t totalImportCount; - uint32_t totalBranchStubCount; - struct peImportTable *tables; + uint32_t idtRVA; + uint32_t tableCount; + uint32_t totalImportCount; + uint32_t totalBranchStubCount; + struct peImportTable *tables; }; struct peExportInfo { - uint16_t count; + uint16_t count; }; // Data structs struct peData { - uint32_t size; - uint32_t baseAddr; - uint32_t entryPoint; - uint32_t tlsAddr; - uint32_t tlsSize; - uint32_t peHeaderOffset; - uint16_t numberOfSections; - uint32_t sectionTableSize; - uint32_t headerSize; - uint32_t pageSize; - uint16_t characteristics; - struct sections sections; - struct peImportInfo peImportInfo; - struct peExportInfo peExportInfo; + uint32_t size; + uint32_t baseAddr; + uint32_t entryPoint; + uint32_t tlsAddr; + uint32_t tlsSize; + uint32_t peHeaderOffset; + uint16_t numberOfSections; + uint32_t sectionTableSize; + uint32_t headerSize; + uint32_t pageSize; + uint16_t characteristics; + struct sections sections; + struct peImportInfo peImportInfo; + struct peExportInfo peExportInfo; }; // Most of these are 8-byte aligned. Basefile is 4KiB aligned. // In order of appearance struct offsets { - uint32_t xexHeader; - uint32_t optHeaderEntries; - uint32_t secInfoHeader; - uint32_t *optHeaders; - uint32_t basefile; + uint32_t xexHeader; + uint32_t optHeaderEntries; + uint32_t secInfoHeader; + uint32_t *optHeaders; + uint32_t basefile; }; struct __attribute__((packed)) xexHeader { - char magic[4]; - uint32_t moduleFlags; - uint32_t peOffset; - uint32_t reserved; - uint32_t secInfoOffset; - uint32_t optHeaderCount; + char magic[4]; + uint32_t moduleFlags; + uint32_t peOffset; + uint32_t reserved; + uint32_t secInfoOffset; + uint32_t optHeaderCount; }; struct __attribute__((packed)) pageDescriptor { - uint32_t sizeAndInfo; // First 28 bits == size, last 4 == info (RO/RW/X) - uint8_t sha1[0x14]; + uint32_t sizeAndInfo; // First 28 bits == size, last 4 == info (RO/RW/X) + uint8_t sha1[0x14]; }; struct __attribute__((packed)) secInfoHeader { - uint32_t headerSize; - uint32_t peSize; - // - IMAGE INFO - - uint8_t signature[0x100]; - uint32_t imageInfoSize; - uint32_t imageFlags; - uint32_t baseAddr; - uint8_t imageSha1[0x14]; - uint32_t importTableCount; - uint8_t importTableSha1[0x14]; - uint8_t mediaID[0x10]; - uint8_t aesKey[0x10]; - uint32_t exportTableAddr; - uint8_t headersHash[0x14]; - uint32_t gameRegion; - // - IMAGE INFO - - uint32_t mediaTypes; - uint32_t pageDescCount; - struct pageDescriptor *descriptors; + uint32_t headerSize; + uint32_t peSize; + // - IMAGE INFO - + uint8_t signature[0x100]; + uint32_t imageInfoSize; + uint32_t imageFlags; + uint32_t baseAddr; + uint8_t imageSha1[0x14]; + uint32_t importTableCount; + uint8_t importTableSha1[0x14]; + uint8_t mediaID[0x10]; + uint8_t aesKey[0x10]; + uint32_t exportTableAddr; + uint8_t headersHash[0x14]; + uint32_t gameRegion; + // - IMAGE INFO - + uint32_t mediaTypes; + uint32_t pageDescCount; + struct pageDescriptor *descriptors; }; struct __attribute__((packed)) basefileFormat { - uint32_t size; - uint16_t encType; - uint16_t compType; - uint32_t dataSize; - uint32_t zeroSize; + uint32_t size; + uint16_t encType; + uint16_t compType; + uint32_t dataSize; + uint32_t zeroSize; }; struct __attribute__((packed)) importTable { - uint32_t size; - uint8_t sha1[0x14]; - uint32_t unknown; // Unique to each executable imported from, and always the same - uint32_t targetVer; - uint32_t minimumVer; - uint8_t padding; - uint8_t tableIndex; - uint16_t addressCount; - uint32_t *addresses; // IAT entry address followed by branch stub code address for functions, just IAT address for other symbols + uint32_t size; + uint8_t sha1[0x14]; + uint32_t unknown; // Unique to each executable imported from, and always the same + uint32_t targetVer; + uint32_t minimumVer; + uint8_t padding; + uint8_t tableIndex; + uint16_t addressCount; + uint32_t *addresses; // IAT entry address followed by branch stub code address for functions, just IAT address for other symbols }; struct __attribute__((packed)) importLibraries { - uint32_t size; - uint32_t nameTableSize; - uint32_t tableCount; - char *nameTable; - struct importTable *importTables; + uint32_t size; + uint32_t nameTableSize; + uint32_t tableCount; + char *nameTable; + struct importTable *importTables; }; struct __attribute__((packed)) tlsInfo { - uint32_t slotCount; - uint32_t rawDataAddr; - uint32_t dataSize; - uint32_t rawDataSize; + uint32_t slotCount; + uint32_t rawDataAddr; + uint32_t dataSize; + uint32_t rawDataSize; }; struct optHeaderEntries { - uint32_t count; - struct optHeaderEntry *optHeaderEntry; + uint32_t count; + struct optHeaderEntry *optHeaderEntry; }; struct __attribute__((packed)) optHeaderEntry { - uint32_t id; - uint32_t dataOrOffset; + uint32_t id; + uint32_t dataOrOffset; }; struct optHeaders { - struct basefileFormat basefileFormat; - struct importLibraries importLibraries; - struct tlsInfo tlsInfo; + struct basefileFormat basefileFormat; + struct importLibraries importLibraries; + struct tlsInfo tlsInfo; }; // Functions used for easier memory management @@ -266,7 +266,7 @@ void freePeDataStruct(struct peData **peData); void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries); void freeOptHeadersStruct(struct optHeaders **optHeaders); void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader, - struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders); + struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders); // Functions used for file data manipulation diff --git a/src/getdata/gethdrdata.c b/src/getdata/gethdrdata.c index a2c6767..5289a3e 100644 --- a/src/getdata/gethdrdata.c +++ b/src/getdata/gethdrdata.c @@ -24,179 +24,224 @@ // dedicated place where we validate the input. bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false { - // Check if we have at least the size of a DOS header, so we don't overrun the PE - fseek(pe, 0, SEEK_END); - size_t finalOffset = ftell(pe); + // Check if we have at least the size of a DOS header, so we don't overrun the PE + fseek(pe, 0, SEEK_END); + size_t finalOffset = ftell(pe); - if (finalOffset < 0x3C + 0x4) - return false; + if (finalOffset < 0x3C + 0x4) + { + return false; + } - // Check magic - fseek(pe, 0, SEEK_SET); - uint16_t magic = get16BitFromPE(pe); + // Check magic + fseek(pe, 0, SEEK_SET); + uint16_t magic = get16BitFromPE(pe); - if (magic != 0x5A4D) // PE magic - return false; + if (magic != 0x5A4D) // PE magic + { + return false; + } - // Check if pointer to PE header is valid - fseek(pe, 0x3C, SEEK_SET); - size_t peHeaderOffset = get32BitFromPE(pe); + // Check if pointer to PE header is valid + fseek(pe, 0x3C, SEEK_SET); + size_t peHeaderOffset = get32BitFromPE(pe); - if (finalOffset < peHeaderOffset) - return false; + if (finalOffset < peHeaderOffset) + { + return false; + } - // Check if the file is big enough to get size of optional header, and therefore size of whole PE header - if (finalOffset < 0x14 + 0x2) - return false; + // Check if the file is big enough to get size of optional header, and therefore size of whole PE header + if (finalOffset < 0x14 + 0x2) + { + return false; + } - // Check section count - fseek(pe, peHeaderOffset + 0x6, SEEK_SET); + // Check section count + fseek(pe, peHeaderOffset + 0x6, SEEK_SET); - uint16_t sectionCount = get16BitFromPE(pe); - if (sectionCount == 0) - return false; + uint16_t sectionCount = get16BitFromPE(pe); - // Check if the file is large enough to contain the whole PE header - fseek(pe, peHeaderOffset + 0x14, SEEK_SET); - uint16_t sizeOfOptHdr = get16BitFromPE(pe); + if (sectionCount == 0) + { + return false; + } - // 0x18 == size of COFF header, 0x28 == size of one entry in section table - if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28)) - return false; + // Check if the file is large enough to contain the whole PE header + fseek(pe, peHeaderOffset + 0x14, SEEK_SET); + uint16_t sizeOfOptHdr = get16BitFromPE(pe); - // Check machine ID - fseek(pe, peHeaderOffset + 0x4, SEEK_SET); + // 0x18 == size of COFF header, 0x28 == size of one entry in section table + if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28)) + { + return false; + } - // 0x1F2 == POWERPCBE - uint16_t machineID = get16BitFromPE(pe); - if (machineID != 0x1F2 && !skipMachineCheck) - return false; + // Check machine ID + fseek(pe, peHeaderOffset + 0x4, SEEK_SET); - // Check subsystem - fseek(pe, peHeaderOffset + 0x5C, SEEK_SET); + // 0x1F2 == POWERPCBE + uint16_t machineID = get16BitFromPE(pe); - uint16_t subsystem = get16BitFromPE(pe); - if (subsystem != 0xE) // 0xE == XBOX - return false; + if (machineID != 0x1F2 && !skipMachineCheck) + { + return false; + } - // Check page size/alignment - fseek(pe, peHeaderOffset + 0x38, SEEK_SET); + // Check subsystem + fseek(pe, peHeaderOffset + 0x5C, SEEK_SET); - // 4KiB and 64KiB are the only valid sizes - uint32_t pageSize = get32BitFromPE(pe); - if (pageSize != 0x1000 && pageSize != 0x10000) - return false; + uint16_t subsystem = get16BitFromPE(pe); - // Check each raw offset + raw size in section table - fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry + if (subsystem != 0xE) // 0xE == XBOX + { + return false; + } - for (uint16_t i = 0; i < sectionCount; i++) { - // If raw size + raw offset exceeds file size, PE is invalid - if (finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe)) - return false; + // Check page size/alignment + fseek(pe, peHeaderOffset + 0x38, SEEK_SET); - // Next entry - fseek(pe, 0x20, SEEK_CUR); - } + // 4KiB and 64KiB are the only valid sizes + uint32_t pageSize = get32BitFromPE(pe); - return true; // Checked enough, this is an Xbox 360 PE file + if (pageSize != 0x1000 && pageSize != 0x10000) + { + return false; + } + + // Check each raw offset + raw size in section table + fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry + + for (uint16_t i = 0; i < sectionCount; i++) + { + // If raw size + raw offset exceeds file size, PE is invalid + if (finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe)) + { + return false; + } + + // Next entry + fseek(pe, 0x20, SEEK_CUR); + } + + return true; // Checked enough, this is an Xbox 360 PE file } int getSectionInfo(FILE *pe, struct sections *sections) { - fseek(pe, 0x3C, SEEK_SET); - uint32_t peOffset = get32BitFromPE(pe); + fseek(pe, 0x3C, SEEK_SET); + uint32_t peOffset = get32BitFromPE(pe); - fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count - sections->count = get16BitFromPE(pe); + fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count + sections->count = get16BitFromPE(pe); - sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata - if (sections->section == NULL) - return ERR_OUT_OF_MEM; + sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata - fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table + if (sections->section == NULL) + { + return ERR_OUT_OF_MEM; + } - for (uint16_t i = 0; i < sections->count; i++) { - fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section - sections->section[i].virtualSize = get32BitFromPE(pe); - sections->section[i].rva = get32BitFromPE(pe); - sections->section[i].rawSize = get32BitFromPE(pe); - sections->section[i].offset = get32BitFromPE(pe); + fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table - fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags - uint32_t characteristics = get32BitFromPE(pe); + for (uint16_t i = 0; i < sections->count; i++) + { + fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section + sections->section[i].virtualSize = get32BitFromPE(pe); + sections->section[i].rva = get32BitFromPE(pe); + sections->section[i].rawSize = get32BitFromPE(pe); + sections->section[i].offset = get32BitFromPE(pe); - if (characteristics & PE_SECTION_FLAG_EXECUTE) - sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 - else if (characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) - sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; - else if (characteristics & PE_SECTION_FLAG_READ) - sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; - else - return ERR_MISSING_SECTION_FLAG; - } + fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags + uint32_t characteristics = get32BitFromPE(pe); - // Don't need to progress any more to get to beginning of next entry, as characteristics is last field - return SUCCESS; + if (characteristics & PE_SECTION_FLAG_EXECUTE) + { + sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 + } + else if (characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) + { + sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; + } + else if (characteristics & PE_SECTION_FLAG_READ) + { + sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; + } + else + { + return ERR_MISSING_SECTION_FLAG; + } + } + + // Don't need to progress any more to get to beginning of next entry, as characteristics is last field + return SUCCESS; } int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) { - // No flags supported at this time (will be used for getting additional info, for e.g. other optional headers) - if (flags) - return ERR_UNKNOWN_DATA_REQUEST; + // No flags supported at this time (will be used for getting additional info, for e.g. other optional headers) + if (flags) + { + return ERR_UNKNOWN_DATA_REQUEST; + } - // Getting PE header offset before we go any further.. - fseek(pe, 0x3C, SEEK_SET); - peData->peHeaderOffset = get32BitFromPE(pe); + // Getting PE header offset before we go any further.. + fseek(pe, 0x3C, SEEK_SET); + peData->peHeaderOffset = get32BitFromPE(pe); - // Number of sections - fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET); - peData->numberOfSections = get16BitFromPE(pe); + // Number of sections + fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET); + peData->numberOfSections = get16BitFromPE(pe); - // Size of section table - peData->sectionTableSize = peData->numberOfSections * 0x28; + // Size of section table + peData->sectionTableSize = peData->numberOfSections * 0x28; - // Size of header - // 0x18 == size of COFF header, get16BitFromPE value == size of optional header - fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET); - peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe); + // Size of header + // 0x18 == size of COFF header, get16BitFromPE value == size of optional header + fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET); + peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe); - // PE characteristics - fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET); - peData->characteristics = get16BitFromPE(pe); + // PE characteristics + fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET); + peData->characteristics = get16BitFromPE(pe); - // Entry point (RVA) - fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); - peData->entryPoint = get32BitFromPE(pe); + // Entry point (RVA) + fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); + peData->entryPoint = get32BitFromPE(pe); - // Base address - fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET); - peData->baseAddr = get32BitFromPE(pe); + // Base address + fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET); + peData->baseAddr = get32BitFromPE(pe); - // Page alignment/size - fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET); - peData->pageSize = get32BitFromPE(pe); + // Page alignment/size + fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET); + peData->pageSize = get32BitFromPE(pe); - // Export tables - fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET); - peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data + // Export tables + fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET); + peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data - // Import tables - fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); - peData->peImportInfo.idtRVA = get32BitFromPE(pe); + // Import tables + fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); + peData->peImportInfo.idtRVA = get32BitFromPE(pe); - // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) - fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); - peData->tlsAddr = get32BitFromPE(pe); - peData->tlsSize = get32BitFromPE(pe); - if (peData->tlsAddr != 0 || peData->tlsSize != 0) - return ERR_UNSUPPORTED_STRUCTURE; + // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) + fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); + peData->tlsAddr = get32BitFromPE(pe); + peData->tlsSize = get32BitFromPE(pe); - // Section info - int ret = getSectionInfo(pe, &(peData->sections)); - if (ret != 0) - return ret; + if (peData->tlsAddr != 0 || peData->tlsSize != 0) + { + return ERR_UNSUPPORTED_STRUCTURE; + } - return SUCCESS; + // Section info + int ret = getSectionInfo(pe, &(peData->sections)); + + if (ret != 0) + { + return ret; + } + + return SUCCESS; } diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 35d65b2..69f43c1 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -20,270 +20,351 @@ int checkForBranchStub(FILE *pe, struct peData *peData) { - uint32_t expectedInstructions[] = { - // lis r, - // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction - 0x3C000000, + uint32_t expectedInstructions[] = + { + // lis r, + // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction + 0x3C000000, - // lwz r, (r) - // Mask 0xFC000000 for instruction - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction - 0x80000000, + // lwz r, (r) + // Mask 0xFC000000 for instruction + // Mask 0x03E00000 and shift right 21 bits for register 1 + // Mask 0x001F0000 and shift right 16 bits for register 2 + // Mask 0x0000FFFF for value + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction + 0x80000000, - // mtctr r - // Mask 0xFC1FFFFF for instruction (mtspr CTR) - // Mask 0x03E00000 and shift right 21 bits for register - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction - 0x7C0903A6, + // mtctr r + // Mask 0xFC1FFFFF for instruction (mtspr CTR) + // Mask 0x03E00000 and shift right 21 bits for register + // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction + 0x7C0903A6, - // bctr - // An assembler told me this, apparently IBM doesn't have documentation on their site for it - 0x4E800420 - }; + // bctr + // An assembler told me this, apparently IBM doesn't have documentation on their site for it + 0x4E800420 + }; - // Read in the set of instructions to check - uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); - if (instructionBuffer == NULL) - return ERR_OUT_OF_MEM; - memset(instructionBuffer, 0, sizeof(uint32_t) * 4); - if (fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) { - nullAndFree((void**)&instructionBuffer); - return ERR_FILE_READ; - } + // Read in the set of instructions to check + uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); + + if (instructionBuffer == NULL) + { + return ERR_OUT_OF_MEM; + } + + memset(instructionBuffer, 0, sizeof(uint32_t) * 4); + + if (fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) + { + nullAndFree((void**)&instructionBuffer); + return ERR_FILE_READ; + } #ifdef LITTLE_ENDIAN_SYSTEM - // Byteswap the instructions - for (uint8_t i = 0; i < 4; i++) - instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); + + // Byteswap the instructions + for (uint8_t i = 0; i < 4; i++) + { + instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); + } + #endif - // Check if each instruction matches - uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; + // Check if each instruction matches + uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; - if (((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && - (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) { - if (((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && - (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) && - (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) { - if (((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && - (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) { - if (instructionBuffer[3] == expectedInstructions[3]) { - uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | - (instructionBuffer[1] & 0x0000FFFF); + if (((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && + (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) + { + if (((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && + (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) && + (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) + { + if (((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && + (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) + { + if (instructionBuffer[3] == expectedInstructions[3]) + { + uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | + (instructionBuffer[1] & 0x0000FFFF); - for (uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) { - if (peData->peImportInfo.tables[i].importCount == - peData->peImportInfo.tables[i].branchStubCount) { - continue; - } + for (uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) + { + if (peData->peImportInfo.tables[i].importCount == + peData->peImportInfo.tables[i].branchStubCount) + { + continue; + } - for (uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) { - if (peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) { - continue; - } + for (uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) + { + if (peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) + { + continue; + } - if (peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) { - uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); - if (currentBranchStubRVA == 0) { - nullAndFree((void**)&instructionBuffer); - return ERR_INVALID_RVA_OR_OFFSET; + if (peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) + { + uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); + + if (currentBranchStubRVA == 0) + { + nullAndFree((void**)&instructionBuffer); + return ERR_INVALID_RVA_OR_OFFSET; + } + + peData->peImportInfo.tables[i].imports[j].branchStubAddr = + peData->baseAddr + currentBranchStubRVA; + peData->peImportInfo.tables[i].branchStubCount++; + peData->peImportInfo.totalBranchStubCount++; + + nullAndFree((void**)&instructionBuffer); + return SUCCESS; + } + } + } } - - peData->peImportInfo.tables[i].imports[j].branchStubAddr = - peData->baseAddr + currentBranchStubRVA; - peData->peImportInfo.tables[i].branchStubCount++; - peData->peImportInfo.totalBranchStubCount++; - - nullAndFree((void**)&instructionBuffer); - return SUCCESS; - } } - } } - } } - } - nullAndFree((void**)&instructionBuffer); - return fseek(pe, -12, SEEK_CUR) != 0 ? ERR_FILE_READ : SUCCESS; + nullAndFree((void**)&instructionBuffer); + return fseek(pe, -12, SEEK_CUR) != 0 ? ERR_FILE_READ : SUCCESS; } int getImports(FILE *pe, struct peData *peData) { - // Make sure the peImportInfo struct is blank, except the IDT RVA - memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t)); + // Make sure the peImportInfo struct is blank, except the IDT RVA + memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t)); - // If there is no IDT, just skip handling imports - if (peData->peImportInfo.idtRVA == 0) - return SUCCESS; + // If there is no IDT, just skip handling imports + if (peData->peImportInfo.idtRVA == 0) + { + return SUCCESS; + } - // Seek to the IDT and read the first entry - uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); - if (idtOffset == 0) - return ERR_INVALID_RVA_OR_OFFSET; - if (fseek(pe, idtOffset, SEEK_SET) != 0) - return ERR_FILE_READ; + // Seek to the IDT and read the first entry + uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); - uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); - if (currentIDT == NULL) - return ERR_OUT_OF_MEM; + if (idtOffset == 0) + { + return ERR_INVALID_RVA_OR_OFFSET; + } - uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons - if (blankIDT == NULL) { - nullAndFree((void**)¤tIDT); - return ERR_OUT_OF_MEM; - } + if (fseek(pe, idtOffset, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); + + if (currentIDT == NULL) + { + return ERR_OUT_OF_MEM; + } + + uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons + + if (blankIDT == NULL) + { + nullAndFree((void**)¤tIDT); + return ERR_OUT_OF_MEM; + } + + if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // While the IDT is not blank, process it + for (uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) + { + // Allocate space for the current table data + peData->peImportInfo.tableCount++; + peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); + + if (peData->peImportInfo.tables == NULL) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_OUT_OF_MEM; + } + + memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank + +#ifdef BIG_ENDIAN_SYSTEM + + // Byteswap the IDT fields + for (uint8_t j = 0; j < 5; j++) + { + currentIDT[j] = __builtin_bswap32(currentIDT[j]); + } + +#endif + + // Retrieve the name pointed to by the table + uint32_t savedOffset = ftell(pe); + uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); + + if (tableNameOffset == 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + + if (fseek(pe, tableNameOffset, SEEK_SET) != 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Allocating is expensive, go 16 bytes at a time to avoid excessive realloc calls + for (uint32_t j = 0;; j += 16) + { + peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); + + if (peData->peImportInfo.tables[i].name == NULL) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_OUT_OF_MEM; + } + + if (fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Check for null terminator + for (uint32_t k = j; k < j + 16; k++) + if (peData->peImportInfo.tables[i].name[k] == '\0') + { + goto doneGettingTableName; + } + } + + doneGettingTableName: + // Seek to the IAT and read the first entry + uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); + + if (iatOffset == 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + + if (fseek(pe, iatOffset, SEEK_SET) != 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + uint32_t currentImport; + + if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // While the import is not blank, process it + for (int j = 0; currentImport != 0; j++) + { +#ifdef BIG_ENDIAN_SYSTEM + // Byteswap the import + currentImport = __builtin_bswap32(currentImport); +#endif + // Allocate space for the current import + peData->peImportInfo.tables[i].importCount++; + peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); + peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later + + // Store the address of the current import entry in iatAddr + uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); + + if (currentImportRVA == 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_INVALID_RVA_OR_OFFSET; + } + + peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; + + // Read the next import + if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + } + + // Add table's import count to total and return to next IDT entry + peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; + + if (fseek(pe, savedOffset, SEEK_SET) != 0) + { + nullAndFree((void**)¤tIDT); + nullAndFree((void**)&blankIDT); + return ERR_FILE_READ; + } + + // Read next IDT + if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) + { + return ERR_FILE_READ; + } + } - if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) { nullAndFree((void**)¤tIDT); nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - // While the IDT is not blank, process it - for (uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) { - // Allocate space for the current table data - peData->peImportInfo.tableCount++; - peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); - if (peData->peImportInfo.tables == NULL) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_OUT_OF_MEM; - } - memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank + // Find the branch stubs + for (uint16_t i = 0; i < peData->sections.count; i++) + { + if (peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) + { + break; // All branch stubs found + } -#ifdef BIG_ENDIAN_SYSTEM - // Byteswap the IDT fields - for (uint8_t j = 0; j < 5; j++) - currentIDT[j] = __builtin_bswap32(currentIDT[j]); -#endif + if (!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) + { + continue; // Skip non-executable section + } - // Retrieve the name pointed to by the table - uint32_t savedOffset = ftell(pe); - uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); - if (tableNameOffset == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_INVALID_RVA_OR_OFFSET; - } - if (fseek(pe, tableNameOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; + // Seek to the start of the current section + if (fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + // While inside section and at least 4 instructions left (15 bytes) + while (ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) + { + int ret = checkForBranchStub(pe, peData); + + if (ret != SUCCESS) + { + return ret; + } + } } - // Allocating is expensive, go 16 bytes at a time to avoid excessive realloc calls - for (uint32_t j = 0;; j += 16) { - peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); - if (peData->peImportInfo.tables[i].name == NULL) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_OUT_OF_MEM; - } - if (fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - - // Check for null terminator - for (uint32_t k = j; k < j + 16; k++) - if (peData->peImportInfo.tables[i].name[k] == '\0') - goto doneGettingTableName; - } - - doneGettingTableName: - // Seek to the IAT and read the first entry - uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); - if (iatOffset == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_INVALID_RVA_OR_OFFSET; - } - if (fseek(pe, iatOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - - uint32_t currentImport; - if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - - // While the import is not blank, process it - for (int j = 0; currentImport != 0; j++) { -#ifdef BIG_ENDIAN_SYSTEM - // Byteswap the import - currentImport = __builtin_bswap32(currentImport); -#endif - // Allocate space for the current import - peData->peImportInfo.tables[i].importCount++; - peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); - peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later - - // Store the address of the current import entry in iatAddr - uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); - if (currentImportRVA == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_INVALID_RVA_OR_OFFSET; - } - peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; - - // Read the next import - if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - } - - // Add table's import count to total and return to next IDT entry - peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; - if (fseek(pe, savedOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - return ERR_FILE_READ; - } - - // Read next IDT - if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) - return ERR_FILE_READ; - } - - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); - - // Find the branch stubs - for (uint16_t i = 0; i < peData->sections.count; i++) { - if (peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) - break; // All branch stubs found - - if (!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) - continue; // Skip non-executable section - - // Seek to the start of the current section - if (fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) - return ERR_FILE_READ; - - // While inside section and at least 4 instructions left (15 bytes) - while (ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) { - int ret = checkForBranchStub(pe, peData); - if (ret != SUCCESS) - return ret; - } - } - - return SUCCESS; + return SUCCESS; } diff --git a/src/main.c b/src/main.c index 4e87d89..bdf8183 100644 --- a/src/main.c +++ b/src/main.c @@ -36,421 +36,504 @@ void dispLibs() { - printf("\nLibraries utilised by SynthXEX:\n\n"); + printf("\nLibraries utilised by SynthXEX:\n\n"); - printf("----- GETOPT_PORT -----\n\n"); - printf("Copyright (c) 2012-2023, Kim Grasman \n"); - printf("All rights reserved.\n\n"); + printf("----- GETOPT_PORT -----\n\n"); + printf("Copyright (c) 2012-2023, Kim Grasman \n"); + printf("All rights reserved.\n\n"); - printf("Redistribution and use in source and binary forms, with or without\n"); - printf("modification, are permitted provided that the following conditions are met:\n"); - printf("* Redistributions of source code must retain the above copyright\n"); - printf(" notice, this list of conditions and the following disclaimer.\n"); - printf("* Redistributions in binary form must reproduce the above copyright\n"); - printf(" notice, this list of conditions and the following disclaimer in the\n"); - printf(" documentation and/or other materials provided with the distribution.\n"); - printf("* Neither the name of Kim Grasman nor the\n"); - printf(" names of contributors may be used to endorse or promote products\n"); - printf(" derived from this software without specific prior written permission.\n\n"); + printf("Redistribution and use in source and binary forms, with or without\n"); + printf("modification, are permitted provided that the following conditions are met:\n"); + printf("* Redistributions of source code must retain the above copyright\n"); + printf(" notice, this list of conditions and the following disclaimer.\n"); + printf("* Redistributions in binary form must reproduce the above copyright\n"); + printf(" notice, this list of conditions and the following disclaimer in the\n"); + printf(" documentation and/or other materials provided with the distribution.\n"); + printf("* Neither the name of Kim Grasman nor the\n"); + printf(" names of contributors may be used to endorse or promote products\n"); + printf(" derived from this software without specific prior written permission.\n\n"); - printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n"); - printf("ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"); - printf("WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"); - printf("DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY\n"); - printf("DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"); - printf("(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"); - printf("LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n"); - printf("ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"); - printf("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"); - printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n"); + printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n"); + printf("ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"); + printf("WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"); + printf("DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY\n"); + printf("DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"); + printf("(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"); + printf("LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n"); + printf("ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"); + printf("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"); + printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n"); - printf("----- GNU NETTLE (SHA-1) -----\n\n"); - printf("Copyright (C) 2001, 2013 Niels Möller\n"); - printf("This file is part of GNU Nettle.\n\n"); + printf("----- GNU NETTLE (SHA-1) -----\n\n"); + printf("Copyright (C) 2001, 2013 Niels Möller\n"); + printf("This file is part of GNU Nettle.\n\n"); - printf("GNU Nettle is free software: you can redistribute it and/or\n"); - printf("modify it under the terms of either:\n\n"); - printf("* the GNU Lesser General Public License as published by the Free\n"); - printf(" Software Foundation; either version 3 of the License, or (at your\n"); - printf(" option) any later version.\n\n"); - printf("or\n\n"); - printf("* the GNU General Public License as published by the Free\n"); - printf(" Software Foundation; either version 2 of the License, or (at your\n"); - printf(" option) any later version.\n\n"); - printf("or both in parallel, as here.\n\n"); + printf("GNU Nettle is free software: you can redistribute it and/or\n"); + printf("modify it under the terms of either:\n\n"); + printf("* the GNU Lesser General Public License as published by the Free\n"); + printf(" Software Foundation; either version 3 of the License, or (at your\n"); + printf(" option) any later version.\n\n"); + printf("or\n\n"); + printf("* the GNU General Public License as published by the Free\n"); + printf(" Software Foundation; either version 2 of the License, or (at your\n"); + printf(" option) any later version.\n\n"); + printf("or both in parallel, as here.\n\n"); - printf("GNU Nettle is distributed in the hope that it will be useful,\n"); - printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"); - printf("General Public License for more details.\n\n"); - printf("You should have received copies of the GNU General Public License and\n"); - printf("the GNU Lesser General Public License along with this program. If\n"); - printf("not, see http://www.gnu.org/licenses/.\n\n"); + printf("GNU Nettle is distributed in the hope that it will be useful,\n"); + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"); + printf("General Public License for more details.\n\n"); + printf("You should have received copies of the GNU General Public License and\n"); + printf("the GNU Lesser General Public License along with this program. If\n"); + printf("not, see http://www.gnu.org/licenses/.\n\n"); } void dispVer() { - printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", VERSION_STRING); - printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", COPYRIGHT); - printf("it under the terms of the GNU Affero General Public License as published by\n"); - printf("the Free Software Foundation, either version 3 of the License, or\n"); - printf("(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\n"); - printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); - printf("GNU Affero General Public License for more details.\n\n"); - printf("You should have received a copy of the GNU Affero General Public License\n"); - printf("along with this program. If not, see .\n\n"); + printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", VERSION_STRING); + printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", COPYRIGHT); + printf("it under the terms of the GNU Affero General Public License as published by\n"); + printf("the Free Software Foundation, either version 3 of the License, or\n"); + printf("(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\n"); + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); + printf("GNU Affero General Public License for more details.\n\n"); + printf("You should have received a copy of the GNU Affero General Public License\n"); + printf("along with this program. If not, see .\n\n"); } void dispHelp(char **argv) { - printf("\nUsage: %s [OPTION] \n\n", argv[0]); - printf("Options:\n"); - printf("-h,\t--help,\t\t\tShow this information\n"); - printf("-v,\t--version,\t\tShow version and licensing information\n"); - printf("-l,\t--libs,\t\t\tShow licensing information of libraries used\n"); - printf("-s,\t--skip-machine-check,\tSkip the PE file machine ID check\n"); - printf("-i,\t--input,\t\tSpecify input PE file path\n"); - printf("-o,\t--output,\t\tSpecify output XEX file path\n"); - printf("-t,\t--type,\t\t\tOverride automatic executable type detection\n\t\t\t\t(options: title, titledll, sysdll, dll)\n\n"); + printf("\nUsage: %s [OPTION] \n\n", argv[0]); + printf("Options:\n"); + printf("-h,\t--help,\t\t\tShow this information\n"); + printf("-v,\t--version,\t\tShow version and licensing information\n"); + printf("-l,\t--libs,\t\t\tShow licensing information of libraries used\n"); + printf("-s,\t--skip-machine-check,\tSkip the PE file machine ID check\n"); + printf("-i,\t--input,\t\tSpecify input PE file path\n"); + printf("-o,\t--output,\t\tSpecify output XEX file path\n"); + printf("-t,\t--type,\t\t\tOverride automatic executable type detection\n\t\t\t\t(options: title, titledll, sysdll, dll)\n\n"); } void handleError(int ret) { - switch (ret) { - case ERR_UNKNOWN_DATA_REQUEST: - printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); - break; - case ERR_FILE_READ: - printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); - break; - case ERR_OUT_OF_MEM: - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - break; - case ERR_MISSING_SECTION_FLAG: - printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); - break; - case ERR_UNSUPPORTED_STRUCTURE: - printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); - break; - case ERR_INVALID_RVA_OR_OFFSET: - printf("%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); - break; - case ERR_INVALID_IMPORT_NAME: - printf("%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); - break; - case ERR_DATA_OVERFLOW: - printf("%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); - break; - default: - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); - break; - } + switch (ret) + { + case ERR_UNKNOWN_DATA_REQUEST: + printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); + break; + + case ERR_FILE_READ: + printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + break; + + case ERR_OUT_OF_MEM: + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + break; + + case ERR_MISSING_SECTION_FLAG: + printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); + break; + + case ERR_UNSUPPORTED_STRUCTURE: + printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + break; + + case ERR_INVALID_RVA_OR_OFFSET: + printf("%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); + break; + + case ERR_INVALID_IMPORT_NAME: + printf("%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); + break; + + case ERR_DATA_OVERFLOW: + printf("%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); + break; + + default: + printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + break; + } } int main(int argc, char **argv) { - static struct option longOptions[] = { - { "help", no_argument, 0, 0 }, - { "version", no_argument, 0, 0 }, - { "libs", no_argument, 0, 0 }, - { "skip-machine-check", no_argument, 0, 0 }, - { "input", required_argument, 0, 0 }, - { "output", required_argument, 0, 0 }, - { "type", required_argument, 0, 0 }, - { 0, 0, 0, 0 } - }; + static struct option longOptions[] = + { + { "help", no_argument, 0, 0 }, + { "version", no_argument, 0, 0 }, + { "libs", no_argument, 0, 0 }, + { "skip-machine-check", no_argument, 0, 0 }, + { "input", required_argument, 0, 0 }, + { "output", required_argument, 0, 0 }, + { "type", required_argument, 0, 0 }, + { 0, 0, 0, 0 } + }; - struct offsets *offsets = calloc(1, sizeof(struct offsets)); - struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader)); - struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader)); - struct peData *peData = calloc(1, sizeof(struct peData)); - struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); - struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); + struct offsets *offsets = calloc(1, sizeof(struct offsets)); + struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader)); + struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader)); + struct peData *peData = calloc(1, sizeof(struct peData)); + struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); + struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); - if (offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || - peData == NULL || optHeaderEntries == NULL || optHeaders == NULL) - { - printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return -1; - } - - int optIndex = 0; - int option = 0; - - bool gotInput = false; - bool gotOutput = false; - bool skipMachineCheck = false; - - char *pePath = NULL; - char *xexfilePath = NULL; - - while ((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) { - if (option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) { - dispHelp(argv); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } else if (option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) { - dispVer(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } else if (option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) { - dispLibs(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } else if (option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) { - printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); - skipMachineCheck = true; - } else if (option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) { - gotInput = true; - pePath = malloc(strlen(optarg) + 1); - if (pePath == NULL) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); + if (offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || + peData == NULL || optHeaderEntries == NULL || optHeaders == NULL) + { + printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; - } - strcpy(pePath, optarg); - } else if (option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) { - gotOutput = true; - xexfilePath = malloc(strlen(optarg) + 1); - if (xexfilePath == NULL) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&pePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return -1; - } - strcpy(xexfilePath, optarg); - } else if (option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) { - if (strcmp(optarg, "title") == 0) { - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; - } else if (strcmp(optarg, "titledll") == 0) { - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; - } else if (strcmp(optarg, "sysdll") == 0) { - xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; - } else if (strcmp(optarg, "dll") == 0) { - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; - } else { - printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", - PRINT_STEM, optarg); - - nullAndFree((void**)&pePath); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return -1; - } } - } - printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT); - printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]); + int optIndex = 0; + int option = 0; - if (!gotInput) { - if (gotOutput) - nullAndFree((void**)&xexfilePath); - printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); - return -1; - } else if (!gotOutput) { - if (gotInput) - nullAndFree((void**)&pePath); - printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); - return -1; - } + bool gotInput = false; + bool gotOutput = false; + bool skipMachineCheck = false; + + char *pePath = NULL; + char *xexfilePath = NULL; + + while ((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) + { + if (option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) + { + dispHelp(argv); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } + else if (option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) + { + dispVer(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } + else if (option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) + { + dispLibs(); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return SUCCESS; + } + else if (option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) + { + printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); + skipMachineCheck = true; + } + else if (option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) + { + gotInput = true; + pePath = malloc(strlen(optarg) + 1); + + if (pePath == NULL) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + + strcpy(pePath, optarg); + } + else if (option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) + { + gotOutput = true; + xexfilePath = malloc(strlen(optarg) + 1); + + if (xexfilePath == NULL) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&pePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + + strcpy(xexfilePath, optarg); + } + else if (option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) + { + if (strcmp(optarg, "title") == 0) + { + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; + } + else if (strcmp(optarg, "titledll") == 0) + { + xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; + } + else if (strcmp(optarg, "sysdll") == 0) + { + xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; + } + else if (strcmp(optarg, "dll") == 0) + { + xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; + } + else + { + printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", + PRINT_STEM, optarg); + + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + } + } + + printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT); + printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]); + + if (!gotInput) + { + if (gotOutput) + { + nullAndFree((void**)&xexfilePath); + } + + printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); + return -1; + } + else if (!gotOutput) + { + if (gotInput) + { + nullAndFree((void**)&pePath); + } + + printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); + return -1; + } + + FILE *pe = fopen(pePath, "rb"); + + if (pe == NULL) + { + printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); + nullAndFree((void**)&pePath); + nullAndFree((void**)&xexfilePath); + return -1; + } - FILE *pe = fopen(pePath, "rb"); - if (pe == NULL) { - printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); nullAndFree((void**)&pePath); + + FILE *xex = fopen(xexfilePath, "wb+"); + + if (xex == NULL) + { + printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); + fclose(pe); + nullAndFree((void**)&xexfilePath); + return -1; + } + + // Keep xexfilePath for later use + // Do NOT free xexfilePath here yet + + int ret = 0; + + printf("%s Validating PE file...\n", PRINT_STEM); + + if (!validatePE(pe, skipMachineCheck)) + { + printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } + + printf("%s PE valid!\n", PRINT_STEM); + + printf("%s Retrieving header data from PE...\n", PRINT_STEM); + ret = getHdrData(pe, peData, 0); + + if (ret != SUCCESS) + { + handleError(ret); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } + + printf("%s Got header data from PE!\n", PRINT_STEM); + + printf("%s Retrieving import data from PE...\n", PRINT_STEM); + ret = getImports(pe, peData); + + if (ret != SUCCESS) + { + handleError(ret); + nullAndFree((void**)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } + + printf("%s Got import data from PE!\n", PRINT_STEM); + + printf("%s Creating basefile from PE...\n", PRINT_STEM); + char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char)); + + if (!basefilePath) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + nullAndFree((void**)&xexfilePath); + fclose(pe); + fclose(xex); + return -1; + } + + strcpy(basefilePath, xexfilePath); + strcat(basefilePath, ".basefile"); + + FILE *basefile = fopen(basefilePath, "wb+"); nullAndFree((void**)&xexfilePath); - return -1; - } - nullAndFree((void**)&pePath); + nullAndFree((void**)&basefilePath); - FILE *xex = fopen(xexfilePath, "wb+"); - if (xex == NULL) { - printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); + if (!basefile) + { + printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(pe); + fclose(xex); + return -1; + } + + // Map the PE into the basefile (RVAs become offsets) + ret = mapPEToBasefile(pe, basefile, peData); fclose(pe); - nullAndFree((void**)&xexfilePath); - return -1; - } - // Keep xexfilePath for later use - // Do NOT free xexfilePath here yet + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } - int ret = 0; + printf("%s Created basefile!\n", PRINT_STEM); - printf("%s Validating PE file...\n", PRINT_STEM); - if (!validatePE(pe, skipMachineCheck)) { - printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - fclose(pe); + // Setting final XEX data structs + printf("%s Building security header...\n", PRINT_STEM); + ret = setSecInfoHeader(secInfoHeader, peData); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + printf("%s Setting page descriptors...\n", PRINT_STEM); + ret = setPageDescriptors(basefile, peData, secInfoHeader); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + // Done with this now + freeSectionsStruct(&(peData->sections)); + + printf("%s Building optional headers...\n", PRINT_STEM); + ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + printf("%s Building XEX header...\n", PRINT_STEM); + ret = setXEXHeader(xexHeader, optHeaderEntries, peData); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + // Done with this now + freePeImportInfoStruct(&(peData->peImportInfo)); + + // Setting data positions + printf("%s Aligning data...\n", PRINT_STEM); + ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + // We're done with this now + freePeDataStruct(&peData); + + // Write out all of the XEX data to file + printf("%s Writing XEX...\n", PRINT_STEM); + ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + // Final pass (sets & writes header hash) + printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); + ret = setHeaderSha1(xex); + + if (ret != SUCCESS) + { + handleError(ret); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + fclose(basefile); + fclose(xex); + return -1; + } + + printf("%s Header SHA1 written!\n", PRINT_STEM); + + // Free files fclose(xex); - return -1; - } - printf("%s PE valid!\n", PRINT_STEM); - - printf("%s Retrieving header data from PE...\n", PRINT_STEM); - ret = getHdrData(pe, peData, 0); - if (ret != SUCCESS) { - handleError(ret); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - printf("%s Got header data from PE!\n", PRINT_STEM); - - printf("%s Retrieving import data from PE...\n", PRINT_STEM); - ret = getImports(pe, peData); - if (ret != SUCCESS) { - handleError(ret); - nullAndFree((void**)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - printf("%s Got import data from PE!\n", PRINT_STEM); - - printf("%s Creating basefile from PE...\n", PRINT_STEM); - char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char)); - if (!basefilePath) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - nullAndFree((void**)&xexfilePath); - fclose(pe); - fclose(xex); - return -1; - } - strcpy(basefilePath, xexfilePath); - strcat(basefilePath, ".basefile"); - - FILE *basefile = fopen(basefilePath, "wb+"); - nullAndFree((void**)&xexfilePath); - nullAndFree((void**)&basefilePath); - - if (!basefile) { - printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(pe); - fclose(xex); - return -1; - } - - // Map the PE into the basefile (RVAs become offsets) - ret = mapPEToBasefile(pe, basefile, peData); - fclose(pe); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(basefile); - fclose(xex); - return -1; - } - printf("%s Created basefile!\n", PRINT_STEM); - - // Setting final XEX data structs - printf("%s Building security header...\n", PRINT_STEM); - ret = setSecInfoHeader(secInfoHeader, peData); - if (ret != SUCCESS) { - handleError(ret); + // Free structs freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - printf("%s Setting page descriptors...\n", PRINT_STEM); - ret = setPageDescriptors(basefile, peData, secInfoHeader); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - // Done with this now - freeSectionsStruct(&(peData->sections)); - - printf("%s Building optional headers...\n", PRINT_STEM); - ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - printf("%s Building XEX header...\n", PRINT_STEM); - ret = setXEXHeader(xexHeader, optHeaderEntries, peData); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - // Done with this now - freePeImportInfoStruct(&(peData->peImportInfo)); - - // Setting data positions - printf("%s Aligning data...\n", PRINT_STEM); - ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - // We're done with this now - freePeDataStruct(&peData); - - // Write out all of the XEX data to file - printf("%s Writing XEX...\n", PRINT_STEM); - ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - // Final pass (sets & writes header hash) - printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); - ret = setHeaderSha1(xex); - if (ret != SUCCESS) { - handleError(ret); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - fclose(basefile); - fclose(xex); - return -1; - } - - printf("%s Header SHA1 written!\n", PRINT_STEM); - - // Free files - fclose(xex); - fclose(basefile); - // Free structs - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - - printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); - return SUCCESS; + printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); + return SUCCESS; } diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index b2b23c5..c1b42c8 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -20,81 +20,123 @@ struct sectionInfo { - uint32_t virtualSize; - uint32_t rva; - uint32_t rawSize; - uint32_t offset; + uint32_t virtualSize; + uint32_t rva; + uint32_t rawSize; + uint32_t offset; }; // Maps the PE file into the basefile (RVAs become offsets) int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { - struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo)); - if (!sectionInfo) - return ERR_OUT_OF_MEM; + struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo)); - if (fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) - return ERR_FILE_READ; + if (!sectionInfo) + { + return ERR_OUT_OF_MEM; + } - for (uint16_t i = 0; i < peData->numberOfSections; i++) { - sectionInfo[i].virtualSize = get32BitFromPE(pe); - sectionInfo[i].rva = get32BitFromPE(pe); - sectionInfo[i].rawSize = get32BitFromPE(pe); - sectionInfo[i].offset = get32BitFromPE(pe); + if (fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - if (fseek(pe, 0x18, SEEK_CUR) != 0) - return ERR_FILE_READ; - } + for (uint16_t i = 0; i < peData->numberOfSections; i++) + { + sectionInfo[i].virtualSize = get32BitFromPE(pe); + sectionInfo[i].rva = get32BitFromPE(pe); + sectionInfo[i].rawSize = get32BitFromPE(pe); + sectionInfo[i].offset = get32BitFromPE(pe); - if (fseek(pe, 0, SEEK_SET) != 0) - return ERR_FILE_READ; + if (fseek(pe, 0x18, SEEK_CUR) != 0) + { + return ERR_FILE_READ; + } + } - uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize); - if (!buffer) - return ERR_OUT_OF_MEM; + if (fseek(pe, 0, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - size_t totalHeader = peData->headerSize + peData->sectionTableSize; + uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize); - if (fread(buffer, 1, totalHeader, pe) != totalHeader) - return ERR_FILE_READ; - if (fwrite(buffer, 1, totalHeader, basefile) != totalHeader) - return ERR_FILE_READ; - - for (uint16_t i = 0; i < peData->numberOfSections; i++) { - buffer = realloc(buffer, sectionInfo[i].rawSize); if (!buffer) - return ERR_OUT_OF_MEM; + { + return ERR_OUT_OF_MEM; + } - if (fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0) - return ERR_FILE_READ; - if (fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize) - return ERR_FILE_READ; + size_t totalHeader = peData->headerSize + peData->sectionTableSize; - if (fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0) - return ERR_FILE_READ; - if (fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize) - return ERR_FILE_READ; - } + if (fread(buffer, 1, totalHeader, pe) != totalHeader) + { + return ERR_FILE_READ; + } - uint32_t currentOffset = ftell(basefile); - uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1; + if (fwrite(buffer, 1, totalHeader, basefile) != totalHeader) + { + return ERR_FILE_READ; + } - if (nextAligned != currentOffset) { - buffer = realloc(buffer, 1); - if (!buffer) - return ERR_OUT_OF_MEM; - buffer[0] = 0; + for (uint16_t i = 0; i < peData->numberOfSections; i++) + { + buffer = realloc(buffer, sectionInfo[i].rawSize); - if (fseek(basefile, nextAligned, SEEK_SET) != 0) - return ERR_FILE_READ; - if (fwrite(buffer, 1, 1, basefile) != 1) - return ERR_FILE_READ; - } + if (!buffer) + { + return ERR_OUT_OF_MEM; + } - peData->size = ftell(basefile); + if (fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - nullAndFree((void**)&buffer); - nullAndFree((void**)§ionInfo); + if (fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize) + { + return ERR_FILE_READ; + } - return SUCCESS; + if (fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + if (fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize) + { + return ERR_FILE_READ; + } + } + + uint32_t currentOffset = ftell(basefile); + uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1; + + if (nextAligned != currentOffset) + { + buffer = realloc(buffer, 1); + + if (!buffer) + { + return ERR_OUT_OF_MEM; + } + + buffer[0] = 0; + + if (fseek(basefile, nextAligned, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + if (fwrite(buffer, 1, 1, basefile) != 1) + { + return ERR_FILE_READ; + } + } + + peData->size = ftell(basefile); + + nullAndFree((void**)&buffer); + nullAndFree((void**)§ionInfo); + + return SUCCESS; } diff --git a/src/placer/placer.c b/src/placer/placer.c index bae4eba..00c965f 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -21,80 +21,93 @@ // Internal struct, defined here so it cannot be accessed outwith this file struct importLibIdcs { - uint32_t header; - uint32_t entry; + uint32_t header; + uint32_t entry; }; int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders, uint32_t *currentOffset, struct importLibIdcs *importLibIdcs) { - // Calloc because 0 values will be used to determine if a header is not present. - offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); - if (offsets->optHeaders == NULL) - return ERR_OUT_OF_MEM; + // Calloc because 0 values will be used to determine if a header is not present. + offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); - // Separate header iterator, i.e. one with it's data outwith the entries - uint32_t sepHeader = 0; - - for (uint32_t i = 0; i < optHeaderEntries->count; i++) { - *currentOffset = getNextAligned(*currentOffset, 0x8); - switch(optHeaderEntries->optHeaderEntry[i].id) { - case XEX_OPT_ID_BASEFILE_FORMAT: - optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; - offsets->optHeaders[sepHeader] = *currentOffset; - *currentOffset += sizeof(struct basefileFormat); - sepHeader++; - break; - case XEX_OPT_ID_IMPORT_LIBS: - importLibIdcs->header = sepHeader; - importLibIdcs->entry = i; - sepHeader++; - break; - case XEX_OPT_ID_TLS_INFO: - optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; - offsets->optHeaders[sepHeader] = *currentOffset; - *currentOffset += sizeof(struct tlsInfo); - sepHeader++; - break; + if (offsets->optHeaders == NULL) + { + return ERR_OUT_OF_MEM; } - } - return SUCCESS; + // Separate header iterator, i.e. one with it's data outwith the entries + uint32_t sepHeader = 0; + + for (uint32_t i = 0; i < optHeaderEntries->count; i++) + { + *currentOffset = getNextAligned(*currentOffset, 0x8); + + switch(optHeaderEntries->optHeaderEntry[i].id) + { + case XEX_OPT_ID_BASEFILE_FORMAT: + optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; + offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += sizeof(struct basefileFormat); + sepHeader++; + break; + + case XEX_OPT_ID_IMPORT_LIBS: + importLibIdcs->header = sepHeader; + importLibIdcs->entry = i; + sepHeader++; + break; + + case XEX_OPT_ID_TLS_INFO: + optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; + offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += sizeof(struct tlsInfo); + sepHeader++; + break; + } + } + + return SUCCESS; } int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct secInfoHeader *secInfoHeader, struct optHeaders *optHeaders) { - // XEX Header - uint32_t currentOffset = 0x0; - offsets->xexHeader = currentOffset; - currentOffset += sizeof(struct xexHeader); + // XEX Header + uint32_t currentOffset = 0x0; + offsets->xexHeader = currentOffset; + currentOffset += sizeof(struct xexHeader); - // Optional header entries (no alignment, they immediately follow XEX header) - offsets->optHeaderEntries = currentOffset; - currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry); + // Optional header entries (no alignment, they immediately follow XEX header) + offsets->optHeaderEntries = currentOffset; + currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry); - // Security header - currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries - offsets->secInfoHeader = currentOffset; - xexHeader->secInfoOffset = currentOffset; - currentOffset += (sizeof(struct secInfoHeader) - sizeof(void*)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); + // Security header + currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries + offsets->secInfoHeader = currentOffset; + xexHeader->secInfoOffset = currentOffset; + currentOffset += (sizeof(struct secInfoHeader) - sizeof(void*)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); - // Optional headers (minus imports) - struct importLibIdcs importLibIdcs; - int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs); - if (ret != SUCCESS) - return ret; + // Optional headers (minus imports) + struct importLibIdcs importLibIdcs; + int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs); - currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports + if (ret != SUCCESS) + { + return ret; + } - // PE basefile - currentOffset = getNextAligned(currentOffset, 0x1000); // 4KiB alignment for basefile - offsets->basefile = currentOffset; - xexHeader->peOffset = currentOffset; + currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports - // Imports, the end of this header is aligned to the start of the basefile, so they are a special case - if (optHeaders->importLibraries.tableCount > 0) { - offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; - optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; - } - return SUCCESS; + // PE basefile + currentOffset = getNextAligned(currentOffset, 0x1000); // 4KiB alignment for basefile + offsets->basefile = currentOffset; + xexHeader->peOffset = currentOffset; + + // Imports, the end of this header is aligned to the start of the basefile, so they are a special case + if (optHeaders->importLibraries.tableCount > 0) + { + offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; + optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; + } + + return SUCCESS; } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index c399ed4..e7c3941 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -20,288 +20,381 @@ void setBasefileFormat(struct basefileFormat *basefileFormat, struct secInfoHeader *secInfoHeader) { - basefileFormat->size = (1 * 8) + 8; // (Block count * size of raw data descriptor) + size of data descriptor - basefileFormat->encType = 0x0; // No encryption - basefileFormat->compType = 0x1; // No compression - basefileFormat->dataSize = secInfoHeader->peSize; - basefileFormat->zeroSize = 0x0; // We aren't going to be removing any zeroes. TODO: implement this, it can make files much smaller + basefileFormat->size = (1 * 8) + 8; // (Block count * size of raw data descriptor) + size of data descriptor + basefileFormat->encType = 0x0; // No encryption + basefileFormat->compType = 0x1; // No compression + basefileFormat->dataSize = secInfoHeader->peSize; + basefileFormat->zeroSize = 0x0; // We aren't going to be removing any zeroes. TODO: implement this, it can make files much smaller } // STUB. TLS info not supported. void setTLSInfo(struct tlsInfo *tlsInfo) { - tlsInfo->slotCount = 0x40; - tlsInfo->rawDataAddr = 0x0; - tlsInfo->dataSize = 0x0; - tlsInfo->rawDataSize = 0x0; + tlsInfo->slotCount = 0x40; + tlsInfo->rawDataAddr = 0x0; + tlsInfo->dataSize = 0x0; + tlsInfo->rawDataSize = 0x0; } int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportInfo *peImportInfo, struct secInfoHeader *secInfoHeader) { - importLibraries->tableCount = peImportInfo->tableCount; - secInfoHeader->importTableCount = peImportInfo->tableCount; + importLibraries->tableCount = peImportInfo->tableCount; + secInfoHeader->importTableCount = peImportInfo->tableCount; - importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); - if (!importLibraries->importTables) - return ERR_OUT_OF_MEM; + importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); - if (peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) { - fprintf(stderr, "ERROR: Invalid tableCount = %d\n", peImportInfo->tableCount); - return ERR_OUT_OF_MEM; - } - - struct importTable *importTables = importLibraries->importTables; - importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); - - int ret = ERR_INVALID_IMPORT_NAME; - - // Allocate name list - char **names = calloc(importLibraries->tableCount, sizeof(char *)); - if (!names) - goto cleanup_tables; - - for (int64_t i = importLibraries->tableCount - 1; i >= 0; i--) { - importTables[i].tableIndex = i; - - const uint8_t majorVer = 2; - const uint8_t minorVer = 0; - char *targetBuildVerStr = NULL; - char *targetHotfixVerStr = NULL; - char *minimumBuildVerStr = NULL; - char *minimumHotfixVerStr = NULL; - uint16_t buildVer = 0; - uint8_t hotfixVer = 0; - char *strtoulRet = NULL; - - uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); - names[i] = strtok(peImportInfo->tables[i].name, "@"); - if (!peImportInfo->tables[i].name) { - fprintf(stderr, "ERROR: tables[%d].name is NULL\n", i); - goto cleanup_names_invalid; + if (!importLibraries->importTables) + { + return ERR_OUT_OF_MEM; } - targetBuildVerStr = strtok(NULL, "."); - if (!targetBuildVerStr) - goto cleanup_names_invalid; - - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) - goto cleanup_names_invalid; - - buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == targetBuildVerStr) - goto cleanup_names_invalid; - - targetHotfixVerStr = strtok(NULL, "+"); - if (!targetHotfixVerStr) - goto cleanup_names_invalid; - - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) - goto cleanup_names_invalid; - - hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) - goto cleanup_names_invalid; - - importTables[i].targetVer = - ((majorVer & 0xF) << 28) | - ((minorVer & 0xF) << 24) | - (buildVer << 8) | - hotfixVer; - - minimumBuildVerStr = strtok(NULL, "."); - if (!minimumBuildVerStr) - goto cleanup_names_invalid; - - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) - + 1 + strlen(minimumBuildVerStr) == oldNameLen) - goto cleanup_names_invalid; - - buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) - goto cleanup_names_invalid; - - minimumHotfixVerStr = strtok(NULL, "\0"); - if (!minimumHotfixVerStr) - goto cleanup_names_invalid; - - hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) - goto cleanup_names_invalid; - - importTables[i].minimumVer = - ((majorVer & 0xF) << 28) | - ((minorVer & 0xF) << 24); - if (strcmp(names[i], "xboxkrnl.exe") == 0) { - importTables[i].unknown = 0x45DC17E0; - } else if (strcmp(names[i], "xam.xex") == 0) { - importTables[i].unknown = 0xFCA15C76; - } else if (strcmp(names[i], "xbdm.xex") == 0) { - importTables[i].unknown = 0xECEB8109; - } else { - goto cleanup_names_invalid; + if (peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) + { + fprintf(stderr, "ERROR: Invalid tableCount = %d\n", peImportInfo->tableCount); + return ERR_OUT_OF_MEM; } - importTables[i].addressCount = - (peImportInfo->tables[i].branchStubCount * 2) + - (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + struct importTable *importTables = importLibraries->importTables; - importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); - if (!importTables[i].addresses) - goto cleanup_names_invalid; + importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); - uint32_t *addresses = importTables[i].addresses; - uint16_t currentAddr = 0; + int ret = ERR_INVALID_IMPORT_NAME; - for (uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { - if (currentAddr >= importTables[i].addressCount) - goto cleanup_names_invalid; + // Allocate name list + char **names = calloc(importLibraries->tableCount, sizeof(char *)); - addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr; - - if (peImportInfo->tables[i].imports[j].branchStubAddr != 0) { - if (currentAddr >= importTables[i].addressCount) - goto cleanup_names_invalid; - addresses[currentAddr++] = peImportInfo->tables[i].imports[j].branchStubAddr; - } + if (!names) + { + goto cleanup_tables; } - importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); - importLibraries->size += importTables[i].size; + for (int64_t i = importLibraries->tableCount - 1; i >= 0; i--) + { + importTables[i].tableIndex = i; - struct sha1_ctx shaContext; - memset(&shaContext, 0, sizeof(shaContext)); - sha1_init(&shaContext); + const uint8_t majorVer = 2; + const uint8_t minorVer = 0; + char *targetBuildVerStr = NULL; + char *targetHotfixVerStr = NULL; + char *minimumBuildVerStr = NULL; + char *minimumHotfixVerStr = NULL; + uint16_t buildVer = 0; + uint8_t hotfixVer = 0; + char *strtoulRet = NULL; - uint16_t addressCount = importTables[i].addressCount; + uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); + names[i] = strtok(peImportInfo->tables[i].name, "@"); + + if (!peImportInfo->tables[i].name) + { + fprintf(stderr, "ERROR: tables[%d].name is NULL\n", i); + goto cleanup_names_invalid; + } + + targetBuildVerStr = strtok(NULL, "."); + + if (!targetBuildVerStr) + { + goto cleanup_names_invalid; + } + + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) + { + goto cleanup_names_invalid; + } + + buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); + + if (*strtoulRet != 0 || strtoulRet == targetBuildVerStr) + { + goto cleanup_names_invalid; + } + + targetHotfixVerStr = strtok(NULL, "+"); + + if (!targetHotfixVerStr) + { + goto cleanup_names_invalid; + } + + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) + { + goto cleanup_names_invalid; + } + + hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); + + if (*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) + { + goto cleanup_names_invalid; + } + + importTables[i].targetVer = + ((majorVer & 0xF) << 28) | + ((minorVer & 0xF) << 24) | + (buildVer << 8) | + hotfixVer; + + minimumBuildVerStr = strtok(NULL, "."); + + if (!minimumBuildVerStr) + { + goto cleanup_names_invalid; + } + + if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + + 1 + strlen(minimumBuildVerStr) == oldNameLen) + { + goto cleanup_names_invalid; + } + + buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); + + if (*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) + { + goto cleanup_names_invalid; + } + + minimumHotfixVerStr = strtok(NULL, "\0"); + + if (!minimumHotfixVerStr) + { + goto cleanup_names_invalid; + } + + hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); + + if (*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) + { + goto cleanup_names_invalid; + } + + importTables[i].minimumVer = + ((majorVer & 0xF) << 28) | + ((minorVer & 0xF) << 24); + + if (strcmp(names[i], "xboxkrnl.exe") == 0) + { + importTables[i].unknown = 0x45DC17E0; + } + else if (strcmp(names[i], "xam.xex") == 0) + { + importTables[i].unknown = 0xFCA15C76; + } + else if (strcmp(names[i], "xbdm.xex") == 0) + { + importTables[i].unknown = 0xECEB8109; + } + else + { + goto cleanup_names_invalid; + } + + importTables[i].addressCount = + (peImportInfo->tables[i].branchStubCount * 2) + + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + + importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); + + if (!importTables[i].addresses) + { + goto cleanup_names_invalid; + } + + uint32_t *addresses = importTables[i].addresses; + uint16_t currentAddr = 0; + + for (uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) + { + if (currentAddr >= importTables[i].addressCount) + { + goto cleanup_names_invalid; + } + + addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr; + + if (peImportInfo->tables[i].imports[j].branchStubAddr != 0) + { + if (currentAddr >= importTables[i].addressCount) + { + goto cleanup_names_invalid; + } + + addresses[currentAddr++] = peImportInfo->tables[i].imports[j].branchStubAddr; + } + } + + importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); + importLibraries->size += importTables[i].size; + + struct sha1_ctx shaContext; + memset(&shaContext, 0, sizeof(shaContext)); + sha1_init(&shaContext); + + uint16_t addressCount = importTables[i].addressCount; #ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - for (uint16_t j = 0; j < addressCount; j++) - addresses[j] = __builtin_bswap32(addresses[j]); + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + + for (uint16_t j = 0; j < addressCount; j++) + { + addresses[j] = __builtin_bswap32(addresses[j]); + } + #endif - sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&importTables[i] + sizeof(uint32_t)); - sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&importTables[i] + sizeof(uint32_t)); + sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); #ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - for (uint16_t j = 0; j < addressCount; j++) - addresses[j] = __builtin_bswap32(addresses[j]); + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + + for (uint16_t j = 0; j < addressCount; j++) + { + addresses[j] = __builtin_bswap32(addresses[j]); + } + #endif - sha1_digest(&shaContext, 0x14, i != 0 ? importTables[i - 1].sha1 : secInfoHeader->importTableSha1); - } + sha1_digest(&shaContext, 0x14, i != 0 ? importTables[i - 1].sha1 : secInfoHeader->importTableSha1); + } - // Allocate offset table - uint32_t *nameOffsets = calloc(importLibraries->tableCount, sizeof(uint32_t)); - if (!nameOffsets) - goto cleanup_names; + // Allocate offset table + uint32_t *nameOffsets = calloc(importLibraries->tableCount, sizeof(uint32_t)); - for (uint32_t i = 0; i < importLibraries->tableCount; i++) { - nameOffsets[i] = importLibraries->nameTableSize; - importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); - } + if (!nameOffsets) + { + goto cleanup_names; + } - importLibraries->size += importLibraries->nameTableSize; - importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); - if (!importLibraries->nameTable) - goto cleanup_offsets; + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + { + nameOffsets[i] = importLibraries->nameTableSize; + importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); + } - char *nameTable = importLibraries->nameTable; - for (uint32_t i = 0; i < importLibraries->tableCount; i++) - strcpy(&(nameTable[nameOffsets[i]]), names[i]); + importLibraries->size += importLibraries->nameTableSize; + importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); - nullAndFree((void**)&nameOffsets); - nullAndFree((void**)&names); - return SUCCESS; + if (!importLibraries->nameTable) + { + goto cleanup_offsets; + } + + char *nameTable = importLibraries->nameTable; + + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + { + strcpy(&(nameTable[nameOffsets[i]]), names[i]); + } + + nullAndFree((void**)&nameOffsets); + nullAndFree((void**)&names); + return SUCCESS; cleanup_offsets: - nullAndFree((void**)&nameOffsets); + nullAndFree((void**)&nameOffsets); cleanup_names: - for (uint32_t i = 0; i < importLibraries->tableCount; i++) - nullAndFree((void**)&(importTables[i].addresses)); + + for (uint32_t i = 0; i < importLibraries->tableCount; i++) + { + nullAndFree((void**)&(importTables[i].addresses)); + } + cleanup_names_invalid: - nullAndFree((void**)&names); + nullAndFree((void**)&names); cleanup_tables: - nullAndFree((void**)&(importLibraries->importTables)); - importLibraries->importTables = NULL; - return ret; + nullAndFree((void**)&(importLibraries->importTables)); + importLibraries->importTables = NULL; + return ret; } // STUB. TODO: Dynamically select, and/or allow user to select, flags void setSysFlags(uint32_t *flags) { - if (flags == NULL) - return; + if (flags == NULL) + { + return; + } - *flags = XEX_SYS_GAMEPAD_DISCONNECT | - XEX_SYS_INSECURE_SOCKETS | - XEX_SYS_XAM_HOOKS | - XEX_SYS_BACKGROUND_DL | - XEX_SYS_ALLOW_CONTROL_SWAP; + *flags = XEX_SYS_GAMEPAD_DISCONNECT | + XEX_SYS_INSECURE_SOCKETS | + XEX_SYS_XAM_HOOKS | + XEX_SYS_BACKGROUND_DL | + XEX_SYS_ALLOW_CONTROL_SWAP; } int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders) { - bool importsPresent = (peData->peImportInfo.totalImportCount > 0) ? true : false; + bool importsPresent = (peData->peImportInfo.totalImportCount > 0) ? true : false; - optHeaderEntries->count = 4; + optHeaderEntries->count = 4; - if (importsPresent) - optHeaderEntries->count++; + if (importsPresent) + { + optHeaderEntries->count++; + } - optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry)); - if (optHeaderEntries->optHeaderEntry == NULL) - return ERR_OUT_OF_MEM; + optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry)); - uint32_t currentHeader = 0; + if (optHeaderEntries->optHeaderEntry == NULL) + { + return ERR_OUT_OF_MEM; + } - // NOTE: Make sure that these headers are handled IN ORDER OF ID. The loader will reject the XEX if they are not. + uint32_t currentHeader = 0; - // Basefile format (0x003FF) - setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader); - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_BASEFILE_FORMAT; - currentHeader++; + // NOTE: Make sure that these headers are handled IN ORDER OF ID. The loader will reject the XEX if they are not. - // Entrypoint (0x10100) - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_ENTRYPOINT; - optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint; - currentHeader++; - - // Import libraries (0x103FF) - if (importsPresent) { - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; - int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); - if (ret != SUCCESS) - return ret; + // Basefile format (0x003FF) + setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader); + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_BASEFILE_FORMAT; currentHeader++; - } - // TLS info (0x20104) - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_TLS_INFO; - setTLSInfo(&(optHeaders->tlsInfo)); - currentHeader++; + // Entrypoint (0x10100) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_ENTRYPOINT; + optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint; + currentHeader++; - // System flags (0x30000) - optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_SYS_FLAGS; + // Import libraries (0x103FF) + if (importsPresent) + { + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; + int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); - // We're using the name of the first element of the struct for clarity, - // it can never be an unaligned access, so ignore that warning. - // Also works for Clang. - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Waddress-of-packed-member" - setSysFlags(&(optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset)); - #pragma GCC diagnostic pop + if (ret != SUCCESS) + { + return ret; + } - //currentHeader++; + currentHeader++; + } - return SUCCESS; + // TLS info (0x20104) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_TLS_INFO; + setTLSInfo(&(optHeaders->tlsInfo)); + currentHeader++; + + // System flags (0x30000) + optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_SYS_FLAGS; + + // We're using the name of the first element of the struct for clarity, + // it can never be an unaligned access, so ignore that warning. + // Also works for Clang. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + setSysFlags(&(optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset)); +#pragma GCC diagnostic pop + + //currentHeader++; + + return SUCCESS; } diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 3e771bd..cc1afcf 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -20,64 +20,79 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint32_t page) { - uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; - uint32_t currentOffset = page * pageSize; + uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; + uint32_t currentOffset = page * pageSize; - for (int32_t i = peData->sections.count - 1; i >= 0; i--) - if (currentOffset >= peData->sections.section[i].rva) - return peData->sections.section[i].permFlag; + for (int32_t i = peData->sections.count - 1; i >= 0; i--) + if (currentOffset >= peData->sections.section[i].rva) + { + return peData->sections.section[i].permFlag; + } - return XEX_SECTION_RODATA | 0b10000; // We're in the PE header, so RODATA + return XEX_SECTION_RODATA | 0b10000; // We're in the PE header, so RODATA } int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *secInfoHeader) { - uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; + uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; - secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); - if (!secInfoHeader->descriptors) - return ERR_OUT_OF_MEM; + secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); - struct pageDescriptor *descriptors = secInfoHeader->descriptors; - - for (int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) { - descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); - - struct sha1_ctx shaContext; - sha1_init(&shaContext); - - if (fseek(pe, i * pageSize, SEEK_SET) != 0) - return ERR_FILE_READ; - - uint8_t *page = malloc(pageSize); - if (!page) - return ERR_OUT_OF_MEM; - - if (fread(page, 1, pageSize, pe) != pageSize) { - nullAndFree((void**)&page); - return ERR_FILE_READ; + if (!secInfoHeader->descriptors) + { + return ERR_OUT_OF_MEM; } - #ifdef LITTLE_ENDIAN_SYSTEM - descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); - #endif + struct pageDescriptor *descriptors = secInfoHeader->descriptors; - sha1_update(&shaContext, pageSize, page); - sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); + for (int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) + { + descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); - #ifdef LITTLE_ENDIAN_SYSTEM - descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); - #endif + struct sha1_ctx shaContext; + sha1_init(&shaContext); - if (i != 0) { - sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); - } else { - sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); + if (fseek(pe, i * pageSize, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } + + uint8_t *page = malloc(pageSize); + + if (!page) + { + return ERR_OUT_OF_MEM; + } + + if (fread(page, 1, pageSize, pe) != pageSize) + { + nullAndFree((void**)&page); + return ERR_FILE_READ; + } + +#ifdef LITTLE_ENDIAN_SYSTEM + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); +#endif + + sha1_update(&shaContext, pageSize, page); + sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); + +#ifdef LITTLE_ENDIAN_SYSTEM + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); +#endif + + if (i != 0) + { + sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); + } + else + { + sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); + } + + nullAndFree((void**)&page); } - nullAndFree((void**)&page); - } - - return SUCCESS; + return SUCCESS; } diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 1252f7a..1cb3f1c 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -20,44 +20,52 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData) { - // Writing data into XEX header. - strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic + // Writing data into XEX header. + strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic - // Module flags (type of executable) - // Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead - if (xexHeader->moduleFlags == 0) { - if (peData->characteristics & PE_CHAR_FLAG_DLL) - xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL - else - xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title - if (peData->peExportInfo.count > 0) - xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions - } + // Module flags (type of executable) + // Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead + if (xexHeader->moduleFlags == 0) + { + if (peData->characteristics & PE_CHAR_FLAG_DLL) + { + xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL + } + else + { + xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title + } - xexHeader->optHeaderCount = optHeaderEntries->count; + if (peData->peExportInfo.count > 0) + { + xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions + } + } - return SUCCESS; + xexHeader->optHeaderCount = optHeaderEntries->count; + + return SUCCESS; } int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) { - // Writing data into security info header (much of this is derived from info in PE) - secInfoHeader->peSize = peData->size; + // Writing data into security info header (much of this is derived from info in PE) + secInfoHeader->peSize = peData->size; - // Setting signature (just a SynthXEX version identifier) - strcpy(secInfoHeader->signature, VERSION_STRING); + // Setting signature (just a SynthXEX version identifier) + strcpy(secInfoHeader->signature, VERSION_STRING); - secInfoHeader->imageInfoSize = 0x174; // Image info size is always 0x174 - secInfoHeader->imageFlags = (peData->pageSize == 0x1000 ? XEX_IMG_FLAG_4KIB_PAGES : 0) | XEX_IMG_FLAG_REGION_FREE; // If page size is 4KiB (small pages), set that flag - secInfoHeader->baseAddr = peData->baseAddr; - //memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID (no longer need to use memset, as we use calloc for these structs now) - //memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key - //secInfoHeader->exportTableAddr = TEMPEXPORTADDR; - secInfoHeader->exportTableAddr = 0; - secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE; - secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type. - secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages) - secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void*)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) + secInfoHeader->imageInfoSize = 0x174; // Image info size is always 0x174 + secInfoHeader->imageFlags = (peData->pageSize == 0x1000 ? XEX_IMG_FLAG_4KIB_PAGES : 0) | XEX_IMG_FLAG_REGION_FREE; // If page size is 4KiB (small pages), set that flag + secInfoHeader->baseAddr = peData->baseAddr; + //memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID (no longer need to use memset, as we use calloc for these structs now) + //memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key + //secInfoHeader->exportTableAddr = TEMPEXPORTADDR; + secInfoHeader->exportTableAddr = 0; + secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE; + secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type. + secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages) + secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void*)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) - return SUCCESS; + return SUCCESS; } diff --git a/src/write/headerhash.c b/src/write/headerhash.c index a7f756e..6e71dbe 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -21,63 +21,90 @@ // TEMPORARY, TO BE REPLACED ELSEWHERE WHEN IMPORT HEADER IS IMPLEMENTED void setImportsSha1(FILE *xex) { - if (fseek(xex, 0, SEEK_SET) != 0) - return; + if (fseek(xex, 0, SEEK_SET) != 0) + { + return; + } - while (get32BitFromXEX(xex) != 0x103FF) { - if (feof(xex)) - return; - } + while (get32BitFromXEX(xex) != 0x103FF) + { + if (feof(xex)) + { + return; + } + } - if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET) != 0) - return; + if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET) != 0) + { + return; + } - uint8_t importCount[4]; - memset(importCount, 0, sizeof(importCount)); + uint8_t importCount[4]; + memset(importCount, 0, sizeof(importCount)); - if (fseek(xex, 0x4, SEEK_CUR) != 0) - return; + if (fseek(xex, 0x4, SEEK_CUR) != 0) + { + return; + } - if (fread(importCount, 1, 4, xex) != 4) - return; + if (fread(importCount, 1, 4, xex) != 4) + { + return; + } - if (fseek(xex, -0x8, SEEK_CUR) != 0) - return; + if (fseek(xex, -0x8, SEEK_CUR) != 0) + { + return; + } - if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR) != 0) - return; + if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR) != 0) + { + return; + } - uint32_t size = get32BitFromXEX(xex) - 0x4; + uint32_t size = get32BitFromXEX(xex) - 0x4; - uint8_t *data = malloc(size); - if (!data) - return; + uint8_t *data = malloc(size); - memset(data, 0, size); + if (!data) + { + return; + } + + memset(data, 0, size); + + if (fread(data, 1, size, xex) != size) + { + nullAndFree((void**)&data); + return; + } + + struct sha1_ctx shaContext; + + sha1_init(&shaContext); + + sha1_update(&shaContext, size, data); - if (fread(data, 1, size, xex) != size) { nullAndFree((void**)&data); - return; - } - struct sha1_ctx shaContext; - sha1_init(&shaContext); - sha1_update(&shaContext, size, data); + uint8_t sha1[20]; - nullAndFree((void**)&data); + memset(sha1, 0, sizeof(sha1)); - uint8_t sha1[20]; - memset(sha1, 0, sizeof(sha1)); - sha1_digest(&shaContext, 20, sha1); + sha1_digest(&shaContext, 20, sha1); - if (fseek(xex, 0x10, SEEK_SET) != 0) - return; + if (fseek(xex, 0x10, SEEK_SET) != 0) + { + return; + } - if (fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET) != 0) - return; + if (fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET) != 0) + { + return; + } - fwrite(importCount, 1, 4, xex); - fwrite(sha1, 1, 20, xex); + fwrite(importCount, 1, 4, xex); + fwrite(sha1, 1, 20, xex); } @@ -85,72 +112,90 @@ void setImportsSha1(FILE *xex) // to determine the hash, but reading the file we just created is easier. int setHeaderSha1(FILE *xex) { - if (fseek(xex, 0x8, SEEK_SET) != 0) - return ERR_FILE_READ; + if (fseek(xex, 0x8, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - uint32_t basefileOffset = get32BitFromXEX(xex); + uint32_t basefileOffset = get32BitFromXEX(xex); - if (fseek(xex, 0x10, SEEK_SET) != 0) - return ERR_FILE_READ; + if (fseek(xex, 0x10, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - uint32_t secInfoOffset = get32BitFromXEX(xex); + uint32_t secInfoOffset = get32BitFromXEX(xex); - uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; - uint32_t remainingSize = basefileOffset - endOfImageInfo; + uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; + uint32_t remainingSize = basefileOffset - endOfImageInfo; - struct sha1_ctx shaContext; - memset(&shaContext, 0, sizeof(shaContext)); - sha1_init(&shaContext); + struct sha1_ctx shaContext; + memset(&shaContext, 0, sizeof(shaContext)); + sha1_init(&shaContext); - uint8_t *remainderOfHeaders = malloc(remainingSize); - if (!remainderOfHeaders) - return ERR_OUT_OF_MEM; + uint8_t *remainderOfHeaders = malloc(remainingSize); - memset(remainderOfHeaders, 0, remainingSize); + if (!remainderOfHeaders) + { + return ERR_OUT_OF_MEM; + } - if (fseek(xex, endOfImageInfo, SEEK_SET) != 0) { + memset(remainderOfHeaders, 0, remainingSize); + + if (fseek(xex, endOfImageInfo, SEEK_SET) != 0) + { + nullAndFree((void**)&remainderOfHeaders); + return ERR_FILE_READ; + } + + if (fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) + { + nullAndFree((void**)&remainderOfHeaders); + return ERR_FILE_READ; + } + + sha1_update(&shaContext, remainingSize, remainderOfHeaders); nullAndFree((void**)&remainderOfHeaders); - return ERR_FILE_READ; - } - if (fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) { - nullAndFree((void**)&remainderOfHeaders); - return ERR_FILE_READ; - } + uint32_t headersLen = secInfoOffset + 0x8; + uint8_t *headersStart = malloc(headersLen); - sha1_update(&shaContext, remainingSize, remainderOfHeaders); - nullAndFree((void**)&remainderOfHeaders); + if (!headersStart) + { + return ERR_OUT_OF_MEM; + } - uint32_t headersLen = secInfoOffset + 0x8; - uint8_t *headersStart = malloc(headersLen); - if (!headersStart) - return ERR_OUT_OF_MEM; + memset(headersStart, 0, headersLen); - memset(headersStart, 0, headersLen); + if (fseek(xex, 0, SEEK_SET) != 0) + { + nullAndFree((void**)&headersStart); + return ERR_FILE_READ; + } - if (fseek(xex, 0, SEEK_SET) != 0) { + if (fread(headersStart, 1, headersLen, xex) != headersLen) + { + nullAndFree((void**)&headersStart); + return ERR_FILE_READ; + } + + sha1_update(&shaContext, headersLen, headersStart); nullAndFree((void**)&headersStart); - return ERR_FILE_READ; - } - if (fread(headersStart, 1, headersLen, xex) != headersLen) { - nullAndFree((void**)&headersStart); - return ERR_FILE_READ; - } + uint8_t headerHash[20]; + memset(headerHash, 0, sizeof(headerHash)); + sha1_digest(&shaContext, 20, headerHash); - sha1_update(&shaContext, headersLen, headersStart); - nullAndFree((void**)&headersStart); + if (fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) + { + return ERR_FILE_READ; + } - uint8_t headerHash[20]; - memset(headerHash, 0, sizeof(headerHash)); - sha1_digest(&shaContext, 20, headerHash); + if (fwrite(headerHash, 1, 20, xex) != 20) + { + return ERR_FILE_READ; + } - if (fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) - return ERR_FILE_READ; - - if (fwrite(headerHash, 1, 20, xex) != 20) - return ERR_FILE_READ; - - return SUCCESS; + return SUCCESS; } diff --git a/src/write/writexex.c b/src/write/writexex.c index 0f30b5f..8af1af0 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -21,189 +21,198 @@ // TEMPORARY WRITE TESTING int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct secInfoHeader *secInfoHeader, struct optHeaders *optHeaders, struct offsets *offsets, FILE *pe, FILE *xex) { - // XEX Header + // XEX Header #ifdef LITTLE_ENDIAN_SYSTEM - // Endian-swap XEX header before writing - xexHeader->moduleFlags = __builtin_bswap32(xexHeader->moduleFlags); - xexHeader->peOffset = __builtin_bswap32(xexHeader->peOffset); - xexHeader->secInfoOffset = __builtin_bswap32(xexHeader->secInfoOffset); - xexHeader->optHeaderCount = __builtin_bswap32(xexHeader->optHeaderCount); -#endif - - fseek(xex, offsets->xexHeader, SEEK_SET); - fwrite(xexHeader, sizeof(uint8_t), sizeof(struct xexHeader), xex); - - // Optional header entries -#ifdef LITTLE_ENDIAN_SYSTEM - // Endian swap opt header entries - for(uint32_t i = 0; i < optHeaderEntries->count; i++) - { - optHeaderEntries->optHeaderEntry[i].id = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].id); - optHeaderEntries->optHeaderEntry[i].dataOrOffset = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].dataOrOffset); - } + // Endian-swap XEX header before writing + xexHeader->moduleFlags = __builtin_bswap32(xexHeader->moduleFlags); + xexHeader->peOffset = __builtin_bswap32(xexHeader->peOffset); + xexHeader->secInfoOffset = __builtin_bswap32(xexHeader->secInfoOffset); + xexHeader->optHeaderCount = __builtin_bswap32(xexHeader->optHeaderCount); #endif - fseek(xex, offsets->optHeaderEntries, SEEK_SET); + fseek(xex, offsets->xexHeader, SEEK_SET); + fwrite(xexHeader, sizeof(uint8_t), sizeof(struct xexHeader), xex); - for(int i = 0; i < optHeaderEntries->count; i++) + // Optional header entries +#ifdef LITTLE_ENDIAN_SYSTEM + + // Endian swap opt header entries + for(uint32_t i = 0; i < optHeaderEntries->count; i++) { - fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); + optHeaderEntries->optHeaderEntry[i].id = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].id); + optHeaderEntries->optHeaderEntry[i].dataOrOffset = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].dataOrOffset); } - // Page descriptors - fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET); - - // So we don't try to dereference an unaligned pointer - struct pageDescriptor *descriptors = secInfoHeader->descriptors; - - for(int i = 0; i < secInfoHeader->pageDescCount; i++) - { -#ifdef LITTLE_ENDIAN_SYSTEM - descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif - - // Writing out current descriptor... - fwrite(&(descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex); - fwrite(descriptors[i].sha1, sizeof(uint8_t), 0x14, xex); + + fseek(xex, offsets->optHeaderEntries, SEEK_SET); + + for(int i = 0; i < optHeaderEntries->count; i++) + { + fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); } - // Basefile - fseek(pe, 0x0, SEEK_SET); - fseek(xex, offsets->basefile, SEEK_SET); - - uint16_t readBufSize = 0x1000; // Reading in 4KiB at a time to avoid excessive memory usage - uint8_t *buffer = malloc(readBufSize * sizeof(uint8_t)); - if(buffer == NULL) {return ERR_OUT_OF_MEM;} - - for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize) + // Page descriptors + fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET); + + // So we don't try to dereference an unaligned pointer + struct pageDescriptor *descriptors = secInfoHeader->descriptors; + + for(int i = 0; i < secInfoHeader->pageDescCount; i++) { - size_t bytesToRead = (secInfoHeader->peSize - i < readBufSize) - ? secInfoHeader->peSize - i - : readBufSize; +#ifdef LITTLE_ENDIAN_SYSTEM + descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); +#endif - size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe); - if (readRet != bytesToRead) - { - fprintf(stderr, "Error: fread failed at offset %u (read %zu of %zu bytes)\n", i, readRet, bytesToRead); - break; - } - - size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex); - if (writeRet != readRet) - { - fprintf(stderr, "Error: fwrite failed at offset %u (wrote %zu of %zu bytes)\n", i, writeRet, readRet); - break; - } + // Writing out current descriptor... + fwrite(&(descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex); + fwrite(descriptors[i].sha1, sizeof(uint8_t), 0x14, xex); } - nullAndFree((void**)&buffer); - - // Security Info -#ifdef LITTLE_ENDIAN_SYSTEM - // Endian-swap secinfo header - secInfoHeader->headerSize = __builtin_bswap32(secInfoHeader->headerSize); - secInfoHeader->peSize = __builtin_bswap32(secInfoHeader->peSize); - secInfoHeader->imageInfoSize = __builtin_bswap32(secInfoHeader->imageInfoSize); - secInfoHeader->imageFlags = __builtin_bswap32(secInfoHeader->imageFlags); - secInfoHeader->baseAddr = __builtin_bswap32(secInfoHeader->baseAddr); - secInfoHeader->importTableCount = __builtin_bswap32(secInfoHeader->importTableCount); - secInfoHeader->exportTableAddr = __builtin_bswap32(secInfoHeader->exportTableAddr); - secInfoHeader->gameRegion = __builtin_bswap32(secInfoHeader->gameRegion); - secInfoHeader->mediaTypes = __builtin_bswap32(secInfoHeader->mediaTypes); - secInfoHeader->pageDescCount = __builtin_bswap32(secInfoHeader->pageDescCount); -#endif + // Basefile + fseek(pe, 0x0, SEEK_SET); + fseek(xex, offsets->basefile, SEEK_SET); - fseek(xex, offsets->secInfoHeader, SEEK_SET); - fwrite(secInfoHeader, sizeof(uint8_t), sizeof(struct secInfoHeader) - sizeof(void*), xex); // sizeof(void*) == size of page descriptor pointer at end - - // Optional headers - uint32_t currentHeader = 0; - - if(optHeaders->basefileFormat.size != 0) // If not 0, it has data. Write it. + uint16_t readBufSize = 0x1000; // Reading in 4KiB at a time to avoid excessive memory usage + uint8_t *buffer = malloc(readBufSize * sizeof(uint8_t)); + + if(buffer == NULL) { - fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); - -#ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->basefileFormat.size = __builtin_bswap32(optHeaders->basefileFormat.size); - optHeaders->basefileFormat.encType = __builtin_bswap16(optHeaders->basefileFormat.encType); - optHeaders->basefileFormat.compType = __builtin_bswap16(optHeaders->basefileFormat.compType); - optHeaders->basefileFormat.dataSize = __builtin_bswap32(optHeaders->basefileFormat.dataSize); - optHeaders->basefileFormat.zeroSize = __builtin_bswap32(optHeaders->basefileFormat.zeroSize); -#endif - - fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), sizeof(struct basefileFormat), xex); - currentHeader++; + return ERR_OUT_OF_MEM; } - if(optHeaders->importLibraries.size != 0) + for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize) { - fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); + size_t bytesToRead = (secInfoHeader->peSize - i < readBufSize) + ? secInfoHeader->peSize - i + : readBufSize; - // Write the main header first + size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe); - // Use these to avoid dereferencing an unaligned pointer - char *nameTable = optHeaders->importLibraries.nameTable; - - // Save the values we need to use before byteswapping - uint32_t nameTableSize = optHeaders->importLibraries.nameTableSize; - uint32_t tableCount = optHeaders->importLibraries.tableCount; - -#ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->importLibraries.size = __builtin_bswap32(optHeaders->importLibraries.size); - optHeaders->importLibraries.nameTableSize = __builtin_bswap32(optHeaders->importLibraries.nameTableSize); - optHeaders->importLibraries.tableCount = __builtin_bswap32(optHeaders->importLibraries.tableCount); -#endif + if (readRet != bytesToRead) + { + fprintf(stderr, "Error: fread failed at offset %u (read %zu of %zu bytes)\n", i, readRet, bytesToRead); + break; + } - fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void*)), xex); - fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex); + size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex); -#ifdef LITTLE_ENDIAN_SYSTEM - // Restore the table count (we require it to free the import libraries struct later) - optHeaders->importLibraries.tableCount = tableCount; -#endif - - // Now write each import table - // Use this to avoid dereferencing an unaligned pointer - struct importTable *importTables = optHeaders->importLibraries.importTables; - - for(uint32_t i = 0; i < tableCount; i++) - { - uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer - uint16_t addressCount = importTables[i].addressCount; - -#ifdef LITTLE_ENDIAN_SYSTEM - importTables[i].size = __builtin_bswap32(importTables[i].size); - importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); - importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); - importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); - importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - - for(uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } -#endif - - fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); - fwrite(addresses, sizeof(uint32_t), addressCount, xex); - } - - currentHeader++; + if (writeRet != readRet) + { + fprintf(stderr, "Error: fwrite failed at offset %u (wrote %zu of %zu bytes)\n", i, writeRet, readRet); + break; + } } - if(optHeaders->tlsInfo.slotCount != 0) - { - fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); + nullAndFree((void**)&buffer); + // Security Info #ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->tlsInfo.slotCount = __builtin_bswap32(optHeaders->tlsInfo.slotCount); - optHeaders->tlsInfo.rawDataAddr = __builtin_bswap32(optHeaders->tlsInfo.rawDataAddr); - optHeaders->tlsInfo.dataSize = __builtin_bswap32(optHeaders->tlsInfo.dataSize); - optHeaders->tlsInfo.rawDataSize = __builtin_bswap32(optHeaders->tlsInfo.rawDataSize); + // Endian-swap secinfo header + secInfoHeader->headerSize = __builtin_bswap32(secInfoHeader->headerSize); + secInfoHeader->peSize = __builtin_bswap32(secInfoHeader->peSize); + secInfoHeader->imageInfoSize = __builtin_bswap32(secInfoHeader->imageInfoSize); + secInfoHeader->imageFlags = __builtin_bswap32(secInfoHeader->imageFlags); + secInfoHeader->baseAddr = __builtin_bswap32(secInfoHeader->baseAddr); + secInfoHeader->importTableCount = __builtin_bswap32(secInfoHeader->importTableCount); + secInfoHeader->exportTableAddr = __builtin_bswap32(secInfoHeader->exportTableAddr); + secInfoHeader->gameRegion = __builtin_bswap32(secInfoHeader->gameRegion); + secInfoHeader->mediaTypes = __builtin_bswap32(secInfoHeader->mediaTypes); + secInfoHeader->pageDescCount = __builtin_bswap32(secInfoHeader->pageDescCount); #endif - fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex); + fseek(xex, offsets->secInfoHeader, SEEK_SET); + fwrite(secInfoHeader, sizeof(uint8_t), sizeof(struct secInfoHeader) - sizeof(void*), xex); // sizeof(void*) == size of page descriptor pointer at end + + // Optional headers + uint32_t currentHeader = 0; + + if(optHeaders->basefileFormat.size != 0) // If not 0, it has data. Write it. + { + fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); + +#ifdef LITTLE_ENDIAN_SYSTEM + optHeaders->basefileFormat.size = __builtin_bswap32(optHeaders->basefileFormat.size); + optHeaders->basefileFormat.encType = __builtin_bswap16(optHeaders->basefileFormat.encType); + optHeaders->basefileFormat.compType = __builtin_bswap16(optHeaders->basefileFormat.compType); + optHeaders->basefileFormat.dataSize = __builtin_bswap32(optHeaders->basefileFormat.dataSize); + optHeaders->basefileFormat.zeroSize = __builtin_bswap32(optHeaders->basefileFormat.zeroSize); +#endif + + fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), sizeof(struct basefileFormat), xex); + currentHeader++; } - return SUCCESS; + if(optHeaders->importLibraries.size != 0) + { + fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); + + // Write the main header first + + // Use these to avoid dereferencing an unaligned pointer + char *nameTable = optHeaders->importLibraries.nameTable; + + // Save the values we need to use before byteswapping + uint32_t nameTableSize = optHeaders->importLibraries.nameTableSize; + uint32_t tableCount = optHeaders->importLibraries.tableCount; + +#ifdef LITTLE_ENDIAN_SYSTEM + optHeaders->importLibraries.size = __builtin_bswap32(optHeaders->importLibraries.size); + optHeaders->importLibraries.nameTableSize = __builtin_bswap32(optHeaders->importLibraries.nameTableSize); + optHeaders->importLibraries.tableCount = __builtin_bswap32(optHeaders->importLibraries.tableCount); +#endif + + fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void*)), xex); + fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex); + +#ifdef LITTLE_ENDIAN_SYSTEM + // Restore the table count (we require it to free the import libraries struct later) + optHeaders->importLibraries.tableCount = tableCount; +#endif + + // Now write each import table + // Use this to avoid dereferencing an unaligned pointer + struct importTable *importTables = optHeaders->importLibraries.importTables; + + for(uint32_t i = 0; i < tableCount; i++) + { + uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer + uint16_t addressCount = importTables[i].addressCount; + +#ifdef LITTLE_ENDIAN_SYSTEM + importTables[i].size = __builtin_bswap32(importTables[i].size); + importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); + importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer); + importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); + importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + + for(uint16_t j = 0; j < addressCount; j++) + { + addresses[j] = __builtin_bswap32(addresses[j]); + } + +#endif + + fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); + fwrite(addresses, sizeof(uint32_t), addressCount, xex); + } + + currentHeader++; + } + + if(optHeaders->tlsInfo.slotCount != 0) + { + fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); + +#ifdef LITTLE_ENDIAN_SYSTEM + optHeaders->tlsInfo.slotCount = __builtin_bswap32(optHeaders->tlsInfo.slotCount); + optHeaders->tlsInfo.rawDataAddr = __builtin_bswap32(optHeaders->tlsInfo.rawDataAddr); + optHeaders->tlsInfo.dataSize = __builtin_bswap32(optHeaders->tlsInfo.dataSize); + optHeaders->tlsInfo.rawDataSize = __builtin_bswap32(optHeaders->tlsInfo.rawDataSize); +#endif + + fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex); + } + + return SUCCESS; } From dba2f4c54c4fab663414101ebf9f016c8472fbe9 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 16 Jun 2025 17:12:27 +0100 Subject: [PATCH 23/38] Change how getXXFromYY functions handle errors (utilise errno) --- src/common/common.h | 1 + src/common/datastorage.c | 28 +++++---- src/getdata/gethdrdata.c | 125 ++++++++++++++++++++++++++++++++++++--- src/pemapper/pemapper.c | 23 +++++++ src/write/headerhash.c | 120 +++++++------------------------------ src/write/headerhash.h | 1 - 6 files changed, 176 insertions(+), 122 deletions(-) diff --git a/src/common/common.h b/src/common/common.h index e8dbdcc..454bb27 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include #include #include diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 7728242..9da40c7 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -213,11 +213,12 @@ uint32_t offsetToRVA(uint32_t offset, struct sections *sections) uint32_t get32BitFromPE(FILE *pe) { uint32_t result; + errno = SUCCESS; - if (fread(&result, sizeof(uint32_t), 1, pe) != 1) + if(fread(&result, sizeof(uint32_t), 1, pe) != 1) { - fprintf(stderr, "%s Error: Failed to read 32-bit value from PE\n", PRINT_STEM); - exit(EXIT_FAILURE); + errno = ERR_FILE_READ; + return 0; } // If system is big endian, swap endianness (PE is LE) @@ -231,11 +232,12 @@ uint32_t get32BitFromPE(FILE *pe) uint16_t get16BitFromPE(FILE *pe) { uint16_t result; + errno = SUCCESS; - if (fread(&result, sizeof(uint16_t), 1, pe) != 1) + if(fread(&result, sizeof(uint16_t), 1, pe) != 1) { - fprintf(stderr, "%s Error: Failed to read 16-bit value from PE\n", PRINT_STEM); - exit(EXIT_FAILURE); + errno = ERR_FILE_READ; + return 0; } #ifdef BIG_ENDIAN_SYSTEM @@ -248,11 +250,12 @@ uint16_t get16BitFromPE(FILE *pe) uint32_t get32BitFromXEX(FILE *xex) { uint32_t result; + errno = SUCCESS; - if (fread(&result, sizeof(uint32_t), 1, xex) != 1) + if(fread(&result, sizeof(uint32_t), 1, xex) != 1) { - fprintf(stderr, "%s Error: Failed to read 32-bit value from XEX\n", PRINT_STEM); - exit(EXIT_FAILURE); + errno = ERR_FILE_READ; + return 0; } #ifdef LITTLE_ENDIAN_SYSTEM @@ -265,11 +268,12 @@ uint32_t get32BitFromXEX(FILE *xex) uint16_t get16BitFromXEX(FILE *xex) { uint16_t result; + errno = SUCCESS; - if (fread(&result, sizeof(uint16_t), 1, xex) != 1) + if(fread(&result, sizeof(uint16_t), 1, xex) != 1) { - fprintf(stderr, "%s Error: Failed to read 16-bit value from XEX\n", PRINT_STEM); - exit(EXIT_FAILURE); + errno = ERR_FILE_READ; + return 0; } #ifdef LITTLE_ENDIAN_SYSTEM diff --git a/src/getdata/gethdrdata.c b/src/getdata/gethdrdata.c index 5289a3e..9e4c525 100644 --- a/src/getdata/gethdrdata.c +++ b/src/getdata/gethdrdata.c @@ -37,7 +37,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false fseek(pe, 0, SEEK_SET); uint16_t magic = get16BitFromPE(pe); - if (magic != 0x5A4D) // PE magic + if (magic != 0x5A4D || errno != SUCCESS) // PE magic { return false; } @@ -46,7 +46,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false fseek(pe, 0x3C, SEEK_SET); size_t peHeaderOffset = get32BitFromPE(pe); - if (finalOffset < peHeaderOffset) + if (finalOffset < peHeaderOffset || errno != SUCCESS) { return false; } @@ -59,10 +59,9 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false // Check section count fseek(pe, peHeaderOffset + 0x6, SEEK_SET); - uint16_t sectionCount = get16BitFromPE(pe); - if (sectionCount == 0) + if (sectionCount == 0 || errno != SUCCESS) { return false; } @@ -72,7 +71,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false uint16_t sizeOfOptHdr = get16BitFromPE(pe); // 0x18 == size of COFF header, 0x28 == size of one entry in section table - if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28)) + if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28) || errno != SUCCESS) { return false; } @@ -83,7 +82,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false // 0x1F2 == POWERPCBE uint16_t machineID = get16BitFromPE(pe); - if (machineID != 0x1F2 && !skipMachineCheck) + if ((machineID != 0x1F2 && !skipMachineCheck) || errno != SUCCESS) { return false; } @@ -93,7 +92,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false uint16_t subsystem = get16BitFromPE(pe); - if (subsystem != 0xE) // 0xE == XBOX + if (subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX { return false; } @@ -104,7 +103,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false // 4KiB and 64KiB are the only valid sizes uint32_t pageSize = get32BitFromPE(pe); - if (pageSize != 0x1000 && pageSize != 0x10000) + if ((pageSize != 0x1000 && pageSize != 0x10000) || errno != SUCCESS) { return false; } @@ -115,7 +114,21 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false for (uint16_t i = 0; i < sectionCount; i++) { // If raw size + raw offset exceeds file size, PE is invalid - if (finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe)) + uint32_t rawSize = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return false; + } + + uint32_t rawOffset = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return false; + } + + if (finalOffset < rawSize + rawOffset) { return false; } @@ -132,9 +145,19 @@ int getSectionInfo(FILE *pe, struct sections *sections) fseek(pe, 0x3C, SEEK_SET); uint32_t peOffset = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count sections->count = get16BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata if (sections->section == NULL) @@ -148,13 +171,41 @@ int getSectionInfo(FILE *pe, struct sections *sections) { fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section sections->section[i].virtualSize = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sections->section[i].rva = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sections->section[i].rawSize = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sections->section[i].offset = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags uint32_t characteristics = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + if (characteristics & PE_SECTION_FLAG_EXECUTE) { sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 @@ -189,10 +240,20 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) fseek(pe, 0x3C, SEEK_SET); peData->peHeaderOffset = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Number of sections fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET); peData->numberOfSections = get16BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Size of section table peData->sectionTableSize = peData->numberOfSections * 0x28; @@ -201,35 +262,81 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET); peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // PE characteristics fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET); peData->characteristics = get16BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Entry point (RVA) fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); peData->entryPoint = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Base address fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET); peData->baseAddr = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Page alignment/size fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET); peData->pageSize = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // Export tables fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET); peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data + if(errno != SUCCESS) + { + return errno; + } + // Import tables fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); peData->peImportInfo.idtRVA = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); peData->tlsAddr = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + peData->tlsSize = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + if (peData->tlsAddr != 0 || peData->tlsSize != 0) { return ERR_UNSUPPORTED_STRUCTURE; diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index c1b42c8..4d9dfa7 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -44,10 +44,33 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) for (uint16_t i = 0; i < peData->numberOfSections; i++) { sectionInfo[i].virtualSize = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sectionInfo[i].rva = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sectionInfo[i].rawSize = get32BitFromPE(pe); + + if(errno != SUCCESS) + { + return errno; + } + sectionInfo[i].offset = get32BitFromPE(pe); + if(errno != SUCCESS) + { + return errno; + } + if (fseek(pe, 0x18, SEEK_CUR) != 0) { return ERR_FILE_READ; diff --git a/src/write/headerhash.c b/src/write/headerhash.c index 6e71dbe..6779919 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -18,114 +18,34 @@ #include "headerhash.h" -// TEMPORARY, TO BE REPLACED ELSEWHERE WHEN IMPORT HEADER IS IMPLEMENTED -void setImportsSha1(FILE *xex) -{ - if (fseek(xex, 0, SEEK_SET) != 0) - { - return; - } - - while (get32BitFromXEX(xex) != 0x103FF) - { - if (feof(xex)) - { - return; - } - } - - if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET) != 0) - { - return; - } - - uint8_t importCount[4]; - memset(importCount, 0, sizeof(importCount)); - - if (fseek(xex, 0x4, SEEK_CUR) != 0) - { - return; - } - - if (fread(importCount, 1, 4, xex) != 4) - { - return; - } - - if (fseek(xex, -0x8, SEEK_CUR) != 0) - { - return; - } - - if (fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR) != 0) - { - return; - } - - uint32_t size = get32BitFromXEX(xex) - 0x4; - - uint8_t *data = malloc(size); - - if (!data) - { - return; - } - - memset(data, 0, size); - - if (fread(data, 1, size, xex) != size) - { - nullAndFree((void**)&data); - return; - } - - struct sha1_ctx shaContext; - - sha1_init(&shaContext); - - sha1_update(&shaContext, size, data); - - nullAndFree((void**)&data); - - uint8_t sha1[20]; - - memset(sha1, 0, sizeof(sha1)); - - sha1_digest(&shaContext, 20, sha1); - - if (fseek(xex, 0x10, SEEK_SET) != 0) - { - return; - } - - if (fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET) != 0) - { - return; - } - - fwrite(importCount, 1, 4, xex); - fwrite(sha1, 1, 20, xex); -} - - // So, it would probably be more sensible to endian-swap all of the data back and determine which structure is where // to determine the hash, but reading the file we just created is easier. int setHeaderSha1(FILE *xex) { - if (fseek(xex, 0x8, SEEK_SET) != 0) + if(fseek(xex, 0x8, SEEK_SET) != 0) { return ERR_FILE_READ; } uint32_t basefileOffset = get32BitFromXEX(xex); - if (fseek(xex, 0x10, SEEK_SET) != 0) + if(errno != SUCCESS) + { + return errno; + } + + if(fseek(xex, 0x10, SEEK_SET) != 0) { return ERR_FILE_READ; } uint32_t secInfoOffset = get32BitFromXEX(xex); + if(errno != SUCCESS) + { + return errno; + } + uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; uint32_t remainingSize = basefileOffset - endOfImageInfo; @@ -135,20 +55,20 @@ int setHeaderSha1(FILE *xex) uint8_t *remainderOfHeaders = malloc(remainingSize); - if (!remainderOfHeaders) + if(!remainderOfHeaders) { return ERR_OUT_OF_MEM; } memset(remainderOfHeaders, 0, remainingSize); - if (fseek(xex, endOfImageInfo, SEEK_SET) != 0) + if(fseek(xex, endOfImageInfo, SEEK_SET) != 0) { nullAndFree((void**)&remainderOfHeaders); return ERR_FILE_READ; } - if (fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) + if(fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) { nullAndFree((void**)&remainderOfHeaders); return ERR_FILE_READ; @@ -160,20 +80,20 @@ int setHeaderSha1(FILE *xex) uint32_t headersLen = secInfoOffset + 0x8; uint8_t *headersStart = malloc(headersLen); - if (!headersStart) + if(!headersStart) { return ERR_OUT_OF_MEM; } memset(headersStart, 0, headersLen); - if (fseek(xex, 0, SEEK_SET) != 0) + if(fseek(xex, 0, SEEK_SET) != 0) { nullAndFree((void**)&headersStart); return ERR_FILE_READ; } - if (fread(headersStart, 1, headersLen, xex) != headersLen) + if(fread(headersStart, 1, headersLen, xex) != headersLen) { nullAndFree((void**)&headersStart); return ERR_FILE_READ; @@ -186,12 +106,12 @@ int setHeaderSha1(FILE *xex) memset(headerHash, 0, sizeof(headerHash)); sha1_digest(&shaContext, 20, headerHash); - if (fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) + if(fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) { return ERR_FILE_READ; } - if (fwrite(headerHash, 1, 20, xex) != 20) + if(fwrite(headerHash, 1, 20, xex) != 20) { return ERR_FILE_READ; } diff --git a/src/write/headerhash.h b/src/write/headerhash.h index 45a7c5d..5ce1177 100644 --- a/src/write/headerhash.h +++ b/src/write/headerhash.h @@ -22,5 +22,4 @@ #include "../common/crypto.h" #include "../common/datastorage.h" -void setImportsSha1(FILE *xex); int setHeaderSha1(FILE *xex); From b646f288122a58bde24ab389830a741b61ed5ecd Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 16 Jun 2025 21:31:17 +0100 Subject: [PATCH 24/38] Tweak formatting, add CONTRIBUTING.md --- CONTRIBUTING.md | 124 ++++++++++++++++++++ src/common/common.h | 4 +- src/common/datastorage.c | 87 +++++++------- src/common/datastorage.h | 6 +- src/getdata/gethdrdata.c | 186 +++++++++--------------------- src/getdata/getimports.c | 201 ++++++++++++++------------------ src/main.c | 122 +++++++++----------- src/pemapper/pemapper.c | 116 +++++++------------ src/placer/placer.c | 18 ++- src/setdata/optheaders.c | 210 ++++++++++++---------------------- src/setdata/pagedescriptors.c | 47 +++----- src/setdata/populateheaders.c | 8 +- src/write/headerhash.c | 45 +++----- src/write/writexex.c | 26 ++--- 14 files changed, 533 insertions(+), 667 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b25a68e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,124 @@ +# Contributing to SynthXEX + +## Bug reports +Please direct any bug reports to either the [main repository](https://git.aidenisik.scot/FreeChainXenon/SynthXEX/issues), or the [GitHub mirror](https://github.com/FreeChainXenon/SynthXEX/issues). + +## Patches +To contribute code, first ensure you have read and adhered to the [code style](#code-style) section. + +There are two ways to contribute code patches: + +- Pull requests, which you can submit either to the [main repository](https://git.aidenisik.scot/FreeChainXenon/SynthXEX/pulls), or the [GitHub mirror](https://github.com/FreeChainXenon/SynthXEX/pulls). + +- Patch files, generated by Git, can be sent to \, which I will review and possibly merge. + +## Code Style + +If you are using [AStyle](https://astyle.sourceforge.net/), the following command will format the code correctly (run from the project root): +``` +astyle --style=allman --add-braces --break-blocks --indent-labels --indent-switches --unpad-paren --align-pointer=name --pad-oper --indent-preproc-block --add-one-line-braces --squeeze-ws --squeeze-lines=1 --recursive "./src/*.c" "./src/*.h" +``` + +The code style used in SynthXEX is the [Allman/BSD style as specified by AStyle](https://astyle.sourceforge.net/astyle.html#_style=allman), with the following extra specifications: + +- C++-style comments are used, rather than multiline/C89-style comments, e.g: +``` +// This is a comment +// This is another comment +// This is yet another comment +``` + +- All conditional statements utilise curly brackets, even when there is only one line within them, e.g: +``` +if(condition) +{ + printf("Hello!\n"); +} +``` + +- It is acceptable to have the curly brackets on the same line as the code if it is short, e.g: +``` +if(condition) { printf("Hello!\n"); } + +if(condition) +{ printf("Hello!\n"); } +``` + +- All brackets immediately succeed/precede the name/keyword they correspond to with no spaces, e.g: +``` +foo(bar); +(float)(a + b); +``` + +- All code blocks have one empty line between the start/end and any other code, e.g.: +``` +int a = 1; + +if(a == 2) +{ + printf("Hi!\n"); +} + +int b = 2; +``` + +- Labels are always 1 indentation level below the current level, e.g: +``` +if(1) +{ + if(2) + { + int a = 1; + label1: + int b = 2; + } +} +``` + +- Case statements are always 1 indentation level above their corresponding switch, e.g.: +``` +switch(val) +{ + case 1: + printf("1\n"); + break; + + default: + printf("0\n"); + break; +} +``` + +- Pointer operators are always attached to variables, e.g: +``` +char *string = NULL; +int a = 0; +setA(&a); +``` + +- Operators are always padded to be one space away from their operands, e.g: +``` +int a = 1 + 2; +``` + +- Commas in function calls, etc, always have one space after them, e.g: +``` +myFunc(1, 2); +``` + +- Preprocessor macros outside functions are indented, just like regular code, e.g: +``` +#ifdef MACRO_ONE + #define MACRO 1 +#else + #define MACRO 0 +#endif +``` + +- There should never be more than one blank line between code, e.g: +``` +int a; +int b; + +int c; +``` diff --git a/src/common/common.h b/src/common/common.h index 454bb27..d60d9d0 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -35,9 +35,9 @@ #define COPYRIGHT "2024-2025" #ifdef GIT_COMMIT -#define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT + #define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT #else -#define VERSION_STRING NAME " " VERSION + #define VERSION_STRING NAME " " VERSION #endif // Print constants diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 9da40c7..5cbffcd 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -23,9 +23,9 @@ // Disable pointer type checking here to make this easier to use void nullAndFree(void **ptr) { - if (ptr != NULL) + if(ptr != NULL) { - if (*ptr != NULL) + if(*ptr != NULL) { freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr); *ptr = NULL; @@ -46,79 +46,76 @@ void nullAndFree(void **ptr) // about if it becomes necessary with future additions. void freeOffsetsStruct(struct offsets **offsets) { - if (offsets != NULL) + if(offsets != NULL) { - if (*offsets != NULL) + if(*offsets != NULL) { - nullAndFree((void**)&((*offsets)->optHeaders)); - nullAndFree((void**)offsets); + nullAndFree((void **) & ((*offsets)->optHeaders)); + nullAndFree((void **)offsets); } } } void freeXexHeaderStruct(struct xexHeader **xexHeader) { - if (xexHeader != NULL) + if(xexHeader != NULL) { - if (*xexHeader != NULL) - { - nullAndFree((void**)xexHeader); - } + if(*xexHeader != NULL) + { nullAndFree((void **)xexHeader); } } } void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader) { - if (secInfoHeader != NULL) + if(secInfoHeader != NULL) { - if (*secInfoHeader != NULL) + if(*secInfoHeader != NULL) { struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer - nullAndFree((void**)&descriptors); + nullAndFree((void **)&descriptors); //(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways - nullAndFree((void**)secInfoHeader); + nullAndFree((void **)secInfoHeader); } } } void freeSectionsStruct(struct sections *sections) { - nullAndFree((void**)&(sections->section)); + nullAndFree((void **) & (sections->section)); } void freePeImportInfoStruct(struct peImportInfo *peImportInfo) { - if (peImportInfo->tables != NULL) + if(peImportInfo->tables != NULL) { // Free the imports within each table first, then the tables, // otherwise we'll have a memory leak - for (uint32_t i = 0; i < peImportInfo->tableCount; i++) + for(uint32_t i = 0; i < peImportInfo->tableCount; i++) { - nullAndFree((void**)&(peImportInfo->tables[i].name)); - nullAndFree((void**)&(peImportInfo->tables[i].imports)); + nullAndFree((void **) & (peImportInfo->tables[i].name)); + nullAndFree((void **) & (peImportInfo->tables[i].imports)); } - nullAndFree((void**)&(peImportInfo->tables)); + nullAndFree((void **) & (peImportInfo->tables)); } } - void freePeDataStruct(struct peData **peData) { - if (*peData != NULL) + if(*peData != NULL) { freeSectionsStruct(&((*peData)->sections)); freePeImportInfoStruct(&((*peData)->peImportInfo)); - nullAndFree((void**)peData); + nullAndFree((void **)peData); } } void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries) { - if (*optHeaderEntries != NULL) + if(*optHeaderEntries != NULL) { - nullAndFree((void**)&((*optHeaderEntries)->optHeaderEntry)); - nullAndFree((void**)optHeaderEntries); + nullAndFree((void **) & ((*optHeaderEntries)->optHeaderEntry)); + nullAndFree((void **)optHeaderEntries); } } @@ -127,28 +124,28 @@ void freeImportLibrariesStruct(struct importLibraries *importLibraries) struct importTable *importTables = importLibraries->importTables; char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers - nullAndFree((void**)&nameTable); + nullAndFree((void **)&nameTable); - if (importTables != NULL) + if(importTables != NULL) { - for (uint32_t i = 0; i < importLibraries->tableCount; i++) + for(uint32_t i = 0; i < importLibraries->tableCount; i++) { uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer - nullAndFree((void**)&addresses); + nullAndFree((void **)&addresses); importTables[i].addresses = addresses; } - nullAndFree((void**)&importTables); + nullAndFree((void **)&importTables); importLibraries->importTables = importTables; } } void freeOptHeadersStruct(struct optHeaders **optHeaders) { - if (*optHeaders != NULL) + if(*optHeaders != NULL) { freeImportLibrariesStruct(&((*optHeaders)->importLibraries)); - nullAndFree((void**)optHeaders); + nullAndFree((void **)optHeaders); } } @@ -165,7 +162,7 @@ void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, uint32_t getNextAligned(uint32_t offset, uint32_t alignment) { - if (offset % alignment) // If offset not aligned + if(offset % alignment) // If offset not aligned { return offset + (alignment - (offset % alignment)); // Align } @@ -175,17 +172,15 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment) uint32_t rvaToOffset(uint32_t rva, struct sections *sections) { - if ((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) + if((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize)) { return 0; // Not found (beyond end of PE) } - for (int32_t i = sections->count - 1; i >= 0; i--) + for(int32_t i = sections->count - 1; i >= 0; i--) { - if (rva >= sections->section[i].rva) - { - return (rva - sections->section[i].rva) + sections->section[i].offset; - } + if(rva >= sections->section[i].rva) + { return (rva - sections->section[i].rva) + sections->section[i].offset; } } return 0; // Not found @@ -193,18 +188,16 @@ uint32_t rvaToOffset(uint32_t rva, struct sections *sections) uint32_t offsetToRVA(uint32_t offset, struct sections *sections) { - if ((sections->count > 0) + if((sections->count > 0) && (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize)) { return 0; // Not found (beyond end of PE) } - for (int32_t i = sections->count - 1; i >= 0; i--) + for(int32_t i = sections->count - 1; i >= 0; i--) { - if (offset >= sections->section[i].offset) - { - return (offset - sections->section[i].offset) + sections->section[i].rva; - } + if(offset >= sections->section[i].offset) + { return (offset - sections->section[i].offset) + sections->section[i].rva; } } return 0; // Not found diff --git a/src/common/datastorage.h b/src/common/datastorage.h index c12817e..0d3d9af 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -22,11 +22,11 @@ // Endian test #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define LITTLE_ENDIAN_SYSTEM + #define LITTLE_ENDIAN_SYSTEM #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define BIG_ENDIAN_SYSTEM + #define BIG_ENDIAN_SYSTEM #else -#error "System is not big or little endian! SynthXEX does not support this archaic dinosaur, sorry!" + #error "System is not big or little endian! SynthXEX does not support this archaic dinosaur, sorry!" #endif // Preprocessor definitions diff --git a/src/getdata/gethdrdata.c b/src/getdata/gethdrdata.c index 9e4c525..510bdf0 100644 --- a/src/getdata/gethdrdata.c +++ b/src/getdata/gethdrdata.c @@ -28,53 +28,41 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false fseek(pe, 0, SEEK_END); size_t finalOffset = ftell(pe); - if (finalOffset < 0x3C + 0x4) - { - return false; - } + if(finalOffset < 0x3C + 0x4) + { return false; } // Check magic fseek(pe, 0, SEEK_SET); uint16_t magic = get16BitFromPE(pe); - if (magic != 0x5A4D || errno != SUCCESS) // PE magic - { - return false; - } + if(magic != 0x5A4D || errno != SUCCESS) // PE magic + { return false; } // Check if pointer to PE header is valid fseek(pe, 0x3C, SEEK_SET); size_t peHeaderOffset = get32BitFromPE(pe); - if (finalOffset < peHeaderOffset || errno != SUCCESS) - { - return false; - } + if(finalOffset < peHeaderOffset || errno != SUCCESS) + { return false; } // Check if the file is big enough to get size of optional header, and therefore size of whole PE header - if (finalOffset < 0x14 + 0x2) - { - return false; - } + if(finalOffset < 0x14 + 0x2) + { return false; } // Check section count fseek(pe, peHeaderOffset + 0x6, SEEK_SET); uint16_t sectionCount = get16BitFromPE(pe); - if (sectionCount == 0 || errno != SUCCESS) - { - return false; - } + if(sectionCount == 0 || errno != SUCCESS) + { return false; } // Check if the file is large enough to contain the whole PE header fseek(pe, peHeaderOffset + 0x14, SEEK_SET); uint16_t sizeOfOptHdr = get16BitFromPE(pe); // 0x18 == size of COFF header, 0x28 == size of one entry in section table - if (finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28) || errno != SUCCESS) - { - return false; - } + if(finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28) || errno != SUCCESS) + { return false; } // Check machine ID fseek(pe, peHeaderOffset + 0x4, SEEK_SET); @@ -82,20 +70,16 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false // 0x1F2 == POWERPCBE uint16_t machineID = get16BitFromPE(pe); - if ((machineID != 0x1F2 && !skipMachineCheck) || errno != SUCCESS) - { - return false; - } + if((machineID != 0x1F2 && !skipMachineCheck) || errno != SUCCESS) + { return false; } // Check subsystem fseek(pe, peHeaderOffset + 0x5C, SEEK_SET); uint16_t subsystem = get16BitFromPE(pe); - if (subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX - { - return false; - } + if(subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX + { return false; } // Check page size/alignment fseek(pe, peHeaderOffset + 0x38, SEEK_SET); @@ -103,35 +87,27 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false // 4KiB and 64KiB are the only valid sizes uint32_t pageSize = get32BitFromPE(pe); - if ((pageSize != 0x1000 && pageSize != 0x10000) || errno != SUCCESS) - { - return false; - } + if((pageSize != 0x1000 && pageSize != 0x10000) || errno != SUCCESS) + { return false; } // Check each raw offset + raw size in section table fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry - for (uint16_t i = 0; i < sectionCount; i++) + for(uint16_t i = 0; i < sectionCount; i++) { // If raw size + raw offset exceeds file size, PE is invalid uint32_t rawSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return false; - } + { return false; } uint32_t rawOffset = get32BitFromPE(pe); if(errno != SUCCESS) - { - return false; - } + { return false; } - if (finalOffset < rawSize + rawOffset) - { - return false; - } + if(finalOffset < rawSize + rawOffset) + { return false; } // Next entry fseek(pe, 0x20, SEEK_CUR); @@ -146,82 +122,60 @@ int getSectionInfo(FILE *pe, struct sections *sections) uint32_t peOffset = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count sections->count = get16BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata - if (sections->section == NULL) - { - return ERR_OUT_OF_MEM; - } + if(sections->section == NULL) + { return ERR_OUT_OF_MEM; } fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table - for (uint16_t i = 0; i < sections->count; i++) + for(uint16_t i = 0; i < sections->count; i++) { fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section sections->section[i].virtualSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sections->section[i].rva = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sections->section[i].rawSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sections->section[i].offset = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags uint32_t characteristics = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } - if (characteristics & PE_SECTION_FLAG_EXECUTE) + if(characteristics & PE_SECTION_FLAG_EXECUTE) { sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 } - else if (characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) - { - sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; - } - else if (characteristics & PE_SECTION_FLAG_READ) - { - sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; - } + else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) + { sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; } + else if(characteristics & PE_SECTION_FLAG_READ) + { sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; } else - { - return ERR_MISSING_SECTION_FLAG; - } + { return ERR_MISSING_SECTION_FLAG; } } // Don't need to progress any more to get to beginning of next entry, as characteristics is last field @@ -231,28 +185,22 @@ int getSectionInfo(FILE *pe, struct sections *sections) int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) { // No flags supported at this time (will be used for getting additional info, for e.g. other optional headers) - if (flags) - { - return ERR_UNKNOWN_DATA_REQUEST; - } + if(flags) + { return ERR_UNKNOWN_DATA_REQUEST; } // Getting PE header offset before we go any further.. fseek(pe, 0x3C, SEEK_SET); peData->peHeaderOffset = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Number of sections fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET); peData->numberOfSections = get16BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Size of section table peData->sectionTableSize = peData->numberOfSections * 0x28; @@ -263,92 +211,70 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // PE characteristics fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET); peData->characteristics = get16BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Entry point (RVA) fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); peData->entryPoint = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Base address fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET); peData->baseAddr = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Page alignment/size fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET); peData->pageSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Export tables fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET); peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data if(errno != SUCCESS) - { - return errno; - } + { return errno; } // Import tables fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); peData->peImportInfo.idtRVA = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } // TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort) fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET); peData->tlsAddr = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } peData->tlsSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } - if (peData->tlsAddr != 0 || peData->tlsSize != 0) - { - return ERR_UNSUPPORTED_STRUCTURE; - } + if(peData->tlsAddr != 0 || peData->tlsSize != 0) + { return ERR_UNSUPPORTED_STRUCTURE; } // Section info int ret = getSectionInfo(pe, &(peData->sections)); - if (ret != 0) - { - return ret; - } + if(ret != 0) + { return ret; } return SUCCESS; } diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 69f43c1..8f8b90f 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -52,69 +52,61 @@ int checkForBranchStub(FILE *pe, struct peData *peData) // Read in the set of instructions to check uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); - if (instructionBuffer == NULL) - { - return ERR_OUT_OF_MEM; - } + if(instructionBuffer == NULL) + { return ERR_OUT_OF_MEM; } memset(instructionBuffer, 0, sizeof(uint32_t) * 4); - if (fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) + if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) { - nullAndFree((void**)&instructionBuffer); + nullAndFree((void **)&instructionBuffer); return ERR_FILE_READ; } #ifdef LITTLE_ENDIAN_SYSTEM // Byteswap the instructions - for (uint8_t i = 0; i < 4; i++) - { - instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); - } + for(uint8_t i = 0; i < 4; i++) + { instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); } #endif // Check if each instruction matches uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; - if (((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && + if(((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) { - if (((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && + if(((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) && (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) { - if (((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && + if(((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) { - if (instructionBuffer[3] == expectedInstructions[3]) + if(instructionBuffer[3] == expectedInstructions[3]) { uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | (instructionBuffer[1] & 0x0000FFFF); - for (uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) + for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) { - if (peData->peImportInfo.tables[i].importCount == + if(peData->peImportInfo.tables[i].importCount == peData->peImportInfo.tables[i].branchStubCount) - { - continue; - } + { continue; } - for (uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) + for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) { - if (peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) - { - continue; - } + if(peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) + { continue; } - if (peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) + if(peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) { uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); - if (currentBranchStubRVA == 0) + if(currentBranchStubRVA == 0) { - nullAndFree((void**)&instructionBuffer); + nullAndFree((void **)&instructionBuffer); return ERR_INVALID_RVA_OR_OFFSET; } @@ -123,7 +115,7 @@ int checkForBranchStub(FILE *pe, struct peData *peData) peData->peImportInfo.tables[i].branchStubCount++; peData->peImportInfo.totalBranchStubCount++; - nullAndFree((void**)&instructionBuffer); + nullAndFree((void **)&instructionBuffer); return SUCCESS; } } @@ -133,7 +125,7 @@ int checkForBranchStub(FILE *pe, struct peData *peData) } } - nullAndFree((void**)&instructionBuffer); + nullAndFree((void **)&instructionBuffer); return fseek(pe, -12, SEEK_CUR) != 0 ? ERR_FILE_READ : SUCCESS; } @@ -143,57 +135,49 @@ int getImports(FILE *pe, struct peData *peData) memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t)); // If there is no IDT, just skip handling imports - if (peData->peImportInfo.idtRVA == 0) - { - return SUCCESS; - } + if(peData->peImportInfo.idtRVA == 0) + { return SUCCESS; } // Seek to the IDT and read the first entry uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections)); - if (idtOffset == 0) - { - return ERR_INVALID_RVA_OR_OFFSET; - } + if(idtOffset == 0) + { return ERR_INVALID_RVA_OR_OFFSET; } - if (fseek(pe, idtOffset, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, idtOffset, SEEK_SET) != 0) + { return ERR_FILE_READ; } uint32_t *currentIDT = malloc(5 * sizeof(uint32_t)); - if (currentIDT == NULL) - { - return ERR_OUT_OF_MEM; - } + if(currentIDT == NULL) + { return ERR_OUT_OF_MEM; } uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons - if (blankIDT == NULL) + if(blankIDT == NULL) { - nullAndFree((void**)¤tIDT); + nullAndFree((void **)¤tIDT); return ERR_OUT_OF_MEM; } - if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) + if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } // While the IDT is not blank, process it - for (uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) + for(uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++) { // Allocate space for the current table data peData->peImportInfo.tableCount++; peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable)); - if (peData->peImportInfo.tables == NULL) + if(peData->peImportInfo.tables == NULL) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_OUT_OF_MEM; } @@ -202,10 +186,8 @@ int getImports(FILE *pe, struct peData *peData) #ifdef BIG_ENDIAN_SYSTEM // Byteswap the IDT fields - for (uint8_t j = 0; j < 5; j++) - { - currentIDT[j] = __builtin_bswap32(currentIDT[j]); - } + for(uint8_t j = 0; j < 5; j++) + { currentIDT[j] = __builtin_bswap32(currentIDT[j]); } #endif @@ -213,76 +195,74 @@ int getImports(FILE *pe, struct peData *peData) uint32_t savedOffset = ftell(pe); uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections)); - if (tableNameOffset == 0) + if(tableNameOffset == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET; } - if (fseek(pe, tableNameOffset, SEEK_SET) != 0) + if(fseek(pe, tableNameOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } // Allocating is expensive, go 16 bytes at a time to avoid excessive realloc calls - for (uint32_t j = 0;; j += 16) + for(uint32_t j = 0;; j += 16) { peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char)); - if (peData->peImportInfo.tables[i].name == NULL) + if(peData->peImportInfo.tables[i].name == NULL) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_OUT_OF_MEM; } - if (fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) + if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } // Check for null terminator - for (uint32_t k = j; k < j + 16; k++) - if (peData->peImportInfo.tables[i].name[k] == '\0') - { - goto doneGettingTableName; - } + for(uint32_t k = j; k < j + 16; k++) + if(peData->peImportInfo.tables[i].name[k] == '\0') + { goto doneGettingTableName; } } doneGettingTableName: // Seek to the IAT and read the first entry uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); - if (iatOffset == 0) + if(iatOffset == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET; } - if (fseek(pe, iatOffset, SEEK_SET) != 0) + if(fseek(pe, iatOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } uint32_t currentImport; - if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } // While the import is not blank, process it - for (int j = 0; currentImport != 0; j++) + for(int j = 0; currentImport != 0; j++) { #ifdef BIG_ENDIAN_SYSTEM // Byteswap the import @@ -296,20 +276,20 @@ int getImports(FILE *pe, struct peData *peData) // Store the address of the current import entry in iatAddr uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); - if (currentImportRVA == 0) + if(currentImportRVA == 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_INVALID_RVA_OR_OFFSET; } peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA; // Read the next import - if (fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) + if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } } @@ -317,54 +297,47 @@ int getImports(FILE *pe, struct peData *peData) // Add table's import count to total and return to next IDT entry peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount; - if (fseek(pe, savedOffset, SEEK_SET) != 0) + if(fseek(pe, savedOffset, SEEK_SET) != 0) { - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); return ERR_FILE_READ; } // Read next IDT - if (fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) - { - return ERR_FILE_READ; - } + if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5) + { return ERR_FILE_READ; } } - nullAndFree((void**)¤tIDT); - nullAndFree((void**)&blankIDT); + nullAndFree((void **)¤tIDT); + nullAndFree((void **)&blankIDT); // Find the branch stubs - for (uint16_t i = 0; i < peData->sections.count; i++) + for(uint16_t i = 0; i < peData->sections.count; i++) { - if (peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) + if(peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) { break; // All branch stubs found } - if (!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) + if(!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) { continue; // Skip non-executable section } // Seek to the start of the current section - if (fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) + { return ERR_FILE_READ; } // While inside section and at least 4 instructions left (15 bytes) - while (ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) + while(ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) { int ret = checkForBranchStub(pe, peData); - if (ret != SUCCESS) - { - return ret; - } + if(ret != SUCCESS) + { return ret; } } } return SUCCESS; } - diff --git a/src/main.c b/src/main.c index bdf8183..4f2e786 100644 --- a/src/main.c +++ b/src/main.c @@ -116,7 +116,7 @@ void dispHelp(char **argv) void handleError(int ret) { - switch (ret) + switch(ret) { case ERR_UNKNOWN_DATA_REQUEST: printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); @@ -177,7 +177,7 @@ int main(int argc, char **argv) struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries)); struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders)); - if (offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || + if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL || optHeaderEntries == NULL || optHeaders == NULL) { printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); @@ -196,43 +196,43 @@ int main(int argc, char **argv) char *pePath = NULL; char *xexfilePath = NULL; - while ((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) + while((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) { - if (option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) + if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) { dispHelp(argv); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } - else if (option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) + else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) { dispVer(); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } - else if (option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) + else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) { dispLibs(); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return SUCCESS; } - else if (option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) + else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) { printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); skipMachineCheck = true; } - else if (option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) + else if(option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) { gotInput = true; pePath = malloc(strlen(optarg) + 1); - if (pePath == NULL) + if(pePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; @@ -240,15 +240,15 @@ int main(int argc, char **argv) strcpy(pePath, optarg); } - else if (option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) + else if(option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) { gotOutput = true; xexfilePath = malloc(strlen(optarg) + 1); - if (xexfilePath == NULL) + if(xexfilePath == NULL) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&pePath); + nullAndFree((void **)&pePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; @@ -256,31 +256,23 @@ int main(int argc, char **argv) strcpy(xexfilePath, optarg); } - else if (option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) + else if(option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) { - if (strcmp(optarg, "title") == 0) - { - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; - } - else if (strcmp(optarg, "titledll") == 0) - { - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; - } - else if (strcmp(optarg, "sysdll") == 0) - { - xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; - } - else if (strcmp(optarg, "dll") == 0) - { - xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; - } + if(strcmp(optarg, "title") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; } + else if(strcmp(optarg, "titledll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; } + else if(strcmp(optarg, "sysdll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; } + else if(strcmp(optarg, "dll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; } else { printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", PRINT_STEM, optarg); - nullAndFree((void**)&pePath); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&pePath); + nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; @@ -291,22 +283,18 @@ int main(int argc, char **argv) printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT); printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]); - if (!gotInput) + if(!gotInput) { - if (gotOutput) - { - nullAndFree((void**)&xexfilePath); - } + if(gotOutput) + { nullAndFree((void **)&xexfilePath); } printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); return -1; } - else if (!gotOutput) + else if(!gotOutput) { - if (gotInput) - { - nullAndFree((void**)&pePath); - } + if(gotInput) + { nullAndFree((void **)&pePath); } printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); return -1; @@ -314,23 +302,23 @@ int main(int argc, char **argv) FILE *pe = fopen(pePath, "rb"); - if (pe == NULL) + if(pe == NULL) { printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); - nullAndFree((void**)&pePath); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&pePath); + nullAndFree((void **)&xexfilePath); return -1; } - nullAndFree((void**)&pePath); + nullAndFree((void **)&pePath); FILE *xex = fopen(xexfilePath, "wb+"); - if (xex == NULL) + if(xex == NULL) { printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); fclose(pe); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); return -1; } @@ -341,10 +329,10 @@ int main(int argc, char **argv) printf("%s Validating PE file...\n", PRINT_STEM); - if (!validatePE(pe, skipMachineCheck)) + if(!validatePE(pe, skipMachineCheck)) { printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); @@ -357,10 +345,10 @@ int main(int argc, char **argv) printf("%s Retrieving header data from PE...\n", PRINT_STEM); ret = getHdrData(pe, peData, 0); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); @@ -373,10 +361,10 @@ int main(int argc, char **argv) printf("%s Retrieving import data from PE...\n", PRINT_STEM); ret = getImports(pe, peData); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); @@ -388,11 +376,11 @@ int main(int argc, char **argv) printf("%s Creating basefile from PE...\n", PRINT_STEM); char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char)); - if (!basefilePath) + if(!basefilePath) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - nullAndFree((void**)&xexfilePath); + nullAndFree((void **)&xexfilePath); fclose(pe); fclose(xex); return -1; @@ -402,10 +390,10 @@ int main(int argc, char **argv) strcat(basefilePath, ".basefile"); FILE *basefile = fopen(basefilePath, "wb+"); - nullAndFree((void**)&xexfilePath); - nullAndFree((void**)&basefilePath); + nullAndFree((void **)&xexfilePath); + nullAndFree((void **)&basefilePath); - if (!basefile) + if(!basefile) { printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -418,7 +406,7 @@ int main(int argc, char **argv) ret = mapPEToBasefile(pe, basefile, peData); fclose(pe); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -433,7 +421,7 @@ int main(int argc, char **argv) printf("%s Building security header...\n", PRINT_STEM); ret = setSecInfoHeader(secInfoHeader, peData); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -445,7 +433,7 @@ int main(int argc, char **argv) printf("%s Setting page descriptors...\n", PRINT_STEM); ret = setPageDescriptors(basefile, peData, secInfoHeader); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -460,7 +448,7 @@ int main(int argc, char **argv) printf("%s Building optional headers...\n", PRINT_STEM); ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -472,7 +460,7 @@ int main(int argc, char **argv) printf("%s Building XEX header...\n", PRINT_STEM); ret = setXEXHeader(xexHeader, optHeaderEntries, peData); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -488,7 +476,7 @@ int main(int argc, char **argv) printf("%s Aligning data...\n", PRINT_STEM); ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -504,7 +492,7 @@ int main(int argc, char **argv) printf("%s Writing XEX...\n", PRINT_STEM); ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -517,7 +505,7 @@ int main(int argc, char **argv) printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); ret = setHeaderSha1(xex); - if (ret != SUCCESS) + if(ret != SUCCESS) { handleError(ret); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index 4d9dfa7..8c2853c 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -31,135 +31,97 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo)); - if (!sectionInfo) - { - return ERR_OUT_OF_MEM; - } + if(!sectionInfo) + { return ERR_OUT_OF_MEM; } - if (fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) + { return ERR_FILE_READ; } - for (uint16_t i = 0; i < peData->numberOfSections; i++) + for(uint16_t i = 0; i < peData->numberOfSections; i++) { sectionInfo[i].virtualSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sectionInfo[i].rva = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sectionInfo[i].rawSize = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } sectionInfo[i].offset = get32BitFromPE(pe); if(errno != SUCCESS) - { - return errno; - } + { return errno; } - if (fseek(pe, 0x18, SEEK_CUR) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, 0x18, SEEK_CUR) != 0) + { return ERR_FILE_READ; } } - if (fseek(pe, 0, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, 0, SEEK_SET) != 0) + { return ERR_FILE_READ; } uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize); - if (!buffer) - { - return ERR_OUT_OF_MEM; - } + if(!buffer) + { return ERR_OUT_OF_MEM; } size_t totalHeader = peData->headerSize + peData->sectionTableSize; - if (fread(buffer, 1, totalHeader, pe) != totalHeader) - { - return ERR_FILE_READ; - } + if(fread(buffer, 1, totalHeader, pe) != totalHeader) + { return ERR_FILE_READ; } - if (fwrite(buffer, 1, totalHeader, basefile) != totalHeader) - { - return ERR_FILE_READ; - } + if(fwrite(buffer, 1, totalHeader, basefile) != totalHeader) + { return ERR_FILE_READ; } - for (uint16_t i = 0; i < peData->numberOfSections; i++) + for(uint16_t i = 0; i < peData->numberOfSections; i++) { buffer = realloc(buffer, sectionInfo[i].rawSize); - if (!buffer) - { - return ERR_OUT_OF_MEM; - } + if(!buffer) + { return ERR_OUT_OF_MEM; } - if (fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0) + { return ERR_FILE_READ; } - if (fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize) - { - return ERR_FILE_READ; - } + if(fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize) + { return ERR_FILE_READ; } - if (fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0) + { return ERR_FILE_READ; } - if (fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize) - { - return ERR_FILE_READ; - } + if(fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize) + { return ERR_FILE_READ; } } uint32_t currentOffset = ftell(basefile); uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1; - if (nextAligned != currentOffset) + if(nextAligned != currentOffset) { buffer = realloc(buffer, 1); - if (!buffer) - { - return ERR_OUT_OF_MEM; - } + if(!buffer) + { return ERR_OUT_OF_MEM; } buffer[0] = 0; - if (fseek(basefile, nextAligned, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(basefile, nextAligned, SEEK_SET) != 0) + { return ERR_FILE_READ; } - if (fwrite(buffer, 1, 1, basefile) != 1) - { - return ERR_FILE_READ; - } + if(fwrite(buffer, 1, 1, basefile) != 1) + { return ERR_FILE_READ; } } peData->size = ftell(basefile); - nullAndFree((void**)&buffer); - nullAndFree((void**)§ionInfo); + nullAndFree((void **)&buffer); + nullAndFree((void **)§ionInfo); return SUCCESS; } diff --git a/src/placer/placer.c b/src/placer/placer.c index 00c965f..86e38a8 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -30,15 +30,13 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea // Calloc because 0 values will be used to determine if a header is not present. offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); - if (offsets->optHeaders == NULL) - { - return ERR_OUT_OF_MEM; - } + if(offsets->optHeaders == NULL) + { return ERR_OUT_OF_MEM; } // Separate header iterator, i.e. one with it's data outwith the entries uint32_t sepHeader = 0; - for (uint32_t i = 0; i < optHeaderEntries->count; i++) + for(uint32_t i = 0; i < optHeaderEntries->count; i++) { *currentOffset = getNextAligned(*currentOffset, 0x8); @@ -84,16 +82,14 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries offsets->secInfoHeader = currentOffset; xexHeader->secInfoOffset = currentOffset; - currentOffset += (sizeof(struct secInfoHeader) - sizeof(void*)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); + currentOffset += (sizeof(struct secInfoHeader) - sizeof(void *)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); // Optional headers (minus imports) struct importLibIdcs importLibIdcs; int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs); - if (ret != SUCCESS) - { - return ret; - } + if(ret != SUCCESS) + { return ret; } currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports @@ -103,7 +99,7 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op xexHeader->peOffset = currentOffset; // Imports, the end of this header is aligned to the start of the basefile, so they are a special case - if (optHeaders->importLibraries.tableCount > 0) + if(optHeaders->importLibraries.tableCount > 0) { offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size; optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header]; diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index e7c3941..863c061 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -43,12 +43,10 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable)); - if (!importLibraries->importTables) - { - return ERR_OUT_OF_MEM; - } + if(!importLibraries->importTables) + { return ERR_OUT_OF_MEM; } - if (peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) + if(peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) { fprintf(stderr, "ERROR: Invalid tableCount = %d\n", peImportInfo->tableCount); return ERR_OUT_OF_MEM; @@ -56,19 +54,17 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn struct importTable *importTables = importLibraries->importTables; - importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void*)); + importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void *)); int ret = ERR_INVALID_IMPORT_NAME; // Allocate name list char **names = calloc(importLibraries->tableCount, sizeof(char *)); - if (!names) - { - goto cleanup_tables; - } + if(!names) + { goto cleanup_tables; } - for (int64_t i = importLibraries->tableCount - 1; i >= 0; i--) + for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--) { importTables[i].tableIndex = i; @@ -85,7 +81,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); names[i] = strtok(peImportInfo->tables[i].name, "@"); - if (!peImportInfo->tables[i].name) + if(!peImportInfo->tables[i].name) { fprintf(stderr, "ERROR: tables[%d].name is NULL\n", i); goto cleanup_names_invalid; @@ -93,41 +89,29 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn targetBuildVerStr = strtok(NULL, "."); - if (!targetBuildVerStr) - { - goto cleanup_names_invalid; - } + if(!targetBuildVerStr) + { goto cleanup_names_invalid; } - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) - { - goto cleanup_names_invalid; - } + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) + { goto cleanup_names_invalid; } buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == targetBuildVerStr) - { - goto cleanup_names_invalid; - } + if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr) + { goto cleanup_names_invalid; } targetHotfixVerStr = strtok(NULL, "+"); - if (!targetHotfixVerStr) - { - goto cleanup_names_invalid; - } + if(!targetHotfixVerStr) + { goto cleanup_names_invalid; } - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) - { - goto cleanup_names_invalid; - } + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen) + { goto cleanup_names_invalid; } hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) - { - goto cleanup_names_invalid; - } + if(*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) + { goto cleanup_names_invalid; } importTables[i].targetVer = ((majorVer & 0xF) << 28) | @@ -137,58 +121,40 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn minimumBuildVerStr = strtok(NULL, "."); - if (!minimumBuildVerStr) - { - goto cleanup_names_invalid; - } + if(!minimumBuildVerStr) + { goto cleanup_names_invalid; } - if (strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + 1 + strlen(minimumBuildVerStr) == oldNameLen) - { - goto cleanup_names_invalid; - } + { goto cleanup_names_invalid; } buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) - { - goto cleanup_names_invalid; - } + if(*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) + { goto cleanup_names_invalid; } minimumHotfixVerStr = strtok(NULL, "\0"); - if (!minimumHotfixVerStr) - { - goto cleanup_names_invalid; - } + if(!minimumHotfixVerStr) + { goto cleanup_names_invalid; } hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10); - if (*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) - { - goto cleanup_names_invalid; - } + if(*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) + { goto cleanup_names_invalid; } importTables[i].minimumVer = ((majorVer & 0xF) << 28) | ((minorVer & 0xF) << 24); - if (strcmp(names[i], "xboxkrnl.exe") == 0) - { - importTables[i].unknown = 0x45DC17E0; - } - else if (strcmp(names[i], "xam.xex") == 0) - { - importTables[i].unknown = 0xFCA15C76; - } - else if (strcmp(names[i], "xbdm.xex") == 0) - { - importTables[i].unknown = 0xECEB8109; - } + if(strcmp(names[i], "xboxkrnl.exe") == 0) + { importTables[i].unknown = 0x45DC17E0; } + else if(strcmp(names[i], "xam.xex") == 0) + { importTables[i].unknown = 0xFCA15C76; } + else if(strcmp(names[i], "xbdm.xex") == 0) + { importTables[i].unknown = 0xECEB8109; } else - { - goto cleanup_names_invalid; - } + { goto cleanup_names_invalid; } importTables[i].addressCount = (peImportInfo->tables[i].branchStubCount * 2) + @@ -196,35 +162,29 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); - if (!importTables[i].addresses) - { - goto cleanup_names_invalid; - } + if(!importTables[i].addresses) + { goto cleanup_names_invalid; } uint32_t *addresses = importTables[i].addresses; uint16_t currentAddr = 0; - for (uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) + for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { - if (currentAddr >= importTables[i].addressCount) - { - goto cleanup_names_invalid; - } + if(currentAddr >= importTables[i].addressCount) + { goto cleanup_names_invalid; } addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr; - if (peImportInfo->tables[i].imports[j].branchStubAddr != 0) + if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) { - if (currentAddr >= importTables[i].addressCount) - { - goto cleanup_names_invalid; - } + if(currentAddr >= importTables[i].addressCount) + { goto cleanup_names_invalid; } addresses[currentAddr++] = peImportInfo->tables[i].imports[j].branchStubAddr; } } - importTables[i].size = (sizeof(struct importTable) - sizeof(void*) + (importTables[i].addressCount * sizeof(uint32_t))); + importTables[i].size = (sizeof(struct importTable) - sizeof(void *) + (importTables[i].addressCount * sizeof(uint32_t))); importLibraries->size += importTables[i].size; struct sha1_ctx shaContext; @@ -240,15 +200,13 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - for (uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } + for(uint16_t j = 0; j < addressCount; j++) + { addresses[j] = __builtin_bswap32(addresses[j]); } #endif - sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void*) - sizeof(uint32_t), (void*)&importTables[i] + sizeof(uint32_t)); - sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void*)addresses); + sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void *) - sizeof(uint32_t), (void *)&importTables[i] + sizeof(uint32_t)); + sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void *)addresses); #ifdef LITTLE_ENDIAN_SYSTEM importTables[i].size = __builtin_bswap32(importTables[i].size); @@ -257,10 +215,8 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); - for (uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } + for(uint16_t j = 0; j < addressCount; j++) + { addresses[j] = __builtin_bswap32(addresses[j]); } #endif @@ -270,12 +226,10 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn // Allocate offset table uint32_t *nameOffsets = calloc(importLibraries->tableCount, sizeof(uint32_t)); - if (!nameOffsets) - { - goto cleanup_names; - } + if(!nameOffsets) + { goto cleanup_names; } - for (uint32_t i = 0; i < importLibraries->tableCount; i++) + for(uint32_t i = 0; i < importLibraries->tableCount; i++) { nameOffsets[i] = importLibraries->nameTableSize; importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t)); @@ -284,35 +238,29 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importLibraries->size += importLibraries->nameTableSize; importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char)); - if (!importLibraries->nameTable) - { - goto cleanup_offsets; - } + if(!importLibraries->nameTable) + { goto cleanup_offsets; } char *nameTable = importLibraries->nameTable; - for (uint32_t i = 0; i < importLibraries->tableCount; i++) - { - strcpy(&(nameTable[nameOffsets[i]]), names[i]); - } + for(uint32_t i = 0; i < importLibraries->tableCount; i++) + { strcpy(&(nameTable[nameOffsets[i]]), names[i]); } - nullAndFree((void**)&nameOffsets); - nullAndFree((void**)&names); + nullAndFree((void **)&nameOffsets); + nullAndFree((void **)&names); return SUCCESS; cleanup_offsets: - nullAndFree((void**)&nameOffsets); + nullAndFree((void **)&nameOffsets); cleanup_names: - for (uint32_t i = 0; i < importLibraries->tableCount; i++) - { - nullAndFree((void**)&(importTables[i].addresses)); - } + for(uint32_t i = 0; i < importLibraries->tableCount; i++) + { nullAndFree((void **) & (importTables[i].addresses)); } cleanup_names_invalid: - nullAndFree((void**)&names); + nullAndFree((void **)&names); cleanup_tables: - nullAndFree((void**)&(importLibraries->importTables)); + nullAndFree((void **) & (importLibraries->importTables)); importLibraries->importTables = NULL; return ret; } @@ -320,10 +268,8 @@ cleanup_tables: // STUB. TODO: Dynamically select, and/or allow user to select, flags void setSysFlags(uint32_t *flags) { - if (flags == NULL) - { - return; - } + if(flags == NULL) + { return; } *flags = XEX_SYS_GAMEPAD_DISCONNECT | XEX_SYS_INSECURE_SOCKETS | @@ -338,17 +284,13 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st optHeaderEntries->count = 4; - if (importsPresent) - { - optHeaderEntries->count++; - } + if(importsPresent) + { optHeaderEntries->count++; } optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry)); - if (optHeaderEntries->optHeaderEntry == NULL) - { - return ERR_OUT_OF_MEM; - } + if(optHeaderEntries->optHeaderEntry == NULL) + { return ERR_OUT_OF_MEM; } uint32_t currentHeader = 0; @@ -365,15 +307,13 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st currentHeader++; // Import libraries (0x103FF) - if (importsPresent) + if(importsPresent) { optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS; int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader); - if (ret != SUCCESS) - { - return ret; - } + if(ret != SUCCESS) + { return ret; } currentHeader++; } diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index cc1afcf..0651896 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -23,11 +23,9 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3 uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; uint32_t currentOffset = page * pageSize; - for (int32_t i = peData->sections.count - 1; i >= 0; i--) - if (currentOffset >= peData->sections.section[i].rva) - { - return peData->sections.section[i].permFlag; - } + for(int32_t i = peData->sections.count - 1; i >= 0; i--) + if(currentOffset >= peData->sections.section[i].rva) + { return peData->sections.section[i].permFlag; } return XEX_SECTION_RODATA | 0b10000; // We're in the PE header, so RODATA } @@ -38,35 +36,29 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); - if (!secInfoHeader->descriptors) - { - return ERR_OUT_OF_MEM; - } + if(!secInfoHeader->descriptors) + { return ERR_OUT_OF_MEM; } struct pageDescriptor *descriptors = secInfoHeader->descriptors; - for (int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) + for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) { descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); struct sha1_ctx shaContext; sha1_init(&shaContext); - if (fseek(pe, i * pageSize, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + if(fseek(pe, i * pageSize, SEEK_SET) != 0) + { return ERR_FILE_READ; } uint8_t *page = malloc(pageSize); - if (!page) - { - return ERR_OUT_OF_MEM; - } + if(!page) + { return ERR_OUT_OF_MEM; } - if (fread(page, 1, pageSize, pe) != pageSize) + if(fread(page, 1, pageSize, pe) != pageSize) { - nullAndFree((void**)&page); + nullAndFree((void **)&page); return ERR_FILE_READ; } @@ -75,24 +67,19 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se #endif sha1_update(&shaContext, pageSize, page); - sha1_update(&shaContext, 0x18, (uint8_t*)&descriptors[i]); + sha1_update(&shaContext, 0x18, (uint8_t *)&descriptors[i]); #ifdef LITTLE_ENDIAN_SYSTEM descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif - if (i != 0) - { - sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); - } + if(i != 0) + { sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); } else - { - sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); - } + { sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); } - nullAndFree((void**)&page); + nullAndFree((void **)&page); } return SUCCESS; } - diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 1cb3f1c..9de77e6 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -25,9 +25,9 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader // Module flags (type of executable) // Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead - if (xexHeader->moduleFlags == 0) + if(xexHeader->moduleFlags == 0) { - if (peData->characteristics & PE_CHAR_FLAG_DLL) + if(peData->characteristics & PE_CHAR_FLAG_DLL) { xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL } @@ -36,7 +36,7 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title } - if (peData->peExportInfo.count > 0) + if(peData->peExportInfo.count > 0) { xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions } @@ -65,7 +65,7 @@ int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE; secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type. secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages) - secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void*)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) + secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void *)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) return SUCCESS; } diff --git a/src/write/headerhash.c b/src/write/headerhash.c index 6779919..f430023 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -23,28 +23,20 @@ int setHeaderSha1(FILE *xex) { if(fseek(xex, 0x8, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + { return ERR_FILE_READ; } uint32_t basefileOffset = get32BitFromXEX(xex); if(errno != SUCCESS) - { - return errno; - } + { return errno; } if(fseek(xex, 0x10, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + { return ERR_FILE_READ; } uint32_t secInfoOffset = get32BitFromXEX(xex); if(errno != SUCCESS) - { - return errno; - } + { return errno; } uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; uint32_t remainingSize = basefileOffset - endOfImageInfo; @@ -56,66 +48,57 @@ int setHeaderSha1(FILE *xex) uint8_t *remainderOfHeaders = malloc(remainingSize); if(!remainderOfHeaders) - { - return ERR_OUT_OF_MEM; - } + { return ERR_OUT_OF_MEM; } memset(remainderOfHeaders, 0, remainingSize); if(fseek(xex, endOfImageInfo, SEEK_SET) != 0) { - nullAndFree((void**)&remainderOfHeaders); + nullAndFree((void **)&remainderOfHeaders); return ERR_FILE_READ; } if(fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize) { - nullAndFree((void**)&remainderOfHeaders); + nullAndFree((void **)&remainderOfHeaders); return ERR_FILE_READ; } sha1_update(&shaContext, remainingSize, remainderOfHeaders); - nullAndFree((void**)&remainderOfHeaders); + nullAndFree((void **)&remainderOfHeaders); uint32_t headersLen = secInfoOffset + 0x8; uint8_t *headersStart = malloc(headersLen); if(!headersStart) - { - return ERR_OUT_OF_MEM; - } + { return ERR_OUT_OF_MEM; } memset(headersStart, 0, headersLen); if(fseek(xex, 0, SEEK_SET) != 0) { - nullAndFree((void**)&headersStart); + nullAndFree((void **)&headersStart); return ERR_FILE_READ; } if(fread(headersStart, 1, headersLen, xex) != headersLen) { - nullAndFree((void**)&headersStart); + nullAndFree((void **)&headersStart); return ERR_FILE_READ; } sha1_update(&shaContext, headersLen, headersStart); - nullAndFree((void**)&headersStart); + nullAndFree((void **)&headersStart); uint8_t headerHash[20]; memset(headerHash, 0, sizeof(headerHash)); sha1_digest(&shaContext, 20, headerHash); if(fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) - { - return ERR_FILE_READ; - } + { return ERR_FILE_READ; } if(fwrite(headerHash, 1, 20, xex) != 20) - { - return ERR_FILE_READ; - } + { return ERR_FILE_READ; } return SUCCESS; } - diff --git a/src/write/writexex.c b/src/write/writexex.c index 8af1af0..21a5e4d 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -48,12 +48,10 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fseek(xex, offsets->optHeaderEntries, SEEK_SET); for(int i = 0; i < optHeaderEntries->count; i++) - { - fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); - } + { fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); } // Page descriptors - fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET); + fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void *), SEEK_SET); // So we don't try to dereference an unaligned pointer struct pageDescriptor *descriptors = secInfoHeader->descriptors; @@ -77,9 +75,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr uint8_t *buffer = malloc(readBufSize * sizeof(uint8_t)); if(buffer == NULL) - { - return ERR_OUT_OF_MEM; - } + { return ERR_OUT_OF_MEM; } for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize) { @@ -89,7 +85,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe); - if (readRet != bytesToRead) + if(readRet != bytesToRead) { fprintf(stderr, "Error: fread failed at offset %u (read %zu of %zu bytes)\n", i, readRet, bytesToRead); break; @@ -97,14 +93,14 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex); - if (writeRet != readRet) + if(writeRet != readRet) { fprintf(stderr, "Error: fwrite failed at offset %u (wrote %zu of %zu bytes)\n", i, writeRet, readRet); break; } } - nullAndFree((void**)&buffer); + nullAndFree((void **)&buffer); // Security Info #ifdef LITTLE_ENDIAN_SYSTEM @@ -122,7 +118,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr #endif fseek(xex, offsets->secInfoHeader, SEEK_SET); - fwrite(secInfoHeader, sizeof(uint8_t), sizeof(struct secInfoHeader) - sizeof(void*), xex); // sizeof(void*) == size of page descriptor pointer at end + fwrite(secInfoHeader, sizeof(uint8_t), sizeof(struct secInfoHeader) - sizeof(void *), xex); // sizeof(void*) == size of page descriptor pointer at end // Optional headers uint32_t currentHeader = 0; @@ -162,7 +158,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr optHeaders->importLibraries.tableCount = __builtin_bswap32(optHeaders->importLibraries.tableCount); #endif - fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void*)), xex); + fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void *)), xex); fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex); #ifdef LITTLE_ENDIAN_SYSTEM @@ -187,13 +183,11 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); for(uint16_t j = 0; j < addressCount; j++) - { - addresses[j] = __builtin_bswap32(addresses[j]); - } + { addresses[j] = __builtin_bswap32(addresses[j]); } #endif - fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void*), xex); + fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void *), xex); fwrite(addresses, sizeof(uint32_t), addressCount, xex); } From f6610667ae65117463ba675c8beb20d7abe23b81 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 16 Jun 2025 22:06:31 +0100 Subject: [PATCH 25/38] Remove prints from everywhere but main, fix bug where only part of an import library version was set --- src/main.c | 18 +++++++++--------- src/setdata/optheaders.c | 14 +++++--------- src/write/writexex.c | 10 ++-------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/main.c b/src/main.c index 4f2e786..b23c232 100644 --- a/src/main.c +++ b/src/main.c @@ -119,39 +119,39 @@ void handleError(int ret) switch(ret) { case ERR_UNKNOWN_DATA_REQUEST: - printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); break; case ERR_FILE_READ: - printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); break; case ERR_OUT_OF_MEM: - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); break; case ERR_MISSING_SECTION_FLAG: - printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); break; case ERR_UNSUPPORTED_STRUCTURE: - printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); break; case ERR_INVALID_RVA_OR_OFFSET: - printf("%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); break; case ERR_INVALID_IMPORT_NAME: - printf("%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); break; case ERR_DATA_OVERFLOW: - printf("%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); break; default: - printf("%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + fprintf(stderr, "%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); break; } } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 863c061..5668fdd 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -47,10 +47,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn { return ERR_OUT_OF_MEM; } if(peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) - { - fprintf(stderr, "ERROR: Invalid tableCount = %d\n", peImportInfo->tableCount); - return ERR_OUT_OF_MEM; - } + { return ERR_OUT_OF_MEM; } struct importTable *importTables = importLibraries->importTables; @@ -82,10 +79,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn names[i] = strtok(peImportInfo->tables[i].name, "@"); if(!peImportInfo->tables[i].name) - { - fprintf(stderr, "ERROR: tables[%d].name is NULL\n", i); - goto cleanup_names_invalid; - } + { goto cleanup_names_invalid; } targetBuildVerStr = strtok(NULL, "."); @@ -145,7 +139,9 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].minimumVer = ((majorVer & 0xF) << 28) | - ((minorVer & 0xF) << 24); + ((minorVer & 0xF) << 24) | + (buildVer << 8) | + hotfixVer; if(strcmp(names[i], "xboxkrnl.exe") == 0) { importTables[i].unknown = 0x45DC17E0; } diff --git a/src/write/writexex.c b/src/write/writexex.c index 21a5e4d..a3b780d 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -86,18 +86,12 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe); if(readRet != bytesToRead) - { - fprintf(stderr, "Error: fread failed at offset %u (read %zu of %zu bytes)\n", i, readRet, bytesToRead); - break; - } + { break; } size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex); if(writeRet != readRet) - { - fprintf(stderr, "Error: fwrite failed at offset %u (wrote %zu of %zu bytes)\n", i, writeRet, readRet); - break; - } + { break; } } nullAndFree((void **)&buffer); From 3c46c6cf74ce1bf4b3602f0c1330aa289b6cac2d Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sat, 21 Jun 2025 21:46:43 +0100 Subject: [PATCH 26/38] Restore comments which were accidentally deleted during reformatting --- src/setdata/optheaders.c | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 5668fdd..2a1fb64 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -38,6 +38,7 @@ void setTLSInfo(struct tlsInfo *tlsInfo) int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportInfo *peImportInfo, struct secInfoHeader *secInfoHeader) { + // Set table count and allocate enough memory for all tables importLibraries->tableCount = peImportInfo->tableCount; secInfoHeader->importTableCount = peImportInfo->tableCount; @@ -49,8 +50,10 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn if(peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535) { return ERR_OUT_OF_MEM; } + // Use this to avoid dereferencing an unaligned pointer struct importTable *importTables = importLibraries->importTables; + // Initialise the size of the import libraries to just the size of the header (- 2 * sizeof(void*) to exclude addresses for internal use only) importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void *)); int ret = ERR_INVALID_IMPORT_NAME; @@ -61,10 +64,15 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn if(!names) { goto cleanup_tables; } + // Populate each table, then compute it's hash and store in the previous table for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--) { + // Set the table index field to the current index importTables[i].tableIndex = i; + // Extract the name, target, and minimum versions from the name string + // Major and minor version are always 2 and 0, respectively + // Strings are definitely null-terminated as otherwise they couldn't have been read in const uint8_t majorVer = 2; const uint8_t minorVer = 0; char *targetBuildVerStr = NULL; @@ -75,24 +83,26 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn uint8_t hotfixVer = 0; char *strtoulRet = NULL; + // Get the name uint32_t oldNameLen = strlen(peImportInfo->tables[i].name); names[i] = strtok(peImportInfo->tables[i].name, "@"); if(!peImportInfo->tables[i].name) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // Encountered '\0', not '@' + // Target versions first targetBuildVerStr = strtok(NULL, "."); if(!targetBuildVerStr) { goto cleanup_names_invalid; } if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // Encountered null terminator instead of '.' buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10); if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // Encountered a non-number, or string was empty targetHotfixVerStr = strtok(NULL, "+"); @@ -107,25 +117,27 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn if(*strtoulRet != 0 || strtoulRet == targetHotfixVerStr) { goto cleanup_names_invalid; } + // Now pack these into the target version bitfield importTables[i].targetVer = ((majorVer & 0xF) << 28) | ((minorVer & 0xF) << 24) | (buildVer << 8) | hotfixVer; + // Now onto the minimum versions, this works much the same minimumBuildVerStr = strtok(NULL, "."); if(!minimumBuildVerStr) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // No more tokens if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) + 1 + strlen(minimumBuildVerStr) == oldNameLen) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // Encountered null terminator instead of '.' buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10); if(*strtoulRet != 0 || strtoulRet == minimumBuildVerStr) - { goto cleanup_names_invalid; } + { goto cleanup_names_invalid; } // Encountered a non-number, or string was empty minimumHotfixVerStr = strtok(NULL, "\0"); @@ -137,12 +149,14 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn if(*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr) { goto cleanup_names_invalid; } + // Now pack these into the minimum version bitfield importTables[i].minimumVer = ((majorVer & 0xF) << 28) | ((minorVer & 0xF) << 24) | (buildVer << 8) | hotfixVer; + // Hardcode a currently unknown value. TODO: find out how this is calculated. if(strcmp(names[i], "xboxkrnl.exe") == 0) { importTables[i].unknown = 0x45DC17E0; } else if(strcmp(names[i], "xam.xex") == 0) @@ -152,18 +166,21 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn else { goto cleanup_names_invalid; } + // Determine the number of addresses (2 for functions, 1 for everything else) importTables[i].addressCount = (peImportInfo->tables[i].branchStubCount * 2) + (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + // Allocate enough memory for the addresses importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); if(!importTables[i].addresses) { goto cleanup_names_invalid; } - uint32_t *addresses = importTables[i].addresses; + uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer uint16_t currentAddr = 0; + // Populate the addresses for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++) { if(currentAddr >= importTables[i].addressCount) @@ -180,15 +197,20 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn } } + // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) importTables[i].size = (sizeof(struct importTable) - sizeof(void *) + (importTables[i].addressCount * sizeof(uint32_t))); importLibraries->size += importTables[i].size; + // Init sha1 hash struct sha1_ctx shaContext; memset(&shaContext, 0, sizeof(shaContext)); sha1_init(&shaContext); + // On little endian this ensures the byteswapped address count doesn't cause any trouble when using it. + // On big endian it's pointless but here so the code isn't too complex with differences between endianness. uint16_t addressCount = importTables[i].addressCount; + // If we're on a little endian system, swap everything into big endian for hashing #ifdef LITTLE_ENDIAN_SYSTEM importTables[i].size = __builtin_bswap32(importTables[i].size); importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); @@ -196,14 +218,17 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + // Byteswap the addresses for(uint16_t j = 0; j < addressCount; j++) { addresses[j] = __builtin_bswap32(addresses[j]); } #endif - + // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) + // +/- sizeof(uint32_t) to exclude table size from hash sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void *) - sizeof(uint32_t), (void *)&importTables[i] + sizeof(uint32_t)); sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void *)addresses); + // If we're on a little endian system, swap everything back into little endian #ifdef LITTLE_ENDIAN_SYSTEM importTables[i].size = __builtin_bswap32(importTables[i].size); importTables[i].unknown = __builtin_bswap32(importTables[i].unknown); @@ -211,6 +236,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer); importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount); + // Byteswap the addresses for(uint16_t j = 0; j < addressCount; j++) { addresses[j] = __builtin_bswap32(addresses[j]); } @@ -237,8 +263,10 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn if(!importLibraries->nameTable) { goto cleanup_offsets; } + // Use this to avoid dereferencing an unaligned pointer char *nameTable = importLibraries->nameTable; + // Populate the name table for(uint32_t i = 0; i < importLibraries->tableCount; i++) { strcpy(&(nameTable[nameOffsets[i]]), names[i]); } From f505302e7133d64b12b4b28c64c8fa7601b897de Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 24 Jun 2025 18:20:43 +0100 Subject: [PATCH 27/38] Change the way versioning works, fix memory leaks --- CMakeLists.txt | 34 ++++++++++--- src/common/common.h | 19 +++---- src/main.c | 94 ++++++++++++++++++----------------- src/setdata/populateheaders.c | 2 +- 4 files changed, 83 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 678229a..7fca87a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,19 @@ file(GLOB_RECURSE allsources add_executable(synthxex ${allsources}) target_include_directories(synthxex PRIVATE ${CMAKE_SOURCE_DIR}/include) -if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) +# Debug/release build handling +if(GENERATOR_IS_MULTI_CONFIG) + set(SYNTHXEX_BUILD_TYPE "MultiConf") # Multi-config generators handle debug build options themselves, don't apply ours +else() + if(CMAKE_BUILD_TYPE) + set(SYNTHXEX_BUILD_TYPE ${CMAKE_BUILD_TYPE}) + else() + set(SYNTHXEX_BUILD_TYPE "Debug") + endif() +endif() + +if(${SYNTHXEX_BUILD_TYPE} MATCHES "Deb") + add_compile_definitions(_DEBUG=1) if(NOT MINGW) target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address -fsanitize=undefined) @@ -46,15 +58,21 @@ if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) else() target_compile_options(synthxex PRIVATE -O0 -g) endif() +endif() - execute_process( - COMMAND git rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) +# Set the version +execute_process( + COMMAND git describe HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE +) - add_compile_definitions(GIT_COMMIT="${GIT_COMMIT}") +# Only use the result from the git command if it's a valid version string +if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-[0-9a-z]+)?$") + add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}") +else() + add_compile_definitions(SYNTHXEX_VERSION="v0.0.3") # Only used as a fallback endif() # Setting install target settings... diff --git a/src/common/common.h b/src/common/common.h index d60d9d0..9fb9f2d 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -29,19 +29,18 @@ #define freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse free #pragma GCC poison free -// Program identifiers -#define NAME "SynthXEX" -#define VERSION "v0.0.4" -#define COPYRIGHT "2024-2025" - -#ifdef GIT_COMMIT - #define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT +// Program identifiers (version set in CMakeLists.txt) +#if defined(DEBUG) || defined(_DEBUG) + #define SYNTHXEX_NAME "SynthXEX-Debug" #else - #define VERSION_STRING NAME " " VERSION + #define SYNTHXEX_NAME "SynthXEX" #endif +#define SYNTHXEX_COPYRIGHT "2024-2025" +#define SYNTHXEX_VERSION_STRING SYNTHXEX_NAME " " SYNTHXEX_VERSION + // Print constants -#define PRINT_STEM "SynthXEX>" +#define SYNTHXEX_PRINT_STEM SYNTHXEX_NAME ">" // Return values #define SUCCESS 0 @@ -55,5 +54,3 @@ #define ERR_INVALID_RVA_OR_OFFSET -7 #define ERR_INVALID_IMPORT_NAME -8 #define ERR_DATA_OVERFLOW -9 - -void infoPrint(char *str); diff --git a/src/main.c b/src/main.c index b23c232..fa39ff1 100644 --- a/src/main.c +++ b/src/main.c @@ -90,8 +90,8 @@ void dispLibs() void dispVer() { - printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", VERSION_STRING); - printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", COPYRIGHT); + printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", SYNTHXEX_VERSION_STRING); + printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", SYNTHXEX_COPYRIGHT); printf("it under the terms of the GNU Affero General Public License as published by\n"); printf("the Free Software Foundation, either version 3 of the License, or\n"); printf("(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\n"); @@ -119,39 +119,39 @@ void handleError(int ret) switch(ret) { case ERR_UNKNOWN_DATA_REQUEST: - fprintf(stderr, "%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_FILE_READ: - fprintf(stderr, "%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Failed to read data from PE file. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_OUT_OF_MEM: - fprintf(stderr, "%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_MISSING_SECTION_FLAG: - fprintf(stderr, "%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: R/W/X flag missing from PE section. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_UNSUPPORTED_STRUCTURE: - fprintf(stderr, "%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_INVALID_RVA_OR_OFFSET: - fprintf(stderr, "%s ERROR: Invalid RVA or offset found. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Invalid RVA or offset found. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_INVALID_IMPORT_NAME: - fprintf(stderr, "%s ERROR: Invalid import name found. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Invalid import name found. Aborting.\n", SYNTHXEX_PRINT_STEM); break; case ERR_DATA_OVERFLOW: - fprintf(stderr, "%s ERROR: Data overflow. Aborting.\n", PRINT_STEM); + fprintf(stderr, "%s ERROR: Data overflow. Aborting.\n", SYNTHXEX_PRINT_STEM); break; default: - fprintf(stderr, "%s ERROR: Unknown error: %d. Aborting.\n", PRINT_STEM, ret); + fprintf(stderr, "%s ERROR: Unknown error: %d. Aborting.\n", SYNTHXEX_PRINT_STEM, ret); break; } } @@ -180,7 +180,7 @@ int main(int argc, char **argv) if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL || peData == NULL || optHeaderEntries == NULL || optHeaders == NULL) { - printf("%s ERROR: Out of memory. Aborting\n", PRINT_STEM); + printf("%s ERROR: Out of memory. Aborting\n", SYNTHXEX_PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; @@ -221,7 +221,7 @@ int main(int argc, char **argv) } else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) { - printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM); + printf("%s WARNING: Skipping machine ID check.\n", SYNTHXEX_PRINT_STEM); skipMachineCheck = true; } else if(option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) @@ -231,7 +231,7 @@ int main(int argc, char **argv) if(pePath == NULL) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); nullAndFree((void **)&xexfilePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -247,7 +247,7 @@ int main(int argc, char **argv) if(xexfilePath == NULL) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); nullAndFree((void **)&pePath); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); @@ -269,7 +269,7 @@ int main(int argc, char **argv) else { printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", - PRINT_STEM, optarg); + SYNTHXEX_PRINT_STEM, optarg); nullAndFree((void **)&pePath); nullAndFree((void **)&xexfilePath); @@ -280,15 +280,16 @@ int main(int argc, char **argv) } } - printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT); - printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]); + printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", SYNTHXEX_PRINT_STEM, SYNTHXEX_VERSION_STRING, SYNTHXEX_COPYRIGHT); + printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", SYNTHXEX_PRINT_STEM, argv[0]); if(!gotInput) { if(gotOutput) { nullAndFree((void **)&xexfilePath); } - printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + printf("%s ERROR: PE input expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM); return -1; } else if(!gotOutput) @@ -296,7 +297,8 @@ int main(int argc, char **argv) if(gotInput) { nullAndFree((void **)&pePath); } - printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + printf("%s ERROR: XEX file output expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM); return -1; } @@ -304,9 +306,10 @@ int main(int argc, char **argv) if(pe == NULL) { - printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM); + printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", SYNTHXEX_PRINT_STEM); nullAndFree((void **)&pePath); nullAndFree((void **)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } @@ -316,9 +319,10 @@ int main(int argc, char **argv) if(xex == NULL) { - printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM); + printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", SYNTHXEX_PRINT_STEM); fclose(pe); nullAndFree((void **)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } @@ -327,38 +331,36 @@ int main(int argc, char **argv) int ret = 0; - printf("%s Validating PE file...\n", PRINT_STEM); + printf("%s Validating PE file...\n", SYNTHXEX_PRINT_STEM); if(!validatePE(pe, skipMachineCheck)) { - printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM); + printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", SYNTHXEX_PRINT_STEM); nullAndFree((void **)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); return -1; } - printf("%s PE valid!\n", PRINT_STEM); + printf("%s PE valid!\n", SYNTHXEX_PRINT_STEM); - printf("%s Retrieving header data from PE...\n", PRINT_STEM); + printf("%s Retrieving header data from PE...\n", SYNTHXEX_PRINT_STEM); ret = getHdrData(pe, peData, 0); if(ret != SUCCESS) { handleError(ret); nullAndFree((void **)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); return -1; } - printf("%s Got header data from PE!\n", PRINT_STEM); + printf("%s Got header data from PE!\n", SYNTHXEX_PRINT_STEM); - printf("%s Retrieving import data from PE...\n", PRINT_STEM); + printf("%s Retrieving import data from PE...\n", SYNTHXEX_PRINT_STEM); ret = getImports(pe, peData); if(ret != SUCCESS) @@ -371,14 +373,14 @@ int main(int argc, char **argv) return -1; } - printf("%s Got import data from PE!\n", PRINT_STEM); + printf("%s Got import data from PE!\n", SYNTHXEX_PRINT_STEM); - printf("%s Creating basefile from PE...\n", PRINT_STEM); + printf("%s Creating basefile from PE...\n", SYNTHXEX_PRINT_STEM); char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char)); if(!basefilePath) { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); nullAndFree((void **)&xexfilePath); fclose(pe); @@ -395,7 +397,7 @@ int main(int argc, char **argv) if(!basefile) { - printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM); + printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", SYNTHXEX_PRINT_STEM); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); fclose(pe); fclose(xex); @@ -415,10 +417,10 @@ int main(int argc, char **argv) return -1; } - printf("%s Created basefile!\n", PRINT_STEM); + printf("%s Created basefile!\n", SYNTHXEX_PRINT_STEM); // Setting final XEX data structs - printf("%s Building security header...\n", PRINT_STEM); + printf("%s Building security header...\n", SYNTHXEX_PRINT_STEM); ret = setSecInfoHeader(secInfoHeader, peData); if(ret != SUCCESS) @@ -430,7 +432,7 @@ int main(int argc, char **argv) return -1; } - printf("%s Setting page descriptors...\n", PRINT_STEM); + printf("%s Setting page descriptors...\n", SYNTHXEX_PRINT_STEM); ret = setPageDescriptors(basefile, peData, secInfoHeader); if(ret != SUCCESS) @@ -445,7 +447,7 @@ int main(int argc, char **argv) // Done with this now freeSectionsStruct(&(peData->sections)); - printf("%s Building optional headers...\n", PRINT_STEM); + printf("%s Building optional headers...\n", SYNTHXEX_PRINT_STEM); ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders); if(ret != SUCCESS) @@ -457,7 +459,7 @@ int main(int argc, char **argv) return -1; } - printf("%s Building XEX header...\n", PRINT_STEM); + printf("%s Building XEX header...\n", SYNTHXEX_PRINT_STEM); ret = setXEXHeader(xexHeader, optHeaderEntries, peData); if(ret != SUCCESS) @@ -473,7 +475,7 @@ int main(int argc, char **argv) freePeImportInfoStruct(&(peData->peImportInfo)); // Setting data positions - printf("%s Aligning data...\n", PRINT_STEM); + printf("%s Aligning data...\n", SYNTHXEX_PRINT_STEM); ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders); if(ret != SUCCESS) @@ -489,7 +491,7 @@ int main(int argc, char **argv) freePeDataStruct(&peData); // Write out all of the XEX data to file - printf("%s Writing XEX...\n", PRINT_STEM); + printf("%s Writing XEX...\n", SYNTHXEX_PRINT_STEM); ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex); if(ret != SUCCESS) @@ -502,7 +504,7 @@ int main(int argc, char **argv) } // Final pass (sets & writes header hash) - printf("%s Calculating and writing header SHA1...\n", PRINT_STEM); + printf("%s Calculating and writing header SHA1...\n", SYNTHXEX_PRINT_STEM); ret = setHeaderSha1(xex); if(ret != SUCCESS) @@ -514,7 +516,7 @@ int main(int argc, char **argv) return -1; } - printf("%s Header SHA1 written!\n", PRINT_STEM); + printf("%s Header SHA1 written!\n", SYNTHXEX_PRINT_STEM); // Free files fclose(xex); @@ -522,6 +524,6 @@ int main(int argc, char **argv) // Free structs freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM); + printf("%s XEX built. Have a nice day!\n\n", SYNTHXEX_PRINT_STEM); return SUCCESS; } diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 9de77e6..e091451 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -53,7 +53,7 @@ int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) secInfoHeader->peSize = peData->size; // Setting signature (just a SynthXEX version identifier) - strcpy(secInfoHeader->signature, VERSION_STRING); + strcpy(secInfoHeader->signature, SYNTHXEX_VERSION_STRING); secInfoHeader->imageInfoSize = 0x174; // Image info size is always 0x174 secInfoHeader->imageFlags = (peData->pageSize == 0x1000 ? XEX_IMG_FLAG_4KIB_PAGES : 0) | XEX_IMG_FLAG_REGION_FREE; // If page size is 4KiB (small pages), set that flag From 9992242f5b3d5598900bcd4bf4b1124006b079ec Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Tue, 24 Jun 2025 18:27:44 +0100 Subject: [PATCH 28/38] Correct version regex in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fca87a..3172bc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,7 +69,7 @@ execute_process( ) # Only use the result from the git command if it's a valid version string -if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-[0-9a-z]+)?$") +if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-[0-9a-f]+)?$") add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}") else() add_compile_definitions(SYNTHXEX_VERSION="v0.0.3") # Only used as a fallback From a518314deeec5f9070195e8f6e9829549ebb4a4d Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Wed, 25 Jun 2025 23:51:54 +0100 Subject: [PATCH 29/38] Add Guix support --- CMakeLists.txt | 6 +-- channels.scm | 14 +++++++ synthxex.scm | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 channels.scm create mode 100644 synthxex.scm diff --git a/CMakeLists.txt b/CMakeLists.txt index 3172bc6..330a67a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -cmake_minimum_required(VERSION 3.25.1) +cmake_minimum_required(VERSION 3.24.2) project(synthxex) if(MSVC) @@ -62,14 +62,14 @@ endif() # Set the version execute_process( - COMMAND git describe HEAD + COMMAND git describe --tags --dirty WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE ) # Only use the result from the git command if it's a valid version string -if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-[0-9a-f]+)?$") +if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$") add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}") else() add_compile_definitions(SYNTHXEX_VERSION="v0.0.3") # Only used as a fallback diff --git a/channels.scm b/channels.scm new file mode 100644 index 0000000..61849c3 --- /dev/null +++ b/channels.scm @@ -0,0 +1,14 @@ +;;; Lock the Guix repository to a specific commit. +;;; For reproducibility purposes in building SynthXEX. + +(list (channel + (name 'guix) + (url "https://codeberg.org/guix/guix.git") + (branch "master") + (commit + "95f8c22bbe9cf25ce492ef520f94a34f7ec3d160") + (introduction + (make-channel-introduction + "9edb3f66fd807b096b48283debdcddccfea34bad" + (openpgp-fingerprint + "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA"))))) diff --git a/synthxex.scm b/synthxex.scm new file mode 100644 index 0000000..52b102d --- /dev/null +++ b/synthxex.scm @@ -0,0 +1,104 @@ +;;; This file is the git repo bundled package definition of SynthXEX for GNU Guix. +;;; To build natively (reproducibly), run "guix time-machine -C channels.scm -- build -f synthxex.scm" in the project root. +;;; To cross compile (reproducibly), run "guix time-machine -C channels.scm -- build -f synthxex.scm --target=" in the project root. +;;; To install, swap "build" for "package" in the above commands +;;; +;;; Copyright (c) 2025 Aiden Isik +;;; +;;; This program is free software: you can redistribute it and/or modify +;;; it under the terms of the GNU Affero General Public License as published by +;;; the Free Software Foundation, either version 3 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 Affero General Public License for more details. +;;; +;;; You should have received a copy of the GNU Affero General Public License +;;; along with this program. If not, see . + + +;;; Specify the modules we need for this package definition +(define-module (synthxex) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (guix licenses) + #:use-module (guix build-system cmake) + #:use-module (git) + #:use-module (gnu packages version-control) + #:use-module (ice-9 regex)) + + +;;; Hardcoded fallback version in case we can't rely on git +(define synthxex-fallback-version "v0.0.3") + + +;;; Determine the version of SynthXEX we are building +;;; Returns: version string of SynthXEX being built +(define (get-synthxex-version) + (cond + ((< (libgit2-init!) 0) + (display "Warning: failed to initialise libgit2. Using fallback version number.\n") + synthxex-fallback-version) + + (else + (let ((synthxex-dir (dirname (current-filename)))) + (cond + ((eq? (openable-repository? synthxex-dir) #f) + (display "Note: not a git repository. Using fallback version number.\n") + (libgit2-shutdown!) + synthxex-fallback-version) + + (else + (let ((synthxex-repo (repository-open synthxex-dir))) + (cond + ((repository-empty? synthxex-repo) + (display "Warning: git repository is empty. Using fallback version number.\n") + (libgit2-shutdown!) + synthxex-fallback-version) + + ((repository-shallow? synthxex-repo) + (display "Warning: git repository is shalllow. Using fallback version number.\n") + (libgit2-shutdown!) + synthxex-fallback-version) + + ((repository-bare? synthxex-repo) + (display "Warning: git repository is bare. Using fallback version number.\n") + (libgit2-shutdown!) + synthxex-fallback-version) + + (else + (let* ((synthxex-desc-opts (make-describe-options #:strategy 'tags)) ; Equivalent to git describe --tags + (synthxex-description (describe-workdir synthxex-repo synthxex-desc-opts)) + (synthxex-format-opts (make-describe-format-options #:dirty-suffix "-dirty")) ; Equivalent to git describe --dirty + (synthxex-version-str (describe-format synthxex-description synthxex-format-opts))) + (libgit2-shutdown!) ; Whatever the result of the regex comparison, we're done with git. Clean up here. + + (cond ; Check if the version string returned is valid. If it isn't, use the fallback. + ((eq? (string-match "^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$" synthxex-version-str) #f) + (display "Warning: version string is invalid. Using fallback version number.\n") + synthxex-fallback-version) + + (else + synthxex-version-str)))))))))))) + + +;;; Define the SynthXEX package +(define-public synthxex + (package + (name "synthxex") + (version (get-synthxex-version)) + (source (local-file (dirname (current-filename)) #:recursive? #t)) + (build-system cmake-build-system) + (arguments + '(#:build-type "Debug" ; This is the version of the package definition bundled in the git repo, so we should build a debug version + #:tests? #f)) ; We don't have a test suite + (native-inputs + (list git)) ; Needed for version number generation by cmake + (synopsis "XEX2 generator for the Xbox 360 games console") + (description "SynthXEX is an XEX2 generator, the final component of development toolchains for the Xbox 360 games console.") + (home-page "https://git.aidenisik.scot/FreeChainXenon/SynthXEX") + (license agpl3+))) + +synthxex From 3527e2e76d675f089a197b3b6644550770e86670 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 26 Jun 2025 01:12:45 +0100 Subject: [PATCH 30/38] Remove tabs from Guile indentation --- channels.scm | 2 +- synthxex.scm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/channels.scm b/channels.scm index 61849c3..a1ee3cb 100644 --- a/channels.scm +++ b/channels.scm @@ -6,7 +6,7 @@ (url "https://codeberg.org/guix/guix.git") (branch "master") (commit - "95f8c22bbe9cf25ce492ef520f94a34f7ec3d160") + "95f8c22bbe9cf25ce492ef520f94a34f7ec3d160") (introduction (make-channel-introduction "9edb3f66fd807b096b48283debdcddccfea34bad" diff --git a/synthxex.scm b/synthxex.scm index 52b102d..5b92d11 100644 --- a/synthxex.scm +++ b/synthxex.scm @@ -95,7 +95,7 @@ '(#:build-type "Debug" ; This is the version of the package definition bundled in the git repo, so we should build a debug version #:tests? #f)) ; We don't have a test suite (native-inputs - (list git)) ; Needed for version number generation by cmake + (list git)) ; Needed for version number generation by cmake (synopsis "XEX2 generator for the Xbox 360 games console") (description "SynthXEX is an XEX2 generator, the final component of development toolchains for the Xbox 360 games console.") (home-page "https://git.aidenisik.scot/FreeChainXenon/SynthXEX") From 9d36dd8ec9cd8307822e00465aa06ea89bc9fac9 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 00:03:06 +0100 Subject: [PATCH 31/38] Do more work on imports --- src/common/common.h | 11 +++---- src/common/datastorage.c | 2 +- src/common/datastorage.h | 3 +- src/getdata/gethdrdata.c | 6 ++-- src/getdata/getimports.c | 21 ++++++++------ src/main.c | 12 +++++--- src/pemapper/pemapper.c | 54 +++++++++++++++++++++++++++++++++-- src/placer/placer.c | 4 +-- src/setdata/optheaders.c | 4 +-- src/setdata/pagedescriptors.c | 4 +-- src/setdata/populateheaders.c | 8 +++--- src/write/writexex.c | 2 +- 12 files changed, 96 insertions(+), 35 deletions(-) diff --git a/src/common/common.h b/src/common/common.h index 9fb9f2d..b8fd862 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -49,8 +49,9 @@ #define ERR_MISSING_SECTION_FLAG -2 #define ERR_FILE_OPEN -3 #define ERR_FILE_READ -4 -#define ERR_OUT_OF_MEM -5 -#define ERR_UNSUPPORTED_STRUCTURE -6 -#define ERR_INVALID_RVA_OR_OFFSET -7 -#define ERR_INVALID_IMPORT_NAME -8 -#define ERR_DATA_OVERFLOW -9 +#define ERR_FILE_WRITE -5 +#define ERR_OUT_OF_MEM -6 +#define ERR_UNSUPPORTED_STRUCTURE -7 +#define ERR_INVALID_RVA_OR_OFFSET -8 +#define ERR_INVALID_IMPORT_NAME -9 +#define ERR_DATA_OVERFLOW -10 diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 5cbffcd..993c9f9 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -162,7 +162,7 @@ void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, uint32_t getNextAligned(uint32_t offset, uint32_t alignment) { - if(offset % alignment) // If offset not aligned + if(offset % alignment) // If offset not aligned { return offset + (alignment - (offset % alignment)); // Align } diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 0d3d9af..50f1c4d 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -93,13 +93,14 @@ struct section struct peImport { - uint32_t iatAddr; + uint32_t iatAddr; // Also contains a flag denoting whether this is a function or variable uint32_t branchStubAddr; }; struct peImportTable { char *name; + uint32_t rva; uint32_t importCount; uint32_t branchStubCount; struct peImport *imports; diff --git a/src/getdata/gethdrdata.c b/src/getdata/gethdrdata.c index 510bdf0..d0569b6 100644 --- a/src/getdata/gethdrdata.c +++ b/src/getdata/gethdrdata.c @@ -35,7 +35,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false fseek(pe, 0, SEEK_SET); uint16_t magic = get16BitFromPE(pe); - if(magic != 0x5A4D || errno != SUCCESS) // PE magic + if(magic != 0x5A4D || errno != SUCCESS) // PE magic { return false; } // Check if pointer to PE header is valid @@ -78,7 +78,7 @@ bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false uint16_t subsystem = get16BitFromPE(pe); - if(subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX + if(subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX { return false; } // Check page size/alignment @@ -168,7 +168,7 @@ int getSectionInfo(FILE *pe, struct sections *sections) if(characteristics & PE_SECTION_FLAG_EXECUTE) { - sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 + sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 } else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) { sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; } diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index 8f8b90f..cb5abdd 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -86,6 +86,7 @@ int checkForBranchStub(FILE *pe, struct peData *peData) { if(instructionBuffer[3] == expectedInstructions[3]) { + // Found a branch stub, check if the address matches any IAT entries uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | (instructionBuffer[1] & 0x0000FFFF); @@ -93,15 +94,16 @@ int checkForBranchStub(FILE *pe, struct peData *peData) { if(peData->peImportInfo.tables[i].importCount == peData->peImportInfo.tables[i].branchStubCount) - { continue; } + { continue; } // We already have all the branch stubs possible for this table, skip it for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) { if(peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) - { continue; } + { continue; } // We already have this branch stub, skip it if(peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) { + // Found the branch stub for the current import, store the address uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); if(currentBranchStubRVA == 0) @@ -152,7 +154,7 @@ int getImports(FILE *pe, struct peData *peData) if(currentIDT == NULL) { return ERR_OUT_OF_MEM; } - uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons + uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons if(blankIDT == NULL) { @@ -181,7 +183,7 @@ int getImports(FILE *pe, struct peData *peData) return ERR_OUT_OF_MEM; } - memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank + memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank #ifdef BIG_ENDIAN_SYSTEM @@ -235,6 +237,9 @@ int getImports(FILE *pe, struct peData *peData) } doneGettingTableName: + // Store the IAT RVA for this table + peData->peImportInfo.tables[i].rva = currentIDT[4]; + // Seek to the IAT and read the first entry uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections)); @@ -271,7 +276,7 @@ int getImports(FILE *pe, struct peData *peData) // Allocate space for the current import peData->peImportInfo.tables[i].importCount++; peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); - peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later + peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later // Store the address of the current import entry in iatAddr uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); @@ -317,19 +322,19 @@ int getImports(FILE *pe, struct peData *peData) { if(peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) { - break; // All branch stubs found + break; // All branch stubs found } if(!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) { - continue; // Skip non-executable section + continue; // Skip non-executable section } // Seek to the start of the current section if(fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) { return ERR_FILE_READ; } - // While inside section and at least 4 instructions left (15 bytes) + // While inside section and at least 4 instructions left (> 15 bytes) while(ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) { int ret = checkForBranchStub(pe, peData); diff --git a/src/main.c b/src/main.c index fa39ff1..2b3e678 100644 --- a/src/main.c +++ b/src/main.c @@ -126,6 +126,10 @@ void handleError(int ret) fprintf(stderr, "%s ERROR: Failed to read data from PE file. Aborting.\n", SYNTHXEX_PRINT_STEM); break; + case ERR_FILE_WRITE: + fprintf(stderr, "%s ERROR: Failed to write data to file. Aborting.\n", SYNTHXEX_PRINT_STEM); + break; + case ERR_OUT_OF_MEM: fprintf(stderr, "%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); break; @@ -288,7 +292,7 @@ int main(int argc, char **argv) if(gotOutput) { nullAndFree((void **)&xexfilePath); } - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); printf("%s ERROR: PE input expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM); return -1; } @@ -297,7 +301,7 @@ int main(int argc, char **argv) if(gotInput) { nullAndFree((void **)&pePath); } - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); printf("%s ERROR: XEX file output expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM); return -1; } @@ -309,7 +313,7 @@ int main(int argc, char **argv) printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", SYNTHXEX_PRINT_STEM); nullAndFree((void **)&pePath); nullAndFree((void **)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } @@ -322,7 +326,7 @@ int main(int argc, char **argv) printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", SYNTHXEX_PRINT_STEM); fclose(pe); nullAndFree((void **)&xexfilePath); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); return -1; } diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index 8c2853c..bb688f7 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -26,14 +26,54 @@ struct sectionInfo uint32_t offset; }; +// Strips the ordinal flags from IAT entries, and swaps them to big endian +int xenonifyIAT(FILE *basefile, struct peData *peData) +{ + // Loop through each import table and handle their IAT entries + for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) + { + // Seek to the first IAT entry for this table + if(fseek(basefile, peData->peImportInfo.tables[i].rva, SEEK_SET) != 0) + { return ERR_FILE_READ; } + + // Loop through each import and handle it's IAT entry + for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) + { + uint32_t iatEntry = get32BitFromPE(basefile); + + if(errno != SUCCESS) + { return ERR_FILE_READ; } + + // Seek back so we can write back to the same place + if(fseek(basefile, -0x4, SEEK_CUR) != 0) + { return ERR_FILE_READ; } + + // Xenonify the IAT entry + iatEntry &= ~PE_IMPORT_ORDINAL_FLAG; // Strip the import by ordinal flag + + // Write back out as big endian (TODO: make a utility function for this like get32BitFromPE) +#ifdef LITTLE_ENDIAN_SYSTEM + iatEntry = __builtin_bswap32(iatEntry); +#endif + + if(fwrite(&iatEntry, sizeof(uint32_t), 1, basefile) < 1) + { return ERR_FILE_WRITE; } + } + } + + return SUCCESS; +} + // Maps the PE file into the basefile (RVAs become offsets) int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { - struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo)); + // Retrieve the section info from PE (TODO: use the data we get from getHdrData for this, right now we're duplicating work) + struct sectionInfo *sectionInfo = malloc(peData->numberOfSections *sizeof(struct sectionInfo)); if(!sectionInfo) { return ERR_OUT_OF_MEM; } + // Seek to the first section in the section table at virtualSize if(fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0) { return ERR_FILE_READ; } @@ -59,6 +99,7 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) if(errno != SUCCESS) { return errno; } + // Seek to the next entry at virtualSize if(fseek(pe, 0x18, SEEK_CUR) != 0) { return ERR_FILE_READ; } } @@ -66,6 +107,7 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) if(fseek(pe, 0, SEEK_SET) != 0) { return ERR_FILE_READ; } + // Copy the PE header and section table to the basefile verbatim uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize); if(!buffer) @@ -79,6 +121,7 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) if(fwrite(buffer, 1, totalHeader, basefile) != totalHeader) { return ERR_FILE_READ; } + // Now map the sections and write them for(uint16_t i = 0; i < peData->numberOfSections; i++) { buffer = realloc(buffer, sectionInfo[i].rawSize); @@ -99,6 +142,8 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { return ERR_FILE_READ; } } + // Pad the rest of the final page with zeroes, we can achieve this by seeking + // to the end and placing a single zero there (unless the data runs all the way up to the end!) uint32_t currentOffset = ftell(basefile); uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1; @@ -118,10 +163,15 @@ int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData) { return ERR_FILE_READ; } } + // Make sure to update the PE (basefile) size peData->size = ftell(basefile); + // We're done with these now, free them nullAndFree((void **)&buffer); nullAndFree((void **)§ionInfo); - return SUCCESS; + // While we're writing the basefile, let's do the required modifications to the IAT. + // We can skip the return check because this is the last function call. + // The outcome of this is the outcome of the whole function. + return xenonifyIAT(basefile, peData); } diff --git a/src/placer/placer.c b/src/placer/placer.c index 86e38a8..b3f98e2 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -76,13 +76,13 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op // Optional header entries (no alignment, they immediately follow XEX header) offsets->optHeaderEntries = currentOffset; - currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry); + currentOffset += optHeaderEntries->count *sizeof(struct optHeaderEntry); // Security header currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries offsets->secInfoHeader = currentOffset; xexHeader->secInfoOffset = currentOffset; - currentOffset += (sizeof(struct secInfoHeader) - sizeof(void *)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)); + currentOffset += (sizeof(struct secInfoHeader) - sizeof(void *)) + (secInfoHeader->pageDescCount *sizeof(struct pageDescriptor)); // Optional headers (minus imports) struct importLibIdcs importLibIdcs; diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 2a1fb64..c0aee6d 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -198,7 +198,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn } // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) - importTables[i].size = (sizeof(struct importTable) - sizeof(void *) + (importTables[i].addressCount * sizeof(uint32_t))); + importTables[i].size = (sizeof(struct importTable) - sizeof(void *) + (importTables[i].addressCount *sizeof(uint32_t))); importLibraries->size += importTables[i].size; // Init sha1 hash @@ -226,7 +226,7 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn // - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format) // +/- sizeof(uint32_t) to exclude table size from hash sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void *) - sizeof(uint32_t), (void *)&importTables[i] + sizeof(uint32_t)); - sha1_update(&shaContext, addressCount * sizeof(uint32_t), (void *)addresses); + sha1_update(&shaContext, addressCount *sizeof(uint32_t), (void *)addresses); // If we're on a little endian system, swap everything back into little endian #ifdef LITTLE_ENDIAN_SYSTEM diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 0651896..c6543c3 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -21,7 +21,7 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint32_t page) { uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount; - uint32_t currentOffset = page * pageSize; + uint32_t currentOffset = page *pageSize; for(int32_t i = peData->sections.count - 1; i >= 0; i--) if(currentOffset >= peData->sections.section[i].rva) @@ -48,7 +48,7 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se struct sha1_ctx shaContext; sha1_init(&shaContext); - if(fseek(pe, i * pageSize, SEEK_SET) != 0) + if(fseek(pe, i *pageSize, SEEK_SET) != 0) { return ERR_FILE_READ; } uint8_t *page = malloc(pageSize); diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index e091451..ee6322d 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -29,16 +29,16 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader { if(peData->characteristics & PE_CHAR_FLAG_DLL) { - xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL + xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL } else { - xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title + xexHeader->moduleFlags |= XEX_MOD_FLAG_TITLE; // The executable is a regular title } if(peData->peExportInfo.count > 0) { - xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions + xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions } } @@ -65,7 +65,7 @@ int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData) secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE; secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type. secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages) - secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void *)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) + secInfoHeader->headerSize = (secInfoHeader->pageDescCount *sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void *)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation) return SUCCESS; } diff --git a/src/write/writexex.c b/src/write/writexex.c index a3b780d..bdc026b 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -72,7 +72,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fseek(xex, offsets->basefile, SEEK_SET); uint16_t readBufSize = 0x1000; // Reading in 4KiB at a time to avoid excessive memory usage - uint8_t *buffer = malloc(readBufSize * sizeof(uint8_t)); + uint8_t *buffer = malloc(readBufSize *sizeof(uint8_t)); if(buffer == NULL) { return ERR_OUT_OF_MEM; } From b8be6481e0e172564a06537c0f445c3402520e42 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 00:08:26 +0100 Subject: [PATCH 32/38] No, iatAddr doesn't contain a flag --- src/common/datastorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 50f1c4d..d6ed6e7 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -93,7 +93,7 @@ struct section struct peImport { - uint32_t iatAddr; // Also contains a flag denoting whether this is a function or variable + uint32_t iatAddr; uint32_t branchStubAddr; }; From 34b49c9a1d559603b2fb947bf8cb1314c1206c26 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 03:21:23 +0100 Subject: [PATCH 33/38] Add module indexes to PE IAT entries --- src/pemapper/pemapper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index bb688f7..98464ed 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -27,6 +27,7 @@ struct sectionInfo }; // Strips the ordinal flags from IAT entries, and swaps them to big endian +// Also adds module indexes to them int xenonifyIAT(FILE *basefile, struct peData *peData) { // Loop through each import table and handle their IAT entries @@ -50,6 +51,7 @@ int xenonifyIAT(FILE *basefile, struct peData *peData) // Xenonify the IAT entry iatEntry &= ~PE_IMPORT_ORDINAL_FLAG; // Strip the import by ordinal flag + iatEntry |= (i & 0x000000FF) << 16; // Add the module index // Write back out as big endian (TODO: make a utility function for this like get32BitFromPE) #ifdef LITTLE_ENDIAN_SYSTEM From 4d67f29fa37b1939e952c34b9f5e972ad5fc7b43 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 15:16:20 +0100 Subject: [PATCH 34/38] Get imports *finally* working (remove special handling for functions and treat them as variables) --- src/common/datastorage.h | 3 - src/getdata/getimports.c | 142 --------------------------------------- src/setdata/optheaders.c | 14 +--- 3 files changed, 2 insertions(+), 157 deletions(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index d6ed6e7..4a72cca 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -94,7 +94,6 @@ struct section struct peImport { uint32_t iatAddr; - uint32_t branchStubAddr; }; struct peImportTable @@ -102,7 +101,6 @@ struct peImportTable char *name; uint32_t rva; uint32_t importCount; - uint32_t branchStubCount; struct peImport *imports; }; @@ -111,7 +109,6 @@ struct peImportInfo uint32_t idtRVA; uint32_t tableCount; uint32_t totalImportCount; - uint32_t totalBranchStubCount; struct peImportTable *tables; }; diff --git a/src/getdata/getimports.c b/src/getdata/getimports.c index cb5abdd..db65128 100644 --- a/src/getdata/getimports.c +++ b/src/getdata/getimports.c @@ -18,119 +18,6 @@ #include "getimports.h" -int checkForBranchStub(FILE *pe, struct peData *peData) -{ - uint32_t expectedInstructions[] = - { - // lis r, - // Mask 0xFC000000 for instruction (we're just comparing in-place, no need for shifts) - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 (should be 0 for lis) - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-addis-cau-add-immediate-shifted-instruction - 0x3C000000, - - // lwz r, (r) - // Mask 0xFC000000 for instruction - // Mask 0x03E00000 and shift right 21 bits for register 1 - // Mask 0x001F0000 and shift right 16 bits for register 2 - // Mask 0x0000FFFF for value - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-lwz-l-load-word-zero-instruction - 0x80000000, - - // mtctr r - // Mask 0xFC1FFFFF for instruction (mtspr CTR) - // Mask 0x03E00000 and shift right 21 bits for register - // https://www.ibm.com/docs/en/aix/7.2.0?topic=set-mtspr-move-special-purpose-register-instruction - 0x7C0903A6, - - // bctr - // An assembler told me this, apparently IBM doesn't have documentation on their site for it - 0x4E800420 - }; - - // Read in the set of instructions to check - uint32_t *instructionBuffer = malloc(4 * sizeof(uint32_t)); - - if(instructionBuffer == NULL) - { return ERR_OUT_OF_MEM; } - - memset(instructionBuffer, 0, sizeof(uint32_t) * 4); - - if(fread(instructionBuffer, sizeof(uint32_t), 4, pe) < 4) - { - nullAndFree((void **)&instructionBuffer); - return ERR_FILE_READ; - } - -#ifdef LITTLE_ENDIAN_SYSTEM - - // Byteswap the instructions - for(uint8_t i = 0; i < 4; i++) - { instructionBuffer[i] = __builtin_bswap32(instructionBuffer[i]); } - -#endif - - // Check if each instruction matches - uint8_t expectedReg = (instructionBuffer[0] & 0x03E00000) >> 21; - - if(((instructionBuffer[0] & 0xFC000000) == expectedInstructions[0]) && - (((instructionBuffer[0] & 0x001F0000) >> 16) == 0)) - { - if(((instructionBuffer[1] & 0xFC000000) == expectedInstructions[1]) && - (((instructionBuffer[1] & 0x03E00000) >> 21) == expectedReg) && - (((instructionBuffer[1] & 0x001F0000) >> 16) == expectedReg)) - { - if(((instructionBuffer[2] & 0xFC1FFFFF) == expectedInstructions[2]) && - (((instructionBuffer[2] & 0x03E00000) >> 21) == expectedReg)) - { - if(instructionBuffer[3] == expectedInstructions[3]) - { - // Found a branch stub, check if the address matches any IAT entries - uint32_t currentLoadAddr = ((instructionBuffer[0] & 0x0000FFFF) << 16) | - (instructionBuffer[1] & 0x0000FFFF); - - for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++) - { - if(peData->peImportInfo.tables[i].importCount == - peData->peImportInfo.tables[i].branchStubCount) - { continue; } // We already have all the branch stubs possible for this table, skip it - - for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) - { - if(peData->peImportInfo.tables[i].imports[j].branchStubAddr != 0) - { continue; } // We already have this branch stub, skip it - - if(peData->peImportInfo.tables[i].imports[j].iatAddr == currentLoadAddr) - { - // Found the branch stub for the current import, store the address - uint32_t currentBranchStubRVA = offsetToRVA(ftell(pe) - 16, &(peData->sections)); - - if(currentBranchStubRVA == 0) - { - nullAndFree((void **)&instructionBuffer); - return ERR_INVALID_RVA_OR_OFFSET; - } - - peData->peImportInfo.tables[i].imports[j].branchStubAddr = - peData->baseAddr + currentBranchStubRVA; - peData->peImportInfo.tables[i].branchStubCount++; - peData->peImportInfo.totalBranchStubCount++; - - nullAndFree((void **)&instructionBuffer); - return SUCCESS; - } - } - } - } - } - } - } - - nullAndFree((void **)&instructionBuffer); - return fseek(pe, -12, SEEK_CUR) != 0 ? ERR_FILE_READ : SUCCESS; -} - int getImports(FILE *pe, struct peData *peData) { // Make sure the peImportInfo struct is blank, except the IDT RVA @@ -276,7 +163,6 @@ int getImports(FILE *pe, struct peData *peData) // Allocate space for the current import peData->peImportInfo.tables[i].importCount++; peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport)); - peData->peImportInfo.tables[i].imports[j].branchStubAddr = 0; // Zero it for later // Store the address of the current import entry in iatAddr uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections)); @@ -316,33 +202,5 @@ int getImports(FILE *pe, struct peData *peData) nullAndFree((void **)¤tIDT); nullAndFree((void **)&blankIDT); - - // Find the branch stubs - for(uint16_t i = 0; i < peData->sections.count; i++) - { - if(peData->peImportInfo.totalBranchStubCount == peData->peImportInfo.totalImportCount) - { - break; // All branch stubs found - } - - if(!(peData->sections.section[i].permFlag & XEX_SECTION_CODE)) - { - continue; // Skip non-executable section - } - - // Seek to the start of the current section - if(fseek(pe, peData->sections.section[i].offset, SEEK_SET) != 0) - { return ERR_FILE_READ; } - - // While inside section and at least 4 instructions left (> 15 bytes) - while(ftell(pe) < (peData->sections.section[i].offset + peData->sections.section[i].rawSize) - 15) - { - int ret = checkForBranchStub(pe, peData); - - if(ret != SUCCESS) - { return ret; } - } - } - return SUCCESS; } diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index c0aee6d..b67de48 100644 --- a/src/setdata/optheaders.c +++ b/src/setdata/optheaders.c @@ -166,10 +166,8 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn else { goto cleanup_names_invalid; } - // Determine the number of addresses (2 for functions, 1 for everything else) - importTables[i].addressCount = - (peImportInfo->tables[i].branchStubCount * 2) + - (peImportInfo->tables[i].importCount - peImportInfo->tables[i].branchStubCount); + // Determine the number of addresses + importTables[i].addressCount = peImportInfo->tables[i].importCount; // Allocate enough memory for the addresses importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t)); @@ -187,14 +185,6 @@ int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportIn { goto cleanup_names_invalid; } addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr; - - if(peImportInfo->tables[i].imports[j].branchStubAddr != 0) - { - if(currentAddr >= importTables[i].addressCount) - { goto cleanup_names_invalid; } - - addresses[currentAddr++] = peImportInfo->tables[i].imports[j].branchStubAddr; - } } // Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end) From 8bc36751c9932b770d8cb79a864f3c24803ce507 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 16:11:26 +0100 Subject: [PATCH 35/38] Bump version, update README --- CMakeLists.txt | 2 +- README.md | 106 ++++++++++++++++++++++++++++++++++++++++++++----- synthxex.scm | 2 +- 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 330a67a..f02d424 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ execute_process( if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$") add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}") else() - add_compile_definitions(SYNTHXEX_VERSION="v0.0.3") # Only used as a fallback + add_compile_definitions(SYNTHXEX_VERSION="v0.0.4") # Only used as a fallback endif() # Setting install target settings... diff --git a/README.md b/README.md index f214efd..9df9ab2 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,17 @@ SynthXEX is the XEX(2) builder (and thus the last step in building an executable This was developed by referencing public knowledge on the XEX(2) file format, along with staring at XEX files in a hex editor to decypher aspects which do not seem to be documented anywhere. No Microsoft code was decompiled to develop SynthXEX, and I ask that any contributors do not do so either. -This is in early development and MANY features are missing (most notable imports/exports). No guarantees are made as to the functionality of the program, or it's stability. Functionality may change at any time. +This is in early development and MANY features are missing (most notable exports). No guarantees are made as to the functionality of the program, or it's stability. Functionality may change at any time. This *should* support any POSIX-compliant operating system, along with Windows. -## Installing -SynthXEX is part of the FreeChainXenon toolchain. It's installer is located [here](https://git.aidenisik.scot/FreeChainXenon/FCX-Installer). - -## Building +## Building (POSIX, standard method) ### Prerequisites - A C compiler (GCC/Clang) -- CMake (>= 3.25.1) +- CMake (>= 3.24.2) - Make - Git @@ -31,7 +28,7 @@ To install these on Debian: ```sudo apt install gcc cmake make git``` ### Downloading -Run: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` +Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` ### Compiling @@ -45,15 +42,102 @@ Build: ```make``` Install: ```sudo make install``` + +## Building (Guix) + +### Prerequisites + +- Guix +- Git + +### Compiling + +Choose ONE of the options below. + +#### Compiling & Installing + +Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` + +Switch to the newly cloned directory: ```cd SynthXEX``` + +Install: ```guix time-machine -C channels.scm -- package -f synthxex.scm``` + +#### Compiling Only + +Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` + +Switch to the newly cloned directory: ```cd SynthXEX``` + +Build: ```guix time-machine -C channels.scm -- build -f synthxex.scm``` + +#### Compiling & Installing to Config + +TODO + + +## Building (Nix) + +### Prerequisites + +- Nix +- Git + +### Compiling + +Choose ONE of the options below. + +#### Compiling & Installing + +Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` + +Switch to the newly cloned directory: ```cd SynthXEX``` + +Run: ```nix profile install .#synthxex``` + +#### Compiling Only + +Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX``` + +Switch to the newly cloned directory: ```cd SynthXEX``` + +Run: ```nix build .#synthxex``` + +#### Compiling & Installing to Config + +Add SynthXEX to your configuration.nix file: + +``` +{ config, modulesPath, lib, pkgs, ... }: + +let + synthxex = builtins.getFlake "git+https://git.aidenisik.scot/FreeChainXenon/SynthXEX"; + synthxexPkgs = synthxex.packages.${pkgs.hostPlatform.system}; +in { + # ... + environment.systemPackages = with pkgs; [ + # ... + synthxexPkgs.synthxex + ]; +} +``` + + ## Special thanks to: - [InvoxiPlayGames](https://github.com/InvoxiPlayGames), for help understanding the XEX format - [emoose](https://github.com/emoose), for xex1tool, which was very useful for taking apart XEX files and getting info from them - [Free60Project](https://github.com/Free60Project), for documentation on the XEX format, which was useful in the early days -- Several other members of the Xbox360Hub Discord server, for a multitude of reasons +- [Vali0004](https://github.com/Vali0004), for helping clean up and harden code, along with Nix packaging +- Several members of the Xbox360Hub Discord server, for a multitude of reasons + + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md). + ## Licensing -### SynthXEX (src/*, CMakeLists.txt) +### SynthXEX Copyright (c) 2024-25 Aiden Isik @@ -70,7 +154,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -### getopt_port (include/getopt_port/*) +### getopt_port Copyright (c) 2012-2023, Kim Grasman @@ -98,7 +182,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -### GNU Nettle (include/nettle/*) +### GNU Nettle Copyright (C) 2001, 2013 Niels Möller diff --git a/synthxex.scm b/synthxex.scm index 5b92d11..2d5d7fc 100644 --- a/synthxex.scm +++ b/synthxex.scm @@ -31,7 +31,7 @@ ;;; Hardcoded fallback version in case we can't rely on git -(define synthxex-fallback-version "v0.0.3") +(define synthxex-fallback-version "v0.0.4") ;;; Determine the version of SynthXEX we are building From bbafc3d7acb77e7dbcd6a56026030015dfcb151e Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 30 Jun 2025 18:07:26 +0100 Subject: [PATCH 36/38] Re-add comments to some files --- src/setdata/pagedescriptors.c | 6 +++++- src/write/headerhash.c | 13 ++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index c6543c3..a67795f 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -39,12 +39,15 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se if(!secInfoHeader->descriptors) { return ERR_OUT_OF_MEM; } - struct pageDescriptor *descriptors = secInfoHeader->descriptors; + struct pageDescriptor *descriptors = secInfoHeader->descriptors; // So we don't dereference an unaligned pointer + // Setting size/info data and calculating hashes for page descriptors for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--) { + // Get page type (rwx) descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i); + // Init sha1 hash struct sha1_ctx shaContext; sha1_init(&shaContext); @@ -62,6 +65,7 @@ int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *se return ERR_FILE_READ; } + // For little endian systems, swap into big endian for hashing, then back (to keep struct endianness consistent) #ifdef LITTLE_ENDIAN_SYSTEM descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo); #endif diff --git a/src/write/headerhash.c b/src/write/headerhash.c index f430023..8bd612e 100644 --- a/src/write/headerhash.c +++ b/src/write/headerhash.c @@ -22,6 +22,7 @@ // to determine the hash, but reading the file we just created is easier. int setHeaderSha1(FILE *xex) { + // Get basefile offset if(fseek(xex, 0x8, SEEK_SET) != 0) { return ERR_FILE_READ; } @@ -30,6 +31,7 @@ int setHeaderSha1(FILE *xex) if(errno != SUCCESS) { return errno; } + // Get secinfo offset if(fseek(xex, 0x10, SEEK_SET) != 0) { return ERR_FILE_READ; } @@ -38,13 +40,15 @@ int setHeaderSha1(FILE *xex) if(errno != SUCCESS) { return errno; } - uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; - uint32_t remainingSize = basefileOffset - endOfImageInfo; + uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; // 0x8 == image info offset in security info, 0x174 == length of that + uint32_t remainingSize = basefileOffset - endOfImageInfo; // How much data is between end of image info and basefile (we hash that too) + // Init sha1 hash struct sha1_ctx shaContext; memset(&shaContext, 0, sizeof(shaContext)); sha1_init(&shaContext); + // Hash first part (remainder of headers is done first, then the start) uint8_t *remainderOfHeaders = malloc(remainingSize); if(!remainderOfHeaders) @@ -67,6 +71,7 @@ int setHeaderSha1(FILE *xex) sha1_update(&shaContext, remainingSize, remainderOfHeaders); nullAndFree((void **)&remainderOfHeaders); + // Hash from start up to image info (0x8 into security header) uint32_t headersLen = secInfoOffset + 0x8; uint8_t *headersStart = malloc(headersLen); @@ -90,11 +95,13 @@ int setHeaderSha1(FILE *xex) sha1_update(&shaContext, headersLen, headersStart); nullAndFree((void **)&headersStart); + // Get final hash uint8_t headerHash[20]; memset(headerHash, 0, sizeof(headerHash)); sha1_digest(&shaContext, 20, headerHash); - if(fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) + // Finally, write it out + if(fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) // 0x164 == offset in secinfo of header hash { return ERR_FILE_READ; } if(fwrite(headerHash, 1, 20, xex) != 20) From 38187b17a52865318596bab7630098685904c6a9 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Wed, 2 Jul 2025 00:24:18 +0100 Subject: [PATCH 37/38] Fix bug where on some platforms (inc. Windows), duplicate IAT entries would be written --- CMakeLists.txt | 2 +- src/pemapper/pemapper.c | 5 +++++ synthxex.scm | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f02d424..cf05e94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ execute_process( if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$") add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}") else() - add_compile_definitions(SYNTHXEX_VERSION="v0.0.4") # Only used as a fallback + add_compile_definitions(SYNTHXEX_VERSION="v0.0.5") # Only used as a fallback endif() # Setting install target settings... diff --git a/src/pemapper/pemapper.c b/src/pemapper/pemapper.c index 98464ed..8885277 100644 --- a/src/pemapper/pemapper.c +++ b/src/pemapper/pemapper.c @@ -40,6 +40,7 @@ int xenonifyIAT(FILE *basefile, struct peData *peData) // Loop through each import and handle it's IAT entry for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++) { + // Read in the current IAT entry uint32_t iatEntry = get32BitFromPE(basefile); if(errno != SUCCESS) @@ -60,6 +61,10 @@ int xenonifyIAT(FILE *basefile, struct peData *peData) if(fwrite(&iatEntry, sizeof(uint32_t), 1, basefile) < 1) { return ERR_FILE_WRITE; } + + // Call file positioning function between reads and writes to the same file. + // This is mandated by the C standard. + fseek(basefile, 0, SEEK_CUR); } } diff --git a/synthxex.scm b/synthxex.scm index 2d5d7fc..ae4c757 100644 --- a/synthxex.scm +++ b/synthxex.scm @@ -31,7 +31,7 @@ ;;; Hardcoded fallback version in case we can't rely on git -(define synthxex-fallback-version "v0.0.4") +(define synthxex-fallback-version "v0.0.5") ;;; Determine the version of SynthXEX we are building From ea64ac981ce76ca0bebe179241fa72a5e69f912e Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Wed, 16 Jul 2025 23:24:33 +0100 Subject: [PATCH 38/38] Use val field of getopt option struct --- src/main.c | 153 +++++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/src/main.c b/src/main.c index 2b3e678..638c0e5 100644 --- a/src/main.c +++ b/src/main.c @@ -164,13 +164,13 @@ int main(int argc, char **argv) { static struct option longOptions[] = { - { "help", no_argument, 0, 0 }, - { "version", no_argument, 0, 0 }, - { "libs", no_argument, 0, 0 }, - { "skip-machine-check", no_argument, 0, 0 }, - { "input", required_argument, 0, 0 }, - { "output", required_argument, 0, 0 }, - { "type", required_argument, 0, 0 }, + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, + { "libs", no_argument, 0, 'l' }, + { "skip-machine-check", no_argument, 0, 's' }, + { "input", required_argument, 0, 'i' }, + { "output", required_argument, 0, 'o' }, + { "type", required_argument, 0, 't' }, { 0, 0, 0, 0 } }; @@ -202,85 +202,86 @@ int main(int argc, char **argv) while((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1) { - if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0)) + switch(option) { - dispHelp(argv); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0)) - { - dispVer(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0)) - { - dispLibs(); - freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, - &optHeaderEntries, &optHeaders); - return SUCCESS; - } - else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0)) - { - printf("%s WARNING: Skipping machine ID check.\n", SYNTHXEX_PRINT_STEM); - skipMachineCheck = true; - } - else if(option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0)) - { - gotInput = true; - pePath = malloc(strlen(optarg) + 1); - - if(pePath == NULL) - { - printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); - nullAndFree((void **)&xexfilePath); + case 'v': + dispVer(); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } + return SUCCESS; - strcpy(pePath, optarg); - } - else if(option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0)) - { - gotOutput = true; - xexfilePath = malloc(strlen(optarg) + 1); - - if(xexfilePath == NULL) - { - printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); - nullAndFree((void **)&pePath); + case 'l': + dispLibs(); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } + return SUCCESS; - strcpy(xexfilePath, optarg); - } - else if(option == 't' || (option == 0 && strcmp(longOptions[optIndex].name, "type") == 0)) - { - if(strcmp(optarg, "title") == 0) - { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; } - else if(strcmp(optarg, "titledll") == 0) - { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; } - else if(strcmp(optarg, "sysdll") == 0) - { xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; } - else if(strcmp(optarg, "dll") == 0) - { xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; } - else - { - printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", - SYNTHXEX_PRINT_STEM, optarg); + case 's': + printf("%s WARNING: Skipping machine ID check.\n", SYNTHXEX_PRINT_STEM); + skipMachineCheck = true; + break; - nullAndFree((void **)&pePath); - nullAndFree((void **)&xexfilePath); + case 'i': + gotInput = true; + pePath = malloc(strlen(optarg) + 1); + + if(pePath == NULL) + { + printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); + nullAndFree((void **)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + + strcpy(pePath, optarg); + break; + + case 'o': + gotOutput = true; + xexfilePath = malloc(strlen(optarg) + 1); + + if(xexfilePath == NULL) + { + printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM); + nullAndFree((void **)&pePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + + strcpy(xexfilePath, optarg); + break; + + case 't': + if(strcmp(optarg, "title") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; } + else if(strcmp(optarg, "titledll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; } + else if(strcmp(optarg, "sysdll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; } + else if(strcmp(optarg, "dll") == 0) + { xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; } + else + { + printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n", + SYNTHXEX_PRINT_STEM, optarg); + + nullAndFree((void **)&pePath); + nullAndFree((void **)&xexfilePath); + freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, + &optHeaderEntries, &optHeaders); + return -1; + } + + break; + + case 'h': + default: + dispHelp(argv); freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - return -1; - } + return SUCCESS; } }