metasploit-framework/lib/rkelly/visitors/evaluation_visitor.rb

420 lines
12 KiB
Ruby

module RKelly
module Visitors
class EvaluationVisitor < Visitor
attr_reader :scope_chain
def initialize(scope)
super()
@scope_chain = scope
@operand = []
end
def visit_SourceElementsNode(o)
o.value.each { |x|
next if scope_chain.returned?
x.accept(self)
}
end
def visit_FunctionDeclNode(o)
end
def visit_VarStatementNode(o)
o.value.each { |x| x.accept(self) }
end
def visit_VarDeclNode(o)
@operand << o.name
o.value.accept(self) if o.value
@operand.pop
end
def visit_IfNode(o)
truthiness = o.conditions.accept(self)
if truthiness.value && truthiness.value != 0
o.value.accept(self)
else
o.else && o.else.accept(self)
end
end
def visit_ResolveNode(o)
scope_chain[o.value]
end
def visit_ThisNode(o)
scope_chain.this
end
def visit_ExpressionStatementNode(o)
o.value.accept(self)
end
def visit_AddNode(o)
left = to_primitive(o.left.accept(self), 'Number')
right = to_primitive(o.value.accept(self), 'Number')
if left.value.is_a?(::String) || right.value.is_a?(::String)
RKelly::JS::Property.new(:add,
"#{left.value}#{right.value}"
)
else
additive_operator(:+, left, right)
end
end
def visit_SubtractNode(o)
RKelly::JS::Property.new(:subtract,
o.left.accept(self).value - o.value.accept(self).value
)
end
def visit_MultiplyNode(o)
left = to_number(o.left.accept(self)).value
right = to_number(o.value.accept(self)).value
return_val =
if [left, right].any? { |x| x.respond_to?(:nan?) && x.nan? }
RKelly::JS::NaN.new
else
[left, right].any? { |x|
x.respond_to?(:intinite?) && x.infinite?
} && [left, right].any? { |x| x == 0
} ? RKelly::JS::NaN.new : left * right
end
RKelly::JS::Property.new(:multiple, return_val)
end
def visit_DivideNode(o)
left = to_number(o.left.accept(self)).value
right = to_number(o.value.accept(self)).value
return_val =
if [left, right].any? { |x|
x.respond_to?(:nan?) && x.nan? ||
x.respond_to?(:intinite?) && x.infinite?
}
RKelly::JS::NaN.new
elsif [left, right].all? { |x| x == 0 }
RKelly::JS::NaN.new
elsif right == 0
left * (right.eql?(0) ? (1.0/0.0) : (-1.0/0.0))
else
left / right
end
RKelly::JS::Property.new(:divide, return_val)
end
def visit_ModulusNode(o)
left = to_number(o.left.accept(self)).value
right = to_number(o.value.accept(self)).value
return_val =
if [left, right].any? { |x| x.respond_to?(:nan?) && x.nan? }
RKelly::JS::NaN.new
elsif [left, right].all? { |x| x.respond_to?(:infinite?) && x.infinite? }
RKelly::JS::NaN.new
elsif right == 0
RKelly::JS::NaN.new
elsif left.respond_to?(:infinite?) && left.infinite?
RKelly::JS::NaN.new
elsif right.respond_to?(:infinite?) && right.infinite?
left
else
left % right
end
RKelly::JS::Property.new(:divide, return_val)
end
def visit_OpEqualNode(o)
left = o.left.accept(self)
right = o.value.accept(self)
left.value = right.value
left.function = right.function
left
end
def visit_OpPlusEqualNode(o)
o.left.accept(self).value += o.value.accept(self).value
end
def visit_AssignExprNode(o)
scope_chain[@operand.last] = o.value.accept(self)
end
def visit_NumberNode(o)
RKelly::JS::Property.new(o.value, o.value)
end
def visit_VoidNode(o)
o.value.accept(self)
RKelly::JS::Property.new(:undefined, :undefined)
end
def visit_NullNode(o)
RKelly::JS::Property.new(nil, nil)
end
def visit_TrueNode(o)
RKelly::JS::Property.new(true, true)
end
def visit_FalseNode(o)
RKelly::JS::Property.new(false, false)
end
def visit_StringNode(o)
RKelly::JS::Property.new(:string,
o.value.gsub(/\A['"]/, '').gsub(/['"]$/, '')
)
end
def visit_FunctionCallNode(o)
left = o.value.accept(self)
arguments = o.arguments.accept(self)
call_function(left, arguments)
end
def visit_NewExprNode(o)
visit_FunctionCallNode(o)
end
def visit_DotAccessorNode(o)
left = o.value.accept(self)
right = left.value[o.accessor]
right.binder = left.value
right
end
def visit_EqualNode(o)
left = o.left.accept(self)
right = o.value.accept(self)
RKelly::JS::Property.new(:equal_node, left.value == right.value)
end
def visit_BlockNode(o)
o.value.accept(self)
end
def visit_FunctionBodyNode(o)
o.value.accept(self)
scope_chain.return
end
def visit_ReturnNode(o)
scope_chain.return = o.value.accept(self)
end
def visit_BitwiseNotNode(o)
orig = o.value.accept(self)
number = to_int_32(orig)
RKelly::JS::Property.new(nil, ~number.value)
end
def visit_PostfixNode(o)
orig = o.operand.accept(self)
number = to_number(orig)
case o.value
when '++'
orig.value = number.value + 1
when '--'
orig.value = number.value - 1
end
number
end
def visit_PrefixNode(o)
orig = o.operand.accept(self)
number = to_number(orig)
case o.value
when '++'
orig.value = number.value + 1
when '--'
orig.value = number.value - 1
end
orig
end
def visit_LogicalNotNode(o)
bool = to_boolean(o.value.accept(self))
bool.value = !bool.value
bool
end
def visit_ArgumentsNode(o)
o.value.map { |x| x.accept(self) }
end
def visit_TypeOfNode(o)
val = o.value.accept(self)
return RKelly::JS::Property.new(:string, 'object') if val.value.nil?
case val.value
when String
RKelly::JS::Property.new(:string, 'string')
when Numeric
RKelly::JS::Property.new(:string, 'number')
when true
RKelly::JS::Property.new(:string, 'boolean')
when false
RKelly::JS::Property.new(:string, 'boolean')
when :undefined
RKelly::JS::Property.new(:string, 'undefined')
else
RKelly::JS::Property.new(:object, 'object')
end
end
def visit_UnaryPlusNode(o)
orig = o.value.accept(self)
to_number(orig)
end
def visit_UnaryMinusNode(o)
orig = o.value.accept(self)
v = to_number(orig)
v.value = v.value == 0 ? -0.0 : 0 - v.value
v
end
%w{
ArrayNode BitAndNode BitOrNode
BitXOrNode BracketAccessorNode BreakNode
CaseBlockNode CaseClauseNode CommaNode ConditionalNode
ConstStatementNode ContinueNode DeleteNode
DoWhileNode ElementNode EmptyStatementNode
ForInNode ForNode
FunctionExprNode GetterPropertyNode GreaterNode GreaterOrEqualNode
InNode InstanceOfNode LabelNode LeftShiftNode LessNode
LessOrEqualNode LogicalAndNode LogicalOrNode
NotEqualNode NotStrictEqualNode
ObjectLiteralNode OpAndEqualNode OpDivideEqualNode
OpLShiftEqualNode OpMinusEqualNode OpModEqualNode
OpMultiplyEqualNode OpOrEqualNode OpRShiftEqualNode
OpURShiftEqualNode OpXOrEqualNode ParameterNode
PropertyNode RegexpNode RightShiftNode
SetterPropertyNode StrictEqualNode
SwitchNode ThrowNode TryNode
UnsignedRightShiftNode
WhileNode WithNode
}.each do |type|
define_method(:"visit_#{type}") do |o|
raise "#{type} not defined"
end
end
private
def to_number(object)
return RKelly::JS::Property.new('0', 0) unless object.value
return_val =
case object.value
when :undefined
RKelly::JS::NaN.new
when false
0
when true
1
when Numeric
object.value
when ::String
s = object.value.gsub(/(\A[\s\xB\xA0]*|[\s\xB\xA0]*\Z)/, '')
if s.length == 0
0
else
case s
when /^([+-])?Infinity/
$1 == '-' ? -1.0/0.0 : 1.0/0.0
when /\A[-+]?\d+\.\d*(?:[eE][-+]?\d+)?$|\A[-+]?\d+(?:\.\d*)?[eE][-+]?\d+$|\A[-+]?\.\d+(?:[eE][-+]?\d+)?$/, /\A[-+]?0[xX][\da-fA-F]+$|\A[+-]?0[0-7]*$|\A[+-]?\d+$/
s.gsub!(/\.(\D)/, '.0\1') if s =~ /\.\w/
s.gsub!(/\.$/, '.0') if s =~ /\.$/
s.gsub!(/^\./, '0.') if s =~ /^\./
s.gsub!(/^([+-])\./, '\10.') if s =~ /^[+-]\./
s = s.gsub(/^[0]*/, '') if /^0[1-9]+$/.match(s)
eval(s)
else
RKelly::JS::NaN.new
end
end
when RKelly::JS::Base
return to_number(to_primitive(object, 'Number'))
end
RKelly::JS::Property.new(nil, return_val)
end
def to_boolean(object)
return RKelly::JS::Property.new(false, false) unless object.value
value = object.value
boolean =
case value
when :undefined
false
when true
true
when Numeric
value == 0 || value.respond_to?(:nan?) && value.nan? ? false : true
when ::String
value.length == 0 ? false : true
when RKelly::JS::Base
true
else
raise
end
RKelly::JS::Property.new(boolean, boolean)
end
def to_int_32(object)
number = to_number(object)
value = number.value
return number if value == 0
if value.respond_to?(:nan?) && (value.nan? || value.infinite?)
RKelly::JS::Property.new(nil, 0)
end
value = ((value < 0 ? -1 : 1) * value.abs.floor) % (2 ** 32)
if value >= 2 ** 31
RKelly::JS::Property.new(nil, value - (2 ** 32))
else
RKelly::JS::Property.new(nil, value)
end
end
def to_primitive(object, preferred_type = nil)
return object unless object.value
case object.value
when false, true, :undefined, ::String, Numeric
RKelly::JS::Property.new(nil, object.value)
when RKelly::JS::Base
call_function(object.value.default_value(preferred_type))
end
end
def additive_operator(operator, left, right)
left, right = to_number(left).value, to_number(right).value
left = left.respond_to?(:nan?) && left.nan? ? 0.0/0.0 : left
right = right.respond_to?(:nan?) && right.nan? ? 0.0/0.0 : right
result = left.send(operator, right)
result = result.respond_to?(:nan?) && result.nan? ? JS::NaN.new : result
RKelly::JS::Property.new(operator, result)
end
def call_function(property, arguments = [])
function = property.function || property.value
case function
when RKelly::JS::Function
scope_chain.new_scope { |chain|
function.js_call(chain, *arguments)
}
when UnboundMethod
RKelly::JS::Property.new(:ruby,
function.bind(property.binder).call(*(arguments.map { |x| x.value}))
)
else
RKelly::JS::Property.new(:ruby,
function.call(*(arguments.map { |x| x.value }))
)
end
end
end
end
end