x86-64 Assembler based on Zydis

Related tags

Miscellaneous zasm
Overview

Zasm : x86-64 Assembler based on Zydis

Why?

Some of my projects were using Zydis and AsmJit where instructions where were first decoded with Zydis and then put into AsmJit's Builder to allow processing/analysing of the instructions/branches before re-encoding/relocating the modified code, there are a couple of downsides to this approach which will be explained further down. Zydis recently introduced a way to use the same structures/data it already has to encode instructions which lead to Zasm. This library aims to be a higher level assembler/decoder which can be used for various things like the previously mentioned example.

A strong difference between Zasm and AsmJit is the focus on accurate instruction data such as operand access, hidden register use, correct cpu flags all of which can be either missing or wrong in AsmJit with some exceptions of course, AsmJit aims to a friendly way to generate code on the fly for lets say scripting or high performance computing. So Zasm is not trying to replace AsmJit in any way, it has a different goals.

The second reason for Zasm is that Zydis Encoder being extremly low level which means you don't have things like labels, Zasm provides a high level class for assembling instructions and provides labels like an ordinary assembler would.

Design

Zasm is composed of three components Program, Decoder, Assembler. While Zasm uses Zydis as the framework a lot of structures do not match that of Zydis that is primarily due to Zasm storing instructions in nodes, storing the raw ZydisDecodedInstruction would be extremly heavy on the memory usage, so in some cases I made the choice to only store what I consider relevant, storing 10'000'000 instructions currently uses about 4~ GiB memory. The instruction stores just about enough information for most analysis to work out of the box.

Program is the container that holds all the data and also serves as a doubly linked list, instructions labels and data are stored as nodes which allows the user to remove/insert/re-order quite easily. This container is also responsible for generating the final output by serializing each node in one or multiple passes, it will never take more than 3 passes at most and in most cases its just two, this is driven by how labels are used, this is something that can be improved in the future.

Decoder is a wrapper class for the Zydis Decoder. Decodes instructions from data.

Assembler is a wrapper class that has specialized overloads for all supported instructions that adds new nodes to the Program being attached to. It also allows to directly add instructions from the Decoder

Examples

Generate a basic function for x64.

using namespace zasm;
using namespace zasm::operands;

Program program(ZydisMachineMode::ZYDIS_MACHINE_MODE_LONG_64);
Assembler assembler(program);

assembler.mov(rax, Imm(0xF00B444));
assembler.ret();

// Encodes all the nodes.
program.serialize(0x00400000);

const auto codeDump = getHexDump(program.getCode(), program.getCodeSize());
std::cout << codeDump << "\n";

Feeding instructions from the decoder and re-encode.

using namespace zasm;
using namespace zasm::operands;

const uint64_t baseAddr = 0x00007FF6BC738ED4;
const std::array<uint8_t, 24> code = {
    0x40, 0x53,             // push rbx
    0x45, 0x8B, 0x18,       // mov r11d, dword ptr ds:[r8]
    0x48, 0x8B, 0xDA,       // mov rbx, rdx
    0x41, 0x83, 0xE3, 0xF8, // and r11d, 0xFFFFFFF8
    0x4C, 0x8B, 0xC9,       // mov r9, rcx
    0x41, 0xF6, 0x00, 0x04, // test byte ptr ds:[r8], 0x4
    0x4C, 0x8B, 0xD1,       // mov r10, rcx
    0x74, 0x13,             // je 0x00007FF6BC738EFF
};

Program program(ZydisMachineMode::ZYDIS_MACHINE_MODE_LONG_64);
Assembler assembler(program);

Decoder decoder(program.getMode());

size_t bytesDecoded = 0;

while (bytesDecoded < code.size())
{
    const auto curAddress = baseAddr + bytesDecoded;

    auto decoderRes = decoder.decode(code.data() + bytesDecoded, code.size() - bytesDecoded, curAddress);
    if (!decoderRes)
    {
        std::cout << "Failed to decode at " << std::hex << curAddress << ", " << decoderRes.error() << "\n";
        return;
    }

    const auto& instr = decoderRes.value();
    assembler.fromInstruction(instr);

    bytesDecoded += instr.getLength();
}

program.serialize(baseAddr);

