• Stars
    star
    299
  • Rank 139,269 (Top 3 %)
  • Language
    Ruby
  • License
    Apache License 2.0
  • Created over 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A pure Ruby assembler

Fisk - A Pure Ruby x86-64 Assembler

Tired of writing Ruby in Ruby? Now you can write assembly in Ruby with Fisk!

This is a pure Ruby x86-64 assembler (I guess). I'm not 100% sure if it counts as pure Ruby because it just reads an XML file and metaprograms most of it. Anyway, you can use it to write assembly in Ruby, then have it assembled.

I named it after Wilson Fisk mainly because it reminds me of this project and because I've been playing lots of Spider-Man.

Usage

Here is an example of assembling something:

fisk = Fisk.new

binary = fisk.asm do
  push rbp
  mov rbp, rsp
  int lit(3)
  pop rbp
  ret
end

Fisk uses Intel assembly syntax, so the first operand is the destination, and the second operand is the source. So for example mov rax, imm8(1) means "put an immediate that is 8 bits wide with the value of 1 in the RAX register".

Sizes and Memory Operands

In order to select the right x86 instruction to emit, Fisk needs to know the types of the operands. Register types are already implied, but things like immediates and memory operands need to be specified.

Below are a few examples.

Putting a 32 bit immediate in the RAX register:

mov rax, imm32(0xFFF)

Dereferencing the value in RAX and storing the value in the RAX register:

mov rax, m64(rax)

Dereferencing the value 8 bytes away RAX and storing the value in the R9 register:

mov r9, m64(rax, 8)

Register Allocation

Fisk supports simple register assignment. You can make temporary registers, then have Fisk assign registers for you. For example:

fisk = Fisk.new

# Make some temporary registers
reg1 = fisk.register("temp1")
reg2 = fisk.register("temp2")

# XOR the two virtual registers
fisk.xor reg1, reg2

# Ask Fisk to assign registers from the pool of registers passed in
fisk.assign_registers([fisk.r9, fisk.r10])

Currently, Fisk won't spill registers for you, it just raises an exception.

Executing Assembly

Now, it's not very fun to assemble something unless you can execute it. So here is an example of how to execute the above assembly. This assembly code will send an interrupt and tell the debugger to stop. So let's write the machine code to some executable memory, and call it from a Ruby program that we will start in lldb.

require "fisk"
require "fisk/helpers"

module Break
  fisk = Fisk.new

  jitbuf = Fisk::Helpers.jitbuffer 4096

  fisk.asm(jitbuf) do
    push rbp
    mov rbp, rsp
    int lit(3)
    pop rbp
    ret
  end

  define_singleton_method :dance!, &jitbuf.to_function([], Fiddle::TYPE_VOID)
end

def deep i = 2
  if i == 0
    Break.dance!
  else
    deep(i - 1)
  end
end

deep

If we launch this script under lldb, the debugger will halt the process when we call the dance! method:

[aaron@tc-lan-adapter ~/g/fisk (master)]$ lldb ~/git/ruby/ruby -- -I lib fun.rb
error: module importing failed: invalid pathname
(lldb) target create "/Users/aaron/git/ruby/ruby"
procCurrent executable set to '/Users/aaron/git/ruby/ruby' (x86_64).
(lldb) settings set -- target.run-args  "-I" "lib" "fun.rb"
(lldb) process launch
Process 33042 launched: '/Users/aaron/git/ruby/ruby' (x86_64)
Process 33042 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
    frame #0: 0x00000001007f4005
->  0x1007f4005: popq   %rbp
    0x1007f4006: retq   
    0x1007f4007: addb   %al, (%rax)
    0x1007f4009: addb   %al, (%rax)
