давным-давно делал тулзу, которая получала некоторый независимый код, настраивала ему импорты, обрамляла код инструкциями сохранения регистров, ставила в конец джамп на определенный адрес, а затем внедряла код в другой процесс и переключала регистр eip одного из потоков на начало кода (соответственно адрес, на который производился джамп в конце кода являлся текущим значением eip, регистры восстанавливались, то есть работоспособность потока после выполнения кода полностью восстанавливалась)... тогда, когда я писал эту тулзу мной была замечена одна странность... суть в том, что функция GetThreadContext возвращала не текущее значение eip, а eip + 1 байт... ну я тогда не стал этим заморачиваться и просто компенсировал смещение... а недавно довелось погонять тулзу на win xp sp1 (на sp2 и sp3 работает, как часы), так вот в sp1 значение eip возвращалось без смещения... дальше больше: после установки на машину студии 2005 и перезагрузки смещение снова появилось... я попытался отследить, с чем это связано, но в ринг3 вызовы GetThreadContext и SetThreadContext ничем не примечательны, поскольку до syscall лишь несколько инструкций, которые проверяют NTSTATUS возврата и все, а в ринг0 я не полез... исходя из перечисленных факторов у меня сложилось впечатление, что эта странность была сделана для удобства отладчика (установки прерывания)... это просто предположение, может кто-нить с этим сталкивался? и самый главный вопрос: по каким факторам можно судить, требуется ли это одно-байтовое смещение или нет? так как хотелось бы унифицировать тулзу и не особо переделывать сам алгоритм...
наблюдал такое с адресом исключения возвращаемым ExceptionHandler'ом при использовании SEH, на некоторых системах он был +1
ну как тебе объяснить... давай, чтобы не путать друг-друга в терминологии, пойдем таким путем... то есть я делаю SuspendThread потоку, получаю значение регистра EIP из контекста потока, потом отнимаю от этого значения единицу, далее внедряю код, переключаю регистр EIP этого потока так, чтобы он указывал на внедренный код, отпускаю поток, код выполняется потом делает джамп на адрес <старое значение EIP - 1> и все нормально (на xp выше sp1, или sp1 с установленной вижуал студией)... то есть мне необходимо вернуть исполнение потока на ту же самую инструкцию, с которой я произвел переключение контекста, но почему то работает именно возврат на адрес <старое значение EIP - 1> (на xp выше sp1, или sp1 с установленной вижуал студией)... на xp sp1 без студии для корректной работы нужно производить джамп именно на <старое значение EIP> (без смещения)... и я не могу понять, почему так происходит... у меня есть теория, мол это сделано для упрощения работы отладчика (вставки инструкции прерывания), я отследил вызовы функций в ринг3 вплоть до syscall, но ничего не нашел... из-за врожденной криворукости в ядро не полез, посмотрел в исходниках винды Nt-версии функций манипуляций с контекстами, но тоже ничего важного для этой темы не обнаружил...
Rel >с установленной вижуал студией Не должно влиять никоим образом на механизм такой глубины. >у меня есть теория, мол это сделано для упрощения работы отладчика Не вяжется. Даже если бы было нечто такое, дельта зависела бы от размера инструкции. Asterix >наблюдал такое с адресом исключения возвращаемым ExceptionHandler'ом при использовании SEH, на некоторых системах он был +1 Такое можно наблюдать для "int 3" закодированной как "CD 03". (На самом деле "int 3" – это trap, однако винда отматывает rip на один байт назад для симуляции fault'а в случае этого прерывания, и, поскольку длина "CD 03" == 2, то со стороны кажется, что rip сдвинут на 1 байт вперёд). Также есть другой случай: если выполняемая инструкция пересекает границу страницы, и страница, следующая за текущей, не валидна, то сохранённый rip будет указывать на начало инструкции – однако адрес, при обращении к которому произошло исключение, будет рапортован как начало невалидной страницы (и здесь всё абсолютно верно; но в зависимости от терминологии, адрес обращения можно или нельзя называть "адресом исключения"). Если же это не кейс, ты не мог бы представить ситуацию, влекущую целевой исход? Rel Куда указывает rip для случая, когда требуется отмотка? Для случая, когда не требуется?
Sol_Ksacap Тогда автор трассирует код, разумеется останов не на всех инструкциях, изза RF некоторые пропускаются, хотя про отладку явно не сказано.
ну вот выдержки по теме, чтобы много писать здесь: Код (Text): // Узнаем тид главного потока DWORD maintid; GetMainThreadTid(GetProcessId(hProc), &maintid); // Получаем хендл главного потока, останавливаем его HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, false, maintid); if(hThread == NULL) { return false; } SuspendThread(hThread); // Получаем текущий контекст потока CONTEXT cont; cont.ContextFlags = CONTEXT_FULL; if(GetThreadContext(hThread, &cont) == FALSE) { return false; } // Сохраняем значение регистра eip MEMADR oldeip = 0; #ifdef _WIN64 oldeip = (MEMADR)cont.Rip; // Работает с Vista x64 #else oldeip = (MEMADR)cont.Eip - 1; // Работает c XP SP2, XP SP3, Vista SP0, Vista SP1, а так же c XP SP1 с установленной студией //oldeip = (MEMADR)cont.Eip; // Работает c XP SP1 без студии #endif // Далее делаем полученный из вне код пригодным для внедрения // - настраиваем все необходимые коду импорты // - в начало кода добавляем пуш все регистры // - в конец кода добавляем поп все регистры // - в самый конец кода ставим либо джамп на адрес oldeip, либо пуш oldeip и рет // Далее внедряем код (адрес по которому попал код - pRemote) // Меняем контекст, отпускаем поток #ifdef _WIN64 cont.Rip = (MEMADR)oldeip; #else cont.Eip = (MEMADR)oldeip; #endif if(SetThreadContext(hThread, &cont) == FALSE) { return false; } ResumeThread(hThread); да, я согласен... сам удивился... не я канеш не спорю, я вполне мог где-то облажаться и сделать эти странные выводы))) поэтому и пишу, что уперся в странную вещь...
оу... я накосячил в выдержках из кода))) просто ещё сплю))) в конце... надо вот так: Код (Text): // Меняем контекст, отпускаем поток #ifdef _WIN64 cont.Rip = (MEMADR)pRemote; #else cont.Eip = (MEMADR)pRemote; #endif if(SetThreadContext(hThread, &cont) == FALSE) { return false; } ResumeThread(hThread);
вот конкретный пример (WinXP SP3): главный поток стоит в ожидании оконного сообщения Код (Text): 7C90E506 8D A4 24 00 00 00 00 lea esp,[esp] 7C90E50D 8D 49 00 lea ecx,[ecx] 7C90E510 8B D4 mov edx,esp 7C90E512 0F 34 sysenter 7C90E514 C3 ret 7C90E515 8D A4 24 00 00 00 00 lea esp,[esp] 7C90E51C 8D 64 24 00 lea esp,[esp] 7C90E520 8D 54 24 08 lea edx,[esp+8] 7C90E524 CD 2E int 2Eh 7C90E526 C3 ret отладчик верно указывает, что он остановился на 7C90E514, функция GetThreadContext возвращает EIP 7C90E515, следовательно вернуть управление мне нужно на EIP - 1... может ли проблема быть в том, что поток стоит в ожидании оконного сообщения?
в WinXP SP1 без студии та же ситуация: Код (Text): 7ffe0304 c3 ret 7ffe0305 8bd4 mov edx,esp 7ffe0307 0f05 syscall 7ffe0309 c3 ret но функция GetThreadContext возвращает верное значение EIP, а именно 7FFE0304...
Возможно, разница (для ХР1) именно в реализации кода ожидания оконного сообщения. Приведи пример кода для ХР1, где "-1" не делаешь - "Работает c XP SP1 без студии".
Код (Text): OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, + Код (Text): SuspendThread(hThread); + = вылет с ошибкой функции SuspendThread. Поток не замораживается, дальше GetThreadContext+SetThreadContext могут вести к совершенно неожиданным последствиям.
Sol_Ksacap это был int 3, но закодированный нормально 0xCC, а разница наблюдалась между поведением на win98 и winXP(или 2k не помню точно)
блин... я нашел, где был косяк... как я и думал, я облажался))) именно в этом дело... оказалось, что отладчик студии неправильно показывал адрес... windbg возвращал верное значение... -1 работала, тк всегда попадала на ret... то есть фактически при обработке оконного сообщения просто пропускалась одна итерация в цикле, и все работало... так что контекст возвращается правильно, извините за беспокойство...
Ну раз никто не захотел проверить, пришлось самому искать возможность итак имеем В XP: Exception: 80000003 at address: 0040107F В Win98: Exception: 80000003 at address: 00401080 что и требовалось доказать