Се­год­ня мы раз­берем­ся с тем, как устро­ена «неп­робива­емая» защита VBA-скрип­тов в Excel, пред­лага­емая ком­пилято­ром DoneEx VBA Compiler. Мы пос­мотрим, как этот инс­тру­мент ком­пилиру­ет мак­росы в натив­ные DLL, какие трю­ки исполь­зует для кон­тро­ля целос­тнос­ти, и шаг за шагом покажем, как обхо­дить про­вер­ки и вос­ста­нав­ливать исходный код из ском­пилиро­ван­ных модулей.

Ты, веро­ятно, обра­щал вни­мание на забав­ный эффект: как толь­ко нуб выучит какой‑нибудь прос­тецкий язык в дос­таточ­ной сте­пени, что­бы самому писать скрип­ты, его охва­тыва­ет все­пог­лоща­ющее чувс­тво собс­твен­ного величия, перехо­дящее в желание про­давать свои подел­ки окру­жающим. Это­му обыч­но сопутс­тву­ет опа­сение, как бы эту бес­ценную интеллек­туаль­ную собс­твен­ность не укра­ли. Ничего пло­хого в этом нет, пос­коль­ку такой страх дает работу целому сег­менту прог­раммис­тов, пишущих защиты для скрип­тов раз­ной сте­пени упо­ротос­ти.

Я уже писал статьи про подоб­ные защиты, раз­работан­ные на JavaScript, Python, PHP и даже AutoIT. Сегод­ня у меня наконец‑то дош­ли руки до темы защиты эксе­лев­ских VBA-скрип­тов.

Не­дав­но я нат­кнул­ся на извес­тный в очень узких кру­гах ком­пилятор DoneEx VBA Compiler. Он позици­они­рует­ся ни мно­го ни мало, как пол­ноцен­ный ком­пилятор Excel VBA в натив­ный код со встро­енной защитой. Сра­зу пре­дуп­реждаю: это поделие дос­таточ­но кри­вое — лич­но мне, что­бы ском­пилиро­вать на нем хоть какой‑то более‑менее работос­пособ­ный при­мер, понадо­билось гораз­до боль­ше вре­мени, чем разоб­рать­ся, как сни­мать саму защиту. Одна­ко ее опи­сание изо­билу­ет гром­кими пре­тен­циоз­ными заяв­лени­ями вро­де «код VBA не может быть ско­пиро­ван или вос­ста­нов­лен и име­ет самый высокий уро­вень защиты от пиратс­тва». Давай поп­робу­ем оспо­рить это утвер­жде­ние.

К сло­ву ска­зать, эти ребята еще и «ком­пилятор» элек­трон­ных книг XLS в EXE-модуль запили­ли, который, прав­да, без уста­нов­ленно­го Excel не запус­кает­ся, но это сов­сем дру­гая исто­рия, которой мы сегод­ня касать­ся не будем.

Итак, пред­положим, что нам в руки попал необыч­ный эксе­лев­ский файл. К нему при­лага­ется одна (или две) DLL-биб­лиоте­ка с тем же име­нем, но воз­можны суф­фиксы *_64 или *_32 в зависи­мос­ти от раз­ряднос­ти. Если нат­равить на них Detect It Easy, прог­рамма показы­вает ком­пилятор MinGW(GCC: (GNU)), одна­ко орди­налы из них экспор­тиру­ются доволь­но подоз­ритель­ные.

Поп­робу­ем открыть защищен­ный документ. На экра­не появ­ляет­ся весь­ма раз­дра­жающее окно с бегущим пол­зунком, а затем выс­какива­ет пред­ложение о регис­тра­ции.

По­ка оно висит, откро­ем наш любимый отладчик x64dbg и при­атта­чим­ся к про­цес­су Excel. При­нуди­тель­но прер­вав его выпол­нение, мы видим занят­ный стек вызовов.

Из него как на ладони вид­на пос­ледова­тель­ность вызовов, порож­дающих это окно. Вна­чале непос­редс­твен­но из VBA-кода вызыва­ется фун­кция SetThisWorkbook, экспор­тиру­емая из нашей ском­пилиро­ван­ной биб­лиоте­ки testvba1_xlsm_64.dll. Эта фун­кция вызыва­ет фун­кцию DummyFunc05 неко­ей загадоч­ной биб­лиоте­ки cbinrtl.dll, отсутс­тву­ющей в катало­ге мак­роса и вооб­ще непонят­но отку­да взяв­шей­ся. А уже она выкиды­вает окно пре­дуп­режде­ния, реали­зован­ное через фун­кцию DialogBoxIndirectParamW.

При деталь­ном рас­смот­рении мы обна­ружи­ваем эту биб­лиоте­ку в под­катало­ге v6n3vk66ej (наз­вание при каж­дом вызове слу­чай­ное) вре­мен­ной пап­ки Windows. Ском­пилиро­ван­ный модуль testvba1_xlsm_64.dll сох­раня­ет ее туда из соот­ветс­тву­юще­го собс­твен­ного ресур­са при вызове SetThisWorkbook, а затем под­чища­ет за собой при отра­бот­ке. Собс­твен­но, ском­пилиро­ван­ный модуль прак­тичес­ки целиком и сос­тоит из биб­лиоте­ки cbinrtl.dll, хра­нящей­ся в нем в явном незашиф­рован­ном виде, так же как и в самом модуле ком­пилято­ра vbaclr4e.exe, от которо­го ее и получа­ет. Это дает надеж­ду на уби­ение двух зай­цев пат­чем фай­ла testvba1_xlsm_64.dll — мож­но пат­чить код и биб­лиоте­ки testvba1_xlsm_64.dll, и порож­даемой ею биб­лиоте­ки cbinrtl.dll.