Target 0: (ruby) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
  * frame #0: 0x00000001007f4005
    frame #1: 0x00007fff2db538e5 libffi.dylib`ffi_call_unix64 + 85
    frame #2: 0x00007fff2db5322a libffi.dylib`ffi_call_int + 692
    frame #3: 0x0000000107458e3d fiddle.bundle`nogvl_ffi_call(ptr=0x00007ffeefbf9bb0) at function.c:204:5
    frame #4: 0x00000001002e704f ruby`rb_nogvl(func=(fiddle.bundle`nogvl_ffi_call at function.c:201), data1=0x00007ffeefbf9bb0, ubf=0x0000000000000000, data2=0x0000000000000000, flags=0) at thread.c:1671:5
    frame #5: 0x00000001002e7570 ruby`rb_thread_call_without_gvl(func=(fiddle.bundle`nogvl_ffi_call at function.c:201), data1=0x00007ffeefbf9bb0, ubf=0x0000000000000000, data2=0x0000000000000000) at thread.c:1787:12
    frame #6: 0x00000001074585a6 fiddle.bundle`function_call(argc=0, argv=0x00000001080280c8, self=0x0000000128f85fd8) at function.c:375:15
    frame #7: 0x0000000100383ca7 ruby`call_cfunc_m1(recv=0x0000000128f85fd8, argc=0, argv=0x00000001080280c8, func=(fiddle.bundle`function_call at function.c:211)) at vm_insnhelper.c:2594:12
    frame #8: 0x000000010037ef99 ruby`vm_call_cfunc_with_frame(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:2924:11
    frame #9: 0x0000000100377603 ruby`vm_call_cfunc(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:2945:12
    frame #10: 0x0000000100376f2b ruby`vm_call_method_each_type(ec=0x0000000100b069b0, cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3414:16
    frame #11: 0x00000001003769e9 ruby`vm_call_method(ec=0x0000000100b069b0, cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3507:20
    frame #12: 0x00000001003578f5 ruby`vm_call_general(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3550:12
    frame #13: 0x000000010036d688 ruby`vm_sendish(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, cd=0x0000000100e2be30, block_handler=0x0000000000000000, method_explorer=mexp_search_method) at vm_insnhelper.c:4525:15
    frame #14: 0x000000010033f3de ruby`vm_exec_core(ec=0x0000000100b069b0, initial=0x0000000000000000) at insns.def:789:11
    frame #15: 0x0000000100361d6e ruby`rb_vm_exec(ec=0x0000000100b069b0, mjit_enable_p=true) at vm.c:2162:22
    frame #16: 0x00000001003884c5 ruby`invoke_bmethod(ec=0x0000000100b069b0, iseq=0x000000010781d168, self=0x0000000128f7d180, captured=0x00000001294ef620, me=0x0000000128f85f10, type=0x0000000022220101, opt_pc=0) at vm.c:1292:11
    frame #17: 0x0000000100360321 ruby`rb_vm_invoke_bmethod [inlined] invoke_iseq_block_from_c(ec=0x0000000100b069b0, captured=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, passed_block_handler=0x0000000000000000, cref=0x0000000000000000, is_lambda=1, me=0x0000000128f85f10) at vm.c:1337:9
    frame #18: 0x000000010036017d ruby`rb_vm_invoke_bmethod [inlined] invoke_block_from_c_proc(ec=0x0000000100b069b0, proc=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, passed_block_handler=0x0000000000000000, is_lambda=1, me=0x0000000128f85f10) at vm.c:1434
    frame #19: 0x00000001003600cf ruby`rb_vm_invoke_bmethod(ec=0x0000000100b069b0, proc=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, block_handler=0x0000000000000000, me=0x0000000128f85f10) at vm.c:1470
    frame #20: 0x000000010037f3e7 ruby`vm_call_bmethod_body(ec=0x0000000100b069b0, calling=0x00007ffeefbfcd20, argv=0x00007ffeefbfcab0) at vm_insnhelper.c:2983:11
    frame #21: 0x0000000100377e8f ruby`vm_call_bmethod(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3003:12
    frame #22: 0x00000001003770c9 ruby`vm_call_method_each_type(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3440:16
    frame #23: 0x00000001003769e9 ruby`vm_call_method(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3507:20
    frame #24: 0x00000001003578f5 ruby`vm_call_general(ec=0x0000000100b069b0, reg_cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3550:12
    frame #25: 0x000000010036d688 ruby`vm_sendish(ec=0x0000000100b069b0, reg_cfp=0x0000000108127ee8, cd=0x0000000100e2bcb0, block_handler=0x0000000000000000, method_explorer=mexp_search_method) at vm_insnhelper.c:4525:15
    frame #26: 0x000000010033f3de ruby`vm_exec_core(ec=0x0000000100b069b0, initial=0x0000000000000000) at insns.def:789:11
    frame #27: 0x0000000100361de7 ruby`rb_vm_exec(ec=0x0000000100b069b0, mjit_enable_p=true) at vm.c:2171:22
    frame #28: 0x0000000100363070 ruby`rb_iseq_eval_main(iseq=0x000000010781da28) at vm.c:2419:11
    frame #29: 0x00000001000dbefb ruby`rb_ec_exec_node(ec=0x0000000100b069b0, n=0x000000010781da28) at eval.c:317:2
    frame #30: 0x00000001000dbd83 ruby`ruby_run_node(n=0x000000010781da28) at eval.c:375:30
    frame #31: 0x00000001000036fc ruby`main(argc=4, argv=0x00007ffeefbff5f8) at main.c:47:9
    frame #32: 0x00007fff20530621 libdyld.dylib`start + 1
    frame #33: 0x00007fff20530621 libdyld.dylib`start + 1

