Skip to content

Commit

Permalink
added x64 tests and fixed 64 assembler
Browse files Browse the repository at this point in the history
  • Loading branch information
altalk23 committed Aug 5, 2023
1 parent 0559e92 commit d001e56
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 47 deletions.
49 changes: 45 additions & 4 deletions src/assembler/X64Assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ uint8_t regv(X64Pointer ptr) {
}

uint8_t lowerv(X64Register reg, uint8_t offset) {
return (regv(reg) < 0x8) << offset;
return (regv(reg) >> 3) << offset;
}

uint8_t lowerv(X64Pointer ptr, uint8_t offset) {
return (regv(ptr) < 0x8) << offset;
return (regv(ptr) >> 3) << offset;
}

uint8_t regIdx(X64Register reg) {
return static_cast<uint8_t>(reg) & 0x7;
}

void rex(X64Assembler* ass, X64Register reg, X64Register reg2, bool wide) {
Expand All @@ -33,7 +37,7 @@ void rex(X64Assembler* ass, X64Pointer ptr, X64Register reg, bool wide) {
}

X86Register x86reg(X64Register reg) {
return static_cast<X86Register>(regv(reg) | 0xf7);
return static_cast<X86Register>(regv(reg) & 0xf7);
}

X86Pointer x86ptr(X64Pointer ptr) {
Expand Down Expand Up @@ -67,6 +71,21 @@ void X64Assembler::sub(X64Register reg, uint32_t value) {
X86Assembler::sub(x86reg(reg), value);
}

void X64Assembler::push(X64Register reg) {
rex(this, reg, RAX, false);
X86Assembler::push(x86reg(reg));
}

void X64Assembler::push(X64Pointer ptr) {
rex(this, ptr, RAX, false);
X86Assembler::push(x86ptr(ptr));
}

void X64Assembler::pop(X64Register reg) {
rex(this, reg, RAX, false);
X86Assembler::pop(x86reg(reg));
}

void X64Assembler::jmp(X64Register reg) {
rex(this, reg, RAX, false);
X86Assembler::jmp(x86reg(reg));
Expand All @@ -86,6 +105,26 @@ void X64Assembler::lea(X64Register reg, std::string const& label) {
X86Assembler::lea(x86reg(reg), label);
}

void X64Assembler::movsd(X64Register reg, X64Pointer ptr) {
rex(this, ptr, RAX, false);
X86Assembler::movsd(x86reg(reg), x86ptr(ptr));
}

void X64Assembler::movsd(X64Pointer ptr, X64Register reg) {
rex(this, ptr, RAX, false);
X86Assembler::movsd(x86ptr(ptr), x86reg(reg));
}

void X64Assembler::movss(X64Register reg, X64Pointer ptr) {
rex(this, ptr, RAX, false);
X86Assembler::movss(x86reg(reg), x86ptr(ptr));
}

void X64Assembler::movss(X64Pointer ptr, X64Register reg) {
rex(this, ptr, RAX, false);
X86Assembler::movss(x86ptr(ptr), x86reg(reg));
}

void X64Assembler::movaps(X64Register reg, X64Pointer ptr) {
rex(this, ptr, RAX, false);
X86Assembler::movaps(x86reg(reg), x86ptr(ptr));
Expand All @@ -98,7 +137,9 @@ void X64Assembler::movaps(X64Pointer ptr, X64Register reg) {

void X64Assembler::mov(X64Register reg, uint32_t value) {
rex(this, reg, RAX, true);
X86Assembler::mov(x86reg(reg), value);
this->write8(0xc7);
this->write8(0xc0 | regIdx(reg));
this->write32(value);
}

void X64Assembler::mov(X64Register reg, X64Pointer ptr) {
Expand Down
25 changes: 24 additions & 1 deletion src/assembler/X64Assembler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ namespace tulip::hook {
offset(offset) {}
};

inline X64Pointer operator+(X64Register reg, int32_t offset) {
return X64Pointer(reg, offset);
}

// Use this to easily express a X64Pointer, like so:
// RegMem32 m;
// m[RSP], m[RSP + 4]
struct RegMem64 {
X64Pointer operator[](X64Pointer ptr) {
return ptr;
}
};

class X64Assembler : public X86Assembler {
public:
X64Assembler(uint64_t baseAddress);
Expand All @@ -54,16 +67,26 @@ namespace tulip::hook {
void add(X64Register reg, uint32_t value);
void sub(X64Register reg, uint32_t value);

void push(X64Register reg);
void push(X64Pointer ptr);
void pop(X64Register reg);

void jmp(X64Register reg);
void jmp(uint64_t address);

void call(X64Register reg);

void lea(X64Register reg, std::string const& label);
void movsd(X64Register reg, X64Pointer ptr);
void movsd(X64Pointer ptr, X64Register reg);

void movss(X64Register reg, X64Pointer ptr);
void movss(X64Pointer ptr, X64Register reg);

void movaps(X64Register reg, X64Pointer ptr);
void movaps(X64Pointer ptr, X64Register reg);

void lea(X64Register reg, std::string const& label);

void mov(X64Register reg, uint32_t value);
void mov(X64Register reg, X64Pointer ptr);
void mov(X64Pointer ptr, X64Register reg);
Expand Down
52 changes: 17 additions & 35 deletions src/assembler/X86Assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,9 @@ void X86Assembler::nop() {
this->write8(0x90);
}

struct X86Operand {
enum class Type {
Register,
ModRM,
} 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) {}
};

static void encodeModRM(X86Assembler* ass, X86Operand op, uint8_t digit) {
void X86Assembler::encodeModRM(X86Operand op, uint8_t digit) {
if (op.m_type == X86Operand::Type::Register) {
ass->write8((0b11 << 6) | (digit << 3) | regIdx(op.m_reg));
this->write8((0b11 << 6) | (digit << 3) | regIdx(op.m_reg));
}
else if (op.m_type == X86Operand::Type::ModRM) {
// the two mod bits
Expand All @@ -63,18 +45,18 @@ static void encodeModRM(X86Assembler* ass, X86Operand op, uint8_t digit) {
mod = 0b00;
}

ass->write8((mod << 6) | (digit << 3) | regIdx(op.m_reg));
this->write8((mod << 6) | (digit << 3) | regIdx(op.m_reg));
if (op.m_reg == X86Register::ESP) {
// this extra byte is used to represent scaled registers,
// however we dont use those, so we only need it for esp
ass->write8(0x24);
this->write8(0x24);
}

if (mod == 0b01) {
ass->write8(op.m_value);
this->write8(op.m_value);
}
else if (mod == 0b10) {
ass->write32(op.m_value);
this->write32(op.m_value);
}
}
}
Expand All @@ -97,7 +79,7 @@ void X86Assembler::push(X86Register reg) {

void X86Assembler::push(X86Pointer reg) {
this->write8(0xFF);
encodeModRM(this, reg, 6);
this->encodeModRM(reg, 6);
}

void X86Assembler::pop(X86Register reg) {
Expand All @@ -106,7 +88,7 @@ void X86Assembler::pop(X86Register reg) {

void X86Assembler::jmp(X86Register reg) {
this->write8(0xFF);
encodeModRM(this, reg, 4);
this->encodeModRM(reg, 4);
}

void X86Assembler::jmp(uint64_t address) {
Expand All @@ -125,40 +107,40 @@ void X86Assembler::movsd(X86Register reg, X86Pointer ptr) {
this->write8(0xF2);
this->write8(0x0F);
this->write8(0x10);
encodeModRM(this, ptr, regIdx(reg));
this->encodeModRM(ptr, regIdx(reg));
}

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

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

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

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

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

void X86Assembler::lea(X86Register reg, std::string const& label) {
Expand All @@ -174,17 +156,17 @@ void X86Assembler::mov(X86Register reg, uint32_t value) {

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

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

void X86Assembler::mov(X86Register dst, X86Register src) {
this->write8(0x89);
encodeModRM(this, dst, regIdx(src));
this->encodeModRM(dst, regIdx(src));
}

void X86Assembler::mov(X86Register reg, std::string const& label) {
Expand Down
21 changes: 21 additions & 0 deletions src/assembler/X86Assembler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,28 @@ namespace tulip::hook {
}
};

struct X86Operand {
enum class Type {
Register,
ModRM,
} 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) {}
};

class X86Assembler : public BaseAssembler {
protected:
void encodeModRM(X86Operand op, uint8_t digit);

public:
X86Assembler(uint64_t baseAddress);
X86Assembler(X86Assembler const&) = delete;
Expand Down
7 changes: 7 additions & 0 deletions test/Assembler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include <vector>

inline std::vector<uint8_t> operator""_bytes(char const* data, size_t size) {
return {reinterpret_cast<uint8_t const*>(data), reinterpret_cast<uint8_t const*>(data + size)};
}
77 changes: 77 additions & 0 deletions test/Assembler64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "../src/assembler/X64Assembler.hpp"
#include "Assembler.hpp"

#include <gtest/gtest.h>

using namespace tulip::hook;

TEST(X64AssemblerTest, NopMov) {
using enum X64Register;
X64Assembler a(0x123);
a.nop();
a.mov(R8, 10);
EXPECT_EQ(a.buffer(), "\x90\x49\xc7\xc0\x0a\x00\x00\x00"_bytes);
}

TEST(X64AssemblerTest, JmpCall) {
using enum X64Register;
X64Assembler a(0x123);
a.jmp(0xb00b5);
a.jmp(RCX);
a.jmp(R8);
a.call(R13);
a.call(RSP);
EXPECT_EQ(a.buffer(), "\xE9\x8D\xFF\x0A\x00\xFF\xE1\x41\xFF\xE0\x41\xFF\xD5\xFF\xD4"_bytes);
}

TEST(X64AssemblerTest, Push) {
using enum X64Register;
X64Assembler a(0x123);
RegMem64 m;
a.push(RAX);
a.push(R12);
a.push(m[RSP + 0x10]);
EXPECT_EQ(a.buffer(), "\x50\x41\x54\xFF\x74\x24\x10"_bytes);
}

TEST(X64AssemblerTest, Mov) {
using enum X64Register;
X64Assembler a(0x123);
RegMem64 m;
a.mov(RAX, RAX);
a.mov(R9, R8);
a.mov(RCX, m[R10 + 4]);
a.mov(R9, m[RBP + 4]);
a.mov(m[RBP + 4], RSP);
a.mov(m[R13], R8);
EXPECT_EQ(
a.buffer(), "\x48\x89\xC0\x4D\x89\xC1\x49\x8B\x4A\x04\x4C\x8B\x4D\x04\x48\x89\x65\x04\x4D\x89\x45\x00"_bytes
);
}

TEST(X64AssemblerTest, Movsd) {
using enum X64Register;
X64Assembler a(0x123);
RegMem64 m;
a.movsd(m[RSP], XMM0);
a.movsd(XMM1, m[RSP + 4]);
EXPECT_EQ(a.buffer(), "\xF2\x0F\x11\x04\x24\xF2\x0F\x10\x4C\x24\x04"_bytes);
}

TEST(X64AssemblerTest, Movss) {
using enum X64Register;
X64Assembler a(0x123);
RegMem64 m;
a.movss(m[RSP], XMM0);
a.movss(XMM1, m[RSP + 4]);
EXPECT_EQ(a.buffer(), "\xF3\x0F\x11\x04\x24\xF3\x0F\x10\x4C\x24\x04"_bytes);
}

TEST(X64AssemblerTest, Movaps) {
using enum X64Register;
X64Assembler a(0x123);
RegMem64 m;
a.movaps(m[RSP], XMM0);
a.movaps(XMM1, m[RSP + 4]);
EXPECT_EQ(a.buffer(), "\x0F\x29\x04\x24\x0F\x28\x4C\x24\x04"_bytes);
}
Loading

0 comments on commit d001e56

Please sign in to comment.