const auto codeDump = getHexDump(program.getCode(), program.getCodeSize());
std::cout << codeDump << "\n";
Comments
  • Performance problem.

    Performance problem.

    I'm currently writing a program which will reencode nearly every instruction of a big problem(a game). So there will be millions of calls on reencode callback. (see here.)

    The problem is that if I declare the Program and Assembler as local variables, then there will be millions of object/memory allocations and deallocations which will be a big impact on speed.

    But if I declare them as global variables, I didn't see any clear methods so the generated code will be accumulated on every call.

    Is there any solutions to this?

    opened by Inori 10
  • A few touches to make zasm useable for something

    A few touches to make zasm useable for something

    Lets say I want to use this write a jit, well in that case I need to be able to pass addresses in of library routines and to get addresses out of generated routines.

    Ie, I need to be able to take the address of a label after serialization, and I need to be able to SET a constant address for some labels before serialization.

    I don't see a way to do these things. There are certainly no examples that do them.

    I made a fork to do this myself.

    opened by differentprogramming 9
  • More basic examples needed

    More basic examples needed

    I asked before for the ability to get the address of a label, and you supplied that.

    Now I need a few other things: Examples of basic things a) actually calling code you assembled. There are no examples of calling code you created. b) an example of calling into user or library code from assembly. c) disassembling to human readable form. Getting the abi right for a platform is complex, and you pretty much need to be able to disassemble and see code that the compiler generates so that you can make templates from entry and exit code that's correct in every detail. Also it would be useful for making sure that the code you're generating is what you thought it was. d) deallocating the result of an assembly - for a jit you not only create code, but you throw it away when you're done with it and are replacing it with altered code.

    Edit I guess I can just use the debugger for disassembly.

    opened by differentprogramming 6
  • Please provide a example ready to build project.

    Please provide a example ready to build project.

    I couldn't use it in my own project. It gives linker errors.

    Build started... 1>------ Build started: Project: Test, Configuration: Release x64 ------ 1>Test.obj : error LNK2001: unresolved external symbol "public: enum zasm::Error __cdecl zasm::Serializer::serialize(class zasm::Program const &,__int64)" (?serialize@Serializer@zasm@@QEAA?AW4Error@2@AEBVProgram@2@_J@Z) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::Serializer::~Serializer(void)" (??1Serializer@zasm@@QEAA@XZ) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::Serializer::Serializer(void)" (??0Serializer@zasm@@QEAA@XZ) 1>Test.obj : error LNK2001: unresolved external symbol "public: enum zasm::Error __cdecl zasm::x86::Assembler::emit(class zasm::Instruction const &)" (?emit@Assembler@x86@zasm@@QEAA?AW4Error@3@AEBVInstruction@3@@Z) 1>Test.obj : error LNK2001: unresolved external symbol "public: virtual __cdecl zasm::x86::Assembler::~Assembler(void)" (??1Assembler@x86@zasm@@UEAA@XZ) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::x86::Assembler::Assembler(class zasm::Program &)" (??0Assembler@x86@zasm@@QEAA@AEAVProgram@2@@Z) 1>Test.obj : error LNK2001: unresolved external symbol "public: enum zasm::MachineMode __cdecl zasm::Program::getMode(void)const " (?getMode@Program@zasm@@QEBA?AW4MachineMode@2@XZ) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::Program::~Program(void)" (??1Program@zasm@@QEAA@XZ) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::Program::Program(enum zasm::MachineMode)" (??0Program@zasm@@QEAA@W4MachineMode@1@@Z) 1>Test.obj : error LNK2001: unresolved external symbol "public: class zasm::Expected<class zasm::Instruction,enum zasm::Error> __cdecl zasm::Decoder::decode(void const *,unsigned __int64,unsigned __int64)" (?decode@Decoder@zasm@@QEAA?AV?$Expected@VInstruction@zasm@@W4Error@2@@2@PEBX_K1@Z) 1>Test.obj : error LNK2001: unresolved external symbol "public: __cdecl zasm::Decoder::Decoder(enum zasm::MachineMode)" (??0Decoder@zasm@@QEAA@W4MachineMode@1@@Z)

    opened by Saltless0 3
  • Calculating offset of memory operand type relative to instruction

    Calculating offset of memory operand type relative to instruction

    image

    In the above example I need to know the offset of the uint (highlighted in grey) for memory operands for the purposes of fixing up rva's in a mutation engine.

    Please can you suggest a way to either extract the size of the mnemonic and I can work it out that way, or even better would be to know the offset of the bytes of the operand relative to the bytes of the full instruction.

    Thanks :)

    opened by CallumCVM 2
  • Calculating addresses of call, jmp etc.

    Calculating addresses of call, jmp etc.

    With zydis, you can call ZydisCalcAbsoluteAddressEx, but there is no way to retreive the relevant data to perform this from the Decoder.

    Can you suggest any way to do this please?

    opened by CallumCVM 2
