Код (Text): push ebp .text:00401062 mov ebp, esp .text:00401064 push 0FFFFFFFFh .text:00401066 push offset aGetprocaddress ; "GetProcAddress" .text:0040106B push 401E92h .text:00401070 mov eax, large fs:0 .text:00401076 push eax .text:00401077 mov large fs:0, esp .text:0040107E sub esp, 58h .text:00401084 mov [ebp-18h], esp .text:00401087 call ds:GetVersion .text:0040108D xor edx, edx .text:0040108F mov dl, ah .text:00401091 mov dword_4036D1, edx .text:00401097 mov ecx, eax .text:00401099 and ecx, 0FFh .text:0040109F mov dword_4036CD, ecx .text:004010A5 shl ecx, 8 .text:004010A8 add ecx, edx .text:004010AA mov dword_4036C9, ecx .text:004010B0 shr eax, 10h .text:004010B3 mov dword_4036C5, eax .text:004010B8 push 7B2Dh .text:004010BD call ds:CloseHandle .text:004010C3 xor eax, eax .text:004010C5 int 2Eh ; DOS 2+ internal - EXECUTE COMMAND .text:004010C5 ; DS:SI -> counted CR-terminated command string .text:004010C7 sub eax, -40001FC7h .text:004010CC push eax .text:004010CD add edx, [esp] .text:004010D0 add esp, 4 .text:004010D3 call edx .text:004010D5 push 1136h .text:004010DA call ds:CloseHandle В зверьке. С точки входа. До .text:004010C3 не доходит, по адресу 401E92h -- нули. Кто-нить понимает что происходит тут?)
Простой антиотладочный механизм(совокупность механизмов) - адрес процедуры защищён два раза ядром, первый раз - генерацией сепшина при подключённом отладосном порте, второй - зависимость адреса от TF. У всякого входа в ядро существует определённая модель вызова и возврата. 0x2e шлюз требует в регистре Eax номер сервиса, в Edx указатель на параметры в стеке. Контекст при возврате зависит от флажка TF(ветвление после проверки, если процессор поддерживает быстрый возврат(Sysexit)), при трассировке значения будут абсолютно другими в Ecx и Edx, в регистре Eax возвращается NTSTATUS код ошибки. При входе в сервис посредством 0x2e шлюза модель возврата: - Edx содержит адрес следующей за Int инструкции. - Ecx содержит указатель на стек. Модель возврата довольно сложна, изза множетсва нюансов. Если вход выполнен при взведённом TF, ядро загружает в регистры Ecx и Edx значения, которые были в ядерном стеке для очистки его(маркер последнего сех-фрейма и слово содержащее предыдущий режим, см. макро EXIT_ALL). Предположим что процессор поддерживает пару инструкций Sysenter/Sysexit: CloseHandle() с хэндлом 0x7B2D. Столь большой хэндл не валидный(врядтле открыто стока описателей). В этом случае если к процессу подключен отладчик(имеется отладочный порт) генерируется програмный сепшин и тред входит в обработку пользовательской ошибки при закрытии хэндла(KiRaiseUserExceptionDispatcher()). В большинстве апи после вызова сервиса следует конверсия ядерного кода ошибки в пользовательский последовательностью функций BaseSetLastNTError -> RtlNtStatusToDosError -> RtlNtStatusToDosErrorNoTeb. Последняя загружает в регистр Edx разность кода ошибки и табличного. Для XP при коде ошибки STATUS_INVALID_HANDLE возвращается 7. Это крайне плохой код, привязка к версии операционной системы, автору следовало вызвать вместо апи сервис NtClose. Если отладочный порт не подключен сервис вернёт в регистре Edx не валидный указатель, далее для 0x2e шлюза это эвляется указателем на стек, так как он не валидный сервис возвратит STATUS_ACCESS_VIOLATION. Далее это значение служит основой для определения адреса процедуры - регистр Edx содержит текущий адрес кода, что и является определением дельта-смещения. Разумеется под отладчиком этот адрес будет другим(Edx = -1..). Тоесть смещение относительно адреса 0x4010C5 будет 0C0000005(STATUS_ACCESS_VIOLATION) - (-0x40001FC7) = 0x1FCC, тоесть адрес 0x403091.