Сейчас на форуме: hgdagon, asfa, bartolomeo (+4 невидимых)

 eXeL@B —› Программирование —› Как потокобезопасно подменить стек не трогая стек исходного потока
Посл.ответ Сообщение

Ранг: 4.1 (гость)
Активность: 0=0
Статус: Участник

Создано: 19 января 2007 10:47
· Личное сообщение · #1

Задача такая:Делаю перехват Api путем инжекта jmp на тело своего обработчика.
В обработчике нужно потокобезопасно сохранить регистры исходного потока, подменить стек на свой,
выполнить действия по обработке, восстановить стек и регистры.
Более подробно алгоритм представляется следующим:
1)потокобезопасно запоминаем регистры использующиеся для подмены стека,
2)меняем стек (StackBase,StackLimit,Esp)
3)pusha
4)тело основного обработчика
5)popa
6)восстанавливаем старый стек
7)восстанавливаем используемые регистры при подмене стека
8)jmp на api.
Сообственно интересует конкретная реализация пунктов 1,2,6,7, причем главное условие сделать это нужно не трогая
вообще стек исходного потока (любые операции со стеком исходного потока запрещены!).
Естественно заранее еще до установки перехвата выделена память в обьеме достаточном для хранения потоковых данных и своих стеков?
Собственно этот вопрос тоже задавался на wasm.ru но пока по subj ответов не получил.
Может у кого будут мысли по данному поводу. Заранее спасибо.




Ранг: 240.5 (наставник)
Активность: 0.190
Статус: Участник
Author of ACKiller

Создано: 19 января 2007 11:07
· Личное сообщение · #2

Сохранение регистров и старого esp в таком случае должно происходить в новый стек.
Вообще инфы в инете должно быть достаточно по этой теме.



Ранг: 24.9 (новичок)
Активность: 0.010
Статус: Участник

Создано: 19 января 2007 11:26
· Личное сообщение · #3

Goldy

Много раз подменял АПИ подобным, образом , ни разу стек не подменял, т.к. не было необходимости. Все и так работает.

Что за программа такая? Почему, любые операции со стеком исходного потока запрещены!?
(АПИ стек не бережет, использует на лево и направо)




Ранг: 105.9 (ветеран)
Активность: 0.060
Статус: Участник

Создано: 19 января 2007 11:36
· Личное сообщение · #4

Что-то не совсем понятно написано. Нахера менять стек? Делаешь инжект на свою процедуру, в ней ты спокойно можешь определить локальные переменные, которые будут "потокобезопасны". Если не хватает памяти то выделяешь еще через VirtualAlloc. Вот и все.



Ранг: 4.1 (гость)
Активность: 0=0
Статус: Участник

Создано: 19 января 2007 12:38
· Личное сообщение · #5

Тут главная задача не перехват АПИ а перехват взаимодействия длл модулей программы между собой, подсчет статистики вызовов анализ параметров и т.д. А данный сабж нужен чтоб свести к нулю воздействие на исполняемую программу (программа может отслеживать модификацию стека, можно вызвать переполнение и т.д.)




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 19 января 2007 14:31 · Поправил: s0larian
· Личное сообщение · #6

Goldy пишет:
А данный сабж нужен чтоб свести к нулю воздействие на исполняемую программу

Не.... ты зря морочешь себе яйца. Если софтина вызывает API, то гарантия только в том, что когда-нить EIP будет показывать на след. инструкцию, стек будет подчищен от аргументов, и твоя часть стека (более высокие адреса) не будет попорчена. Что и как происходит с оставшимся местом на стеке - не известно и не важно. Более того, что б это проверить надо задекларировать char stack[1024]; и это читать - что есть бред - это ведь зависит от версии винды, версии SP, версии IE, MFC и т.д.

Ну а по поводу потоков - то что на стеке - уже само собой видимо только потоку. Твой код перехвата будет работать в контексте (то есть на стеке) вызывающего потока.




Ранг: 105.9 (ветеран)
Активность: 0.060
Статус: Участник

Создано: 19 января 2007 14:49
· Личное сообщение · #7

Goldy
По поводу затруднительных пунктов 1,2,6,7 - тут все очень просто. Выкинь их вообще.




Ранг: 2014.5 (!!!!), 1278thx
Активность: 1.340.25
Статус: Модератор
retired

Создано: 20 января 2007 00:00
· Личное сообщение · #8

Можешь попытаться объявить массив двордов, после получения управления запомнить в своей переменной esp, поменять esp на свой массив, потом уже 3-5, восстанавливать esp. При необходимости копировать сзначения изначального стека в свой.
Второй вариант-копировать изначальный стек в свой массив, работать в общем стеке, а потом скопировать из массива обратно в общий.



Ранг: 163.7 (ветеран)
Активность: 0.070
Статус: Участник

Создано: 20 января 2007 00:40
· Личное сообщение · #9

rsdn.ru/article/baseserv/stack.xml



Ранг: 162.2 (ветеран)
Активность: 0.090
Статус: Участник

Создано: 20 января 2007 03:04
· Личное сообщение · #10

Goldy пишет:
3)pusha
4)тело основного обработчика
5)popa

pushad/popad только. Сам один раз часа 3 из-за этого баг искал.



Ранг: 4.1 (гость)
Активность: 0=0
Статус: Участник

Создано: 20 января 2007 10:40
· Личное сообщение · #11

Archer->Главный вопрос был как это сделать потокобезопасно! В твоем варианте это не проходит!
S_T_A_S_->Это я уже давно прочел
Вобще мне нужно 4 байта для сохранения одного регистра и это место должно быть или в TLS или вычисляться динамически в моем куске памяти в зависемости от того под каким потоком мы работаем.
Остальной процесс подмены стека и т.д. понятен.




Ранг: 240.5 (наставник)
Активность: 0.190
Статус: Участник
Author of ACKiller

Создано: 20 января 2007 10:48
· Личное сообщение · #12

Goldy пишет:
Вобще мне нужно 4 байта для сохранения одного регистра и это место должно быть или в TLS или вычисляться динамически в моем куске памяти в зависемости от того под каким потоком мы работаем.

А в чем проблема? ESP для каждого потока свой, новый стек ведь тоже - так и сохраняй старый esp в новом стеке. Или я чего-то не понял?



Ранг: 4.1 (гость)
Активность: 0=0
Статус: Участник

Создано: 20 января 2007 12:43
· Личное сообщение · #13

Проблема в том как не используя регистров вычислить динамически в зависемости от индефикатора потока нужный кусок памяти для сохранения регистра, причем нужно это сделать не меняя ни одного регистра так как стек текущего потока по условию трогать нельзя тоесть push отпадает. Или можно воспользоваться местом в TLS потока но для этого нужно знать какой элемент структуры можно безопасно затереть без потери функционала (пока еще не нашел).
Условно ситуция:
Поток A-вызывает функцию F
Поток В-вызывает функцию F
Что получается если в момент вызова A если мы используем статический адрес для сохранения esp например
mov [Addr],esp
выполнили команду
переключились на поток B
опять выполнили команду mov [Addr],esp
в результате работа потока A нарушена так как восстановление esp будет из другого адреса.
Для исключения этой ситуации поток A должен сохранять esp в Addr1,B в Addr2 и т.д. Тоесть адрес
должен меняться взависемости от потока под которым работает функция.
Теперь надеюсь смысл понятен?




Ранг: 240.5 (наставник)
Активность: 0.190
Статус: Участник
Author of ACKiller

Создано: 20 января 2007 12:58
· Личное сообщение · #14

Ну тогда
HoBleen пишет:
ESP для каждого потока свой, новый стек ведь тоже - так и сохраняй старый esp в новом стеке.

Стек надо формировать динамически, тогда проблемма с конкретным адресом для разных потоков отпадет.

А вообще как-то слабо вериться что такое извращение необходимо ИМХО.



Ранг: 163.7 (ветеран)
Активность: 0.070
Статус: Участник

Создано: 20 января 2007 18:22 · Поправил: S_T_A_S_
· Личное сообщение · #15

Goldy
А создание процесса ты можешь контроллировать? Впринципе, твою задачу можно заметно упростить, если место под запасной стек резервировать непосредственно ниже стека треда. Тогда в хуке нужно быдет лишь отнять от esp некоторую константу. Хотя вся эта затея с подменой стека ИМХО довольно сомнительна, там что, реально есть серьёзные проверки? Скорее всего достаточно закоммитить нижнюю половину стека треда и подправить лимит, что бы ОС туда не намусорила (а часто на стеке юзается не больше нескольких десятков К, хотя в хзаголовке PE стоит 1M)



Ранг: 4.6 (гость)
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2007 02:02 · Поправил: entusiast
· Личное сообщение · #16

HoBleen, иногда по непонятным причинам перестает работать Win32 API в перехватчиках. У меня такое было, когда я перехватом распаковывал Ил-2. Пришлось динамически менять стек.

Goldy,
примерно так. Приблизительно (!), ибо исходник сдох с переноской.
pushad
VirtualAlloc(NULL,1000h,MEM_COMMIT,PAGE_READWRITE)
push eax
add eax,1000h
pop dword ptr [eax] // 'remember' buffer for furhter VirtualFree
sub eax,4
mov [eax],esp
sub eax,4 // 'remember' old stack
Это подготовка буфера к юзу в качестве стека.
Дальше, если тебе нужны параметры исходной ф-ции,надо скопировать кус стека в буфер от eax вниз.
или же иными словами, при SZ=4+sizeof Parameters от eax-SZ вверх. Исходный вычислить от текущего esp
т.е. что-то вроде
mov esi,esp
add esi,0x1C // skip pushad if unneeded
mov edi,eax
mov ecx,50h // 50h байт может оказаться достаточно. Если мало - увеличь
sub edi,ecx
shr ecx,2
rep movsd // скопировали кусок старого стека.
sub edi,50h // откатили edi обратно вниз. (***)
mov esp,edi // все. Стек переадресован.
Call OurAPIReplaceFunction// получит первым параметром RetAddress вызванной, далее, как положено.
После возврата, стек указывает в наш буфер. Надо восстановить.
mov esi,esp
and esi,1000h
add esi,0FF8h
mov esp,[esi]// по идее, если нет ошибок, стек должен восстановиться.
push eax // сохраняем результат
add esi,4
push MEM_RELEASE
push 0
push dword ptr[esi]
call VirtualFree
pop eax // взяли в еах рензультат
mov [esp+....],eax // вместо .... смещение в стеке такое, чтоб после popad eax попал в еах.
popad
retn
(***) - Надо уточнять смещения. Я постоянно промахиваюсь на +/-1 после блочных операций, мог слажать.
Идея понятна?

PS. Это работает для __stdcall & __cdecl, где параметры передаются только стеком. для __фастколл надо передавать и контекст и стек.



Ранг: 4.1 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 февраля 2007 03:50
· Личное сообщение · #17

Хех сам алгоритм переадресации стека у меня и так давно написан и работает. Ты лучше скажи как это сделать вобще не трогая стека исследуемой программы. А у тебя первой же командой pushad затирается нижний байты старого стека, которые могут отслеживаться программой
Идея S_T_A_S_ наиболее предпочтительна, но при этом приходится жертвовать большим кол-вом памяти в случае большого числа потоков.



Ранг: 4.6 (гость)
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2007 08:14
· Личное сообщение · #18

Goldy, понятно, сорри. Я не углядел условия о неизменности стека. но, кстати, даже обращение к TLS все-таки требует "порчи" стека регистров... Т.е. даже если ты знаешь индекс, то TLSSetValue все равно через стек работает....
А насчет идеи S_T_A_S, то вопрос- как не трогая ничего определить ThreadID? Даже, если есть обходной путь без вызовов АПИ (а он, кстати, есть?), то.... а что.... можно попробовать ковырнуть TEB, если я, конечно, правильно понял эту структуру. Там есть поля Spare2-Spare4 ULONG каждый. Может, так выйдет.....
Смотрю ntundoc, структуры TEB и NT_TIB - похоже, все-таки, это оно доступно через селектор fs:....
Попробуй, может, выйдет чего...... Тогда можно в Spare записать нужный адрес при создании потока....
Но тут у меня практики никакой - с этими структами я только в области SEH и IsDebuggerPresent общался.




Ранг: 240.5 (наставник)
Активность: 0.190
Статус: Участник
Author of ACKiller

Создано: 01 февраля 2007 08:32
· Личное сообщение · #19

Goldy
Можешь с помощью эмуляции RtlEnterCriticalSection (Через Lock inc без использования стека) использовать способ и для многопоточного приложения - статическая область памяти используется как временный стек для создания нового стека. После xxxAlloc заменяешь стек и освобождаешь этот временный, после этого другой поток может использовать его для создания своего стека.

Думаю, основной момент понятен?


 eXeL@B —› Программирование —› Как потокобезопасно подменить стек не трогая стек исходного потока
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати