From 3d4fe95a27dcbeb142518ceb904a7a46f990f706 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Fri, 7 Feb 2025 00:16:02 +0000 Subject: [PATCH 01/50] Fix get16BitFromPE return type --- src/common/common.h | 2 +- src/common/datastorage.c | 2 +- src/common/datastorage.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/common.h b/src/common/common.h index d3eeeeb..4f483be 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -26,7 +26,7 @@ // Program identifiers #define NAME "SynthXEX" -#define VERSION "v0.0.1" +#define VERSION "v0.0.2" #define COPYRIGHT "2024-25" #ifdef GIT_COMMIT diff --git a/src/common/datastorage.c b/src/common/datastorage.c index b23258e..7b877d4 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -50,7 +50,7 @@ uint32_t get32BitFromPE(FILE *pe) return result; } -uint32_t get16BitFromPE(FILE *pe) +uint16_t get16BitFromPE(FILE *pe) { uint8_t store[2]; fread(store, sizeof(uint8_t), 2, pe); diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 941ca13..5d778a9 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -193,5 +193,5 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment); // TODO: combine these into a single function uint32_t get32BitFromPE(FILE *pe); -uint32_t get16BitFromPE(FILE *pe); +uint16_t get16BitFromPE(FILE *pe); uint32_t get32BitFromXEX(FILE *xex); From 184dee1d5e44ccfd9fcdca63a4b28e808e527d7a Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Wed, 19 Feb 2025 22:07:17 +0000 Subject: [PATCH 02/50] Start work on making the final XEX smaller --- CMakeLists.txt | 3 ++ src/common/datastorage.h | 10 ++++- src/main.c | 12 ++++++ src/setdata/elimzeroes.c | 93 ++++++++++++++++++++++++++++++++++++++++ src/setdata/elimzeroes.h | 24 +++++++++++ src/setdata/optheaders.c | 11 +++-- src/write/writexex.c | 15 +++++-- 7 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 src/setdata/elimzeroes.c create mode 100644 src/setdata/elimzeroes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d01b84..9916c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,9 +40,12 @@ 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/elimzeroes.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/setdata/elimzeroes.c) + list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.h) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 5d778a9..faa9edc 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -145,13 +145,19 @@ struct __attribute__((packed)) secInfoHeader struct pageDescriptor *descriptors; }; +struct __attribute__((packed)) zeroEliminations +{ + uint32_t dataSize; + uint32_t zeroSize; +}; + struct __attribute__((packed)) basefileFormat { uint32_t size; uint16_t encType; uint16_t compType; - uint32_t dataSize; - uint32_t zeroSize; + uint32_t zeroElimCount; + struct zeroEliminations *zeroEliminations; }; struct __attribute__((packed)) importLibraries diff --git a/src/main.c b/src/main.c index 619fa43..4778612 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include "setdata/populateheaders.h" #include "setdata/pagedescriptors.h" #include "setdata/optheaders.h" +#include "setdata/elimzeroes.h" #include "placer/placer.h" #include "write/writexex.h" #include "write/headerhash.h" @@ -363,6 +364,17 @@ int main(int argc, char **argv) printf("%s Building optional headers...\n", PRINT_STEM); ret = setOptHeaders(&secInfoHeader, &peData, &optHeaderEntries, &optHeaders); + if(ret == ERR_OUT_OF_MEM) + { + printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); + fclose(basefile); + fclose(xex); + return -1; + } + + printf("%s Eliminating zeroes...\n", PRINT_STEM); + ret = eliminateZeroes(basefile, &(optHeaders.basefileFormat)); + if(ret == ERR_OUT_OF_MEM) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); diff --git a/src/setdata/elimzeroes.c b/src/setdata/elimzeroes.c new file mode 100644 index 0000000..fc6583a --- /dev/null +++ b/src/setdata/elimzeroes.c @@ -0,0 +1,93 @@ +// 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 "elimzeroes.h" + +// Specify which parts of the basefile are just zeroes, to be eliminated. +// This is somewhat inefficient and definitely an abuse of the read syscall, +// so if anyone has a better implementation, feel free to contribute. +int eliminateZeroes(FILE *basefile, struct basefileFormat *basefileFormat) +{ + bool continuingPrevLoop = false; + + fseek(basefile, 0, SEEK_SET); + + basefileFormat->zeroEliminations = malloc(1 * sizeof(struct zeroEliminations)); // free'd in writeXEX + if(basefileFormat->zeroEliminations == NULL) {return ERR_OUT_OF_MEM;} + + for(int i = 0;; i++) + { + uint8_t nextByte = 1; + basefileFormat->zeroEliminations = realloc(basefileFormat->zeroEliminations, (i + 1) * sizeof(struct zeroEliminations)); + if(basefileFormat->zeroEliminations == NULL) {return ERR_OUT_OF_MEM;} + + // If we've not already cleared the data for use in this set of fields, do so + // Also, update the size for the basefile format and the number of sets of fields + if(!continuingPrevLoop) + { + basefileFormat->size += 0x8; + basefileFormat->zeroElimCount += 1; + basefileFormat->zeroEliminations[i].dataSize = 0; + basefileFormat->zeroEliminations[i].zeroSize = 0; + } + + continuingPrevLoop = false; + + // Data size field + while(nextByte != 0) + { + if(fread(&nextByte, sizeof(uint8_t), 1, basefile) < 1) + { + return SUCCESS; + } + + if(nextByte != 0) + { + basefileFormat->zeroEliminations[i].dataSize++; + } + } + + fseek(basefile, -1, SEEK_CUR); + + // Zero size field + while(nextByte == 0) + { + if(fread(&nextByte, sizeof(uint8_t), 1, basefile) < 1) + { + return SUCCESS; + } + + if(nextByte == 0) + { + basefileFormat->zeroEliminations[i].zeroSize++; + } + } + + fseek(basefile, -1, SEEK_CUR); + + // Deciding whether to eliminate zeroes or not (is there less than 8 bytes worth of them?) + if(basefileFormat->zeroEliminations[i].zeroSize <= 8) // If we don't have enough zeroes, continue in the current set of fields + { + basefileFormat->zeroEliminations[i].dataSize += basefileFormat->zeroEliminations[i].zeroSize; + basefileFormat->zeroEliminations[i].zeroSize = 0; + i--; + continuingPrevLoop = true; + continue; + } + } +} diff --git a/src/setdata/elimzeroes.h b/src/setdata/elimzeroes.h new file mode 100644 index 0000000..da76ef7 --- /dev/null +++ b/src/setdata/elimzeroes.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 eliminateZeroes(FILE *basefile, struct basefileFormat *basefileFormat); diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index 0ce9caa..d9208fd 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 @@ -18,13 +18,12 @@ #include "optheaders.h" -void setBasefileFormat(struct basefileFormat *basefileFormat, struct secInfoHeader *secInfoHeader) +void setBasefileFormat(struct basefileFormat *basefileFormat) { - basefileFormat->size = (1 * 8) + 8; // (Block count * size of raw data descriptor) + size of data descriptor + basefileFormat->size = 0x8; // This will be increased when zero-elimination is done 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->zeroElimCount = 0; // Starting from 0, will be increased when zero-elimination is done } // STUB. TLS info not supported. @@ -54,7 +53,7 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;} // First optional header (basefile format) - setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader); + setBasefileFormat(&(optHeaders->basefileFormat)); optHeaderEntries->optHeaderEntry[0].id = XEX_OPT_ID_BASEFILE_FORMAT; // Second optional header (entrypoint) diff --git a/src/write/writexex.c b/src/write/writexex.c index 4199bb2..30263b0 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 @@ -113,11 +113,16 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr optHeaders->basefileFormat.size = htonl(optHeaders->basefileFormat.size); optHeaders->basefileFormat.encType = htons(optHeaders->basefileFormat.encType); optHeaders->basefileFormat.compType = htons(optHeaders->basefileFormat.compType); - optHeaders->basefileFormat.dataSize = htonl(optHeaders->basefileFormat.dataSize); - optHeaders->basefileFormat.zeroSize = htonl(optHeaders->basefileFormat.zeroSize); + + for(uint32_t i = 0; i < optHeaders->basefileFormat.zeroElimCount; i++) + { + optHeaders->basefileFormat.zeroEliminations[i].dataSize = htonl(optHeaders->basefileFormat.zeroEliminations[i].dataSize); + optHeaders->basefileFormat.zeroEliminations[i].zeroSize = htonl(optHeaders->basefileFormat.zeroEliminations[i].zeroSize); + } #endif - fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), sizeof(struct basefileFormat), xex); + fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), 0x8, xex); // Write basefile format header + fwrite(optHeaders->basefileFormat.zeroEliminations, sizeof(struct zeroEliminations), optHeaders->basefileFormat.zeroElimCount, xex); // Write zero elimination information currentHeader++; } @@ -148,7 +153,9 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex); } + + free(optHeaders->basefileFormat.zeroEliminations); // Alloc'd in elimzeroes. free(offsets->optHeaders); // Alloc'd in placer. return SUCCESS; } From 65e10e8782616d35a7eaf93db7730afcc574a378 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 23 Feb 2025 18:05:54 +0000 Subject: [PATCH 03/50] Fix bug in placer, where optional headers may end up overwriting each other --- src/placer/placer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/placer/placer.c b/src/placer/placer.c index 60700ee..fa16232 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -38,9 +38,9 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea switch(optHeaderEntries->optHeaderEntry[i].id) { case XEX_OPT_ID_BASEFILE_FORMAT: - *currentOffset += sizeof(struct basefileFormat); optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += (sizeof(struct basefileFormat) - sizeof(uint32_t) - sizeof(void*)) + (optHeaders->basefileFormat.zeroElimCount * sizeof(struct zeroEliminations)); sepHeader++; break; @@ -51,9 +51,9 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea break; case XEX_OPT_ID_TLS_INFO: - *currentOffset += sizeof(struct tlsInfo); optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; offsets->optHeaders[sepHeader] = *currentOffset; + *currentOffset += sizeof(struct tlsInfo); sepHeader++; break; } From 3d61b9b24f6f54b97178314635089d75fe398607 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Sun, 23 Feb 2025 22:28:50 +0000 Subject: [PATCH 04/50] Roll back zero-elimination support (it only seems to work between PE sections, not within them, making it effectively useless for the structure of XEX generated by FreeChainXenon) --- CMakeLists.txt | 3 -- src/common/datastorage.h | 10 +---- src/main.c | 12 ------ src/placer/placer.c | 2 +- src/setdata/elimzeroes.c | 93 ---------------------------------------- src/setdata/elimzeroes.h | 24 ----------- src/setdata/optheaders.c | 11 ++--- src/write/writexex.c | 15 ++----- 8 files changed, 13 insertions(+), 157 deletions(-) delete mode 100644 src/setdata/elimzeroes.c delete mode 100644 src/setdata/elimzeroes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9916c58..0d01b84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,12 +40,9 @@ 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/elimzeroes.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/setdata/elimzeroes.c) - list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.h) list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.h) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index faa9edc..5d778a9 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -145,19 +145,13 @@ struct __attribute__((packed)) secInfoHeader struct pageDescriptor *descriptors; }; -struct __attribute__((packed)) zeroEliminations -{ - uint32_t dataSize; - uint32_t zeroSize; -}; - struct __attribute__((packed)) basefileFormat { uint32_t size; uint16_t encType; uint16_t compType; - uint32_t zeroElimCount; - struct zeroEliminations *zeroEliminations; + uint32_t dataSize; + uint32_t zeroSize; }; struct __attribute__((packed)) importLibraries diff --git a/src/main.c b/src/main.c index 4778612..619fa43 100644 --- a/src/main.c +++ b/src/main.c @@ -23,7 +23,6 @@ #include "setdata/populateheaders.h" #include "setdata/pagedescriptors.h" #include "setdata/optheaders.h" -#include "setdata/elimzeroes.h" #include "placer/placer.h" #include "write/writexex.h" #include "write/headerhash.h" @@ -364,17 +363,6 @@ int main(int argc, char **argv) printf("%s Building optional headers...\n", PRINT_STEM); ret = setOptHeaders(&secInfoHeader, &peData, &optHeaderEntries, &optHeaders); - if(ret == ERR_OUT_OF_MEM) - { - printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); - fclose(basefile); - fclose(xex); - return -1; - } - - printf("%s Eliminating zeroes...\n", PRINT_STEM); - ret = eliminateZeroes(basefile, &(optHeaders.basefileFormat)); - if(ret == ERR_OUT_OF_MEM) { printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM); diff --git a/src/placer/placer.c b/src/placer/placer.c index fa16232..baf4854 100644 --- a/src/placer/placer.c +++ b/src/placer/placer.c @@ -40,7 +40,7 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea case XEX_OPT_ID_BASEFILE_FORMAT: optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset; offsets->optHeaders[sepHeader] = *currentOffset; - *currentOffset += (sizeof(struct basefileFormat) - sizeof(uint32_t) - sizeof(void*)) + (optHeaders->basefileFormat.zeroElimCount * sizeof(struct zeroEliminations)); + *currentOffset += sizeof(struct basefileFormat); sepHeader++; break; diff --git a/src/setdata/elimzeroes.c b/src/setdata/elimzeroes.c deleted file mode 100644 index fc6583a..0000000 --- a/src/setdata/elimzeroes.c +++ /dev/null @@ -1,93 +0,0 @@ -// 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 "elimzeroes.h" - -// Specify which parts of the basefile are just zeroes, to be eliminated. -// This is somewhat inefficient and definitely an abuse of the read syscall, -// so if anyone has a better implementation, feel free to contribute. -int eliminateZeroes(FILE *basefile, struct basefileFormat *basefileFormat) -{ - bool continuingPrevLoop = false; - - fseek(basefile, 0, SEEK_SET); - - basefileFormat->zeroEliminations = malloc(1 * sizeof(struct zeroEliminations)); // free'd in writeXEX - if(basefileFormat->zeroEliminations == NULL) {return ERR_OUT_OF_MEM;} - - for(int i = 0;; i++) - { - uint8_t nextByte = 1; - basefileFormat->zeroEliminations = realloc(basefileFormat->zeroEliminations, (i + 1) * sizeof(struct zeroEliminations)); - if(basefileFormat->zeroEliminations == NULL) {return ERR_OUT_OF_MEM;} - - // If we've not already cleared the data for use in this set of fields, do so - // Also, update the size for the basefile format and the number of sets of fields - if(!continuingPrevLoop) - { - basefileFormat->size += 0x8; - basefileFormat->zeroElimCount += 1; - basefileFormat->zeroEliminations[i].dataSize = 0; - basefileFormat->zeroEliminations[i].zeroSize = 0; - } - - continuingPrevLoop = false; - - // Data size field - while(nextByte != 0) - { - if(fread(&nextByte, sizeof(uint8_t), 1, basefile) < 1) - { - return SUCCESS; - } - - if(nextByte != 0) - { - basefileFormat->zeroEliminations[i].dataSize++; - } - } - - fseek(basefile, -1, SEEK_CUR); - - // Zero size field - while(nextByte == 0) - { - if(fread(&nextByte, sizeof(uint8_t), 1, basefile) < 1) - { - return SUCCESS; - } - - if(nextByte == 0) - { - basefileFormat->zeroEliminations[i].zeroSize++; - } - } - - fseek(basefile, -1, SEEK_CUR); - - // Deciding whether to eliminate zeroes or not (is there less than 8 bytes worth of them?) - if(basefileFormat->zeroEliminations[i].zeroSize <= 8) // If we don't have enough zeroes, continue in the current set of fields - { - basefileFormat->zeroEliminations[i].dataSize += basefileFormat->zeroEliminations[i].zeroSize; - basefileFormat->zeroEliminations[i].zeroSize = 0; - i--; - continuingPrevLoop = true; - continue; - } - } -} diff --git a/src/setdata/elimzeroes.h b/src/setdata/elimzeroes.h deleted file mode 100644 index da76ef7..0000000 --- a/src/setdata/elimzeroes.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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 eliminateZeroes(FILE *basefile, struct basefileFormat *basefileFormat); diff --git a/src/setdata/optheaders.c b/src/setdata/optheaders.c index d9208fd..0ce9caa 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-25 Aiden Isik +// Copyright (c) 2024 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,12 +18,13 @@ #include "optheaders.h" -void setBasefileFormat(struct basefileFormat *basefileFormat) +void setBasefileFormat(struct basefileFormat *basefileFormat, struct secInfoHeader *secInfoHeader) { - basefileFormat->size = 0x8; // This will be increased when zero-elimination is done + 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->zeroElimCount = 0; // Starting from 0, will be increased when zero-elimination is done + 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. @@ -53,7 +54,7 @@ int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, st if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;} // First optional header (basefile format) - setBasefileFormat(&(optHeaders->basefileFormat)); + setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader); optHeaderEntries->optHeaderEntry[0].id = XEX_OPT_ID_BASEFILE_FORMAT; // Second optional header (entrypoint) diff --git a/src/write/writexex.c b/src/write/writexex.c index 30263b0..4199bb2 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-25 Aiden Isik +// Copyright (c) 2024 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 @@ -113,16 +113,11 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr optHeaders->basefileFormat.size = htonl(optHeaders->basefileFormat.size); optHeaders->basefileFormat.encType = htons(optHeaders->basefileFormat.encType); optHeaders->basefileFormat.compType = htons(optHeaders->basefileFormat.compType); - - for(uint32_t i = 0; i < optHeaders->basefileFormat.zeroElimCount; i++) - { - optHeaders->basefileFormat.zeroEliminations[i].dataSize = htonl(optHeaders->basefileFormat.zeroEliminations[i].dataSize); - optHeaders->basefileFormat.zeroEliminations[i].zeroSize = htonl(optHeaders->basefileFormat.zeroEliminations[i].zeroSize); - } + optHeaders->basefileFormat.dataSize = htonl(optHeaders->basefileFormat.dataSize); + optHeaders->basefileFormat.zeroSize = htonl(optHeaders->basefileFormat.zeroSize); #endif - fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), 0x8, xex); // Write basefile format header - fwrite(optHeaders->basefileFormat.zeroEliminations, sizeof(struct zeroEliminations), optHeaders->basefileFormat.zeroElimCount, xex); // Write zero elimination information + fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), sizeof(struct basefileFormat), xex); currentHeader++; } @@ -153,9 +148,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex); } - - free(optHeaders->basefileFormat.zeroEliminations); // Alloc'd in elimzeroes. free(offsets->optHeaders); // Alloc'd in placer. return SUCCESS; } From bf3ee87a96ad920f62c292bbf8ee3e6e393835f9 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Mon, 24 Mar 2025 02:53:18 +0000 Subject: [PATCH 05/50] Add functionality to determine page types within the ELF section --- README.md | 8 ++- src/common/common.h | 2 +- src/common/datastorage.c | 20 ++++++ src/common/datastorage.h | 8 ++- src/getdata/getdata.c | 147 +++++++++++++++++++++++++++++++++------ 5 files changed, 160 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 4223874..ccbc2c6 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,12 @@ Build: ```make``` Install: ```sudo make install``` +## 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 + ## Licensing ### SynthXEX (src/*, CMakeLists.txt) @@ -121,4 +127,4 @@ General Public License for more details. You should have received copies of the GNU General Public License and the GNU Lesser General Public License along with this program. If -not, see http://www.gnu.org/licenses/. \ No newline at end of file +not, see http://www.gnu.org/licenses/. diff --git a/src/common/common.h b/src/common/common.h index 4f483be..410446e 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -26,7 +26,7 @@ // Program identifiers #define NAME "SynthXEX" -#define VERSION "v0.0.2" +#define VERSION "v0.0.3" #define COPYRIGHT "2024-25" #ifdef GIT_COMMIT diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 7b877d4..82ef985 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -89,3 +89,23 @@ uint32_t get32BitFromXEX(FILE *xex) return result; } + +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; + } + + // If system and file endianness don't match we need to change it +#ifdef LITTLE_ENDIAN_SYSTEM + result = ntohs(result); +#endif + + return result; +} diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 5d778a9..b827126 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -61,6 +61,11 @@ #define PE_SECTION_FLAG_READ 0x40000000 #define PE_SECTION_FLAG_WRITE 0x80000000 +// RWX flags (ELF) +#define ELF_SECTION_FLAG_WRITE 0x1 +#define ELF_SECTION_FLAG_ALLOC 0x2 +#define ELF_SECTION_FLAG_EXECUTABLE 0x4 + // RWX flags (XEX) #define XEX_SECTION_CODE 0x1 #define XEX_SECTION_RWDATA 0x2 @@ -69,7 +74,7 @@ // Page RWX flags struct sections { - uint16_t count; + uint32_t count; struct sectionPerms *sectionPerms; }; @@ -195,3 +200,4 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment); uint32_t get32BitFromPE(FILE *pe); uint16_t get16BitFromPE(FILE *pe); uint32_t get32BitFromXEX(FILE *xex); +uint16_t get16BitFromXEX(FILE *xex); diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index e230834..decc2af 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -132,32 +132,135 @@ int getSectionRwxFlags(FILE *pe, struct sections *sections) if(sections->sectionPerms == 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(uint32_t i = 0; i < sections->count; i++) { - fseek(pe, 0xC, SEEK_CUR); // Seek to RVA of section - sections->sectionPerms[i].rva = get32BitFromPE(pe); + // Check if we're part of the ELF section. If we are, handle it's segment permission flags. + char sectionName[9] = {0}; // 8 chars, followed by null terminator + fread(sectionName, sizeof(char), 0x8, pe); - fseek(pe, 0x14, SEEK_CUR); // Now progress to characteristics, where we will check flags - uint32_t characteristics = get32BitFromPE(pe); + if(!strcmp(sectionName, ".elf")) // .elf section + { + fseek(pe, 0x4, SEEK_CUR); + uint32_t elfRVA = get32BitFromPE(pe); // Store the base RVA for the ELF - if(characteristics & PE_SECTION_FLAG_EXECUTE) - { - sections->sectionPerms[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; - } - else if(characteristics & PE_SECTION_FLAG_READ) - { - sections->sectionPerms[i].permFlag = XEX_SECTION_RODATA | 0b10000; - } - else - { - return ERR_MISSING_SECTION_FLAG; - } + // Get the offset in the PE of the ELF + uint32_t elfSize = get32BitFromPE(pe); // Store the size of the ELF + uint32_t elfBaseOffset = get32BitFromPE(pe); - // Don't need to progress any more to get to beginning of next entry, as characteristics is last field + // Set the end of the current section entry as the offset to return to when we're done with the ELF + uint32_t returnToOffset = ftell(pe) + 0x10; + + // Verify that the .elf section actually contains an ELF. If it doesn't, just treat it like any other section. + fseek(pe, elfBaseOffset, SEEK_SET); + uint8_t elfMagic[4] = {0x7F, 'E', 'L', 'F'}; + uint8_t readMagic[4] = {0}; + fread(readMagic, sizeof(uint8_t), 0x4, pe); + + // If we're NOT an ELF, handle this section like any other + if(memcmp(elfMagic, readMagic, 0x4 * sizeof(uint8_t))) + { + goto notElfInSection; + } + + // Get the section table offset + fseek(pe, elfBaseOffset + 0x20, SEEK_SET); + uint32_t sectionTableOffset = get32BitFromXEX(pe); // Should probably rename this function... Used for any big-endian value. + + // Get the number of sections + fseek(pe, elfBaseOffset + 0x30, SEEK_SET); + uint16_t sectionCount = get16BitFromXEX(pe); + + // Check that the data we got makes sense (smaller than .elf section) + if(sectionTableOffset + (sectionCount * 0x28) > elfSize || sectionCount < 2) // 0x28 == size of each entry + { + goto notElfInSection; + } + + // The offsets and sizes seem to check out, start processing the permissions + fseek(pe, elfBaseOffset + sectionTableOffset + 0x28, SEEK_SET); // +0x28 to skip to index 1 (0 is empty) + sections->count += sectionCount; + sections->sectionPerms = realloc(sections->sectionPerms, sections->count * sizeof(struct sectionPerms)); + if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} + + for(uint32_t j = i; i < j + sectionCount; i++) + { + // Get the flags + fseek(pe, 0x8, SEEK_CUR); + uint32_t flags = get32BitFromXEX(pe); + + // Calculate the RVA of the current section + fseek(pe, 0x4, SEEK_CUR); + sections->sectionPerms[i].rva = get32BitFromXEX(pe) + elfRVA; + + // Translate ELF permissions to XEX permissions + if(flags & ELF_SECTION_FLAG_EXECUTABLE) + { + sections->sectionPerms[i].permFlag = XEX_SECTION_CODE | 0b10000; + } + else if(flags & ELF_SECTION_FLAG_WRITE) + { + sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; + } + else if(flags & ELF_SECTION_FLAG_ALLOC) + { + sections->sectionPerms[i].permFlag = XEX_SECTION_RODATA | 0b10000; + } + else + { + // If an ELF section doesn't have any of the above flags, it's a non-runtime section. + // Ignore it and move onto the next one. + sectionCount--; + sections->count--; + sections->sectionPerms = realloc(sections->sectionPerms, sections->count * sizeof(struct sectionPerms)); + if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} + i--; + } + + // Move to end of current entry + fseek(pe, 0x14, SEEK_CUR); + + continue; + + // This code is here so we only ever trigger it when we explicitly jump to it + notElfInSection: + // Seek back to the start of the section entry + fseek(pe, returnToOffset - 0x28, SEEK_SET); + + // Jump to the code to handle normal sections + goto normalSectionHandling; + } + + // Return to the PE section table at the start of the next entry + fseek(pe, returnToOffset, SEEK_SET); + } + else // Not the .elf section + { + normalSectionHandling: + fseek(pe, 0x4, SEEK_CUR); // Seek to RVA of section + sections->sectionPerms[i].rva = get32BitFromPE(pe); + + fseek(pe, 0x14, 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 + } + else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) + { + sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; + } + else if(characteristics & PE_SECTION_FLAG_READ) + { + sections->sectionPerms[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; From cdb37b50998e1300d96538cdfc89d963328c0748 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 15:42:36 +0100 Subject: [PATCH 06/50] Roll back ELF permission handling, it is no longer needed --- src/common/datastorage.h | 7 +- src/getdata/getdata.c | 143 ++++++--------------------------------- 2 files changed, 21 insertions(+), 129 deletions(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index b827126..327592f 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -61,11 +61,6 @@ #define PE_SECTION_FLAG_READ 0x40000000 #define PE_SECTION_FLAG_WRITE 0x80000000 -// RWX flags (ELF) -#define ELF_SECTION_FLAG_WRITE 0x1 -#define ELF_SECTION_FLAG_ALLOC 0x2 -#define ELF_SECTION_FLAG_EXECUTABLE 0x4 - // RWX flags (XEX) #define XEX_SECTION_CODE 0x1 #define XEX_SECTION_RWDATA 0x2 @@ -74,7 +69,7 @@ // Page RWX flags struct sections { - uint32_t count; + uint16_t count; struct sectionPerms *sectionPerms; }; diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index decc2af..e230834 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -132,135 +132,32 @@ int getSectionRwxFlags(FILE *pe, struct sections *sections) if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table - for(uint32_t i = 0; i < sections->count; i++) + for(uint16_t i = 0; i < sections->count; i++) { - // Check if we're part of the ELF section. If we are, handle it's segment permission flags. - char sectionName[9] = {0}; // 8 chars, followed by null terminator - fread(sectionName, sizeof(char), 0x8, pe); + fseek(pe, 0xC, SEEK_CUR); // Seek to RVA of section + sections->sectionPerms[i].rva = get32BitFromPE(pe); - if(!strcmp(sectionName, ".elf")) // .elf section + fseek(pe, 0x14, SEEK_CUR); // Now progress to characteristics, where we will check flags + uint32_t characteristics = get32BitFromPE(pe); + + if(characteristics & PE_SECTION_FLAG_EXECUTE) { - fseek(pe, 0x4, SEEK_CUR); - uint32_t elfRVA = get32BitFromPE(pe); // Store the base RVA for the ELF - - // Get the offset in the PE of the ELF - uint32_t elfSize = get32BitFromPE(pe); // Store the size of the ELF - uint32_t elfBaseOffset = get32BitFromPE(pe); - - // Set the end of the current section entry as the offset to return to when we're done with the ELF - uint32_t returnToOffset = ftell(pe) + 0x10; - - // Verify that the .elf section actually contains an ELF. If it doesn't, just treat it like any other section. - fseek(pe, elfBaseOffset, SEEK_SET); - uint8_t elfMagic[4] = {0x7F, 'E', 'L', 'F'}; - uint8_t readMagic[4] = {0}; - fread(readMagic, sizeof(uint8_t), 0x4, pe); - - // If we're NOT an ELF, handle this section like any other - if(memcmp(elfMagic, readMagic, 0x4 * sizeof(uint8_t))) - { - goto notElfInSection; - } - - // Get the section table offset - fseek(pe, elfBaseOffset + 0x20, SEEK_SET); - uint32_t sectionTableOffset = get32BitFromXEX(pe); // Should probably rename this function... Used for any big-endian value. - - // Get the number of sections - fseek(pe, elfBaseOffset + 0x30, SEEK_SET); - uint16_t sectionCount = get16BitFromXEX(pe); - - // Check that the data we got makes sense (smaller than .elf section) - if(sectionTableOffset + (sectionCount * 0x28) > elfSize || sectionCount < 2) // 0x28 == size of each entry - { - goto notElfInSection; - } - - // The offsets and sizes seem to check out, start processing the permissions - fseek(pe, elfBaseOffset + sectionTableOffset + 0x28, SEEK_SET); // +0x28 to skip to index 1 (0 is empty) - sections->count += sectionCount; - sections->sectionPerms = realloc(sections->sectionPerms, sections->count * sizeof(struct sectionPerms)); - if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} - - for(uint32_t j = i; i < j + sectionCount; i++) - { - // Get the flags - fseek(pe, 0x8, SEEK_CUR); - uint32_t flags = get32BitFromXEX(pe); - - // Calculate the RVA of the current section - fseek(pe, 0x4, SEEK_CUR); - sections->sectionPerms[i].rva = get32BitFromXEX(pe) + elfRVA; - - // Translate ELF permissions to XEX permissions - if(flags & ELF_SECTION_FLAG_EXECUTABLE) - { - sections->sectionPerms[i].permFlag = XEX_SECTION_CODE | 0b10000; - } - else if(flags & ELF_SECTION_FLAG_WRITE) - { - sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; - } - else if(flags & ELF_SECTION_FLAG_ALLOC) - { - sections->sectionPerms[i].permFlag = XEX_SECTION_RODATA | 0b10000; - } - else - { - // If an ELF section doesn't have any of the above flags, it's a non-runtime section. - // Ignore it and move onto the next one. - sectionCount--; - sections->count--; - sections->sectionPerms = realloc(sections->sectionPerms, sections->count * sizeof(struct sectionPerms)); - if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;} - i--; - } - - // Move to end of current entry - fseek(pe, 0x14, SEEK_CUR); - - continue; - - // This code is here so we only ever trigger it when we explicitly jump to it - notElfInSection: - // Seek back to the start of the section entry - fseek(pe, returnToOffset - 0x28, SEEK_SET); - - // Jump to the code to handle normal sections - goto normalSectionHandling; - } - - // Return to the PE section table at the start of the next entry - fseek(pe, returnToOffset, SEEK_SET); + sections->sectionPerms[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1 } - else // Not the .elf section + else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) { - normalSectionHandling: - fseek(pe, 0x4, SEEK_CUR); // Seek to RVA of section - sections->sectionPerms[i].rva = get32BitFromPE(pe); - - fseek(pe, 0x14, 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 - } - else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE) - { - sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; - } - else if(characteristics & PE_SECTION_FLAG_READ) - { - sections->sectionPerms[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 + sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000; } + else if(characteristics & PE_SECTION_FLAG_READ) + { + sections->sectionPerms[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; From 3108eb3d4a5d9d0a5821f2be091f676b090cf982 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 19:13:01 +0100 Subject: [PATCH 07/50] Add support for DLLs --- src/common/datastorage.h | 20 +++++++++++++++++++- src/getdata/getdata.c | 14 +++++++++++++- src/main.c | 2 +- src/setdata/populateheaders.c | 21 +++++++++++++++++++-- src/setdata/populateheaders.h | 2 +- 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 327592f..857306c 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -35,8 +35,13 @@ #define XEX_IMG_FLAG_4KIB_PAGES 0x10000000 #define XEX_IMG_FLAG_REGION_FREE 0x20000000 +// PE characteristics flags +#define PE_CHAR_FLAG_DLL 0x2000 + // Module flags -#define XEX_MOD_FLAG_TITLE 0x00000001 +#define XEX_MOD_FLAG_TITLE 0x00000001 +#define XEX_MOD_FLAG_EXPORTS 0x00000002 +#define XEX_MOD_FLAG_DLL 0x00000008 // Region flags #define XEX_REG_FLAG_REGION_FREE 0xFFFFFFFF @@ -79,6 +84,16 @@ struct sectionPerms uint32_t rva; }; +struct peImportInfo +{ + uint16_t count; +}; + +struct peExportInfo +{ + uint16_t count; +}; + // Data structs struct peData { @@ -92,7 +107,10 @@ struct peData 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. diff --git a/src/getdata/getdata.c b/src/getdata/getdata.c index e230834..1455e89 100644 --- a/src/getdata/getdata.c +++ b/src/getdata/getdata.c @@ -193,7 +193,11 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) // 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); + // Entry point (RVA) fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET); peData->entryPoint = get32BitFromPE(pe); @@ -206,6 +210,14 @@ int getHdrData(FILE *pe, struct peData *peData, uint8_t flags) 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 + + // Import tables + fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET); + peData->peImportInfo.count = (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); peData->tlsAddr = get32BitFromPE(pe); diff --git a/src/main.c b/src/main.c index 619fa43..c380cb0 100644 --- a/src/main.c +++ b/src/main.c @@ -344,7 +344,7 @@ int main(int argc, char **argv) // Setting final XEX data structs printf("%s Building XEX header...\n", PRINT_STEM); - setXEXHeader(&xexHeader); + setXEXHeader(&xexHeader, &peData); printf("%s Building security header...\n", PRINT_STEM); setSecInfoHeader(&secInfoHeader, &peData); diff --git a/src/setdata/populateheaders.c b/src/setdata/populateheaders.c index 9f51fbf..bbb68df 100644 --- a/src/setdata/populateheaders.c +++ b/src/setdata/populateheaders.c @@ -18,11 +18,28 @@ #include "populateheaders.h" -void setXEXHeader(struct xexHeader *xexHeader) +void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData) { // Writing data into XEX header. strcpy(xexHeader->magic, "XEX2"); // Magic - xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; // Hard-coding until more options supported + + // Module flags (type of executable) + 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 + } + xexHeader->optHeaderCount = 0x4; // Hard-coding until more optional headers supported, then maybe it can be determined dynamically. } diff --git a/src/setdata/populateheaders.h b/src/setdata/populateheaders.h index 2996e0b..a6fb05f 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); +void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData); void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData); From 58c7757044d1d8164966c15b7705dce70522cfa0 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 20:48:06 +0100 Subject: [PATCH 08/50] Make endian conversions more portable --- src/common/datastorage.c | 6 ++-- src/common/datastorage.h | 2 +- src/setdata/pagedescriptors.c | 4 +-- src/write/writexex.c | 54 +++++++++++++++++------------------ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/common/datastorage.c b/src/common/datastorage.c index 82ef985..e5d807e 100644 --- a/src/common/datastorage.c +++ b/src/common/datastorage.c @@ -44,7 +44,7 @@ uint32_t get32BitFromPE(FILE *pe) // If system is big endian, swap endianness (PE is LE) #ifdef BIG_ENDIAN_SYSTEM - result = htonl(result); + result = __builtin_bswap32(result); #endif return result; @@ -84,7 +84,7 @@ uint32_t get32BitFromXEX(FILE *xex) // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM - result = ntohl(result); + result = __builtin_bswap32(result); #endif return result; @@ -104,7 +104,7 @@ uint16_t get16BitFromXEX(FILE *xex) // If system and file endianness don't match we need to change it #ifdef LITTLE_ENDIAN_SYSTEM - result = ntohs(result); + result = __builtin_bswap16(result); #endif return result; diff --git a/src/common/datastorage.h b/src/common/datastorage.h index 857306c..fa6c232 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -22,7 +22,7 @@ #include "common.h" // Endian test -#if 'AB' == 0b100000101000010 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define LITTLE_ENDIAN_SYSTEM #else #define BIG_ENDIAN_SYSTEM diff --git a/src/setdata/pagedescriptors.c b/src/setdata/pagedescriptors.c index 06c4f95..9fdc41d 100644 --- a/src/setdata/pagedescriptors.c +++ b/src/setdata/pagedescriptors.c @@ -56,14 +56,14 @@ 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 = htonl(secInfoHeader->descriptors[i].sizeAndInfo); + secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); #endif sha1_update(&shaContext, pageSize, page); sha1_update(&shaContext, 0x18, (uint8_t*)&secInfoHeader->descriptors[i]); #ifdef LITTLE_ENDIAN_SYSTEM - secInfoHeader->descriptors[i].sizeAndInfo = ntohl(secInfoHeader->descriptors[i].sizeAndInfo); + secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); #endif if(i != 0) diff --git a/src/write/writexex.c b/src/write/writexex.c index 4199bb2..1007bd7 100644 --- a/src/write/writexex.c +++ b/src/write/writexex.c @@ -24,10 +24,10 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr // XEX Header #ifdef LITTLE_ENDIAN_SYSTEM // Endian-swap XEX header before writing - xexHeader->moduleFlags = htonl(xexHeader->moduleFlags); - xexHeader->peOffset = htonl(xexHeader->peOffset); - xexHeader->secInfoOffset = htonl(xexHeader->secInfoOffset); - xexHeader->optHeaderCount = htonl(xexHeader->optHeaderCount); + 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); @@ -38,8 +38,8 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr // Endian swap opt header entries for(uint32_t i = 0; i < optHeaderEntries->count; i++) { - optHeaderEntries->optHeaderEntry[i].id = htonl(optHeaderEntries->optHeaderEntry[i].id); - optHeaderEntries->optHeaderEntry[i].dataOrOffset = htonl(optHeaderEntries->optHeaderEntry[i].dataOrOffset); + optHeaderEntries->optHeaderEntry[i].id = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].id); + optHeaderEntries->optHeaderEntry[i].dataOrOffset = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].dataOrOffset); } #endif @@ -58,7 +58,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr for(int i = 0; i < secInfoHeader->pageDescCount; i++) { #ifdef LITTLE_ENDIAN_SYSTEM - secInfoHeader->descriptors[i].sizeAndInfo = htonl(secInfoHeader->descriptors[i].sizeAndInfo); + secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo); #endif // Writing out current descriptor... @@ -87,16 +87,16 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr // Security Info #ifdef LITTLE_ENDIAN_SYSTEM // Endian-swap secinfo header - secInfoHeader->headerSize = htonl(secInfoHeader->headerSize); - secInfoHeader->peSize = htonl(secInfoHeader->peSize); - secInfoHeader->imageInfoSize = htonl(secInfoHeader->imageInfoSize); - secInfoHeader->imageFlags = htonl(secInfoHeader->imageFlags); - secInfoHeader->baseAddr = htonl(secInfoHeader->baseAddr); - secInfoHeader->importTableCount = htonl(secInfoHeader->importTableCount); - secInfoHeader->exportTableAddr = htonl(secInfoHeader->exportTableAddr); - secInfoHeader->gameRegion = htonl(secInfoHeader->gameRegion); - secInfoHeader->mediaTypes = htonl(secInfoHeader->mediaTypes); - secInfoHeader->pageDescCount = htonl(secInfoHeader->pageDescCount); + 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 fseek(xex, offsets->secInfoHeader, SEEK_SET); @@ -110,11 +110,11 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); #ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->basefileFormat.size = htonl(optHeaders->basefileFormat.size); - optHeaders->basefileFormat.encType = htons(optHeaders->basefileFormat.encType); - optHeaders->basefileFormat.compType = htons(optHeaders->basefileFormat.compType); - optHeaders->basefileFormat.dataSize = htonl(optHeaders->basefileFormat.dataSize); - optHeaders->basefileFormat.zeroSize = htonl(optHeaders->basefileFormat.zeroSize); + 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); @@ -127,7 +127,7 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr uint32_t importLibsSize = optHeaders->importLibraries.size; // Need to use this value, so save it before we endian-swap #ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->importLibraries.size = htonl(optHeaders->importLibraries.size); + optHeaders->importLibraries.size = __builtin_bswap32(optHeaders->importLibraries.size); #endif fwrite(&(optHeaders->importLibraries.size), sizeof(uint8_t), 0x4, xex); @@ -140,10 +140,10 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET); #ifdef LITTLE_ENDIAN_SYSTEM - optHeaders->tlsInfo.slotCount = htonl(optHeaders->tlsInfo.slotCount); - optHeaders->tlsInfo.rawDataAddr = htonl(optHeaders->tlsInfo.rawDataAddr); - optHeaders->tlsInfo.dataSize = htonl(optHeaders->tlsInfo.dataSize); - optHeaders->tlsInfo.rawDataSize = htonl(optHeaders->tlsInfo.rawDataSize); + 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); From ecde90741c365e7e0f01c5a293ff67393d6ebfab Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 21:03:45 +0100 Subject: [PATCH 09/50] Remove redundant header include --- src/common/datastorage.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index fa6c232..b2ea146 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -18,7 +18,6 @@ #pragma once -#include // For endian-swapping #include "common.h" // Endian test From 4978378d31adb32ceaa424271aa136beba42e8a7 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 21:17:41 +0100 Subject: [PATCH 10/50] Check endianness more thoroughly, for completeness' sake --- src/common/datastorage.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/datastorage.h b/src/common/datastorage.h index b2ea146..0f2aa77 100644 --- a/src/common/datastorage.h +++ b/src/common/datastorage.h @@ -23,8 +23,10 @@ // Endian test #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define LITTLE_ENDIAN_SYSTEM -#else +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define BIG_ENDIAN_SYSTEM +#else +#error "System is not big or little endian! SynthXEX does not support this archaic dinosaur, sorry!" #endif // Preprocessor definitions From 27f8afec9a4cdfd9309e1467a3d49f51cbaf6b24 Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Thu, 29 May 2025 23:03:39 +0100 Subject: [PATCH 11/50] Strip down & integrate Nettle into source tree --- CMakeLists.txt | 10 +- README.md | 5 +- include/nettle/COPYING.LESSERv3 | 165 ++++++++ include/nettle/COPYINGv2 | 339 ++++++++++++++++ include/nettle/COPYINGv3 | 674 ++++++++++++++++++++++++++++++++ include/nettle/README | 58 +++ include/nettle/macros.h | 246 ++++++++++++ include/nettle/nettle-types.h | 131 +++++++ include/nettle/nettle-write.h | 57 +++ include/nettle/sha1-compress.c | 260 ++++++++++++ include/nettle/sha1.c | 100 +++++ include/nettle/sha1.h | 92 +++++ include/nettle/write-be32.c | 77 ++++ 13 files changed, 2209 insertions(+), 5 deletions(-) create mode 100644 include/nettle/COPYING.LESSERv3 create mode 100644 include/nettle/COPYINGv2 create mode 100644 include/nettle/COPYINGv3 create mode 100644 include/nettle/README create mode 100644 include/nettle/macros.h create mode 100644 include/nettle/nettle-types.h create mode 100644 include/nettle/nettle-write.h create mode 100644 include/nettle/sha1-compress.c create mode 100644 include/nettle/sha1.c create mode 100644 include/nettle/sha1.h create mode 100644 include/nettle/write-be32.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d01b84..168d580 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,11 +52,17 @@ 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... add_executable(synthxex ${allsources}) target_include_directories(synthxex PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(synthxex PRIVATE libnettle.a) # SynthXEX uses the nettle library for it's cryptography -target_compile_options(synthxex PRIVATE -Wno-multichar) # Shut up the torrent of warnings from the endian check # If we're doing a debug build, compile with debugging and git commit hash if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) diff --git a/README.md b/README.md index ccbc2c6..c04ffc9 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,9 @@ SynthXEX is part of the FreeChainXenon toolchain. It's installer is located [her - A C compiler (GCC/Clang) - CMake (>= 3.25.1) - Make -- GNU Nettle development libraries - Git -To install these on Debian: ```sudo apt install gcc cmake make nettle-dev git``` +To install these on Debian: ```sudo apt install gcc cmake make git``` ### Downloading @@ -99,7 +98,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 (SHA1) +### GNU Nettle (include/nettle/*) Copyright (C) 2001, 2013 Niels Möller diff --git a/include/nettle/COPYING.LESSERv3 b/include/nettle/COPYING.LESSERv3 new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/include/nettle/COPYING.LESSERv3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/include/nettle/COPYINGv2 b/include/nettle/COPYINGv2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/include/nettle/COPYINGv2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/include/nettle/COPYINGv3 b/include/nettle/COPYINGv3 new file mode 100644 index 0000000..2a00065 --- /dev/null +++ b/include/nettle/COPYINGv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/include/nettle/README b/include/nettle/README new file mode 100644 index 0000000..0264cd2 --- /dev/null +++ b/include/nettle/README @@ -0,0 +1,58 @@ +MODIFIED BY AIDEN ISIK + +Changes: +- Stripped out everything except requirements for SHA1 hashing + +------------------------- + +What is GNU Nettle? A quote from the introduction in the Nettle Manual: + + Nettle is a cryptographic library that is designed to fit easily in more + or less any context: In crypto toolkits for object-oriented languages + (C++, Python, Pike, ...), in applications like LSH or GNUPG, or even in + kernel space. In most contexts, you need more than the basic + cryptographic algorithms, you also need some way to keep track of available + algorithms, their properties and variants. You often have some algorithm + selection process, often dictated by a protocol you want to implement. + + And as the requirements of applications differ in subtle and not so + subtle ways, an API that fits one application well can be a pain to use + in a different context. And that is why there are so many different + cryptographic libraries around. + + Nettle tries to avoid this problem by doing one thing, the low-level + crypto stuff, and providing a simple but general interface to it. + In particular, Nettle doesn't do algorithm selection. It doesn't do + memory allocation. It doesn't do any I/O. + + The idea is that one can build several application and context specific + interfaces on top of Nettle, and share the code, test cases, benchmarks, + documentation, etc. Examples are the Nettle module for the Pike + language, and LSH, which both use an object-oriented abstraction on top + of the library. + +GNU Nettle is free software; you can redistribute it and/or modify it +under the terms contained in the files COPYING* (see the manual for +information on how these licenses apply). + +If you have downloaded a Nettle release, build it with the usual +./configure && make && make check && make install (see the INSTALL +file for further instructions). Using GNU make is strongly +recommended. Nettle's support for public key algorithms, such as RSA +and ECDSA, depends on the GNU GMP library. + +You can also get Nettle from git, see +http://www.lysator.liu.se/~nisse/nettle/ for current instructions. In +particular, you need to run the ./.bootstrap script after checkout and +before running ./configure. + +Read the manual. Mail me if you have any questions or suggestions. + +You may want to subscribe to the nettle-bugs mailing list. See +. + +See CONTRIBUTING.md for information on contibuting patches. + + +Happy hacking, +/Niels Möller diff --git a/include/nettle/macros.h b/include/nettle/macros.h new file mode 100644 index 0000000..320f61c --- /dev/null +++ b/include/nettle/macros.h @@ -0,0 +1,246 @@ +/* macros.h + + Copyright (C) 2001, 2010 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_MACROS_H_INCLUDED +#define NETTLE_MACROS_H_INCLUDED + +/* Reads a 64-bit integer, in network, big-endian, byte order */ +#define READ_UINT64(p) \ +( (((uint64_t) (p)[0]) << 56) \ + | (((uint64_t) (p)[1]) << 48) \ + | (((uint64_t) (p)[2]) << 40) \ + | (((uint64_t) (p)[3]) << 32) \ + | (((uint64_t) (p)[4]) << 24) \ + | (((uint64_t) (p)[5]) << 16) \ + | (((uint64_t) (p)[6]) << 8) \ + | ((uint64_t) (p)[7])) + +#define WRITE_UINT64(p, i) \ +do { \ + (p)[0] = ((i) >> 56) & 0xff; \ + (p)[1] = ((i) >> 48) & 0xff; \ + (p)[2] = ((i) >> 40) & 0xff; \ + (p)[3] = ((i) >> 32) & 0xff; \ + (p)[4] = ((i) >> 24) & 0xff; \ + (p)[5] = ((i) >> 16) & 0xff; \ + (p)[6] = ((i) >> 8) & 0xff; \ + (p)[7] = (i) & 0xff; \ +} while(0) + +/* Reads a 32-bit integer, in network, big-endian, byte order */ +#define READ_UINT32(p) \ +( (((uint32_t) (p)[0]) << 24) \ + | (((uint32_t) (p)[1]) << 16) \ + | (((uint32_t) (p)[2]) << 8) \ + | ((uint32_t) (p)[3])) + +#define WRITE_UINT32(p, i) \ +do { \ + (p)[0] = ((i) >> 24) & 0xff; \ + (p)[1] = ((i) >> 16) & 0xff; \ + (p)[2] = ((i) >> 8) & 0xff; \ + (p)[3] = (i) & 0xff; \ +} while(0) + +/* Analogous macros, for 24 and 16 bit numbers */ +#define READ_UINT24(p) \ +( (((uint32_t) (p)[0]) << 16) \ + | (((uint32_t) (p)[1]) << 8) \ + | ((uint32_t) (p)[2])) + +#define WRITE_UINT24(p, i) \ +do { \ + (p)[0] = ((i) >> 16) & 0xff; \ + (p)[1] = ((i) >> 8) & 0xff; \ + (p)[2] = (i) & 0xff; \ +} while(0) + +#define READ_UINT16(p) \ +( (((uint32_t) (p)[0]) << 8) \ + | ((uint32_t) (p)[1])) + +#define WRITE_UINT16(p, i) \ +do { \ + (p)[0] = ((i) >> 8) & 0xff; \ + (p)[1] = (i) & 0xff; \ +} while(0) + +/* And the other, little-endian, byteorder */ +#define LE_READ_UINT64(p) \ +( (((uint64_t) (p)[7]) << 56) \ + | (((uint64_t) (p)[6]) << 48) \ + | (((uint64_t) (p)[5]) << 40) \ + | (((uint64_t) (p)[4]) << 32) \ + | (((uint64_t) (p)[3]) << 24) \ + | (((uint64_t) (p)[2]) << 16) \ + | (((uint64_t) (p)[1]) << 8) \ + | ((uint64_t) (p)[0])) + +#define LE_WRITE_UINT64(p, i) \ +do { \ + (p)[7] = ((i) >> 56) & 0xff; \ + (p)[6] = ((i) >> 48) & 0xff; \ + (p)[5] = ((i) >> 40) & 0xff; \ + (p)[4] = ((i) >> 32) & 0xff; \ + (p)[3] = ((i) >> 24) & 0xff; \ + (p)[2] = ((i) >> 16) & 0xff; \ + (p)[1] = ((i) >> 8) & 0xff; \ + (p)[0] = (i) & 0xff; \ +} while (0) + +#define LE_READ_UINT32(p) \ +( (((uint32_t) (p)[3]) << 24) \ + | (((uint32_t) (p)[2]) << 16) \ + | (((uint32_t) (p)[1]) << 8) \ + | ((uint32_t) (p)[0])) + +#define LE_WRITE_UINT32(p, i) \ +do { \ + (p)[3] = ((i) >> 24) & 0xff; \ + (p)[2] = ((i) >> 16) & 0xff; \ + (p)[1] = ((i) >> 8) & 0xff; \ + (p)[0] = (i) & 0xff; \ +} while(0) + +/* Analogous macros, for 16 bit numbers */ +#define LE_READ_UINT16(p) \ + ( (((uint32_t) (p)[1]) << 8) \ + | ((uint32_t) (p)[0])) + +#define LE_WRITE_UINT16(p, i) \ + do { \ + (p)[1] = ((i) >> 8) & 0xff; \ + (p)[0] = (i) & 0xff; \ + } while(0) + +/* Macro to make it easier to loop over several blocks. */ +#define FOR_BLOCKS(length, dst, src, blocksize) \ + assert( !((length) % (blocksize))); \ + for (; (length); ((length) -= (blocksize), \ + (dst) += (blocksize), \ + (src) += (blocksize)) ) + +/* The masking of the right shift is needed to allow n == 0 (using + just 32 - n and 64 - n results in undefined behaviour). Most uses + of these macros use a constant and non-zero rotation count. */ +#define ROTL32(n,x) (((x)<<(n)) | ((x)>>((-(n)&31)))) + +#define ROTL64(n,x) (((x)<<(n)) | ((x)>>((-(n))&63))) + +/* Requires that size > 0 */ +#define INCREMENT(size, ctr) \ + do { \ + unsigned increment_i = (size) - 1; \ + if (++(ctr)[increment_i] == 0) \ + while (increment_i > 0 \ + && ++(ctr)[--increment_i] == 0 ) \ + ; \ + } while (0) + + +/* Helper macro for Merkle-Damgård hash functions. Assumes the context + structs includes the following fields: + + uint8_t block[...]; // Buffer holding one block + unsigned int index; // Index into block +*/ + +/* Currently used by sha512 (and sha384) only. */ +#define MD_INCR(ctx) ((ctx)->count_high += !++(ctx)->count_low) + +/* Takes the compression function f as argument. NOTE: also clobbers + length and data. */ +#define MD_UPDATE(ctx, length, data, f, incr) \ + do { \ + if (!length) goto __md_done; \ + if ((ctx)->index) \ + { \ + /* Try to fill partial block */ \ + unsigned __md_left = sizeof((ctx)->block) - (ctx)->index; \ + if ((length) < __md_left) \ + { \ + memcpy((ctx)->block + (ctx)->index, (data), (length)); \ + (ctx)->index += (length); \ + goto __md_done; /* Finished */ \ + } \ + else \ + { \ + memcpy((ctx)->block + (ctx)->index, (data), __md_left); \ + \ + f((ctx), (ctx)->block); \ + (incr); \ + \ + (data) += __md_left; \ + (length) -= __md_left; \ + } \ + } \ + while ((length) >= sizeof((ctx)->block)) \ + { \ + f((ctx), (data)); \ + (incr); \ + \ + (data) += sizeof((ctx)->block); \ + (length) -= sizeof((ctx)->block); \ + } \ + memcpy ((ctx)->block, (data), (length)); \ + (ctx)->index = (length); \ + __md_done: \ + ; \ + } while (0) + +/* Pads the block to a block boundary with the bit pattern 1 0*, + leaving size octets for the length field at the end. If needed, + compresses the block and starts a new one. */ +#define MD_PAD(ctx, size, f) \ + do { \ + unsigned __md_i; \ + __md_i = (ctx)->index; \ + \ + /* Set the first char of padding to 0x80. This is safe since there \ + is always at least one byte free */ \ + \ + assert(__md_i < sizeof((ctx)->block)); \ + (ctx)->block[__md_i++] = 0x80; \ + \ + if (__md_i > (sizeof((ctx)->block) - (size))) \ + { /* No room for length in this block. Process it and \ + pad with another one */ \ + memset((ctx)->block + __md_i, 0, sizeof((ctx)->block) - __md_i); \ + \ + f((ctx), (ctx)->block); \ + __md_i = 0; \ + } \ + memset((ctx)->block + __md_i, 0, \ + sizeof((ctx)->block) - (size) - __md_i); \ + \ + } while (0) + +#endif /* NETTLE_MACROS_H_INCLUDED */ diff --git a/include/nettle/nettle-types.h b/include/nettle/nettle-types.h new file mode 100644 index 0000000..75d9a56 --- /dev/null +++ b/include/nettle/nettle-types.h @@ -0,0 +1,131 @@ +/* nettle-types.h + + Copyright (C) 2005, 2014 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_TYPES_H +#define NETTLE_TYPES_H + +/* For size_t */ +#include +#include + +/* Attributes we want to use in installed header files, and hence + can't rely on config.h. */ +#ifdef __GNUC__ + +#define _NETTLE_ATTRIBUTE_PURE __attribute__((pure)) +#ifndef _NETTLE_ATTRIBUTE_DEPRECATED +/* Variant without message is supported since gcc-3.1 or so. */ +#define _NETTLE_ATTRIBUTE_DEPRECATED __attribute__((deprecated)) +#endif + +#else /* !__GNUC__ */ + +#define _NETTLE_ATTRIBUTE_PURE +#define _NETTLE_ATTRIBUTE_DEPRECATED + +#endif /* !__GNUC__ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* An aligned 16-byte block. */ +union nettle_block16 +{ + uint8_t b[16]; + unsigned long w[16 / sizeof(unsigned long)] _NETTLE_ATTRIBUTE_DEPRECATED; + uint64_t u64[2]; +}; + +union nettle_block8 +{ + uint8_t b[8]; + uint64_t u64; +}; + +/* Randomness. Used by key generation and dsa signature creation. */ +typedef void nettle_random_func(void *ctx, + size_t length, uint8_t *dst); + +/* Progress report function, mainly for key generation. */ +typedef void nettle_progress_func(void *ctx, int c); + +/* Realloc function, used by struct nettle_buffer. */ +typedef void *nettle_realloc_func(void *ctx, void *p, size_t length); + +/* Ciphers */ +typedef void nettle_set_key_func(void *ctx, const uint8_t *key); + +/* For block ciphers, const context. */ +typedef void nettle_cipher_func(const void *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); + + +/* Uses a void * for cipher contexts. Used for crypt operations where + the internal state changes during the encryption. */ +typedef void nettle_crypt_func(void *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); + +/* Hash algorithms */ +typedef void nettle_hash_init_func(void *ctx); +typedef void nettle_hash_update_func(void *ctx, + size_t length, + const uint8_t *src); +typedef void nettle_hash_digest_func(void *ctx, + size_t length, uint8_t *dst); + +/* ASCII armor codecs. NOTE: Experimental and subject to change. */ + +typedef size_t nettle_armor_length_func(size_t length); +typedef void nettle_armor_init_func(void *ctx); + +typedef size_t nettle_armor_encode_update_func(void *ctx, + char *dst, + size_t src_length, + const uint8_t *src); + +typedef size_t nettle_armor_encode_final_func(void *ctx, char *dst); + +typedef int nettle_armor_decode_update_func(void *ctx, + size_t *dst_length, + uint8_t *dst, + size_t src_length, + const char *src); + +typedef int nettle_armor_decode_final_func(void *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_TYPES_H */ diff --git a/include/nettle/nettle-write.h b/include/nettle/nettle-write.h new file mode 100644 index 0000000..48f0ea7 --- /dev/null +++ b/include/nettle/nettle-write.h @@ -0,0 +1,57 @@ +/* nettle-write.h + + Internal functions to write out word-sized data to byte arrays. + + Copyright (C) 2010 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_WRITE_H_INCLUDED +#define NETTLE_WRITE_H_INCLUDED + +/* For size_t */ +#include +#include + +/* Write the word array at SRC to the byte array at DST, using little + endian (le) or big endian (be) byte order, and truncating the + result to LENGTH bytes. */ + +/* FIXME: Use a macro shortcut to memcpy for native endianness. */ +void +_nettle_write_be32(size_t length, uint8_t *dst, + const uint32_t *src); +void +_nettle_write_le32(size_t length, uint8_t *dst, + const uint32_t *src); + +void +_nettle_write_le64(size_t length, uint8_t *dst, + const uint64_t *src); + +#endif /* NETTLE_WRITE_H_INCLUDED */ diff --git a/include/nettle/sha1-compress.c b/include/nettle/sha1-compress.c new file mode 100644 index 0000000..43fcbf0 --- /dev/null +++ b/include/nettle/sha1-compress.c @@ -0,0 +1,260 @@ +/* sha1-compress.c + + The compression function of the sha1 hash function. + + Copyright (C) 2001, 2004 Peter Gutmann, Andrew Kuchling, Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +/* Here's the first paragraph of Peter Gutmann's posting, + * <30ajo5$oe8@ccu2.auckland.ac.nz>: + * + * The following is my SHA (FIPS 180) code updated to allow use of the "fixed" + * SHA, thanks to Jim Gillogly and an anonymous contributor for the information on + * what's changed in the new version. The fix is a simple change which involves + * adding a single rotate in the initial expansion function. It is unknown + * whether this is an optimal solution to the problem which was discovered in the + * SHA or whether it's simply a bandaid which fixes the problem with a minimum of + * effort (for example the reengineering of a great many Capstone chips). + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef SHA1_DEBUG +# define SHA1_DEBUG 0 +#endif + +#if SHA1_DEBUG +# include +# define DEBUG(i) \ + fprintf(stderr, "%2d: %8x %8x %8x %8x %8x\n", i, A, B, C, D ,E) +#else +# define DEBUG(i) +#endif + +#include +#include +#include + +#include "sha1.h" + +#include "macros.h" + +/* A block, treated as a sequence of 32-bit words. */ +#define SHA1_DATA_LENGTH 16 + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/* FIXME: Can save a temporary in f3 by using ( (x & y) + (z & (x ^ + y)) ), and then, in the round, compute one of the terms and add it + into the destination word before computing the second term. Credits + to George Spelvin for pointing this out. Unfortunately, gcc + doesn't seem to be smart enough to take advantage of this. */ + +/* #define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) Rounds 0-19 */ +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +/* #define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) Rounds 40-59 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4 f2 + +/* The SHA Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHA changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#define expand(W,i) ( W[ i & 15 ] = \ + ROTL32( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + + +/* The prototype SHA sub-round. The fundamental sub-round is: + + a' = e + ROTL32( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL32( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL32( 5, a ) + f( b, c, d ) + k + data, b = ROTL32( 30, b ) ) + +/* For fat builds */ +#if HAVE_NATIVE_sha1_compress +void +_nettle_sha1_compress_c(uint32_t *state, const uint8_t *input); +#define nettle_sha1_compress _nettle_sha1_compress_c +#endif + +/* Perform the SHA transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds. */ + +void +nettle_sha1_compress(uint32_t *state, const uint8_t *input) +{ + uint32_t data[SHA1_DATA_LENGTH]; + uint32_t A, B, C, D, E; /* Local vars */ + int i; + + for (i = 0; i < SHA1_DATA_LENGTH; i++, input+= 4) + { + data[i] = READ_UINT32(input); + } + + /* Set up first buffer and local data buffer */ + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + E = state[4]; + + DEBUG(-1); + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, data[ 0] ); DEBUG(0); + subRound( E, A, B, C, D, f1, K1, data[ 1] ); DEBUG(1); + subRound( D, E, A, B, C, f1, K1, data[ 2] ); + subRound( C, D, E, A, B, f1, K1, data[ 3] ); + subRound( B, C, D, E, A, f1, K1, data[ 4] ); + subRound( A, B, C, D, E, f1, K1, data[ 5] ); + subRound( E, A, B, C, D, f1, K1, data[ 6] ); + subRound( D, E, A, B, C, f1, K1, data[ 7] ); + subRound( C, D, E, A, B, f1, K1, data[ 8] ); + subRound( B, C, D, E, A, f1, K1, data[ 9] ); + subRound( A, B, C, D, E, f1, K1, data[10] ); + subRound( E, A, B, C, D, f1, K1, data[11] ); + subRound( D, E, A, B, C, f1, K1, data[12] ); + subRound( C, D, E, A, B, f1, K1, data[13] ); + subRound( B, C, D, E, A, f1, K1, data[14] ); + subRound( A, B, C, D, E, f1, K1, data[15] ); DEBUG(15); + subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) ); DEBUG(16); + subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) ); DEBUG(17); + subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) ); DEBUG(18); + subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) ); DEBUG(19); + + subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) ); DEBUG(20); + subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) ); DEBUG(21); + subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) ); DEBUG(38); + subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) ); DEBUG(39); + + subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) ); DEBUG(40); + subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) ); DEBUG(41); + subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) ); DEBUG(58); + subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) ); DEBUG(59); + + subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) ); DEBUG(60); + subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) ); DEBUG(61); + subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) ); DEBUG(78); + subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) ); DEBUG(79); + + /* Build message digest */ + state[0] += A; + state[1] += B; + state[2] += C; + state[3] += D; + state[4] += E; + +#if SHA1_DEBUG + fprintf(stderr, "99: %8x %8x %8x %8x %8x\n", + state[0], state[1], state[2], state[3], state[4]); +#endif +} diff --git a/include/nettle/sha1.c b/include/nettle/sha1.c new file mode 100644 index 0000000..af73096 --- /dev/null +++ b/include/nettle/sha1.c @@ -0,0 +1,100 @@ +/* sha1.c + + The sha1 hash function. + Defined by http://www.itl.nist.gov/fipspubs/fip180-1.htm. + + Copyright (C) 2001, 2013 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "sha1.h" + +#include "macros.h" +#include "nettle-write.h" + +/* Initialize the SHA values */ +void +sha1_init(struct sha1_ctx *ctx) +{ + /* FIXME: Put the buffer last in the struct, and arrange so that we + can initialize with a single memcpy. */ + static const uint32_t iv[_SHA1_DIGEST_LENGTH] = + { + /* SHA initial values, first 4 identical to md5's. */ + 0x67452301L, + 0xEFCDAB89L, + 0x98BADCFEL, + 0x10325476L, + 0xC3D2E1F0L, + }; + + memcpy(ctx->state, iv, sizeof(ctx->state)); + ctx->count = 0; + + /* Initialize buffer */ + ctx->index = 0; +} + +#define COMPRESS(ctx, data) (nettle_sha1_compress((ctx)->state, data)) + +void +sha1_update(struct sha1_ctx *ctx, + size_t length, const uint8_t *data) +{ + MD_UPDATE (ctx, length, data, COMPRESS, ctx->count++); +} + +void +sha1_digest(struct sha1_ctx *ctx, + size_t length, + uint8_t *digest) +{ + uint64_t bit_count; + + assert(length <= SHA1_DIGEST_SIZE); + + MD_PAD(ctx, 8, COMPRESS); + + /* There are 512 = 2^9 bits in one block */ + bit_count = (ctx->count << 9) | (ctx->index << 3); + + /* append the 64 bit count */ + WRITE_UINT64(ctx->block + (SHA1_BLOCK_SIZE - 8), bit_count); + nettle_sha1_compress(ctx->state, ctx->block); + + _nettle_write_be32(length, digest, ctx->state); + sha1_init(ctx); +} diff --git a/include/nettle/sha1.h b/include/nettle/sha1.h new file mode 100644 index 0000000..32b79dc --- /dev/null +++ b/include/nettle/sha1.h @@ -0,0 +1,92 @@ +/* sha1.h + + The sha1 hash function. + + Copyright (C) 2001, 2012 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_SHA1_H_INCLUDED +#define NETTLE_SHA1_H_INCLUDED + +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define sha1_init nettle_sha1_init +#define sha1_update nettle_sha1_update +#define sha1_digest nettle_sha1_digest +#define sha1_compress nettle_sha1_compress + +/* SHA1 */ + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 +/* For backwards compatibility */ +#define SHA1_DATA_SIZE SHA1_BLOCK_SIZE + +/* Digest is kept internally as 5 32-bit words. */ +#define _SHA1_DIGEST_LENGTH 5 + +struct sha1_ctx +{ + uint32_t state[_SHA1_DIGEST_LENGTH]; /* State variables */ + uint64_t count; /* 64-bit block count */ + unsigned int index; /* index into buffer */ + uint8_t block[SHA1_BLOCK_SIZE]; /* SHA1 data buffer */ +}; + +void +sha1_init(struct sha1_ctx *ctx); + +void +sha1_update(struct sha1_ctx *ctx, + size_t length, + const uint8_t *data); + +void +sha1_digest(struct sha1_ctx *ctx, + size_t length, + uint8_t *digest); + +/* SHA1 compression function. STATE points to 5 uint32_t words, + and DATA points to 64 bytes of input data, possibly unaligned. */ +void +sha1_compress(uint32_t *state, const uint8_t *data); + +/* Old name, for backwards compatibility. */ +#define _nettle_sha1_compress nettle_sha1_compress + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_SHA1_H_INCLUDED */ diff --git a/include/nettle/write-be32.c b/include/nettle/write-be32.c new file mode 100644 index 0000000..39d1862 --- /dev/null +++ b/include/nettle/write-be32.c @@ -0,0 +1,77 @@ +/* write-be32.c + + Copyright (C) 2001 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "nettle-write.h" + +#include "macros.h" + +void +_nettle_write_be32(size_t length, uint8_t *dst, + const uint32_t *src) +{ + size_t i; + size_t words; + unsigned leftover; + + words = length / 4; + leftover = length % 4; + + for (i = 0; i < words; i++, dst += 4) + WRITE_UINT32(dst, src[i]); + + if (leftover) + { + uint32_t word; + unsigned j = leftover; + + word = src[i]; + + switch (leftover) + { + default: + abort(); + case 3: + dst[--j] = (word >> 8) & 0xff; + /* Fall through */ + case 2: + dst[--j] = (word >> 16) & 0xff; + /* Fall through */ + case 1: + dst[--j] = (word >> 24) & 0xff; + } + } +} From 5939f22d523d0837d30aac07d60cdbe29860437e Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Fri, 30 May 2025 02:05:09 +0100 Subject: [PATCH 12/50] Modify build system to support Windows, fix bug where a size value used to determine whether to access memory was not zero by default --- CMakeLists.txt | 16 ++++++++++++++-- README.md | 2 +- src/main.c | 3 +++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 168d580..d2e3ea8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,12 @@ 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() +endif() + # Setting sources... set(allsources ${CMAKE_SOURCE_DIR}/src/main.c) @@ -66,8 +72,14 @@ 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")) - target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address) - target_link_options(synthxex PRIVATE -fsanitize=address) + + # MinGW doesn't support ASAN + if(NOT (MINGW)) + target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address) + target_link_options(synthxex PRIVATE -fsanitize=address) + else() + target_compile_options(synthxex PRIVATE -O0 -g) + endif() execute_process( COMMAND git rev-parse --short HEAD diff --git a/README.md b/README.md index c04ffc9..f214efd 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This was developed by referencing public knowledge on the XEX(2) file format, al 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 *should* support any POSIX-compliant operating system. Windows is not supported at the moment (maybe WSL would work). +This *should* support any POSIX-compliant operating system, along with Windows. ## Installing diff --git a/src/main.c b/src/main.c index c380cb0..a45c249 100644 --- a/src/main.c +++ b/src/main.c @@ -245,6 +245,9 @@ 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; + printf("%s Validating PE file...\n", PRINT_STEM); if(!validatePE(pe, skipMachineCheck)) From 868e78d81b9b452437714e80682c1936c0aacb5f Mon Sep 17 00:00:00 2001 From: Aiden Isik Date: Fri, 30 May 2025 18:35:13 +0100 Subject: [PATCH 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 19/50] 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 20/50] 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 21/50] 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 22/50] 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 23/50] 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 24/50] 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 25/50] 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 26/50] 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 27/50] 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 28/50] 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 29/50] 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 30/50] 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 31/50] 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 32/50] 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 33/50] 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 34/50] 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 35/50] 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 36/50] 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 37/50] 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 38/50] 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 39/50] 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 40/50] 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 41/50] 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 42/50] 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 43/50] 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 44/50] 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 45/50] 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 46/50] 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 47/50] 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 48/50] 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 49/50] 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 50/50] 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; } }