Skip to content

Commit

Permalink
Make X64Assembler subclass X86
Browse files Browse the repository at this point in the history
  • Loading branch information
altalk23 committed Aug 5, 2023
1 parent d79ba3a commit 29d7d9a
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 103 deletions.
137 changes: 48 additions & 89 deletions src/assembler/X64Assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,157 +7,116 @@ uint8_t regv(X64Register reg) {
}

uint8_t regv(X64Pointer ptr) {
return regv(ptr.m_register);
return regv(ptr.reg);
}

uint8_t regl(X64Register reg) {
return regv(reg) & 0x7;
}

uint8_t regl(X64Pointer ptr) {
return regv(ptr) & 0x7;
uint8_t lowerv(X64Register reg, uint8_t offset) {
return (regv(reg) < 0x8) << offset;
}

uint8_t regx(X64Register reg) {
return regv(reg) - 0x80;
uint8_t lowerv(X64Pointer ptr, uint8_t offset) {
return (regv(ptr) < 0x8) << offset;
}

bool espcheck(X64Pointer ptr) {
return regl(ptr) == 0x4;
void rex(X64Assembler* ass, X64Register reg, X64Register reg2, bool wide) {
auto rexv = 0x40 | lowerv(reg, 0) | lowerv(reg2, 2) | (wide << 3);
if (rexv != 0x40) {
ass->write8(rexv);
}
}

bool lowerreg(X64Register reg) {
return regv(reg) < 0x8;
void rex(X64Assembler* ass, X64Pointer ptr, X64Register reg, bool wide) {
auto rexv = 0x40 | lowerv(ptr, 0) | lowerv(reg, 2) | (wide << 3);
if (rexv != 0x40) {
ass->write8(rexv);
}
}

bool lowerreg(X64Pointer ptr) {
return regv(ptr) < 0x8;
X86Register x86reg(X64Register reg) {
return static_cast<X86Register>(regv(reg));
}

uint8_t lowerv(X64Register reg, uint8_t offset) {
return lowerreg(reg) << offset;
}

uint8_t lowerv(X64Pointer ptr, uint8_t offset) {
return lowerreg(ptr) << offset;
X86Pointer x86ptr(X64Pointer ptr) {
return {x86reg(ptr.reg), ptr.offset};
}

X64Assembler::X64Assembler(uint64_t baseAddress) :
BaseAssembler(baseAddress) {}
X86Assembler(baseAddress) {}

X64Assembler::~X64Assembler() {}

void X64Assembler::label32(std::string const& name) {
m_labelUpdates.push_back({this->currentAddress(), name, 4});
this->write32(0);
}

void X64Assembler::updateLabels() {
for (auto const& update : m_labelUpdates) {
this->rewrite32(update.m_address, m_labels[update.m_name] - update.m_address - 4);
}
}

using enum X64Register;

void X64Assembler::nop() {
this->write8(0x90);
X86Assembler::nop();
}

void X64Assembler::add(X64Register reg, uint32_t value) {
this->write8(0x48 | lowerv(reg, 0));
this->write8(0x81);
this->write8(0xC0 | regl(reg));
this->write32(value);
rex(this, reg, RAX, true);
X86Assembler::add(x86reg(reg), value);
}

void X64Assembler::sub(X64Register reg, uint32_t value) {
this->write8(0x48 | lowerv(reg, 0));
this->write8(0x81);
this->write8(0xE8 | regl(reg));
this->write32(value);
rex(this, reg, RAX, true);
X86Assembler::sub(x86reg(reg), value);
}

void X64Assembler::jmp(X64Register reg) {
this->write8(0x40 | lowerv(reg, 0));
this->write8(0xFF);
this->write8(0xE0 | regl(reg));
rex(this, reg, RAX, false);
X86Assembler::jmp(x86reg(reg));
}

void X64Assembler::jmp(uint64_t address) {
this->write8(0xE9);
this->write32(address - this->currentAddress() - 4);
X86Assembler::jmp(address);
}

void X64Assembler::call(X64Register reg) {
this->write8(0x40 | lowerv(reg, 0));
this->write8(0xFF);
this->write8(0xD0 | regl(reg));
rex(this, reg, RAX, false);
X86Assembler::call(x86reg(reg));
}

void X64Assembler::lea(X64Register reg, std::string const& label) {
this->write8(0x48 | lowerv(reg, 2));
this->write8(0x8D);
this->write8(0x05 | regl(reg) * 8);
this->label32(label);
rex(this, RAX, reg, true);
X86Assembler::lea(x86reg(reg), label);
}

void X64Assembler::movaps(X64Register reg, X64Pointer ptr) {
this->write8(0x40 | lowerv(ptr, 0));
this->write8(0x0F);
this->write8(0x28);
this->write8(0x80 | regl(ptr) | regx(reg) * 8);
if (espcheck(ptr)) {
this->write8(0x24);
}
this->write32(ptr.m_offset);
rex(this, ptr, RAX, false);
X86Assembler::movaps(x86reg(reg), x86ptr(ptr));
}

void X64Assembler::movaps(X64Pointer ptr, X64Register reg) {
this->write8(0x40 | lowerv(ptr, 0));
this->write8(0x0F);
this->write8(0x29);
this->write8(0x80 | regl(ptr) | regx(reg) * 8);
if (espcheck(ptr)) {
this->write8(0x24);
}
this->write32(ptr.m_offset);
rex(this, ptr, RAX, false);
X86Assembler::movaps(x86ptr(ptr), x86reg(reg));
}

void X64Assembler::mov(X64Register reg, uint32_t value) {
this->write8(0x48 | lowerv(reg, 0));
this->write8(0xC7);
this->write8(0xC0 | regl(reg));
this->write32(value);
rex(this, reg, RAX, true);
X86Assembler::mov(x86reg(reg), value);
}

void X64Assembler::mov(X64Register reg, X64Pointer ptr) {
this->write8(0x48 | lowerv(ptr, 0) | lowerv(reg, 2));
this->write8(0x8B);
this->write8(0x80 | regl(ptr) | regl(reg) * 8);
if (espcheck(ptr)) {
this->write8(0x24);
}
this->write32(ptr.m_offset);
rex(this, ptr, reg, true);
X86Assembler::mov(x86reg(reg), x86ptr(ptr));
}

void X64Assembler::mov(X64Pointer ptr, X64Register reg) {
this->write8(0x48 | lowerv(ptr, 0) | lowerv(reg, 2));
this->write8(0x89);
this->write8(0x80 | regl(ptr) | regl(reg) * 8);
if (espcheck(ptr)) {
this->write8(0x24);
}
this->write32(ptr.m_offset);
rex(this, ptr, reg, true);
X86Assembler::mov(x86ptr(ptr), x86reg(reg));
}

void X64Assembler::mov(X64Register reg, X64Register reg2) {
this->write8(0x48 | lowerv(reg, 0) | lowerv(reg2, 2));
this->write8(0x89);
this->write8(0xC0 | regl(reg) | regl(reg2) * 8);
rex(this, reg, reg2, true);
X86Assembler::mov(x86reg(reg), x86reg(reg2));
}

void X64Assembler::mov(X64Register reg, std::string const& label) {
this->write8(0x48 | lowerv(reg, 2));
this->write8(0x8B);
this->write8(0x05 | regl(reg) * 8);
this->label32(label);
rex(this, RAX, reg, true);
X86Assembler::mov(x86reg(reg), label);
}
14 changes: 8 additions & 6 deletions src/assembler/X64Assembler.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "BaseAssembler.hpp"
#include "X86Assembler.hpp"

namespace tulip::hook {

Expand All @@ -21,7 +21,6 @@ namespace tulip::hook {
R13,
R14,
R15,
RIP = 0x40,
XMM0 = 0x80,
XMM1,
XMM2,
Expand All @@ -33,18 +32,21 @@ namespace tulip::hook {
};

struct X64Pointer {
X64Register m_register;
int32_t m_offset = 0;
X64Register reg;
int32_t offset = 0;

X64Pointer(X64Register reg, int32_t offset = 0) :
reg(reg),
offset(offset) {}
};

class X64Assembler : public BaseAssembler {
class X64Assembler : public X86Assembler {
public:
X64Assembler(uint64_t baseAddress);
X64Assembler(X64Assembler const&) = delete;
X64Assembler(X64Assembler&&) = delete;
~X64Assembler();

void label32(std::string const& name);
void updateLabels() override;

void nop();
Expand Down
61 changes: 54 additions & 7 deletions src/assembler/X86Assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ using namespace tulip::hook;

// register index, even if its xmm
uint8_t regIdx(X86Register reg) {
if (reg > X86Register::XMM0)
if (reg > X86Register::XMM0) {
return static_cast<uint8_t>(reg) - static_cast<uint8_t>(X86Register::XMM0);
}
return static_cast<uint8_t>(reg);
}

Expand All @@ -14,6 +15,17 @@ X86Assembler::X86Assembler(uint64_t baseAddress) :

X86Assembler::~X86Assembler() {}

void X86Assembler::label32(std::string const& name) {
m_labelUpdates.push_back({this->currentAddress(), name, 4});
this->write32(0);
}

void X86Assembler::updateLabels() {
for (auto const& update : m_labelUpdates) {
this->rewrite32(update.m_address, m_labels[update.m_name]);
}
}

void X86Assembler::nop() {
this->write8(0x90);
}
Expand All @@ -25,20 +37,29 @@ struct X86Operand {
} m_type;
X86Register m_reg;
uint32_t m_value = 0;
X86Operand(X86Register reg) : m_reg(reg), m_type(Type::Register) {}
X86Operand(X86Pointer ptr) : m_reg(ptr.reg), m_value(ptr.offset), m_type(Type::ModRM) {}

X86Operand(X86Register reg) :
m_reg(reg),
m_type(Type::Register) {}

X86Operand(X86Pointer ptr) :
m_reg(ptr.reg),
m_value(ptr.offset),
m_type(Type::ModRM) {}
};

static void encodeModRM(X86Assembler* ass, X86Operand op, uint8_t digit) {
if (op.m_type == X86Operand::Type::Register) {
ass->write8((0b11 << 6) | (digit << 3) | regIdx(op.m_reg));
} else if (op.m_type == X86Operand::Type::ModRM) {
}
else if (op.m_type == X86Operand::Type::ModRM) {
// the two mod bits
uint8_t mod;
// [ebp] is forced to be [ebp + 0]
if (op.m_value || op.m_reg == X86Register::EBP) {
mod = op.m_value <= 0xff ? 0b01 : 0b10;
} else {
}
else {
mod = 0b00;
}

Expand All @@ -49,10 +70,12 @@ static void encodeModRM(X86Assembler* ass, X86Operand op, uint8_t digit) {
ass->write8(0x24);
}

if (mod == 0b01)
if (mod == 0b01) {
ass->write8(op.m_value);
else if (mod == 0b10)
}
else if (mod == 0b10) {
ass->write32(op.m_value);
}
}
}

Expand Down Expand Up @@ -126,6 +149,24 @@ void X86Assembler::movss(X86Pointer ptr, X86Register reg) {
encodeModRM(this, ptr, regIdx(reg));
}

void X86Assembler::movaps(X86Register reg, X86Pointer ptr) {
this->write8(0x0F);
this->write8(0x28);
encodeModRM(this, ptr, regIdx(reg));
}

void X86Assembler::movaps(X86Pointer ptr, X86Register reg) {
this->write8(0x0F);
this->write8(0x29);
encodeModRM(this, ptr, regIdx(reg));
}

void X86Assembler::lea(X86Register reg, std::string const& label) {
this->write8(0x8D);
this->write8(0x05 | regIdx(reg) << 3);
this->label32(label);
}

void X86Assembler::mov(X86Register reg, uint32_t value) {
this->write8(0xB8 | regIdx(reg));
this->write32(value);
Expand All @@ -145,3 +186,9 @@ void X86Assembler::mov(X86Register dst, X86Register src) {
this->write8(0x89);
encodeModRM(this, dst, regIdx(src));
}

void X86Assembler::mov(X86Register reg, std::string const& label) {
this->write8(0x8B);
this->write8(0x05 | regIdx(reg) << 3);
this->label32(label);
}
Loading

0 comments on commit 29d7d9a

Please sign in to comment.