├── README.md ├── CMakeLists.txt ├── LICENSE ├── .github └── workflows │ └── main.yml └── TE2PE.c /README.md: -------------------------------------------------------------------------------- 1 | # TE2PE 2 | Primitive TE to PE32 converter 3 | 4 | This program tries to convert Terse Executable image used to store PEI modules in different UEFI-compatible firmwares into normal PE32 image 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.13) 2 | 3 | PROJECT(TE2PE) 4 | 5 | SET(PROJECT_SOURCES TE2PE.c) 6 | 7 | ADD_EXECUTABLE(TE2PE ${PROJECT_SOURCES}) 8 | 9 | IF(UNIX) 10 | SET_TARGET_PROPERTIES(TE2PE PROPERTIES OUTPUT_NAME te2pe) 11 | ENDIF() 12 | 13 | INSTALL( 14 | TARGETS TE2PE 15 | RUNTIME DESTINATION bin 16 | ) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2025, Nikolaj Schlej 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | release: 8 | types: [published] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build_mac: 15 | name: Build on macOS 16 | runs-on: macos-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Create build directory 21 | run: cmake -E make_directory ${{runner.workspace}}/build 22 | - name: Configure everything 23 | working-directory: ${{runner.workspace}}/build 24 | run: cmake -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="11.0" ../TE2PE/ 25 | - name: Build everything 26 | working-directory: ${{runner.workspace}}/build 27 | shell: bash 28 | run: cmake --build . --config Release 29 | - name: Create dist directory 30 | run: cmake -E make_directory ${{runner.workspace}}/dist 31 | - name: Archive everything 32 | working-directory: ${{runner.workspace}}/build 33 | shell: bash 34 | run: | 35 | zip -qryj ../dist/te2pe_universal_mac.zip ./te2pe 36 | - name: Upload to artifacts 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: macOS builds 40 | path: ${{runner.workspace}}/dist/*.zip 41 | 42 | build_linux: 43 | name: Build on Linux 44 | runs-on: ubuntu-latest 45 | 46 | steps: 47 | - uses: actions/checkout@v3 48 | - name: Create build directory 49 | run: cmake -E make_directory ${{runner.workspace}}/build 50 | - name: Configure everything 51 | working-directory: ${{runner.workspace}}/build 52 | run: cmake ../TE2PE/ 53 | - name: Build everything 54 | working-directory: ${{runner.workspace}}/build 55 | shell: bash 56 | run: cmake --build . --config Release 57 | - name: Create dist directory 58 | run: cmake -E make_directory ${{runner.workspace}}/dist 59 | - name: Archive everything 60 | working-directory: ${{runner.workspace}}/build 61 | shell: bash 62 | run: | 63 | zip -qryj ../dist/te2pe_linux.zip ./te2pe 64 | - name: Upload to artifacts 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: Linux builds 68 | path: ${{runner.workspace}}/dist/*.zip 69 | 70 | build_freebsd: 71 | name: Build on FreeBSD 72 | runs-on: ubuntu-latest 73 | steps: 74 | - uses: actions/checkout@v3 75 | - name: Build on FreeBSD inside Ubuntu VM 76 | id: test 77 | uses: cross-platform-actions/action@v0.27.0 78 | with: 79 | operating_system: freebsd 80 | version: '13.3' 81 | shell: sh 82 | run: | 83 | sudo pkg install -y zip cmake 84 | mkdir dist 85 | mkdir build 86 | cd build 87 | cmake .. 88 | cmake --build . --config Release 89 | zip -qryj ../dist/te2pe_freebsd.zip ./te2pe 90 | - name: Upload to artifacts 91 | uses: actions/upload-artifact@v4 92 | with: 93 | name: FreeBSD builds 94 | path: dist/*.zip 95 | 96 | build_windows_x86: 97 | name: Build on Windows x86 98 | runs-on: windows-2019 99 | steps: 100 | - uses: actions/checkout@v3 101 | - name: Create build directory 102 | run: cmake -E make_directory ${{runner.workspace}}/build 103 | - name: Configure everything 104 | shell: bash 105 | working-directory: ${{runner.workspace}}/build 106 | run: cmake -G "Visual Studio 16 2019" -A Win32 -T "v141_xp" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded" ../TE2PE/ 107 | - name: Build everything 108 | working-directory: ${{runner.workspace}}/build 109 | shell: bash 110 | run: cmake --build . --config Release 111 | - name: Create dist directory 112 | run: cmake -E make_directory ${{runner.workspace}}/dist 113 | - name: Archive everything 114 | working-directory: ${{runner.workspace}}/build/Release 115 | shell: bash 116 | run: | 117 | 7z a ../../dist/te2pe_win32.zip TE2PE.exe 118 | - name: Upload to artifacts 119 | uses: actions/upload-artifact@v4 120 | with: 121 | name: Windows x86 builds 122 | path: ${{runner.workspace}}\dist\*.zip 123 | -------------------------------------------------------------------------------- /TE2PE.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Nikolaj Schlej 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 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. 11 | */ 12 | 13 | // I don't know any machine with 64-bit PEI, so only 32-bit images are supported right now 14 | // If you've found 64-bit TE file, please open the issue on project's GitHub repo 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #pragma pack(push, 1) 22 | 23 | // Basic types 24 | #define UINT8 uint8_t 25 | #define UINT16 uint16_t 26 | #define UINT32 uint32_t 27 | #define UINT64 uint64_t 28 | #define UINTN unsigned long 29 | #define VOID void 30 | 31 | // Only I386 images are supported now 32 | #define IMAGE_FILE_MACHINE_UNKNOWN 0 33 | #define IMAGE_FILE_MACHINE_I386 0x014c 34 | #define IMAGE_FILE_MACHINE_IA64 0x0200 35 | #define IMAGE_FILE_MACHINE_AMD64 0x8664 36 | #define IMAGE_FILE_MACHINE_ARM 0x01c0 37 | #define IMAGE_FILE_MACHINE_THUMB 0x01c2 38 | #define IMAGE_FILE_MACHINE_ARMV7 0x01c4 39 | #define IMAGE_FILE_MACHINE_ARM64 0xAA64 40 | 41 | #define EFI_IMAGE_DOS_SIGNATURE 0x5A4D // MZ 42 | #define EFI_IMAGE_PE_SIGNATURE 0x00004550 // PE 43 | #define EFI_IMAGE_TE_SIGNATURE 0x5A56 // VZ 44 | 45 | #define EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC 0x10b 46 | #define EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC 0x20b 47 | 48 | // DOS header 49 | typedef struct _EFI_IMAGE_DOS_HEADER { 50 | UINT16 e_magic; // Magic number 51 | UINT16 e_cblp; // Bytes on last page of file 52 | UINT16 e_cp; // Pages in file 53 | UINT16 e_crlc; // Relocations 54 | UINT16 e_cparhdr; // Size of header in paragraphs 55 | UINT16 e_minalloc; // Minimum extra paragraphs needed 56 | UINT16 e_maxalloc; // Maximum extra paragraphs needed 57 | UINT16 e_ss; // Initial (relative) SS value 58 | UINT16 e_sp; // Initial SP value 59 | UINT16 e_csum; // Checksum 60 | UINT16 e_ip; // Initial IP value 61 | UINT16 e_cs; // Initial (relative) CS value 62 | UINT16 e_lfarlc; // File address of relocation table 63 | UINT16 e_ovno; // Overlay number 64 | UINT16 e_res[4]; // Reserved words 65 | UINT16 e_oemid; // OEM identifier (for e_oeminfo) 66 | UINT16 e_oeminfo; // OEM information; e_oemid specific 67 | UINT16 e_res2[10]; // Reserved words 68 | UINT32 e_lfanew; // File address of new header 69 | } EFI_IMAGE_DOS_HEADER; 70 | 71 | // COFF file header (object and image) 72 | typedef struct _EFI_IMAGE_FILE_HEADER { 73 | UINT16 Machine; 74 | UINT16 NumberOfSections; 75 | UINT32 TimeDateStamp; 76 | UINT32 PointerToSymbolTable; 77 | UINT32 NumberOfSymbols; 78 | UINT16 SizeOfOptionalHeader; 79 | UINT16 Characteristics; 80 | } EFI_IMAGE_FILE_HEADER; 81 | 82 | // Characteristics 83 | #define EFI_IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file 84 | #define EFI_IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references) 85 | #define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line numbers stripped from file 86 | #define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file 87 | #define EFI_IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed 88 | #define EFI_IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine 89 | #define EFI_IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file 90 | #define EFI_IMAGE_FILE_SYSTEM 0x1000 // System File 91 | #define EFI_IMAGE_FILE_DLL 0x2000 // File is a DLL 92 | #define EFI_IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed 93 | 94 | // Header Data Directories. 95 | typedef struct _EFI_IMAGE_DATA_DIRECTORY { 96 | UINT32 VirtualAddress; 97 | UINT32 Size; 98 | } EFI_IMAGE_DATA_DIRECTORY; 99 | 100 | // Directory Entries 101 | #define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0 102 | #define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1 103 | #define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2 104 | #define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 105 | #define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4 106 | #define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5 107 | #define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6 108 | #define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 109 | #define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 110 | #define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9 111 | #define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 112 | 113 | #define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 114 | 115 | // PE32 Optional Header 116 | #define EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC 0x10b 117 | 118 | // Optional Header Standard Fields for PE32 119 | typedef struct _EFI_IMAGE_OPTIONAL_HEADER32{ 120 | // Standard fields 121 | UINT16 Magic; 122 | UINT8 MajorLinkerVersion; 123 | UINT8 MinorLinkerVersion; 124 | UINT32 SizeOfCode; 125 | UINT32 SizeOfInitializedData; 126 | UINT32 SizeOfUninitializedData; 127 | UINT32 AddressOfEntryPoint; 128 | UINT32 BaseOfCode; 129 | UINT32 BaseOfData; // PE32 contains this additional field, which is absent in PE32+ 130 | 131 | // Optional Header Windows-Specific Fields 132 | UINT32 ImageBase; 133 | UINT32 SectionAlignment; 134 | UINT32 FileAlignment; 135 | UINT16 MajorOperatingSystemVersion; 136 | UINT16 MinorOperatingSystemVersion; 137 | UINT16 MajorImageVersion; 138 | UINT16 MinorImageVersion; 139 | UINT16 MajorSubsystemVersion; 140 | UINT16 MinorSubsystemVersion; 141 | UINT32 Win32VersionValue; 142 | UINT32 SizeOfImage; 143 | UINT32 SizeOfHeaders; 144 | UINT32 CheckSum; 145 | UINT16 Subsystem; 146 | UINT16 DllCharacteristics; 147 | UINT32 SizeOfStackReserve; 148 | UINT32 SizeOfStackCommit; 149 | UINT32 SizeOfHeapReserve; 150 | UINT32 SizeOfHeapCommit; 151 | UINT32 LoaderFlags; 152 | UINT32 NumberOfRvaAndSizes; 153 | EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; 154 | } EFI_IMAGE_OPTIONAL_HEADER32; 155 | 156 | // Optional Header Standard Fields for PE32+ 157 | typedef struct _EFI_IMAGE_OPTIONAL_HEADER64 { 158 | // 159 | // Standard fields. 160 | // 161 | UINT16 Magic; 162 | UINT8 MajorLinkerVersion; 163 | UINT8 MinorLinkerVersion; 164 | UINT32 SizeOfCode; 165 | UINT32 SizeOfInitializedData; 166 | UINT32 SizeOfUninitializedData; 167 | UINT32 AddressOfEntryPoint; 168 | UINT32 BaseOfCode; 169 | 170 | // 171 | // Optional Header Windows-Specific Fields. 172 | // 173 | UINT64 ImageBase; 174 | UINT32 SectionAlignment; 175 | UINT32 FileAlignment; 176 | UINT16 MajorOperatingSystemVersion; 177 | UINT16 MinorOperatingSystemVersion; 178 | UINT16 MajorImageVersion; 179 | UINT16 MinorImageVersion; 180 | UINT16 MajorSubsystemVersion; 181 | UINT16 MinorSubsystemVersion; 182 | UINT32 Win32VersionValue; 183 | UINT32 SizeOfImage; 184 | UINT32 SizeOfHeaders; 185 | UINT32 CheckSum; 186 | UINT16 Subsystem; 187 | UINT16 DllCharacteristics; 188 | UINT64 SizeOfStackReserve; 189 | UINT64 SizeOfStackCommit; 190 | UINT64 SizeOfHeapReserve; 191 | UINT64 SizeOfHeapCommit; 192 | UINT32 LoaderFlags; 193 | UINT32 NumberOfRvaAndSizes; 194 | EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; 195 | } EFI_IMAGE_OPTIONAL_HEADER64; 196 | 197 | // PE32 image header 198 | typedef struct _EFI_IMAGE_PE_HEADER { 199 | UINT32 Signature; 200 | EFI_IMAGE_FILE_HEADER FileHeader; 201 | EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader; 202 | } EFI_IMAGE_PE_HEADER; 203 | 204 | // PE32 image header 205 | typedef struct _EFI_IMAGE_PEPLUS_HEADER { 206 | UINT32 Signature; 207 | EFI_IMAGE_FILE_HEADER FileHeader; 208 | EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader; 209 | } EFI_IMAGE_PEPLUS_HEADER; 210 | 211 | typedef union _EFI_IMAGE_PE_HEADERS { 212 | EFI_IMAGE_PE_HEADER Header32; 213 | EFI_IMAGE_PEPLUS_HEADER Header64; 214 | } EFI_IMAGE_PE_HEADERS; 215 | 216 | // Section Table. This table immediately follows the optional header. 217 | typedef struct _EFI_IMAGE_SECTION_HEADER { 218 | UINT8 Name[8]; 219 | union { 220 | UINT32 PhysicalAddress; 221 | UINT32 VirtualSize; 222 | } Misc; 223 | UINT32 VirtualAddress; 224 | UINT32 SizeOfRawData; 225 | UINT32 PointerToRawData; 226 | UINT32 PointerToRelocations; 227 | UINT32 PointerToLinenumbers; 228 | UINT16 NumberOfRelocations; 229 | UINT16 NumberOfLinenumbers; 230 | UINT32 Characteristics; 231 | } EFI_IMAGE_SECTION_HEADER; 232 | 233 | // Header format for TE images, defined in the PI Specification 1.0. 234 | typedef struct { 235 | UINT16 Signature; // The signature for TE format = "VZ" 236 | UINT16 Machine; // From original file header 237 | UINT8 NumberOfSections; // From original file header 238 | UINT8 Subsystem; // From original optional header 239 | UINT16 StrippedSize; // Number of bytes we removed from header 240 | UINT32 AddressOfEntryPoint; // Offset to entry point -- from original optional header 241 | UINT32 BaseOfCode; // From original image -- required for ITP debug 242 | UINT64 ImageBase; // From original file header (ORLY?) 243 | EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; // Only base relocation and debug directories 244 | } EFI_IMAGE_TE_HEADER; 245 | 246 | // Data directory indexes in TE image header 247 | #define EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC 0 248 | #define EFI_IMAGE_TE_DIRECTORY_ENTRY_DEBUG 1 249 | 250 | // Return values 251 | #define ERR_SUCCESS 0 252 | #define ERR_OUT_OF_MEMORY 1 253 | #define ERR_INVALID_PARAMETER 2 254 | #define ERR_INVALID_IMAGE 3 255 | #define ERR_FILE_OPEN 4 256 | #define ERR_FILE_READ 5 257 | #define ERR_FILE_CREATE 6 258 | #define ERR_FILE_WRITE 7 259 | 260 | #pragma pack(pop) 261 | 262 | // DOS header 263 | static EFI_IMAGE_DOS_HEADER DosHeader = { 0 }; 264 | 265 | // PE header 266 | static EFI_IMAGE_PE_HEADERS PeHeader = { 0 }; 267 | 268 | // Convert function 269 | UINT8 convert(UINT8* te, UINTN teSize, UINT8** peOut, UINTN* peOutSize, char apply_fixup) 270 | { 271 | UINTN i; 272 | UINT8* pe; 273 | UINTN peSize; 274 | EFI_IMAGE_TE_HEADER* teHeader; 275 | EFI_IMAGE_SECTION_HEADER* sectionHeader; 276 | UINT32 SectionsCount = 0; 277 | UINT32 ConvSize = 0; 278 | unsigned char Is64Bit = 0; 279 | 280 | // Check arguments for sanity 281 | if (!te || teSize <= sizeof(EFI_IMAGE_TE_HEADER) || !peOut || !peOutSize) { 282 | printf("convert: called with invalid parameter\n"); 283 | return ERR_INVALID_PARAMETER; 284 | } 285 | 286 | // Check TE header to be valid and remove it from the input 287 | teHeader = (EFI_IMAGE_TE_HEADER*) te; 288 | te += sizeof(EFI_IMAGE_TE_HEADER); 289 | teSize -= sizeof(EFI_IMAGE_TE_HEADER); 290 | 291 | if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { 292 | printf("convert: TE signature not found. Not a TE image, maybe?\n"); 293 | return ERR_INVALID_IMAGE; 294 | } 295 | 296 | // Calculate PE image size 297 | peSize = teHeader->StrippedSize + teSize; 298 | 299 | // Start filling DosHeader and PeHeader based on current TE header 300 | DosHeader.e_magic = EFI_IMAGE_DOS_SIGNATURE; 301 | switch (teHeader->Machine) { 302 | case IMAGE_FILE_MACHINE_ARM: 303 | case IMAGE_FILE_MACHINE_THUMB: 304 | case IMAGE_FILE_MACHINE_ARMV7: 305 | case IMAGE_FILE_MACHINE_I386: 306 | Is64Bit = 0; 307 | 308 | PeHeader.Header32.Signature = EFI_IMAGE_PE_SIGNATURE; 309 | PeHeader.Header32.FileHeader.Machine = teHeader->Machine; 310 | PeHeader.Header32.FileHeader.NumberOfSections = teHeader->NumberOfSections; 311 | PeHeader.Header32.FileHeader.SizeOfOptionalHeader = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); 312 | PeHeader.Header32.FileHeader.Characteristics = 0x210E; 313 | PeHeader.Header32.OptionalHeader.Magic = EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC; 314 | PeHeader.Header32.OptionalHeader.AddressOfEntryPoint = teHeader->AddressOfEntryPoint; 315 | PeHeader.Header32.OptionalHeader.BaseOfCode = teHeader->BaseOfCode; 316 | if (apply_fixup) PeHeader.Header32.OptionalHeader.ImageBase = (UINT32)(teHeader->ImageBase - teHeader->StrippedSize + sizeof(EFI_IMAGE_TE_HEADER)); 317 | else PeHeader.Header32.OptionalHeader.ImageBase = (UINT32)(teHeader->ImageBase); 318 | PeHeader.Header32.OptionalHeader.SectionAlignment = 0x10; 319 | PeHeader.Header32.OptionalHeader.FileAlignment = 0x10; 320 | PeHeader.Header32.OptionalHeader.SizeOfImage = (UINT32)peSize; 321 | PeHeader.Header32.OptionalHeader.Subsystem = teHeader->Subsystem; 322 | PeHeader.Header32.OptionalHeader.NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; 323 | PeHeader.Header32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = teHeader->DataDirectory[0].VirtualAddress; 324 | PeHeader.Header32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = teHeader->DataDirectory[0].Size; 325 | PeHeader.Header32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = teHeader->DataDirectory[1].VirtualAddress; 326 | PeHeader.Header32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = teHeader->DataDirectory[1].Size; 327 | break; 328 | 329 | case IMAGE_FILE_MACHINE_AMD64: 330 | case IMAGE_FILE_MACHINE_IA64: 331 | case IMAGE_FILE_MACHINE_ARM64: 332 | Is64Bit = 1; 333 | 334 | PeHeader.Header64.Signature = EFI_IMAGE_PE_SIGNATURE; 335 | PeHeader.Header64.FileHeader.Machine = teHeader->Machine; 336 | PeHeader.Header64.FileHeader.NumberOfSections = teHeader->NumberOfSections; 337 | PeHeader.Header64.FileHeader.SizeOfOptionalHeader = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); 338 | PeHeader.Header64.FileHeader.Characteristics = 0x210E; 339 | PeHeader.Header64.OptionalHeader.Magic = EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC; 340 | PeHeader.Header64.OptionalHeader.AddressOfEntryPoint = teHeader->AddressOfEntryPoint; 341 | PeHeader.Header64.OptionalHeader.BaseOfCode = teHeader->BaseOfCode; 342 | if (apply_fixup) PeHeader.Header64.OptionalHeader.ImageBase = teHeader->ImageBase - teHeader->StrippedSize + sizeof(EFI_IMAGE_TE_HEADER); 343 | else PeHeader.Header64.OptionalHeader.ImageBase = teHeader->ImageBase; 344 | PeHeader.Header64.OptionalHeader.SectionAlignment = 0x10; 345 | PeHeader.Header64.OptionalHeader.FileAlignment = 0x10; 346 | PeHeader.Header64.OptionalHeader.SizeOfImage = (UINT32)peSize; 347 | PeHeader.Header64.OptionalHeader.Subsystem = teHeader->Subsystem; 348 | PeHeader.Header64.OptionalHeader.NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; 349 | PeHeader.Header64.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = teHeader->DataDirectory[0].VirtualAddress; 350 | PeHeader.Header64.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = teHeader->DataDirectory[0].Size; 351 | PeHeader.Header64.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = teHeader->DataDirectory[1].VirtualAddress; 352 | PeHeader.Header64.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = teHeader->DataDirectory[1].Size; 353 | break; 354 | 355 | default: 356 | printf("ERROR: Invalid or unknown PE Optional Header Magic\n"); 357 | return ERR_INVALID_IMAGE; 358 | } 359 | 360 | // Not filled are e_lfanew, SizeOfHeaders, SizeOfCode, SizeOfInitData, SizeOfUninitData, BaseOfData 361 | 362 | // Parse sections to determine unfilled elements 363 | sectionHeader = (EFI_IMAGE_SECTION_HEADER*)(teHeader + 1); 364 | // Fill size of headers based on the first section offset 365 | if (Is64Bit) { 366 | PeHeader.Header64.OptionalHeader.SizeOfHeaders = sectionHeader->PointerToRawData; 367 | } else { 368 | PeHeader.Header32.OptionalHeader.SizeOfHeaders = sectionHeader->PointerToRawData; 369 | } 370 | 371 | SectionsCount = teHeader->NumberOfSections; 372 | for (i = 0; i < SectionsCount; i++, sectionHeader++) 373 | { 374 | // Try code section 375 | if (!strncmp((char *)sectionHeader->Name, ".text", sizeof(sectionHeader->Name))) { 376 | // Check code section sanity 377 | if (sectionHeader->PointerToRawData != teHeader->BaseOfCode) { 378 | printf("convert: .text->PointerToRawData (%08Xh) != BaseOfCode (%08Xh). Invalid TE image?\n", sectionHeader->PointerToRawData, teHeader->BaseOfCode); 379 | return ERR_INVALID_IMAGE; 380 | } 381 | 382 | if (Is64Bit) { 383 | PeHeader.Header64.OptionalHeader.SizeOfCode = sectionHeader->SizeOfRawData; 384 | } else { 385 | PeHeader.Header32.OptionalHeader.SizeOfCode = sectionHeader->SizeOfRawData; 386 | } 387 | } 388 | // Try initialized data section 389 | else if (!strncmp((char *)sectionHeader->Name, ".data", sizeof(sectionHeader->Name))) { 390 | if (Is64Bit == 0) { 391 | PeHeader.Header32.OptionalHeader.BaseOfData = sectionHeader->PointerToRawData; 392 | PeHeader.Header32.OptionalHeader.SizeOfInitializedData = sectionHeader->SizeOfRawData; 393 | } else { 394 | PeHeader.Header64.OptionalHeader.SizeOfInitializedData = sectionHeader->SizeOfRawData; 395 | } 396 | } 397 | // Try uninitialized data section 398 | else if (!strncmp((char *)sectionHeader->Name, ".rdata", sizeof(sectionHeader->Name))) { 399 | if (Is64Bit) { 400 | PeHeader.Header64.OptionalHeader.SizeOfUninitializedData = sectionHeader->SizeOfRawData; 401 | } else { 402 | PeHeader.Header32.OptionalHeader.SizeOfUninitializedData = sectionHeader->SizeOfRawData; 403 | } 404 | } 405 | // Try relocation section 406 | else if (!strncmp((char *)sectionHeader->Name, ".reloc", sizeof(sectionHeader->Name))) { 407 | //TODO: add more sanity checks in case of incorrect images 408 | } 409 | // Try relocation section 410 | else if (!strncmp((char *)sectionHeader->Name, ".debug", sizeof(sectionHeader->Name))) { 411 | //TODO: add more sanity checks in case of incorrect images 412 | } 413 | else { 414 | UINT8 name[sizeof(sectionHeader->Name) + 1]; 415 | name[sizeof(sectionHeader->Name)] = 0; // Ensure trailing zero 416 | memcpy(name, sectionHeader->Name, sizeof(sectionHeader->Name)); 417 | printf("convert: unknown section \"%s\" found in TE image\n", name); 418 | } 419 | } 420 | 421 | // Calculate e_lfanew 422 | if (Is64Bit) { 423 | DosHeader.e_lfanew = teHeader->StrippedSize - sizeof(EFI_IMAGE_PEPLUS_HEADER); 424 | } else { 425 | DosHeader.e_lfanew = teHeader->StrippedSize - sizeof(EFI_IMAGE_PE_HEADER); 426 | } 427 | 428 | // Allocate buffer for PE image 429 | pe = (UINT8*)malloc(peSize); 430 | if (!pe) { 431 | printf("convert: failed to allocate enough memory for PE image\n"); 432 | return ERR_OUT_OF_MEMORY; 433 | } 434 | // Zero allocated memory 435 | memset(pe, 0, peSize); 436 | 437 | // Copy filled data into newly allocated buffer 438 | memcpy(pe, &DosHeader, sizeof(EFI_IMAGE_DOS_HEADER)); 439 | 440 | if (Is64Bit) { 441 | memcpy(pe + DosHeader.e_lfanew, &PeHeader, sizeof(EFI_IMAGE_PEPLUS_HEADER)); 442 | } else { 443 | memcpy(pe + DosHeader.e_lfanew, &PeHeader, sizeof(EFI_IMAGE_PE_HEADER)); 444 | } 445 | 446 | memcpy(pe + teHeader->StrippedSize, te, teSize); 447 | 448 | // Fill output parameters 449 | *peOut = pe; 450 | *peOutSize = peSize; 451 | 452 | return ERR_SUCCESS; 453 | } 454 | 455 | // Main 456 | int main(int argc, char* argv[]) 457 | { 458 | FILE* file; 459 | UINT8* buffer; 460 | UINT8* image; 461 | UINTN filesize; 462 | UINTN imagesize; 463 | UINTN read; 464 | UINT8 status; 465 | char opt_fixup = 0; 466 | const char* infile = NULL; 467 | const char* outfile = NULL; 468 | 469 | // Check options 470 | if (argc >= 2 && (memcmp(argv[1], "-f", 3) == 0 || memcmp(argv[1], "-fixup", 7) == 0)) { 471 | opt_fixup = 1; 472 | } 473 | 474 | // Check arguments count 475 | if ((opt_fixup && argc !=4) || (!opt_fixup && argc !=3)) { 476 | // Print usage and exit 477 | printf("TE2PE v0.1.4 - converts Terse Executable image file into PE32 image file\n\n" 478 | "Usage: TE2PE [-f] te_file.bin pe_file.bin\n" 479 | "Options: -f, --fixup Apply TE image base fix-up for PE image entry point\n" 480 | " The fixup is required for certain TE images\n" 481 | " extracted from AMI AptioV-based UEFI binaries\n" 482 | " Try this option if the resulting PE image\n" 483 | " appears to have incorrect entry point offset\n"); 484 | return ERR_INVALID_PARAMETER; 485 | } 486 | 487 | // Set infile and outfile 488 | if (opt_fixup) { 489 | infile = argv[2]; 490 | outfile = argv[3]; 491 | } 492 | else { 493 | infile = argv[1]; 494 | outfile = argv[2]; 495 | } 496 | 497 | // Read input file 498 | file = fopen(infile, "rb"); 499 | if (!file) { 500 | printf("Can't open input file\n"); 501 | return ERR_FILE_OPEN; 502 | } 503 | 504 | // Get file size 505 | fseek(file, 0, SEEK_END); 506 | filesize = ftell(file); 507 | fseek(file, 0, SEEK_SET); 508 | 509 | // Allocate buffer 510 | buffer = (UINT8*)malloc(filesize); 511 | if (!buffer) { 512 | printf("Can't allocate memory for input file\n"); 513 | return ERR_OUT_OF_MEMORY; 514 | } 515 | 516 | // Read the whole file into buffer 517 | read = fread((VOID*)buffer, 1, filesize, file); 518 | if (read != filesize) { 519 | printf("Can't read input file\n"); 520 | return ERR_FILE_READ; 521 | } 522 | 523 | // Close input file 524 | fclose(file); 525 | 526 | // Call conversion routine 527 | status = convert(buffer, filesize, &image, &imagesize, opt_fixup); 528 | if (status) 529 | return status; 530 | 531 | // Create output file 532 | file = fopen(outfile, "wb"); 533 | if (!file) { 534 | printf("Can't create output file\n"); 535 | return ERR_FILE_CREATE; 536 | } 537 | 538 | // Write converted image 539 | if (fwrite(image, 1, imagesize, file) != imagesize) { 540 | printf("Can't write to output file\n"); 541 | return ERR_FILE_WRITE; 542 | } 543 | 544 | // Close output file 545 | fclose(file); 546 | 547 | return ERR_SUCCESS; 548 | } 549 | --------------------------------------------------------------------------------