Методы деобфускации javascript, который использует конкатенацию строк для имен свойств

Я пытаюсь понять, как расшифровать javascript, который выглядит так:

https://jsfiddle.net/douglasg14b/4951br9f/2/

var testString = 'Test | String'

var wf6 = {
 fq4: 'su',
 k8d: 'bs',
 l8z: 'tri',
 cy1: 'ng',
 t5j: 'te',
 ol: 'stS',
 x3q: 'tri',
 l9x: 'ng',
 gh: 'xO'
};


//Obfuscated
let test1 = testString[wf6.fq4 + wf6.k8d + wf6.l8z + wf6.cy1](4,11);

//Normal
let test2 = testString.substring(4,11);
let test3;

//More complex obfuscation
(function moreComplex(){
	let h = "i",
        w = "nde",
        T0 = "f",
        hj = '|',
        a = eval(wf6.t5j + wf6.ol + wf6.x3q + wf6.l9x).length;
    //Obfuscated
    test3 = testString[wf6.fq4 + wf6.k8d + wf6.l8z + wf6.cy1](testString[h + w + wf6.gh + T0](hj), a);
    
    //Normal
    let test4 = testString.substring(testString.indexOf('|'), testString.length);
        
})();

$('.span1').text(test1);
$('.span2').text(test3);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="span1"></span><br>
<span class="span2"></span>

Это небольшой пример, файл, с которым я работаю, имеет длину ~ 60 тыс. строк и полон такого рода обфускации. Везде, где в качестве имени свойства может быть использована строка, используется этот вид запутывания.

Я могу придумать, как это сделать, чтобы оценить все конкатенации строк, чтобы они превратились в читаемый эквивалент. Хотя я не уверен, как это сделать и игнорировать весь другой рабочий код, который существует между всеми конкатенациями.

Мысли?

Дополнительный вопрос. Есть ли обычно используемое название для такого рода запутывания, которое могло бы упростить поиск?

Изменить: добавлен более сложный пример.


person Douglas Gaskell    schedule 08.10.2016    source источник


Ответы (1)


Основная идея у вас верна: вам нужно частично оценить запрограммировать и предварительно вычислить все постоянные вычисления. В вашем случае постоянные вычисления, представляющие основной интерес, представляют собой шаги конкатенации над значениями, которые не меняются.

Для этого вам понадобится система преобразования программ (PTS). Это инструмент, который будет читать/анализировать исходный код для указанного языка и строить абстрактное синтаксическое дерево, позволять вам задавать преобразования и анализировать AST и запускать их, а затем снова выдавать модифицированный AST как исходный код.

В вашем случае вы, очевидно, хотите, чтобы PTS знал JavaScript из коробки (редко) или готов принять описание JavaScript, а затем прочитать JavaScript (что более типично) в надежде, что вы сможете создать или получить JavaScript. описание легко. [Я создаю PTS с доступными описаниями JavaScript, см. мою биографию].

Имея это в руках, вам необходимо:

  • закодируйте анализатор, который проверяет каждую переменную, найденную в выражении, чтобы определить, является ли это выражение константой (например, "wf6"). Чтобы продемонстрировать, что это константа, вам нужно будет найти определение переменной и убедиться, что все значения, используемые в определении переменной, сами по себе являются константами. Если существует более одного определения переменной, возможно, вам придется проверить, что все определения дают одно и то же значение. Вам необходимо проверить наличие побочных эффектов для переменной (например, нет вызовов функций "foo(...,wf6,...)", которые позволили бы изменить значение переменной). Вам нужно беспокоиться о том, существует ли команда eval для достижения такого побочного эффекта [это практически невозможно сделать, поэтому вам часто приходится просто игнорировать eval и предполагать, что они не делают таких вещей]. У многих PTS есть возможность создавать такие анализаторы; некоторые легче, чем другие.
  • Для каждой переменной с постоянным значением замените значение этой переменной в коде
  • Для каждого подвыражения с постоянным значением после таких замен «сложите» (вычислите) результат этого выражения и подставьте это значение для этого подвыражения и повторяйте до тех пор, пока дальнейшее свертывание невозможно. Очевидно, вы хотите сделать это, по крайней мере, для всех операторов "+". [OP только что изменил свой пример; он захочет сделать это и для "eval" операторов, когда все его операнды постоянны].
  • Возможно, вам придется повторить этот процесс, так как свертывание выражения может сделать очевидным, что переменная теперь имеет постоянное значение.

Описанный выше процесс в литературе по компиляторам называется "константным распространением" и является особенностью многих компиляторов.

В вашем случае вы можете ограничить свертывание констант только конкатенацией строк. Однако, если у вас есть адекватный механизм для распространения значений констант, выполнение всех или большинства операций над константами не так сложно. Вам может понадобиться это, чтобы отменить другие обфускации, связанные с константами, так как это похоже на стиль обфускации, используемый в коде, над которым вы работаете.

