Как символы влияют на обход стека вызовов?

Я пытаюсь проанализировать аварийный дамп с помощью windbg, и я получаю непостоянные аварийные дампы в зависимости от того, какие символы загружены. Мое простое понимание состоит в том, что символы только помогают указать на то, на что ссылается стек, но сам стек остается неизменным. Это явно неправильно, но теперь я не знаю, на что, черт возьми, я смотрю.

Вот стек вызовов со всеми загруженными символами:

0:000> kn
 # ChildEBP RetAddr  
00 0012e120 7d61f60f ntdll!ZwGetContextThread+0x12
01 0012e130 000f0005 ntdll!RtlFreeHeap+0x711
WARNING: Frame IP not in any known module. Following frames may be wrong.
02 0012e1d0 6d5b5b20 0xf0005
03 0012e314 6d5b407f dbghelp!Win32LiveSystemProvider::OpenMapping+0x228
04 0012e464 0012e488 dbghelp!GenAllocateModuleObject+0x1ad
05 0012e4e4 6d5b588e 0x12e488
06 0012e69c 7d4d132f dbghelp!Win32LiveSystemProvider::GetOsCsdString+0x4d
07 0012e6b8 6d5b5fd2 kernel32!ReadProcessMemory+0x1b
08 0012e6e0 6d5b604e dbghelp!Win32LiveSystemProvider::ReadVirtual+0x3d
09 0012e700 6d5b2f3d dbghelp!Win32LiveSystemProvider::ReadAllVirtual+0x1d
0a 0012e728 6d5b304f dbghelp!WriteMemoryFromProcess+0x35
0b 0012e7ac 6d5b345b dbghelp!WriteThreadList+0xc1
0c 0012e7cc 6d5b367b dbghelp!WriteDumpData+0x83
0d 0012e90c 6d5b3778 dbghelp!MiniDumpProvideDump+0x174
*** WARNING: Unable to verify checksum for ERRHNDLR.dll
0e 0012e96c 0091235d dbghelp!MiniDumpWriteDump+0xc8
*** WARNING: Unable to verify timestamp for msvcr90.dll
0f 0012e9fc 7857dcaa ERRHNDLR!ExceptionTranslator+0x25d [c:\redacted\errorhandler.cpp @ 230]
10 0012ea48 7857d4f5 msvcr90!_CallSETranslator+0xa5
11 0012ea7c 7857d8c0 msvcr90!__CxxExceptionFilter+0x217
12 0012eadc 7857d9dd msvcr90!__CxxExceptionFilter+0x5e2
13 0012eb10 7857db94 msvcr90!__InternalCxxFrameHandler+0xdb
*** WARNING: Unable to verify checksum for PROGRAM.exe
14 0012eb84 004f1c9e msvcr90!__CxxFrameHandler3+0x26
15 0012eba8 004f1c9e PROGRAM!__sse2_available_init+0x1269c
16 0012ec0c 00130000 PROGRAM!__sse2_available_init+0x1269c
17 00000000 00000000 0x130000

Я могу сказать, что произошло что-то плохое, но, похоже, это произошло, как только приложение запустилось, а это не так.

Вот тот же стек вызовов, но без загруженных символов для msvcr90

0:000> kn
 # ChildEBP RetAddr  
00 0012e120 7d61f60f ntdll!ZwGetContextThread+0x12
01 0012e130 000f0005 ntdll!RtlFreeHeap+0x711
WARNING: Frame IP not in any known module. Following frames may be wrong.
02 0012e1d0 6d5b5b20 0xf0005
03 0012e314 6d5b407f dbghelp!Win32LiveSystemProvider::OpenMapping+0x228
04 0012e464 0012e488 dbghelp!GenAllocateModuleObject+0x1ad
05 0012e4e4 6d5b588e 0x12e488
06 0012e69c 7d4d132f dbghelp!Win32LiveSystemProvider::GetOsCsdString+0x4d
07 0012e6b8 6d5b5fd2 kernel32!ReadProcessMemory+0x1b
08 0012e6e0 6d5b604e dbghelp!Win32LiveSystemProvider::ReadVirtual+0x3d
09 0012e700 6d5b2f3d dbghelp!Win32LiveSystemProvider::ReadAllVirtual+0x1d
0a 0012e728 6d5b304f dbghelp!WriteMemoryFromProcess+0x35
0b 0012e7ac 6d5b345b dbghelp!WriteThreadList+0xc1
0c 0012e7cc 6d5b367b dbghelp!WriteDumpData+0x83
0d 0012e90c 6d5b3778 dbghelp!MiniDumpProvideDump+0x174
*** WARNING: Unable to verify checksum for ERRHNDLR.dll
0e 0012e96c 0091235d dbghelp!MiniDumpWriteDump+0xc8
*** WARNING: Unable to verify timestamp for msvcr90.dll
*** ERROR: Module load completed but symbols could not be loaded for msvcr90.dll
0f 0012e9fc 7857dcaa ERRHNDLR!ExceptionTranslator+0x25d [c:redacted\errorhandler.cpp @ 230]
10 0012ea48 7857d4f5 msvcr90+0x5dcaa
11 0012ea7c 7857d8c0 msvcr90+0x5d4f5
12 0012eadc 7857d9dd msvcr90+0x5d8c0
13 0012eb10 7857db94 msvcr90+0x5d9dd
14 0012eb4c 7d61ec4a msvcr90+0x5db94
15 0012eb70 7d61ec1b ntdll!ExecuteHandler2+0x26
16 0012ec18 7d61ea56 ntdll!ExecuteHandler+0x24
17 0012ec18 026fe31a ntdll!KiUserExceptionDispatcher+0xe
*** WARNING: Unable to verify checksum for Storage.dll
18 0012ef4c 026fddd0 Storage!CList<Property *,Property *>::AddTail+0xa [c:\program files (x86)\microsoft visual studio 9.0\vc\atlmfc\include\afxtempl.h @ 1003]
*** WARNING: Unable to verify checksum for Storage2.dll
19 0012ef54 0274f5ec Storage!PropertyList::Add+0x10 [c:\redacted\propertylist.cpp @ 236]
1a 0012ef5c 0012f280 Storage2!Thing::Process+0x12c [c:\redacted\thing.cpp @ 345]
1b 0012ef60 0fe8be80 0x12f280
*** WARNING: Unable to verify checksum for PROGRAM.exe
1c 0012f368 0043d9a1 0xfe8be80
1d 0012f3b0 004f1c9e PROGRAM!View::SelectObject+0x151 [c:\redacted\view.cpp @ 2724]
1e 0012f3d4 004ea73b PROGRAM!__sse2_available_init+0x1269c
*** WARNING: Unable to verify checksum for DLL1.dll
1f 0012f450 02847893 PROGRAM!__sse2_available_init+0xb139
*** WARNING: Unable to verify checksum for DLL2.dll
20 0012f4ac 02c06398 DLL1!_RawDllMainProxy+0x1ed5
21 0012f534 02c06b86 DLL2!__sse2_available_init+0x40eb
22 0012f5a8 02c03fdd DLL2!__sse2_available_init+0x48d9
23 0012f5e0 02c052f4 DLL2!__sse2_available_init+0x1d30
24 0012f664 0283c231 DLL2!__sse2_available_init+0x3047
25 0012f6b4 028475aa DLL1!Logic::Send+0x121 [c:\redacted\logic.cpp @ 438]
26 0012f750 7d94757c DLL1!_RawDllMainProxy+0x1bec
27 0012f7a4 00000000 user32!UserCallWinProcCheckWow+0x128

