From 7c8e6a727dd6e160e7d2408fc1054ae1e3698776 Mon Sep 17 00:00:00 2001 From: david942j Date: Wed, 6 Mar 2019 13:04:10 +0800 Subject: [PATCH] Refactor (#66) * Update README.md * Update docs * Add a test of fetching remote build to ensure full coverage * Refactor and clean code --- README.md | 124 +++++++++++++++-------------- lib/one_gadget/abi.rb | 2 +- lib/one_gadget/emulators/amd64.rb | 2 +- lib/one_gadget/emulators/i386.rb | 2 +- lib/one_gadget/emulators/x86.rb | 60 +++++++------- lib/one_gadget/helper.rb | 80 ++++++++++++------- spec/emulators/amd64_spec.rb | 2 +- spec/emulators/i386_spec.rb | 2 +- spec/emulators/instruction_spec.rb | 10 +-- spec/gadget_spec.rb | 16 +++- spec/helper_spec.rb | 25 +++--- spec/one_gadget_amd64_spec.rb | 18 ++--- spec/one_gadget_i386_spec.rb | 25 +++--- spec/spec_helper.rb | 24 +++--- 14 files changed, 212 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index 6ab054c..0599ed5 100644 --- a/README.md +++ b/README.md @@ -76,95 +76,97 @@ $ one_gadget -b 60131540dadc6796cab33388349e6e4e68692053 # [rsp+0x70] == NULL $ one_gadget /lib32/libc.so.6 -# 0x3a7cc execve("/bin/sh", esp+0x28, environ) -# constraints: -# esi is the GOT address of libc -# [esp+0x28] == NULL -# -# 0x3a7ce execve("/bin/sh", esp+0x2c, environ) -# constraints: -# esi is the GOT address of libc -# [esp+0x2c] == NULL -# -# 0x3a7d2 execve("/bin/sh", esp+0x30, environ) -# constraints: -# esi is the GOT address of libc -# [esp+0x30] == NULL -# -# 0x3a7d9 execve("/bin/sh", esp+0x34, environ) +# 0x3cbea execve("/bin/sh", esp+0x34, environ) # constraints: # esi is the GOT address of libc # [esp+0x34] == NULL # -# 0x5f875 execl("/bin/sh", eax) +# 0x3cbec execve("/bin/sh", esp+0x38, environ) +# constraints: +# esi is the GOT address of libc +# [esp+0x38] == NULL +# +# 0x3cbf0 execve("/bin/sh", esp+0x3c, environ) +# constraints: +# esi is the GOT address of libc +# [esp+0x3c] == NULL +# +# 0x3cbf7 execve("/bin/sh", esp+0x40, environ) +# constraints: +# esi is the GOT address of libc +# [esp+0x40] == NULL +# +# 0x6729f execl("/bin/sh", eax) # constraints: # esi is the GOT address of libc # eax == NULL # -# 0x5f876 execl("/bin/sh", [esp]) +# 0x672a0 execl("/bin/sh", [esp]) # constraints: # esi is the GOT address of libc # [esp] == NULL +# +# 0x13573e execl("/bin/sh", eax) +# constraints: +# ebx is the GOT address of libc +# eax == NULL +# +# 0x13573f execl("/bin/sh", [esp]) +# constraints: +# ebx is the GOT address of libc +# [esp] == NULL $ one_gadget /lib/x86_64-linux-gnu/libc.so.6 -# 0x45526 execve("/bin/sh", rsp+0x30, environ) +# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) # constraints: -# rax == NULL +# rcx == NULL # -# 0x4557a execve("/bin/sh", rsp+0x30, environ) -# constraints: -# [rsp+0x30] == NULL -# -# 0xf1651 execve("/bin/sh", rsp+0x40, environ) +# 0x4f322 execve("/bin/sh", rsp+0x40, environ) # constraints: # [rsp+0x40] == NULL # -# 0xf24cb execve("/bin/sh", rsp+0x60, environ) +# 0x10a38c execve("/bin/sh", rsp+0x70, environ) # constraints: -# [rsp+0x60] == NULL +# [rsp+0x70] == NULL # show all gadgets found $ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --level 1 -# 0x45526 execve("/bin/sh", rsp+0x30, environ) +# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) # constraints: -# rax == NULL +# rcx == NULL # -# 0x4557a execve("/bin/sh", rsp+0x30, environ) -# constraints: -# [rsp+0x30] == NULL -# -# 0xcde41 execve("/bin/sh", r15, r13) -# constraints: -# [r15] == NULL || r15 == NULL -# [r13] == NULL || r13 == NULL -# -# 0xce0e1 execve("/bin/sh", [rbp-0x78], [rbp-0x50]) -# constraints: -# [[rbp-0x78]] == NULL || [rbp-0x78] == NULL -# [[rbp-0x50]] == NULL || [rbp-0x50] == NULL -# -# 0xce0e5 execve("/bin/sh", r9, [rbp-0x50]) -# constraints: -# [r9] == NULL || r9 == NULL -# [[rbp-0x50]] == NULL || [rbp-0x50] == NULL -# -# 0xce0e9 execve("/bin/sh", r9, rdx) -# constraints: -# [r9] == NULL || r9 == NULL -# [rdx] == NULL || rdx == NULL -# -# 0xf1651 execve("/bin/sh", rsp+0x40, environ) +# 0x4f322 execve("/bin/sh", rsp+0x40, environ) # constraints: # [rsp+0x40] == NULL # -# 0xf165d execve("/bin/sh", rsi, [rax]) +# 0xe569f execve("/bin/sh", r14, r12) +# constraints: +# [r14] == NULL || r14 == NULL +# [r12] == NULL || r12 == NULL +# +# 0xe5858 execve("/bin/sh", [rbp-0x88], [rbp-0x70]) +# constraints: +# [[rbp-0x88]] == NULL || [rbp-0x88] == NULL +# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL +# +# 0xe585f execve("/bin/sh", r10, [rbp-0x70]) +# constraints: +# [r10] == NULL || r10 == NULL +# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL +# +# 0xe5863 execve("/bin/sh", r10, rdx) +# constraints: +# [r10] == NULL || r10 == NULL +# [rdx] == NULL || rdx == NULL +# +# 0x10a38c execve("/bin/sh", rsp+0x70, environ) +# constraints: +# [rsp+0x70] == NULL +# +# 0x10a398 execve("/bin/sh", rsi, [rax]) # constraints: # [rsi] == NULL || rsi == NULL # [[rax]] == NULL || [rax] == NULL -# -# 0xf24cb execve("/bin/sh", rsp+0x60, environ) -# constraints: -# [rsp+0x60] == NULL ``` @@ -182,11 +184,11 @@ $ one_gadget ./spec/data/libc-2.19.so -s 'echo "offset ->"' ```ruby require 'one_gadget' OneGadget.gadgets(file: '/lib/x86_64-linux-gnu/libc.so.6') -#=> [283942, 284026, 988753, 992459] +#=> [324293, 324386, 1090444] # or in shorter way one_gadget('/lib/x86_64-linux-gnu/libc.so.6', level: 1) -#=> [283942, 284026, 843329, 844001, 844005, 844009, 988753, 988765, 992459] +#=> [324293, 324386, 939679, 940120, 940127, 940131, 1090444, 1090456] # from build id one_gadget('60131540dadc6796cab33388349e6e4e68692053') diff --git a/lib/one_gadget/abi.rb b/lib/one_gadget/abi.rb index 4e4e3a0..e6f1279 100644 --- a/lib/one_gadget/abi.rb +++ b/lib/one_gadget/abi.rb @@ -1,5 +1,5 @@ module OneGadget - # define the abi of different architecture. + # Defines the abi of different architectures. module ABI # Define class methods here. module ClassMethods diff --git a/lib/one_gadget/emulators/amd64.rb b/lib/one_gadget/emulators/amd64.rb index 511fdc2..0695c0a 100644 --- a/lib/one_gadget/emulators/amd64.rb +++ b/lib/one_gadget/emulators/amd64.rb @@ -1,5 +1,5 @@ -require 'one_gadget/emulators/x86' require 'one_gadget/abi' +require 'one_gadget/emulators/x86' module OneGadget module Emulators diff --git a/lib/one_gadget/emulators/i386.rb b/lib/one_gadget/emulators/i386.rb index b8ff503..d4f902d 100644 --- a/lib/one_gadget/emulators/i386.rb +++ b/lib/one_gadget/emulators/i386.rb @@ -1,5 +1,5 @@ -require 'one_gadget/emulators/x86' require 'one_gadget/abi' +require 'one_gadget/emulators/x86' module OneGadget module Emulators diff --git a/lib/one_gadget/emulators/x86.rb b/lib/one_gadget/emulators/x86.rb index 8cdd8a8..023b7b1 100644 --- a/lib/one_gadget/emulators/x86.rb +++ b/lib/one_gadget/emulators/x86.rb @@ -88,69 +88,69 @@ module OneGadget registers.include?(reg) end - def inst_mov(tar, src) + def inst_mov(dst, src) src = OneGadget::Emulators::Lambda.parse(src, predefined: registers) - if register?(tar) - registers[tar] = src + if register?(dst) + registers[dst] = src else # Just ignore strange case... - return unless tar.include?(sp) + return unless dst.include?(sp) - tar = OneGadget::Emulators::Lambda.parse(tar, predefined: registers) - return if tar.deref_count != 1 # should not happen + dst = OneGadget::Emulators::Lambda.parse(dst, predefined: registers) + return if dst.deref_count != 1 # should not happen - tar.ref! - stack[tar.evaluate(eval_dict)] = src + dst.ref! + stack[dst.evaluate(eval_dict)] = src end end # This instruction moves 128bits. - def inst_movaps(tar, src) + def inst_movaps(dst, src) # XXX: here we only support `movaps [sp+*], xmm*` # TODO: This need an extra constraint: sp & 0xf == 0 - src, tar = check_xmm_sp(src, tar) { raise_unsupported('movaps', tar, src) } - off = tar.evaluate(eval_dict) + src, dst = check_xmm_sp(src, dst) { raise_unsupported('movaps', dst, src) } + off = dst.evaluate(eval_dict) (128 / self.class.bits).times do |i| stack[off + i * size_t] = src[i] end end - # Mov *src to tar[:64] - def inst_movq(tar, src) + # Mov *src to dst[:64] + def inst_movq(dst, src) # XXX: here we only support `movq xmm*, [sp+*]` - tar, src = check_xmm_sp(tar, src) { raise_unsupported('movq', tar, src) } + dst, src = check_xmm_sp(dst, src) { raise_unsupported('movq', dst, src) } off = src.evaluate(eval_dict) (64 / self.class.bits).times do |i| - tar[i] = stack[off + i * size_t] + dst[i] = stack[off + i * size_t] end end - # Move *src to tar[64:128] - def inst_movhps(tar, src) + # Move *src to dst[64:128] + def inst_movhps(dst, src) # XXX: here we only support `movhps xmm*, [sp+*]` - tar, src = check_xmm_sp(tar, src) { raise_unsupported('movhps', tar, src) } + dst, src = check_xmm_sp(dst, src) { raise_unsupported('movhps', dst, src) } off = src.evaluate(eval_dict) (64 / self.class.bits).times do |i| - tar[i + 64 / self.class.bits] = stack[off + i * size_t] + dst[i + 64 / self.class.bits] = stack[off + i * size_t] end end - # check if (tar, src) in form (xmm*, [sp+*]) - def check_xmm_sp(tar, src) - return yield unless tar.start_with?('xmm') && register?(tar) && src.include?(sp) + # check if (dst, src) in form (xmm*, [sp+*]) + def check_xmm_sp(dst, src) + return yield unless dst.start_with?('xmm') && register?(dst) && src.include?(sp) - tar_lm = OneGadget::Emulators::Lambda.parse(tar, predefined: registers) + dst_lm = OneGadget::Emulators::Lambda.parse(dst, predefined: registers) src_lm = OneGadget::Emulators::Lambda.parse(src, predefined: registers) return yield if src_lm.deref_count != 1 src_lm.ref! - [tar_lm, src_lm] + [dst_lm, src_lm] end - def inst_lea(tar, src) + def inst_lea(dst, src) src = OneGadget::Emulators::Lambda.parse(src, predefined: registers) src.ref! - registers[tar] = src + registers[dst] = src end def inst_push(val) @@ -170,16 +170,16 @@ module OneGadget registers[dst] = 0 end - def inst_add(tar, src) + def inst_add(dst, src) src = OneGadget::Emulators::Lambda.parse(src, predefined: registers) - registers[tar] += src + registers[dst] += src end - def inst_sub(tar, src) + def inst_sub(dst, src) src = OneGadget::Emulators::Lambda.parse(src, predefined: registers) raise Error::ArgumentError, "Can't handle -= of type #{src.class}" unless src.is_a?(Integer) - registers[tar] -= src + registers[dst] -= src end # yap, nop diff --git a/lib/one_gadget/helper.rb b/lib/one_gadget/helper.rb index b375fac..b1f29f4 100644 --- a/lib/one_gadget/helper.rb +++ b/lib/one_gadget/helper.rb @@ -15,7 +15,7 @@ module OneGadget module_function - # Verify if `build_id` is a valid SHA1 hex format. + # Checks if +build_id+ is a valid SHA1 hex format. # @param [String] build_id # BuildID. # @raise [Error::ArgumentError] @@ -40,7 +40,7 @@ module OneGadget # @param [String] path Relative path. # @return [String] Absolute path, with symlink resolved. # @example - # abspath('/lib/x86_64-linux-gnu/libc.so.6') + # Helper.abspath('/lib/x86_64-linux-gnu/libc.so.6') # #=> '/lib/x86_64-linux-gnu/libc-2.23.so' def abspath(path) Pathname.new(File.expand_path(path)).realpath.to_s @@ -51,10 +51,10 @@ module OneGadget # @param [String] path Path to target file. # @return [Boolean] If the file is an ELF or not. # @example - # valid_elf_file?('/etc/passwd') - # => false - # valid_elf_file?('/lib64/ld-linux-x86-64.so.2') - # => true + # Helper.valid_elf_file?('/etc/passwd') + # #=> false + # Helper.valid_elf_file?('/lib64/ld-linux-x86-64.so.2') + # #=> true def valid_elf_file?(path) # A light-weight way to check if is a valid ELF file # Checks at least one phdr should present. @@ -64,7 +64,8 @@ module OneGadget false end - # Checks if the file of given path is a valid ELF file + # Checks if the file of given path is a valid ELF file. + # # An error message will be shown if given path is not a valid ELF. # # @param [String] path Path to target file. @@ -80,7 +81,7 @@ module OneGadget # @param [String] path Absolute file path. # @return [String] Target build id. # @example - # build_id_of('/lib/x86_64-linux-gnu/libc-2.23.so') + # Helper.build_id_of('/lib/x86_64-linux-gnu/libc-2.23.so') # #=> '60131540dadc6796cab33388349e6e4e68692053' def build_id_of(path) File.open(path) { |f| ELFTools::ELFFile.new(f).build_id } @@ -125,7 +126,7 @@ module OneGadget end # Fetch the latest release version's tag name. - # @return [String] The tag name, in form +vx.x.x+. + # @return [String] The tag name, in form +vX.X.X+. def latest_tag releases_url = 'https://github.com/david942j/one_gadget/releases/latest' @latest_tag ||= url_request(releases_url).split('/').last @@ -145,7 +146,6 @@ module OneGadget # @return [Tempfile] The temp file be created. def download_build(file) temp = Tempfile.new(['gadgets', file + '.rb']) - url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb'))) temp.write(url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb')))) temp.tap(&:close) end @@ -160,7 +160,7 @@ module OneGadget # @param [String] url The url. # @return [String] # The request response body. - # If the response is '302 Found', return the location in header. + # If the response is +302 Found+, returns the location in header. def url_request(url) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) @@ -178,10 +178,13 @@ module OneGadget nil end - # Fetch the file archiecture of +file+. + # Fetch the ELF archiecture of +file+. # @param [String] file The target ELF filename. # @return [Symbol] - # Only supports architecture amd64 and i386 now. + # Currently supports amd64, i386, arm, aarch64, and mips. + # @example + # Helper.architecture('/bin/cat') + # #=> :amd64 def architecture(file) return :invalid unless File.exist?(file) @@ -201,35 +204,58 @@ module OneGadget end # Present number in hex format. - # @param [Integer] val The number. - # @param [Boolean] psign Need to show plus sign when +val >= 0+. - # @return [String] string in hex format. + # @param [Integer] val + # The number. + # @param [Boolean] psign + # If needs to show the plus sign when +val >= 0+. + # @return [String] + # String in hex format. # @example - # hex(32) #=> 0x20 - # hex(32, psign: true) #=> +0x20 - # hex(-40) #=> -0x28 - # hex(0) #=> 0x0 - # hex(0, psign: true) #=> +0x0 + # Helper.hex(32) #=> '0x20' + # Helper.hex(32, psign: true) #=> '+0x20' + # Helper.hex(-40) #=> '-0x28' + # Helper.hex(0) #=> '0x0' + # Helper.hex(0, psign: true) #=> '+0x0' def hex(val, psign: false) return format("#{psign ? '+' : ''}0x%x", val) if val >= 0 format('-0x%x', -val) end - # For checking a string is actually an integer. - # @param [String] str String to be checked. - # @return [Boolean] If +str+ can be converted into integer. + # Checks if a string can be converted into an integer. + # @param [String] str + # String to be checked. + # @return [Boolean] + # If +str+ can be converted into an integer. # @example # Helper.integer? '1234' - # # => true + # #=> true # Helper.integer? '0x1234' - # # => true + # #=> true # Helper.integer? '0xheapoverflow' - # # => false + # #=> false def integer?(str) true if Integer(str) rescue ArgumentError, TypeError false end + + # Cross-platform way of finding an executable in +$PATH+. + # + # @param [String] cmd + # @return [String?] + # @example + # Helper.which('ruby') + # #=> "/usr/bin/ruby" + def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each do |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + end + end + nil + end end end diff --git a/spec/emulators/amd64_spec.rb b/spec/emulators/amd64_spec.rb index a788c33..b165f55 100644 --- a/spec/emulators/amd64_spec.rb +++ b/spec/emulators/amd64_spec.rb @@ -2,7 +2,7 @@ require 'one_gadget/emulators/amd64' describe OneGadget::Emulators::Amd64 do before(:each) do - @processor = OneGadget::Emulators::Amd64.new + @processor = described_class.new end describe 'process' do diff --git a/spec/emulators/i386_spec.rb b/spec/emulators/i386_spec.rb index c0be142..3cc8603 100644 --- a/spec/emulators/i386_spec.rb +++ b/spec/emulators/i386_spec.rb @@ -2,7 +2,7 @@ require 'one_gadget/emulators/i386' describe OneGadget::Emulators::I386 do before(:each) do - @processor = OneGadget::Emulators::I386.new + @processor = described_class.new end describe 'process' do diff --git a/spec/emulators/instruction_spec.rb b/spec/emulators/instruction_spec.rb index 580170f..63bfcdf 100644 --- a/spec/emulators/instruction_spec.rb +++ b/spec/emulators/instruction_spec.rb @@ -17,20 +17,20 @@ describe OneGadget::Emulators::Instruction do it 'fetch_args' do expect(@mov.fetch_args(<<-'EOS')).to eq ['rax', '[rip+0x2c16b4]'] - d67e5: 48 8b 05 b4 16 2c 00 mov rax,QWORD PTR [rip+0x2c16b4] # 397ea0 <_DYNAMIC+0x340> + d67e5: 48 8b 05 b4 16 2c 00 mov rax,QWORD PTR [rip+0x2c16b4] # 397ea0 <_DYNAMIC+0x340> EOS expect(@mov.fetch_args(<<-'EOS')).to eq ['rdx', '[rax]'] - d67f8: 48 8b 10 mov rdx,QWORD PTR [rax] + d67f8: 48 8b 10 mov rdx,QWORD PTR [rax] EOS expect { @mov.fetch_args('mov a, b, c') }.to raise_error(OneGadget::Error::ArgumentError) expect(@lea.fetch_args(<<-'EOS')).to eq ['rsi', '[rsp+0x70]'] - d67ec: 48 8d 74 24 70 lea rsi,[rsp+0x70] + d67ec: 48 8d 74 24 70 lea rsi,[rsp+0x70] EOS expect(@lea.fetch_args(<<-'EOS')).to eq ['rdi', '[rip+0x8ab61]'] - d67f1: 48 8d 3d 61 ab 08 00 lea rdi,[rip+0x8ab61] # 161359 <_nl_POSIX_name+0x154> + d67f1: 48 8d 3d 61 ab 08 00 lea rdi,[rip+0x8ab61] # 161359 <_nl_POSIX_name+0x154> EOS expect(@call.fetch_args(<<-'EOS')).to eq ['b8470 '] - d67fb: e8 70 1c fe ff call b8470 + d67fb: e8 70 1c fe ff call b8470 EOS end end diff --git a/spec/gadget_spec.rb b/spec/gadget_spec.rb index 14d5ed9..aee387b 100644 --- a/spec/gadget_spec.rb +++ b/spec/gadget_spec.rb @@ -1,16 +1,17 @@ +require 'tempfile' + require 'one_gadget/gadget' require 'one_gadget/helper' describe OneGadget::Gadget do before(:all) do @build_id = 'fake_id' - OneGadget::Helper.color_off! # disable colorize for easy testing. OneGadget::Gadget.add(@build_id, 0x1234, constraints: ['[rsp+0x30] == NULL', 'rax == 0'], effect: 'execve("/bin/sh", rsp+0x30, rax)') end after(:all) do - OneGadget::Gadget::ClassMethods::BUILDS.delete @build_id + OneGadget::Gadget::ClassMethods::BUILDS.delete(@build_id) end it 'inspect' do @@ -22,6 +23,17 @@ constraints: EOS end + it 'remote' do + id = 'remote_has_this' + allow(OneGadget::Helper).to receive(:remote_builds).and_return([id]) + allow(OneGadget::Helper).to receive(:download_build).with(id) { Tempfile.new(['remote', '.rb']) } + expect { hook_logger { described_class.builds(id) } }.to output(<<-EOS).to_stdout +[OneGadget] The desired one-gadget can be found in lastest version! + Update with: $ gem update one_gadget && gem cleanup one_gadget + EOS + OneGadget::Gadget::ClassMethods::BUILDS.delete(id) + end + context 'builds_info' do it 'normal' do expect(described_class.builds_info('58c735bc7b19b0aeb395cce70cf63bd62ac75e4a').join("\n")).to eq <<-EOS.strip diff --git a/spec/helper_spec.rb b/spec/helper_spec.rb index 8d184fe..0622006 100644 --- a/spec/helper_spec.rb +++ b/spec/helper_spec.rb @@ -2,44 +2,45 @@ require 'one_gadget/helper' describe OneGadget::Helper do before(:all) do - @libcpath = File.join(__dir__, 'data', 'libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so') + @libcpath = data_path('libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so') end + it 'abspath' do - expect(OneGadget::Helper.abspath('./spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so')) + expect(described_class.abspath('./spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so')) .to eq @libcpath end it 'valid_elf_file?' do - expect(OneGadget::Helper.valid_elf_file?(__FILE__)).to be false - expect(OneGadget::Helper.valid_elf_file?(@libcpath)).to be true + expect(described_class.valid_elf_file?(__FILE__)).to be false + expect(described_class.valid_elf_file?(@libcpath)).to be true end it 'build_id_of' do - expect(OneGadget::Helper.build_id_of(@libcpath)).to eq '60131540dadc6796cab33388349e6e4e68692053' + expect(described_class.build_id_of(@libcpath)).to eq '60131540dadc6796cab33388349e6e4e68692053' end it 'colorize' do allow(described_class).to receive(:color_enabled?).and_return(true) - expect(OneGadget::Helper.colorize('123', sev: :integer)).to eq "\e[38;5;189m123\e[0m" + expect(described_class.colorize('123', sev: :integer)).to eq "\e[38;5;189m123\e[0m" end it 'url_request' do val = :val - expect { hook_logger { val = OneGadget::Helper.url_request('oao') } }.to output(<<-EOS.strip).to_stdout + expect { hook_logger { val = described_class.url_request('oao') } }.to output(<<-EOS.strip).to_stdout [OneGadget] undefined method `request_uri' for # EOS expect(val).to be_nil end it 'architecture' do - expect(OneGadget::Helper.architecture(@libcpath)).to be :amd64 - expect(OneGadget::Helper.architecture(__FILE__)).to be :invalid - expect(OneGadget::Helper.architecture(File.join(__dir__, 'data', 'aarch64-libc-2.24.so'))).to be :aarch64 - # Just use for test unknown =~ = + expect(described_class.architecture(@libcpath)).to be :amd64 + expect(described_class.architecture(__FILE__)).to be :invalid + expect(described_class.architecture(data_path('aarch64-libc-2.24.so'))).to be :aarch64 + # for testing 'unknown' Tempfile.create(['tmp', '.elf']) do |f| f.write("\x7fELF\x02\x01\x01" + "\x00" * 9 + "\x01" * 48) f.close - expect(OneGadget::Helper.architecture(f.path)).to be :unknown + expect(described_class.architecture(f.path)).to be :unknown end end end diff --git a/spec/one_gadget_amd64_spec.rb b/spec/one_gadget_amd64_spec.rb index 1b44cc4..92b28ac 100644 --- a/spec/one_gadget_amd64_spec.rb +++ b/spec/one_gadget_amd64_spec.rb @@ -1,38 +1,32 @@ -require 'mkmf' - require 'one_gadget' require 'one_gadget/error' describe 'one_gadget_amd64' do - before(:all) do - @data_path = ->(file) { File.join(__dir__, 'data', file) } - end - describe 'from file' do before(:each) do - skip 'binutils not installed' if find_executable0('objdump').nil? + skip_unless_objdump end it 'libc-2.19' do - path = @data_path['libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so'] + path = data_path('libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so') expect(OneGadget.gadgets(file: path, force_file: true, level: 1)) .to eq [0x46428, 0x4647c, 0xc1ba3, 0xc1bf2, 0xe4968, 0xe5765, 0xe5771, 0xe66bd] end it 'libc-2.24' do - path = @data_path['libc-2.24-8cba3297f538691eb1875be62986993c004f3f4d.so'] + path = data_path('libc-2.24-8cba3297f538691eb1875be62986993c004f3f4d.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x3f356, 0x3f3aa, 0xd67e5] expect(one_gadget(path)).to eq OneGadget.gadgets(file: path) end it 'libc-2.26' do - path = @data_path['libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.so'] + path = data_path('libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x47c46, 0x47c9a, 0xfccde, 0xfdb8e] expect(one_gadget(path)).to eq OneGadget.gadgets(file: path) end it 'libc-2.27' do - path = @data_path['libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.so'] + path = data_path('libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x4f2c5, 0x4f322, 0x10a38c] expect(one_gadget(path)).to eq OneGadget.gadgets(file: path) end @@ -72,11 +66,9 @@ describe 'one_gadget_amd64' do it 'fetch from remote' do entry = OneGadget::Gadget::ClassMethods::BUILDS.delete(@build_id) - OneGadget::Gadget::ClassMethods::BUILDS[:a] = 1 # silence the logger allow(OneGadget::Logger).to receive(:ask_update) expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty - OneGadget::Gadget::ClassMethods::BUILDS.delete(:a) OneGadget::Gadget::ClassMethods::BUILDS[@build_id] = entry unless entry.nil? end diff --git a/spec/one_gadget_i386_spec.rb b/spec/one_gadget_i386_spec.rb index 4586405..c3a63cd 100644 --- a/spec/one_gadget_i386_spec.rb +++ b/spec/one_gadget_i386_spec.rb @@ -1,47 +1,44 @@ -require 'mkmf' - require 'one_gadget' describe 'one_gadget_i386' do - before(:each) do - @build_id = '926eb99d49cab2e5622af38ab07395f5b32035e9' - @data_path = ->(file) { File.join(__dir__, 'data', file) } - end - describe 'from file' do before(:each) do - skip 'binutils not installed' if find_executable0('objdump').nil? + skip_unless_objdump end it 'libc-2.19' do - path = @data_path['libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so'] + path = data_path('libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x3fd27, 0x64c64, 0x64c6a, 0x64c6e] end it 'libc-2.23' do ans = [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6] - path = @data_path['libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so'] + path = data_path('libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans end it 'libc-2.26' do ans = [0x3cc2f, 0x3cc31, 0x3cc35, 0x3cc3c, 0x66e7f, 0x66e80, 0x132fbe, 0x132fbf] - path = @data_path['libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.so'] + path = data_path('libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans end it 'libc-2.27' do ans = [0x3cbea, 0x3cbec, 0x3cbf0, 0x3cbf7, 0x6729f, 0x672a0, 0x13573e, 0x13573f] - path = @data_path['libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.so'] + path = data_path('libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.so') expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans end it 'special filename' do - path = File.join(__dir__, 'data', 'filename$with+special&keys') - expect(OneGadget.gadgets(file: path)).not_to be_empty + expect(OneGadget.gadgets(file: data_path('filename$with+special&keys'))).not_to be_empty end end + describe 'from build id' do + before(:all) do + @build_id = '926eb99d49cab2e5622af38ab07395f5b32035e9' + end + it 'normal' do # only check not empty because the gadgets might add frequently. expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index aa8cf67..3ff3681 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ -require 'one_gadget/helper' require 'simplecov' +require 'one_gadget/helper' +require 'one_gadget/logger' + SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( [SimpleCov::Formatter::HTMLFormatter] ) @@ -9,17 +11,17 @@ SimpleCov.start do end module Helpers - def hook_logger(&_block) - require 'one_gadget/logger' + def hook_logger + OneGadget::Logger.instance_variable_get(:@logger).reopen($stdout) + yield + end - # no method 'reopen' before ruby 2.3 - org_logger = OneGadget::Logger.instance_variable_get(:@logger) - new_logger = ::Logger.new($stdout) - new_logger.formatter = org_logger.formatter - OneGadget::Logger.instance_variable_set(:@logger, new_logger) - ret = yield - OneGadget::Logger.instance_variable_set(:@logger, org_logger) - ret + def skip_unless_objdump + skip 'binutils not installed' if OneGadget::Helper.which('objdump').nil? + end + + def data_path(file) + File.join(__dir__, 'data', file) end end