Вам понадобится специальное правило, которое преобразует

var['string'](args)

в

 var.string(args)

как завершающий шаг.

У вас есть еще одна сложность: вы знаете, что у вас есть весь JavaScript, относящийся к созданию переменных с постоянными значениями. На одной веб-странице может быть много включенных фрагментов JavaScript; вам понадобятся все они, чтобы продемонстрировать отсутствие побочных эффектов для переменной. Я предполагаю, что в вашем случае вы уверены, что у вас есть все это.

Что касается создания значений с известными константами, вас может волновать сложный случай: выражение, которое создает постоянные значения из непостоянных операндов. Представьте, что запутанное выражение было:

   x=random(); // produce a value between 0 and 1
   one=x+(1-x); // not constant by constant propagation, but constant by algebraic relations
   teststring['st'[one]+'vu'[one+1]+'bz'[one]+...](4,11)

Вы можете видеть, что он всегда вычисляет «подстроку» как свойство. Вы можете добавить правило преобразования, которое понимает прием, используемый для вычисления единицы, например, правило для каждого алгебраического приема, используемого для вычисления известных констант. К сожалению для вас, существует бесконечное количество алгебраических теорем, которые можно использовать для создания констант; сколько действительно используется в вашем примере бит кода? [Добро пожаловать к проблеме реверс-инжиниринга с умным противником].

Нет, ничего из этого "легкого". Вероятно, именно поэтому был выбран используемый метод запутывания.

person Ira Baxter    schedule 08.10.2016
comment
Можете ли вы связать инструмент, который вы написали? Может ли он делать то, о чем здесь просят? - person Bergi; 08.10.2016
comment
Если вы можете выполнить анализ константы вручную (что обычно просто, поскольку обфускаторы в основном используют только константы), вы часто можете выполнить оценку вручную выполнение замены регулярных выражений в коде - person Bergi; 08.10.2016
comment
@Bergi: этот инструмент называется набором инструментов для реинжиниринга программного обеспечения DMS. См. semanticdesigns.com/Products/DMS/DMSToolkit.html. - person Ira Baxter; 08.10.2016
comment
@Bergi: регулярное выражение, применяемое к коду, обычно не работает, потому что вы применяете его к тексту с вложенными структурами () [] {} , а регулярные выражения не могут отслеживать вложенность. См. stackoverflow.com/questions/1732348/ Как бы вы решили проблему OP таким образом? Приведите конкретный пример и помните, что у него 60 тысяч строк кода. - person Ira Baxter; 08.10.2016
comment
Ух ты! Это чертовски хороший ответ, довольно продвинутый и тяжелый материал. Это не то, что я могу сделать быстро или даже быстро понять. Определенно что-то, что займет у меня некоторое время, чтобы полностью понять и использовать. Спасибо. - person Douglas Gaskell; 09.10.2016
comment
@IraBaxter Я бы взял объект замены, а затем сделал бы code.replace(/wf6\.(\w+)/g, (_, p) => JSON.stringify(wf6[p])).replace(/"\s+\+\s+"/g, "").replace(/(\w+)\["(\w+)"\]/g, "$1.$2"). Конечно, это не работает для общего случая, это быстрое и грязное решение, которое вы можете использовать, когда у вас нет PTS под рукой. Часто инструменты запутывания недостаточно сложны, чтобы выполнять несколько проходов с разными заменами, поэтому одна замена регулярного выражения, как указано выше, обычно уже приводит к довольно читаемому коду. - person Bergi; 09.10.2016
comment
@Bergi: Да, если бы вы знали пары ‹varname,constvalue› заранее, вы могли бы сделать это, и это могло бы работать относительно хорошо с конкретным приведенным примером (может быть, от wf1, wf2, ... wf6, ... wf9721 до сделать это действительно раздражающим даже таким образом). Тупые обфускаторы могут быть спасителями ОП. Умный добавил бы несколько различных методов запутывания, которые составляют (что делает отмену почти невозможной с помощью регулярных выражений). Мое личное мнение таково, что Оп, возможно, лучше найти что-то более конструктивное, чем распутывание, но если он настаивает, ему придется попробовать другие методы. - person Ira Baxter; 09.10.2016
comment
@Bergi: плохая сторона использования подобных регулярных выражений - это ручной шаг. Если Op собирается декодировать только одну программу, то это может быть выходом. Если ему нужно декодировать вторую версию той же программы, в которой ‹varname,constvalues› были зашифрованы случайным образом, это будет довольно сложно. Если он хочет делать много этого, ручная работа, вероятно, будет непреодолимой. Так что его непосредственные цели имеют значение. - person Ira Baxter; 09.10.2016
comment
@IraBaxter полностью согласен. - person Bergi; 09.10.2016