mirror of https://github.com/decompals/wibo.git
first commit
This commit is contained in:
commit
d1a4fd35a7
|
@ -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.
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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 <typename T>
|
||||||
|
T *fromRVA(uint32_t rva) {
|
||||||
|
return (T *) (rva + (uint8_t *) imageBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T *fromRVA(T *rva) {
|
||||||
|
return fromRVA<T>((uint32_t) rva);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,322 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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<PEImportDirectoryEntry>(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<PEHintNameTableEntry>(lookup);
|
||||||
|
printf(" Name: %s\n", hintName->name);
|
||||||
|
*addressTable = (uint32_t) resolveStubByName(name, hintName->name);
|
||||||
|
}
|
||||||
|
++lookupTable;
|
||||||
|
++addressTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
++dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoint = fromRVA<void>(header32.addressOfEntryPoint);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include <asm/ldt.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue