diff --git a/bindings/ruby/sample_arm.rb b/bindings/ruby/sample_arm.rb new file mode 100644 index 00000000..dd0e4a06 --- /dev/null +++ b/bindings/ruby/sample_arm.rb @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby + +require 'unicorn' +require 'unicorn/arm_const' + +include Unicorn + +# code to be emulated +ARM_CODE = "\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3 +THUMB_CODE = "\x83\xb0" # sub sp, #0xc +# memory address where emulation starts +ADDRESS = 0x10000 + + +# callback for tracing basic blocks +$hook_block = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size]) +end + + +# callback for tracing instructions +$hook_code = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size]) +end + + +# Test ARM +def test_arm() + puts("Emulate ARM code") + begin + # Initialize emulator in ARM mode + mu = Uc.new UC_ARCH_ARM, UC_MODE_ARM + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, ARM_CODE) + + # initialize machine registers + mu.reg_write(UC_ARM_REG_R0, 0x1234) + mu.reg_write(UC_ARM_REG_R2, 0x6789) + mu.reg_write(UC_ARM_REG_R3, 0x3333) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + ARM_CODE.bytesize) + + # now print out some registers + puts(">>> Emulation done. Below is the CPU context") + + r0 = mu.reg_read(UC_ARM_REG_R0) + r1 = mu.reg_read(UC_ARM_REG_R1) + puts(">>> R0 = 0x%x" % r0) + puts(">>> R1 = 0x%x" % r1) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +def test_thumb() + puts("Emulate THUMB code") + begin + # Initialize emulator in thumb mode + mu = Uc.new UC_ARCH_ARM, UC_MODE_THUMB + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, THUMB_CODE) + + # initialize machine registers + mu.reg_write(UC_ARM_REG_SP, 0x1234) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + THUMB_CODE.bytesize) + + # now print out some registers + puts(">>> Emulation done. Below is the CPU context") + + sp = mu.reg_read(UC_ARM_REG_SP) + puts(">>> SP = 0x%x" % sp) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +test_arm() +puts("=" * 20) +test_thumb() diff --git a/bindings/ruby/sample_arm64.rb b/bindings/ruby/sample_arm64.rb new file mode 100644 index 00000000..45b0c9c6 --- /dev/null +++ b/bindings/ruby/sample_arm64.rb @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby +# Sample code for ARM64 of Unicorn. Nguyen Anh Quynh +# Ruby sample ported by Sascha Schirra +require 'unicorn' +require 'unicorn/arm64_const' + +include Unicorn + +# code to be emulated +ARM64_CODE = "\xab\x01\x0f\x8b" #add x11, x13, x15 + +# memory address where emulation starts +ADDRESS = 0x10000 + + +# callback for tracing basic blocks +$hook_block = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size]) +end + + +# callback for tracing instructions +$hook_code = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size]) +end + + +# Test ARM64 +def test_arm64() + puts("Emulate ARM64 code") + begin + # Initialize emulator in ARM mode + mu = Uc.new UC_ARCH_ARM64, UC_MODE_ARM + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, ARM64_CODE) + + # initialize machine registers + mu.reg_write(UC_ARM64_REG_X11, 0x1234) + mu.reg_write(UC_ARM64_REG_X13, 0x6789) + mu.reg_write(UC_ARM64_REG_X15, 0x3333) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + ARM64_CODE.bytesize) + + # now print out some registers + puts(">>> Emulation done. Below is the CPU context") + + x11 = mu.reg_read(UC_ARM64_REG_X11) + x13 = mu.reg_read(UC_ARM64_REG_X13) + x15 = mu.reg_read(UC_ARM64_REG_X15) + puts(">>> X11 = 0x%x" % x11) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +test_arm64() diff --git a/bindings/ruby/sample_m68k.rb b/bindings/ruby/sample_m68k.rb new file mode 100644 index 00000000..1acff39f --- /dev/null +++ b/bindings/ruby/sample_m68k.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env ruby +# Sample code for ARM of Unicorn. Nguyen Anh Quynh +# Ruby sample ported by Sascha Schirra + +require 'unicorn' +require 'unicorn/m68k_const' + +include Unicorn + +# code to be emulated +M68K_CODE = "\x76\xed" # movq #-19, %d3 +# memory address where emulation starts +ADDRESS = 0x10000 + + +# callback for tracing basic blocks +$hook_block = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size]) +end + + +# callback for tracing instructions +$hook_code = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size]) +end + + +# Test m68k +def test_m68k() + puts("Emulate M68K code") + begin + # Initialize emulator in m68k mode + mu = Uc.new UC_ARCH_M68K, UC_MODE_BIG_ENDIAN + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, M68K_CODE) + + # initialize machine registers + mu.reg_write(UC_M68K_REG_D3, 0x1234) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + M68K_CODE.bytesize) + + # now print out some registers + puts(">>> Emulation done. Below is the CPU context") + + d3 = mu.reg_read(UC_M68K_REG_D3) + puts(">>> D3 = 0x%x" % d3) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +test_m68k() diff --git a/bindings/ruby/sample_mips.rb b/bindings/ruby/sample_mips.rb new file mode 100644 index 00000000..ec30e87d --- /dev/null +++ b/bindings/ruby/sample_mips.rb @@ -0,0 +1,104 @@ +#!/usr/bin/env ruby +# Sample code for MIPS of Unicorn. Nguyen Anh Quynh +# Ruby sample ported by Sascha Schirra +require 'unicorn' +require 'unicorn/mips_const' + +include Unicorn + +# code to be emulated +MIPS_CODE_EB = "\x34\x21\x34\x56" # ori $at, $at, 0x3456; +MIPS_CODE_EL = "\x56\x34\x21\x34" # ori $at, $at, 0x3456; + +# memory address where emulation starts +ADDRESS = 0x10000 + + +# callback for tracing basic blocks +$hook_block = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size]) +end + + +# callback for tracing instructions +$hook_code = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size]) +end + +# Test MIPS EB +def test_mips_eb() + puts("Emulate MIPS code (big-endian)") + begin + # Initialize emulator in MIPS32 + EB mode + mu = Uc.new UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, MIPS_CODE_EB) + + # initialize machine registers + mu.reg_write(UC_MIPS_REG_1, 0x6789) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + MIPS_CODE_EB.bytesize) + + # now puts out some registers + puts(">>> Emulation done. Below is the CPU context") + + r1 = mu.reg_read(UC_MIPS_REG_1) + puts(">>> r1 = 0x%x" % r1) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +# Test MIPS EL +def test_mips_el() + puts("Emulate MIPS code (little-endian)") + begin + # Initialize emulator in MIPS32 + EL mode + mu = Uc.new UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, MIPS_CODE_EL) + + # initialize machine registers + mu.reg_write(UC_MIPS_REG_1, 0x6789) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + MIPS_CODE_EL.bytesize) + + # now puts out some registers + puts(">>> Emulation done. Below is the CPU context") + + r1 = mu.reg_read(UC_MIPS_REG_1) + puts(">>> r1 = 0x%x" % r1) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +test_mips_eb() +puts("=" * 20) +test_mips_el() diff --git a/bindings/ruby/sample_sparc.rb b/bindings/ruby/sample_sparc.rb new file mode 100644 index 00000000..052be189 --- /dev/null +++ b/bindings/ruby/sample_sparc.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env ruby +# Sample code for SPARC of Unicorn. Nguyen Anh Quynh +# Ruby sample ported by Sascha Schirra +require 'unicorn' +require 'unicorn/sparc_const' + +include Unicorn + +# code to be emulated +SPARC_CODE = "\x86\x00\x40\x02" # add %g1, %g2, %g3; +# memory address where emulation starts +ADDRESS = 0x10000 + + +# callback for tracing basic blocks +$hook_block = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size]) +end + + +# callback for tracing instructions +$hook_code = Proc.new do |uc, address, size, user_data| + puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size]) +end + +# Test SPARC +def test_sparc() + puts("Emulate SPARC code") + begin + # Initialize emulator in SPARC EB mode + mu = Uc.new UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, SPARC_CODE) + + # initialize machine registers + mu.reg_write(UC_SPARC_REG_G1, 0x1230) + mu.reg_write(UC_SPARC_REG_G2, 0x6789) + mu.reg_write(UC_SPARC_REG_G3, 0x5555) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, $hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, $hook_code) + + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + SPARC_CODE.bytesize) + + # now puts out some registers + puts(">>> Emulation done. Below is the CPU context") + + g3 = mu.reg_read(UC_SPARC_REG_G3) + puts(">>> G3 = 0x%x" %g3) + + rescue UcError => e + puts("ERROR: %s" % e) + end +end + + +test_sparc() diff --git a/bindings/ruby/sample_x86_gdt.rb b/bindings/ruby/sample_x86_gdt.rb new file mode 100644 index 00000000..ea6c9431 --- /dev/null +++ b/bindings/ruby/sample_x86_gdt.rb @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby +require 'unicorn' +require 'unicorn/x86_const' + +include Unicorn + +F_GRANULARITY = 0x8 +F_PROT_32 = 0x4 +F_LONG = 0x2 +F_AVAILABLE = 0x1 + +A_PRESENT = 0x80 + +A_PRIV_3 = 0x60 +A_PRIV_2 = 0x40 +A_PRIV_1 = 0x20 +A_PRIV_0 = 0x0 + +A_CODE = 0x10 +A_DATA = 0x10 +A_TSS = 0x0 +A_GATE = 0x0 + +A_DATA_WRITABLE = 0x2 +A_CODE_READABLE = 0x2 + +A_DIR_CON_BIT = 0x4 + +S_GDT = 0x0 +S_LDT = 0x4 +S_PRIV_3 = 0x3 +S_PRIV_2 = 0x2 +S_PRIV_1 = 0x1 +S_PRIV_0 = 0x0 + +def create_selector(idx, flags) + to_ret = flags + to_ret |= idx << 3 + return to_ret +end + +def create_gdt_entry(base, limit, access, flags) + + to_ret = limit & 0xffff; + to_ret |= (base & 0xffffff) << 16; + to_ret |= (access & 0xff) << 40; + to_ret |= ((limit >> 16) & 0xf) << 48; + to_ret |= (flags & 0xff) << 52; + to_ret |= ((base >> 24) & 0xff) << 56; + return [to_ret].pack('Q') +end + +def write_gdt(uc, gdt, mem) + gdt.each_index do |idx| + offset = idx * GDT_ENTRY_SIZE + uc.mem_write(mem + offset, gdt[idx]) + end +end + +CODE_ADDR = 0x40000 +CODE_SIZE = 0x1000 + +GDT_ADDR = 0x3000 +GDT_LIMIT = 0x1000 +GDT_ENTRY_SIZE = 0x8 + +GS_SEGMENT_ADDR = 0x5000 +GS_SEGMENT_SIZE = 0x1000 + +uc = Uc.new UC_ARCH_X86, UC_MODE_32 + +uc.mem_map(GDT_ADDR, GDT_LIMIT) +uc.mem_map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE) +uc.mem_map(CODE_ADDR, CODE_SIZE) + +gdt = Array.new (31) {|i| create_gdt_entry(0,0,0,0)} +gdt[15] = create_gdt_entry(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) +gdt[16] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) # Data Segment +gdt[17] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_CODE | A_CODE_READABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) # Code Segment +gdt[18] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_0 | A_DIR_CON_BIT, F_PROT_32) # Stack Segment + +write_gdt(uc, gdt, GDT_ADDR) +uc.reg_write(UC_X86_REG_GDTR, [0, GDT_ADDR, gdt.length * GDT_ENTRY_SIZE-1, 0x0]) + +selector = create_selector(15, S_GDT | S_PRIV_3) +uc.reg_write(UC_X86_REG_GS, selector) + +selector = create_selector(16, S_GDT | S_PRIV_3) +uc.reg_write(UC_X86_REG_DS, selector) + +selector = create_selector(17, S_GDT | S_PRIV_3) +uc.reg_write(UC_X86_REG_CS, selector) + +selector = create_selector(18, S_GDT | S_PRIV_0) +uc.reg_write(UC_X86_REG_SS, selector) + + diff --git a/bindings/ruby/unicorn_gem/ext/unicorn.c b/bindings/ruby/unicorn_gem/ext/unicorn.c index 8681d4ae..c7979343 100644 --- a/bindings/ruby/unicorn_gem/ext/unicorn.c +++ b/bindings/ruby/unicorn_gem/ext/unicorn.c @@ -367,7 +367,6 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){ err = uc_hook_add(_uc, &trace, htype, cb_hook_insn_syscall,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end), NUM2INT(arg1)); break; } - err = uc_hook_add(_uc, &trace, htype, cb_hook_intr,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); } else if(htype == UC_HOOK_INTR){ err = uc_hook_add(_uc, &trace, htype, cb_hook_intr,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); diff --git a/bindings/ruby/unicorn_gem/pkg/.gitignore b/bindings/ruby/unicorn_gem/pkg/.gitignore index e394af59..b7e77251 100644 --- a/bindings/ruby/unicorn_gem/pkg/.gitignore +++ b/bindings/ruby/unicorn_gem/pkg/.gitignore @@ -7,4 +7,4 @@ /pkg/ /spec/reports/ /tmp/ -.gem +*.gem diff --git a/bindings/ruby/unicorn_gem/pkg/unicorn-0.9.0.gem b/bindings/ruby/unicorn_gem/pkg/unicorn-0.9.0.gem deleted file mode 100644 index 57b7a6b3..00000000 Binary files a/bindings/ruby/unicorn_gem/pkg/unicorn-0.9.0.gem and /dev/null differ diff --git a/bindings/ruby/unicorn_gem/unicorn.gemspec b/bindings/ruby/unicorn_gem/unicorn.gemspec index 18f38bb8..67640837 100644 --- a/bindings/ruby/unicorn_gem/unicorn.gemspec +++ b/bindings/ruby/unicorn_gem/unicorn.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |spec| spec.version = Unicorn::VERSION spec.authors = ["Sascha Schirra"] spec.email = ["sashs@scoding.de"] - spec.license = GPLv2 + spec.license = 'GPL-2.0' spec.summary = %q{Ruby binding for Unicorn-Engine} spec.description = %q{Ruby binding for Unicorn-Engine } spec.homepage = "https://unicorn-engine.org"