Compare commits
38 commits
synthxex-0
...
main
Author | SHA1 | Date | |
---|---|---|---|
ea64ac981c | |||
38187b17a5 | |||
bbafc3d7ac | |||
8bc36751c9 | |||
4d67f29fa3 | |||
34b49c9a1d | |||
b8be6481e0 | |||
9d36dd8ec9 | |||
3527e2e76d | |||
a518314dee | |||
9992242f5b | |||
f505302e71 | |||
3c46c6cf74 | |||
f6610667ae | |||
b646f28812 | |||
dba2f4c54c | |||
1ff1e8f93f | |||
![]() |
4e053b09ca | ||
![]() |
1938c71369 | ||
434ed63db9 | |||
3d57c01b92 | |||
7b1d147cf0 | |||
75e2a58ade | |||
1dab6189be | |||
d82fd383f0 | |||
32bdb84887 | |||
81b649ca32 | |||
1b62027120 | |||
080df81ef6 | |||
aee6624e02 | |||
c0607ecc49 | |||
24c68d7ff1 | |||
0005775f85 | |||
4041036c65 | |||
f3e972a99f | |||
8ff9bb7582 | |||
6c5274ea54 | |||
868e78d81b |
28 changed files with 2699 additions and 1165 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
build
|
||||
result
|
||||
*~
|
||||
*#
|
||||
\#*
|
||||
*.orig
|
106
CMakeLists.txt
106
CMakeLists.txt
|
@ -16,79 +16,63 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# 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)
|
||||
cmake_minimum_required(VERSION 3.24.2)
|
||||
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/getdata.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.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"))
|
||||
# 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()
|
||||
|
||||
# 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()
|
||||
if(${SYNTHXEX_BUILD_TYPE} MATCHES "Deb")
|
||||
add_compile_definitions(_DEBUG=1)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
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()
|
||||
endif()
|
||||
|
||||
add_compile_definitions(GIT_COMMIT="${GIT_COMMIT}")
|
||||
# Set the version
|
||||
execute_process(
|
||||
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]+-g[0-9a-f]+(-dirty)?)?$")
|
||||
add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}")
|
||||
else()
|
||||
add_compile_definitions(SYNTHXEX_VERSION="v0.0.5") # Only used as a fallback
|
||||
endif()
|
||||
|
||||
# Setting install target settings...
|
||||
|
|
124
CONTRIBUTING.md
Normal file
124
CONTRIBUTING.md
Normal file
|
@ -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 \<aidenisik+git@member.fsf.org\>, 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;
|
||||
```
|
106
README.md
106
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
### getopt_port (include/getopt_port/*)
|
||||
### getopt_port
|
||||
|
||||
Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
14
channels.scm
Normal file
14
channels.scm
Normal file
|
@ -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")))))
|
60
flake.lock
generated
Normal file
60
flake.lock
generated
Normal file
|
@ -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
|
||||
}
|
35
flake.nix
Normal file
35
flake.nix
Normal file
|
@ -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 {};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -19,24 +19,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Program identifiers
|
||||
#define NAME "SynthXEX"
|
||||
#define VERSION "v0.0.3"
|
||||
#define COPYRIGHT "2024-25"
|
||||
// Forbid the use of the free function
|
||||
#define freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse free
|
||||
#pragma GCC poison free
|
||||
|
||||
#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
|
||||
|
@ -45,7 +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
|
||||
|
||||
void infoPrint(char *str);
|
||||
#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
|
||||
|
|
|
@ -18,94 +18,260 @@
|
|||
|
||||
#include "datastorage.h"
|
||||
|
||||
uint32_t getNextAligned(uint32_t offset, uint32_t alignment)
|
||||
// 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(offset % alignment) // If offset not aligned
|
||||
if(ptr != NULL)
|
||||
{
|
||||
return offset + (alignment - (offset % alignment)); // Align
|
||||
if(*ptr != NULL)
|
||||
{
|
||||
freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr);
|
||||
*ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return offset; // Offset already aligned
|
||||
}
|
||||
|
||||
// TODO: Combine all of these into a single function
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
if(offsets != NULL)
|
||||
{
|
||||
if(*offsets != NULL)
|
||||
{
|
||||
nullAndFree((void **) & ((*offsets)->optHeaders));
|
||||
nullAndFree((void **)offsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freeXexHeaderStruct(struct xexHeader **xexHeader)
|
||||
{
|
||||
if(xexHeader != NULL)
|
||||
{
|
||||
if(*xexHeader != NULL)
|
||||
{ nullAndFree((void **)xexHeader); }
|
||||
}
|
||||
}
|
||||
|
||||
void freeSecInfoHeaderStruct(struct secInfoHeader **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));
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
void freeOptHeadersStruct(struct optHeaders **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);
|
||||
}
|
||||
|
||||
uint32_t getNextAligned(uint32_t offset, uint32_t alignment)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
return 0; // Not found
|
||||
}
|
||||
|
||||
uint32_t get32BitFromPE(FILE *pe)
|
||||
{
|
||||
uint8_t store[4];
|
||||
fread(store, sizeof(uint8_t), 4, pe);
|
||||
uint32_t result;
|
||||
errno = SUCCESS;
|
||||
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(fread(&result, sizeof(uint32_t), 1, pe) != 1)
|
||||
{
|
||||
result |= store[i] << i * 8;
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If system is big endian, swap endianness (PE is LE)
|
||||
// If system is big endian, swap endianness (PE is LE)
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
result = __builtin_bswap32(result);
|
||||
return __builtin_bswap32(result);
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t get16BitFromPE(FILE *pe)
|
||||
{
|
||||
uint8_t store[2];
|
||||
fread(store, sizeof(uint8_t), 2, pe);
|
||||
uint16_t result;
|
||||
errno = SUCCESS;
|
||||
|
||||
uint16_t result = 0;
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
if(fread(&result, sizeof(uint16_t), 1, pe) != 1)
|
||||
{
|
||||
result |= store[i] << i * 8;
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If system is big endian, swap endianness (PE is LE)
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
result = htons(result);
|
||||
return __builtin_bswap16(result);
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t get32BitFromXEX(FILE *xex)
|
||||
{
|
||||
uint8_t store[4];
|
||||
fread(store, sizeof(uint8_t), 4, xex);
|
||||
uint32_t result;
|
||||
errno = SUCCESS;
|
||||
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(fread(&result, sizeof(uint32_t), 1, xex) != 1)
|
||||
{
|
||||
result |= store[i] << i * 8;
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If system and file endianness don't match we need to change it
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
result = __builtin_bswap32(result);
|
||||
return __builtin_bswap32(result);
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t get16BitFromXEX(FILE *xex)
|
||||
{
|
||||
uint8_t store[2];
|
||||
fread(store, sizeof(uint8_t), 2, xex);
|
||||
uint16_t result;
|
||||
errno = SUCCESS;
|
||||
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
if(fread(&result, sizeof(uint16_t), 1, xex) != 1)
|
||||
{
|
||||
result |= store[i] << i * 8;
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If system and file endianness don't match we need to change it
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
result = __builtin_bswap16(result);
|
||||
return __builtin_bswap16(result);
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -75,142 +78,201 @@
|
|||
// Page RWX flags
|
||||
struct sections
|
||||
{
|
||||
uint16_t count;
|
||||
struct sectionPerms *sectionPerms;
|
||||
uint16_t count;
|
||||
struct section *section;
|
||||
};
|
||||
|
||||
struct sectionPerms
|
||||
struct section
|
||||
{
|
||||
uint8_t permFlag;
|
||||
uint32_t rva;
|
||||
uint8_t permFlag;
|
||||
uint32_t virtualSize;
|
||||
uint32_t rva;
|
||||
uint32_t rawSize;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct peImport
|
||||
{
|
||||
uint32_t iatAddr;
|
||||
};
|
||||
|
||||
struct peImportTable
|
||||
{
|
||||
char *name;
|
||||
uint32_t rva;
|
||||
uint32_t importCount;
|
||||
struct peImport *imports;
|
||||
};
|
||||
|
||||
struct peImportInfo
|
||||
{
|
||||
uint16_t count;
|
||||
uint32_t idtRVA;
|
||||
uint32_t tableCount;
|
||||
uint32_t totalImportCount;
|
||||
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
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) importLibraries
|
||||
{
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
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
|
||||
|
||||
// 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.
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// 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
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "getdata.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
|
||||
// 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
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check magic
|
||||
fseek(pe, 0, SEEK_SET);
|
||||
uint16_t magic = get16BitFromPE(pe);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
uint16_t sectionCount = get16BitFromPE(pe);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Check machine ID
|
||||
fseek(pe, peHeaderOffset + 0x4, SEEK_SET);
|
||||
uint16_t machineID = get16BitFromPE(pe);
|
||||
|
||||
if(machineID != 0x1F2 && !skipMachineCheck) // 0x1F2 == POWERPCBE
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check subsystem
|
||||
fseek(pe, peHeaderOffset + 0x5C, SEEK_SET);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
fseek(pe, 0x20, SEEK_CUR); // Next entry
|
||||
}
|
||||
|
||||
return true; // Checked enough, this is an Xbox 360 PE file
|
||||
}
|
||||
|
||||
int getSectionRwxFlags(FILE *pe, struct sections *sections)
|
||||
{
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
uint32_t peOffset = get32BitFromPE(pe);
|
||||
|
||||
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;}
|
||||
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, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
|
||||
// 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);
|
||||
peData->tlsSize = get32BitFromPE(pe);
|
||||
if(peData->tlsAddr != 0 || peData->tlsSize != 0) {return ERR_UNSUPPORTED_STRUCTURE;}
|
||||
|
||||
// Page RWX flags
|
||||
int ret = getSectionRwxFlags(pe, &(peData->sections));
|
||||
if(ret != 0) {return ret;}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
280
src/getdata/gethdrdata.c
Normal file
280
src/getdata/gethdrdata.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// 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
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#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
|
||||
// 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
|
||||
{
|
||||
// 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; }
|
||||
|
||||
// Check magic
|
||||
fseek(pe, 0, SEEK_SET);
|
||||
uint16_t magic = get16BitFromPE(pe);
|
||||
|
||||
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; }
|
||||
|
||||
// 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);
|
||||
uint16_t sectionCount = get16BitFromPE(pe);
|
||||
|
||||
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; }
|
||||
|
||||
// Check machine ID
|
||||
fseek(pe, peHeaderOffset + 0x4, SEEK_SET);
|
||||
|
||||
// 0x1F2 == POWERPCBE
|
||||
uint16_t machineID = get16BitFromPE(pe);
|
||||
|
||||
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; }
|
||||
|
||||
// Check page size/alignment
|
||||
fseek(pe, peHeaderOffset + 0x38, SEEK_SET);
|
||||
|
||||
// 4KiB and 64KiB are the only valid sizes
|
||||
uint32_t pageSize = get32BitFromPE(pe);
|
||||
|
||||
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++)
|
||||
{
|
||||
// If raw size + raw offset exceeds file size, PE is invalid
|
||||
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; }
|
||||
|
||||
// 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);
|
||||
|
||||
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)
|
||||
{ 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);
|
||||
|
||||
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
|
||||
}
|
||||
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; }
|
||||
|
||||
// Getting PE header offset before we go any further..
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
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; }
|
||||
|
||||
// Section info
|
||||
int ret = getSectionInfo(pe, &(peData->sections));
|
||||
|
||||
if(ret != 0)
|
||||
{ return ret; }
|
||||
|
||||
return SUCCESS;
|
||||
}
|
|
@ -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 <sys/stat.h>
|
||||
#include "../common/common.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
206
src/getdata/getimports.c
Normal file
206
src/getdata/getimports.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "getimports.h"
|
||||
|
||||
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; }
|
||||
|
||||
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:
|
||||
// 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));
|
||||
|
||||
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));
|
||||
|
||||
// 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);
|
||||
return SUCCESS;
|
||||
}
|
24
src/getdata/getimports.h
Normal file
24
src/getdata/getimports.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
int getImports(FILE *pe, struct peData *peData);
|
717
src/main.c
717
src/main.c
|
@ -16,10 +16,14 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// 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"
|
||||
#include "getdata/getdata.h"
|
||||
#include "getdata/gethdrdata.h"
|
||||
#include "getdata/getimports.h"
|
||||
#include "setdata/populateheaders.h"
|
||||
#include "setdata/pagedescriptors.h"
|
||||
#include "setdata/optheaders.h"
|
||||
|
@ -32,382 +36,499 @@
|
|||
|
||||
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 <kim.grasman@gmail.com>\n");
|
||||
printf("All rights reserved.\n\n");
|
||||
printf("----- GETOPT_PORT -----\n\n");
|
||||
printf("Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>\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 <https://www.gnu.org/licenses/>.\n\n");
|
||||
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");
|
||||
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 <https://www.gnu.org/licenses/>.\n\n");
|
||||
}
|
||||
|
||||
void dispHelp(char **argv)
|
||||
{
|
||||
printf("\nUsage: %s [OPTION] <ARG>\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\n");
|
||||
printf("\nUsage: %s [OPTION] <ARG>\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:
|
||||
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", 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;
|
||||
|
||||
case ERR_MISSING_SECTION_FLAG:
|
||||
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", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_INVALID_RVA_OR_OFFSET:
|
||||
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", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_DATA_OVERFLOW:
|
||||
fprintf(stderr, "%s ERROR: Data overflow. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s ERROR: Unknown error: %d. Aborting.\n", SYNTHXEX_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},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
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:", longOptions, &optIndex)) != -1)
|
||||
static struct option longOptions[] =
|
||||
{
|
||||
if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0))
|
||||
{
|
||||
dispHelp(argv);
|
||||
return SUCCESS;
|
||||
}
|
||||
else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0))
|
||||
{
|
||||
dispVer();
|
||||
return SUCCESS;
|
||||
}
|
||||
else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0))
|
||||
{
|
||||
dispLibs();
|
||||
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);
|
||||
{ "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 }
|
||||
};
|
||||
|
||||
if(pePath == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
if(xexfilePath != NULL) {free(xexfilePath);}
|
||||
return -1;
|
||||
}
|
||||
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));
|
||||
|
||||
strncpy(pePath, optarg, strlen(optarg) + 1);
|
||||
}
|
||||
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);
|
||||
if(pePath != NULL) {free(pePath);}
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(xexfilePath, optarg, strlen(optarg) + 1);
|
||||
}
|
||||
if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL ||
|
||||
peData == NULL || optHeaderEntries == NULL || optHeaders == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting\n", SYNTHXEX_PRINT_STEM);
|
||||
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;
|
||||
|
||||
// Check we got everything we need
|
||||
if(!gotInput)
|
||||
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(gotOutput)
|
||||
{
|
||||
free(xexfilePath);
|
||||
}
|
||||
switch(option)
|
||||
{
|
||||
case 'v':
|
||||
dispVer();
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return SUCCESS;
|
||||
|
||||
printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM);
|
||||
return -1;
|
||||
}
|
||||
else if(!gotOutput)
|
||||
{
|
||||
if(gotInput)
|
||||
{
|
||||
free(pePath);
|
||||
}
|
||||
case 'l':
|
||||
dispLibs();
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return SUCCESS;
|
||||
|
||||
printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM);
|
||||
return -1;
|
||||
case 's':
|
||||
printf("%s WARNING: Skipping machine ID check.\n", SYNTHXEX_PRINT_STEM);
|
||||
skipMachineCheck = true;
|
||||
break;
|
||||
|
||||
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 SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Opening the files now that they've been validated
|
||||
FILE *pe = fopen(pePath, "rb");
|
||||
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(pe == NULL)
|
||||
if(!gotInput)
|
||||
{
|
||||
printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM);
|
||||
free(pePath);
|
||||
free(xexfilePath);
|
||||
return -1;
|
||||
if(gotOutput)
|
||||
{ nullAndFree((void **)&xexfilePath); }
|
||||
|
||||
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)
|
||||
{
|
||||
if(gotInput)
|
||||
{ nullAndFree((void **)&pePath); }
|
||||
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: XEX file output expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(pePath);
|
||||
FILE *pe = fopen(pePath, "rb");
|
||||
|
||||
FILE *xex = fopen(xexfilePath, "wb+");
|
||||
|
||||
if(xex == NULL)
|
||||
if(pe == NULL)
|
||||
{
|
||||
printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
free(xexfilePath);
|
||||
return -1;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
nullAndFree((void **)&pePath);
|
||||
|
||||
int ret;
|
||||
FILE *xex = fopen(xexfilePath, "wb+");
|
||||
|
||||
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;
|
||||
|
||||
printf("%s Validating PE file...\n", PRINT_STEM);
|
||||
|
||||
if(!validatePE(pe, skipMachineCheck))
|
||||
if(xex == NULL)
|
||||
{
|
||||
printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM);
|
||||
free(xexfilePath);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
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;
|
||||
}
|
||||
|
||||
printf("%s PE valid!\n", PRINT_STEM);
|
||||
// Keep xexfilePath for later use
|
||||
// Do NOT free xexfilePath here yet
|
||||
|
||||
// Reading in header data from PE
|
||||
printf("%s Retrieving header data from PE...\n", PRINT_STEM);
|
||||
ret = getHdrData(pe, &peData, 0);
|
||||
int ret = 0;
|
||||
|
||||
if(ret == ERR_UNKNOWN_DATA_REQUEST)
|
||||
printf("%s Validating PE file...\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
if(!validatePE(pe, skipMachineCheck))
|
||||
{
|
||||
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);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
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);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Got header data from PE!\n", PRINT_STEM);
|
||||
printf("%s PE valid!\n", SYNTHXEX_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));
|
||||
printf("%s Retrieving header data from PE...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = getHdrData(pe, peData, 0);
|
||||
|
||||
if(basefilePath == NULL)
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
free(xexfilePath);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
handleError(ret);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(basefilePath, xexfilePath);
|
||||
strcat(basefilePath, ".basefile");
|
||||
printf("%s Got header data from PE!\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
FILE* basefile = fopen(basefilePath, "wb+");
|
||||
free(xexfilePath); // *Now* we're done with it.
|
||||
free(basefilePath);
|
||||
printf("%s Retrieving import data from PE...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = getImports(pe, peData);
|
||||
|
||||
if(basefile == NULL)
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
handleError(ret);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
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);
|
||||
printf("%s Got import data from PE!\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
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);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Created basefile!\n", PRINT_STEM);
|
||||
strcpy(basefilePath, xexfilePath);
|
||||
strcat(basefilePath, ".basefile");
|
||||
|
||||
// Setting final XEX data structs
|
||||
printf("%s Building XEX header...\n", PRINT_STEM);
|
||||
setXEXHeader(&xexHeader, &peData);
|
||||
FILE *basefile = fopen(basefilePath, "wb+");
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
nullAndFree((void **)&basefilePath);
|
||||
|
||||
printf("%s Building security header...\n", PRINT_STEM);
|
||||
setSecInfoHeader(&secInfoHeader, &peData);
|
||||
|
||||
printf("%s Setting page descriptors...\n", PRINT_STEM);
|
||||
ret = setPageDescriptors(basefile, &peData, &secInfoHeader);
|
||||
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
if(!basefile)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Building optional headers...\n", PRINT_STEM);
|
||||
ret = setOptHeaders(&secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
// Map the PE into the basefile (RVAs become offsets)
|
||||
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);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Setting data positions...
|
||||
printf("%s Aligning data...\n", PRINT_STEM);
|
||||
ret = placeStructs(&offsets, &xexHeader, &optHeaderEntries, &secInfoHeader, &optHeaders);
|
||||
printf("%s Created basefile!\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
// Setting final XEX data structs
|
||||
printf("%s Building security header...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setSecInfoHeader(secInfoHeader, peData);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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);
|
||||
printf("%s Setting page descriptors...\n", SYNTHXEX_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);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
handleError(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);
|
||||
// Done with this now
|
||||
freeSectionsStruct(&(peData->sections));
|
||||
|
||||
// Final pass (sets & writes header hash)
|
||||
printf("%s Calculating and writing header SHA1...\n", PRINT_STEM);
|
||||
setHeaderSha1(xex);
|
||||
printf("%s Header SHA1 written!\n", PRINT_STEM);
|
||||
printf("%s Building optional headers...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders);
|
||||
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM);
|
||||
return SUCCESS;
|
||||
printf("%s Building XEX header...\n", SYNTHXEX_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", SYNTHXEX_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", SYNTHXEX_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", SYNTHXEX_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", SYNTHXEX_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", SYNTHXEX_PRINT_STEM);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -20,69 +20,165 @@
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
// 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
|
||||
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++)
|
||||
{
|
||||
// Read in the current IAT entry
|
||||
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
|
||||
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
|
||||
iatEntry = __builtin_bswap32(iatEntry);
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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));
|
||||
|
||||
for(uint16_t i = 0; i < peData->numberOfSections; i++)
|
||||
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; }
|
||||
|
||||
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
|
||||
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; }
|
||||
|
||||
// Seek to the next entry at virtualSize
|
||||
if(fseek(pe, 0x18, SEEK_CUR) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
}
|
||||
|
||||
// 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(fseek(pe, 0, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
fread(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, pe);
|
||||
fwrite(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, basefile);
|
||||
// Copy the PE header and section table to the basefile verbatim
|
||||
uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize);
|
||||
|
||||
// Now map the sections and write them
|
||||
for(uint16_t i = 0; i < peData->numberOfSections; i++)
|
||||
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(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 * sizeof(uint8_t));
|
||||
if(buffer == NULL) {return ERR_OUT_OF_MEM;}
|
||||
buffer = realloc(buffer, sectionInfo[i].rawSize);
|
||||
|
||||
fseek(pe, sectionInfo[i].offset, SEEK_SET);
|
||||
fread(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, pe);
|
||||
if(!buffer)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
fseek(basefile, sectionInfo[i].rva, SEEK_SET);
|
||||
fwrite(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, basefile);
|
||||
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;
|
||||
// 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;
|
||||
|
||||
if(nextAligned != currentOffset)
|
||||
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);
|
||||
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);
|
||||
// Make sure to update the PE (basefile) size
|
||||
peData->size = ftell(basefile);
|
||||
|
||||
// We're done with these, free them
|
||||
free(buffer);
|
||||
free(sectionInfo);
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -21,85 +21,89 @@
|
|||
// 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)
|
||||
{
|
||||
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
|
||||
// Calloc because 0 values will be used to determine if a header is not present.
|
||||
offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t));
|
||||
|
||||
for(uint32_t i = 0; i < optHeaderEntries->count; i++)
|
||||
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++)
|
||||
{
|
||||
*currentOffset = getNextAligned(*currentOffset, 0x8);
|
||||
*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;
|
||||
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_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;
|
||||
}
|
||||
case XEX_OPT_ID_TLS_INFO:
|
||||
optHeaderEntries->optHeaderEntry[i].dataOrOffset = *currentOffset;
|
||||
offsets->optHeaders[sepHeader] = *currentOffset;
|
||||
*currentOffset += sizeof(struct tlsInfo);
|
||||
sepHeader++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
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
|
||||
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 etc
|
||||
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;
|
||||
uint32_t importLibsIdx; // Entry in opt header entries of import libs
|
||||
int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs);
|
||||
// Optional headers (minus imports)
|
||||
struct importLibIdcs importLibIdcs;
|
||||
int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
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)
|
||||
{
|
||||
return ret;
|
||||
offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size;
|
||||
optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header];
|
||||
}
|
||||
|
||||
//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
|
||||
//offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size;
|
||||
//optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header];
|
||||
|
||||
return SUCCESS;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -20,55 +20,335 @@
|
|||
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
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;
|
||||
|
||||
// Allocate name list
|
||||
char **names = calloc(importLibraries->tableCount, sizeof(char *));
|
||||
|
||||
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;
|
||||
char *targetHotfixVerStr = NULL;
|
||||
char *minimumBuildVerStr = NULL;
|
||||
char *minimumHotfixVerStr = NULL;
|
||||
uint16_t buildVer = 0;
|
||||
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; } // 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; } // Encountered null terminator instead of '.'
|
||||
|
||||
buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10);
|
||||
|
||||
if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr)
|
||||
{ goto cleanup_names_invalid; } // Encountered a non-number, or string was empty
|
||||
|
||||
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; }
|
||||
|
||||
// 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; } // No more tokens
|
||||
|
||||
if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr)
|
||||
+ 1 + strlen(minimumBuildVerStr) == oldNameLen)
|
||||
{ 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; } // Encountered a non-number, or string was empty
|
||||
|
||||
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; }
|
||||
|
||||
// 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)
|
||||
{ importTables[i].unknown = 0xFCA15C76; }
|
||||
else if(strcmp(names[i], "xbdm.xex") == 0)
|
||||
{ importTables[i].unknown = 0xECEB8109; }
|
||||
else
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
// 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));
|
||||
|
||||
if(!importTables[i].addresses)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
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)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
|
||||
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)
|
||||
{ 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]); }
|
||||
|
||||
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)
|
||||
{
|
||||
*flags = XEX_SYS_GAMEPAD_DISCONNECT |
|
||||
XEX_SYS_INSECURE_SOCKETS |
|
||||
XEX_SYS_XAM_HOOKS |
|
||||
XEX_SYS_BACKGROUND_DL |
|
||||
XEX_SYS_ALLOW_CONTROL_SWAP;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
optHeaderEntries->count = 4;
|
||||
optHeaderEntries->optHeaderEntry = calloc(5, sizeof(struct optHeaderEntry));
|
||||
if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;}
|
||||
bool importsPresent = (peData->peImportInfo.totalImportCount > 0) ? true : false;
|
||||
|
||||
// First optional header (basefile format)
|
||||
setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader);
|
||||
optHeaderEntries->optHeaderEntry[0].id = XEX_OPT_ID_BASEFILE_FORMAT;
|
||||
optHeaderEntries->count = 4;
|
||||
|
||||
// Second optional header (entrypoint)
|
||||
optHeaderEntries->optHeaderEntry[1].id = XEX_OPT_ID_ENTRYPOINT;
|
||||
optHeaderEntries->optHeaderEntry[1].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint;
|
||||
if(importsPresent)
|
||||
{ optHeaderEntries->count++; }
|
||||
|
||||
// Third optional header (import libs)
|
||||
//optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_IMPORT_LIBS;
|
||||
optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry));
|
||||
|
||||
// Fourth optional header (tls info)
|
||||
optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_TLS_INFO;
|
||||
setTLSInfo(&(optHeaders->tlsInfo));
|
||||
if(optHeaderEntries->optHeaderEntry == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
// Fifth optional header (system flags)
|
||||
optHeaderEntries->optHeaderEntry[3].id = XEX_OPT_ID_SYS_FLAGS;
|
||||
setSysFlags(&(optHeaderEntries->optHeaderEntry[3].dataOrOffset));
|
||||
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[currentHeader].id = XEX_OPT_ID_BASEFILE_FORMAT;
|
||||
currentHeader++;
|
||||
|
||||
// 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; }
|
||||
|
||||
currentHeader++;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -20,61 +20,70 @@
|
|||
|
||||
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(int i = peData->sections.count - 1; i >= 0; i--)
|
||||
{
|
||||
if(currentOffset >= peData->sections.sectionPerms[i].rva)
|
||||
{
|
||||
return peData->sections.sectionPerms[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;
|
||||
secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); // The free() for this is called after written to XEX
|
||||
if(secInfoHeader->descriptors == NULL) {return ERR_OUT_OF_MEM;}
|
||||
uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount;
|
||||
|
||||
// Setting size/info data and calculating hashes for page descriptors
|
||||
for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--)
|
||||
secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor));
|
||||
|
||||
if(!secInfoHeader->descriptors)
|
||||
{ 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);
|
||||
// Get page type (rwx)
|
||||
descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i);
|
||||
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
sha1_init(&shaContext);
|
||||
// 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(fseek(pe, i *pageSize, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// For little endian systems, swap into big endian for hashing, then back (to keep struct endianness consistent)
|
||||
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;
|
||||
}
|
||||
|
||||
// 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, pageSize, page);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1);
|
||||
}
|
||||
if(i != 0)
|
||||
{ sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); }
|
||||
else
|
||||
{ sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); }
|
||||
|
||||
nullAndFree((void **)&page);
|
||||
}
|
||||
|
||||
free(peData->sections.sectionPerms); // Alloc'd in getdata
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -18,49 +18,54 @@
|
|||
|
||||
#include "populateheaders.h"
|
||||
|
||||
void 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
|
||||
// Writing data into XEX header.
|
||||
strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic
|
||||
|
||||
// Module flags (type of executable)
|
||||
xexHeader->moduleFlags = 0;
|
||||
// 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->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;
|
||||
|
||||
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;
|
||||
// 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));
|
||||
strcpy(secInfoHeader->signature, VERSION_STRING);
|
||||
// Setting signature (just a SynthXEX version identifier)
|
||||
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
|
||||
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
|
||||
//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;
|
||||
}
|
||||
|
|
|
@ -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 optHeaderEntries *optHeaderEntries, struct peData *peData);
|
||||
int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData);
|
||||
|
|
|
@ -18,71 +18,94 @@
|
|||
|
||||
#include "headerhash.h"
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
uint32_t size = get32BitFromXEX(xex) - 0x4;
|
||||
|
||||
uint8_t data[size];
|
||||
fread(data, sizeof(uint8_t), size, xex);
|
||||
|
||||
struct sha1_ctx shaContext;
|
||||
sha1_init(&shaContext);
|
||||
sha1_update(&shaContext, size, data);
|
||||
|
||||
uint8_t sha1[20];
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Get basefile offset
|
||||
if(fseek(xex, 0x8, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
fseek(xex, 0x10, SEEK_SET); // Secinfo offset
|
||||
uint32_t secInfoOffset = get32BitFromXEX(xex);
|
||||
uint32_t basefileOffset = 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)
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
sha1_init(&shaContext);
|
||||
// Get secinfo offset
|
||||
if(fseek(xex, 0x10, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// 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);
|
||||
sha1_update(&shaContext, remainingSize, remainderOfHeaders);
|
||||
uint32_t secInfoOffset = get32BitFromXEX(xex);
|
||||
|
||||
uint8_t headersStart[secInfoOffset + 0x8]; // Hash from start up to image info (0x8 into security header)
|
||||
fseek(xex, 0, SEEK_SET);
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
fread(headersStart, sizeof(uint8_t), secInfoOffset + 0x8, xex);
|
||||
sha1_update(&shaContext, secInfoOffset + 0x8, headersStart);
|
||||
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)
|
||||
|
||||
// Get final hash
|
||||
uint8_t headerHash[20];
|
||||
sha1_digest(&shaContext, 20, headerHash);
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
memset(&shaContext, 0, sizeof(shaContext));
|
||||
sha1_init(&shaContext);
|
||||
|
||||
// Finally, write it out
|
||||
fseek(xex, secInfoOffset + 0x164, SEEK_SET); // 0x164 == offset in secinfo of header hash
|
||||
fwrite(headerHash, sizeof(uint8_t), 20, xex);
|
||||
// Hash first part (remainder of headers is done first, then the start)
|
||||
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);
|
||||
nullAndFree((void **)&remainderOfHeaders);
|
||||
|
||||
// Hash from start up to image info (0x8 into security header)
|
||||
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);
|
||||
|
||||
// Get final hash
|
||||
uint8_t headerHash[20];
|
||||
memset(headerHash, 0, sizeof(headerHash));
|
||||
sha1_digest(&shaContext, 20, headerHash);
|
||||
|
||||
// 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)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -22,5 +22,4 @@
|
|||
#include "../common/crypto.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
void setImportsSha1(FILE *xex);
|
||||
void setHeaderSha1(FILE *xex);
|
||||
int setHeaderSha1(FILE *xex);
|
||||
|
|
|
@ -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
|
||||
|
@ -21,134 +21,186 @@
|
|||
// 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);
|
||||
// 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);
|
||||
fseek(xex, offsets->xexHeader, SEEK_SET);
|
||||
fwrite(xexHeader, sizeof(uint8_t), sizeof(struct xexHeader), xex);
|
||||
|
||||
// Optional header entries
|
||||
// Optional header entries
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
// Endian swap opt header entries
|
||||
for(uint32_t i = 0; i < optHeaderEntries->count; i++)
|
||||
|
||||
// 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);
|
||||
optHeaderEntries->optHeaderEntry[i].id = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].id);
|
||||
optHeaderEntries->optHeaderEntry[i].dataOrOffset = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].dataOrOffset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
fseek(xex, offsets->optHeaderEntries, SEEK_SET);
|
||||
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);
|
||||
}
|
||||
for(int i = 0; i < optHeaderEntries->count; i++)
|
||||
{ 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);
|
||||
|
||||
// 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++)
|
||||
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);
|
||||
// Writing out current descriptor...
|
||||
fwrite(&(descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex);
|
||||
fwrite(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);
|
||||
|
||||
// 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));
|
||||
|
||||
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;}
|
||||
if(buffer == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize)
|
||||
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)
|
||||
{ break; }
|
||||
|
||||
size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex);
|
||||
|
||||
if(writeRet != readRet)
|
||||
{ break; }
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
nullAndFree((void **)&buffer);
|
||||
|
||||
// Security Info
|
||||
// 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);
|
||||
// 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
|
||||
|
||||
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
|
||||
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;
|
||||
// Optional headers
|
||||
uint32_t currentHeader = 0;
|
||||
|
||||
if(optHeaders->basefileFormat.size != 0) // If not 0, it has data. Write it.
|
||||
if(optHeaders->basefileFormat.size != 0) // If not 0, it has data. Write it.
|
||||
{
|
||||
fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET);
|
||||
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);
|
||||
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++;
|
||||
fwrite(&(optHeaders->basefileFormat), sizeof(uint8_t), sizeof(struct basefileFormat), xex);
|
||||
currentHeader++;
|
||||
}
|
||||
|
||||
if(optHeaders->importLibraries.size != 0)
|
||||
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
|
||||
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.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);
|
||||
currentHeader++;
|
||||
}
|
||||
|
||||
if(optHeaders->tlsInfo.slotCount != 0)
|
||||
{
|
||||
fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET);
|
||||
fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void *)), xex);
|
||||
fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex);
|
||||
|
||||
#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);
|
||||
// Restore the table count (we require it to free the import libraries struct later)
|
||||
optHeaders->importLibraries.tableCount = tableCount;
|
||||
#endif
|
||||
|
||||
fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex);
|
||||
// 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++;
|
||||
}
|
||||
|
||||
free(offsets->optHeaders); // Alloc'd in placer.
|
||||
return SUCCESS;
|
||||
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;
|
||||
}
|
||||
|
|
22
synthxex.nix
Normal file
22
synthxex.nix
Normal file
|
@ -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
|
||||
'';
|
||||
}
|
104
synthxex.scm
Normal file
104
synthxex.scm
Normal file
|
@ -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=<target triplet>" 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; 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.5")
|
||||
|
||||
|
||||
;;; 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
|
Loading…
Add table
Add a link
Reference in a new issue