Owner
ζeh Matt
Software Engineer/Bug destroyer
ζeh Matt
x86 Assembler used for generating shellcode

Intel x86 assembler [email protected] syntax: Decimal “integers begin with a non-zero digit followed by zero or more decimal digits (0–9)” B

thescientist 1 Dec 28, 2022
A port of the Linux x86 IOLI crackme challenges to x86-64

This is a port of the original Linux x86 IOLI crackme binaries to x86-64. The original set of IOLI crackmes can be found here: https://github.com/Maij

Julian Daeumer 4 Mar 19, 2022
Commodore 6502ASM, the original 6502/65C02/65CE02 Assembler used by Commodore for C65 project

Commodore 6502ASM This is the source code of the 6502/65C02/65CE02 assembler developed and used by Commodore for the C65 project. It aims to be compat

Michael Steil 17 Nov 29, 2022
ASMotor is a portable and generic assembler engine and development system written in ANSI C99

ASMotor is a portable and generic assembler engine and development system written in ANSI C99 and licensed under the GNU Public License v3. The package consists of the assembler, the librarian and the linker. It can be used as either a cross or native development system.

null 42 Nov 18, 2022
A mini assembler for x86_64, written for fun and learning.

minias A mini assembler for x86_64, written for fun and learning. Goals: A simple, tiny, fast implementation (in that order). Assemble the output of c

null 193 Dec 9, 2022
A simple assembler, made primarily for assembling output from my compiler.

Assembler This assembler is not currently meant for general use. It supports only the instructions and features emitted (and used) in my C compiler. I

null 2 Nov 14, 2021
Toy 8 bit CPU with a real assembler

neko8 neko8 is a 8 bit CPU emulator designed to be easy to learn written in C. It uses its own simple architecture and can be programmed in its own fo

rem 4 Jan 4, 2022
A basic assembler

Assembler ASSEMBLER DERLEYİCİSİ Programlama Dilleri (derleyiciler) giriş olarak yazılan bir programın kaynak kodunu alır (kodun doğru yazıldığı varsay

Batuhan Tomo 1 Nov 22, 2021
A fully customisable assembler for your own instruction sets

CASM A fully customisable assembler for your own instruction sets! What Is CASM? ?? Documentation ?? Command-Line Usage ?? How To Install CASM ?? Buil

Sjoerd Vermeulen 2 May 7, 2022
Macos-arm64-emulation - A guide for emulating macOS arm64e on an x86-based host.

macos-arm64-emulation Use the following guide to download and configure all of the necessary tools and files for emulating the macOS arm64e kernel. Th

Cylance 233 Jan 7, 2023
a small C library for x86 CPU detection and feature extraction

libcpuid libcpuid provides CPU identification for the x86 (and x86_64). For details about the programming API, you might want to take a look at the pr

Veselin Georgiev 358 Dec 26, 2022
x86 emulator on Raspberry Pi Pico

picox86 x86 emulator on Raspberry Pi Pico https://user-images.githubusercontent.com/10139098/110543817-13299080-812b-11eb-9c88-674cdae919fc.mp4 PCB fr

null 39 Nov 9, 2022
SerenityOS - Graphical Unix-like operating system for x86 computers. 🐞

SerenityOS is a love letter to '90s user interfaces with a custom Unix-like core. It flatters with sincerity by stealing beautiful ideas from various other systems.

SerenityOS 23.5k Jan 4, 2023
Obfuscate calls to imports by patching in stubs. ICO works on both X86 and X64 binaries.

ICO adds a new section into the image, then begins building stubs for each import that uses a extremely basic routine to decrypt an RVA and places them into the section.

null 43 Dec 15, 2022
Programming language that compiles into a x86 ELF executable.

ocean Programming language that compiles into a x86 ELF executable. The main goal at the moment is to create a C compiler, which can atleast compile i

Richard 166 Jul 27, 2022
rdtsc x86 instruction to detect virtual machines

rdtsc_detector rdtsc x86 instruction to detect virtual machines What is rdtsc? The Time Stamp Counter (TSC) is a 64-bit register present on all x86 pr

null 4 Apr 29, 2022
A D++ Discord Bot template for Visual Studio 2019 (x64 and x86)

D++ Windows Bot Template A D++ Discord Bot template for Visual Studio 2019 (x64 and x86, release and debug). The result of this tutorial. This templat

brainbox.cc 28 Dec 24, 2022
An experimental operating system for x86 and ARM

Odyssey - an experimental operating system for x86 and ARM

Anuradha Weeraman 39 Dec 28, 2022