diff --git a/bindings/ruby/Makefile b/bindings/ruby/Makefile index 6fa1faf3..2eb376eb 100644 --- a/bindings/ruby/Makefile +++ b/bindings/ruby/Makefile @@ -5,7 +5,7 @@ # Use bundle install && rake to install gem and test install: gen_const cd unicorn_gem && rake build - cd unicorn_gem && gem install --local pkg/unicorn-0.9.0.gem + cd unicorn_gem && gem install --local pkg/unicorn-1.0.0.gem gen_const: cd .. && python const_generator.py ruby diff --git a/bindings/ruby/sample_x86.rb b/bindings/ruby/sample_x86.rb index 86496d9b..9a9740e6 100644 --- a/bindings/ruby/sample_x86.rb +++ b/bindings/ruby/sample_x86.rb @@ -258,7 +258,7 @@ def test_i386_invalid_mem_write() r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) - puts ">>> ECX = 0x%x" % r_ecx + puts ">>> ECX = 0x%x" % r_ecx puts ">>> EDX = 0x%x" % r_edx begin @@ -282,6 +282,47 @@ def test_i386_invalid_mem_write() end end +def test_i386_context_save() + + puts("Save/restore CPU context in opaque blob") + address = 0 + code = '\x40' # inc eax + begin + # Initialize emulator + mu = Uc.new UC_ARCH_X86, UC_MODE_32 + + # map 8KB memory for this emulation + mu.mem_map(address, 8 * 1024, UC_PROT_ALL) + + # write machine code to be emulated to memory + mu.mem_write(address, code) + + # set eax to 1 + mu.reg_write(UC_X86_REG_EAX, 1) + + puts(">>> Running emulation for the first time") + mu.emu_start(address, address+1) + + puts(">>> Emulation done. Below is the CPU context") + puts(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + puts(">>> Saving CPU context") + saved_context = mu.context_save() + + puts(">>> Running emulation for the second time") + mu.emu_start(address, address+1) + puts(">>> Emulation done. Below is the CPU context") + puts(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + + puts(">>> CPU context restored. Below is the CPU context") + mu.context_restore(saved_context) + puts(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) + + rescue UcError => e + puts("ERROR: %s" % e) + end + +end + # Test X86 32 bit with IN/OUT instruction def test_i386_inout() puts("Emulate i386 code with IN/OUT instructions") @@ -500,6 +541,8 @@ test_i386_invalid_mem_read() puts("=" * 20) test_i386_invalid_mem_write() puts("=" * 20) +test_i386_context_save() +puts("=" * 20) test_i386_inout() puts("=" * 20) test_x86_64() diff --git a/bindings/ruby/unicorn_gem/ext/unicorn.c b/bindings/ruby/unicorn_gem/ext/unicorn.c index bd23086c..ea20f10b 100644 --- a/bindings/ruby/unicorn_gem/ext/unicorn.c +++ b/bindings/ruby/unicorn_gem/ext/unicorn.c @@ -26,12 +26,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. VALUE UnicornModule = Qnil; VALUE UcClass = Qnil; VALUE UcError = Qnil; +VALUE SavedContext = Qnil; void Init_unicorn() { rb_require("unicorn/unicorn_const"); UnicornModule = rb_define_module("Unicorn"); UcError = rb_define_class_under(UnicornModule, "UcError", rb_eStandardError); + SavedContext = rb_define_class_under(UnicornModule, "SavedContext", rb_cObject); UcClass = rb_define_class_under(UnicornModule, "Uc", rb_cObject); rb_define_method(UcClass, "initialize", m_uc_initialize, 2); @@ -47,6 +49,9 @@ void Init_unicorn() { rb_define_method(UcClass, "hook_add", m_uc_hook_add, -1); rb_define_method(UcClass, "hook_del", m_uc_hook_del, 1); rb_define_method(UcClass, "query", m_uc_hook_del, 1); + rb_define_method(UcClass, "context_save", m_uc_context_save, 0); + rb_define_method(UcClass, "context_update", m_uc_context_update, 1); + rb_define_method(UcClass, "contest_restore", m_uc_context_restore, 1); } VALUE m_uc_initialize(VALUE self, VALUE arch, VALUE mode) { @@ -422,3 +427,53 @@ VALUE m_uc_query(VALUE self, VALUE query_mode){ } return INT2NUM(result); } + +VALUE m_uc_context_save(VALUE self){ + uc_err err; + uc_engine *_uc; + Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); + + uc_context *_context; + err = uc_context_alloc(_uc, &_context); + if (err != UC_ERR_OK) { + rb_raise(UcError, "%s", uc_strerror(err)); + } + + err = uc_context_save(_uc, _context); + if (err != UC_ERR_OK) { + rb_raise(UcError, "%s", uc_strerror(err)); + } + + VALUE sc = Data_Wrap_Struct(SavedContext, 0, uc_free, _context); + return sc; +} + +VALUE m_uc_context_update(VALUE self, VALUE context){ + uc_err err; + uc_engine *_uc; + Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); + + uc_context *_context; + Data_Get_Struct(context, uc_context, _context); + + err = uc_context_save(_uc, _context); + if (err != UC_ERR_OK) { + rb_raise(UcError, "%s", uc_strerror(err)); + } + return Qnil; +} + +VALUE m_uc_context_restore(VALUE self, VALUE context){ + uc_err err; + uc_engine *_uc; + Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); + + uc_context *_context; + Data_Get_Struct(context, uc_context, _context); + + err = uc_context_restore(_uc, _context); + if (err != UC_ERR_OK) { + rb_raise(UcError, "%s", uc_strerror(err)); + } + return Qnil; +} diff --git a/bindings/ruby/unicorn_gem/ext/unicorn.h b/bindings/ruby/unicorn_gem/ext/unicorn.h index 05fb2608..a6cd09af 100644 --- a/bindings/ruby/unicorn_gem/ext/unicorn.h +++ b/bindings/ruby/unicorn_gem/ext/unicorn.h @@ -30,4 +30,7 @@ VALUE m_uc_mem_unmap(VALUE self, VALUE address, VALUE size); VALUE m_uc_mem_protect(VALUE self, VALUE address, VALUE size, VALUE perms); VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self); VALUE m_uc_hook_del(VALUE self, VALUE hook); -VALUE m_uc_query(VALUE self, VALUE query_mode); \ No newline at end of file +VALUE m_uc_query(VALUE self, VALUE query_mode); +VALUE m_uc_context_save(VALUE self); +VALUE m_uc_context_update(VALUE self, VALUE context); +VALUE m_uc_context_restore(VALUE self, VALUE context);