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
125 126 127 128 129 130 131 132 |
# File 'lib/jsobfu/obfuscator.rb', line 125 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
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/jsobfu/obfuscator.rb', line 66 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
80 81 82 83 84 85 86 |
# File 'lib/jsobfu/obfuscator.rb', line 80 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
156 157 158 159 160 161 162 |
# File 'lib/jsobfu/obfuscator.rb', line 156 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.
136 137 138 139 140 141 142 |
# File 'lib/jsobfu/obfuscator.rb', line 136 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 “{}”
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/jsobfu/obfuscator.rb', line 145 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.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/jsobfu/obfuscator.rb', line 102 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 |
# 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 scope.pop! ret end |
#visit_StringNode(o) ⇒ Object
164 165 166 167 168 169 170 |
# File 'lib/jsobfu/obfuscator.rb', line 164 def visit_StringNode(o) unless @memory_sensitive o.value = JSObfu::Utils::transform_string(o.value, scope) end super end |
#visit_TryNode(o) ⇒ Object
172 173 174 175 176 177 178 179 |
# File 'lib/jsobfu/obfuscator.rb', line 172 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.
89 90 91 92 93 94 95 |
# File 'lib/jsobfu/obfuscator.rb', line 89 def visit_VarDeclNode(o) unless @preserved_identifiers.include?(o.name) o.name = JSObfu::Utils::random_var_encoding(rename_var(o.name)) end super end |