Class: JSObfu::Obfuscator
- Inherits:
-
ECMANoWhitespaceVisitor
- Object
- RKelly::Visitors::ECMAVisitor
- ECMANoWhitespaceVisitor
- JSObfu::Obfuscator
- Defined in:
- lib/jsobfu/obfuscator.rb
Constant Summary collapse
- DEFAULT_GLOBAL =
unresolved lookups are rewritten as property lookups on the global object
'window'
Instance Attribute Summary collapse
-
#global ⇒ String
readonly
The global object in this JS environment.
-
#renames ⇒ Hash
readonly
Of original var/fn names to our new random neames.
-
#scope ⇒ JSObfu::Scope
readonly
The scope maintained while walking the ast.
Instance Method Summary collapse
-
#initialize(opts = {}) ⇒ Obfuscator
constructor
A new instance of Obfuscator.
-
#visit_DotAccessorNode(o) ⇒ Object
Called on a dot lookup, like X.Y.
- #visit_FunctionDeclNode(o) ⇒ Object
- #visit_FunctionExprNode(o) ⇒ Object
- #visit_NumberNode(o) ⇒ Object
-
#visit_ParameterNode(o) ⇒ Object
Called when a parameter is declared.
-
#visit_PropertyNode(o) ⇒ Object
A property node in an object “{}”.
-
#visit_ResolveNode(o) ⇒ Object
Called whenever a variable is referred to (not declared).
-
#visit_SourceElementsNode(o) ⇒ Object
Maintains a stack of closures that we have visited.
- #visit_StringNode(o) ⇒ Object
- #visit_TryNode(o) ⇒ Object
-
#visit_VarDeclNode(o) ⇒ Object
Called whenever a variable is declared.
Methods inherited from ECMANoWhitespaceVisitor
#function_params_and_body, #visit_ArgumentsNode, #visit_ArrayNode, #visit_AssignExprNode, #visit_BitwiseNotNode, #visit_BlockNode, #visit_BracketAccessorNode, #visit_BreakNode, #visit_CaseBlockNode, #visit_CaseClauseNode, #visit_CommaNode, #visit_ConditionalNode, #visit_ConstStatementNode, #visit_ContinueNode, #visit_DeleteNode, #visit_DoWhileNode, #visit_ElementNode, #visit_EmptyStatementNode, #visit_ExpressionStatementNode, #visit_FalseNode, #visit_ForInNode, #visit_ForNode, #visit_FunctionBodyNode, #visit_FunctionCallNode, #visit_GetterPropertyNode, #visit_IfNode, #visit_LabelNode, #visit_LessNode, #visit_LogicalNotNode, #visit_NewExprNode, #visit_NullNode, #visit_ObjectLiteralNode, #visit_OpEqualNode, #visit_ParentheticalNode, #visit_PostfixNode, #visit_PrefixNode, #visit_RegexpNode, #visit_ReturnNode, #visit_SetterPropertyNode, #visit_SwitchNode, #visit_ThisNode, #visit_ThrowNode, #visit_TrueNode, #visit_TypeOfNode, #visit_UnaryMinusNode, #visit_UnaryPlusNode, #visit_VarStatementNode, #visit_VoidNode, #visit_WhileNode, #visit_WithNode
Constructor Details
#initialize(opts = {}) ⇒ Obfuscator
Returns a new instance of Obfuscator.
21 22 23 24 25 26 |
# File 'lib/jsobfu/obfuscator.rb', line 21 def initialize(opts={}) @scope = opts.fetch(:scope, JSObfu::Scope.new) @global = opts.fetch(:global, DEFAULT_GLOBAL).to_s @renames = {} super() end |
Instance Attribute Details
#global ⇒ String (readonly)
Returns the global object in this JS environment.
12 13 14 |
# File 'lib/jsobfu/obfuscator.rb', line 12 def global @global end |
#renames ⇒ Hash (readonly)
Returns of original var/fn names to our new random neames.
9 10 11 |
# File 'lib/jsobfu/obfuscator.rb', line 9 def renames @renames end |
#scope ⇒ JSObfu::Scope (readonly)
Returns the scope maintained while walking the ast.
6 7 8 |
# File 'lib/jsobfu/obfuscator.rb', line 6 def scope @scope end |
Instance Method Details
#visit_DotAccessorNode(o) ⇒ Object
Called on a dot lookup, like X.Y
107 108 109 110 |
# File 'lib/jsobfu/obfuscator.rb', line 107 def visit_DotAccessorNode(o) obf_str = JSObfu::Utils::transform_string(o.accessor, scope, :quotes => false) "#{o.value.accept(self)}[(#{obf_str})]" end |
#visit_FunctionDeclNode(o) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/jsobfu/obfuscator.rb', line 56 def visit_FunctionDeclNode(o) o.value = if o.value and o.value.length > 0 JSObfu::Utils::random_var_encoding(scope.rename_var(o.value)) else if rand(3) != 0 JSObfu::Utils::random_var_encoding(scope.random_var_name) end end super end |
#visit_FunctionExprNode(o) ⇒ Object
68 69 70 71 72 73 74 |
# File 'lib/jsobfu/obfuscator.rb', line 68 def visit_FunctionExprNode(o) if o.value != 'function' o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) end super end |
#visit_NumberNode(o) ⇒ Object
130 131 132 133 |
# File 'lib/jsobfu/obfuscator.rb', line 130 def visit_NumberNode(o) o.value = JSObfu::Utils::transform_number(o.value) super end |
#visit_ParameterNode(o) ⇒ Object
Called when a parameter is declared. “Shadowed” parameters in the original source are preserved - the randomized name is “shadowed” from the outer scope.
114 115 116 117 118 |
# File 'lib/jsobfu/obfuscator.rb', line 114 def visit_ParameterNode(o) o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) super end |
#visit_PropertyNode(o) ⇒ Object
A property node in an object “{}”
121 122 123 124 125 126 127 128 |
# File 'lib/jsobfu/obfuscator.rb', line 121 def visit_PropertyNode(o) # if it is a non-alphanumeric property, obfuscate the string's bytes if o.name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/ o.instance_variable_set :@name, '"'+JSObfu::Utils::random_string_encoding(o.name)+'"' end super end |
#visit_ResolveNode(o) ⇒ Object
Called whenever a variable is referred to (not declared).
If the variable was never added to scope, it is assumed to be a global object (like “document”), and hence will not be obfuscated.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/jsobfu/obfuscator.rb', line 88 def visit_ResolveNode(o) new_val = rename_var(o.value, :generate => false) if new_val o.value = JSObfu::Utils::random_var_encoding(new_val) super else if o.value.to_s == global.to_s # if the ref is the global object, don't obfuscate it on itself. This helps # "shimmed" globals (like `window=this` at the top of the script) work reliably. super else # A global is used, at least obfuscate the lookup "#{global}[#{JSObfu::Utils::transform_string(o.value, scope, :quotes => false)}]" end end end |
#visit_SourceElementsNode(o) ⇒ Object
Maintains a stack of closures that we have visited. This method is called everytime we visit a nested function.
Javascript is functionally-scoped, so a function(){} creates its own unique closure. When resolving variables, Javascript looks “up” the closure stack, ending up as a property lookup in the global scope (available as ‘window` in all browsers)
This is changed in newer ES versions, where a ‘let` keyword has been introduced, which has regular C-style block scoping. We’ll ignore this feature since it is not yet widely used.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/jsobfu/obfuscator.rb', line 39 def visit_SourceElementsNode(o) scope.push! hoister = JSObfu::Hoister.new(parent_scope: scope) o.value.each { |x| hoister.accept(x) } hoister.scope.keys.each do |key| rename_var(key) end ret = super scope.pop! ret end |
#visit_StringNode(o) ⇒ Object
135 136 137 138 |
# File 'lib/jsobfu/obfuscator.rb', line 135 def visit_StringNode(o) o.value = JSObfu::Utils::transform_string(o.value, scope) super end |
#visit_TryNode(o) ⇒ Object
140 141 142 143 144 145 |
# File 'lib/jsobfu/obfuscator.rb', line 140 def visit_TryNode(o) if o.catch_block o.instance_variable_set :@catch_var, rename_var(o.catch_var) end super end |
#visit_VarDeclNode(o) ⇒ Object
Called whenever a variable is declared.
77 78 79 80 81 |
# File 'lib/jsobfu/obfuscator.rb', line 77 def visit_VarDeclNode(o) o.name = JSObfu::Utils::random_var_encoding(rename_var(o.name)) super end |