Эй, это действительно может быть полезно! Это также ближе к тому, что отображается в Visual Studio, когда я использую его для отладки аварийного дампа. Но стек вызовов VS совершенно другой ниже «Storage2!Thing::Process», что предполагает, что несвязанные функции каким-то образом находятся в стеке вызовов, поэтому я пробую windbg.

Итак, что мне не хватает? Почему выгрузка символов должна показывать потенциально более полезный стек вызовов?


person Joe    schedule 26.12.2011    source источник
comment
Отсутствие файла .pdb иногда может быть лучше, чем наличие неправильного файла .pdb. ммв.   -  person Hans Passant    schedule 26.12.2011
comment
Я получаю pdbs прямо из MS (теоретически), кроме своих, очевидно. Не знаю, как я наткнулся на Wipe msvcr90, удалите сервер символов из настроек и повторите попытку.   -  person Joe    schedule 26.12.2011
comment
Только что обнаружил, что если я не забуду сделать .ecxr, стек в любом случае будет отличным. ГАГ   -  person Joe    schedule 26.12.2011
comment
Используйте !analyze -v, чтобы автоматизировать кучу всего этого.   -  person Hans Passant    schedule 26.12.2011


Ответы (1)


Это длинный ответ, но вкратце: на x86 PDB содержат информацию FPO, которая позволяет отладчику надежно раскручивать стек вызовов. Это требуется в случае кадров FPO, где EBP не используется в качестве указателя кадра. При отсутствии PDB отладчик предполагает, что каждый кадр является кадром EBP, и будет просто проходить по цепочке EBP, пока не достигнет конца (т. е. до нечитаемого значения EBP).

Для получения более подробной информации о кадрах FPO и EBP есть хорошая статья здесь:

http://www.nynaeve.net/?p=91

Теперь, чтобы перейти к вашей проблеме. Первый стек вызовов, который вы показали, абсолютно правильный. Какой-то модуль выдал исключение, поэтому операционная система начала разматывать кадры вызовов в поисках обработчика исключения. К сожалению, никто не обработал ошибку, поэтому запустился обработчик исключений по умолчанию, который привел к сбою приложения. Поскольку стек вызовов вредоносного кода был раскручен, вы не видите в стеке ничего, кроме компонентов, поставляемых операционной системой.

Во втором случае у вас нет символов, поэтому ОС обрабатывает каждый кадр вызова, как будто это EBP. В данном случае вам «повезло» и вы подобрали мусорный EBP, который начал раскручивать старый стек вызовов. Хотя в данном случае это указывало на правильное решение, это своего рода отвлекающий маневр, который может привести к тому, что вы начнете свой анализ с неверных данных и потратите МНОГО времени (был там, сделал это!).

Команда .excr всегда подходит в случае исключения. Это работает, потому что операционная система сохраняет состояние регистра процессора во время исключения, прежде чем разворачивать кадры вызовов в поисках обработчика исключения. Команда .excr использует это состояние, чтобы вернуть вас во времени к моменту обнаружения плохого состояния, а не к тому моменту, когда операционная система пыталась его обработать.

-Скотт

person snoone    schedule 27.12.2011
comment
Я подозреваю, что это еще не все. Разматывание стека происходит также как часть обработки исключений и выполняется успешно независимо от наличия символов (в том числе и в x86). Почему? Не означает ли это, что информация раскрутки встроена в PE и для кадров FPO? - person Ofek Shilon; 28.01.2019
comment
Обработка исключений x86 требует, чтобы каждый фрейм с обработчиком исключений помещался в связанный список (либо для потока, либо для каждого процессора, в зависимости от режима) в своем прологе. Затем операционная система может пройтись по списку и узнать, куда раскрутить стек, если возникнет исключение. У операционной системы нет никакой магии, которая позволяла бы ей проходить каждый отдельный кадр в цепочке вызовов. x64 — это отдельная история (см. Unwind Data) - person snoone; 29.01.2019