Note that in order to produce a stack trace like the above, the Ruby binary must include the debugging symbols (otherwise, the ruby frames will not be displayed); this is accomplished by specifying the --ggdb3 compiler flag:

  • if compiling Ruby from source, use ./configure optflags=-gddb3 in the build process
  • if using a version manager, refer to the help; example for RVM: optflags="-ggdb3" rvm install 3.0.2 --disable-binary

More Repositories

1

initial-v

It's a BMW shifter converted to a Bluetooth Keyboard that you use with Vim
C++
980
star
2

asmrepl

A REPL for x86-64 assembly language
Ruby
866
star
3

rails_autolink

The auto_link function from Rails
Ruby
589
star
4

the_metal

A spike for thoughts about Rack 2.0
Ruby
519
star
5

analog-terminal-bell

A bell for your terminal that is analog
OpenSCAD
482
star
6

tenderjit

JIT for Ruby that is written in Ruby
Ruby
418
star
7

phuby

phuby wraps PHP in a loving embrace
Ruby
265
star
8

esp8266aq

ESP8266 and Plantower AQ sensor
C++
216
star
9

hana

An implementation of JSON Patch and JSON Pointer for Ruby
Ruby
214
star
10

recma

Pure ruby javascript parser and interpreter.
Ruby
196
star
11

dnssd

Multicast DNS client for ruby! YAY!
Ruby
173
star
12

ruby-glossary

Just a glossary of terms I've found in Ruby source code
171
star
13

enterprise

Make ruby ruby application enter the enterprise with the enterprise gem
Ruby
155
star
14

heap-analyzer

A heap analyzer for MRI that isn't very good.
JavaScript
141
star
15

namecase

Properly case people's names
Ruby
128
star
16

refreshing

A Rails Engine that gives Language Server support to Rails apps
Ruby
115
star
17

uart

Simple serial / UART interface for Ruby
Ruby
109
star
18

tusk

Message busses with Observable API
Ruby
101
star
19

heapfrag

Heap visualizer for Ruby
Ruby
97
star
20

nfc

NFC is a ruby wrapper for the Near Field Communication library.
C
90
star
21

tinygql

A tiny and experimental GraphQL parser in Ruby
Ruby
84
star
22

rubycommitters.org

The source for rubycommitters.org
Ruby
81
star
23

ds9

Wrapper around nghttp2
C
80
star
24

fibur

Concurrent execution during Ruby I/O
Ruby
78
star
25

our_pc

Our Procedure Calls
Ruby
71
star
26

magic_scan

My magic card recognition system
Ruby
69
star
27

av_capture

A wrapper around av_capture on OS X
Objective-C
68
star
28

aarch64

Pure Ruby ARM64 Assembler
Ruby
67
star
29

gda

A SQL parser. It wraps libgda
C
64
star
30

taka

Taka is a DOM (core and html) implementation for Ruby
Ruby
63
star
31

minitest-emoji

View your test output as emoji
Ruby
60
star
32

mmap

A wrapper around mmap
C
57
star
33

neversaydie

NeverSayDie will let you rescue from SEGV's and is evil
Ruby
55
star
34

reloader

A demo of live streams on Rails 4
Ruby
50
star
35

HTML5DeckBuilder

an html5 mtg deck builder
JavaScript
49
star
36

sphero

A ruby gem for the sphero ball
Ruby
45
star
37

dot_vim

my dot vim directory
Vim Script
44
star
38

markup_validity

Test for valid markup with test/unit or rspec
Ruby
44
star
39

defrost

Never let pesky "frozen" objects get in your way again! Use Defrost to remove the frozen state from your objects!
Ruby
42
star
40

my_thing

demo of regression test selection
Ruby
42
star
41

gem_survey

a survey script for gems
Ruby
41
star
42

spectacular

View SNMP in your browser
Ruby
38
star
43

zeroconf

