# 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}')`;
        }
    }