Странность с функцией GetThreadContext

Тема в разделе "WASM.WIN32", создана пользователем Rel, 15 мар 2010.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    давным-давно делал тулзу, которая получала некоторый независимый код, настраивала ему импорты, обрамляла код инструкциями сохранения регистров, ставила в конец джамп на определенный адрес, а затем внедряла код в другой процесс и переключала регистр eip одного из потоков на начало кода (соответственно адрес, на который производился джамп в конце кода являлся текущим значением eip, регистры восстанавливались, то есть работоспособность потока после выполнения кода полностью восстанавливалась)... тогда, когда я писал эту тулзу мной была замечена одна странность... суть в том, что функция GetThreadContext возвращала не текущее значение eip, а eip + 1 байт... ну я тогда не стал этим заморачиваться и просто компенсировал смещение... а недавно довелось погонять тулзу на win xp sp1 (на sp2 и sp3 работает, как часы), так вот в sp1 значение eip возвращалось без смещения... дальше больше: после установки на машину студии 2005 и перезагрузки смещение снова появилось... я попытался отследить, с чем это связано, но в ринг3 вызовы GetThreadContext и SetThreadContext ничем не примечательны, поскольку до syscall лишь несколько инструкций, которые проверяют NTSTATUS возврата и все, а в ринг0 я не полез... исходя из перечисленных факторов у меня сложилось впечатление, что эта странность была сделана для удобства отладчика (установки прерывания)... это просто предположение, может кто-нить с этим сталкивался? и самый главный вопрос: по каким факторам можно судить, требуется ли это одно-байтовое смещение или нет? так как хотелось бы унифицировать тулзу и не особо переделывать сам алгоритм...
     
  2. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    наблюдал такое с адресом исключения возвращаемым ExceptionHandler'ом при использовании SEH, на некоторых системах он был +1
     
  3. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Rel
    Как это понять ?
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    ну как тебе объяснить... давай, чтобы не путать друг-друга в терминологии, пойдем таким путем... то есть я делаю SuspendThread потоку, получаю значение регистра EIP из контекста потока, потом отнимаю от этого значения единицу, далее внедряю код, переключаю регистр EIP этого потока так, чтобы он указывал на внедренный код, отпускаю поток, код выполняется потом делает джамп на адрес <старое значение EIP - 1> и все нормально (на xp выше sp1, или sp1 с установленной вижуал студией)... то есть мне необходимо вернуть исполнение потока на ту же самую инструкцию, с которой я произвел переключение контекста, но почему то работает именно возврат на адрес <старое значение EIP - 1> (на xp выше sp1, или sp1 с установленной вижуал студией)... на xp sp1 без студии для корректной работы нужно производить джамп именно на <старое значение EIP> (без смещения)... и я не могу понять, почему так происходит... у меня есть теория, мол это сделано для упрощения работы отладчика (вставки инструкции прерывания), я отследил вызовы функций в ринг3 вплоть до syscall, но ничего не нашел... из-за врожденной криворукости в ядро не полез, посмотрел в исходниках винды Nt-версии функций манипуляций с контекстами, но тоже ничего важного для этой темы не обнаружил...
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Rel
    Код покажите, чтото никак не пойму что вы делаете.
     
  6. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Rel
    >с установленной вижуал студией
    Не должно влиять никоим образом на механизм такой глубины.

    >у меня есть теория, мол это сделано для упрощения работы отладчика
    Не вяжется. Даже если бы было нечто такое, дельта зависела бы от размера инструкции.

    Asterix
    >наблюдал такое с адресом исключения возвращаемым ExceptionHandler'ом при использовании SEH, на некоторых системах он был +1
    Такое можно наблюдать для "int 3" закодированной как "CD 03". (На самом деле "int 3" – это trap, однако винда отматывает rip на один байт назад для симуляции fault'а в случае этого прерывания, и, поскольку длина "CD 03" == 2, то со стороны кажется, что rip сдвинут на 1 байт вперёд).
    Также есть другой случай: если выполняемая инструкция пересекает границу страницы, и страница, следующая за текущей, не валидна, то сохранённый rip будет указывать на начало инструкции – однако адрес, при обращении к которому произошло исключение, будет рапортован как начало невалидной страницы (и здесь всё абсолютно верно; но в зависимости от терминологии, адрес обращения можно или нельзя называть "адресом исключения").
    Если же это не кейс, ты не мог бы представить ситуацию, влекущую целевой исход?

    Rel
    Куда указывает rip для случая, когда требуется отмотка? Для случая, когда не требуется?
     
  7. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Sol_Ksacap
    Тогда автор трассирует код, разумеется останов не на всех инструкциях, изза RF некоторые пропускаются, хотя про отладку явно не сказано.
     
  8. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    ну вот выдержки по теме, чтобы много писать здесь:
    Код (Text):
    1. // Узнаем тид главного потока
    2. DWORD maintid;
    3. GetMainThreadTid(GetProcessId(hProc), &maintid);
    4.    
    5. // Получаем хендл главного потока, останавливаем его
    6. HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, false, maintid);
    7. if(hThread == NULL) { return false; }
    8. SuspendThread(hThread);
    9.  
    10. // Получаем текущий контекст потока
    11. CONTEXT cont;
    12. cont.ContextFlags = CONTEXT_FULL;
    13. if(GetThreadContext(hThread, &cont) == FALSE) { return false; }
    14.  
    15. // Сохраняем значение регистра eip
    16. MEMADR oldeip = 0;
    17. #ifdef _WIN64
    18.     oldeip = (MEMADR)cont.Rip; // Работает с Vista x64
    19. #else
    20.     oldeip = (MEMADR)cont.Eip - 1; // Работает c XP SP2, XP SP3, Vista SP0, Vista SP1, а так же c XP SP1 с установленной студией
    21.     //oldeip = (MEMADR)cont.Eip; // Работает c XP SP1 без студии
    22. #endif
    23.  
    24. // Далее делаем полученный из вне код пригодным для внедрения
    25. // - настраиваем все необходимые коду импорты
    26. // - в начало кода добавляем пуш все регистры
    27. // - в конец кода добавляем поп все регистры
    28. // - в самый конец кода ставим либо джамп на адрес oldeip, либо пуш oldeip и рет
    29.  
    30. // Далее внедряем код (адрес по которому попал код - pRemote)
    31.  
    32. // Меняем контекст, отпускаем поток
    33. #ifdef _WIN64
    34.     cont.Rip = (MEMADR)oldeip;
    35. #else
    36.     cont.Eip = (MEMADR)oldeip;
    37. #endif
    38.     if(SetThreadContext(hThread, &cont) == FALSE) { return false; }
    39.     ResumeThread(hThread);
    да, я согласен... сам удивился... не я канеш не спорю, я вполне мог где-то облажаться и сделать эти странные выводы))) поэтому и пишу, что уперся в странную вещь...
     
  9. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    оу... я накосячил в выдержках из кода))) просто ещё сплю))) в конце... надо вот так:
    Код (Text):
    1. // Меняем контекст, отпускаем поток
    2. #ifdef _WIN64
    3.     cont.Rip = (MEMADR)pRemote;
    4. #else
    5.     cont.Eip = (MEMADR)pRemote;
    6. #endif
    7.     if(SetThreadContext(hThread, &cont) == FALSE) { return false; }
    8.     ResumeThread(hThread);
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    вот конкретный пример (WinXP SP3):
    главный поток стоит в ожидании оконного сообщения
    Код (Text):
    1. 7C90E506 8D A4 24 00 00 00 00 lea         esp,[esp]
    2. 7C90E50D 8D 49 00         lea         ecx,[ecx]
    3. 7C90E510 8B D4            mov         edx,esp
    4. 7C90E512 0F 34            sysenter        
    5. 7C90E514 C3               ret              
    6. 7C90E515 8D A4 24 00 00 00 00 lea         esp,[esp]
    7. 7C90E51C 8D 64 24 00      lea         esp,[esp]
    8. 7C90E520 8D 54 24 08      lea         edx,[esp+8]
    9. 7C90E524 CD 2E            int         2Eh  
    10. 7C90E526 C3               ret
    отладчик верно указывает, что он остановился на 7C90E514, функция GetThreadContext возвращает EIP 7C90E515, следовательно вернуть управление мне нужно на EIP - 1... может ли проблема быть в том, что поток стоит в ожидании оконного сообщения?
     
  11. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    в WinXP SP1 без студии та же ситуация:
    Код (Text):
    1. 7ffe0304 c3              ret
    2. 7ffe0305 8bd4            mov     edx,esp
    3. 7ffe0307 0f05            syscall
    4. 7ffe0309 c3              ret
    но функция GetThreadContext возвращает верное значение EIP, а именно 7FFE0304...
     
  12. gorodon

    gorodon New Member

    Публикаций:
    0
    Регистрация:
    19 окт 2009
    Сообщения:
    301
    Возможно, разница (для ХР1) именно в реализации кода ожидания оконного сообщения.
    Приведи пример кода для ХР1, где "-1" не делаешь - "Работает c XP SP1 без студии".
     
  13. gorodon

    gorodon New Member

    Публикаций:
    0
    Регистрация:
    19 окт 2009
    Сообщения:
    301
    Нет, нет - давай без отладчика - сдампи сам код....
     
  14. gorodon

    gorodon New Member

    Публикаций:
    0
    Регистрация:
    19 окт 2009
    Сообщения:
    301
    Кстати - в этих двух примерах кода использовался один и тот же отладчик или разные?
     
  15. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    Код (Text):
    1. OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
    +
    Код (Text):
    1. SuspendThread(hThread);
    +
    = вылет с ошибкой функции SuspendThread. Поток не замораживается, дальше GetThreadContext+SetThreadContext могут вести к совершенно неожиданным последствиям.
     
  16. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    Sol_Ksacap
    это был int 3, но закодированный нормально 0xCC, а разница наблюдалась между
    поведением на win98 и winXP(или 2k не помню точно)
     
  17. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    блин... я нашел, где был косяк... как я и думал, я облажался)))

    именно в этом дело... оказалось, что отладчик студии неправильно показывал адрес... windbg возвращал верное значение... -1 работала, тк всегда попадала на ret... то есть фактически при обработке оконного сообщения просто пропускалась одна итерация в цикле, и все работало... так что контекст возвращается правильно, извините за беспокойство...
     
  18. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    проверьте под 9x, 2k, XP
    что кажет?
     
  19. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    больше интересует 9х, т.к. нет под рукой
     
  20. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    Ну раз никто не захотел проверить, пришлось самому искать возможность

    итак имеем

    В XP:
    Exception: 80000003 at address: 0040107F

    В Win98:
    Exception: 80000003 at address: 00401080

    что и требовалось доказать