@@ -13,22 +13,17 @@ import {
13
13
BinaryExpression ,
14
14
FunctionDeclaration ,
15
15
ThisExpression ,
16
- FunctionExpression ,
16
+ ConditionalExpression ,
17
17
} from "../../util/gen" ;
18
- import {
19
- append ,
20
- clone ,
21
- getLexContext ,
22
- isLexContext ,
23
- prepend ,
24
- } from "../../util/insert" ;
18
+ import { append , clone , prepend } from "../../util/insert" ;
25
19
import { isDirective , isPrimitive } from "../../util/compare" ;
26
20
27
21
import { ObfuscateOrder } from "../../order" ;
28
22
import { isModuleSource } from "../string/stringConcealing" ;
29
23
import { ComputeProbabilityMap } from "../../probability" ;
30
24
import { ok } from "assert" ;
31
25
import { chance , choice , getRandomInteger } from "../../util/random" ;
26
+ import { getBlock } from "../../traverse" ;
32
27
33
28
/**
34
29
* [Duplicate Literals Removal](https://docs.jscrambler.com/code-integrity/documentation/transformations/duplicate-literals-removal) replaces duplicate literals with a variable name.
@@ -49,35 +44,40 @@ import { chance, choice, getRandomInteger } from "../../util/random";
49
44
* ```
50
45
*/
51
46
export default class DuplicateLiteralsRemoval extends Transform {
47
+ // The array holding all the duplicate literals
52
48
arrayName : string ;
49
+ // The array expression node to be inserted into the program
53
50
arrayExpression : Node ;
51
+
52
+ /**
53
+ * Literals in the array
54
+ */
54
55
map : Map < string , number > ;
55
- first : Map < string , Location | null > ;
56
56
57
57
/**
58
- * getter fn name -> accumulative shift
58
+ * Literals are saved here the first time they are seen.
59
59
*/
60
- fnShifts : Map < string , number > ;
60
+ first : Map < string , Location > ;
61
61
62
62
/**
63
- * lex context -> getter fn name
63
+ * Block -> { functionName, indexShift }
64
64
*/
65
- fnGetters : Map < Node , string > ;
65
+ functions : Map < Node , { functionName : string ; indexShift : number } > ;
66
66
67
67
constructor ( o ) {
68
68
super ( o , ObfuscateOrder . DuplicateLiteralsRemoval ) ;
69
69
70
70
this . map = new Map ( ) ;
71
71
this . first = new Map ( ) ;
72
72
73
- this . fnShifts = new Map ( ) ;
74
- this . fnGetters = new Map ( ) ;
73
+ this . functions = new Map ( ) ;
75
74
}
76
75
77
76
apply ( tree ) {
78
77
super . apply ( tree ) ;
79
78
80
- if ( this . arrayName && this . arrayExpression . elements . length ) {
79
+ if ( this . arrayName && this . arrayExpression . elements . length > 0 ) {
80
+ // This function simply returns the array
81
81
var getArrayFn = this . getPlaceholder ( ) ;
82
82
append (
83
83
tree ,
@@ -88,22 +88,79 @@ export default class DuplicateLiteralsRemoval extends Transform {
88
88
)
89
89
) ;
90
90
91
+ // This variable holds the array
91
92
prepend (
92
93
tree ,
93
94
VariableDeclaration (
94
95
VariableDeclarator (
95
96
this . arrayName ,
96
97
CallExpression (
97
- MemberExpression (
98
- Identifier ( getArrayFn ) ,
99
- Identifier ( "call" ) ,
100
- false
101
- ) ,
98
+ MemberExpression ( Identifier ( getArrayFn ) , Literal ( "call" ) , true ) ,
102
99
[ ThisExpression ( ) ]
103
100
)
104
101
)
105
102
)
106
103
) ;
104
+
105
+ // Create all the functions needed
106
+ for ( var blockNode of this . functions . keys ( ) ) {
107
+ var { functionName, indexShift } = this . functions . get ( blockNode ) ;
108
+
109
+ var propertyNode : Node = BinaryExpression (
110
+ "-" ,
111
+ Identifier ( "index_param" ) ,
112
+ Literal ( indexShift )
113
+ ) ;
114
+
115
+ var indexRangeInclusive = [
116
+ 0 + indexShift - 1 ,
117
+ this . map . size + indexShift ,
118
+ ] ;
119
+
120
+ // The function uses mangling to hide the index being accessed
121
+ var mangleCount = getRandomInteger ( 1 , 10 ) ;
122
+ for ( var i = 0 ; i < mangleCount ; i ++ ) {
123
+ var operator = choice ( [ ">" , "<" ] ) ;
124
+ var compareValue = choice ( indexRangeInclusive ) ;
125
+
126
+ var test = BinaryExpression (
127
+ operator ,
128
+ Identifier ( "index_param" ) ,
129
+ Literal ( compareValue )
130
+ ) ;
131
+
132
+ var alternate = BinaryExpression (
133
+ "-" ,
134
+ Identifier ( "index_param" ) ,
135
+ Literal ( getRandomInteger ( - 100 , 100 ) )
136
+ ) ;
137
+
138
+ var testValue =
139
+ ( operator === ">" && compareValue === indexRangeInclusive [ 0 ] ) ||
140
+ ( operator === "<" && compareValue === indexRangeInclusive [ 1 ] ) ;
141
+
142
+ propertyNode = ConditionalExpression (
143
+ test ,
144
+ testValue ? propertyNode : alternate ,
145
+ ! testValue ? propertyNode : alternate
146
+ ) ;
147
+ }
148
+
149
+ var returnArgument = MemberExpression (
150
+ Identifier ( this . arrayName ) ,
151
+ propertyNode ,
152
+ true
153
+ ) ;
154
+
155
+ prepend (
156
+ blockNode ,
157
+ FunctionDeclaration (
158
+ functionName ,
159
+ [ Identifier ( "index_param" ) ] ,
160
+ [ ReturnStatement ( returnArgument ) ]
161
+ )
162
+ ) ;
163
+ }
107
164
}
108
165
}
109
166
@@ -122,104 +179,44 @@ export default class DuplicateLiteralsRemoval extends Transform {
122
179
* @param parents
123
180
* @param index
124
181
*/
125
- toCaller ( object : Node , parents : Node [ ] , index : number ) {
126
- // get all the getters defined here or higher
127
- var getterNames = [ object , ...parents ]
128
- . map ( ( x ) => this . fnGetters . get ( x ) )
129
- . filter ( ( x ) => x ) ;
130
-
131
- // use random getter function
132
- var getterName = choice ( getterNames ) ;
133
-
134
- // get this literals context
135
- var lexContext = getLexContext ( object , parents ) ;
136
-
137
- var hasGetterHere = this . fnGetters . has ( lexContext ) ;
138
-
139
- // create one if none are available (or by random chance if none are here locally)
140
- var shouldCreateNew =
141
- ! getterName || ( ! hasGetterHere && Math . random ( ) > 0.9 ) ;
142
-
143
- if ( shouldCreateNew ) {
144
- ok ( ! this . fnGetters . has ( lexContext ) ) ;
145
-
146
- var lexContextIndex = parents . findIndex (
147
- ( x ) => x !== lexContext && isLexContext ( x )
148
- ) ;
149
- var basedOn =
150
- lexContextIndex !== - 1
151
- ? choice (
152
- parents
153
- . slice ( lexContextIndex + 1 )
154
- . map ( ( x ) => this . fnGetters . get ( x ) )
155
- . filter ( ( x ) => x )
156
- )
157
- : null ;
158
-
159
- var body = [ ] ;
160
- var thisShift = getRandomInteger ( - 250 , 250 ) ;
161
- // the name of the getter
162
- getterName = this . getPlaceholder ( ) + "_dLR_" + this . fnGetters . size ;
163
-
164
- if ( basedOn ) {
165
- var shift = this . fnShifts . get ( basedOn ) ;
166
- ok ( typeof shift === "number" ) ;
167
-
168
- body = [
169
- ReturnStatement (
170
- CallExpression ( Identifier ( basedOn ) , [
171
- BinaryExpression ( "+" , Identifier ( "index" ) , Literal ( thisShift ) ) ,
172
- ] )
173
- ) ,
174
- ] ;
175
-
176
- this . fnShifts . set ( getterName , shift + thisShift ) ;
177
- } else {
178
- // from scratch
179
-
180
- body = [
181
- ReturnStatement (
182
- MemberExpression (
183
- Identifier ( this . arrayName ) ,
184
- BinaryExpression ( "+" , Identifier ( "index" ) , Literal ( thisShift ) ) ,
185
- true
186
- )
187
- ) ,
188
- ] ;
182
+ transformLiteral ( object : Node , parents : Node [ ] , index : number ) {
183
+ var blockNode = choice ( parents . filter ( ( x ) => this . functions . has ( x ) ) ) ;
184
+
185
+ // Create initial function if none exist
186
+ if ( this . functions . size === 0 ) {
187
+ var root = parents [ parents . length - 1 ] ;
188
+ var rootFunctionName = this . getPlaceholder ( ) + "_dLR_0" ;
189
+ this . functions . set ( root , {
190
+ functionName : rootFunctionName ,
191
+ indexShift : getRandomInteger ( - 100 , 100 ) ,
192
+ } ) ;
193
+
194
+ blockNode = root ;
195
+ }
189
196
190
- this . fnShifts . set ( getterName , thisShift ) ;
191
- }
197
+ // If no function here exist, possibly create new chained function
198
+ var block = getBlock ( object , parents ) ;
199
+ if ( ! this . functions . has ( block ) && chance ( 50 - this . functions . size ) ) {
200
+ var newFunctionName =
201
+ this . getPlaceholder ( ) + "_dLR_" + this . functions . size ;
192
202
193
- this . fnGetters . set ( lexContext , getterName ) ;
203
+ this . functions . set ( block , {
204
+ functionName : newFunctionName ,
205
+ indexShift : getRandomInteger ( - 100 , 100 ) ,
206
+ } ) ;
194
207
195
- prepend (
196
- lexContext ,
197
- VariableDeclaration (
198
- VariableDeclarator (
199
- getterName ,
200
- CallExpression (
201
- FunctionExpression (
202
- [ ] ,
203
- [
204
- ReturnStatement (
205
- FunctionExpression ( [ Identifier ( "index" ) ] , body )
206
- ) ,
207
- ]
208
- ) ,
209
- [ ]
210
- )
211
- )
212
- )
213
- ) ;
208
+ blockNode = block ;
214
209
}
215
210
216
- var theShift = this . fnShifts . get ( getterName ) ;
211
+ // Derive the function to call from the selected blockNode
212
+ var { functionName, indexShift } = this . functions . get ( blockNode ) ;
217
213
218
- this . replaceIdentifierOrLiteral (
219
- object ,
220
- CallExpression ( Identifier ( getterName ) , [ Literal ( index - theShift ) ] ) ,
221
- parents
222
- ) ;
214
+ // Call the function given it's indexShift
215
+ var callExpression = CallExpression ( Identifier ( functionName ) , [
216
+ Literal ( index + indexShift ) ,
217
+ ] ) ;
218
+
219
+ this . replaceIdentifierOrLiteral ( object , callExpression , parents ) ;
223
220
}
224
221
225
222
transform ( object : Node , parents : Node [ ] ) {
@@ -234,7 +231,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
234
231
}
235
232
236
233
// HARD CODED LIMIT of 10,000 (after 1,000 elements)
237
- if ( this . map . size > 1000 && ! chance ( this . map . size / 100 ) ) return ;
234
+ if ( this . map . size > 1000 && chance ( this . map . size / 100 ) ) return ;
238
235
239
236
if (
240
237
this . arrayName &&
@@ -244,54 +241,58 @@ export default class DuplicateLiteralsRemoval extends Transform {
244
241
return ;
245
242
}
246
243
247
- var value ;
244
+ var stringValue ;
248
245
if ( object . type == "Literal" ) {
249
- value = typeof object . value + ":" + object . value ;
246
+ stringValue = typeof object . value + ":" + object . value ;
250
247
if ( object . value === null ) {
251
- value = "null:null" ;
248
+ stringValue = "null:null" ;
252
249
} else {
253
250
// Skip empty strings
254
251
if ( typeof object . value === "string" && ! object . value ) {
255
252
return ;
256
253
}
257
254
}
258
255
} else if ( object . type == "Identifier" ) {
259
- value = "identifier:" + object . name ;
256
+ stringValue = "identifier:" + object . name ;
260
257
} else {
261
258
throw new Error ( "Unsupported primitive type: " + object . type ) ;
262
259
}
263
260
264
- ok ( value ) ;
261
+ ok ( stringValue ) ;
265
262
266
- if ( ! this . first . has ( value ) && ! this . map . has ( value ) ) {
267
- this . first . set ( value , [ object , parents ] ) ;
268
- } else {
263
+ if ( this . map . has ( stringValue ) || this . first . has ( stringValue ) ) {
264
+ // Create the array if not already made
269
265
if ( ! this . arrayName ) {
270
266
this . arrayName = this . getPlaceholder ( ) ;
271
267
this . arrayExpression = ArrayExpression ( [ ] ) ;
272
268
}
273
269
274
- var firstLocation = this . first . get ( value ) ;
270
+ // Delete with first location
271
+ var firstLocation = this . first . get ( stringValue ) ;
275
272
if ( firstLocation ) {
276
- this . first . set ( value , null ) ;
277
273
var index = this . map . size ;
278
274
279
- ok ( ! this . map . has ( value ) ) ;
280
- this . map . set ( value , index ) ;
275
+ ok ( ! this . map . has ( stringValue ) ) ;
276
+ this . map . set ( stringValue , index ) ;
277
+ this . first . delete ( stringValue ) ;
281
278
282
279
var pushing = clone ( object ) ;
283
280
this . arrayExpression . elements . push ( pushing ) ;
284
281
285
282
ok ( this . arrayExpression . elements [ index ] === pushing ) ;
286
283
287
- this . toCaller ( firstLocation [ 0 ] , firstLocation [ 1 ] , index ) ;
284
+ this . transformLiteral ( firstLocation [ 0 ] , firstLocation [ 1 ] , index ) ;
288
285
}
289
286
290
- var index = this . map . get ( value ) ;
287
+ var index = this . map . get ( stringValue ) ;
291
288
ok ( typeof index === "number" ) ;
292
289
293
- this . toCaller ( object , parents , index ) ;
290
+ this . transformLiteral ( object , parents , index ) ;
291
+ return ;
294
292
}
293
+
294
+ // Save this, maybe a duplicate will be found.
295
+ this . first . set ( stringValue , [ object , parents ] ) ;
295
296
} ;
296
297
}
297
298
}
0 commit comments