Поп­робу­ем это реали­зовать. Сра­зу нап­рашива­ется гипоте­за, что защита сос­редото­чена в фун­кции DummyFunc05, — ведь имен­но ее вызов и выводит сооб­щение о незаре­гис­три­рован­ной вер­сии. Одна­ко ее нель­зя прос­то так взять и закоро­тить — Excel при этом пада­ет с ошиб­кой. При бли­жай­шем рас­смот­рении мы видим ее вызовы в IDA. Почему так про­исхо­дит? Эта фун­кция запол­няет некую жиз­ненно важ­ную для прог­раммы струк­туру qword_62FC9180.

Зна­чит, при­дет­ся копать вглубь кода DummyFunc05, бла­го основные вызовы у нас уже раз­мечены на сте­ке. Доволь­но быс­тро мы натыка­емся на раз­вилку в коде cbinrtl.dll, которая ведет к запол­нению струк­туры qword_62FC9180 (на сле­дующем скрин­шоте она обоз­начена как a2, пос­коль­ку переда­ется вто­рым парамет­ром в про­цеду­ру).

Фун­кция sub_180047A20 в незаре­гис­три­рован­ной вер­сии выкиды­вает окна c пре­дуп­режде­ниями, одна­ко, если ском­пилиро­вать модуль без защиты, эта фун­кция прос­то воз­вра­щает 1 без лиш­них слов. Если в отладчи­ке закоро­тить ее на return 1, все тоже работа­ет безо вся­ких пре­дуп­режде­ний, но при попыт­ке пат­ча модуля testvba1_xlsm_64.dll нас ждет сюр­приз. Находим во вло­жен­ном в файл testvba1_xlsm_64.dll модуле cbinrtl.dll мес­то, соот­ветс­тву­ющее sub_180047A20, и пат­чим его нуж­ным обра­зом.

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

Пер­вое, что бро­сает­ся в гла­за, — про­вер­ка 128-бит­ного хеша SHA-1 непос­редс­твен­но перед заг­рузкой cbinrtl.dll. Обра­ти вни­мание на вер­хнюю часть скрин­шота  — заг­рузка модуля про­исхо­дит толь­ко тог­да, ког­да фун­кция sub_62FC28D4 дает доб­ро.

Внут­ри она реали­зова­на так.

Фун­кция sub_62FC27F7 чита­ет весь модуль, sub_62FC2095 счи­тает его хеш, который затем срав­нива­ется с тес­товым зна­чени­ем при помощи memcmp (его воз­вра­щает sub_62FC1720). Эту про­вер­ку лег­ко мож­но закоро­тить, одна­ко ей дело не огра­ничи­вает­ся. Если мы это сде­лаем, то модуль cbinrtl.dll заг­ружа­ется, пат­ченная фун­кция DummyFunc05 кор­рек­тно отра­баты­вает, одна­ко в самом кон­це SetThisWorkbook вылета­ет по эксепше­ну при вызове неко­его безымян­ного callback из того же cbinrtl.dll.

В непат­ченном вари­анте ука­затель на фун­кцию qword_62FC90B0 содер­жит совер­шенно дру­гой адрес, который отра­баты­вает нор­маль­но. Пом­нишь, в начале нашего повес­тво­вания я упо­минал некую жиз­ненно важ­ную струк­туру qword_62FC9180, запол­няемую в DummyFunc05? Она как раз и содер­жит этот адрес и, если модуль про­пат­чен, запол­няет­ся некор­рек­тно. Раз­работ­чики решили поум­ничать и мак­сималь­но запутать этот путь, но мы хит­рее их и поэто­му поищем обходную тро­пин­ку.

Нам извес­тно, что сущес­тву­ет как минимум еще одна про­вер­ка целос­тнос­ти, помимо sub_62FC28D4, которая заново перечи­тыва­ет файл cbinrtl.dll (все дан­ные, счи­тан­ные фун­кци­ей sub_62FC28D4, оста­ются локаль­но внут­ри нее). Поэто­му мы сра­зу по отра­бот­ке sub_62FC28D4 ста­вим точ­ку оста­нова на ядер­ную фун­кцию ReadFile, и — о чудо! — она тут же сра­баты­вает на новом чте­нии фай­ла cbinrtl.dll. Сно­ва смот­рим на стек вызовов.

Очень инте­рес­но: выходит, на этот раз новорож­денная cbinrtl.dll переп­роверя­ет сама себя на невин­ность. Дви­гаясь по сте­ку вызовов вверх, мы обна­ружи­ваем саму про­цеду­ру под­сче­та хеша.

На скрин­шоте фун­кция cbinrtl.7FF9985145B0 воз­вра­щает в регис­тре RAX адрес ука­зате­ля на 512-бит­ный хеш Whirlpool от модуля cbinrtl.dll (выделен в дам­пе). В этом слу­чае запат­чить про­вер­ку не так прос­то, в отли­чие от пре­дыду­щего слу­чая, где хеши тупо срав­нивались при помощи memcmp. Раз­работ­чики решили заморо­чить­ся по мак­симуму, про­делы­вая над получен­ным хешем мно­жес­тво муд­реных манипу­ляций, которые в ито­ге и при­водят к неоче­вид­ному переме­шива­нию дан­ных в струк­туре qword_62FC9180.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии