From 5682c080f57275258bf2200bee3571a86aa860bc Mon Sep 17 00:00:00 2001 From: david942j Date: Thu, 9 Feb 2017 11:59:19 +0800 Subject: [PATCH] refactor builds' format --- .rubocop.yml | 3 ++ lib/one_gadget.rb | 12 +++-- lib/one_gadget/abi.rb | 1 + ...0131540dadc6796cab33388349e6e4e68692053.rb | 13 ----- ...0131540dadc6796cab33388349e6e4e68692053.rb | 6 +++ lib/one_gadget/fetcher.rb | 8 ++- lib/one_gadget/gadget.rb | 54 ++++++++++++------- lib/one_gadget/helper.rb | 4 +- spec/gadget_spec.rb | 9 ++-- spec/one_gadget_spec.rb | 6 ++- 10 files changed, 70 insertions(+), 46 deletions(-) delete mode 100644 lib/one_gadget/builds/60131540dadc6796cab33388349e6e4e68692053.rb create mode 100644 lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb diff --git a/.rubocop.yml b/.rubocop.yml index 92efd55..609cf35 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,3 +13,6 @@ Rails: Metrics/LineLength: Enabled: true Max: 120 + +Style/FileName: + Enabled: false diff --git a/lib/one_gadget.rb b/lib/one_gadget.rb index 45e1e60..368a2c9 100644 --- a/lib/one_gadget.rb +++ b/lib/one_gadget.rb @@ -3,12 +3,16 @@ # @author david942j module OneGadget class << self - # The man entry of gem +one_gadget+ - # @option [String] file + # The man entry of gem +one_gadget+. + # If want to find gadgets from file, it will search gadgets by its + # build id first. + # + # @param [String] filepath # The relative path of +libc.so.6+. - # @option [String] build_id + # @param [String] build_id # The BuildID of target +libc.so.6+. - # @return [Array] + # @return [Array, Array] + # The gadgets found. # @example # OneGadget.gadgets(filepath: './libc.so.6') # OneGadget.gadgets(build_id: '60131540dadc6796cab33388349e6e4e68692053') diff --git a/lib/one_gadget/abi.rb b/lib/one_gadget/abi.rb index 0f9371b..445e283 100644 --- a/lib/one_gadget/abi.rb +++ b/lib/one_gadget/abi.rb @@ -5,6 +5,7 @@ module OneGadget module ClassMethods LINUX_X86_32 = %w(eax ebx ecx edx edi esi ebp esp).freeze LINUX_X86_64 = LINUX_X86_32 + %w(rax rbx rcx rdx rdi rsi rbp rsp) + 7.upto(15).map { |i| "r#{i}" } + # Only support x86-64 now. def registers LINUX_X86_64 end diff --git a/lib/one_gadget/builds/60131540dadc6796cab33388349e6e4e68692053.rb b/lib/one_gadget/builds/60131540dadc6796cab33388349e6e4e68692053.rb deleted file mode 100644 index 0c2175a..0000000 --- a/lib/one_gadget/builds/60131540dadc6796cab33388349e6e4e68692053.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'one_gadget/gadget' -# Ubuntu GLIBC 2.23-0ubuntu5 -# ELF 64-bit LSB shared object, x86-64 -build_id = File.basename(__FILE__, '.rb') -OneGadget::Gadget.define(build_id) do |g| - g.offset = 0x4526a - g.constraints = ['[rsp+0x30]=NULL'] -end - -OneGadget::Gadget.define(build_id) do |g| - g.offset = 0xef6c4 - g.constraints = ['[rsp+0x50]=NULL'] -end diff --git a/lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb b/lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb new file mode 100644 index 0000000..1efc9a8 --- /dev/null +++ b/lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb @@ -0,0 +1,6 @@ +require 'one_gadget/gadget' +# Ubuntu GLIBC 2.23-0ubuntu5 +# ELF 64-bit LSB shared object, x86-64 +build_id = File.basename(__FILE__, '.rb').split('-').last +OneGadget::Gadget.add(build_id, 0x4526a, constraints: ['[rsp+0x30] == NULL']) +OneGadget::Gadget.add(build_id, 0xef6c4, constraints: ['[rsp+0x50] == NULL']) diff --git a/lib/one_gadget/fetcher.rb b/lib/one_gadget/fetcher.rb index 3b6a6f8..757b73d 100644 --- a/lib/one_gadget/fetcher.rb +++ b/lib/one_gadget/fetcher.rb @@ -6,10 +6,14 @@ module OneGadget module Fetcher # Define class methods here. module ClassMethods + # Fetch one-gadget offsets of this build id. # @param [String] build_id The targets' BuildID. - # @option [Boolean] details + # @param [Boolean] details # If needs to return the gadgets' constraints. - # @return [Array] + # @return [Array, Array] + # If +details+ is +false+, +Array+ is returned, which + # contains offset only. + # Otherwise, array of gadgets is returned. def from_build_id(build_id, details: false) if (build_id =~ /\A#{OneGadget::Helper::BUILD_ID_FORMAT}\Z/).nil? raise ArgumentError, format('invalid BuildID format: %p', build_id) diff --git a/lib/one_gadget/gadget.rb b/lib/one_gadget/gadget.rb index 08d0f6c..de3e423 100644 --- a/lib/one_gadget/gadget.rb +++ b/lib/one_gadget/gadget.rb @@ -5,13 +5,23 @@ module OneGadget module Gadget # Information of a gadget. class Gadget + # @return [Integer] The gadget's address offset. attr_accessor :offset + # @return [Array] The constraints need for this gadget. attr_accessor :constraints - def constraints=(value) - raise ArgumentError, value unless value.is_a?(Array) - @constraints = value + + # Initialize method of {Gadget} instance. + # @param [Integer] offset The relative address offset of this gadget. + # @option options [Array] :constraints + # The constraints need for this gadget. Defaults to +[]+. + # @example + # OneGadget::Gadget::Gadget.new(0x12345, constraints: ['rax == 0']) + def initialize(offset, **options) + @offset = offset + @constraints = options[:constraints] || [] end + # Show gadget in a pretty way. def inspect str = format("#{OneGadget::Helper.colorize('offset', sev: :sym)}: 0x%x\n", offset) unless constraints.nil? @@ -22,29 +32,37 @@ module OneGadget OneGadget::ABI.registers.each { |reg| str.gsub!(reg, OneGadget::Helper.colorize(reg, sev: :reg)) } str + "\n" end - - # Only check if +offset+ being set now. - def self_check! - raise ArgumentError, format('invalid offset: %p', offset) unless offset.is_a?(Integer) && offset >= 0 - end end # Define class methods here. module ClassMethods BUILDS_PATH = File.join(File.dirname(__FILE__), 'builds').freeze - CACHE = Hash.new { |h, k| h[k] = [] } + BUILDS = Hash.new { |h, k| h[k] = [] } + # Get gadgets from pre-defined corpus. + # @param [String] build_id Desired build id. + # @return [Array] Gadgets. def builds(build_id) - return CACHE[build_id] if CACHE.key?(build_id) - return [] unless File.exist?(File.join(BUILDS_PATH, build_id + '.rb')) - require File.join(BUILDS_PATH, build_id + '.rb') - CACHE[build_id] + require_all if BUILDS.empty? + return BUILDS[build_id] if BUILDS.key?(build_id) + # TODO: fetch remote builds information. + [] end - def define(build_id) - g = OneGadget::Gadget::Gadget.new - yield(g) - g.self_check! - CACHE[build_id] << g + # Add a gadget, for scripts in builds/ to use. + # @param [String] build_id The target's build id. + # @param [Integer] offset The relative address offset of this gadget. + # @param [Hash] options See {Gadget::Gadget#initialize} for more information. + # @return [void] + def add(build_id, offset, **options) + BUILDS[build_id] << OneGadget::Gadget::Gadget.new(offset, **options) + end + + private + + def require_all + Dir.glob(File.join(BUILDS_PATH, '**', '*.rb')).each do |dic| + require dic + end end end extend ClassMethods diff --git a/lib/one_gadget/helper.rb b/lib/one_gadget/helper.rb index 25f1343..7951d8d 100644 --- a/lib/one_gadget/helper.rb +++ b/lib/one_gadget/helper.rb @@ -29,10 +29,12 @@ module OneGadget bid.first end + # Disable colorize def color_off! @disable_color = true end + # Enable colorize def color_on! @disable_color = false end @@ -47,7 +49,7 @@ module OneGadget sym: "\e[38;5;229m", # pry like }.freeze - # Wrapper color codes for for pretty inspect. + # Wrapper color codes for pretty inspect. # @param [String] str Contents to colorize. # @option [Symbol] sev Specific which kind of color want to use, valid symbols are defined in +COLOR_CODE+. # @return [String] Wrapper with color codes. diff --git a/spec/gadget_spec.rb b/spec/gadget_spec.rb index 09428fa..ed151fc 100644 --- a/spec/gadget_spec.rb +++ b/spec/gadget_spec.rb @@ -4,18 +4,15 @@ describe OneGadget::Gadget do before(:all) do @build_id = 'fake_id' OneGadget::Helper.color_off! # disable colorize for easy testing. - OneGadget::Gadget.define(@build_id) do |g| - g.offset = 0x1234 - g.constraints = ['[rsp+0x30]=NULL', 'rax=0'] - end + OneGadget::Gadget.add(@build_id, 0x1234, constraints: ['[rsp+0x30] == NULL', 'rax == 0']) end it 'inspect' do expect(OneGadget::Gadget.builds(@build_id).map(&:inspect).join).to eq <<-EOS offset: 0x1234 constraints: - [rsp+0x30]=NULL - rax=0 + [rsp+0x30] == NULL + rax == 0 EOS end end diff --git a/spec/one_gadget_spec.rb b/spec/one_gadget_spec.rb index 51ec305..41fa00b 100644 --- a/spec/one_gadget_spec.rb +++ b/spec/one_gadget_spec.rb @@ -2,6 +2,8 @@ require 'one_gadget' describe 'one_gadget' do before(:all) do + # To force require all again. + OneGadget::Gadget::ClassMethods::BUILDS.clear @build_id = '60131540dadc6796cab33388349e6e4e68692053' @libcpath = File.join(File.dirname(__FILE__), 'data', 'libc-2.19.so') end @@ -11,12 +13,12 @@ describe 'one_gadget' do end describe 'from build id' do - it 'from build id' do + it 'normal' do # only check not empty because the gadgets might add frequently. expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty end - it 'invalid id' do + it 'invalid' do expect { OneGadget.gadgets(build_id: '^_^') }.to raise_error(ArgumentError, 'invalid BuildID format: "^_^"') end end