Сейчас на форуме: NIKOLA, r0lka, johnniewalker, vsv1 (+4 невидимых) |
eXeL@B —› Крэки, обсуждения —› Изменение адреса api в многопоточной задаче |
Посл.ответ | Сообщение |
|
Создано: 03 февраля 2020 13:18 · Поправил: Kindly · Личное сообщение · #1 Есть обычный вызов: Code:
И похожих вызовов по софту штук 50. Чтобы не патчить везде одно и то же, я сделал эмуляцию ответа после отработки api, предварительно запоминая адрес, находящийся в: [rsp+0x58] и далее подменял по тому адресу данные и все прекрасно работало. Но потом была обнова Windows 10, обнова софта, и мои костыли перестали быть точными. они работали как и прежде, но софт осуществляет также проверки во время выполнения многопоточных задач. и вот именно в этот момент путем отладки было выяснено, что патч код промазывает и пишет байты не по тому адресу. с виду все должно быть верно, и я проверил утилитой RunAsDate, которая также в многопоточной задаче облажалась и сработало внутреннее ограничение регистрации в софте. казалось бы, софт в rcx ставит указатель на записанные далее байты, но не так все просто. во время выполнения многопоточной задачи и кучи вызовов проверок времени лицухи, меняются страницы с данными, например: lea rcx, ss:[rsp+0x58] < в rcx допустим адрес в памяти 0015D000 но в какой то момент после отработки api в: movzx eax, word ptr ss:[rsp+0x5A] movzx ecx, word ptr ss:[rsp+0x58] в [rsp+0x58] уже нет этого 0015D000 адреса и настоящих данных времени по этому адресу нет (кроме записанных патчем), но зато все данные той страницы оказываются по адресу: 001FA000. И это не всегда, но в какой то определенный момент из 10-15 вызовов один-второй раз это случается. То есть каким то образом именно внутри api происходит копирование или переназначение страницы стека, лежавшего в [rsp+0x58] до начала выполнения api. напомню, что это происходит только во время запуска многопоточной задачи, при этом проц и память могут грузиться до 100%. вопрос в том, с какого перепугу создается такая переадресация или как это называется? при этом софт работает правильно и ему по барабану, что входные выходные адреса перед обработкой api могли поменяться. проблемы с патчем нет, выкинул эмуляцию вместе с апи. но что это за момент и почему так происходит (или начало происходить после обнов)? ----- Array[Login..Logout] of Life |
Ранг: 419.0 (мудрец), 647thx Активность: 0.46↗0.51 Статус: Участник "Тибериумный реверсинг" |
Создано: 03 февраля 2020 14:07 · Личное сообщение · #2 Kindly пишет: call GetSystemTime Какая-то особая уличная магия В Windows 7, Windows 10 реализация не отличается - вызывается сначала Code:
внутри которой Code:
Но там отсутсвуют всякие realloc. Обычное чтение из PEB и цепочка из мат операций. Kindly пишет: но в какой то момент после отработки api А если записывать данные по адресу перед выполнением RET из api?! |
|
Создано: 03 февраля 2020 14:19 · Поправил: Kindly · Личное сообщение · #3 ELF_7719116 пишет: А если записывать данные по адресу перед выполнением RET из api?! я не хукаю и не эмулирую апи внутри своей dll, если это имеется в виду. даю в коде отработать оригинальному вызову, поэтому перед самой RET именно в api я ничего не могу записать. но если все 50 вызовов вынести на такой патч-код, убрав api, но записывая по rcx данные времени напрямую: Code:
все работает прекрасно и ничего "не сходит с ума" может быть это происходит из-за переполнения памяти и ресурсов пк, когда старые данные выгружаются и выделяется новое освободившееся пространство с новыми адресами? винда это учитывает и делает перераспределение, или не так? я грешным делом подумал, что добавили проверок на триал в структурах присутствующих api: GetFileInformationByHandle FindFirstFile потому как в защите и такой момент есть, когда создаются временные файлы с рандомными именами и текущими атрибутами. но все оказалось "проще", но такого еще не замечал. кстати, предыдущая версия софта работает нормально с прошлым методом. а счас уж если RunAsDate не справилась, то что про мои костыли говорить ----- Array[Login..Logout] of Life |
|
Создано: 03 февраля 2020 15:43 · Поправил: user99 · Личное сообщение · #4 |
|
Создано: 03 февраля 2020 15:47 · Поправил: kunix · Личное сообщение · #5 Вдруг там добавили отдельный поток, который мониторит момент перезаписи переменной и сразу же ремапит? Типа такая лайтовая защита от перехвата. Без залезания в дебри винды. Звучит бредово, но вызывать напрямую GetSystemTime тоже так себе идея. Или может это у вас некий проеб с перехватом вашим. Как перехват работает-то? |
|
Создано: 03 февраля 2020 15:52 · Личное сообщение · #6 Kindly пишет: может быть это происходит из-за переполнения памяти и ресурсов пк Имхо переполнение не обязательно. Виндовс С++ memory management. У каждого треда свой стэк и видать при конфликтах аллокации происходит какой то вариат тактики аллокации. Либо что бы было быстро или акуратно. Copy constructors? |
|
Создано: 03 февраля 2020 17:21 · Поправил: Kindly · Личное сообщение · #7 kunix пишет: Вдруг там добавили отдельный поток, который мониторит момент перезаписи переменной и сразу же ремапит? ну хз, или это связано с модулями, версией или настройками компиля C++ kunix пишет: Как перехват работает-то? костыли точно записывают в свободное место адрес и точно восстанавливают. запоминаю адрес записи штампа времени и после выполнения api пишу по запомненному адресу свои данные. вообще я не хотел менять часы минуты секунды и достаточно было одного qword, где месяцы и год, но благо в расчетах времени софт юзает GetTickCount64. friend пишет: Виндовс С++ memory management. вот это похоже ближе, надо изучить. софт как раз на нем. но самое интересное, что это в api windows выполняется и идут в нее одни параметры (адрес заполнения), а после выполнения данные переносятся на другой адрес памяти. причем так происходят в многопотоке не всегда, а именно "когда надо", раз на 10-15 вызовов примерно или вообще похоже рандомно. в x64dbg многопоточные задачи отлаживать почти нельзя нормально, код коробит, трассировка толком не работает, непонятно бряки срабатывают, вобщем ----- Array[Login..Logout] of Life |
|
Создано: 03 февраля 2020 19:20 · Личное сообщение · #8 |
|
Создано: 03 февраля 2020 20:39 · Поправил: Kindly · Личное сообщение · #9 kunix Костыль примерно следующий: Code:
Здесь я извращаюсь и получаю адрес своей секции кода в случае применения релоков, добавляю длину 200 байт, чтобы физически записать RCX адрес в свободное пространство секции и потом записать по нему свои значения. На эту прививку у меня устанавливаются 50 одинаковых вызовов GetSystemTime, так я их одним махом и обрабатываю. Вместо этих костылей, я могу просто влупить: Code:
что сейчас и сделано. Что я по вашему должен синхронизировать, если я запоминаю параметр передачи до вызова api и пишу свои байты после возврата. Вопрос только в том, что костыль при применении оригинальной api всегда корректно не работает в многопоточных задачах, также как не справляется и тулза от NirSoft, что намекает про некую магическую особенность. Но короткий томагавк без участия самой api работает безупречно, но небольшой минус в том, что часы минуты и секунды будут статичными, но в моем случае это по боку и на функционал не влияет, только на проверку даты лицухи. ----- Array[Login..Logout] of Life |
|
Создано: 03 февраля 2020 20:59 · Поправил: kunix · Личное сообщение · #10 Костыль зачетный. У вас там несколько потоков используют общий кусок памяти в секции кода (по адресу rbx). Может поэтому? Нужды в этом особой нет в данном костыле, все можно в стеке сделать. >Что я по вашему должен синхронизировать По вашему описанию было непонятно, как все работает. Я уже начинал думать, что вы отлаживаете треды из другого процесса и подменяете значения из другого треда. Отсюда разговор про синхронизацию. > не справляется и тулза от NirSoft, что намекает про некую магическую особенность Это может быть по другой причине. Банальный детект перехвата или может не все функции перехвачены. |
|
Создано: 03 февраля 2020 21:08 · Поправил: Kindly · Личное сообщение · #11 kunix пишет: Банальный детект перехвата или может не все функции перехвачены. нету там никакого детекта перехвата. акцентирую, что одна и та же функция с проверкой истечения триала вызывается софтом при запуске, при открытии проекта, при запуске многопоточной задачи. то есть исполнение идет одинаково корректно и данные успешно подменяются постоянно при работе софта. и вызывает "сбой" в многопоточной задаче не сама функция проверки триала, а именно исполнение api в недрах windows, и винда отдает результат уже с другой страницей памяти, которая понимается программой и принимается как родная, см. 1 пост. и я склоняюсь к тому, что RunAsDate также не умеет пока эмулировать свои api в многопотоке - а им в поддержку писать бесполезно (мне и не нужно), у них ответ мол "мы не помогаем крякать софт, утилита для других целей" ----- Array[Login..Logout] of Life |
|
Создано: 03 февраля 2020 23:33 · Поправил: kunix · Личное сообщение · #12 > которая понимается программой и принимается как родная Звучит совершенно фантастически. Как вы это себе представляете? Это надо пропатчить все указатели на данный адрес в программе. > RunAsDate также не умеет пока эмулировать свои api в многопотоке Ну, если честно, вы тоже пока не умеете. У вас там глобальные переменные без синхронизации. Вы адрес переменной со временем сохраняете в одну и ту же переменную, для всех потоков. Вполне может быть, что другой поток успеет перезаписать значение по адресу rbx+0x8. И тогда вот эта хрень перепишет не то, что надо. Code:
Как насчет соорудить spinlock в вашем костыле? Или убрать глобальные переменные вообще. | Сообщение посчитали полезным: Kindly |
|
Создано: 04 февраля 2020 00:42 · Поправил: Kindly · Личное сообщение · #13 kunix пишет: Как вы это себе представляете? Это надо пропатчить все указатели на данный адрес в программе. да, все родные вызовы GetSystemTime меняются на вызов костыля, и там все новые обращения каждый раз входят с новыми адресами. но проблема не в костыле (в этом случае он уже неприменим), а в особенности исполнения api в многопоточной задаче. вопрос не в подмене данных. проблема понять, почему именно в api такое происходит, входят одни параметры, потом возвращаются другие, не совсем это понятно и отлаживать эту задачу не могу, начинает стопориться отладчик и невозможно дальше выполнять код. kunix пишет: Вполне может быть, что другой поток успеет перезаписать значение по адресу rbx+0x8. да ничего он не перезаписывает, результат GetSystemTime оказывается вообще не по адресу сохраненного параметра в костыле. повторюсь, это не каждый раз так происходит, при выполнении многопоточной задачи подмена в основном срабатывает нормально раз 10, потом 11-ый раз мимо, на 12-ый раз - точно и далее вот так как то так через раз через 10. почему оно так работает на той же функции непонятно, когда в RCX всегда передается место для возвращаемого значения времени: Code:
почему иногда оно указывает один адрес, а возвращает в rsp+0x58 совсем другой. при этом копирует или формирует возврат данных на другой адрес внутри api, и прога подхватывает их как ни в чем не бывало. Code:
kunix пишет: Как насчет соорудить spinlock в вашем костыле? не знаю что такое, мне не нужно оно. пишу патч код в теле софта же. ----- Array[Login..Logout] of Life |
|
Создано: 04 февраля 2020 00:57 · Поправил: kunix · Личное сообщение · #14 > проблема понять, почему именно в api такое происходит, входят одни параметры, потом возвращаются другие, не совсем это понятно и отлаживать эту задачу не могу Вы меня не поняли. Программа выделяет память и передает указатель в GetSystemTime. Как такое может быть, чтобы GetSystemTime вернула результат по другому адресу? Если программа ждет результат именно по тому, который она передала. Это же лютая дичь.т > результат GetSystemTime оказывается вообще не по адресу сохраненного параметра в костыле А что такое адрес сохраненного параметра в костыле? Это глобальная переменная. Ее может перезапивать другой тред, пока вы смотрите в отладчик. > срабатывает нормально раз 10, потом 11-ый раз мимо, на 12-ый раз Выглядит как типичный race между несколькими тредами. > при этом копирует или формирует возврат данных на другой адрес внутри api, и прога подхватывает их как ни в чем не бывало. Может, вы смотрите в отладчике на разные треды и не замечаете этого? Или просто глючный отладчик? Короче, я бы на вашем месте переместил бы перехват GetSystemTime в loader.dll и добавил бы синхронизацию на входе и выходе. Потом убрал бы. И сравнил эти два варианта. Вместо отладчика - чисто логгинг через OutputDebugString. Все будет видно, чо и как. | Сообщение посчитали полезным: mak, Kindly |
|
Создано: 04 февраля 2020 08:21 · Личное сообщение · #15 Kindly, проблема не внутри WinApi'шной функции (не нужно ошибки своих костыли валить на майкрософт), а в ваших глобальных переменных для хранения локальных данных. Я вам это написал практически сразу, kunix выдал тот же диагноз. Либо делайте синхронизацию, как советует kunix, либо убирайте глобальный блок для данных и храните все в стеке, вообще не понятно зачем там эти навороты, т.к. обычный стек может спокойной сохранить ваши 3 переменные, к тому же это гарантированно потокобезопасный вариант. | Сообщение посчитали полезным: Kindly |
|
Создано: 04 февраля 2020 14:23 · Личное сообщение · #16 ну блин, сделал так, код проги: Code:
прыгнули на 0x0000000141901126: Code:
далее проверяем год, прыгнув сюда 0x000000014190114C Code:
прикол в том, что бряк на NOP сработал и записался при сравнении в [rsp+0x58] год 2020. я не юзал никаких PUSH POP, не записывался в ESP, не трогал EBP - все аккуратно. когда сработал бряк на NOP я посмотрел, записано ли у меня по адресу мои данные, и да, считывание сохраненного адреса и запись нужных байт состоялась, но какого хера вот тут: Code:
не лежит мое значение? я взял и сравнил адреса, при срабатывании бряка они остались. адрес RCX, который сохранялся это: 0000000025CCF638 E3 07 а это адрес [rsp+0x58] непосредственно при выполнении инструкции movzx ecx, word ptr ss:[rsp+0x58] и почему то по этому адрес лежит адресс: 0000000025ACF638 E4 07 какого хрена отличие адресов ровно на 200000 байт? какого хрена вообще так происходит - где криминал? ----- Array[Login..Logout] of Life |
|
Создано: 04 февраля 2020 14:43 · Личное сообщение · #17 Криминал в глобальной переменной, можно переписывать этот код хоть 100 раз, но если не решить проблему с глобальной переменной, патч потокобезопасным не станет. Добавлено спустя 2 минуты Почему нельзя сделать Code:
? |
|
Создано: 04 февраля 2020 15:11 · Поправил: Kindly · Личное сообщение · #18 Все верно! Проблема оказалась в использовании переменной для записи адреса, потому как обычный push/pop RCX адреса решает проблему полностью, если использовать api: Code:
но получается, что также все работает при использовании глобальной переменной, если не использовать вызов api в патч-коде. user99 пишет: но если не решить проблему с глобальной переменной, патч потокобезопасным не станет. именно, поэтому уберу нафиг этот костыль с переменной и по-людски запишу ) писал эту хрень давно и вроде работала, пока не попались мультитредовые приложения. user99 пишет: Почему нельзя сделать именно, и да, отодвинуться на безопасное место в RSP также скорее всего стоит. Всем спасибо! Отставлю еще пару лайков через 24 часа Темку закрою вечером, может кто-что хочет что дополнить, хотя уже все ясно )) ----- Array[Login..Logout] of Life |
|
Создано: 04 февраля 2020 15:34 · Личное сообщение · #19 |
eXeL@B —› Крэки, обсуждения —› Изменение адреса api в многопоточной задаче |
Эта тема закрыта. Ответы больше не принимаются. |