All files / src/node-transformers/node-obfuscators/replacers StringLiteralReplacer.ts

100% Statements 41/41
100% Branches 14/14
100% Functions 2/2
100% Lines 38/38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 1171x 1x           1x   1x 1x 1x 1x     1x       1x         1x 50x                                           3046x   3046x 3046x                 44745x       44745x 32725x     12020x               32725x       1x   1x     216x 216x   216x     32725x 32724x     32725x   32725x   32725x 26548x   6177x 6177x     32725x 32725x 32725x   32725x 216x     32509x      
import { injectable, inject } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
 
import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IStorage } from '../../../interfaces/storages/IStorage';
 
import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
 
import { AbstractReplacer } from './AbstractReplacer';
import { CryptUtils } from '../../../utils/CryptUtils';
import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
import { Utils } from '../../../utils/Utils';
 
@injectable()
export class StringLiteralReplacer extends AbstractReplacer {
    /**
     * @type {number}
     */
    private static readonly minimumLengthForStringArray: number = 3;
 
    /**
     * @type {string[]}
     */
    private static readonly rc4Keys: string[] = RandomGeneratorUtils.getRandomGenerator()
        .n(() => RandomGeneratorUtils.getRandomGenerator().string({length: 4}), 50);
 
    /**
     * @type {IStorage<ICustomNodeGroup>}
     */
    private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
 
    /**
     * @type {IStorage<string>}
     */
    private readonly stringArrayStorage: IStorage<string>;
 
    /**
     * @param customNodeGroupStorage
     * @param stringArrayStorage
     * @param options
     */
    constructor (
        @inject(ServiceIdentifiers['IStorage<ICustomNodeGroup>']) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
        @inject(ServiceIdentifiers['IStorage<string>']) stringArrayStorage: IStorage<string>,
        @inject(ServiceIdentifiers.IOptions) options: IOptions
    ) {
        super(options);
 
        this.customNodeGroupStorage = customNodeGroupStorage;
        this.stringArrayStorage = stringArrayStorage;
    }
 
    /**
     * @param nodeValue
     * @returns {string}
     */
    public replace (nodeValue: string): string {
        const replaceWithStringArrayFlag: boolean = (
            nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray
            && RandomGeneratorUtils.getRandomFloat(0, 1) <= this.options.stringArrayThreshold
        );
 
        if (this.options.stringArray && replaceWithStringArrayFlag) {
            return this.replaceStringLiteralWithStringArrayCall(nodeValue);
        }
 
        return `'${Utils.stringToUnicodeEscapeSequence(nodeValue)}'`;
    }
 
    /**
     * @param value
     * @returns {string}
     */
    private replaceStringLiteralWithStringArrayCall (value: string): string {
        let rc4Key: string = '';
 
        switch (this.options.stringArrayEncoding) {
            case StringArrayEncoding.base64:
                value = CryptUtils.btoa(value);
 
                break;
 
            case StringArrayEncoding.rc4:
                rc4Key = RandomGeneratorUtils.getRandomGenerator().pickone(StringLiteralReplacer.rc4Keys);
                value = CryptUtils.btoa(CryptUtils.rc4(value, rc4Key));
 
                break;
        }
 
        if (this.options.unicodeEscapeSequence) {
            value = Utils.stringToUnicodeEscapeSequence(value);
        }
 
        const indexOfExistingValue: number = <number>this.stringArrayStorage.getKeyOf(value);
 
        let indexOfValue: number;
 
        if (indexOfExistingValue >= 0) {
            indexOfValue = indexOfExistingValue;
        } else {
            indexOfValue = this.stringArrayStorage.getLength();
            this.stringArrayStorage.set(null, value);
        }
 
        const rotatedStringArrayStorageId: string = Utils.stringRotate(this.stringArrayStorage.getStorageId(), 2);
        const stringArrayStorageCallsWrapperName: string = `_${Utils.hexadecimalPrefix}${rotatedStringArrayStorageId}`;
        const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(indexOfValue)}`;
 
        if (this.options.stringArrayEncoding === StringArrayEncoding.rc4) {
            return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}', '${Utils.stringToUnicodeEscapeSequence(rc4Key)}')`;
        }
 
        return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}')`;
    }
}