никто из вас не исследовал, как работает загрузчик PE из mscorlib'а? именно загрузка метаданных дотнета... может, кто на вскидку вспомнит такие проекты на гитхабе или еще где? есть такая интересная задачка... для защиты от декомпиляции как то искаверкать дотнетовский PE-файл так, чтобы его сложно было восстановить из изкаверканного состояния или из дампа памяти... но при этом можно было бы его руками загрузить в текущий процесс и он нормально функционировал... дотнет версии 2.0 и выше, только олдскул, моно и неткор не берем в рассчет)... в принципе, если я правильно понимаю структуру, то загрузчику должно быть достаточно только метаданных дотнета, больше никакие PE структуры особо роли не играют... но тогда всегда можно просто сдампить метаданные из памяти и вставить их в заготовленную болванку из PE струтуры, тоже задача не для нуба, но все же довольно просто... еще, если у кого есть клевая инфа по .NET обфускаторам/протекторам, то скидывайте, я почитаю... как бы базовые вещи типа переименовывания классов/методов, шифрования константных данных и контрол флоу флеттенинг и тд я знаю... что-нить дальше этого... сорсы ConfuserEx уже изучал)...
Rel, Там же байт код для вм, он налету из модулей выбирается, не только при загрузке. Значит если пошифровать данные и затем налету их дешифровать, то получится что декомпилить нельзя, дампить тоже нечего, но при этом работает. --- Сообщение объединено, 9 май 2019 --- Я статистику посмотрел визором в семпле, взял этот https://ru.wikipedia.org/wiki/Paint.NET Что я сделал. Визор мониторит все выборки из памяти начиная со старта апп(CorExeMain()). Если выборка из основного модуля(те он адресуется), то адрес адресующей инструкции сохраняется(eg: Ip: mov r,[Image] -> save(Ip)) в массив, при условии что там такой адрес не сохранён. Тоесть в буфер накапливаются адреса инструкций которые обращались к образу. После запуска апп в буфере ~500 Ip's. На скрине буфер справа внизу. Это значит что если блокировать проекцию через исключения, то их будет слишком мало, что бы это повлияло на работу апп. Но для этого каждую выбирающую инструкцию(из этого лога) нужно изменить, те записывать туда ветвления на выбирающие стабы. Это довольно просто сделать. Тогда получится с-анклав, данные будут выбираться/дешифроваться и возвращаться без выборки в исходный образ. Это значит что в таком случае содержимое его нужно лишь опционально(в памяти его нет) для получения дешифрованных данных.
Решил реализовать эту идею. Показались первые подводные камни. 1. Инструкция вызывающая исключение(выполняющая выборку в анклав) патчится. Проблема в размере ветви, точнее это классическая проблема патча. Не в том что размера ветви не хватает для записи переходника, а как быть с jcc/jmp short. Следующая ветвь также может быть короткой. Но это мелкая помеха. 2. Многопоток, основная проблема. Точнее сложность". Выбирающая инструкция обнаруживается и патчится. Далее должна подменяться выборка https://wasm.in/blogs/softvernye-anklavy.548/ A. При этом контекст использовать нельзя, тк как rollback" его приведёт к поломке логики работы апп. Поэтому можно использовать декодер эффективного адреса(MODR/M). Для этого добавляется disp32. Тоесть каждый поток должен иметь свою инструкцию для модификации. Получается нужно при выборке добавлять инструкцию в список связанный с тредом(тлс). B. Во втором варианте можно не использовать модификацию инструкции, а привязать к локальному для треда сегменту. Те изменить EA на константу(disp32) и добавить префикс переопр. сегмента Fs. Тогда для каждого потока выборка произойдёт в TEB. При этом нужно туда и от туда копировать данные порциями. В остальном пока проблем не видно.
Запустил я это под отладкой, сразу появилась главная проблема(ожидаема). Если пропатчить ветвлением(x5) две линейные инструкции и на вторую есть ветвление, то после патча логика ломается, обычно в конце процедур. Что с этим делать не ясно, по простому видимо никак не получится.
Что бы это всё обработать я вижу лишь один способ - пересобрать налету процедуры. Это в принципе не проблема. Вопрос такой, куда тс пропал ?
тс - магл, он не понимает эльфийский, на котором Инде разговаривает... он ждет, пока кто-нить накидает техник, понятных маглам...
Попытка решить #2. Не используем патч, если размер инструкции < 5B. Задача сводится к определению начала ветви/процедуры. Так как конвенция вызова произвольна и возможны вложенные вызовы, то единственно возможный(кроме статистических, как это делается при стек трейсе; но в данном случае погрешность не допустима) вариант - взять указатель из bp-фрейма и выполнить цикл построения GP, на каждой итерации увеличивая уровень вложенности процедурной. Так постепенно в описание будет включен целевой адрес. Тогда нужно пройти по cfg назад и найти ветвление на текущую ветвь для патча(eg call > 5B) - пересобрать процедуру. Разумеется это всё должно быть синхронно, обёрнуто RWL. Это сработает, так как в попытке #1 это и была проблема. Но есчо одна проблема - как быть на x64, хотя в этом случае возможен вероятностный подход, тоесть листать стек аналогично как при стек трейсе, проверять указатель и строить GP. Попробую реализовать второй раз.
Опять неудача. Обнаружилась инструкция call [r + disp], те начала процедуры нет и узнать нельзя(r теряется). Причём размер инструкции <5, не пропатчить напрямую. Как вариант можно туда останов вписать и собирать процедуры, потом восстановить. По простому короче не получится