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 34 |
# 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) @preserved_identifiers = opts.fetch(:preserved_identifiers, []) @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
126 127 128 129 130 131 132 133 |
# File 'lib/jsobfu/obfuscator.rb', line 126 def visit_DotAccessorNode(o) if @memory_sensitive || @preserved_identifiers.include?(o.accessor) 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
67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/jsobfu/obfuscator.rb', line 67 def visit_FunctionDeclNode(o) o.value = if o.value and o.value.length > 0 unless @preserved_identifiers.include?(o.value) JSObfu::Utils::random_var_encoding(scope.rename_var(o.value)) end else if rand(3) != 0 JSObfu::Utils::random_var_encoding(scope.random_var_name) end end super end |
#visit_FunctionExprNode(o) ⇒ Object
81 82 83 84 85 86 87 |
# File 'lib/jsobfu/obfuscator.rb', line 81 def visit_FunctionExprNode(o) if o.value != 'function' && !@preserved_identifiers.include?(o.value) o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) end super end |
#visit_NumberNode(o) ⇒ Object
157 158 159 160 161 162 163 |
# File 'lib/jsobfu/obfuscator.rb', line 157 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.
137 138 139 140 141 142 143 |
# File 'lib/jsobfu/obfuscator.rb', line 137 def visit_ParameterNode(o) unless @preserved_identifiers.include?(o.value) o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) end super end |
#visit_PropertyNode(o) ⇒ Object
A property node in an object “{}”
146 147 148 149 150 151 152 153 154 155 |
# File 'lib/jsobfu/obfuscator.rb', line 146 def visit_PropertyNode(o) # if it is a non-alphanumeric property, obfuscate the string's bytes unless @memory_sensitive || @preserved_identifiers.include?(o.name) 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.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/jsobfu/obfuscator.rb', line 103 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 || @preserved_identifiers.include?(o.value.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.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/jsobfu/obfuscator.rb', line 47 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| unless @preserved_identifiers.include?(key) rename_var(key) end end ret = super # maintain a single top-level scope scope.pop!(retain: scope.depth == 0) ret end |
#visit_StringNode(o) ⇒ Object
165 166 167 168 169 170 171 |
# File 'lib/jsobfu/obfuscator.rb', line 165 def visit_StringNode(o) unless @memory_sensitive o.value = JSObfu::Utils::transform_string(o.value, scope) end super end |
#visit_TryNode(o) ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/jsobfu/obfuscator.rb', line 173 def visit_TryNode(o) if o.catch_block unless @preserved_identifiers.include?(o.catch_var) o.instance_variable_set :@catch_var, rename_var(o.catch_var) end end super end |
#visit_VarDeclNode(o) ⇒ Object
Called whenever a variable is declared.
90 91 92 93 94 95 96 |
# File 'lib/jsobfu/obfuscator.rb', line 90 def visit_VarDeclNode(o) unless @preserved_identifiers.include?(o.name) o.name = JSObfu::Utils::random_var_encoding(rename_var(o.name)) end super end |