From bd221ec9b97f5195c70aca08e7ccc14431d805cf Mon Sep 17 00:00:00 2001 From: Connor-Jay Dunn Date: Tue, 24 Sep 2024 00:36:28 +0100 Subject: [PATCH] initial commit --- LICENSE | 21 + README.md | 66 ++ screenshots/after.png | Bin 0 -> 12419 bytes screenshots/before.png | Bin 0 -> 33141 bytes src/function.cpp | 133 ++++ src/function.h | 28 + src/instruction.cpp | 80 +++ src/instruction.h | 33 + src/main.cpp | 40 ++ src/pe.cpp | 271 ++++++++ src/pe.h | 48 ++ src/util.cpp | 7 + src/util.h | 22 + src/virtual_instruction.cpp | 39 ++ src/virtual_instruction.h | 22 + src/vm.cpp | 778 ++++++++++++++++++++++ src/vm.h | 61 ++ src/vm_handler.cpp | 13 + src/vm_handler.h | 1230 +++++++++++++++++++++++++++++++++++ src/vm_section.cpp | 82 +++ src/vm_section.h | 30 + 21 files changed, 3004 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 screenshots/after.png create mode 100644 screenshots/before.png create mode 100644 src/function.cpp create mode 100644 src/function.h create mode 100644 src/instruction.cpp create mode 100644 src/instruction.h create mode 100644 src/main.cpp create mode 100644 src/pe.cpp create mode 100644 src/pe.h create mode 100644 src/util.cpp create mode 100644 src/util.h create mode 100644 src/virtual_instruction.cpp create mode 100644 src/virtual_instruction.h create mode 100644 src/vm.cpp create mode 100644 src/vm.h create mode 100644 src/vm_handler.cpp create mode 100644 src/vm_handler.h create mode 100644 src/vm_section.cpp create mode 100644 src/vm_section.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..446a7cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Connor-Jay Dunn + +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..e7f4b26 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# BinaryShield + +**BinaryShield** is an open-source, bin-to-bin x86-64 code virtualizer designed to offer strong protection against reverse engineering efforts. It translates commonly used x86-64 instructions into a custom bytecode, which is executed by a secure, purpose-built virtual machine. For more information on virtualization and the technical details of how the BinaryShield VM works, click [here](https://connorjaydunn.github.io/blog/posts/binaryshield-a-bin2bin-x86-64-code-virtualizer/). + +Features +---- +* _Bytecode encryption (soon)_ +* Multi-Thread safe VM +* _VM handler mutation (soon)_ +* Stack-Based, RISC VM +* _Multiple VM handler instances (soon)_ +* Wide range of supported opcodes +* Trivial to implement support for new opcodes +* _VM handler integrity checks (soon)_ +* Over 60+ VM handlers + +Screenshots +--- + +

+ +
+ before virtualization +

+ +

+ +
+ after virtualization +

