Created
November 1, 2022 19:13
-
-
Save AltimorTASDK/48d198c10033830c0aca1ef7a8e542de to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <cstdint> | |
#include <fstream> | |
#include <iostream> | |
#include <memory> | |
#include <stdexcept> | |
#include <Windows.h> | |
char *rva_to_pointer(char *data, uint32_t rva) | |
{ | |
const auto &dos = *(IMAGE_DOS_HEADER*)data; | |
const auto &pe = *(IMAGE_NT_HEADERS64*)&data[dos.e_lfanew]; | |
// After the end of _IMAGE_OPTIONAL_HEADER | |
const auto &opt = pe.OptionalHeader; | |
const auto *sections = (IMAGE_SECTION_HEADER*)&opt.DataDirectory[opt.NumberOfRvaAndSizes]; | |
for (auto i = 0; i < pe.FileHeader.NumberOfSections; i++) { | |
const auto start = sections[i].VirtualAddress; | |
const auto end = start + sections[i].Misc.VirtualSize; | |
if (rva >= start && rva < end) | |
return &data[rva - start + sections[i].PointerToRawData]; | |
} | |
throw std::out_of_range("RVA not in section bounds"); | |
} | |
uint64_t xtea_next() | |
{ | |
static constexpr uint32_t key[] = { 0x6DEAC50E, 0x5703A8A5, 0x3A2D6F68, 0xFF8E988E }; | |
static constinit uint32_t v0 = 0x1AE1DA57; | |
static constinit uint32_t v1 = 0x66808190; | |
uint32_t sum = 0; | |
for (auto i = 0; i < 64; i++) { | |
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); | |
sum += 0x9E3779B9; | |
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); | |
} | |
return ((uint64_t)v1 << 32) | v0; | |
} | |
uint32_t decode_7bit(const char **stream) | |
{ | |
char byte; | |
uint32_t shift = 0; | |
uint32_t value = 0; | |
do { | |
byte = *(*stream)++; | |
value += (uint32_t)(byte & 0x7F) << shift; | |
shift += 7; | |
} while (byte & 0x80); | |
return value; | |
} | |
void unpack(char *data, const char *rva_stream) | |
{ | |
uint32_t rva = 0; | |
uint32_t size = 0; | |
while (true) { | |
const auto xor_key = xtea_next(); | |
for (auto shift = 0; shift < 64; shift += 8) { | |
while (size == 0) { | |
rva += decode_7bit(&rva_stream); | |
if (rva == (uint32_t)-1) | |
return; | |
size = decode_7bit(&rva_stream); | |
} | |
*rva_to_pointer(data, rva++) ^= xor_key >> shift; | |
size--; | |
} | |
} | |
} | |
int main(int argc, const char *argv[]) | |
{ | |
if (argc < 3) { | |
std::cerr << "Usage: unpacker <cod.exe> <unpacked.exe>\n"; | |
return 1; | |
} | |
const auto *in_path = argv[1]; | |
const auto *out_path = argv[2]; | |
auto in_file = std::ifstream(in_path, std::ios::binary | std::ios::ate); | |
if (!in_file.is_open()) { | |
std::cerr << "Failed to open \"" << in_path << "\"\n"; | |
return 1; | |
} | |
const auto length = in_file.tellg(); | |
const auto data = std::make_unique<char[]>(length); | |
in_file.seekg(0); | |
in_file.read(data.get(), length); | |
in_file.close(); | |
auto out_file = std::ofstream(out_path, std::ios::binary); | |
if (!out_file.is_open()) { | |
std::cerr << "Failed to open \"" << out_path << "\"\n"; | |
return 1; | |
} | |
static constexpr char rva_pattern[] = "\x80\x20\xBB\xC1\x01\x05\xBB\xC1"; | |
const auto *rva_stream = std::search(data.get(), data.get() + length, | |
rva_pattern, rva_pattern + sizeof(rva_pattern) - 1); | |
if (rva_stream == data.get() + length) { | |
std::cerr << "Failed to find RVA stream\n"; | |
return 1; | |
} | |
unpack(data.get(), rva_stream); | |
out_file.write(data.get(), length); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment