Обратил внимание на то, что после загрузки РЕ-файла регистры eax..edi инициализируются определенными значениями. Хотелось бы знать, откуда эти значения собсно берутся (особенно интересуют esi и edi, т.к. под разными отладчиками эти значения разные, по крайней мере у меня). Поделитесь инфой, плиз, что почитать и тд... PS: Поиском пользовался, набрел на http://wasm.ru/forum/viewtopic.php?id=10496, да вот только по ссылке bogrus перебрасывает в список форумов
наверное не имеет особого значения... скорее всего в секции Крит их какая-то функция не почистила за собой
Rockphorr посмотрел. Из того,что меня интересует: в ebx лежит указатель на peb, в edx - на KiFastSystemCallRet, а вот esi/edi - когда как, по-разному - в Олли1.10 одни значения, в бете 2й другие, без отладчика третьи. Blackbeam может быть, так оно и есть...
Esi при Оле равен -1 поэтому используют это правильно как антиотладку http://vx.eof-project.net/viewtopic.php?id=142
Flint_ta Насчет разных не знаю, пока одна только машинка под рукой, гляну на днях вин2к, хр, висту. Для начала разобраться хотелось-бы, а там видно будет
Прежде всего, информация, откуда вообще берутся значения регистров. Скажу сразу, что изучал всё это довольно давно, так что проверял поведение 2k и XP. Впрочем, не думаю, что что-то существенно поменялось. Новый Win32-процесс создаётся функцией kernel32.CreateProcess[Internal]W (в 2k была CreateProcessW, начиная с XP это переходник к CreateProcessInternalW, которая и работает). В процессе работы эта функция делает много чего и, в частности, вызывает NtCreateThread для создания нового потока. NtCreateThread принимает в качестве одного из аргументов контекст потока. В случае CreateProcess[Internal]W контекст формирует процедура BaseInitializeContext. Она инициализирует сегментные регистры в стандартные значения cs=18h, ds=es=ss=20h, fs=38h, gs=0 (позднее ядро добавит к этим значениям CPL=3), заполняет eax=указатель на entrypoint PE-файла, ebx=указатель на PEB, eip=BaseProcessStartThunk, esp=указатель на последний dword в стеке. Все прочие регистры не инициализируются, и их фактические значения представляют собой мусор из стека (в частности, зависят от того, что программа-создатель процесса делала со стеком до вызова CreateProcess*). Следующий этап в жизни процесса - инициализация (включающая загрузку dll и вызов их точек входа с DLL_PROCESS_ATTACH) - реализована посредством APC и на значения регистров не влияет (за исключением случаев, когда какая-то точка входа dll либо tls callback поставила себе целью напакостить), поэтому этот этап можно пропустить. Далее в действие вступает BaseProcessStartThunk. Эта функция устанавливает SEH-фрейм, вызывает NtSetInformationThread, вызывает точку входа PE и (если оттуда вернулось управление) вызывает ExitThread с кодом, возвращённым точкой входа. Изменения в состоянии регистров, произведённые BaseProcessStartThunk, следующие. Volatile-регистры eax,ecx,edx устанавливаются при последнем вызове NativeAPI. eax принимает значение статуса операции, а поскольку она всегда успешна, то eax=0. ecx и edx зависят от используемого механизма системного вызова, в случае sysenter/sysexit из XP SP1 ecx=esp-8, edx=7FFE0304h, начиная с SP2 edx=KiFastSystemCallRet. Значение ebx не трогается, так что там остаётся указатель на PEB, записанный в BaseInitializeContext. Значения esi и edi не трогаются, так что там остаётся мусор из стека CreateProcess*. ebp и esp устанавливаются соответственно стековому фрейму, то есть ebp=(верхушка стека - 10h), esp=(верхушка стека - 3Ch).
"реализована посредством APC и на значения регистров не влияет (за исключением случаев, когда какая-то точка входа dll либо tls callback поставила себе целью напакостить)" мм, как она сможет напакостить?
При user-mode APC контекст прерванного потока хранится в пользовательском стеке (в данном случае - естественно, близко к верхушке), ничто не мешает значения в этом стеке немного подкорректировать. (Смотреть, например, ntdll.KiUserApcDispatcher - в процессе начальной инициализации она самая верхняя по стеку вызовов.)
>При user-mode APC контекст прерванного потока хранится в пользовательском стеке ...И именно на этот контекст будет указывать третий аргумент DllMain\TlsCallback ("LPVOID lpvReserved"). diamond, спасибо за чёткое описание.
Перед KiInitializeUserApc esp=top-4 (из исходного контекста), дальше так: esp = (esp & ~3) - sizeof(CONTEXT) - 0x10 dword [esp] = <apc routine> (для рассматриваемого случая LdrInitializeThunk; KiUserApcDispatcher затирает значение адресом возврата в себя) dword [esp+4] = context (0; LdrInitializeThunk затирает значение указателем на сохранённый контекст) dword [esp+8] = arg1 (база ntdll.dll) dword [esp+12] = arg2 (0) CONTEXT [esp+16] = сохранённый контекст eip = KiUserApcDispatcher Так что контекст располагается по top-2D0h.
точно Код (C++): TopOfStack = (ContextFrame.Esp & ~(__alignof(EXCEPTION_REGISTRATION_RECORD)-1)); Length = CONTEXT_ALIGNED_SIZE + sizeof(KAPC_RECORD); UserStack = ((TopOfStack - sizeof (EXCEPTION_REGISTRATION_RECORD)) & ~CONTEXT_ROUND) - Length; ... TrapFrame->HardwareEsp = UserStack;