Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
McSimp committed Nov 1, 2020
0 parents commit 5473458
Show file tree
Hide file tree
Showing 14 changed files with 842 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
.vscode
*.dll
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "pe-parse"]
path = pe-parse
url = https://github.com/trailofbits/pe-parse.git
17 changes: 17 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.12)
project(linoodle)
enable_language(ASM)
find_package(Threads REQUIRED)

# Build PE parser library
option(BUILD_SHARED_LIBS "" OFF)
add_subdirectory(pe-parse)
set(BUILD_SHARED_LIBS ON)

# Build linoodle
add_library(linoodle linoodle.cpp windows_api.cpp windows_library.cpp)
target_link_libraries(linoodle PRIVATE pe-parse Threads::Threads)

# Build tester
add_executable(testlinoodle test/test.cpp)
target_link_libraries(testlinoodle PRIVATE Threads::Threads ${CMAKE_DL_LIBS})
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 Will Donohoe

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.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# linoodle

A Linux wrapper for the [Oodle Data Compression](http://www.radgametools.com/oodlecompressors.htm) library.

It essentially works like this:

1. Parse the Windows Oodle DLL in the current directory (`oo2core_8_win64.dll`) using the `pe-parse` library
2. Map the PE into memory
3. Perform relocations
4. Resolve imports to point to our own implementation of the minimum set of required Windows API functions
5. Setup the `gs` register to point to a fake `TIB` structure
6. Execute the DLL's entry point
7. Execute whatever Oodle exports you want (currently only `OodleLZ_Decompress`)

## Usage

When you clone this repository, make sure you use `--recurse-submodules` to get the `pe-parse` submodule.
Otherwise you can run `git submodule update --init` after you've cloned it.

After that, just use cmake to build the project and you'll get a `liblinoodle.so` file created in the `build`
directory. You can link to this or use `dlopen`, then call the `OodleLZ_Decompress` export.

```
mkdir build
cd build
cmake ..
cd ..
cmake --build build/
```

This will also build an executable which tests that the library works. Run it with `./build/testlinoodle`.
29 changes: 29 additions & 0 deletions linoodle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

#include "windows_library.h"

typedef __attribute__((ms_abi)) size_t(*tDecompressFunc)(uint8_t* srcBuf, size_t srcLen, uint8_t* dstBuf, size_t dstLen, int64_t unk1, int64_t unk2, int64_t unk3, int64_t unk4, int64_t unk5, int64_t unk6, int64_t unk7, int64_t unk8, int64_t unk9, int64_t unk10);

class OodleWrapper {
public:
OodleWrapper() :
m_oodleLib(WindowsLibrary::Load("oo2core_8_win64.dll"))
{
m_decompressFunc = reinterpret_cast<tDecompressFunc>(m_oodleLib.GetExport("OodleLZ_Decompress"));
}

size_t Decompress(uint8_t* srcBuf, size_t srcLen, uint8_t* dstBuf, size_t dstLen, int64_t unk1, int64_t unk2, int64_t unk3, int64_t unk4, int64_t unk5, int64_t unk6, int64_t unk7, int64_t unk8, int64_t unk9, int64_t unk10)
{
WindowsLibrary::SetupCall();
return m_decompressFunc(srcBuf, srcLen, dstBuf, dstLen, unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8, unk9, unk10);
}

private:
WindowsLibrary m_oodleLib;
tDecompressFunc m_decompressFunc;
};

OodleWrapper g_oodleWrapper;
extern "C" size_t OodleLZ_Decompress(uint8_t * srcBuf, size_t srcLen, uint8_t * dstBuf, size_t dstLen, int64_t unk1, int64_t unk2, int64_t unk3, int64_t unk4, int64_t unk5, int64_t unk6, int64_t unk7, int64_t unk8, int64_t unk9, int64_t unk10)
{
return g_oodleWrapper.Decompress(srcBuf, srcLen, dstBuf, dstLen, unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8, unk9, unk10);
}
1 change: 1 addition & 0 deletions pe-parse
Submodule pe-parse added at 76e3d4
Binary file added test/compressed.data
Binary file not shown.
Binary file added test/decompressed.data
Binary file not shown.
84 changes: 84 additions & 0 deletions test/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <fstream>
#include <vector>
#include <iostream>
#include <string.h>
#include <dlfcn.h>
#include <pthread.h>

typedef size_t(*tDecompressFunc)(uint8_t* srcBuf, size_t srcLen, uint8_t* dstBuf, size_t dstLen, int64_t unk1, int64_t unk2, int64_t unk3, int64_t unk4, int64_t unk5, int64_t unk6, int64_t unk7, int64_t unk8, int64_t unk9, int64_t unk10);

std::vector<char> ReadAllData(const char* path)
{
std::ifstream file(path, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (!file.read(buffer.data(), size)) {
throw std::runtime_error("Failed to open file");
}
return buffer;
}

void* TestFunc(void* arg)
{
for (int i = 0; i < 2000; i++)
{
// Load compiled library
void* lib = dlopen("build/liblinoodle.so", RTLD_LAZY);
if (lib == nullptr) {
std::cout << "dlopen failed: " << dlerror() << std::endl;
return nullptr;
}
auto decompress = reinterpret_cast<tDecompressFunc>(dlsym(lib, "OodleLZ_Decompress"));

// Load test data
auto compressedData = ReadAllData("test/compressed.data");
auto decompressedData = ReadAllData("test/decompressed.data");

for (int j = 0; j < 10; j++)
{
// Decompress the data
uint8_t* destBuf = reinterpret_cast<uint8_t*>(malloc(decompressedData.size() + 4096));
size_t result = decompress(reinterpret_cast<uint8_t*>(compressedData.data()), compressedData.size(), destBuf, decompressedData.size(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

// Ensure it's what we expect
if (result != decompressedData.size()) {
std::cout << "Resulting size from OodleLZ_Decompress does not match (expected " << decompressedData.size() << ", got " << result << ")" << std::endl;
return nullptr;
}

if (memcmp(destBuf, decompressedData.data(), decompressedData.size()) != 0) {
std::cout << "Decompressed data does not match" << std::endl;
return nullptr;
}

free(destBuf);
}

// Unload the library
if (dlclose(lib) != 0) {
std::cout << "Failed to dlclose library" << std::endl;
return nullptr;
}
}

return nullptr;
}

int main()
{
// Spin up multiple threads to run the test
const int NUM_THREADS = 8;
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], nullptr, TestFunc, nullptr);
}

// Wait for the threads to finish
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], nullptr);
}

std::cout << "Test succeeded!" << std::endl;
return 0;
}
Loading

0 comments on commit 5473458

Please sign in to comment.