mirror of
https://github.com/connorjaydunn/BinaryShield
synced 2026-06-06 15:24:33 +00:00
initial commit
This commit is contained in:
@@ -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.
|
||||
@@ -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 |
@@ -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; }
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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);
|
||||
@@ -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; }
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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());
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
Reference in New Issue
Block a user