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'
- BUILTIN_METHODS =
some “global” functions are actually keywords, like void(5)
['void']
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.
27 28 29 30 31 32 33 |
# File 'lib/jsobfu/obfuscator.rb', line 27 def initialize(opts={}) @scope = opts.fetch(:scope) { JSObfu::Scope.new } @global = opts.fetch(:global, DEFAULT_GLOBAL).to_s @memory_sensitive = !!opts.fetch(:memory_sensitive, false) @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
118 119 120 121 122 123 124 125 |
# File 'lib/jsobfu/obfuscator.rb', line 118 def visit_DotAccessorNode(o) if @memory_sensitive super else obf_str = JSObfu::Utils::transform_string(o.accessor, scope, :quotes => false) "#{o.value.accept(self)}[(#{obf_str})]" end end |
#visit_FunctionDeclNode(o) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/jsobfu/obfuscator.rb', line 63 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
75 76 77 78 79 80 81 |
# File 'lib/jsobfu/obfuscator.rb', line 75 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
147 148 149 150 151 152 153 |
# File 'lib/jsobfu/obfuscator.rb', line 147 def visit_NumberNode(o) unless @memory_sensitive o.value = JSObfu::Utils::transform_number(o.value) end 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.
129 130 131 132 133 |
# File 'lib/jsobfu/obfuscator.rb', line 129 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 “{}”
136 137 138 139 140 141 142 143 144 145 |
# File 'lib/jsobfu/obfuscator.rb', line 136 def visit_PropertyNode(o) # if it is a non-alphanumeric property, obfuscate the string's bytes unless @memory_sensitive if o.name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/ o.instance_variable_set :@name, '"'+JSObfu::Utils::random_string_encoding(o.name)+'"' end 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.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/jsobfu/obfuscator.rb', line 95 def visit_ResolveNode(o) if is_builtin_method?(o.value) return super end new_val = rename_var(o.value, :generate => false) if new_val o.value = JSObfu::Utils::random_var_encoding(new_val) super else if @memory_sensitive || 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.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/jsobfu/obfuscator.rb', line 46 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
155 156 157 158 159 160 161 |
# File 'lib/jsobfu/obfuscator.rb', line 155 def visit_StringNode(o) unless @memory_sensitive o.value = JSObfu::Utils::transform_string(o.value, scope) end super end |
#visit_TryNode(o) ⇒ Object
163 164 165 166 167 168 |
# File 'lib/jsobfu/obfuscator.rb', line 163 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.
84 85 86 87 88 |
# File 'lib/jsobfu/obfuscator.rb', line 84 def visit_VarDeclNode(o) o.name = JSObfu::Utils::random_var_encoding(rename_var(o.name)) super end |