one_gadget 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43bf496ed69e442f3a6672eff7b0e3f7dffe9256
4
- data.tar.gz: 2ef6da1b0f7173adf036ad43e99533fd98005606
3
+ metadata.gz: 25904f1508d192a00e1702d7adb48d52af589ae0
4
+ data.tar.gz: abe1ec3e6b3f57420326aad5100a3a6d4a260b6b
5
5
  SHA512:
6
- metadata.gz: fa4a2851e9ad28b690c7f2d7c4bcb4c3797d4b587b0b5a02d130d06628576aabbc73812d837c8efd04ea099c98659f0b9447c3d184894781198deae234707a44
7
- data.tar.gz: 76ff23514c99da82136f7f55d3ba44d7dfcf48a56dba3db853e994ad90bcd8f341d963cb5cf9fb53bebf562dcadf1819bab6d02fbbd5f80625ce9ac20f94504d
6
+ metadata.gz: 0d8593c144d504c00ea3f1e3b80397391012a3414054bdc586c52340830b22b7bb7ec57b054c20d6198ded00b25443dbe42225f2804da504474d531777ba922e
7
+ data.tar.gz: a06dce9ee9693fe06422000c6d7105ab55655fbdd79b4edcf69dd9f2a618b9b5f4e5c041941f809fa251296825e63e7ae2f4c90a401ac3d20d05dbe37b8138ac
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![Build Status](https://travis-ci.org/david942j/one_gadget.svg?branch=master)](https://travis-ci.org/david942j/one_gadget)
2
+ ![](http://ruby-gem-downloads-badge.herokuapp.com/one_gadget?type=total&color=orange)
2
3
  [![Code Climate](https://codeclimate.com/github/david942j/one_gadget/badges/gpa.svg)](https://codeclimate.com/github/david942j/one_gadget)
3
4
  [![Issue Count](https://codeclimate.com/github/david942j/one_gadget/badges/issue_count.svg)](https://codeclimate.com/github/david942j/one_gadget)
4
5
  [![Test Coverage](https://codeclimate.com/github/david942j/one_gadget/badges/coverage.svg)](https://codeclimate.com/github/david942j/one_gadget/coverage)
@@ -22,6 +23,10 @@ Available on RubyGems.org!
22
23
  gem install one_gadget
23
24
  ```
24
25
 
26
+ ## Implementation
27
+
28
+ OneGadget use simple self-implement symbolic execution to find the constraints of gadgets.
29
+
25
30
  ## Usage
26
31
 
27
32
  ### Command Line Tool
@@ -0,0 +1,44 @@
1
+ require 'one_gadget/emulators/processor'
2
+ require 'one_gadget/emulators/instruction'
3
+
4
+ module OneGadget
5
+ module Emulators
6
+ # Emulator of amd64 instruction set.
7
+ class Amd64 < Processor
8
+ REGISTERS = %w(rax rbx rcx rdx rdi rsi rbp rsp rip) + 7.upto(15).map { |i| "r#{i}" }
9
+
10
+ # Instantiate a {Amd64} object.
11
+ def initialize
12
+ super(REGISTERS)
13
+ end
14
+
15
+ # Process one command.
16
+ # @param [String] cmd
17
+ # One line from result of objdump.
18
+ # @return [void]
19
+ def process(cmd)
20
+ inst, args = parse(cmd)
21
+ # where should this be defined..?
22
+ return if inst.inst == 'call' # later
23
+ case inst.inst
24
+ when 'mov', 'lea'
25
+ tar = args[0]
26
+ src = OneGadget::Emulators::Lambda.parse(args[1], predefined: @registers)
27
+ end
28
+ src.ref! if inst.inst == 'lea'
29
+
30
+ @registers[tar] = src
31
+ end
32
+
33
+ # Support instruction set.
34
+ # @return [Array<Instruction>] The support instructions.
35
+ def instructions
36
+ [
37
+ Instruction.new('mov', 2),
38
+ Instruction.new('lea', 2),
39
+ Instruction.new('call', 1)
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,36 @@
1
+ module OneGadget
2
+ module Emulators
3
+ # Define instruction name and it's argument count.
4
+ class Instruction
5
+ attr_reader :inst # @return [String] The instruction name.
6
+ attr_reader :argc # @return [Integer] Count of arguments.
7
+ # Instantiate a n{Instruction} object.
8
+ # @param [String] inst The instruction name.
9
+ # @param [Integer] argc Count of arguments.
10
+ def initialize(inst, argc)
11
+ @inst = inst
12
+ @argc = argc
13
+ end
14
+
15
+ # Extract arguments from command.
16
+ # @param [String] cmd
17
+ # @return [Array<String>] Arguments.
18
+ def fetch_args(cmd)
19
+ idx = cmd.index(inst)
20
+ cmd = cmd[0...cmd.rindex('#')] if cmd.rindex('#')
21
+ args = cmd[idx + inst.size..-1].split(',')
22
+ raise ArgumentError, "Incorrect argument number in #{cmd}, expect: #{argc}" if args.size != argc
23
+ args.map do |arg|
24
+ arg.gsub(/QWORD|DWORD|WORD|BYTE|PTR/, '').strip
25
+ end
26
+ end
27
+
28
+ # If the command contains this instruction.
29
+ # @param [String] cmd
30
+ # @return [Boolean]
31
+ def match?(cmd)
32
+ cmd.include?(inst)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,105 @@
1
+ require 'one_gadget/helper'
2
+
3
+ module OneGadget
4
+ module Emulators
5
+ # A {Lambda} object can be:
6
+ # 1. {String} # variable name
7
+ # 2. {Numeric}
8
+ # 3. {Lambda} + {Numeric}
9
+ # 4. dereference {Lambda}
10
+ class Lambda
11
+ attr_accessor :obj # @return [String, Lambda] The object currently related to.
12
+ attr_accessor :immi # @return [Integer] The immidiate value currently added.
13
+ attr_accessor :deref_count # @return [Integer] The times of dereference.
14
+ # Instantiate a {Lambda} object.
15
+ # @param [Lambda, String] obj
16
+ def initialize(obj)
17
+ @immi = 0
18
+ @obj = obj
19
+ @deref_count = 0
20
+ end
21
+
22
+ # Implement addition with +Numeric+.
23
+ # @param [Numeric] other Value to add.
24
+ # @return [Lambda] The result.
25
+ def +(other)
26
+ raise ArgumentError, 'Expect other to be Numeric.' unless other.is_a?(Numeric)
27
+ if deref_count > 0
28
+ ret = Lambda.new(self)
29
+ else
30
+ ret = Lambda.new(obj)
31
+ ret.immi = immi
32
+ end
33
+ ret.immi += other
34
+ ret
35
+ end
36
+
37
+ # Implement substract with +Numeric+.
38
+ # @param [Numeric] other Value to substract.
39
+ # @return [Lambda] The result.
40
+ def -(other)
41
+ self.+(-other)
42
+ end
43
+
44
+ # Increase dreference count with 1.
45
+ # @return [void]
46
+ def deref!
47
+ @deref_count += 1
48
+ end
49
+
50
+ # Decrease dreference count with 1.
51
+ # @return [void]
52
+ def ref!
53
+ raise ArgumentError, 'Cannot reference anymore!' if @deref_count <= 0
54
+ @deref_count -= 1
55
+ end
56
+
57
+ # A new {Lambda} object with dreference count increase 1.
58
+ # @return [Lambda]
59
+ def deref
60
+ ret = Lambda.new(obj)
61
+ ret.immi = immi
62
+ ret.deref_count = deref_count + 1
63
+ ret
64
+ end
65
+
66
+ # Expand the lambda presentation.
67
+ # @return [String] The expand result.
68
+ def to_s
69
+ str = ''
70
+ str += '[' * deref_count
71
+ str += obj.to_s unless obj.nil?
72
+ str += OneGadget::Helper.hex(immi, psign: true) unless immi.zero?
73
+ str += ']' * deref_count
74
+ str
75
+ end
76
+
77
+ class << self
78
+ # Target: parse something like +[rsp+0x50]+ into a {Lambda} object.
79
+ # @param [String] arg
80
+ # @param [Hash{String => Lambda}] predefined
81
+ # @return [OneGadget::Emulators::Lambda, Integer]
82
+ # If +arg+ contains number only, return it.
83
+ # Otherwise, return a {Lambda} object.
84
+ # @example
85
+ # parse('[rsp+0x50]') #=> #<Lambda @obj='rsp', @immi=80, @deref_count=1>
86
+ def parse(arg, predefined: {})
87
+ ret = Lambda.new('tmp')
88
+ if arg[0] == '[' # a little hack because there should nerver something like +[[rsp+1]+2]+ to parse.
89
+ arg = arg[1..-2]
90
+ ret.deref_count += 1
91
+ end
92
+ return Integer(arg) if OneGadget::Helper.integer?(arg)
93
+ sign = arg =~ /[+-]/
94
+ raise ArgumentError, "Not support #{arg}" if sign && !OneGadget::Helper.integer?(arg[sign..-1])
95
+ if sign
96
+ ret.immi = Integer(arg[sign..-1])
97
+ arg = arg[0, sign]
98
+ end
99
+ ret.obj = predefined[arg] || arg
100
+ ret
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,36 @@
1
+ require 'one_gadget/emulators/lambda'
2
+
3
+ module OneGadget
4
+ # Instruction emulator to solve the constraint of gadgets.
5
+ module Emulators
6
+ # Base class of a processor.
7
+ class Processor
8
+ attr_reader :registers # @return [Hash{String => OneGadget::Emulators::Lambda}] The current registers' state.
9
+ # Instantiate a {Processor} object.
10
+ # @param [Array<String>] registers Registers that supported in the architecture.
11
+ def initialize(registers)
12
+ @registers = registers.map { |reg| [reg, OneGadget::Emulators::Lambda.new(reg)] }.to_h
13
+ end
14
+
15
+ # Parse one command into instruction and arguments.
16
+ # @param [String] cmd One line of result of objdump.
17
+ # @return [Array(Instruction, Array<String>)]
18
+ # The parsing result.
19
+ def parse(cmd)
20
+ inst = instructions.find { |i| i.match?(cmd) }
21
+ raise ArgumentError, "Not implemented instruction in #{cmd}" if inst.nil?
22
+ [inst, inst.fetch_args(cmd)]
23
+ end
24
+
25
+ # Method need to be implemented in inheritors.
26
+ # @return [void]
27
+ def process(_cmd); raise NotImplementedError
28
+ end
29
+
30
+ # Method need to be implemented in inheritors.
31
+ # @return [Array<Instruction>] The support instructions.
32
+ def instructions; raise NotImplementedError
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,4 +1,5 @@
1
1
  require 'one_gadget/fetchers/base'
2
+ require 'one_gadget/emulators/amd64'
2
3
  module OneGadget
3
4
  module Fetcher
4
5
  # Fetcher for amd64.
@@ -13,10 +14,32 @@ module OneGadget
13
14
  true
14
15
  end
15
16
  cands.map do |candidate|
16
- convert_to_gadget(candidate) do |line|
17
- ['rsi'].any? { |r| line.include?(r) }
18
- end
19
- end
17
+ processor = OneGadget::Emulators::Amd64.new
18
+ candidate.lines.each { |l| processor.process(l) }
19
+ offset = offset_of(candidate)
20
+ constraints = gen_constraints(processor)
21
+ next nil if constraints.nil? # impossible be a gadget
22
+ OneGadget::Gadget::Gadget.new(offset, constraints: constraints)
23
+ end.compact
24
+ end
25
+
26
+ private
27
+
28
+ def gen_constraints(processor)
29
+ # check rdi should always related to rip
30
+ return unless processor.registers['rdi'].to_s.include?('rip')
31
+ # rsi or [rsi] should be zero
32
+ [
33
+ should_null(processor.registers['rsi'].to_s),
34
+ should_null(processor.registers['rdx'].to_s, allow_global: true)
35
+ ].compact
36
+ end
37
+
38
+ def should_null(str, allow_global: false)
39
+ return nil if allow_global && str.include?('rip')
40
+ ret = "[#{str}] == NULL"
41
+ ret += " || #{str} == NULL" unless str.include?('rsp')
42
+ ret
20
43
  end
21
44
  end
22
45
  end
@@ -54,6 +54,11 @@ module OneGadget
54
54
  match.split.first.to_i(16)
55
55
  end
56
56
 
57
+ def offset_of(assembly)
58
+ lines = assembly.lines
59
+ lines.first.scan(/^([\da-f]+):/)[0][0].to_i(16)
60
+ end
61
+
57
62
  def convert_to_gadget(assembly, &block)
58
63
  lines = assembly.lines
59
64
  offset = lines.first.scan(/^([\da-f]+):/)[0][0].to_i(16)
@@ -137,6 +137,37 @@ module OneGadget
137
137
  return :i386 if str.include?('Intel 80386')
138
138
  :unknown
139
139
  end
140
+
141
+ # Present number in hex format.
142
+ # @param [Integer] val The number.
143
+ # @param [Boolean] psign Need to show plus sign when +val >= 0+.
144
+ # @return [String] string in hex format.
145
+ # @example
146
+ # hex(32) #=> 0x20
147
+ # hex(32, psign: true) #=> +0x20
148
+ # hex(-40) #=> -0x28
149
+ # hex(0) #=> 0x0
150
+ # hex(0, psign: true) #=> +0x0
151
+ def hex(val, psign: false)
152
+ return format("#{psign ? '+' : ''}0x%x", val) if val >= 0
153
+ format('-0x%x', -val)
154
+ end
155
+
156
+ # For checking a string is actually an integer.
157
+ # @param [String] str String to be checked.
158
+ # @return [Boolean] If +str+ can be converted into integer.
159
+ # @example
160
+ # Helper.integer? '1234'
161
+ # # => true
162
+ # Helper.integer? '0x1234'
163
+ # # => true
164
+ # Helper.integer? '0xheapoverflow'
165
+ # # => false
166
+ def integer?(str)
167
+ true if Integer(str)
168
+ rescue ArgumentError, TypeError
169
+ false
170
+ end
140
171
  end
141
172
  extend ClassMethods
142
173
  end
@@ -1,3 +1,3 @@
1
1
  module OneGadget
2
- VERSION = '1.1.1'.freeze
2
+ VERSION = '1.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: one_gadget
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-14 00:00:00.000000000 Z
11
+ date: 2017-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -101,6 +101,10 @@ files:
101
101
  - lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb
102
102
  - lib/one_gadget/builds/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.rb
103
103
  - lib/one_gadget/builds/libc-2.23-edceed30099baad51871c5fc277daf9b74dc726a.rb
104
+ - lib/one_gadget/emulators/amd64.rb
105
+ - lib/one_gadget/emulators/instruction.rb
106
+ - lib/one_gadget/emulators/lambda.rb
107
+ - lib/one_gadget/emulators/processor.rb
104
108
  - lib/one_gadget/fetcher.rb
105
109
  - lib/one_gadget/fetchers/amd64.rb
106
110
  - lib/one_gadget/fetchers/base.rb
@@ -129,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
133
  version: '0'
130
134
  requirements: []
131
135
  rubyforge_project:
132
- rubygems_version: 2.5.2
136
+ rubygems_version: 2.6.10
133
137
  signing_key:
134
138
  specification_version: 4
135
139
  summary: one_gadget