Class: JSObfu::Obfuscator
- Inherits:
-
ECMANoWhitespaceVisitor
- Object
- RKelly::Visitors::ECMAVisitor
- ECMANoWhitespaceVisitor
- JSObfu::Obfuscator
- Defined in:
- lib/jsobfu/obfuscator.rb
Constant Summary
- 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)
-
- (String) global
readonly
The global object in this JS environment.
-
- (Hash) renames
readonly
Of original var/fn names to our new random neames.
-
- (JSObfu::Scope) scope
readonly
The scope maintained while walking the ast.
Instance Method Summary (collapse)
-
- (Obfuscator) initialize(opts = {})
constructor
A new instance of Obfuscator.
- - (Boolean) is_builtin_method?(method) protected
-
- (Object) rename_var(var_name, opts = {})
protected
Assigns the var var_name a new obfuscated name.
-
- (Object) visit_DotAccessorNode(o)
Called on a dot lookup, like X.Y.
- - (Object) visit_FunctionDeclNode(o)
- - (Object) visit_FunctionExprNode(o)
- - (Object) visit_NumberNode(o)
-
- (Object) visit_ParameterNode(o)
Called when a parameter is declared.
-
- (Object) visit_PropertyNode(o)
A property node in an object "{}".
-
- (Object) visit_ResolveNode(o)
Called whenever a variable is referred to (not declared).
-
- (Object) visit_SourceElementsNode(o)
Maintains a stack of closures that we have visited.
- - (Object) visit_StringNode(o)
- - (Object) visit_TryNode(o)
-
- (Object) visit_VarDeclNode(o)
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
- (Obfuscator) initialize(opts = {})
Returns a new instance of Obfuscator
24 25 26 27 28 29 |
# File 'lib/jsobfu/obfuscator.rb', line 24 def initialize(opts={}) @scope = opts.fetch(:scope, JSObfu::Scope.new) @global = opts.fetch(:global, DEFAULT_GLOBAL).to_s @renames = {} super() end |
Instance Attribute Details
- (String) global (readonly)
Returns the global object in this JS environment
12 13 14 |
# File 'lib/jsobfu/obfuscator.rb', line 12 def global @global end |
- (Hash) renames (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 |
- (JSObfu::Scope) 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
- (Boolean) is_builtin_method?(method) (protected)
161 162 163 |
# File 'lib/jsobfu/obfuscator.rb', line 161 def is_builtin_method?(method) BUILTIN_METHODS.include?(method) end |
- (Object) rename_var(var_name, opts = {}) (protected)
Assigns the var var_name a new obfuscated name
157 158 159 |
# File 'lib/jsobfu/obfuscator.rb', line 157 def rename_var(var_name, opts={}) @renames[var_name] = scope.rename_var(var_name, opts) end |
- (Object) visit_DotAccessorNode(o)
Called on a dot lookup, like X.Y
114 115 116 117 |
# File 'lib/jsobfu/obfuscator.rb', line 114 def visit_DotAccessorNode(o) obf_str = JSObfu::Utils::transform_string(o.accessor, scope, :quotes => false) "#{o.value.accept(self)}[(#{obf_str})]" end |
- (Object) visit_FunctionDeclNode(o)
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/jsobfu/obfuscator.rb', line 59 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 |
- (Object) visit_FunctionExprNode(o)
71 72 73 74 75 76 77 |
# File 'lib/jsobfu/obfuscator.rb', line 71 def visit_FunctionExprNode(o) if o.value != 'function' o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) end super end |
- (Object) visit_NumberNode(o)
137 138 139 140 |
# File 'lib/jsobfu/obfuscator.rb', line 137 def visit_NumberNode(o) o.value = JSObfu::Utils::transform_number(o.value) super end |
- (Object) visit_ParameterNode(o)
Called when a parameter is declared. "Shadowed" parameters in the original source are preserved - the randomized name is "shadowed" from the outer scope.
121 122 123 124 125 |
# File 'lib/jsobfu/obfuscator.rb', line 121 def visit_ParameterNode(o) o.value = JSObfu::Utils::random_var_encoding(rename_var(o.value)) super end |
- (Object) visit_PropertyNode(o)
A property node in an object "{}"
128 129 130 131 132 133 134 135 |
# File 'lib/jsobfu/obfuscator.rb', line 128 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 |
- (Object) visit_ResolveNode(o)
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.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/jsobfu/obfuscator.rb', line 91 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 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 |
- (Object) visit_SourceElementsNode(o)
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.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/jsobfu/obfuscator.rb', line 42 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 |
- (Object) visit_StringNode(o)
142 143 144 145 |
# File 'lib/jsobfu/obfuscator.rb', line 142 def visit_StringNode(o) o.value = JSObfu::Utils::transform_string(o.value, scope) super end |
- (Object) visit_TryNode(o)
147 148 149 150 151 152 |
# File 'lib/jsobfu/obfuscator.rb', line 147 def visit_TryNode(o) if o.catch_block o.instance_variable_set :@catch_var, rename_var(o.catch_var) end super end |
- (Object) visit_VarDeclNode(o)
Called whenever a variable is declared.
80 81 82 83 84 |
# File 'lib/jsobfu/obfuscator.rb', line 80 def visit_VarDeclNode(o) o.name = JSObfu::Utils::random_var_encoding(rename_var(o.name)) super end |