+ +Dependencies +--- +* C++14 or higher, +* [Zydis](https://github.com/zyantific/zydis) + +Usage +---- +```bash +binaryshield.exe +``` + +Example: + +```bash +binaryshield.exe calc.exe 0x16D0 0x16E6 +``` + +TODO +---- + +* Bytecode encryption +* VM context collision check +* VM handler mutation +* VM handler integrity checks +* Multiple VM handler instances +* Anti-Debugger checks +* Add function by code markers +* Randomised VM context +* Ability to virtualize areas of code, not just functions + +Disclaimer +--- +**BinaryShield** is currently in a very early stage of development and is **not suitable for commercial use** at this time. While the core functionality is in place, there may still be bugs, incomplete features, and potential security vulnerabilities. + +I am actively working on improving and expanding the tool, and will continue to release updates regularly. Feedback and contributions are welcome. \ No newline at end of file diff --git a/screenshots/after.png b/screenshots/after.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab932f6abe09e74786d34c538ecb0752ebf76ca GIT binary patch literal 12419 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 SOuYc`=ZwYqlSL=4-~WH!2b_KY literal 0 HcmV?d00001 diff --git a/screenshots/before.png b/screenshots/before.png new file mode 100644 index 0000000000000000000000000000000000000000..d615e76342c8784b5f2e617d0c178eba9f0e9ab9 GIT binary patch literal 33141 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 dA4H0W>>{&vOo2l#eV` bytes) : startRva(startRva), endRva(endRva), bytes(bytes) {}; + +bool Function::disassemble() +{ + // initialise zydis decoder + ZydisDecoder decoder; + if (ZYAN_FAILED(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64))) + { + std::cerr << "error initializing ZydisDecoder" << std::endl; + return 0; + } + + ZydisDecodedInstruction instructionInfo; + ZydisDecodedOperand operandInfo[ZYDIS_MAX_OPERAND_COUNT]; + + // first pass: disassemble each instruction + DWORD offset = 0; + while (offset < bytes.size()) + { + if (ZYAN_FAILED(ZydisDecoderDecodeFull(&decoder, bytes.data() + offset, bytes.size(), &instructionInfo, operandInfo))) + { + std::cerr << "error decoding instruction" << std::endl; + return 0; + } + + instructions.push_back(Instruction(instructionInfo, operandInfo, offset + startRva)); + + offset += instructionInfo.length; + } + + // second pass: find branch destinations + for (int i = 0; i < instructions.size(); i++) + { + if (instructions[i].isBranchInstruction()) + { + for (int j = 0; j < instructions.size(); j++) + { + // check if instruction is destination of our branch + if (instructions[i].getRva() + + instructions[i].getOperandInfo()[0].imm.value.s + + instructions[i].getInstructionInfo().length == instructions[j].getRva()) + { + instructions[i].setDestInstructionIndex(j); + } + } + } + } + + return 1; +} + +bool Function::compileInstructionsToVirtualInstructions() +{ + // push context onto virtual stack + VM::popVmContext(&instructions[0]); + instructions[0].compileToVirtualInstructions(); + + // compile each instruction to a set of virtual instructions + for (int i = 1; i < instructions.size(); i++) + { + if (!instructions[i].compileToVirtualInstructions()) + // unable to virtualise instruciton, likely no support implemented + return 0; + } + + return 1; +} + +void Function::resolveBranchInstructions(DWORD bytecodeRva) +{ + for (int i = 0; i < instructions.size(); i++) + { + if (instructions[i].isBranchInstruction()) + { + // get bytecode rva of the destination instruction + DWORD targetBytecodeRva = getBytecodeIndex(instructions[i].getDestInstructionIndex()) + bytecodeRva; + + // set branch instructions operand to rva of destination instruction's bytecode rva + if (instructions[i].isConditionalBranchInstruction()) + { + // if conditional branch, second virtual instruction is the "push target.rva" + instructions[i].getVirtualInstructions()[1].setOperand(targetBytecodeRva); + } + else + { + // if non-conditional branch, first virtual instruction is the "push target.rva" + instructions[i].getVirtualInstructions()[0].setOperand(targetBytecodeRva); + } + } + } +} + +DWORD Function::getBytecodeIndex(int instructionIndex) +{ + DWORD bytecodeIndex = 0; + + /* + the following is a hacky solution to the fact instruction[0] will have "non-real" instructions + to deal with the vm context. A better solution will be required in order to support x86 call. + Perhaps we should implement a second vector of type VirtualInstruction that will store these + "injected" instructions. + */ + if (instructionIndex == 0) + { + // length of "injected" virtual instructions (VM::popVmContext()) + bytecodeIndex = 0x55; + } + + // calculate the bytecode index of instructionIndex via summing all previous instruction bytecode sizes + for (int i = 0; i < instructionIndex; i++) + bytecodeIndex += instructions[i].getVirtualInstructionBytes().size(); + + return bytecodeIndex; +} + +std::vector Function::getVirtualInstructionBytes() +{ + std::vector bytes; + + for (int i = 0; i < instructions.size(); i++) + { + std::vector virtualInstructionBytes = instructions[i].getVirtualInstructionBytes(); + bytes.insert(bytes.end(), virtualInstructionBytes.begin(), virtualInstructionBytes.end()); + } + + return bytes; +} + +DWORD Function::getStartRva() { return startRva; } + +DWORD Function::getEndRva() { return endRva; } \ No newline at end of file diff --git a/src/function.h b/src/function.h new file mode 100644 index 0000000..91b77e4 --- /dev/null +++ b/src/function.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +#include "instruction.h" +#include "virtual_instruction.h" + +class Function +{ +public: + Function(DWORD startRva, DWORD endRva, std::vector bytes); + + bool disassemble(); + bool compileInstructionsToVirtualInstructions(); + void resolveBranchInstructions(DWORD bytecodeRva); + DWORD getBytecodeIndex(int instructionIndex); + + std::vector getVirtualInstructionBytes(); + DWORD getStartRva(); + DWORD getEndRva(); +private: + DWORD startRva; + DWORD endRva; + std::vector bytes; + std::vector instructions; +}; \ No newline at end of file diff --git a/src/instruction.cpp b/src/instruction.cpp new file mode 100644 index 0000000..1d35275 --- /dev/null +++ b/src/instruction.cpp @@ -0,0 +1,80 @@ +#include "instruction.h" + +Instruction::Instruction(ZydisDecodedInstruction instructionInfo, ZydisDecodedOperand* operandInfo, DWORD rva) : instructionInfo(instructionInfo), rva(rva) +{ + this->operandInfo = std::vector(operandInfo, operandInfo + instructionInfo.operand_count); +} + +bool Instruction::compileToVirtualInstructions() +{ + // generate the corrosponding virtual instructions + if (!VM::compileInstructionToVirtualInstructions(this)) + { + std::cerr << "error compiling instruction" << std::endl; + return 0; + } + + return 1; +} + +void Instruction::addVirtualInstruction(VirtualInstruction virtualInstruction) { virtualInstructions.push_back(virtualInstruction); } + +bool Instruction::isBranchInstruction() +{ + switch (instructionInfo.mnemonic) + { + case ZYDIS_MNEMONIC_JNBE: + case ZYDIS_MNEMONIC_JB: + case ZYDIS_MNEMONIC_JBE: + case ZYDIS_MNEMONIC_JCXZ: + case ZYDIS_MNEMONIC_JECXZ: + case ZYDIS_MNEMONIC_JKNZD: + case ZYDIS_MNEMONIC_JKZD: + case ZYDIS_MNEMONIC_JL: + case ZYDIS_MNEMONIC_JLE: + case ZYDIS_MNEMONIC_JNB: + case ZYDIS_MNEMONIC_JNL: + case ZYDIS_MNEMONIC_JNLE: + case ZYDIS_MNEMONIC_JNO: + case ZYDIS_MNEMONIC_JNP: + case ZYDIS_MNEMONIC_JNS: + case ZYDIS_MNEMONIC_JNZ: + case ZYDIS_MNEMONIC_JO: + case ZYDIS_MNEMONIC_JP: + case ZYDIS_MNEMONIC_JRCXZ: + case ZYDIS_MNEMONIC_JS: + case ZYDIS_MNEMONIC_JZ: + case ZYDIS_MNEMONIC_JMP: + return 1; + } + return 0; +} + +// if instruction is a branch instruction, but not JMP, then it must be a conditional branch +bool Instruction::isConditionalBranchInstruction() { return (isBranchInstruction() && instructionInfo.mnemonic != ZYDIS_MNEMONIC_JMP); } + +void Instruction::setDestInstructionIndex(int destInstructionIndex) { this->destInstructionIndex = destInstructionIndex; } + +DWORD Instruction::getRva() { return rva; } + +ZydisDecodedInstruction Instruction::getInstructionInfo() { return instructionInfo; } + +std::vector Instruction::getOperandInfo() { return operandInfo; } + +int Instruction::getDestInstructionIndex() { return destInstructionIndex; } + +std::vector Instruction::getVirtualInstructionBytes() +{ + std::vector bytes; + + // iterate over each virtual instruction and get its raw bytes + for (int i = 0; i < virtualInstructions.size(); i++) + { + std::vector virtualInstructionBytes = virtualInstructions[i].getBytes(); + bytes.insert(bytes.end(), virtualInstructionBytes.begin(), virtualInstructionBytes.end()); + } + + return bytes; +} + +std::vector& Instruction::getVirtualInstructions() { return virtualInstructions; } \ No newline at end of file diff --git a/src/instruction.h b/src/instruction.h new file mode 100644 index 0000000..b7128f3 --- /dev/null +++ b/src/instruction.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +#include "vm.h" +#include "virtual_instruction.h" + +class Instruction +{ +public: + Instruction(ZydisDecodedInstruction instructionInfo, ZydisDecodedOperand* operandInfo, DWORD rva); + + bool compileToVirtualInstructions(); + void addVirtualInstruction(VirtualInstruction virtualInstruction); + bool isBranchInstruction(); + bool isConditionalBranchInstruction(); + void setDestInstructionIndex(int destInstructionIndex); + DWORD getRva(); + ZydisDecodedInstruction getInstructionInfo(); + std::vector getOperandInfo(); + int getDestInstructionIndex(); + std::vector getVirtualInstructionBytes(); + std::vector& getVirtualInstructions(); +private: + DWORD rva; + int destInstructionIndex; // index of instruction the current instruction will jump or call + ZydisDecodedInstruction instructionInfo; + std::vector operandInfo; + std::vector virtualInstructions; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..601f347 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "pe.h" + +int main(int argc, char* argv[]) +{ + if (argc != 4) + { + std::cerr << "usage: binaryshield.exe " << std::endl; + return 1; + } + + std::cout << "starting..." << std::endl; + + PE pe(argv[1]); + + if (!pe.load()) + return 1; + + pe.addFunctionByRva(std::stoi(argv[2], nullptr, 16), std::stoi(argv[3], nullptr, 16)); + + std::cout << "virtualizing function(s)..." << std::endl; + + if (!pe.virtualizeFunctions()) + return 1; + + std::cout << "success" << std::endl; + + if (!pe.addVmSection()) + return 1; + + if (!pe.save("protected.exe")) + return 1; + + std::cout << "exiting..." << std::endl; + + return 0; +} \ No newline at end of file diff --git a/src/pe.cpp b/src/pe.cpp new file mode 100644 index 0000000..9030ff0 --- /dev/null +++ b/src/pe.cpp @@ -0,0 +1,271 @@ +#include "pe.h" + +PE::PE(std::string path) : path(path) {}; + +PE::~PE() { close(); } + +bool PE::load() +{ + if (!open()) + return 0; + + if (!emitRead()) + return 0; + + close(); + + if (!parseHeaders()) + return 0; + + return 1; +} + +bool PE::save(std::string path) +{ + // create a new file for writing our protected.exe to + HANDLE hOutputFile = CreateFileA(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hOutputFile == INVALID_HANDLE_VALUE) + { + std::cerr << "error while creating output file: " << std::endl; + return 0; + } + + // write our protected.exe bytes to new file + DWORD bytesWritten = 0; + if (!WriteFile(hOutputFile, bytes.data(), bytes.size(), &bytesWritten, NULL)) + { + std::cerr << "error while writing to output file: " << std::endl; + CloseHandle(hOutputFile); + return 0; + } + + CloseHandle(hOutputFile); + return 1; +} + +void PE::addFunctionByRva(DWORD startRva, DWORD endRva) +{ + functions.push_back(Function + ( + startRva, + endRva, + std::vector(bytes.begin() + rvaToFileOffset(startRva), bytes.begin() + rvaToFileOffset(endRva)) + )); +} + +bool PE::virtualizeFunction(Function function) +{ + // todo: check if function can be vm'd (i.e. is sizeof(function->bytes) >= 5) + + if (!vmSection.isInitialised()) + vmSection.initialise(getNewSectionVirtualAddress(), getNewSectionFileOffset()); + + if (!function.disassemble()) + return 0; + + if (!function.compileInstructionsToVirtualInstructions()) + return 0; + + removeOriginalFunctionBytes(function); + + DWORD bytecodeRva = vmSection.getWritePointerRva(); + + // resolve branch instructions now we know bytecode rva + function.resolveBranchInstructions(bytecodeRva); + + vmSection.addBytes(function.getVirtualInstructionBytes()); + + // redirect function to a vm trampoline + redirectFunctionToVmTramp(function, vmSection.getWritePointerRva()); + vmSection.addVmTramp(bytecodeRva); + + return 1; +} + +bool PE::virtualizeFunctions() +{ + // iterate over each function and virtulize it + for (int i = 0; i < functions.size(); i++) + { + if (!virtualizeFunction(functions[i])) + return 0; + } + + return 1; +} + +bool PE::addVmSection() { return addSection(".binshld", 0xE0000000, vmSection.getBytes()); } + +DWORD PE::rvaToFileOffset(DWORD rva) +{ + // find section rva lies within and calculate file offset + for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) + { + if (rva >= pSectionHeader[i].VirtualAddress && + rva < pSectionHeader[i].VirtualAddress + (pSectionHeader[i].Misc.VirtualSize)) + { + return (rva - pSectionHeader[i].VirtualAddress) + pSectionHeader[i].PointerToRawData; + } + } + + return 0x0; // this should never happen, throw exception here +} + +DWORD PE::fileOffsetToRva(DWORD offset) +{ + // find section rva lies within and calculate rva + for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) + { + if (offset >= pSectionHeader[i].PointerToRawData && + offset < pSectionHeader[i].PointerToRawData + (pSectionHeader[i].SizeOfRawData)) + { + return (offset - pSectionHeader[i].PointerToRawData) + pSectionHeader[i].PointerToRawData; + } + } + + // this should never happen + return 0x0; +} + +bool PE::parseHeaders() +{ + // verify we have a pe via the dos header + pDosHeader = (PIMAGE_DOS_HEADER)bytes.data(); + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + std::cerr << "invalid dos header" << std::endl; + return 0; + } + + // verify we havea valid pe via the nt header + pNtHeader = (PIMAGE_NT_HEADERS)(bytes.data() + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) + { + std::cerr << "invalid nt header" << std::endl; + return 0; + } + + // pSectionHeader is a pointer to the start of an array of type IMAGE_SECTION_HEADER + pSectionHeader = (PIMAGE_SECTION_HEADER)(bytes.data() + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); + + return 1; +} + +bool PE::open() +{ + hFile = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + std::cerr << "error while oppening input file: " << std::endl; + return 0; + } + + return 1; +} + +void PE::close() +{ + // if already closed, ignore, else close handle to file and set hFile to nullptr + if (hFile != nullptr) + { + CloseHandle(hFile); + hFile = nullptr; + } +} + +bool PE::emitRead() +{ + // get file size + DWORD size = GetFileSize(hFile, NULL); + if (size == INVALID_FILE_SIZE) + { + std::cerr << "error while getting input file size: " << std::endl; + return 0; + } + + // resize our bytes vector to size of binary + bytes.resize(size); + + // read raw file bytes into our bytes vector + DWORD bytesRead; + if (!ReadFile(hFile, bytes.data(), size, &bytesRead, NULL)) + { + std::cerr << "error while reading input file: " << std::endl; + return 0; + } + + return 1; +} + +bool PE::addSection(std::string name, DWORD flags, std::vector bytes) +{ + // get next section's file offset + DWORD newSectionFO = getNewSectionFileOffset(); + + // create IMAGE_SECTION_HEADER for new section + PIMAGE_SECTION_HEADER pNewSectionHeader = &pSectionHeader[pNtHeader->FileHeader.NumberOfSections]; + pNewSectionHeader->Characteristics = flags; + pNewSectionHeader->Misc.VirtualSize = align(bytes.size(), pNtHeader->OptionalHeader.SectionAlignment); + pNewSectionHeader->SizeOfRawData = align(bytes.size(), pNtHeader->OptionalHeader.FileAlignment); + pNewSectionHeader->VirtualAddress = getNewSectionVirtualAddress(); + pNewSectionHeader->PointerToRawData = newSectionFO; + CopyMemory(pNewSectionHeader->Name, name.c_str(), min(name.size(), IMAGE_SIZEOF_SHORT_NAME)); + + // increase section count in the file header, and update size of image + pNtHeader->FileHeader.NumberOfSections += 1; + pNtHeader->OptionalHeader.SizeOfImage += pNewSectionHeader->Misc.VirtualSize; + + // resize our bytes vector to fit our new section bytes in + this->bytes.resize(newSectionFO + pNewSectionHeader->SizeOfRawData); + + // actually copy the section bytes into our bytes vector + std::copy(bytes.begin(), bytes.end(), this->bytes.begin() + newSectionFO); + + // parse section headers again (likely not required as we only store pointers to headers) + if (!parseHeaders()) + return 0; + + return 1; +} + +DWORD PE::getNewSectionVirtualAddress() +{ + // return the next section's "virtual address", which is actually its relative virtual address + return align + ( + pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].VirtualAddress + pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].Misc.VirtualSize, + pNtHeader->OptionalHeader.SectionAlignment + ); +} + +DWORD PE::getNewSectionFileOffset() +{ + // return the next section's file offset + return align + ( + pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].PointerToRawData + pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].SizeOfRawData, + pNtHeader->OptionalHeader.FileAlignment + ); +} + +void PE::redirectFunctionToVmTramp(Function function, DWORD vmTrampRva) +{ + std::vector relToVmTrapBytes = convertToByteVector(vmTrampRva - (function.getStartRva() + 5)); + + // replace first bytes of instruction with a redirection to the vm trampoline jump + bytes[rvaToFileOffset(function.getStartRva())] = 0xE9; + std::copy(relToVmTrapBytes.begin(), relToVmTrapBytes.end(), bytes.begin() + rvaToFileOffset(function.getStartRva() + 1)); +} + +void PE::removeOriginalFunctionBytes(Function function) +{ + // replace all the original function bytes with 0xCC + std::fill + ( + bytes.begin() + rvaToFileOffset(function.getStartRva()), + bytes.begin() + rvaToFileOffset(function.getEndRva()), + 0xCC + ); +} \ No newline at end of file diff --git a/src/pe.h b/src/pe.h new file mode 100644 index 0000000..fce614d --- /dev/null +++ b/src/pe.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +#include "vm_section.h" +#include "function.h" +#include "util.h" + +class PE +{ +public: + PE(std::string path); + ~PE(); + + bool load(); + bool save(std::string path); + + void addFunctionByRva(DWORD startRva, DWORD endRva); + bool virtualizeFunctions(); + bool addVmSection(); + + DWORD rvaToFileOffset(DWORD rva); + DWORD fileOffsetToRva(DWORD offset); +private: + std::string path; + HANDLE hFile = nullptr; + std::vector bytes; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNtHeader; + PIMAGE_SECTION_HEADER pSectionHeader; + bool parseHeaders(); + bool open(); + void close(); + bool emitRead(); + bool addSection(std::string name, DWORD flags, std::vector bytes); + DWORD getNewSectionVirtualAddress(); + DWORD getNewSectionFileOffset(); + + std::vector functions; + bool virtualizeFunction(Function function); + void redirectFunctionToVmTramp(Function function, DWORD vmTrampRva); + void removeOriginalFunctionBytes(Function function); + + VMSection vmSection; +}; \ No newline at end of file diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..e75e2fd --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,7 @@ +#include "util.h" + +DWORD align(DWORD x, DWORD alignment) { return (x + alignment - 1) & ~(alignment - 1); } + +DWORD rvaToFileOffset(DWORD rva, DWORD virtualAddress, DWORD pointerToRawData) { return (rva - virtualAddress) + pointerToRawData; } + +DWORD fileOffsetToRva(DWORD fileOffset, DWORD virtualAddress, DWORD pointerToRawData) { return (fileOffset - pointerToRawData) + virtualAddress; } \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..12addd6 --- /dev/null +++ b/src/util.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +template +std::vector convertToByteVector(T x) +{ + std::vector bytes; + size_t byteCount = sizeof(T); + + for (size_t i = 0; i < byteCount; ++i) + bytes.push_back((BYTE)((x >> (i * 8)) & 0xFF)); + + return bytes; +} + +DWORD align(DWORD x, DWORD alignment); + +DWORD rvaToFileOffset(DWORD rva, DWORD virtualAddress, DWORD pointerToRawData); + +DWORD fileOffsetToRva(DWORD fileOffset, DWORD virtualAddress, DWORD pointerToRawData); \ No newline at end of file diff --git a/src/virtual_instruction.cpp b/src/virtual_instruction.cpp new file mode 100644 index 0000000..0e84785 --- /dev/null +++ b/src/virtual_instruction.cpp @@ -0,0 +1,39 @@ +#include "virtual_instruction.h" + +VirtualInstruction::VirtualInstruction(DWORD vmHandlerRva, long long operand, BYTE operandSize) + : vmHandlerRva(vmHandlerRva), + operand(operand), + operandSize(operandSize/8), + hasOperand(true) +{}; + +VirtualInstruction::VirtualInstruction(DWORD vmHandlerRva) : vmHandlerRva(vmHandlerRva) {}; + +std::vector VirtualInstruction::getBytes() +{ + std::vector bytes; + + // convert vmHandlerRva into a BYTE vector + std::vector vmHandlerRvaBytes = convertToByteVector(vmHandlerRva); + bytes.insert(bytes.end(), vmHandlerRvaBytes.begin(), vmHandlerRvaBytes.end()); + + // if operand exists, convert it into a BYTE vector + if (hasOperand) + { + std::vector operandBytes; + + switch (operandSize) + { + case 8: operandBytes = convertToByteVector(operand); break; + case 4: operandBytes = convertToByteVector(operand); break; + case 2: operandBytes = convertToByteVector(operand); break; + case 1: operandBytes = convertToByteVector(operand); break; + } + + bytes.insert(bytes.end(), operandBytes.begin(), operandBytes.end()); + } + + return bytes; +} + +void VirtualInstruction::setOperand(long long operand) { this->operand = operand; } \ No newline at end of file diff --git a/src/virtual_instruction.h b/src/virtual_instruction.h new file mode 100644 index 0000000..1c26ea2 --- /dev/null +++ b/src/virtual_instruction.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "util.h" + +class VirtualInstruction +{ +public: + VirtualInstruction(DWORD vmHandlerRva, long long operand, BYTE operandSize); + VirtualInstruction(DWORD vmHandlerRva); + + std::vector getBytes(); + + void setOperand(long long operand); +private: + DWORD vmHandlerRva; + long long operand; + bool hasOperand = false; + BYTE operandSize; +}; \ No newline at end of file diff --git a/src/vm.cpp b/src/vm.cpp new file mode 100644 index 0000000..0150464 --- /dev/null +++ b/src/vm.cpp @@ -0,0 +1,778 @@ +#include "instruction.h" +#include "vm.h" + +namespace VM +{ + std::vector vmHandlers; + + bool compileInstructionToVirtualInstructions(Instruction* instruction) + { + switch (instruction->getInstructionInfo().mnemonic) + { + case ZYDIS_MNEMONIC_PUSH: return x86PushHandler(instruction); + case ZYDIS_MNEMONIC_POP: return x86PopHandler(instruction); + case ZYDIS_MNEMONIC_MOV: return x86MovHandler(instruction); + case ZYDIS_MNEMONIC_LEA: return x86LeaHandler(instruction); + case ZYDIS_MNEMONIC_RET: return x86RetHandler(instruction); + case ZYDIS_MNEMONIC_CMP: return x86CmpHandler(instruction); + case ZYDIS_MNEMONIC_TEST: return x86TestHandler(instruction); + case ZYDIS_MNEMONIC_JMP: return x86JmpHandler(instruction); + case ZYDIS_MNEMONIC_JNZ: return x86JneHandler(instruction); + case ZYDIS_MNEMONIC_NOP: return 1; + + case ZYDIS_MNEMONIC_ADD: + case ZYDIS_MNEMONIC_SUB: + case ZYDIS_MNEMONIC_XOR: + case ZYDIS_MNEMONIC_AND: + case ZYDIS_MNEMONIC_OR: + case ZYDIS_MNEMONIC_SHL: + return x86ArithmeticHandler(instruction, instruction->getInstructionInfo().mnemonic, true); + } + return 0; + } + + bool x86PushHandler(Instruction* instruction) + { + std::vector operandInfo = instruction->getOperandInfo(); + + switch (operandInfo[0].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + emitPushRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + { + emitPushImmediate(instruction, operandInfo[0].imm.value.s, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_MEMORY: + { + calculateEffectiveAddress(instruction, operandInfo[0].mem); + emitRead(instruction, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + + return 0; + } + + bool x86PopHandler(Instruction* instruction) + { + std::vector operandInfo = instruction->getOperandInfo(); + + switch (operandInfo[0].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: return 0; + case ZYDIS_OPERAND_TYPE_MEMORY: return 0; + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + } + + bool x86MovHandler(Instruction* instruction) + { + std::vector operandInfo = instruction->getOperandInfo(); + + switch (operandInfo[0].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + switch (operandInfo[1].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPushRegister(instruction, operandInfo[1].reg.value, operandInfo[0].size); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + { + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPushImmediate(instruction, operandInfo[1].imm.value.s, operandInfo[0].size); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_MEMORY: + { + calculateEffectiveAddress(instruction, operandInfo[1].mem); + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitRead(instruction, operandInfo[0].size); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: return 0; + case ZYDIS_OPERAND_TYPE_MEMORY: + { + calculateEffectiveAddress(instruction, operandInfo[0].mem); + emitPopRegister(instruction, R0, 64); + + switch (operandInfo[1].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + emitPushRegister(instruction, operandInfo[1].reg.value, operandInfo[0].size); + emitPushRegister(instruction, R0, 64); + emitWrite(instruction, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + { + emitPushImmediate(instruction, operandInfo[1].imm.value.s, operandInfo[0].size); + emitPushRegister(instruction, R0, 64); + emitWrite(instruction, operandInfo[0].size); + return 1; + } + case ZYDIS_OPERAND_TYPE_MEMORY: return 0; + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + } + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + + return 0; + } + + bool x86LeaHandler(Instruction* instruction) + { + std::vector operandInfo = instruction->getOperandInfo(); + + calculateEffectiveAddress(instruction, operandInfo[1].mem); + emitPopRegister(instruction, R0, 64); + emitPushRegister(instruction, R0, operandInfo[0].size); + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + + return 1; + } + + bool x86RetHandler(Instruction* instruction) + { + // add check for operand (i.e. ret 0xC) + pushVmContext(instruction); + emitExit(instruction); + return 1; + } + + bool x86CmpHandler(Instruction* instruction) { return x86ArithmeticHandler(instruction, ZYDIS_MNEMONIC_SUB, false); } + + bool x86TestHandler(Instruction* instruction) { return x86ArithmeticHandler(instruction, ZYDIS_MNEMONIC_AND, false); } + + bool x86JmpHandler(Instruction* instruction) + { + if (instruction->getOperandInfo()[0].type != ZYDIS_OPERAND_TYPE_IMMEDIATE) + return 0; + emitJmp(instruction); + return 1; + } + + bool x86JneHandler(Instruction* instruction) + { + if (instruction->getOperandInfo()[0].type != ZYDIS_OPERAND_TYPE_IMMEDIATE) + return 0; + emitPushRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + emitJne(instruction); + return 1; + } + + bool x86ArithmeticHandler(Instruction* instruction, ZydisMnemonic operation, bool storeOutput) + { + std::vector operandInfo = instruction->getOperandInfo(); + + switch (operandInfo[0].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + switch (operandInfo[1].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + emitPushRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + emitPushRegister(instruction, operandInfo[1].reg.value, operandInfo[0].size); + emitArithmetic(instruction, operation, operandInfo[0].size); + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + if (storeOutput) + { + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + } + else + { + emitPopRegister(instruction, R0, operandInfo[0].size); + } + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + emitPushRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + emitPushImmediate(instruction, operandInfo[1].imm.value.s, operandInfo[0].size); + emitArithmetic(instruction, operation, operandInfo[0].size); + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + if (storeOutput) + { + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + } + else + { + emitPopRegister(instruction, R0, operandInfo[0].size); + } + return 1; + case ZYDIS_OPERAND_TYPE_MEMORY: + { + calculateEffectiveAddress(instruction, operandInfo[1].mem); + emitRead(instruction, operandInfo[0].size); + emitPushRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + emitArithmetic(instruction, operation, operandInfo[0].size); + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + + if (storeOutput) + { + if (operandInfo[0].size == 32) + zeroRegister(instruction, operandInfo[0].reg.value); + emitPopRegister(instruction, operandInfo[0].reg.value, operandInfo[0].size); + } + else + { + emitPopRegister(instruction, R0, operandInfo[0].size); + } + return 1; + } + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + return 0; + case ZYDIS_OPERAND_TYPE_MEMORY: + { + calculateEffectiveAddress(instruction, operandInfo[0].mem); + emitPushRegister(instruction, ZYDIS_REGISTER_RSP, 64); + emitRead(instruction, 64); + emitPopRegister(instruction, R0, 64); + + switch (operandInfo[1].type) + { + case ZYDIS_OPERAND_TYPE_REGISTER: + { + emitRead(instruction, operandInfo[0].size); + emitPushRegister(instruction, operandInfo[1].reg.value, operandInfo[0].size); + emitArithmetic(instruction, operation, operandInfo[0].size); + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + if (storeOutput) + { + emitPushRegister(instruction, R0, 64); + emitWrite(instruction, operandInfo[0].size); + } + else + { + emitPopRegister(instruction, R0, operandInfo[0].size); + } + return 1; + } + case ZYDIS_OPERAND_TYPE_IMMEDIATE: + { + emitRead(instruction, operandInfo[0].size); + emitPushImmediate(instruction, operandInfo[1].imm.value.s, operandInfo[0].size); + emitArithmetic(instruction, operation, operandInfo[0].size); + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + if (storeOutput) + { + emitPushRegister(instruction, R0, 64); + emitWrite(instruction, operandInfo[0].size); + } + else + { + emitPopRegister(instruction, R0, operandInfo[0].size); + } + return 1; + } + case ZYDIS_OPERAND_TYPE_MEMORY: return 0; + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + } + case ZYDIS_OPERAND_TYPE_POINTER: return 0; + case ZYDIS_OPERAND_TYPE_UNUSED: return 0; + } + + return 0; + } + + BYTE getVirtualRegisterIndex(ZydisRegister reg) + { + switch (reg) + { + case ZYDIS_REGISTER_RFLAGS: + return 0; + + case ZYDIS_REGISTER_RAX: + case ZYDIS_REGISTER_EAX: + case ZYDIS_REGISTER_AX: + case ZYDIS_REGISTER_AL: + return 1; + + case ZYDIS_REGISTER_RBX: + case ZYDIS_REGISTER_EBX: + case ZYDIS_REGISTER_BX: + case ZYDIS_REGISTER_BL: + return 2; + + case ZYDIS_REGISTER_RCX: + case ZYDIS_REGISTER_ECX: + case ZYDIS_REGISTER_CX: + case ZYDIS_REGISTER_CL: + return 3; + + case ZYDIS_REGISTER_RDX: + case ZYDIS_REGISTER_EDX: + case ZYDIS_REGISTER_DX: + case ZYDIS_REGISTER_DL: + return 4; + + case ZYDIS_REGISTER_RSI: + case ZYDIS_REGISTER_ESI: + case ZYDIS_REGISTER_SI: + case ZYDIS_REGISTER_SIL: + return 5; + + case ZYDIS_REGISTER_RDI: + case ZYDIS_REGISTER_EDI: + case ZYDIS_REGISTER_DI: + case ZYDIS_REGISTER_DIL: + return 6; + + case ZYDIS_REGISTER_RBP: + case ZYDIS_REGISTER_EBP: + case ZYDIS_REGISTER_BP: + case ZYDIS_REGISTER_BPL: + return 7; + + case ZYDIS_REGISTER_R8: + case ZYDIS_REGISTER_R8D: + case ZYDIS_REGISTER_R8W: + case ZYDIS_REGISTER_R8B: + return 8; + + case ZYDIS_REGISTER_R9: + case ZYDIS_REGISTER_R9D: + case ZYDIS_REGISTER_R9W: + case ZYDIS_REGISTER_R9B: + return 9; + + case ZYDIS_REGISTER_R10: + case ZYDIS_REGISTER_R10D: + case ZYDIS_REGISTER_R10W: + case ZYDIS_REGISTER_R10B: + return 10; + + case ZYDIS_REGISTER_R11: + case ZYDIS_REGISTER_R11D: + case ZYDIS_REGISTER_R11W: + case ZYDIS_REGISTER_R11B: + return 11; + + case ZYDIS_REGISTER_R12: + case ZYDIS_REGISTER_R12D: + case ZYDIS_REGISTER_R12W: + case ZYDIS_REGISTER_R12B: + return 12; + + case ZYDIS_REGISTER_R13: + case ZYDIS_REGISTER_R13D: + case ZYDIS_REGISTER_R13W: + case ZYDIS_REGISTER_R13B: + return 13; + + case ZYDIS_REGISTER_R14: + case ZYDIS_REGISTER_R14D: + case ZYDIS_REGISTER_R14W: + case ZYDIS_REGISTER_R14B: + return 14; + + case ZYDIS_REGISTER_R15: + case ZYDIS_REGISTER_R15D: + case ZYDIS_REGISTER_R15W: + case ZYDIS_REGISTER_R15B: + return 15; + + default: + return -1; // something went wrong + } + } + + BYTE getVirtualRegisterIndex(VIRTUAL_REGISTERS reg) + { + switch (reg) + { + case R0: return 16; + case R1: return 17; + + default: return -1; // something went wrong + } + } + + DWORD getVmHandlerRva(VMHandlerTypes type) + { + for (int i = 0; i < vmHandlers.size(); i++) + { + if (vmHandlers[i].getType() == type) + { + return vmHandlers[i].getRva(); + } + } + + return -1; // this will never happen so long as all vm handlers were created (maybe throw an exception here to tell the user where they went wrong) + } + + void popVmContext(Instruction* instruction) + { + emitPopRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RAX, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RBX, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RCX, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RDX, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RSI, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RDI, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_RBP, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R8, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R9, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R10, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R11, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R12, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R13, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R14, 64); + emitPopRegister(instruction, ZYDIS_REGISTER_R15, 64); + emitPopRegister(instruction, R0, 64); + } + + void pushVmContext(Instruction* instruction) + { + emitPushRegister(instruction, ZYDIS_REGISTER_R15, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R14, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R13, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R12, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R11, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R10, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R9, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_R8, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RBP, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RDI, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RSI, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RDX, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RCX, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RBX, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RAX, 64); + emitPushRegister(instruction, ZYDIS_REGISTER_RFLAGS, 64); + } + + template + void emitPushRegister(Instruction* instruction, T reg, BYTE size) + { + switch (reg) + { + case ZYDIS_REGISTER_RSP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHRSP64))); return; + case ZYDIS_REGISTER_ESP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHRSP32))); return; + case ZYDIS_REGISTER_SP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHRSP16))); return; + case ZYDIS_REGISTER_SPL: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHRSP8))); return; + } + + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHR64), getVirtualRegisterIndex(reg), 8)); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHR32), getVirtualRegisterIndex(reg), 8)); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHR16), getVirtualRegisterIndex(reg), 8)); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHR8), getVirtualRegisterIndex(reg), 8)); return; + } + } + + template + void emitPopRegister(Instruction* instruction, T reg, BYTE size) + { + switch (reg) + { + case ZYDIS_REGISTER_RSP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPRSP64))); return; + case ZYDIS_REGISTER_ESP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPRSP32))); return; + case ZYDIS_REGISTER_SP: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPRSP16))); return; + case ZYDIS_REGISTER_SPL: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPRSP8))); return; + } + + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPR64), getVirtualRegisterIndex(reg), 8)); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPR32), getVirtualRegisterIndex(reg), 8)); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPR16), getVirtualRegisterIndex(reg), 8)); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(POPR8), getVirtualRegisterIndex(reg), 8)); return; + } + } + + void emitPushImmediate(Instruction* instruction, long long immediate, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHI64), immediate, 64)); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHI32), immediate, 32)); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHI16), immediate, 16)); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(PUSHI8), immediate, 8)); return; + } + } + + void emitRead(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(READ64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(READ32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(READ16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(READ8))); return; + } + } + + void emitWrite(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(WRITE64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(WRITE32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(WRITE16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(WRITE8))); return; + } + } + + void emitJmp(Instruction* instruction) { instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(JMP), 0x0, 32)); } + + void emitJne(Instruction* instruction) { instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(JNE), 0x0, 32)); } + + void emitExit(Instruction* instruction) { instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(EXIT))); } + + void emitAdd(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(ADD64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(ADD32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(ADD16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(ADD8))); return; + } + } + + void emitSub(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SUB64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SUB32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SUB16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SUB8))); return; + } + } + + void emitXor(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(XOR64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(XOR32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(XOR16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(XOR8))); return; + } + } + + void emitAnd(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(AND64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(AND32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(AND16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(AND8))); return; + } + } + + void emitOr(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(OR64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(OR32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(OR16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(OR8))); return; + } + } + + void emitShl(Instruction* instruction, BYTE size) + { + switch (size) + { + case 64: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SHL64))); return; + case 32: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SHL32))); return; + case 16: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SHL16))); return; + case 8: instruction->addVirtualInstruction(VirtualInstruction(getVmHandlerRva(SHL8))); return; + } + } + + void emitArithmetic(Instruction* instruction, ZydisMnemonic operation, BYTE size) + { + switch (operation) + { + case ZYDIS_MNEMONIC_ADD: emitAdd(instruction, size); return; + case ZYDIS_MNEMONIC_SUB: emitSub(instruction, size); return; + case ZYDIS_MNEMONIC_XOR: emitXor(instruction, size); return; + case ZYDIS_MNEMONIC_AND: emitAnd(instruction, size); return; + case ZYDIS_MNEMONIC_OR: emitOr(instruction, size); return; + case ZYDIS_MNEMONIC_SHL: emitShl(instruction, size); return; + } + } + + void zeroRegister(Instruction* instruction, ZydisRegister reg) + { + emitPushImmediate(instruction, 0x0, 64); + emitPopRegister(instruction, reg, 64); + } + + void calculateEffectiveAddress(Instruction* instruction, ZydisDecodedOperandMem mem) + { + ZydisRegister base = mem.base; + ZydisRegister index = mem.index; + BYTE scale = mem.scale; + bool hasDisplacement = mem.disp.has_displacement; + long long displacement = mem.disp.value; + + bool is64Bits = (base != ZYDIS_REGISTER_NONE && ZydisRegisterGetWidth(ZYDIS_MACHINE_MODE_LONG_64, base) == 64) || + (index != ZYDIS_REGISTER_NONE && ZydisRegisterGetWidth(ZYDIS_MACHINE_MODE_LONG_64, index) == 64); + + if (base == ZYDIS_REGISTER_RIP) + { + emitPushImmediate(instruction, instruction->getRva() + instruction->getInstructionInfo().length + displacement, 64); + emitPushRegister(instruction, R1, 64); + emitArithmetic(instruction, ZYDIS_MNEMONIC_ADD, 64); + emitPopRegister(instruction, R0, 64); + return; + } + + if (base != ZYDIS_REGISTER_NONE) + { + if (base == ZYDIS_REGISTER_RSP) + { + emitPushRegister(instruction, ZYDIS_REGISTER_RSP, 64); + } + else + { + if (is64Bits) + { + emitPushRegister(instruction, base, 64); + } + else + { + emitPushRegister(instruction, base, 32); + } + } + } + else + { + if (is64Bits) + { + emitPushImmediate(instruction, 0x0, 64); + } + else + { + emitPushImmediate(instruction, 0x0, 32); + } + } + + if (index != ZYDIS_REGISTER_NONE) + { + if (is64Bits) + { + emitPushRegister(instruction, index, 64); + } + else + { + emitPushRegister(instruction, index, 32); + } + + if (scale > 1) + { + emitPushImmediate(instruction, std::log2(scale), 64); + if (is64Bits) + { + emitArithmetic(instruction, ZYDIS_MNEMONIC_SHL, 64); + } + else + { + emitArithmetic(instruction, ZYDIS_MNEMONIC_SHL, 32); + } + emitPopRegister(instruction, R0, 64); + } + } + else + { + if (is64Bits) + { + emitPushImmediate(instruction, 0x0, 64); + } + else + { + emitPushImmediate(instruction, 0x0, 32); + } + } + + if (hasDisplacement) + { + if (is64Bits) + { + emitPushImmediate(instruction, displacement, 64); + } + else + { + emitPushImmediate(instruction, displacement, 32); + } + } + else + { + if (is64Bits) + { + emitPushImmediate(instruction, 0x0, 64); + } + else + { + emitPushImmediate(instruction, 0x0, 32); + } + } + + if (is64Bits) + { + emitArithmetic(instruction, ZYDIS_MNEMONIC_ADD, 64); + emitPopRegister(instruction, R0, 64); + emitArithmetic(instruction, ZYDIS_MNEMONIC_ADD, 64); + emitPopRegister(instruction, R0, 64); + } + else + { + emitArithmetic(instruction, ZYDIS_MNEMONIC_ADD, 32); + emitPopRegister(instruction, R0, 32); + emitArithmetic(instruction, ZYDIS_MNEMONIC_ADD, 32); + emitPopRegister(instruction, R0, 32); + } + } +} \ No newline at end of file diff --git a/src/vm.h b/src/vm.h new file mode 100644 index 0000000..3ed832d --- /dev/null +++ b/src/vm.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include "vm_handler.h" +#include "virtual_instruction.h" + +class Instruction; + +namespace VM +{ + enum VIRTUAL_REGISTERS + { + R0, // general purpose register + R1, // module base + }; + + extern std::vector vmHandlers; + + bool compileInstructionToVirtualInstructions(Instruction* instruction); + + bool x86PushHandler(Instruction* instruction); + bool x86PopHandler(Instruction* instruction); + bool x86MovHandler(Instruction* instruction); + bool x86LeaHandler(Instruction* instruction); + bool x86RetHandler(Instruction* instruction); + bool x86CmpHandler(Instruction* instruction); + bool x86TestHandler(Instruction* instruction); + bool x86JmpHandler(Instruction* instruction); + bool x86JneHandler(Instruction* instruction); + bool x86ArithmeticHandler(Instruction* instruction, ZydisMnemonic operation, bool storeOutput); + + BYTE getVirtualRegisterIndex(ZydisRegister reg); + BYTE getVirtualRegisterIndex(VIRTUAL_REGISTERS reg); + DWORD getVmHandlerRva(VMHandlerTypes type); + + void popVmContext(Instruction* instruction); + void pushVmContext(Instruction* instruction); + + template + void emitPushRegister(Instruction* instruction, T reg, BYTE size); + template + void emitPopRegister(Instruction* instruction, T reg, BYTE size); + void emitPushImmediate(Instruction* instruction, long long immediate, BYTE size); + void emitRead(Instruction* instruction, BYTE size); + void emitWrite(Instruction* instruction, BYTE size); + void emitJmp(Instruction* instruction); + void emitJne(Instruction* instruction); + void emitExit(Instruction* instruction); + void emitAdd(Instruction* instruction, BYTE size); + void emitSub(Instruction* instruction, BYTE size); + void emitXor(Instruction* instruction, BYTE size); + void emitAnd(Instruction* instruction, BYTE size); + void emitOr(Instruction* instruction, BYTE size); + void emitShl(Instruction* instruction, BYTE size); + void emitArithmetic(Instruction* instruction, ZydisMnemonic operation, BYTE size); + void zeroRegister(Instruction* instruction, ZydisRegister reg); + void calculateEffectiveAddress(Instruction* instruction, ZydisDecodedOperandMem mem); +} \ No newline at end of file diff --git a/src/vm_handler.cpp b/src/vm_handler.cpp new file mode 100644 index 0000000..f36f819 --- /dev/null +++ b/src/vm_handler.cpp @@ -0,0 +1,13 @@ +#include "vm_handler.h" + +VMHandlerTypes VMHandler::getType() { return type; } + +DWORD VMHandler::getRva() { return rva; } + +DWORD VMHandler::getFileOffset() { return fileOffset; } + +std::vector VMHandler::getBytes() { return bytes; } + +void VMHandler::setRva(DWORD rva) { this->rva = rva; } + +void VMHandler::setFileOffset(DWORD fileOffset) { this->fileOffset = fileOffset; } \ No newline at end of file diff --git a/src/vm_handler.h b/src/vm_handler.h new file mode 100644 index 0000000..ece0402 --- /dev/null +++ b/src/vm_handler.h @@ -0,0 +1,1230 @@ +#pragma once + +#include +#include + +#define JMP_TO_NEXT_HANDLER \ + 0x41, 0x8B, 0x45, 0x00, /* mov eax, dword ptr [r13] */ \ + 0x49, 0x83, 0xC5, 0x04, /* add r13, 0x04 */ \ + 0x4C, 0x01, 0xF0, /* add rax, r14 */ \ + 0xFF, 0xE0, /* jmp rax */ + +/* + +afaik, stack collision check is only required for +handlers that push values onto stack, or modify the vsp. They +shouldn't be required on other handlers since they operate +within a safe range (I think). + +*/ + +#define CONTEXT_COLLISION_CHECK \ + 0x90, + + +enum VMHandlerTypes +{ + ENTER, EXIT, + + PUSHI64, PUSHI32, PUSHI16, PUSHI8, + + PUSHR64, PUSHR32, PUSHR16, PUSHR8, + + PUSHRSP64, PUSHRSP32, PUSHRSP16, PUSHRSP8, + + POPRSP64, POPRSP32, POPRSP16, POPRSP8, + + POPR64, POPR32, POPR16, POPR8, + + READ64, READ32, READ16, READ8, + + WRITE64, WRITE32, WRITE16, WRITE8, + + ADD64, ADD32, ADD16, ADD8, + + SUB64, SUB32, SUB16, SUB8, + + XOR64, XOR32, XOR16, XOR8, + + AND64, AND32, AND16, AND8, + + OR64, OR32, OR16, OR8, + + NAND64, NAND32, NAND16, NAND8, + + NOR64, NOR32, NOR16, NOR8, + + SHL64, SHL32, SHL16, SHL8, + + JMP, + + JNE, +}; + +class VMHandler +{ +public: + VMHandlerTypes getType(); + DWORD getRva(); + DWORD getFileOffset(); + std::vector getBytes(); + + void setRva(DWORD rva); + void setFileOffset(DWORD fileOffset); +protected: + VMHandlerTypes type; + DWORD rva; + DWORD fileOffset; + std::vector bytes; +}; + +class Enter : public VMHandler +{ +public: + Enter() + { + type = ENTER; + bytes = + { + 0x41, 0x57, // push r15 + 0x41, 0x56, // push r14 + 0x41, 0x55, // push r13 + 0x41, 0x54, // push r12 + 0x41, 0x53, // push r11 + 0x41, 0x52, // push r10 + 0x41, 0x51, // push r9 + 0x41, 0x50, // push r8 + 0x55, // push rbp + 0x57, // push rdi + 0x56, // push rsi + 0x52, // push rdx + 0x51, // push rcx + 0x53, // push rbx + 0x50, // push rax + 0x9C, // pushfq + 0x49, 0x89, 0xE7, // mov r15, rsp + 0x48, 0x81, 0xEC, 0x0, 0x1, 0x0, 0x0, // sub rsp, 0x100 + 0x65, 0x4C, 0x8B, 0x34, 0x25, 0x60, 0x0, 0x0, 0x0, // mov r14, qword ptr gs:[0x60] + 0x4D, 0x8B, 0x76, 0x10, // mov r14, qword ptr [r13+0x10] + 0x4C, 0x89, 0xB4, 0x24, 0x88, 0x00, 0x00, 0x00, // mov qword ptr [rsp+0x88], r14 + 0x45, 0x8B, 0xAF, 0x80, 0x0, 0x0, 0x0, // mov r13d, dword ptr [r15+0x80] + 0x4D, 0x01, 0xF5, // add r13, r14 + JMP_TO_NEXT_HANDLER + }; + }; +private: + +}; +class Exit : public VMHandler +{ +public: + Exit() + { + type = EXIT; + bytes = + { + 0x4C, 0x89, 0xFC, // mov rsp, r15 + 0x9D, // popfq + 0x58, // pop rax + 0x5B, // pop rbx + 0x59, // pop rcx + 0x5A, // pop rdx + 0x5E, // pop rsi + 0x5F, // pop rdi + 0x5D, // pop rbp + 0x41, 0x58, // pop r8 + 0x41, 0x59, // pop r9 + 0x41, 0x5A, // pop r10 + 0x41, 0x5B, // pop r11 + 0x41, 0x5C, // pop r12 + 0x41, 0x5D, // pop r13 + 0x41, 0x5E, // pop r14 + 0x41, 0x5F, // pop r15 + 0xC3, // ret + }; + }; +private: + +}; + +class PushR64 : public VMHandler +{ +public: + PushR64() + { + type = PUSHR64; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x48, 0x8B, 0x04, 0xC4, // mov rax, qword ptr [rsp+rax*0x08] + 0x49, 0x83, 0xEF, 0x08, // sub r15, 0x08 + 0x49, 0x89, 0x07, // mov qword ptr [r15], rax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushR32 : public VMHandler +{ +public: + PushR32() + { + type = PUSHR32; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x8B, 0x04, 0xC4, // mov eax, dword ptr [rsp+rax*0x08] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x89, 0x07, // mov dword ptr [r15], eax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushR16 : public VMHandler +{ +public: + PushR16() + { + type = PUSHR16; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x66, 0x8B, 0x04, 0xC4, // mov ax, word ptr [rsp+rax*0x08] + 0x49, 0x83, 0xEF, 0x02, // sub r15, 0x02 + 0x66, 0x41, 0x89, 0x07, // mov word ptr [r15], ax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushR8 : public VMHandler +{ +public: + PushR8() + { + type = PUSHR8; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x8A, 0x04, 0xC4, // mov al, byte ptr [rsp+rax*0x08] + 0x49, 0x83, 0xEF, 0x01, // sub r15, 0x01 + 0x41, 0x88, 0x07, // mov byte ptr [r15], al + JMP_TO_NEXT_HANDLER + }; + } +}; + +class PushRSP64 : public VMHandler +{ +public: + PushRSP64() + { + type = PUSHRSP64; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x4C, 0x89, 0xF8, // mov rax, r15 + 0x49, 0x83, 0xEF, 0x08, // sub r15, 0x08 + 0x49, 0x89, 0x07, // mov qword ptr [r15], rax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushRSP32 : public VMHandler +{ +public: + PushRSP32() + { + type = PUSHRSP32; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x44, 0x89, 0xF8, // mov eax, r15d + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x89, 0x07, // mov dword ptr [r15], eax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushRSP16 : public VMHandler +{ +public: + PushRSP16() + { + type = PUSHRSP16; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x66, 0x44, 0x89, 0xF8, // mov ax, r15w + 0x49, 0x83, 0xEF, 0x02, // sub r15, 0x02 + 0x66, 0x41, 0x89, 0x07, // mov word ptr [r15], ax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushRSP8 : public VMHandler +{ +public: + PushRSP8() + { + type = PUSHRSP8; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x44, 0x88, 0xF8, // mov al, r15b + 0x49, 0x83, 0xEF, 0x01, // sub r15, 0x01 + 0x41, 0x88, 0x07, // mov byte ptr [r15], al + JMP_TO_NEXT_HANDLER + }; + } +}; + +class PopRSP64 : public VMHandler +{ +public: + PopRSP64() + { + type = POPRSP64; + bytes = + { + 0x4D, 0x8B, 0x3F, // mov r15, qword ptr [r15] + CONTEXT_COLLISION_CHECK + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopRSP32 : public VMHandler +{ +public: + PopRSP32() + { + type = POPRSP32; + bytes = + { + 0x45, 0x8B, 0x3F, // mov r15d, dword ptr [r15] + CONTEXT_COLLISION_CHECK + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopRSP16 : public VMHandler +{ +public: + PopRSP16() + { + type = POPRSP16; + bytes = + { + 0x66, 0x45, 0x8B, 0x3F, // mov r15w, word ptr [r15] + CONTEXT_COLLISION_CHECK + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopRSP8 : public VMHandler +{ +public: + PopRSP8() + { + type = POPRSP8; + bytes = + { + 0x45, 0x8A, 0x3F, // mov r15b, byte ptr [r15] + CONTEXT_COLLISION_CHECK + JMP_TO_NEXT_HANDLER + }; + } +}; + +class PopR64 : public VMHandler +{ +public: + PopR64() + { + type = POPR64; + bytes = + { + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x49, 0x8B, 0x1F, // mov rbx, qword ptr [r15] + 0x49, 0x83, 0xC7, 0x08, // add r15, 0x08 + 0x48, 0x89, 0x1C, 0xC4, // mov qword ptr [rsp+rax*8], rbx + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopR32 : public VMHandler +{ +public: + PopR32() + { + type = POPR32; + bytes = + { + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x41, 0x8B, 0x1F, // mov ebx, dword ptr [r15] + 0x49, 0x83, 0xC7, 0x04, // add r15, 0x04 + 0x89, 0x1C, 0xC4, // mov dword ptr [rsp+rax*8], ebx + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopR16 : public VMHandler +{ +public: + PopR16() + { + type = POPR16; + bytes = + { + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x66, 0x41, 0x8B, 0x1F, // mov bx, word ptr [r15] + 0x49, 0x83, 0xC7, 0x02, // add r15, 0x02 + 0x66, 0x89, 0x1C, 0xC4, // mov word ptr [rsp+rax*8], bx + JMP_TO_NEXT_HANDLER + }; + } +}; +class PopR8 : public VMHandler +{ +public: + PopR8() + { + type = POPR8; + bytes = + { + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, // and rax, 0xFF + 0x49, 0xFF, 0xC5, // inc r13 + 0x41, 0x8A, 0x1F, // mov bl, byte ptr [r15] + 0x49, 0x83, 0xC7, 0x01, // add r15, 0x01 + 0x88, 0x1C, 0xC4, // mov byte ptr [rsp+rax*8], bl + JMP_TO_NEXT_HANDLER + }; + } +}; + +class PushI64 : public VMHandler +{ +public: + PushI64() + { + type = PUSHI64; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x49, 0x8B, 0x45, 0x00, // mov rax, qword ptr [r13] + 0x49, 0x83, 0xC5, 0x08, // add r13, 0x08 + 0x49, 0x83, 0xEF, 0x08, // sub r15, 0x08 + 0x49, 0x89, 0x07, // mov qword ptr [r15], rax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushI32 : public VMHandler +{ +public: + PushI32() + { + type = PUSHI32; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x41, 0x8B, 0x45, 0x00, // mov eax, dword ptr [r13] + 0x49, 0x83, 0xC5, 0x04, // add r13, 0x04 + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x89, 0x07, // mov dword ptr [r15], eax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushI16 : public VMHandler +{ +public: + PushI16() + { + type = PUSHI16; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x66, 0x41, 0x8B, 0x45, 0x00, // mov ax, word ptr [r13] + 0x49, 0x83, 0xC5, 0x02, // add r13, 0x02 + 0x49, 0x83, 0xEF, 0x02, // sub r15, 0x02 + 0x66, 0x41, 0x89, 0x07, // mov word ptr [r15], ax + JMP_TO_NEXT_HANDLER + }; + } +}; +class PushI8 : public VMHandler +{ +public: + PushI8() + { + type = PUSHI8; + bytes = + { + CONTEXT_COLLISION_CHECK + 0x41, 0x8A, 0x45, 0x00, // mov al, byte ptr [r13] + 0x49, 0x83, 0xC5, 0x01, // add r13, 0x01 + 0x49, 0x83, 0xEF, 0x01, // sub r15, 0x01 + 0x41, 0x88, 0x07, // mov byte ptr [r15], al + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Read64 : public VMHandler +{ +public: + Read64() + { + type = READ64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x48, 0x8B, 0x00, // mov rax, qword ptr [rax] + 0x49, 0x89, 0x07, // mov qword ptr [r15], rax + JMP_TO_NEXT_HANDLER + }; + } +}; +class Read32 : public VMHandler +{ +public: + Read32() + { + type = READ32; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x8B, 0x00, // mov eax, dword ptr [rax] + 0x49, 0x83, 0xC7, 0x04, // add r15, 0x04 + 0x41, 0x89, 0x07, // mov dword ptr [r15], eax + JMP_TO_NEXT_HANDLER + }; + } +}; +class Read16 : public VMHandler +{ +public: + Read16() + { + type = READ16; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x66, 0x8B, 0x00, // mov ax, word ptr [rax] + 0x49, 0x83, 0xC7, 0x06, // add r15, 0x06 + 0x66, 0x41, 0x89, 0x07, // mov word ptr [r15], ax + JMP_TO_NEXT_HANDLER + }; + } +}; +class Read8 : public VMHandler +{ +public: + Read8() + { + type = READ8; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x8A, 0x00, // mov al, byte ptr [rax] + 0x49, 0x83, 0xC7, 0x07, // add r15, 0x07 + 0x41, 0x88, 0x07, // mov byte ptr [r15], al + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Write64 : public VMHandler +{ +public: + Write64() + { + type = WRITE64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x8B, 0x5F, 0x08, // mov rbx, qword ptr [r15+0x08] + 0x49, 0x83, 0xC7, 0x10, // add r15, 0x10 + 0x48, 0x89, 0x18, // mov qword ptr [rax], rbx + JMP_TO_NEXT_HANDLER + }; + } +}; +class Write32 : public VMHandler +{ +public: + Write32() + { + type = WRITE32; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x41, 0x8B, 0x5F, 0x08, // mov ebx, dword ptr [r15+0x08] + 0x49, 0x83, 0xC7, 0x0C, // add r15, 0x0C + 0x89, 0x18, // mov dword ptr [rax], ebx + JMP_TO_NEXT_HANDLER + }; + } +}; +class Write16 : public VMHandler +{ +public: + Write16() + { + type = WRITE16; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x66, 0x41, 0x8B, 0x5F, 0x08, // mov bx, word ptr [r15+0x08] + 0x49, 0x83, 0xC7, 0x0A, // add r15, 0x0A + 0x66, 0x89, 0x18, // mov word ptr [rax], bx + JMP_TO_NEXT_HANDLER + }; + } +}; +class Write8 : public VMHandler +{ +public: + Write8() + { + type = WRITE8; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x41, 0x8A, 0x5F, 0x08, // mov bl, byte ptr [r15+0x08] + 0x49, 0x83, 0xC7, 0x09, // add r15, 0x9 + 0x88, 0x18, // mov byte ptr [rax], bl + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Add64 : public VMHandler +{ +public: + Add64() + { + type = ADD64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x01, 0x47, 0x08, // add qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Add32 : public VMHandler +{ +public: + Add32() + { + type = ADD32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x01, 0x47, 0x08, // add dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Add16 : public VMHandler +{ +public: + Add16() + { + type = ADD16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x41, 0x01, 0x47, 0x08, // add word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Add8 : public VMHandler +{ +public: + Add8() + { + type = ADD8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x41, 0x00, 0x47, 0x08, // add byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Sub64 : public VMHandler +{ +public: + Sub64() + { + type = SUB64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x29, 0x47, 0x08, // sub qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Sub32 : public VMHandler +{ +public: + Sub32() + { + type = SUB32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x29, 0x47, 0x08, // sub dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Sub16 : public VMHandler +{ +public: + Sub16() + { + type = SUB16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x41, 0x29, 0x47, 0x08, // sub word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Sub8 : public VMHandler +{ +public: + Sub8() + { + type = SUB8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x41, 0x28, 0x47, 0x08, // sub byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Xor64 : public VMHandler +{ +public: + Xor64() + { + type = XOR64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x31, 0x47, 0x08, // xor qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Xor32 : public VMHandler +{ +public: + Xor32() + { + type = XOR32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x31, 0x47, 0x08, // xor dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Xor16 : public VMHandler +{ +public: + Xor16() + { + type = XOR16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x41, 0x31, 0x47, 0x08, // xor word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Xor8 : public VMHandler +{ +public: + Xor8() + { + type = XOR8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x41, 0x30, 0x47, 0x08, // xor byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class And64 : public VMHandler +{ +public: + And64() + { + type = AND64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x21, 0x47, 0x08, // and qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class And32 : public VMHandler +{ +public: + And32() + { + type = AND32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x21, 0x47, 0x08, // and dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class And16 : public VMHandler +{ +public: + And16() + { + type = AND16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x41, 0x21, 0x47, 0x08, // and word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class And8 : public VMHandler +{ +public: + And8() + { + type = AND8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x41, 0x20, 0x47, 0x08, // and byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Or64 : public VMHandler +{ +public: + Or64() + { + type = OR64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x09, 0x47, 0x08, // or qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Or32 : public VMHandler +{ +public: + Or32() + { + type = OR32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x41, 0x09, 0x47, 0x08, // or dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Or16 : public VMHandler +{ +public: + Or16() + { + type = OR16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x41, 0x09, 0x47, 0x08, // or word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Or8 : public VMHandler +{ +public: + Or8() + { + type = OR8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x41, 0x08, 0x47, 0x08, // or byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Nand64 : public VMHandler +{ +public: + Nand64() + { + type = NAND64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x8B, 0x5F, 0x08, // mov rbx, qword ptr [r15+0x08] + 0x48, 0x21, 0xD8, // not rax, rbx + 0x48, 0xF7, 0xD0, // not rax + 0x49, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nand32 : public VMHandler +{ +public: + Nand32() + { + type = NAND32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x41, 0x8B, 0x5F, 0x04, // mov ebx, dword ptr [r15+0x04] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x21, 0xD8, // and eax, ebx + 0xF7, 0xD0, // not eax + 0x41, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nand16 : public VMHandler +{ +public: + Nand16() + { + type = NAND16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x66, 0x41, 0x8B, 0x5F, 0x02, // mov bx, word ptr [r15+0x02] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x21, 0xD8, // and ax, bx + 0x66, 0xF7, 0xD0, // not ax + 0x66, 0x41, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nand8 : public VMHandler +{ +public: + Nand8() + { + type = NAND8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, word ptr [r15] + 0x41, 0x8A, 0x5F, 0x01, // mov bl, word ptr [r15+0x01] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x20, 0xD8, // and al, bl + 0xF6, 0xD0, // not al + 0x41, 0x88, 0x47, 0x08, // mov qword ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Nor64 : public VMHandler +{ +public: + Nor64() + { + type = NOR64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x49, 0x8B, 0x5F, 0x08, // mov rbx, qword ptr [r15+0x08] + 0x48, 0x09, 0xD8, // or rax, rbx + 0x48, 0xF7, 0xD0, // not rax + 0x49, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nor32 : public VMHandler +{ +public: + Nor32() + { + type = NOR32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x41, 0x8B, 0x5F, 0x04, // mov ebx, dword ptr [r15+0x04] + 0x49, 0x83, 0xEF, 0x04, // sub r15, 0x04 + 0x09, 0xD8, // or eax, ebx + 0xF7, 0xD0, // not eax + 0x41, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nor16 : public VMHandler +{ +public: + Nor16() + { + type = NOR16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x66, 0x41, 0x8B, 0x5F, 0x02, // mov bx, word ptr [r15+0x02] + 0x49, 0x83, 0xEF, 0x06, // sub r15, 0x06 + 0x66, 0x09, 0xD8, // or ax, bx + 0x66, 0xF7, 0xD0, // not ax + 0x66, 0x41, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Nor8 : public VMHandler +{ +public: + Nor8() + { + type = NOR8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, word ptr [r15] + 0x41, 0x8A, 0x5F, 0x01, // mov bl, word ptr [r15+0x01] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x08, 0xD8, // or al, bl + 0xF6, 0xD0, // not al + 0x41, 0x88, 0x47, 0x08, // mov qword ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Shl64 : public VMHandler +{ +public: + Shl64() + { + type = SHL64; + bytes = + { + 0x49, 0x8B, 0x07, // mov rax, qword ptr [r15] + 0x41, 0x8A, 0x4F, 0x08, // mov cl, byte ptr [r15+0x08] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x48, 0xD3, 0xE0, // shl rax, cl + 0x49, 0x89, 0x47, 0x08, // mov qword ptr [r15+0x08], rax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Shl32 : public VMHandler +{ +public: + Shl32() + { + type = SHL32; + bytes = + { + 0x41, 0x8B, 0x07, // mov eax, dword ptr [r15] + 0x41, 0x8A, 0x4F, 0x04, // mov cl, byte ptr [r15+0x04] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0xD3, 0xE0, // shl eax, cl + 0x41, 0x89, 0x47, 0x08, // mov dword ptr [r15+0x08], eax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Shl16 : public VMHandler +{ +public: + Shl16() + { + type = SHL16; + bytes = + { + 0x66, 0x41, 0x8B, 0x07, // mov ax, word ptr [r15] + 0x41, 0x8A, 0x4F, 0x02, // mov cl, byte ptr [r15+0x02] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0x66, 0xD3, 0xE0, // shl ax, cl + 0x66, 0x41, 0x89, 0x47, 0x08, // mov word ptr [r15+0x08], ax + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; +class Shl8 : public VMHandler +{ +public: + Shl8() + { + type = SHL8; + bytes = + { + 0x41, 0x8A, 0x07, // mov al, byte ptr [r15] + 0x41, 0x8A, 0x4F, 0x01, // mov cl, byte ptr [r15+0x01] + 0x49, 0x83, 0xEF, 0x07, // sub r15, 0x07 + 0xD2, 0xE0, // shl al, cl + 0x41, 0x88, 0x47, 0x08, // mov byte ptr [r15+0x08], al + 0x9C, // pushfq + 0x41, 0x8F, 0x07, // pop qword ptr [r15] + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Jmp : public VMHandler +{ +public: + Jmp() + { + type = JMP; + bytes = + { + 0x45, 0x8B, 0x6D, 0x00, // mov r13d, dword ptr [r13] + 0x4D, 0x01, 0xF5, // add r13, r14 + JMP_TO_NEXT_HANDLER + }; + } +}; + +class Jne : public VMHandler +{ +public: + Jne() + { + type = JNE; + bytes = + { + 0x41, 0x8B, 0x45, 0x00, // mov eax, dword ptr [r13] + 0x49, 0x83, 0xC5, 0x04, // add r13, 0x4 + 0x4C, 0x01, 0xF0, // add rax, r14 + 0x41, 0xFF, 0x37, // push qword ptr [r15] + 0x9D, // popfq + 0x4D, 0x8D, 0x7F, 0x08, // lea r15, qword ptr [r15+0x08] + 0x4C, 0x0F, 0x45, 0xE8, // cmovne r13, rax + JMP_TO_NEXT_HANDLER + }; + } +}; \ No newline at end of file diff --git a/src/vm_section.cpp b/src/vm_section.cpp new file mode 100644 index 0000000..14fd1d8 --- /dev/null +++ b/src/vm_section.cpp @@ -0,0 +1,82 @@ +#include "vm_section.h" + +void VMSection::initialise(DWORD virtualAddress, DWORD pointerToRawData) +{ + this->virtualAddress = virtualAddress; + this->pointerToRawData = pointerToRawData; + addVmHandlers(); + initialised = true; +} + +bool VMSection::isInitialised() { return initialised; } + +DWORD VMSection::getWritePointer() { return writePointer; } + +DWORD VMSection::getWritePointerFileOffset() { return writePointer + pointerToRawData; } + +DWORD VMSection::getWritePointerRva() { return fileOffsetToRva(getWritePointerFileOffset(), virtualAddress, pointerToRawData); } + +std::vector VMSection::getBytes() { return bytes; } + +void VMSection::addVmTramp(DWORD bytecodeRva) +{ + addBytes({ 0x68 }); addBytes(convertToByteVector(bytecodeRva)); // push bytecodeRva + + DWORD relToEnterHandler = VM::getVmHandlerRva(ENTER) - fileOffsetToRva(writePointer + pointerToRawData + 5, virtualAddress, pointerToRawData); + + if (relToEnterHandler <= 127 && relToEnterHandler >= -128) + { + addBytes({ 0xEB, (BYTE)(relToEnterHandler & 0xFF) }); // jmp short vmenter + } + else + { + addBytes({ 0xE9 }); addBytes(convertToByteVector(relToEnterHandler)); // jmp far vmenter + } +} + +void VMSection::addBytes(std::vector bytes) +{ + this->bytes.insert(this->bytes.begin() + writePointer, bytes.begin(), bytes.end()); + writePointer += bytes.size(); +} + +void VMSection::addVmHandlers() +{ + // there is likely an elegant way of doing this + + VMHandler* vmHandlers[] = + { + new Enter(), new Exit(), + new PushR64(), new PushR32(), new PushR16(), new PushR8(), + new PopR64(), new PopR32(), new PopR16(), new PopR8(), + new PushI64(), new PushI32(), new PushI16(), new PushI8(), + new PushRSP64(), new PushRSP32(), new PushRSP16(), new PushRSP8(), + new PopRSP64(), new PopRSP32(), new PopRSP16(), new PopRSP8(), + new Add64(), new Add32(), new Add16(), new Add8(), + new Sub64(), new Sub32(), new Sub16(), new Sub8(), + new Xor64(), new Xor32(), new Xor16(), new Xor8(), + new And64(), new And32(), new And16(), new And8(), + new Or64(), new Or32(), new Or16(), new Or8(), + new Nand64(), new Nand32(), new Nand16(), new Nand8(), + new Nor64(), new Nor32(), new Nor16(), new Nor8(), + new Shl64(), new Shl32(), new Shl16(), new Shl8(), + new Read64(), new Read32(), new Read16(), new Read8(), + new Write64(), new Write32(), new Write16(), new Write8(), + new Jmp(), + new Jne(), + }; + + for (int i = 0; i < std::size(vmHandlers); i++) + addVmHandler(*vmHandlers[i]); + + for (int i = 0; i < std::size(vmHandlers); i++) + delete vmHandlers[i]; +} + +void VMSection::addVmHandler(VMHandler vmHandler) +{ + vmHandler.setFileOffset(writePointer + pointerToRawData); + vmHandler.setRva(fileOffsetToRva(writePointer + pointerToRawData, virtualAddress, pointerToRawData)); + VM::vmHandlers.push_back(vmHandler); + addBytes(vmHandler.getBytes()); +} \ No newline at end of file diff --git a/src/vm_section.h b/src/vm_section.h new file mode 100644 index 0000000..69a8ef6 --- /dev/null +++ b/src/vm_section.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "vm.h" +#include "vm_handler.h" +#include "util.h" + +class VMSection +{ +public: + void initialise(DWORD virtualAddress, DWORD pointerToRawData); + bool isInitialised(); + DWORD getWritePointer(); + DWORD getWritePointerFileOffset(); + DWORD getWritePointerRva(); + std::vector getBytes(); + void addVmTramp(DWORD bytecodeRva); + void addBytes(std::vector bytes); +private: + bool initialised = false; + DWORD pointerToRawData; + DWORD virtualAddress; + DWORD writePointer; + std::vector bytes; + + void addVmHandlers(); + void addVmHandler(VMHandler vmHandler); +}; \ No newline at end of file