Last active
October 25, 2018 05:58
-
-
Save msievers/e3d623119ead819b1da28f52e2b34958 to your computer and use it in GitHub Desktop.
Example for hijacking ruby via FFI and using native structs and functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "ffi" | |
module MRI | |
extend FFI::Library | |
ffi_lib [FFI::CURRENT_PROCESS, "ruby"] | |
attach_function :rb_str_resize, :rb_str_resize, [:pointer, :long], :pointer | |
def self.sizeof(type) | |
Class.new(FFI::Struct) do | |
layout(member: type) | |
end | |
.size | |
end | |
VALUE = typedef :pointer, :VALUE | |
class RBasic < FFI::Struct | |
layout( | |
flags: VALUE, | |
klass: VALUE | |
) | |
end | |
RSTRING_EMBED_LEN_MAX = ((sizeof(VALUE)*3) / sizeof(:char)) - 1 | |
class RString < FFI::Struct | |
layout( | |
basic: RBasic, | |
as: Class.new(FFI::Union) do | |
layout( | |
heap: Class.new(FFI::Struct) do | |
layout( | |
len: :long, | |
ptr: :pointer, | |
aux: Class.new(FFI::Union) do | |
layout( | |
capa: :long, | |
shared: VALUE | |
) | |
end | |
) | |
end, | |
ary: [:char, RSTRING_EMBED_LEN_MAX] | |
) | |
end | |
) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "ffo" | |
require_relative "./mri.rb" | |
# Notes | |
# - a strings memory address is defined by it's object_id shift left by 1, thatswhy "foo".object_id << 1 | |
# - strings are embedded directly into the RString structure if they are less that 24 chars | |
# - for strings longer than 24 chars, their content is placed on the heap | |
short_string = "foobar" | |
short_rstring = Zstandard::MRI::RString.new(FFI::Pointer.new(short_string.object_id << 1)) | |
short_rstring[:as][:ary].to_s | |
# => "foobar" | |
long_string = "foobar" * 4 # 24 chars is the threshold for string embedding | |
long_rstring = Zstandard::MRI::RString.new(FFI::Pointer.new(long_string.object_id << 1)) | |
long_rstring[:as][:heap][:len] | |
# => 24 | |
long_rstring[:as][:heap][:ptr].read_bytes(long_rstring[:as][:heap][:len]) | |
# => "foobarfoobarfoobarfoobar" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment