Сейчас на форуме: NIKOLA, r0lka, johnniewalker, vsv1 (+4 невидимых)

 eXeL@B —› Крэки, обсуждения —› Изменение адреса api в многопоточной задаче
Посл.ответ Сообщение


Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 03 февраля 2020 13:18 · Поправил: Kindly
· Личное сообщение · #1

Есть обычный вызов:

Code:
  1.          lea rcx, ss:[rsp+0x58]
  2.          call GetSystemTime
  3.          movzx eax, word ptr ss:[rsp+0x5A]
  4.          movzx ecx, word ptr ss:[rsp+0x58]

И похожих вызовов по софту штук 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.460.51
Статус: Участник
"Тибериумный реверсинг"

Создано: 03 февраля 2020 14:07
· Личное сообщение · #2

Kindly пишет:
call GetSystemTime

Какая-то особая уличная магия
В Windows 7, Windows 10 реализация не отличается - вызывается сначала
Code:
  1. 00000000006AEE5F  | call qword ptr ds:[<&RtlTimeToTimeFields>]                    |

внутри которой
Code:
  1. 00007FF850E42D1F  | call <ntdll.RtlpTimeToTimeFieldsNoLeapSeconds>                |

Но там отсутсвуют всякие realloc. Обычное чтение из PEB и цепочка из мат операций.
Kindly пишет:
но в какой то момент после отработки api

А если записывать данные по адресу перед выполнением RET из api?!




Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 03 февраля 2020 14:19 · Поправил: Kindly
· Личное сообщение · #3

ELF_7719116 пишет:
А если записывать данные по адресу перед выполнением RET из api?!

я не хукаю и не эмулирую апи внутри своей dll, если это имеется в виду. даю в коде отработать оригинальному вызову, поэтому перед самой RET именно в api я ничего не могу записать.
но если все 50 вызовов вынести на такой патч-код, убрав api, но записывая по rcx данные времени напрямую:

Code:
  1.          push rax
  2.          mov rax, 0x01......
  3.          mov [rcx], rax
  4.          mov rax, 0x02......
  5.          mov [rcx+0x8], rax
  6.          pop rax
  7.          ret

все работает прекрасно и ничего "не сходит с ума"
может быть это происходит из-за переполнения памяти и ресурсов пк, когда старые данные выгружаются и выделяется новое освободившееся пространство с новыми адресами? винда это учитывает и делает перераспределение, или не так?

я грешным делом подумал, что добавили проверок на триал в структурах присутствующих api:
GetFileInformationByHandle
FindFirstFile
потому как в защите и такой момент есть, когда создаются временные файлы с рандомными именами и текущими атрибутами.
но все оказалось "проще", но такого еще не замечал. кстати, предыдущая версия софта работает нормально с прошлым методом. а счас уж если RunAsDate не справилась, то что про мои костыли говорить

-----
Array[Login..Logout] of Life




Ранг: 43.1 (посетитель), 20thx
Активность: 0.160.29
Статус: Участник

Создано: 03 февраля 2020 15:43 · Поправил: user99
· Личное сообщение · #4

Без кода сложно понять где проблемы.

Kindly пишет:
предварительно запоминая адрес

Может этот адрес запоминается в глобальную переменную? Тогда нет ничего удивительно в проблемах с многопоточностью.
А может быть десятки других косяков.

| Сообщение посчитали полезным: kunix, Kindly

Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 03 февраля 2020 15:47 · Поправил: kunix
· Личное сообщение · #5

Вдруг там добавили отдельный поток, который мониторит момент перезаписи переменной и сразу же ремапит? Типа такая лайтовая защита от перехвата. Без залезания в дебри винды.
Звучит бредово, но вызывать напрямую GetSystemTime тоже так себе идея.
Или может это у вас некий проеб с перехватом вашим. Как перехват работает-то?



Ранг: 13.2 (новичок), 13thx
Активность: 0.28=0.28
Статус: Участник

Создано: 03 февраля 2020 15:52
· Личное сообщение · #6

Kindly пишет:
может быть это происходит из-за переполнения памяти и ресурсов пк

Имхо переполнение не обязательно.
Виндовс С++ memory management.
У каждого треда свой стэк и видать при конфликтах аллокации
происходит какой то вариат тактики аллокации.
Либо что бы было быстро или акуратно.
Copy constructors?




Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 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




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 03 февраля 2020 19:20
· Личное сообщение · #8

С ваших слов звучит так, что вы не синхронизируете два события - возврат из API и патч значения.
Тогда это вообще не обязано работать Мне даже в голову поначалу не пришло, что так можно.




Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 03 февраля 2020 20:39 · Поправил: Kindly
· Личное сообщение · #9

kunix
Костыль примерно следующий:
Code:
  1.          call @L00000001
  2.          push rax
  3.          mov rax, ds:[rsp+0x8]
  4.          mov [rbx], rax
  5.          mov rax, rcx
  6.          mov [rbx+0x8], rax
  7.          mov rax, ds:[rsp-0x8]
  8.          mov [rbx+0x10], rax
  9.          pop rax
  10.          call [GetSystemTime address]
  11.          push rax
  12.          push rcx
  13.          mov rcx, [rbx+0x8]
  14.          mov rax, 0x110001000B07E2
  15.          mov [rcx], rax
  16.          pop rcx
  17.          mov rax, [rbx]
  18.          mov ds:[rsp+0x8], rax
  19.          pop rax
  20.          mov rbx, [rbx+0x10]
  21.          ret
  22.  
  23. @L00000001:
  24.          mov ds:[rsp-0x8], rbx
  25.          mov rbx, ds:[rsp]
  26.          mov bl, 0x0
  27.          add rbx, 0x200
  28.          ret


Здесь я извращаюсь и получаю адрес своей секции кода в случае применения релоков, добавляю длину 200 байт, чтобы физически записать RCX адрес в свободное пространство секции и потом записать по нему свои значения.
На эту прививку у меня устанавливаются 50 одинаковых вызовов GetSystemTime, так я их одним махом и обрабатываю. Вместо этих костылей, я могу просто влупить:

Code:
  1.              push rax
  2.              mov rax, 0x01......
  3.              mov [rcx], rax
  4.              mov rax, 0x02......
  5.              mov [rcx+0x8], rax
  6.              pop rax
  7.              ret

что сейчас и сделано.

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

-----
Array[Login..Logout] of Life




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 03 февраля 2020 20:59 · Поправил: kunix
· Личное сообщение · #10

Костыль зачетный.
У вас там несколько потоков используют общий кусок памяти в секции кода (по адресу rbx).
Может поэтому?
Нужды в этом особой нет в данном костыле, все можно в стеке сделать.

>Что я по вашему должен синхронизировать
По вашему описанию было непонятно, как все работает.
Я уже начинал думать, что вы отлаживаете треды из другого процесса и подменяете значения из другого треда.
Отсюда разговор про синхронизацию.

> не справляется и тулза от NirSoft, что намекает про некую магическую особенность
Это может быть по другой причине. Банальный детект перехвата или может не все функции перехвачены.




Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 03 февраля 2020 21:08 · Поправил: Kindly
· Личное сообщение · #11

kunix пишет:
Банальный детект перехвата или может не все функции перехвачены.

нету там никакого детекта перехвата. акцентирую, что одна и та же функция с проверкой истечения триала вызывается софтом при запуске, при открытии проекта, при запуске многопоточной задачи. то есть исполнение идет одинаково корректно и данные успешно подменяются постоянно при работе софта. и вызывает "сбой" в многопоточной задаче не сама функция проверки триала, а именно исполнение api в недрах windows, и винда отдает результат уже с другой страницей памяти, которая понимается программой и принимается как родная, см. 1 пост. и я склоняюсь к тому, что RunAsDate также не умеет пока эмулировать свои api в многопотоке - а им в поддержку писать бесполезно (мне и не нужно), у них ответ мол "мы не помогаем крякать софт, утилита для других целей"

-----
Array[Login..Logout] of Life




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 03 февраля 2020 23:33 · Поправил: kunix
· Личное сообщение · #12

> которая понимается программой и принимается как родная
Звучит совершенно фантастически. Как вы это себе представляете?
Это надо пропатчить все указатели на данный адрес в программе.

> RunAsDate также не умеет пока эмулировать свои api в многопотоке
Ну, если честно, вы тоже пока не умеете. У вас там глобальные переменные без синхронизации.
Вы адрес переменной со временем сохраняете в одну и ту же переменную, для всех потоков.
Вполне может быть, что другой поток успеет перезаписать значение по адресу rbx+0x8.
И тогда вот эта хрень перепишет не то, что надо.
Code:
  1. mov rcx, [rbx+0x8]
  2. mov rax, 0x110001000B07E2
  3. mov [rcx], rax

Как насчет соорудить spinlock в вашем костыле? Или убрать глобальные переменные вообще.

| Сообщение посчитали полезным: Kindly


Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 04 февраля 2020 00:42 · Поправил: Kindly
· Личное сообщение · #13

kunix пишет:
Как вы это себе представляете?
Это надо пропатчить все указатели на данный адрес в программе.

да, все родные вызовы GetSystemTime меняются на вызов костыля, и там все новые обращения каждый раз входят с новыми адресами. но проблема не в костыле (в этом случае он уже неприменим), а в особенности исполнения api в многопоточной задаче. вопрос не в подмене данных. проблема понять, почему именно в api такое происходит, входят одни параметры, потом возвращаются другие, не совсем это понятно и отлаживать эту задачу не могу, начинает стопориться отладчик и невозможно дальше выполнять код.

kunix пишет:
Вполне может быть, что другой поток успеет перезаписать значение по адресу rbx+0x8.

да ничего он не перезаписывает, результат GetSystemTime оказывается вообще не по адресу сохраненного параметра в костыле. повторюсь, это не каждый раз так происходит, при выполнении многопоточной задачи подмена в основном срабатывает нормально раз 10, потом 11-ый раз мимо, на 12-ый раз - точно и далее вот так как то так через раз через 10. почему оно так работает на той же функции непонятно, когда в RCX всегда передается место для возвращаемого значения времени:
Code:
  1.          lea rcx, ss:[rsp+0x58]
  2.          call GetSystemTime

почему иногда оно указывает один адрес, а возвращает в rsp+0x58 совсем другой. при этом копирует или формирует возврат данных на другой адрес внутри api, и прога подхватывает их как ни в чем не бывало.
Code:
  1.          movzx ecx, word ptr ss:[rsp+0x58]

kunix пишет:
Как насчет соорудить spinlock в вашем костыле?

не знаю что такое, мне не нужно оно. пишу патч код в теле софта же.

-----
Array[Login..Logout] of Life




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 04 февраля 2020 00:57 · Поправил: kunix
· Личное сообщение · #14

> проблема понять, почему именно в api такое происходит, входят одни параметры, потом возвращаются другие, не совсем это понятно и отлаживать эту задачу не могу
Вы меня не поняли. Программа выделяет память и передает указатель в GetSystemTime.
Как такое может быть, чтобы GetSystemTime вернула результат по другому адресу? Если программа ждет результат именно по тому, который она передала.
Это же лютая дичь.т

> результат GetSystemTime оказывается вообще не по адресу сохраненного параметра в костыле
А что такое адрес сохраненного параметра в костыле? Это глобальная переменная. Ее может перезапивать другой тред, пока вы смотрите в отладчик.

> срабатывает нормально раз 10, потом 11-ый раз мимо, на 12-ый раз
Выглядит как типичный race между несколькими тредами.

> при этом копирует или формирует возврат данных на другой адрес внутри api, и прога подхватывает их как ни в чем не бывало.
Может, вы смотрите в отладчике на разные треды и не замечаете этого? Или просто глючный отладчик?

Короче, я бы на вашем месте переместил бы перехват GetSystemTime в loader.dll и добавил бы синхронизацию на входе и выходе. Потом убрал бы. И сравнил эти два варианта.
Вместо отладчика - чисто логгинг через OutputDebugString. Все будет видно, чо и как.

| Сообщение посчитали полезным: mak, Kindly

Ранг: 43.1 (посетитель), 20thx
Активность: 0.160.29
Статус: Участник

Создано: 04 февраля 2020 08:21
· Личное сообщение · #15

Kindly, проблема не внутри WinApi'шной функции (не нужно ошибки своих костыли валить на майкрософт), а в ваших глобальных переменных для хранения локальных данных. Я вам это написал практически сразу, kunix выдал тот же диагноз.
Либо делайте синхронизацию, как советует kunix, либо убирайте глобальный блок для данных и храните все в стеке, вообще не понятно зачем там эти навороты, т.к. обычный стек может спокойной сохранить ваши 3 переменные, к тому же это гарантированно потокобезопасный вариант.

| Сообщение посчитали полезным: Kindly


Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 04 февраля 2020 14:23
· Личное сообщение · #16

ну блин, сделал так, код проги:
Code:
  1.          lea rcx, ss:[rsp+0x58]
  2.          jmp 0x0000000141901126 << тут был вызов api GetSystemTime
  3.          nop
  4.          movzx eax, word ptr ss:[rsp+0x5A]
  5.          jmp 0x000000014190114C << тут был word с годом

прыгнули на 0x0000000141901126:
Code:
  1.          mov qword ptr ds:[0x0000000141901180], rcx << сохранил в свою секцию RCX
  2.          call qword ptr ds:[0x0000000140EE4770] << выполнил GetSystemTime
  3.          mov rax, 0x110001000B07E3 << записал свое значение
  4.          mov rcx, qword ptr ds:[0x0000000141901180] << прочитал адрес
  5.          mov qword ptr ds:[rcx], rax << подменил
  6.          jmp 0x0000000140BFCA4B << вернулся

далее проверяем год, прыгнув сюда 0x000000014190114C
Code:
  1.          movzx ecx, word ptr ss:[rsp+0x58] << оригинальный код
  2.          cmp word ptr ss:[rsp+0x58], 0x7E3 << проверяем или тут год 2019
  3.          je short @lab1 << если нет, то срабатывает бряк на NOP
  4.          nop
  5.          nop
  6.          nop
  7.          nop
  8.          nop
  9.  
  10. @lab1:
  11.          jmp 0x0000000140BFCA55

прикол в том, что бряк на NOP сработал и записался при сравнении в [rsp+0x58] год 2020.
я не юзал никаких PUSH POP, не записывался в ESP, не трогал EBP - все аккуратно.
когда сработал бряк на NOP я посмотрел, записано ли у меня по адресу мои данные, и да, считывание сохраненного адреса и запись нужных байт состоялась, но какого хера вот тут:
Code:
  1. movzx ecx, word ptr ss:[rsp+0x58]

не лежит мое значение?

я взял и сравнил адреса, при срабатывании бряка они остались. адрес RCX, который сохранялся это:

0000000025CCF638
E3 07

а это адрес [rsp+0x58] непосредственно при выполнении инструкции
movzx ecx, word ptr ss:[rsp+0x58]

и почему то по этому адрес лежит адресс:
0000000025ACF638
E4 07

какого хрена отличие адресов ровно на 200000 байт? какого хрена вообще так происходит - где криминал?

-----
Array[Login..Logout] of Life




Ранг: 43.1 (посетитель), 20thx
Активность: 0.160.29
Статус: Участник

Создано: 04 февраля 2020 14:43
· Личное сообщение · #17

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

Добавлено спустя 2 минуты
Почему нельзя сделать
Code:
  1. push rcx
  2. sub rsp, 0x20
  3. call GetSystemTime
  4. add rsp, 0x20
  5. pop rcx
  6. mov rax, свое_значение
  7. mov [rcx], rax

?




Ранг: 275.9 (наставник), 340thx
Активность: 0.22=0.22
Статус: Участник
RBC

Создано: 04 февраля 2020 15:11 · Поправил: Kindly
· Личное сообщение · #18

Все верно! Проблема оказалась в использовании переменной для записи адреса, потому как обычный push/pop RCX адреса решает проблему полностью, если использовать api:

Code:
  1.          push rcx
  2.          call GetSystemTime
  3.          mov rax, 0x110001000B07E3
  4.          pop rcx
  5.          mov qword ptr ds:[rcx], rax


но получается, что также все работает при использовании глобальной переменной, если не использовать вызов api в патч-коде.
user99 пишет:
но если не решить проблему с глобальной переменной, патч потокобезопасным не станет.

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

user99 пишет:
Почему нельзя сделать

именно, и да, отодвинуться на безопасное место в RSP также скорее всего стоит.

Всем спасибо! Отставлю еще пару лайков через 24 часа Темку закрою вечером, может кто-что хочет что дополнить, хотя уже все ясно ))

-----
Array[Login..Logout] of Life




Ранг: 43.1 (посетитель), 20thx
Активность: 0.160.29
Статус: Участник

Создано: 04 февраля 2020 15:34
· Личное сообщение · #19

Kindly пишет:
но получается, что также все работает при использовании глобальной переменной, если не использовать вызов api в патч-коде

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


 eXeL@B —› Крэки, обсуждения —› Изменение адреса api в многопоточной задаче
Эта тема закрыта. Ответы больше не принимаются.
   Для печати Для печати