Dirtbox/Xbe.cpp

633 lines
23 KiB
C++

// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->Core->Xbe.cpp
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
// *
// * All rights reserved
// *
// ******************************************************************
#include "Xbe.h"
#include "MicroExe.h"
#include <memory.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#define XBE_ERROR(str, ...) do \
{ \
printf("\nError in Xbe::Xbe: " str "\n", __VA_ARGS__); \
if (XbeFile != 0) \
fclose(XbeFile); \
exit(1); \
} while(0)
#define XBE_PATCH_ERROR(str, ...) do \
{ \
printf("\nError in Xbe::PatchExe: " str "\n", __VA_ARGS__); \
return 1; \
} while(0)
#define XBE_WRITE_ERROR(str, ...) do \
{ \
printf("\nError in Xbe::WriteExe: " str "\n", __VA_ARGS__); \
if (ExeBuffer != 0) \
delete [] ExeBuffer; \
return 1; \
} while(0)
// ******************************************************************
// * Signature for MapRegisters patch and the replacing code
// ******************************************************************
#define PATCH_LENGTH 11
static uint08 PatchSignature[] =
"\xC7\x01\x00\x00\x00\xFD"
"\xA1\x04\x18\x00\xFD";
static void __declspec(naked) PatchCode()
{
__asm
{
mov dword ptr [ecx], 0x84000000
xor eax, eax
inc eax
ret
int 3
}
}
// ******************************************************************
// * Trampoline that will be located at entry point
// ******************************************************************
#define TRAMPOLINE_LENGTH 10
static void __declspec(naked) TrampolineCode()
{
__asm
{
mov edx, 0x000100EC
call dword ptr [edx]
ret
nop
nop
}
}
// ******************************************************************
// * constructor
// ******************************************************************
Xbe::Xbe(const char *Filename)
{
HeaderEx = 0;
HeaderExSize = 0;
SectionHeader = 0;
SectionName = 0;
LibraryVersion = 0;
KernelLibraryVersion = 0;
XapiLibraryVersion = 0;
Tls = 0;
Section = 0;
printf("Xbe::Xbe: Opening Xbe file...");
FILE *XbeFile = fopen(Filename, "rb");
if(XbeFile == 0)
XBE_ERROR("Could not open Xbe file.");
printf("OK\n");
// ******************************************************************
// * remember xbe path
// ******************************************************************
{
printf("Xbe::Xbe Storing Xbe Path...");
strcpy(Path, Filename);
int v = 0, c = 0;
while(Path[v] != '\0')
{
if(Path[v] == '\\')
c = v + 1;
v++;
}
Path[c] = '\0';
}
printf("OK\n");
// ******************************************************************
// * read xbe image header
// ******************************************************************
{
printf("Xbe::Xbe: Reading Image Header...");
if(fread(&Header, sizeof(Xbe::HEADER), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Image Header");
if(Header.Magic != *(uint32 *)"XBEH")
XBE_ERROR("Invalid magic number in Xbe file");
printf("OK\n");
}
// ******************************************************************
// * read xbe image header extra bytes
// ******************************************************************
if(Header.SizeOfHeaders > sizeof(Xbe::HEADER))
{
printf("Xbe::Xbe: Reading Image Header Extra Bytes...");
uint32 HeaderExSize = RoundUp(Header.SizeOfHeaders, PAGE_SIZE) - sizeof(Header);
HeaderEx = new char[HeaderExSize];
if(fread(HeaderEx, HeaderExSize, 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Image Header (Ex)");
printf("OK\n");
}
// ******************************************************************
// * read xbe certificate
// ******************************************************************
{
printf("Xbe::Xbe: Reading Certificate...");
fseek(XbeFile, Header.CertificateAddr - Header.BaseAddr, SEEK_SET);
if(fread(&Certificate, sizeof(Xbe::CERTIFICATE), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Certificate");
setlocale(LC_ALL, "English");
wcstombs(AsciiTitle, Certificate.TitleName, 40);
printf("OK\n");
printf("Xbe::Xbe: Title identified as %s\n", AsciiTitle);
}
// ******************************************************************
// * read xbe section headers
// ******************************************************************
{
printf("Xbe::Xbe: Reading Section Headers...\n");
fseek(XbeFile, Header.SectionHeadersAddr - Header.BaseAddr, SEEK_SET);
SectionHeader = new Xbe::SECTION_HEADER[Header.Sections];
for(uint32 v = 0; v < Header.Sections; v++)
{
printf("Xbe::Xbe: Reading Section Header 0x%.04X...", v);
if(fread(&SectionHeader[v], sizeof(Xbe::SECTION_HEADER), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Section Header %d (%Xh)", v, v);
printf("OK\n");
}
}
// ******************************************************************
// * read xbe section names
// ******************************************************************
{
printf("Xbe::Xbe: Reading Section Names...\n");
SectionName = new char[Header.Sections][9];
for(uint32 v = 0; v < Header.Sections; v++)
{
printf("Xbe::Xbe: Reading Section Name 0x%.04X...", v);
uint08 *sn = GetAddr(SectionHeader[v].SectionNameAddr);
memset(SectionName[v], 0, 9);
if(sn != 0)
{
for(int b = 0; b < 8; b++)
{
SectionName[v][b] = sn[b];
if(SectionName[v][b] == '\0')
break;
}
}
printf("OK (%s)\n", SectionName[v]);
}
}
// ******************************************************************
// * read xbe library versions
// ******************************************************************
if(Header.LibraryVersionsAddr != 0)
{
printf("Xbe::Xbe: Reading Library Versions...\n");
fseek(XbeFile, Header.LibraryVersionsAddr - Header.BaseAddr, SEEK_SET);
LibraryVersion = new Xbe::LIBRARY_VERSION[Header.LibraryVersions];
for(uint32 v = 0; v < Header.LibraryVersions; v++)
{
printf("Xbe::Xbe: Reading Library Version 0x%.04X...", v);
if(fread(&LibraryVersion[v], sizeof(Xbe::LIBRARY_VERSION), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Library Version %d (%Xh)", v, v);
printf("OK\n");
}
// ******************************************************************
// * read xbe kernel library version
// ******************************************************************
{
printf("Xbe::Xbe: Reading Kernel Library Version...");
if(Header.KernelLibraryVersionAddr == 0)
XBE_ERROR("Could not locate kernel library version");
fseek(XbeFile, Header.KernelLibraryVersionAddr - Header.BaseAddr, SEEK_SET);
KernelLibraryVersion = new Xbe::LIBRARY_VERSION;
if(fread(KernelLibraryVersion, sizeof(Xbe::LIBRARY_VERSION), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Kernel Version");
printf("OK\n");
}
// ******************************************************************
// * read xbe xapi library version
// ******************************************************************
{
printf("Xbe::Xbe: Reading Xapi Library Version...");
if(Header.XapiLibraryVersionAddr == 0)
XBE_ERROR("Could not locate Xapi Library Version", true);
fseek(XbeFile, Header.XapiLibraryVersionAddr - Header.BaseAddr, SEEK_SET);
XapiLibraryVersion = new Xbe::LIBRARY_VERSION;
if(fread(XapiLibraryVersion, sizeof(Xbe::LIBRARY_VERSION), 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Xapi Version", true);
printf("OK\n");
}
}
// ******************************************************************
// * read xbe sections
// ******************************************************************
{
printf("Xbe::Xbe: Reading Sections...\n");
Section = new uint08*[Header.Sections];
memset(Section, 0, Header.Sections);
for(uint32 v = 0; v < Header.Sections; v++)
{
printf("Xbe::Xbe: Reading Section 0x%.04X...", v);
uint32 RawSize = SectionHeader[v].SizeOfRaw;
uint32 RawAddr = SectionHeader[v].RawAddr;
Section[v] = new uint08[RawSize];
fseek(XbeFile, RawAddr, SEEK_SET);
if(RawSize == 0)
{
printf("OK\n");
continue;
}
if(fread(Section[v], RawSize, 1, XbeFile) != 1)
XBE_ERROR("Unexpected end of file while reading Xbe Section %d (%Xh) (%s)", v, v, SectionName[v]);
printf("OK\n");
}
}
// ******************************************************************
// * read xbe thread local storage
// ******************************************************************
if(Header.TlsAddr != 0)
{
printf("Xbe::Xbe: Reading Thread Local Storage...");
void *Addr = GetAddr(Header.TlsAddr);
if(Addr == 0)
XBE_ERROR("Could not locate Thread Local Storage");
Tls = new Xbe::TLS;
memcpy(Tls, Addr, sizeof(Xbe::TLS));
printf("OK\n");
}
fclose(XbeFile);
return;
}
// ******************************************************************
// * deconstructor
// ******************************************************************
Xbe::~Xbe()
{
if(Section != 0)
{
for(uint32 v = 0; v < Header.Sections; v++)
delete[] Section[v];
delete[] Section;
}
delete XapiLibraryVersion;
delete KernelLibraryVersion;
delete[] LibraryVersion;
delete Tls;
delete[] SectionName;
delete[] SectionHeader;
delete[] HeaderEx;
}
// ******************************************************************
// * Patcher
// ******************************************************************
int32 Xbe::PatchXbe()
{
printf("Xbe::PatchExe Patching initialization flags in Xbe...");
Header.InitFlags.MountUtilityDrive = 0;
Header.InitFlags.FormatUtilityDrive = 0;
printf("OK\n");
printf("Xbe::PatchExe Patching MapRegisters in Xbe...");
// ******************************************************************
// * find section with Direct3D code
// ******************************************************************
uint32 v;
for (v = 0; v < Header.Sections; v++)
if (strncmp(SectionName[v], "D3D", 9) == 0)
break;
if (v == Header.Sections)
XBE_PATCH_ERROR("Could not find D3D section");
// ******************************************************************
// * find patching location in D3D section
// ******************************************************************
uint32 i;
for (i = 0; i < SectionHeader[v].SizeOfRaw - PATCH_LENGTH; i++)
if (memcmp(&Section[v][i], PatchSignature, PATCH_LENGTH) == 0)
break;
if (i == SectionHeader[v].SizeOfRaw - PATCH_LENGTH)
XBE_PATCH_ERROR("Could not find signature in D3D section");
// ******************************************************************
// * patch MapRegisters
// ******************************************************************
memcpy(&Section[v][i], &PatchCode, PATCH_LENGTH);
printf("OK\n");
return 0;
}
// ******************************************************************
// * Writer
// ******************************************************************
int32 Xbe::WriteExe(const char *Filename)
{
printf("Xbe::WriteExe Converting Xbe to Exe...");
if (PatchXbe() != 0)
return 1;
// ******************************************************************
// * create buffer for "as-loaded" XBE/EXE hybrid
// ******************************************************************
uint08 *ExeBuffer = new uint08[Header.SizeOfImage];
if (ExeBuffer == 0)
XBE_WRITE_ERROR("Cannot allocate buffer for Exe");
memset(ExeBuffer, 0, Header.SizeOfImage);
// ******************************************************************
// * write xbe section headers
// ******************************************************************
memcpy(ExeBuffer + 0, &Header, sizeof(Xbe::HEADER));
memcpy(ExeBuffer + Header.SizeOfImageHeader, HeaderEx, HeaderExSize);
// ******************************************************************
// * write xbe sections
// ******************************************************************
for (uint32 v = 0; v < Header.Sections; v++)
{
uint32 offs = SectionHeader[v].VirtualAddr - Header.BaseAddr;
memcpy(ExeBuffer + offs, Section[v], SectionHeader[v].SizeOfRaw);
}
// ******************************************************************
// * patch digital signature with PE stub
// ******************************************************************
MICRO_EXE_HEADERS ExeHeaders;
ExeHeaders.DosHeader.Magic = *(uint16 *)"MZ";
ExeHeaders.DosHeader.Unused = 0;
ExeHeaders.Header.Magic = *(uint32 *)"PE\0\0";
ExeHeaders.Header.Machine = IMAGE_FILE_MACHINE_I386;
ExeHeaders.Header.NumberOfSections = 1;
ExeHeaders.Header.TimeDateStamp = Header.TimeDate;
ExeHeaders.Header.PointerToSymbolTable = 0;
ExeHeaders.Header.NumberOfSymbols = 0;
ExeHeaders.Header.SizeOfOptionalHeader = sizeof(MICRO_EXE_HEADERS::OPTIONAL_HEADER);
ExeHeaders.Header.Characteristics =
IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE |
IMAGE_FILE_LARGE_ADDRESS_AWARE | IMAGE_FILE_32BIT_MACHINE;
ExeHeaders.OptionalHeader.Magic = 0x010B;
ExeHeaders.OptionalHeader.MajorLinkerVersion = 0x06;
ExeHeaders.OptionalHeader.MinorLinkerVersion = 0x00;
ExeHeaders.OptionalHeader.SizeOfCode = 0;
ExeHeaders.OptionalHeader.SizeOfInitializedData = 0;
ExeHeaders.OptionalHeader.SizeOfUninitializedData = 0;
ExeHeaders.OptionalHeader.AddressOfEntryPoint = (uint32)&ExeHeaders.Trampoline - (uint32)&ExeHeaders;
ExeHeaders.OptionalHeader.BaseOfCode = 0;
ExeHeaders.OptionalHeader.BaseOfData = 0;
ExeHeaders.OptionalHeader.ImageBase = Header.BaseAddr;
ExeHeaders.OptionalHeader.Lfanew_SectionAlignment = EXE_ALIGNMENT;
ExeHeaders.OptionalHeader.FileAlignment = EXE_ALIGNMENT;
ExeHeaders.OptionalHeader.MajorOperatingSystemVersion = 4;
ExeHeaders.OptionalHeader.MinorOperatingSystemVersion = 0;
// This is where the imported DLL name "DbE\0" will be located
strncpy(ExeHeaders.OptionalHeader.DirtboxDllName, "DbE", 4);
ExeHeaders.OptionalHeader.MajorSubsystemVersion = 4;
ExeHeaders.OptionalHeader.MinorSubsystemVersion = 0;
ExeHeaders.OptionalHeader.Win32VersionValue = 0;
ExeHeaders.OptionalHeader.SizeOfImage = Header.SizeOfImage; // already aligned at 0x20
ExeHeaders.OptionalHeader.SizeOfHeaders = RoundUp(sizeof(MICRO_EXE_HEADERS), EXE_ALIGNMENT);
ExeHeaders.OptionalHeader.CheckSum = 0;
ExeHeaders.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
ExeHeaders.OptionalHeader.DllCharacteristics = 0x400;
ExeHeaders.OptionalHeader.SizeOfStackReserve = 0x100000;
ExeHeaders.OptionalHeader.SizeOfStackCommit = 0x1000;
ExeHeaders.OptionalHeader.SizeOfHeapReserve = 0x100000;
ExeHeaders.OptionalHeader.SizeOfHeapCommit = 0x1000;
ExeHeaders.OptionalHeader.LoaderFlags = 0;
ExeHeaders.OptionalHeader.NumberOfRvaAndSizes = 4;
// ******************************************************************
// * the other directories
// ******************************************************************
for (uint32 v = 0; v < 4; v++)
{
ExeHeaders.OptionalHeader.DataDirectory[v].VirtualAddress = 0;
ExeHeaders.OptionalHeader.DataDirectory[v].Size = 0;
}
// ******************************************************************
// * import directory
// ******************************************************************
uint32 offs = (uint32)&ExeHeaders.ImageImportDescriptor - (uint32)&ExeHeaders;
ExeHeaders.OptionalHeader.DataDirectory[1].VirtualAddress = offs;
ExeHeaders.OptionalHeader.DataDirectory[1].Size =
2*sizeof(MICRO_EXE_HEADERS::IMAGE_IMPORT_DESCRIPTOR);
// ******************************************************************
// * the one and only section header
// ******************************************************************
uint32 XbeSectionAddress = 0; // the whole image
uint32 XbeSectionSize = Header.SizeOfImage - XbeSectionAddress; // already aligned at 0x20
strncpy(ExeHeaders.SectionHeader.Name, "loldong", 8);
ExeHeaders.SectionHeader.VirtualSize = XbeSectionSize;
ExeHeaders.SectionHeader.VirtualAddress = XbeSectionAddress;
ExeHeaders.SectionHeader.SizeOfRawData = XbeSectionSize;
ExeHeaders.SectionHeader.PointerToRawData = XbeSectionAddress;
ExeHeaders.SectionHeader.PointerToRelocations = 0;
ExeHeaders.SectionHeader.PointerToLinenumbers = 0;
ExeHeaders.SectionHeader.NumberOfRelocations = 0;
ExeHeaders.SectionHeader.NumberOfLinenumbers = 0;
ExeHeaders.SectionHeader.Characteristics = 0x60000020;
// ******************************************************************
// * image import descriptor, only one DLL
// ******************************************************************
uint32 offsIat = (uint32)&ExeHeaders.ImportAddressTable - (uint32)&ExeHeaders;
uint32 offsName = (uint32)ExeHeaders.OptionalHeader.DirtboxDllName - (uint32)&ExeHeaders;
ExeHeaders.ImageImportDescriptor[0].OriginalFirstThunk = offsIat;
ExeHeaders.ImageImportDescriptor[0].TimeDateStamp = 0;
ExeHeaders.ImageImportDescriptor[0].ForwarderChain = 0;
ExeHeaders.ImageImportDescriptor[0].Name = offsName;
ExeHeaders.ImageImportDescriptor[0].FirstThunk = offsIat;
ExeHeaders.ImageImportDescriptor[1].OriginalFirstThunk = 0;
ExeHeaders.ImageImportDescriptor[1].TimeDateStamp = 0;
ExeHeaders.ImageImportDescriptor[1].ForwarderChain = 0;
ExeHeaders.ImageImportDescriptor[1].Name = 0;
ExeHeaders.ImageImportDescriptor[1].FirstThunk = 0;
// ******************************************************************
// * import address table, import by ordinal
// ******************************************************************
ExeHeaders.ImportAddressTable[0] = 0x80000001;
ExeHeaders.ImportAddressTable[1] = 0;
// ******************************************************************
// * trampoline that calls the Dirtbox loader in the DLL
// ******************************************************************
memcpy(ExeHeaders.Trampoline, &TrampolineCode, TRAMPOLINE_LENGTH);
// ******************************************************************
// * replaces the magic and digital signature with PE headers
// ******************************************************************
memcpy(ExeBuffer + 0, &ExeHeaders, sizeof(MICRO_EXE_HEADERS));
// ******************************************************************
// * write the created buffer into EXE file
// ******************************************************************
FILE *ExeFile = fopen(Filename, "wb");
if (ExeFile == 0)
XBE_WRITE_ERROR("Could not open Exe file");
fwrite(ExeBuffer, Header.SizeOfImage, 1, ExeFile);
fclose(ExeFile);
// ******************************************************************
// * free buffer for Exe file
// ******************************************************************
delete [] ExeBuffer;
printf("OK\n");
return 0;
}
// ******************************************************************
// * GetAddr
// ******************************************************************
uint08 *Xbe::GetAddr(uint32 VirtualAddress)
{
uint32 offs = VirtualAddress - Header.BaseAddr;
// ******************************************************************
// * offset in image header
// ******************************************************************
if(offs < sizeof(Header))
return &((uint08*)&Header)[offs];
// ******************************************************************
// * offset in image header extra bytes
// ******************************************************************
if(offs < Header.SizeOfHeaders)
return (uint08*)&HeaderEx[offs - sizeof(Header)];
// ******************************************************************
// * offset in some random section
// ******************************************************************
{
for(uint32 v = 0; v < Header.Sections; v++)
{
uint32 VirtAddr = SectionHeader[v].VirtualAddr;
uint32 VirtSize = SectionHeader[v].VirtualSize;
if( (VirtualAddress >= VirtAddr) && (VirtualAddress < (VirtAddr + VirtSize)) )
return &Section[v][VirtualAddress - VirtAddr];
}
}
return 0;
}