commit d1a4fd35a714906a0a96097eaa74ddaf7a283916 Author: Ash Wolf Date: Mon Jun 13 02:20:18 2022 +0200 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..366edae --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ash Wolf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..07065be --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# WiBo + +An experiment to try and write a minimal, low-fuss wrapper that can run really simple command-line 32-bit Windows binaries on Linux - with less faff and less dependencies than WINE. + +Don't run this on any untrusted executables, I implore you. (Or probably just don't run it at all... :p) + + g++ -g -m32 -std=c++20 -lstdc++ main.cpp kernel32.cpp advapi32.cpp loader.cpp + +If you need something like this project (but more mature), you might find [taviso/loadlibrary](https://github.com/taviso/loadlibrary) more interesting. + +--- + +Rough to-do list: + +- Implement more APIs +- Do something intelligent with Windows `HANDLE`s +- Pass the command line properly +- Convert paths in environment variables (and the structure of `PATH` itself, maybe) to Windows format +- Implement PE relocations rather than just failing unceremoniously +- Make the PE loader work for DLLs as well in case we ever want to load some diff --git a/advapi32.cpp b/advapi32.cpp new file mode 100644 index 0000000..74908c8 --- /dev/null +++ b/advapi32.cpp @@ -0,0 +1,14 @@ +#include "common.h" + +namespace advapi32 { + unsigned int WIN_FUNC RegOpenKeyExA(void *hKey, const char *lpSubKey, unsigned int ulOptions, void *samDesired, void **phkResult) { + printf("RegOpenKeyExA(key=%p, subkey=%s, ...)\n", hKey, lpSubKey); + return 1; // screw them for now + } +} + +void *wibo::resolveAdvApi32(const char *name) { + if (strcmp(name, "RegOpenKeyExA") == 0) return (void *) advapi32::RegOpenKeyExA; + return 0; +} + diff --git a/common.h b/common.h new file mode 100644 index 0000000..aa07c6a --- /dev/null +++ b/common.h @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +#define WIN_FUNC __attribute__((stdcall)) + +namespace wibo { + extern uint32_t lastError; + extern char *commandLine; + + void *resolveKernel32(const char *name); + void *resolveAdvApi32(const char *name); + void *resolveStubByName(const char *dllName, const char *funcName); + void *resolveStubByOrdinal(const char *dllName, uint16_t ordinal); + + struct Executable { + Executable(); + ~Executable(); + bool loadPE(FILE *file); + + void *imageBuffer; + size_t imageSize; + void *entryPoint; + + template + T *fromRVA(uint32_t rva) { + return (T *) (rva + (uint8_t *) imageBuffer); + } + + template + T *fromRVA(T *rva) { + return fromRVA((uint32_t) rva); + } + }; +} diff --git a/kernel32.cpp b/kernel32.cpp new file mode 100644 index 0000000..223c969 --- /dev/null +++ b/kernel32.cpp @@ -0,0 +1,322 @@ +#include "common.h" +#include +#include +#include +#include + +namespace kernel32 { + uint32_t WIN_FUNC GetLastError() { + return wibo::lastError; + } + + void *WIN_FUNC GetCurrentProcess() { + return (void *) 0xFFFFFFFF; + } + + void WIN_FUNC InitializeCriticalSection(void *param) { + // printf("InitializeCriticalSection(...)\n"); + } + void WIN_FUNC DeleteCriticalSection(void *param) { + // printf("DeleteCriticalSection(...)\n"); + } + void WIN_FUNC EnterCriticalSection(void *param) { + // printf("EnterCriticalSection(...)\n"); + } + void WIN_FUNC LeaveCriticalSection(void *param) { + // printf("LeaveCriticalSection(...)\n"); + } + + /* + * TLS (Thread-Local Storage) + */ + enum { MAX_TLS_VALUES = 100 }; + static bool tlsValuesUsed[MAX_TLS_VALUES] = { false }; + static void *tlsValues[MAX_TLS_VALUES]; + unsigned int WIN_FUNC TlsAlloc() { + printf("TlsAlloc()\n"); + for (int i = 0; i < MAX_TLS_VALUES; i++) { + if (tlsValuesUsed[i] == false) { + tlsValuesUsed[i] = true; + tlsValues[i] = 0; + printf("...returning %d\n", i); + return i; + } + } + printf("...returning nothing\n"); + return 0xFFFFFFFF; + } + unsigned int WIN_FUNC TlsFree(unsigned int dwTlsIndex) { + printf("TlsFree(%u)\n", dwTlsIndex); + if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) { + tlsValuesUsed[dwTlsIndex] = false; + return 1; + } else { + return 0; + } + } + void *WIN_FUNC TlsGetValue(unsigned int dwTlsIndex) { + // printf("TlsGetValue(%u)\n", dwTlsIndex); + if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) + return tlsValues[dwTlsIndex]; + else + return 0; + } + unsigned int WIN_FUNC TlsSetValue(unsigned int dwTlsIndex, void *lpTlsValue) { + // printf("TlsSetValue(%u, %p)\n", dwTlsIndex, lpTlsValue); + if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) { + tlsValues[dwTlsIndex] = lpTlsValue; + return 1; + } else { + return 0; + } + } + + /* + * Memory + */ + void *WIN_FUNC GlobalAlloc(uint32_t uFlags, size_t dwBytes) { + printf("GlobalAlloc(flags=%x, size=%x)\n", uFlags, dwBytes); + if (uFlags & 2) { + // GMEM_MOVEABLE - not implemented rn + return 0; + } else { + // GMEM_FIXED - this is simpler + void *buffer = malloc(dwBytes); + if (buffer && (uFlags & 0x40)) { + // GMEM_ZEROINT + memset(buffer, 0, dwBytes); + } + return buffer; + } + } + void *WIN_FUNC GlobalFree(void *hMem) { + free(hMem); + return 0; + } + + /* + * Environment + */ + char *WIN_FUNC GetCommandLineA() { + return wibo::commandLine; + } + + char *WIN_FUNC GetEnvironmentStrings() { + // Step 1, figure out the size of the buffer we need. + size_t bufSize = 0; + char **work = environ; + + while (*work) { + bufSize += strlen(*work) + 1; + work++; + } + bufSize++; + + // Step 2, actually build that buffer + char *buffer = (char *) malloc(bufSize); + char *ptr = buffer; + work = environ; + + while (*work) { + size_t strSize = strlen(*work); + memcpy(ptr, *work, strSize); + ptr[strSize] = 0; + ptr += strSize + 1; + work++; + } + *ptr = 0; // an extra null at the end + + return buffer; + } + + void WIN_FUNC FreeEnvironmentStringsA(char *buffer) { + free(buffer); + } + + /* + * I/O + */ + void *WIN_FUNC GetStdHandle(uint32_t nStdHandle) { + switch (nStdHandle) { + case ((uint32_t) -10): // STD_INPUT_HANDLE + return stdin; + case ((uint32_t) -11): // STD_OUTPUT_HANDLE + return stdout; + case ((uint32_t) -12): // STD_ERROR_HANDLE + return stderr; + default: + return (void *) 0xFFFFFFFF; + } + } + + unsigned int WIN_FUNC DuplicateHandle(void *hSourceProcessHandle, void *hSourceHandle, void *hTargetProcessHandle, void **lpTargetHandle, unsigned int dwDesiredAccess, unsigned int bInheritHandle, unsigned int dwOptions) { + // This is kinda silly... + if (hSourceHandle == stdin || hSourceHandle == stdout || hSourceHandle == stderr) { + // Just pretend we duplicated it, why not + *lpTargetHandle = hSourceHandle; + return 1; + } + + // This probably won't come up + printf("Unhandled DuplicateHandle(source=%p)\n", hSourceHandle); + return 0; + } + + std::filesystem::path pathFromWindows(const char *inStr) { + // Convert to forward slashes + std::string str = inStr; + std::replace(str.begin(), str.end(), '\\', '/'); + + // Remove the drive letter + if (str.starts_with("c:/") || str.starts_with("C:/")) { + str.erase(0, 2); + } + + return std::filesystem::path(str); + } + + std::string pathToWindows(const std::filesystem::path &path) { + std::string str = path; + + if (path.is_absolute()) { + str.insert(0, "C:"); + } + + std::replace(str.begin(), str.end(), '/', '\\'); + return str; + } + + unsigned int WIN_FUNC GetFullPathNameA(const char *lpFileName, unsigned int nBufferLength, char *lpBuffer, char **lpFilePart) { + printf("GetFullPathNameA(%s)...\n", lpFileName); + std::filesystem::path absPath = std::filesystem::absolute(pathFromWindows(lpFileName)); + std::string absStr = pathToWindows(absPath); + printf("AbsPath: %s - %s\n", absPath.c_str(), absStr.c_str()); + + // Enough space? + if ((absStr.size() + 1) <= nBufferLength) { + strcpy(lpBuffer, absStr.c_str()); + + // Do we need to fill in FilePart? + if (lpFilePart) { + *lpFilePart = 0; + if (!std::filesystem::is_directory(absPath)) { + *lpFilePart = strrchr(lpBuffer, '\\'); + if (*lpFilePart) + *lpFilePart += 1; + } + } + + return absStr.size(); + } else { + return absStr.size() + 1; + } + } + + unsigned int WIN_FUNC GetFileAttributesA(const char *lpFileName) { + auto path = pathFromWindows(lpFileName); + printf("GetFileAttributesA(%s)... (%s)\n", lpFileName, path.c_str()); + auto status = std::filesystem::status(path); + + wibo::lastError = 0; + + switch (status.type()) { + case std::filesystem::file_type::regular: + printf("File exists\n"); + return 0x80; // FILE_ATTRIBUTE_NORMAL + case std::filesystem::file_type::directory: + return 0x10; // FILE_ATTRIBUTE_DIRECTORY + case std::filesystem::file_type::not_found: + default: + printf("File does not exist\n"); + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND + return 0xFFFFFFFF; // INVALID_FILE_ATTRIBUTES + } + } + + unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) { + // for now, we VERY naively assume that the handle is a FILE* + // haha this is gonna come back and bite me, isn't it + wibo::lastError = 0; + + size_t written = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, (FILE *) hFile); + if (lpNumberOfBytesWritten) + *lpNumberOfBytesWritten = written; + + if (written == 0) + wibo::lastError = 29; // ERROR_WRITE_FAULT + + return (written == nNumberOfBytesToWrite); + } + + /* + * Console Nonsense + */ + unsigned int WIN_FUNC SetConsoleCtrlHandler(void *HandlerRoutine, unsigned int Add) { + // This is a function that gets called when doing ^C + // We might want to call this later (being mindful that it'll be stdcall I think) + + // For now, just pretend we did the thing + return 1; + } + + struct CONSOLE_SCREEN_BUFFER_INFO { + int16_t dwSize_x; + int16_t dwSize_y; + int16_t dwCursorPosition_x; + int16_t dwCursorPosition_y; + uint16_t wAttributes; + int16_t srWindow_left; + int16_t srWindow_top; + int16_t srWindow_right; + int16_t srWindow_bottom; + int16_t dwMaximumWindowSize_x; + int16_t dwMaximumWindowSize_y; + }; + + unsigned int WIN_FUNC GetConsoleScreenBufferInfo(void *hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo) { + // Tell a lie + // mwcc doesn't care about anything else + lpConsoleScreenBufferInfo->dwSize_x = 80; + lpConsoleScreenBufferInfo->dwSize_y = 25; + + return 1; + } + + unsigned int WIN_FUNC GetSystemDirectoryA(char *lpBuffer, unsigned int uSize) { + strcpy(lpBuffer, "C:\\Windows\\System32"); + return strlen(lpBuffer); + } + + unsigned int WIN_FUNC GetWindowsDirectoryA(char *lpBuffer, unsigned int uSize) { + strcpy(lpBuffer, "C:\\Windows"); + return strlen(lpBuffer); + } +} + +void *wibo::resolveKernel32(const char *name) { + if (strcmp(name, "GetLastError") == 0) return (void *) kernel32::GetLastError; + if (strcmp(name, "GetCurrentProcess") == 0) return (void *) kernel32::GetCurrentProcess; + if (strcmp(name, "InitializeCriticalSection") == 0) return (void *) kernel32::InitializeCriticalSection; + if (strcmp(name, "DeleteCriticalSection") == 0) return (void *) kernel32::DeleteCriticalSection; + if (strcmp(name, "EnterCriticalSection") == 0) return (void *) kernel32::EnterCriticalSection; + if (strcmp(name, "LeaveCriticalSection") == 0) return (void *) kernel32::LeaveCriticalSection; + if (strcmp(name, "GlobalAlloc") == 0) return (void *) kernel32::GlobalAlloc; + if (strcmp(name, "GlobalFree") == 0) return (void *) kernel32::GlobalFree; + if (strcmp(name, "TlsAlloc") == 0) return (void *) kernel32::TlsAlloc; + if (strcmp(name, "TlsFree") == 0) return (void *) kernel32::TlsFree; + if (strcmp(name, "TlsGetValue") == 0) return (void *) kernel32::TlsGetValue; + if (strcmp(name, "TlsSetValue") == 0) return (void *) kernel32::TlsSetValue; + if (strcmp(name, "GetCommandLineA") == 0) return (void *) kernel32::GetCommandLineA; + if (strcmp(name, "GetEnvironmentStrings") == 0) return (void *) kernel32::GetEnvironmentStrings; + if (strcmp(name, "FreeEnvironmentStringsA") == 0) return (void *) kernel32::FreeEnvironmentStringsA; + if (strcmp(name, "GetStdHandle") == 0) return (void *) kernel32::GetStdHandle; + if (strcmp(name, "DuplicateHandle") == 0) return (void *) kernel32::DuplicateHandle; + if (strcmp(name, "GetFullPathNameA") == 0) return (void *) kernel32::GetFullPathNameA; + if (strcmp(name, "GetFileAttributesA") == 0) return (void *) kernel32::GetFileAttributesA; + if (strcmp(name, "WriteFile") == 0) return (void *) kernel32::WriteFile; + if (strcmp(name, "SetConsoleCtrlHandler") == 0) return (void *) kernel32::SetConsoleCtrlHandler; + if (strcmp(name, "GetConsoleScreenBufferInfo") == 0) return (void *) kernel32::GetConsoleScreenBufferInfo; + if (strcmp(name, "GetSystemDirectoryA") == 0) return (void *) kernel32::GetSystemDirectoryA; + if (strcmp(name, "GetWindowsDirectoryA") == 0) return (void *) kernel32::GetWindowsDirectoryA; + return 0; +} diff --git a/loader.cpp b/loader.cpp new file mode 100644 index 0000000..32ffabe --- /dev/null +++ b/loader.cpp @@ -0,0 +1,207 @@ +#include "common.h" +#include +#include +#include +#include +#include + +struct PEHeader { + uint8_t magic[4]; // "PE\0\0" + uint16_t machine; + uint16_t numberOfSections; + uint32_t timeDateStamp; + uint32_t pointerToSymbolTable; + uint32_t numberOfSymbols; + uint16_t sizeOfOptionalHeader; + uint16_t characteristics; +}; +struct PEImageDataDirectory { + uint32_t virtualAddress; + uint32_t size; +}; +struct PE32Header { + uint16_t magic; // 0x10B for PE32 + uint8_t majorLinkerVersion; + uint8_t minorLinkerVersion; + uint32_t sizeOfCode; + uint32_t sizeOfInitializedData; + uint32_t sizeOfUninitializedData; + uint32_t addressOfEntryPoint; + uint32_t baseOfCode; + uint32_t baseOfData; + uint32_t imageBase; + uint32_t sectionAlignment; + uint32_t fileAlignment; + uint16_t majorOperatingSystemVersion; + uint16_t minorOperatingSystemVersion; + uint16_t majorImageVersion; + uint16_t minorImageVersion; + uint16_t majorSubsystemVersion; + uint16_t minorSubsystemVersion; + uint32_t win32VersionValue; + uint32_t sizeOfImage; + uint32_t sizeOfHeaders; + uint32_t checkSum; + uint16_t subsystem; + uint16_t dllCharacteristics; + uint32_t sizeOfStackReserve; + uint32_t sizeOfStackCommit; + uint32_t sizeOfHeapReserve; + uint32_t sizeOfHeapCommit; + uint32_t loaderFlags; + uint32_t numberOfRvaAndSizes; + PEImageDataDirectory exportTable; + PEImageDataDirectory importTable; // * + PEImageDataDirectory resourceTable; // * + PEImageDataDirectory exceptionTable; + PEImageDataDirectory certificateTable; + PEImageDataDirectory baseRelocationTable; // * + PEImageDataDirectory debug; // * + PEImageDataDirectory architecture; + PEImageDataDirectory globalPtr; + PEImageDataDirectory tlsTable; + PEImageDataDirectory loadConfigTable; + PEImageDataDirectory boundImport; + PEImageDataDirectory iat; + PEImageDataDirectory delayImportDescriptor; + PEImageDataDirectory clrRuntimeHeader; + PEImageDataDirectory reserved; +}; +struct PESectionHeader { + char name[8]; + uint32_t virtualSize; + uint32_t virtualAddress; + uint32_t sizeOfRawData; + uint32_t pointerToRawData; + uint32_t pointerToRelocations; + uint32_t pointerToLinenumbers; + uint16_t numberOfRelocations; + uint16_t numberOfLinenumbers; + uint32_t characteristics; +}; +struct PEImportDirectoryEntry { + uint32_t *importLookupTable; + uint32_t timeDateStamp; + uint32_t forwarderChain; + char *name; + uint32_t *importAddressTable; +}; +struct PEHintNameTableEntry { + uint16_t hint; + char name[1]; // variable length +}; + +uint16_t read16(FILE *file) { + uint16_t v = 0; + fread(&v, 2, 1, file); + return v; +} +uint32_t read32(FILE *file) { + uint32_t v = 0; + fread(&v, 4, 1, file); + return v; +} + + +wibo::Executable::Executable() { + imageBuffer = nullptr; + imageSize = 0; +} + +wibo::Executable::~Executable() { + if (imageBuffer) { + munmap(imageBuffer, imageSize); + } +} + +bool wibo::Executable::loadPE(FILE *file) { + // Skip to PE header + fseek(file, 0x3C, SEEK_SET); + uint32_t offsetToPE = read32(file); + fseek(file, offsetToPE, SEEK_SET); + + // Read headers + PEHeader header; + fread(&header, sizeof header, 1, file); + if (memcmp(header.magic, "PE\0\0", 4) != 0) + return false; + if (header.machine != 0x14C) // i386 + return false; + + printf("Sections: %d / Size of optional header: %x\n", header.numberOfSections, header.sizeOfOptionalHeader); + + PE32Header header32; + memset(&header32, 0, sizeof header32); + fread(&header32, std::min(sizeof(header32), (size_t) header.sizeOfOptionalHeader), 1, file); + if (header32.magic != 0x10B) + return false; + + printf("Image Base: %x / Size: %x\n", header32.imageBase, header32.sizeOfImage); + + long pageSize = sysconf(_SC_PAGE_SIZE); + printf("Page size: %x\n", pageSize); + + // Build buffer + imageSize = header32.sizeOfImage; + imageBuffer = mmap((void *) header32.imageBase, header32.sizeOfImage, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0); + if (imageBuffer == MAP_FAILED) { + perror("Image mapping failed!"); + imageBuffer = 0; + return false; + } + + // Read the sections + fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET); + + for (int i = 0; i < header.numberOfSections; i++) { + PESectionHeader section; + fread(§ion, sizeof section, 1, file); + + char name[9]; + memcpy(name, section.name, 8); + name[8] = 0; + printf("Section %d: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress, section.virtualSize, section.sizeOfRawData, section.pointerToRawData); + + if (section.sizeOfRawData > 0) { + // Grab this data + long savePos = ftell(file); + fseek(file, section.pointerToRawData, SEEK_SET); + void *sectionBase = (void *) (header32.imageBase + section.virtualAddress); + fread(sectionBase, section.virtualSize, 1, file); + fseek(file, savePos, SEEK_SET); + } + } + + // Handle imports + PEImportDirectoryEntry *dir = fromRVA(header32.importTable.virtualAddress); + + while (dir->name) { + char *name = fromRVA(dir->name); + printf("DLL Name: %s\n", name); + uint32_t *lookupTable = fromRVA(dir->importLookupTable); + uint32_t *addressTable = fromRVA(dir->importAddressTable); + + while (*lookupTable) { + uint32_t lookup = *lookupTable; + if (lookup & 0x80000000) { + // Import by ordinal + uint16_t ordinal = lookup & 0xFFFF; + printf(" Ordinal: %d\n", ordinal); + *addressTable = (uint32_t) resolveStubByOrdinal(name, ordinal); + } else { + // Import by name + PEHintNameTableEntry *hintName = fromRVA(lookup); + printf(" Name: %s\n", hintName->name); + *addressTable = (uint32_t) resolveStubByName(name, hintName->name); + } + ++lookupTable; + ++addressTable; + } + + ++dir; + } + + entryPoint = fromRVA(header32.addressOfEntryPoint); + + return true; +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3bcd930 --- /dev/null +++ b/main.cpp @@ -0,0 +1,97 @@ +#include "common.h" +#include +#include +#include +#include +#include + +uint32_t wibo::lastError = 0; +char *wibo::commandLine; + +void stub() { + // should go through all the functions imported by mwcceppc.exe + // and create template stubs for them, at least... + printf("Unhandled function\n"); + exit(0); +} + +uint32_t __attribute__((stdcall)) CoInitialize(void *pvReserved) { + printf("CoInitialize(...)\n"); + return 0; // S_OK I think? +} + +void *wibo::resolveStubByName(const char *dllName, const char *funcName) { + if (strcmp(dllName, "KERNEL32.dll") == 0) { + void *func = wibo::resolveKernel32(funcName); + if (func) + return func; + } + if (strcmp(dllName, "ADVAPI32.dll") == 0) { + void *func = wibo::resolveAdvApi32(funcName); + if (func) + return func; + } + if (strcmp(dllName, "ole32.dll") == 0) { + if (strcmp(funcName, "CoInitialize") == 0) return (void *) CoInitialize; + } + + printf("Missing function: %s (%s)\n", dllName, funcName); + return (void *) stub; +} + +void *wibo::resolveStubByOrdinal(const char *dllName, uint16_t ordinal) { + return (void *) stub; +} + +// Windows Thread Information Block +struct TIB { + void *sehFrame; + void *stackBase; + void *stackLimit; + void *subSystemTib; + void *fiberData; + void *arbitraryDataSlot; + TIB *tib; +}; + +int main(int argc, char **argv) { + // Create TIB + TIB tib; + tib.tib = &tib; + + struct user_desc tibDesc; + tibDesc.entry_number = 0; + tibDesc.base_addr = (unsigned int) &tib; + tibDesc.limit = 0x1000; + tibDesc.seg_32bit = 1; + tibDesc.contents = 0; // hopefully this is ok + tibDesc.read_exec_only = 0; + tibDesc.limit_in_pages = 0; + tibDesc.seg_not_present = 0; + tibDesc.useable = 1; + if (syscall(SYS_modify_ldt, 1, &tibDesc, sizeof tibDesc) != 0) { + perror("Failed to modify LDT\n"); + return 1; + } + + // Build a command line (todo, fill this with argv etc) + wibo::commandLine = new char[1024]; + strcpy(wibo::commandLine, "mwcceppc.exe"); + + wibo::Executable exec; + + FILE *f = fopen("mwcceppc.exe", "rb"); + exec.loadPE(f); + fclose(f); + + uint16_t tibSegment = (tibDesc.entry_number << 3) | 7; + // Invoke the damn thing + asm( + "movw %0, %%fs; call *%1" + : + : "r"(tibSegment), "r"(exec.entryPoint) + ); + printf("We came back\n"); + + return 0; +}