Class: OneGadget::Emulators::Lambda

Inherits:
Object
  • Object
show all
Defined in:
lib/one_gadget/emulators/lambda.rb

Overview

A Lambda object can be:

  1. String (variable name)
  2. Numeric
  3. Lambda + Numeric
  4. dereferenced Lambda

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj) ⇒ Lambda

Instantiate a OneGadget::Emulators::Lambda object.

Parameters:



22
23
24
25
26
# File 'lib/one_gadget/emulators/lambda.rb', line 22

def initialize(obj)
  @immi = 0
  @obj = obj
  @deref_count = 0
end

Instance Attribute Details

#deref_countInteger

Returns The times of dereference.

Returns:

  • (Integer)

    The times of dereference.



16
17
18
# File 'lib/one_gadget/emulators/lambda.rb', line 16

def deref_count
  @deref_count
end

#immiInteger

Returns The immidiate value currently added.

Returns:

  • (Integer)

    The immidiate value currently added.



15
16
17
# File 'lib/one_gadget/emulators/lambda.rb', line 15

def immi
  @immi
end

#objString, Lambda

Returns The object currently related to.

Returns:

  • (String, Lambda)

    The object currently related to.



14
15
16
# File 'lib/one_gadget/emulators/lambda.rb', line 14

def obj
  @obj
end

#opString?

Returns The operator applied to #obj, for an operation (see operation).

Returns:

  • (String, nil)

    The operator applied to #obj, for an operation (see operation).



17
18
19
# File 'lib/one_gadget/emulators/lambda.rb', line 17

def op
  @op
end

#rhsInteger, ...

Returns The operator's right operand.

Returns:

  • (Integer, Lambda, String, nil)

    The operator's right operand.



18
19
20
# File 'lib/one_gadget/emulators/lambda.rb', line 18

def rhs
  @rhs
end

Class Method Details

.operation(lhs, op, rhs) ⇒ Lambda

A value this emulator can name but not fold: an operation applied to lhs, whose result no plain base+offset expresses. Kept as the operation itself, so a constraint on it still says what the caller has to arrange.

Examples:

(amd64) and rax, 0xf leaves rax holding (rax & 0xf)

Lambda.operation(registers['rax'], '&', 0xf)

Parameters:

  • lhs (Lambda, String)

    The left operand.

  • op (String)

    The operator, as it should render.

  • rhs (Integer, Lambda, String)

    The right operand.

Returns:



100
101
102
103
104
105
# File 'lib/one_gadget/emulators/lambda.rb', line 100

def self.operation(lhs, op, rhs)
  Lambda.new(lhs).tap do |lmda|
    lmda.op = op
    lmda.rhs = rhs
  end
end

.parse(argument, predefined: {}) ⇒ OneGadget::Emulators::Lambda, Integer

Target: parse string like [rsp+0x50] into a OneGadget::Emulators::Lambda object.

Examples:

obj = Lambda.parse('[rsp+0x50]')
#=> #<Lambda @obj='rsp', @immi=80, @deref_count=1>
Lambda.parse('obj+0x30', predefined: { 'obj' => obj }).to_s
#=> '[rsp+0x50]+0x30'
Lambda.parse('[x0, -104]')
#=> #<Lambda @obj='x0', @immi=-104, @deref_count=1>

Parameters:

  • argument (String)

    The instruction operand to parse, e.g. +[rsp+0x50]+ or +rax+0x30+.

  • predefined (Hash{String => Lambda}) (defaults to: {})

    Maps register/variable names to known OneGadget::Emulators::Lambda values, used to resolve +argument+'s base.

Returns:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/one_gadget/emulators/lambda.rb', line 158