Multicast DNS client and server written in pure Ruby
Ruby
31
star
44

leap_motion

Ruby bindings to the Leap Motion C++ library
C++
30
star
45

paddle

Paddle is an rdoc plugin for emitting epub books suitable for use with iBooks
Ruby
29
star
46

horo

RDoc style extracted from Rails and refactored for RDoc 2.5.x
Ruby
27
star
47

earworm

What is that song? Earworm uses libofa and MusicDNS to tell you.
Ruby
26
star
48

streamdeck-ruby-plugin

Minimal Stream Deck plugin written in Ruby
Ruby
24
star
49

raop

RAOP Client is an Airport Express client written in ruby. It allows you to stream music to an Airport Express from Ruby.
Ruby
24
star
50

lolwut

Demo of SSE and reloading with AC::Live
Ruby
23
star
51

purdytest

Colorized output for minitest
Ruby
23
star
52

dejour

find awesome stuff on the network
Ruby
22
star
53

qrtools

QR Decoder for Ruby. Uses libdecodeqr
C++
22
star
54

playpen

A wrapper around the OS X security library
Ruby
21
star
55

geera

A commandline client for JIRA
Ruby
20
star
56

rjson

An JSON parser written with Racc
Ruby
20
star
57

widen

A library for narrowing and widening characters
Ruby
19
star
58

arghhh

I can't figure out SSL, please help!
Ruby
19
star
59

heap-utils

Just random scripts I'm using for Ruby heap info
Ruby
19
star
60

icanhasaudio

I am in ur computar, encodin' and decodin' ur MP3z.
C
19
star
61

housefire

Dumb campfire clone
Ruby
18
star
62

xml_truth

A collection of XML/HTML parser benchmarks for Ruby
Ruby
18
star
63

tldr

Too Long Didn't Run
Ruby
17
star
64

adequatehq.com

Adequate HQ
17
star
65

stree

A wrapper around libstree
C
16
star
66

allocation_sampler

C
16
star
67

wreckster

A Ruby API for Rexster
Ruby
15
star
68

chicken-yaml

libyaml wrapper for chicken scheme
Scheme
15
star
69

ruby-pprof

A Ruby program for parsing gperftools output
Ruby
14
star
70

uchip

Ruby library for interacting with MCP2221 and MCP2221a
Ruby
14
star
71

betabrite

This is a ruby API for the BetaBrite LED sign
Ruby
14
star
72

orient

Binary protocol implementation for Orient DB and Ruby
Ruby
14
star
73

fsmjs

Simulating fsm in JS
14
star
74

imgur2

Quick hack for uploading to imgur
Ruby
13
star
75

roastr

My Christmas roast
R
13
star
76

zomg

An OMG IDL parser and ruby code generator
Ruby
12
star
77

quail

Zero MQ ruby wrapper
Ruby
12
star
78

jit_buffer

General purpose buffer for use with building JITs
Ruby
10
star
79

hacks

Code for accessing CRuby constants and functions as Ruby
Ruby
10
star
80

meow

Meow gives you access to Growl notifications using RubyCocoa
Ruby
10
star
81

awkward

Produce dot files that represent your object graph
Ruby
10
star
82

libxml2

my fork of libxml2
C
10
star
83

WORF

WORF, the DWARF parser
Ruby
10
star
84

mallcop

libssh2 wrapper
Ruby
9
star
85

milton

Ruby
9
star
86

odinflex

Different utilities that I've written
Ruby
9
star
87

wank

Ruby
9
star
88

remotehash

An OpenDHT client library
Ruby
9
star
89

kedama

A wrapper around ming. Create swf files in ruby using ming.
Ruby
9
star
90

rubypan

Ruby
8
star
91

tree_diff

Parse html using different parsers, then show differences between the generated trees
Ruby
8
star
92

hatstone

A minimal Ruby wrapper for Capstone disassembler
C++
8
star
93

e-zine

My E-Zine
7
star
94

rsat

A pure Ruby SAT solver
Ruby
7
star
95

tenderlove.github.com

My awesome website
HTML
7
star
96

compiler

Proof of concept for converting Journey routes to state tables
Ruby
7
star
97

myhidapi

A wrapper around hidapi for Ruby
C
7
star
98

magic

I found some of my old code from 2004 on a USB stick. This code is not for use, it's just for fun
Roff
7
star
99

hearts

I love hearts!
Ruby
6
star
100

writev

A gem that adds the `writev` method to IO
Ruby
6
star