one_gadget 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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