def parse(argument, predefined: {})
  arg = argument
  return 0 if arg.empty? || arg == '!'

  literal = Integer(arg, exception: false)
  return literal unless literal.nil?

  # nested []
  if arg[0] == '['
    ridx = arg.rindex(']')
    immi = parse(arg[(ridx + 1)..])
    inner = parse(arg[1...ridx], predefined:)
    # An absolute address (the +[0x1234]+ in +lea rax, [0x1234]+) parses to an Integer;
    # model it as a based-nowhere Lambda so it can still be dereferenced.
    inner = Lambda.new(nil).tap { |l| l.immi = inner } if inner.is_a?(Integer)
    lm = inner.deref
    lm += immi unless immi.zero?
    return lm
  end

  base, disp = mem_obj(arg)
  obj = predefined[base]
  # A register may currently hold a call-target string (after a call);
  # treat that as a fresh symbolic base. Lambda/Integer/xmm-array pass through.
  obj = Lambda.new(base) if obj.nil? || obj.is_a?(String)
  obj += disp unless disp.zero?
  obj
end

Instance Method Details

#+(other) ⇒ Lambda

Implement addition with Numeric.

Parameters:

  • other (Numeric)

    Value to add.

Returns:

Raises:



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/one_gadget/emulators/lambda.rb', line 31

def +(other)
  raise Error::InstructionArgumentError, "Expect other(#{other}) to be numeric." unless other.is_a?(Numeric)

  if deref_count.positive?
    ret = Lambda.new(self)
  else
    ret = Lambda.new(obj)
    ret.immi = immi
    ret.op = op
    ret.rhs = rhs
  end
  ret.immi += other
  ret
end

#-(other) ⇒ Lambda

Implement subtract with Numeric.

Parameters:

  • other (Numeric)

    Value to substract.

Returns:



49
50
51
# File 'lib/one_gadget/emulators/lambda.rb', line 49

def -(other)
  self + -other
end

#base_strString

What #to_s renders before any offset: the operation this names, or the object it is based on.

Returns:

  • (String)


116
117
118
119
120
121
122
# File 'lib/one_gadget/emulators/lambda.rb', line 116

def base_str
  return '' if obj.nil?
  return obj.to_s unless operation?

  rendered = rhs.is_a?(Integer) ? OneGadget::Helper.hex(rhs) : rhs.to_s
  "(#{obj} #{op} #{rendered})"
end

#derefLambda

A new OneGadget::Emulators::Lambda object with dereference count increase 1.

Returns:



71
72
73
74
75
76
77
78
# File 'lib/one_gadget/emulators/lambda.rb', line 71

def deref
  ret = Lambda.new(obj)
  ret.immi = immi
  ret.op = op
  ret.rhs = rhs
  ret.deref_count = deref_count + 1
  ret
end

#deref!void

This method returns an undefined value.

Increase dereference count by 1.



55
56
57
# File 'lib/one_gadget/emulators/lambda.rb', line 55

def deref!
  @deref_count += 1
end

#evaluate(context) ⇒ Integer

Evaluates the value of lambda. Only supports +rsp+0x30+ form.

Examples:

l = Lambda.parse('rax+0x30')
l.evaluate('rax' => 2)
#=> 50

Parameters:

  • context (Hash{String => Integer})

    The context.

Returns:

  • (Integer)

    Result of evaluation.



133
134
135
136
137
138
139
# File 'lib/one_gadget/emulators/lambda.rb', line 133

def evaluate(context)
  if deref_count.positive? || (obj && !context.key?(obj))
    raise Error::InstructionArgumentError, "Can't eval #{self}"
  end

  context[obj] + immi
end

#operation?Boolean

Whether this names an operation rather than a base+offset (see operation).

Returns:

  • (Boolean)


109
110
111
# File 'lib/one_gadget/emulators/lambda.rb', line 109

def operation?
  !op.nil?
end

#ref!self

Decrease dereference count by 1.

Returns:

  • (self)

Raises:

  • (Error::InstrutionArgumentError)

    When this object cannot be referenced anymore.



62
63
64
65
66
67
# File 'lib/one_gadget/emulators/lambda.rb', line 62

def ref!
  raise Error::InstructionArgumentError, 'Cannot reference anymore!' if @deref_count <= 0

  @deref_count -= 1
  self
end

#to_sString

Expand the lambda presentation.

Returns:

  • (String)

    The expand result.



82
83
84
85
86
87
88
89
# File 'lib/one_gadget/emulators/lambda.rb', line 82

def to_s
  str = ''
  str += '[' * deref_count
  str += base_str
  str += OneGadget::Helper.hex(immi, psign: true) unless immi.zero?
  str += ']' * deref_count
  str
end