![]() |
eXeL@B —› Вопросы новичков —› Помогите определить виртуальную машину из MapEdit (с Geopainting.com) |
Посл.ответ | Сообщение |
|
Создано: 22 сентября 2013 20:08 · Поправил: pinky_p · Личное сообщение · #1 Итак, есть программа MapEdit для работы с картами. http://geopainting.com/download/mapedit2-0-77-1.zip Я хочу разобраться, как она делает некоторые вещи, но нужные мне участки кода исполняются на виртуальной машине. Для примера рассмотрим функцию some_gui_func(). Она вызывает функцию VM_ENTER(), и опкоды, идущие после вызова (начиная с адреса some_gui_func_VM_ENTER), исполняются на виртуальной машине. Оригинальные опкоды перезаписаны мусором, а исполняемые виртуализированные опкоды загружаются из ресурсов EXE (LoadResource(GetModuleHandle(0), 1, 0x0A)) функцией VM_LoadOpcodes(). VM_ENTER() сохраняет значения регистров и передает управление VM_ENTER_GATE(), которая вызывает VM_RUN(), которая и исполняет виртуализированные опкоды. Я почти полностью дизассемблировал VM_RUN(), это не сложно. Проблема в том, что VM_RUN() может вызывать дополнительные функции в зависимости от опкода и их довольно много. Первые два виртуализированных опкода some_gui_func() в запакованном виде в ресурсах EXE выглядят так: Code:
Они же в распакованном виде при исполнении: Code:
Адрес очередного опкода находится в регистре EDI при исполнении метки next_opcode внутри VM_RUN(). Нужные адреса внутри mapedit.exe: Code:
Также приаттачен IDC-скрипт для добавления имен в базу. Может быть кто-то знает, что это за виртуальная машина и имеет дизассемблер для нее? Очень надеюсь сэкономить себе кучу времени. Дополнительное обсуждение защиты программы: ![]() ![]() |
|
Создано: 22 сентября 2013 21:16 · Поправил: reversecode · Личное сообщение · #2 вообще то как то так .text:0066A488 _CALL_VM_sub_66A488 .text:0066A588 _LEAVE_VM_sub_66A588 и вообще они там не один если судить по рефам на .data:007D77C8 _VM_crit_stru_7D77C8 .data:0078F86C off_78F86C dd offset sub_668647 похоже на сами обработчики опкодов вм еще там подозрительный аллокатор new и delete .text:0040327D _new_sub_40327D там на маленькие куски до 128 байт, по другому срабатывает лень думать, но как быоно не на саму ВМ работало, да еще и может свою адрессацию, а может все таки просто соптимизировано для опкодов, что бы ускорить скорость самой вм дальше интерес пропал ![]() ![]() |
|
Создано: 22 сентября 2013 21:38 · Личное сообщение · #3 reversecode пишет: .text:0066A488 _CALL_VM_sub_66A488 .text:0066A588 _LEAVE_VM_sub_66A588 Эти функции - это на самом деле одна функция. Это один из обработчиков опкодов (а именно - CALL), которые вызывает VM_RUN. То есть, VM_RUN формирует указатель на операнд1, операнд2 и передает это в доп обработчик. reversecode пишет: data:0078F86C off_78F86C dd offset sub_668647 похоже на сами обработчики опкодов вм Все верно, только это не сами обработчики, а аллокаторы объектов обработчиков. Короче, эта VM исполняет те же x86 опкоды, записанные в другой форме. И я надеюсь, что кто-то ее знает и имеет дизассемблер. ![]() |
|
Создано: 22 сентября 2013 21:44 · Поправил: reversecode · Личное сообщение · #4 pinky_p пишет: Эти функции - это на самом деле одна функция. это и так понятно) рад что вы тоже это видите pinky_p пишет: Все верно, только это не сами обработчики, а аллокаторы объектов обработчиков. это сами обработчики только опкоды написаны на с++, и для вас это аллокаторы для меня это обработчики потому что это таблица соответствий pinky_p пишет: И я надеюсь, что кто-то ее знает и имеет дизассемблер. глупая затея ждать что кто то даже если имеет то поделится) реверсите сами каждый обработчик ![]() |
|
Создано: 02 октября 2013 19:09 · Личное сообщение · #5 Разбудили вы мой интерес к этой программе. ВМ действительно не сложная, обработчики КОПов даже без обфускации. Но все это сделано специально для этой программы! Автор постарался, даже FPU не поленился добавить. Хотя вот логику ту же можно было в NAND или NOR разложить. Пока сложно догонять, что делают вот такие хендлеры: Code:
Добавлено спустя 9 часов 43 минуты pinky_p На данный момент у меня такая таблица. Не совпадения/дополнения есть? Code:
![]() |
|
Создано: 03 октября 2013 12:04 · Личное сообщение · #6 int Блин, ну вы просто монстр ![]() У меня сейчас очень мало времени, поэтому я определил hand_call_x86_func и на том забил. Еще добавлю, что sub_66A37E - это джамп, условный или безусловный (условие проверяется в VM_RUN), причем оно может прыгать за пределы VM (??). sub_66AC5D вычисляет адрес виртуализированной инструкции, куда прыгать. ![]() |
|
Создано: 03 октября 2013 14:12 · Поправил: reversecode · Личное сообщение · #7 |
|
Создано: 03 октября 2013 15:23 · Личное сообщение · #8 pinky_p пишет: Блин, ну вы просто монстр Да ладно, после вмпрота и обсида это цветочки. Посмотрел куда сохраняются регистры, проследил куда они копируются. Дальше понятно - смотрим какие параметры попадают в обработчики. Создал три структуры под классы которые всем заправляют. Дальше декомпилятор сам показывает код обработчиков int пишет: Пока сложно догонять, что делают вот такие хендлеры: Вот этот обработчик для примера оказался std. А вот как сейчас выглядит код в IDA: Code:
А вот так в лучах: Code:
Тут даже думать не надо, о том, что это за обработчик. Сразу видно. Взяли объект класса vm_object, у него в начале переменные типа char с флагами (туда memcpy копирует их - легко проследить). По смещению понятно, что это флаг DF, IDA сама его предлагает в пункте контекстного меню "Structure offset". Ну а дальше справочник по x86 командам - это std (хотя данная инструкция очень популярная, ее и так запомнить не сложно, а вот всякие бит тесты типа bsr, bsf, bts - это не упомнишь, я гуглил их описания). Поэтому ничего монстерского тут нет. Я вот переходы еще пока не разобрал, VM_x_FLAG_cmp_sub_66A238 очень не хватало) Терпеть не могу флаги разбирать и условные переходы. pinky_p пишет: sub_66A37E - это джамп, условный или безусловный На этом я стормозил, меня vm_exit (0x66A3D3) испугал в теле этой функции. ![]() |
|
Создано: 03 октября 2013 15:34 · Личное сообщение · #9 |
|
Создано: 03 октября 2013 16:23 · Поправил: RebelNeo · Личное сообщение · #10 |
|
Создано: 03 октября 2013 16:53 · Личное сообщение · #11 reversecode пишет: VM_x_FLAGS_sub_66A212 Я назвал set_flags_ZSP. ZSP подчеркивает, что другие флаги метод не трогает. Почти все обработчики распознаны (не без вашей помощи ![]() Code:
На кой черт две инструкции безусловного перехода hand_jmp_1 и hand_jmp_2 пока не пойму. Может по размеру перехода отличаются. Там просто пока классы/структуры используются, которые заполняются в тех местах, где я еще не делал анализ. Флаговые инструкции hand_jcc и hand_setcc тоже легко распознались, благодаря reversecode (спасибо за VM_x_FLAG_cmp_sub_66A238). Также все легко и понятно с hand_jecxz и hand_loop. Но вот что это за конструкции, мне пока непонятно, прошу помощь зала: Code:
Т.е. что-то похожее на переходы/циклы. Только я не припомню, чтобы флаг нуля участвовал при определении необходимости сделать переход. ![]() ![]() |
|
Создано: 03 октября 2013 17:01 · Личное сообщение · #12 |
|
Создано: 03 октября 2013 17:03 · Личное сообщение · #13 |
|
Создано: 03 октября 2013 17:05 · Поправил: ARCHANGEL · Личное сообщение · #14 |
|
Создано: 03 октября 2013 17:08 · Личное сообщение · #15 |
|
Создано: 03 октября 2013 17:09 · Поправил: reversecode · Личное сообщение · #16 |
|
Создано: 03 октября 2013 18:01 · Личное сообщение · #17 reversecode Да пофиг на него, плагин к IDA, бряк на vm_entry и вперед - Edit -> Plugins -> KillMathaFuckinVM ![]() А вообще еще два года назад у меня была идея создать универсальный декомпилятор для виртуальных машин. По моим оценкам он бы победил вмпротект, фимку, код виртуализер, криптер, обсидиум. И с этой ВМ он бы в два счета бы разобрался. Не удается победить пока StarForce (слишком не шаблонная реализация), Enigma - для этих ВМ нужен частный инструмент, заточенный специально под реализацию ВМ. Вопрос только в том, что для создания такого инструмента нужно очень много времени, которого обычно нет. ![]() |
|
Создано: 03 октября 2013 18:03 · Личное сообщение · #18 |
|
Создано: 03 октября 2013 18:07 · Личное сообщение · #19 |
|
Создано: 03 октября 2013 18:12 · Личное сообщение · #20 |
|
Создано: 03 октября 2013 19:05 · Личное сообщение · #21 Да все гениальное просто. Составляем шаблоны всех существующих инструкций x86. Ищем циклическую обработку КОПов - конвеер ленты ВМ. Алгоритм реализован у Вамита в свипере - работает для виртуализера и вмпрота. Далее поиск таблицы КОПов, если таковая имеется, обычно используют или таблицу или switch здоровый - оба варианта легко ловятся, ибо разветвление очень сильное. Следующий этап - деобфускация всех КОПов в таблице. Дальше поиск ленты, это сложный этап, его проще в динамике ловить, ибо каждый норовит ее сделать нелинейной, зашифрованной и т.д. Главное поймать КОПы и их параметры. Потом разворачивание ленты в граф. Замена виртуальных инструкций на их разложения в x86 коде (ест-но не тупым копированием, а лишь то, что осталось после деобфускации). Потом по шаблонам инструкций x86 все сводится к проблеме эквивалентности алгоритмов - просто блоками ловить реальные x86 инструкции и заменять 10 - 20 инструкций на одну x86. Как бы сильно не раскладывали инструкцию на ее атомарные операции, все равно каждая инструкция выполняется в одном потоке выполнения кода ("поток" здесь не в смысле thread, а в смысле control flow) - т.е. мы по-любому получим, что, к примеру, 10 инструкций изменяют стэк, при этом изменяют указатель стэка на 4 байта вниз, изменения регистров гасятся (т.е. init значение участка кода равняется результирующему значению), изменений данных кроме стэка нет, а дальше работает шаблон X86_PUSH=(R_ESP-4, [R_ESP] = DATA) и превращает эти 10 инструкций в PUSH R32 или PUSH I32 (в зависимости от типа DATA). Если разложения в атомарные операции нескольких инструкций между собой перемешаны, т.е. сначала для одной инструкции выполняется какая-то операция, потом для следующей другая, потом снова первая обрабатывается - все равно это разные потоки выполнения, они отделяются друг от друга анализатором и все равно сработает шаблон. Ну а дальше руками разгребать очищенные трассы кода и думать о том, как их вставлять обратно. Но это не тоже самое, что в оригинальной ВМ бегать по VM_RUN. Это уже обычный x86 код, который, возможно, не до конца оптимизирован. Поясню почему нельзя натравливать шаблоны инструкций x86 сразу на обработчики. Дело в том, что не всегда это работает. Во-первых, в обработчики часто вставляют не нужный мусор, т.е. то, что к выполнению инструкции не имеет отношения. Это может быть анти-отладка, анти-дамп, что угодно. Но заготовка шаблона не сойдется ни с одной инструкцией. Автоматически зарубить анти-отладочные приемы невозможно. Это должно быть не на плечах декомпилятора. Во-вторых, бывают такие виды ВМ, что в них или не захотели (например, все ВМ в которых логика и арифметика реализована через базовые инструкции - сложение и любая функция Шеффера) или даже нельзя по архитектуре некоторые инструкции запихнуть в один шаблон. В них одна x86 инструкция раскладывается на несколько виртуальных инструкций. А вот когда ленту ВМ разворачиваем, тогда уже шаблоном можно подобрать "оптимизацию" десятка инструкций в одну x86, которая делает все тоже самое. Проблема почти всех ВМ в том, что люди начитаются книжек или бывшие крекеры их садятся писать, которые пару тройку ВМ разобрали и пришла им в голову гениальная идея написать свою. В итоге получаем очередной штамп. У многих ВМ есть эти параметры: 1. Наличие цикла исполнения кода. А значит ВМ легко задетектить. Цикл, каждый проход которого выполняет новый код - это ВМ. Ну нельзя написать код с циклом, который так себя ведет. Если у кого-то из программистов получится именно это - он написал ВМ, сам того не осознавая. 2. switch-таблица. Здесь я имею в виду, что вызов обработчика в коде выглядит как mov reg, [base + index * mem_multiplier] (где для 32-бит множитель mem_multiplier понятное дело равен 4-ке) или сразу вызов call [base + index * mem_multiplier]. Даже если таблица под обфускацией, как в вмпроте, легко отслеживаем регистры, которые участвуют в индексации и составляем шаблон деобфускации. Потом определяем размер таблицы и КОПы у нас в руках. На каждый обработчик натравливаем деобфускатор. 3. Наличие ленты исполняемого виртуального кода. Ну, и как следствие указатель команд в локальной переменной или чаще в регистре. Ну, хранить виртуальные инструкции как-то же надо? Почти все реализации ВМ используют лобовое решение - тупо массив с КОПами и их параметрами. Бывает, что параметры лежат в отдельном массиве (типа экономия места на ленте). Спалить обращение к ленте с учетом конвейерной обработки данных на ней - не проблема. Вот еще один штамп, в студенческие годы игрался, написано под DOS (т.е. 16-битная ВМ): Code:
А теперь внимание. Я не ковырял лично StarForce, как-то в игры не играю, соответственно и материал не попадается для работы с игровыми протекторами. Ну так вот, по словам моих знакомых игро-ломателей, в StarForce нет ни одного пункта из выше перечисленных. Там просто нечего ломать) Там вся ВМ заведомо раскручена. Нет ни циклов, никаких таблиц, просто вермишель виртуализованного кода. И каждый обработчик сам определяет, какой обработчик ему выполнять следующим. ![]() |
|
Создано: 03 октября 2013 20:43 · Личное сообщение · #22 в чем универсальность я не уловил) все равно опкоды нужно разбирать без них лента китайская азбука )) хорошо что в даном случае опкоды банальные в реализации, потому что скорость нужна а бывают реализации самой логики на с++ где огого какой код избыточный ![]() |
|
Создано: 04 октября 2013 08:42 · Поправил: dosprog · Личное сообщение · #23 Простой лоадер тут сделать не получится. Нужен непростой. Который можно было бы закрутить и посвыше... Патч? - Согласен с ранее прозвучавшим в параллельной (закрытой) теме мнением, что все обсуждения здесь прямиком пойдут в наработки защиты будущей версии рассматриваемого софта. Что касается "декомпилятора", то (думаю) много чести делать его индивидуально для отдельной версии отдельной софтины. Это же не MSVBVM, которую в микрософт уже вряд-ли будут кардинально изменять... А сама тема разбора VM что-ж, интересная. Но всё это крайне неуниверсально. Трата времени. P.S.(к следующему посту) -- Int, не обращайте внимания, мысли вслух. Считайте, что спам. ![]() |
|
Создано: 04 октября 2013 15:35 · Личное сообщение · #24 reversecode Где я написал, что опкоды надо разбирать? ![]() Это делает автомат, которые вычленяет их код из цикла ВМ (это просто ветки графа), очищает и преобразует в шаблоны. У Вамита, например, именно так и происходит. Только он не использует шаблоны в чистом виде, он их сравнивает с теми, которые заведомо разобраны. Я не могу публиковать детали реализации его инструмента, мой труд внесенный в Sweeper минимален и авторские права, включая детали реализации, остаются по праву у Вамита. Я только составлял шаблоны под вмпрот. В создании анализаторов не участвовал. Мой же метод не требует первоначального изучения ВМ. Только его реализовать надо. ![]() reversecode пишет: а бывают реализации самой логики на с++ где огого какой код избыточный Примеры в студию! ![]() dosprog А вас кто-то заставляет что-то делать по этой программе и этой ВМ? Смысл комментария не понятен. Что касается MSVBVM - уже достаточно создано декомпиляторов P-кода. И кардинально изменять ее (ВМ) тоже не будут, просто потому, что VB 6 был последний нативный продукт и его разработка прекращена. P.S. И не пытайтесь доказать, что это невозможно или не универсально. VMSweeper же работает. Просто я хотел пойти дальше и избавиться вообще от шаблонов. Шаблоны плохо использовать хотя бы потому, что одно изменение реализации хотя бы одного обработчика ломает возможность использования инструмента. У нас на этом месте с Вамитом возник спор, но только его инструмент работает, а мой существует только на бумаге и в виде кусков кода, который надо переписывать. ![]() |
|
Создано: 04 октября 2013 16:11 · Поправил: reversecode · Личное сообщение · #25 int пишет: Это делает автомат, которые вычленяет их код из цикла ВМ (это просто ветки графа), очищает и преобразует в шаблоны. это будет работать на очень узком кругу вм мысленно рисую ситуацию ... вм... в ней один опкод это не опкод из x86, а вымышленый опкод который делает сразу md5 или md5(md5(x)) и реализация этого md5 на С++ с обьектами где ты ни в какой шаблон это не выкрутишь, потому что контекст привязан на ВМ, которая инициализируется гараздо раньше самого вхождения в ВМ цикл... вот как твой метод будет работать в этой ситуации? а то что то до меня туго доходит)) sweeper все реверсили видели, опокды там видны все понятно, лишний раз рассказывать не надо) add идея в том что бы опкоды разворачивать в их реализации и выплевывать в таком виде код? или как ![]() |
|
Создано: 04 октября 2013 17:28 · Личное сообщение · #26 reversecode пишет: идея в том что бы опкоды разворачивать в их реализации и выплевывать в таком виде код? или как Приблизительно, да. Только не в таком виде, а чистить. Самое интересное - это чистить. Это потребует чтобы каждая инструкция была разложена в атомарное представление. И когда одна инструкция представлена через другие - находить наиболее оптимальные замены. Код в таком случае, может и не соответствовать оригинальному, который был еще до виртуализации. Но он будет достаточно понятным для анализа. Это и есть ключевая идея и самая сложная часть. Кстати, иногда ВМ не мешает, а наоборот открывает возможности. Я как-то ломал программу под вмпротом, мне нужен был бряк на данные. Легко! Ставим бряк на обработчик команды, которая читает данные (такой обработчик может быть не один - на все сразу ставится точка останова). И вуаля! Условный бряк в IDA дал мне то, что нужно! А как бы я решал эту же задачу (пусть для определенности ida 5.2 - в ней нет точек останова на память встроенных в отладчик)? Ставил бы точки останова на все подряд инструкции? Где искать нужные данные я тогда не знал. Возможности использовать железные бряки тоже не было. А вмпрот очень мило классифицировал все инструкции на их типы (заметьте - один бряк на стрелке Пирса дает отлов ВСЕЙ логики, если ее только какой-нибудь умник сдвигами и сложениями не реализовал, кстати не проверял такую возможность). Понятно, что много мусора будет ловить, но правильный фильтр решает все проблемы. ![]() reversecode Шаблонов с MD5 не будет просто по определению инструмента. А как будет работать инструмент со старфорс? Да тоже никак, ибо не подходит под решаемую задачу. А сделать крутую ВМ, которую не возьмет ни один девм тоже легко: применить всякие интересные штуки типа великой теоремы ферма и повесить мертвую ветку, выполнение которой будет противоречить этой теореме. Девм не знает содержимого сотен книг по кибернетике, непрерывной математики и криптографии. Можно запутать средства авто анализа так, что даже человек будет неделями вылавливать трюк за трюком вручную. Просто никто эти идеи в жизнь не воплатил. Деобфускация, вообще говоря, алгоритмически не разрешимая задача, равно как и обфускация. Можно легко написать код, который при виртуализации тем же вмпротом сломается и будет не работоспособен. И что теперь делать? Можно ничего не делать. А можно решать задачи под частные случаи и это довольно эффективно на практике. Возвращаясь к основной теме, хочу сказать, что данная ВМ имеет одну неприятную особенность. Часть обработчиков перехватывается на уровне цикла ВМ и они исполняются прямо внутри цикла, без вызова обработчиков, хотя обработчики соответствующие есть. Т.е. тупо вынесли часть обработчиков в тело цикла. Я уже получил трассу выполнения одной виртуализованной функции, приятного в ней мало. ![]() |
|
Создано: 04 октября 2013 18:41 · Поправил: Модератор · Личное сообщение · #27 |
![]() |
eXeL@B —› Вопросы новичков —› Помогите определить виртуальную машину из MapEdit (с Geopainting.com) |