initial commit

This commit is contained in:
Connor-Jay Dunn
2024-09-24 00:36:28 +01:00
commit bd221ec9b9
21 changed files with 3004 additions and 0 deletions
+21
View File
@@ -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.
+66
View File
@@ -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
---
<p align="center">
<img src="https://github.com/connorjaydunn/BinaryShield/blob/main/screenshots/before.png"/>
<br>
before virtualization
</p>
<p align="center">
<img src="https://github.com/connorjaydunn/BinaryShield/blob/main/screenshots/after.png"/>
<br>
after virtualization
</p>
Dependencies
---
* C++14 or higher,
* [Zydis](https://github.com/zyantific/zydis)
Usage
----
```bash
binaryshield.exe <target binary path> <start-rva> <end-rva>
```
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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+133
View File
@@ -0,0 +1,133 @@
#include "function.h"
Function::Function(DWORD startRva, DWORD endRva, std::vector<BYTE> 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<BYTE> Function::getVirtualInstructionBytes()
{
std::vector<BYTE> bytes;
for (int i = 0; i < instructions.size(); i++)
{
std::vector<BYTE> virtualInstructionBytes = instructions[i].getVirtualInstructionBytes();
bytes.insert(bytes.end(), virtualInstructionBytes.begin(), virtualInstructionBytes.end());
}
return bytes;
}
DWORD Function::getStartRva() { return startRva; }
DWORD Function::getEndRva() { return endRva; }
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <iostream>
#include <windows.h>
#include <vector>
#include "instruction.h"
#include "virtual_instruction.h"
class Function
{
public:
Function(DWORD startRva, DWORD endRva, std::vector<BYTE> bytes);
bool disassemble();
bool compileInstructionsToVirtualInstructions();
void resolveBranchInstructions(DWORD bytecodeRva);
DWORD getBytecodeIndex(int instructionIndex);
std::vector<BYTE> getVirtualInstructionBytes();
DWORD getStartRva();
DWORD getEndRva();
private:
DWORD startRva;
DWORD endRva;
std::vector<BYTE> bytes;
std::vector<Instruction> instructions;
};
+80
View File
@@ -0,0 +1,80 @@
#include "instruction.h"
Instruction::Instruction(ZydisDecodedInstruction instructionInfo, ZydisDecodedOperand* operandInfo, DWORD rva) : instructionInfo(instructionInfo), rva(rva)
{
this->operandInfo = std::vector<ZydisDecodedOperand>(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<ZydisDecodedOperand> Instruction::getOperandInfo() { return operandInfo; }
int Instruction::getDestInstructionIndex() { return destInstructionIndex; }
std::vector<BYTE> Instruction::getVirtualInstructionBytes()
{
std::vector<BYTE> bytes;
// iterate over each virtual instruction and get its raw bytes
for (int i = 0; i < virtualInstructions.size(); i++)
{
std::vector<BYTE> virtualInstructionBytes = virtualInstructions[i].getBytes();
bytes.insert(bytes.end(), virtualInstructionBytes.begin(), virtualInstructionBytes.end());
}
return bytes;
}
std::vector<VirtualInstruction>& Instruction::getVirtualInstructions() { return virtualInstructions; }
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <iostream>
#include <windows.h>
#include <vector>
#include <Zydis/Zydis.h>
#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<ZydisDecodedOperand> getOperandInfo();
int getDestInstructionIndex();
std::vector<BYTE> getVirtualInstructionBytes();
std::vector<VirtualInstruction>& getVirtualInstructions();
private:
DWORD rva;
int destInstructionIndex; // index of instruction the current instruction will jump or call
ZydisDecodedInstruction instructionInfo;
std::vector<ZydisDecodedOperand> operandInfo;
std::vector<VirtualInstruction> virtualInstructions;
};
+40
View File
@@ -0,0 +1,40 @@
#include <iostream>
#include <cstdlib>
#include <string>
#include "pe.h"
int main(int argc, char* argv[])
{
if (argc != 4)
{
std::cerr << "usage: binaryshield.exe <target binary path> <start-rva> <end-rva>" << 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;
}
+271
View File
@@ -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<BYTE>(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<BYTE> 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<BYTE> relToVmTrapBytes = convertToByteVector<DWORD>(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
);
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <iostream>
#include <windows.h>
#include <string>
#include <vector>
#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<BYTE> 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<BYTE> bytes);
DWORD getNewSectionVirtualAddress();
DWORD getNewSectionFileOffset();
std::vector<Function> functions;
bool virtualizeFunction(Function function);
void redirectFunctionToVmTramp(Function function, DWORD vmTrampRva);
void removeOriginalFunctionBytes(Function function);
VMSection vmSection;
};
+7
View File
@@ -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; }
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include <vector>
template <typename T>
std::vector<BYTE> convertToByteVector(T x)
{
std::vector<BYTE> 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);
+39
View File
@@ -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<BYTE> VirtualInstruction::getBytes()
{
std::vector<BYTE> bytes;
// convert vmHandlerRva into a BYTE vector
std::vector<BYTE> vmHandlerRvaBytes = convertToByteVector<DWORD>(vmHandlerRva);
bytes.insert(bytes.end(), vmHandlerRvaBytes.begin(), vmHandlerRvaBytes.end());
// if operand exists, convert it into a BYTE vector
if (hasOperand)
{
std::vector<BYTE> operandBytes;
switch (operandSize)
{
case 8: operandBytes = convertToByteVector<long long>(operand); break;
case 4: operandBytes = convertToByteVector<DWORD>(operand); break;
case 2: operandBytes = convertToByteVector<WORD>(operand); break;
case 1: operandBytes = convertToByteVector<BYTE>(operand); break;
}
bytes.insert(bytes.end(), operandBytes.begin(), operandBytes.end());
}
return bytes;
}
void VirtualInstruction::setOperand(long long operand) { this->operand = operand; }
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include <vector>
#include "util.h"
class VirtualInstruction
{
public:
VirtualInstruction(DWORD vmHandlerRva, long long operand, BYTE operandSize);
VirtualInstruction(DWORD vmHandlerRva);
std::vector<BYTE> getBytes();
void setOperand(long long operand);
private:
DWORD vmHandlerRva;
long long operand;
bool hasOperand = false;
BYTE operandSize;
};
+778
View File
@@ -0,0 +1,778 @@
#include "instruction.h"
#include "vm.h"
namespace VM
{
std::vector<VMHandler> 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<ZydisDecodedOperand> 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<ZydisDecodedOperand> 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<ZydisDecodedOperand> 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<ZydisDecodedOperand> 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<ZydisDecodedOperand> 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 <typename T>
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 <typename T>
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);
}
}
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <windows.h>
#include <vector>
#include <Zydis/Zydis.h>
#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<VMHandler> 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 <typename T>
void emitPushRegister(Instruction* instruction, T reg, BYTE size);
template <typename T>
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);
}
+13
View File
@@ -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<BYTE> VMHandler::getBytes() { return bytes; }
void VMHandler::setRva(DWORD rva) { this->rva = rva; }
void VMHandler::setFileOffset(DWORD fileOffset) { this->fileOffset = fileOffset; }
+1230
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -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<BYTE> VMSection::getBytes() { return bytes; }
void VMSection::addVmTramp(DWORD bytecodeRva)
{
addBytes({ 0x68 }); addBytes(convertToByteVector<DWORD>(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<DWORD>(relToEnterHandler)); // jmp far vmenter
}
}
void VMSection::addBytes(std::vector<BYTE> 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());
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <windows.h>
#include <vector>
#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<BYTE> getBytes();
void addVmTramp(DWORD bytecodeRva);
void addBytes(std::vector<BYTE> bytes);
private:
bool initialised = false;
DWORD pointerToRawData;
DWORD virtualAddress;
DWORD writePointer;
std::vector<BYTE> bytes;
void addVmHandlers();
void addVmHandler(VMHandler vmHandler);
};