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 +4 -4
- data/README.md +5 -0
- data/lib/one_gadget/emulators/amd64.rb +44 -0
- data/lib/one_gadget/emulators/instruction.rb +36 -0
- data/lib/one_gadget/emulators/lambda.rb +105 -0
- data/lib/one_gadget/emulators/processor.rb +36 -0
- data/lib/one_gadget/fetchers/amd64.rb +27 -4
- data/lib/one_gadget/fetchers/base.rb +5 -0
- data/lib/one_gadget/helper.rb +31 -0
- data/lib/one_gadget/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25904f1508d192a00e1702d7adb48d52af589ae0
|
4
|
+
data.tar.gz: abe1ec3e6b3f57420326aad5100a3a6d4a260b6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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)
|
data/lib/one_gadget/helper.rb
CHANGED
@@ -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
|
data/lib/one_gadget/version.rb
CHANGED
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.
|
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-
|
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.
|
136
|
+
rubygems_version: 2.6.10
|
133
137
|
signing_key:
|
134
138
|
specification_version: 4
|
135
139
|
summary: one_gadget
|