Utilizando el formulario de información CONTEXT
puede encontrar sección de función y el desplazamiento de imagen PE. Por ejemplo, puede usar esta información para obtener el nombre de la función del archivo .map generado por el vinculador.
Obtener CONTEXT
struct. Usted está interesado en el miembro del contador del programa. Como CONTEXT
depende de la plataforma, debe resolverlo usted mismo. Ya lo hace cuando lo inicializa, por ejemplo STACKFRAME64.AddrPC.Offset = CONTEXT.Rip
para Windows x64. Ahora comenzamos la caminata a pie y usamos STACKFRAME64.AddrPC.Offset
, rellenado por StaclkWalk64
como nuestro punto de partida.
Debe traducirlo a la dirección virtual relativa (RVA) utilizando la dirección base de asignación: RVA = STACKFRAME64.AddrPC.Offset - AllocationBase
. Puede obtener AllocationBase
usando VirtualQuery
.
Una vez que tenga eso, necesita encontrar en qué sección cae este RVA y restar la dirección de inicio de la sección para obtener SectionOffset: SectionOffset = RVA - SectionBase = STACKFRAME64.AddrPC.Offset - AllocationBase - SectionBase
. Para hacerlo, debe acceder a la estructura del encabezado de la imagen PE (IMAGE_DOS_HEADER, IMAGE_NT_HEADER, IMAGE_SECTION_HEADER) para obtener el número de secciones en PE y sus direcciones de inicio/finalización. Es bastante sencillo.
Eso es todo. Ahora tiene el número de sección y el desplazamiento en la imagen PE. El desplazamiento de función es el desplazamiento más alto más pequeño que SectionOffset en el archivo .map.
puedo publicar código más tarde, si lo desea.
EDIT: Código para imprimir function address
(suponemos una CPU x64 genérico):
#include <iostream>
#include <windows.h>
#include <dbghelp.h>
void GenerateReport(void)
{
::CONTEXT lContext;
::ZeroMemory(&lContext, sizeof(::CONTEXT));
::RtlCaptureContext(&lContext);
::STACKFRAME64 lFrameStack;
::ZeroMemory(&lFrameStack, sizeof(::STACKFRAME64));
lFrameStack.AddrPC.Offset = lContext.Rip;
lFrameStack.AddrFrame.Offset = lContext.Rbp;
lFrameStack.AddrStack.Offset = lContext.Rsp;
lFrameStack.AddrPC.Mode = lFrameStack.AddrFrame.Mode = lFrameStack.AddrStack.Mode = AddrModeFlat;
::DWORD lTypeMachine = IMAGE_FILE_MACHINE_AMD64;
for(auto i = ::DWORD(); i < 32; i++)
{
if(!::StackWalk64(lTypeMachine, ::GetCurrentProcess(), ::GetCurrentThread(), &lFrameStack, lTypeMachine == IMAGE_FILE_MACHINE_I386 ? 0 : &lContext,
nullptr, &::SymFunctionTableAccess64, &::SymGetModuleBase64, nullptr))
{
break;
}
if(lFrameStack.AddrPC.Offset != 0)
{
::MEMORY_BASIC_INFORMATION lInfoMemory;
::VirtualQuery((::PVOID)lFrameStack.AddrPC.Offset, &lInfoMemory, sizeof(lInfoMemory));
::DWORD64 lBaseAllocation = reinterpret_cast<::DWORD64>(lInfoMemory.AllocationBase);
::TCHAR lNameModule[ 1024 ];
::GetModuleFileName(reinterpret_cast<::HMODULE>(lBaseAllocation), lNameModule, 1024);
PIMAGE_DOS_HEADER lHeaderDOS = reinterpret_cast<PIMAGE_DOS_HEADER>(lBaseAllocation);
PIMAGE_NT_HEADERS lHeaderNT = reinterpret_cast<PIMAGE_NT_HEADERS>(lBaseAllocation + lHeaderDOS->e_lfanew);
PIMAGE_SECTION_HEADER lHeaderSection = IMAGE_FIRST_SECTION(lHeaderNT);
::DWORD64 lRVA = lFrameStack.AddrPC.Offset - lBaseAllocation;
::DWORD64 lNumberSection = ::DWORD64();
::DWORD64 lOffsetSection = ::DWORD64();
for(auto lCnt = ::DWORD64(); lCnt < lHeaderNT->FileHeader.NumberOfSections; lCnt++, lHeaderSection++)
{
::DWORD64 lSectionBase = lHeaderSection->VirtualAddress;
::DWORD64 lSectionEnd = lSectionBase + max(lHeaderSection->SizeOfRawData, lHeaderSection->Misc.VirtualSize);
if((lRVA >= lSectionBase) && (lRVA <= lSectionEnd))
{
lNumberSection = lCnt + 1;
lOffsetSection = lRVA - lSectionBase;
break;
}
}
std::cout << lNameModule << " : 000" << lNumberSection << " : " << reinterpret_cast< void * >(lOffsetSection) << std::endl;
}
else
{
break;
}
}
}
void Run(void);
void Run(void)
{
GenerateReport();
std::cout << "------------------" << std::endl;
}
int main(void)
{
::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
::SymInitialize(::GetCurrentProcess(), 0, 1);
try
{
Run();
}
catch(...)
{
}
::SymCleanup(::GetCurrentProcess());
return (0);
}
Aviso, nuestra pila de llamadas es (de adentro hacia afuera) GenerateReport()->Run()->main()
. La salida del programa (en mi máquina, ruta es absoluta):
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000002F8D
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 00000000000031EB
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000003253
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000007947
C:\Windows\system32\kernel32.dll : 0001 : 000000000001552D
C:\Windows\SYSTEM32\ntdll.dll : 0001 : 000000000002B521
------------------
Ahora, pila de llamadas en términos de direcciones es (adentro hacia afuera) 00002F8D->000031EB->00003253->00007947->0001552D->0002B521
. Comparando los tres primeros desplazamientos a contenido .map
archivo:
...
0001:00002f40 [email protected]@YAXXZ 0000000140003f40 f FMain.obj
0001:000031e0 [email protected]@YAXXZ 00000001400041e0 f FMain.obj
0001:00003220 main 0000000140004220 f FMain.obj
...
donde 00002f40
es la más cercana a menores compensadas 00002F8D
y así sucesivamente. Últimos tres direcciones se refieren a funciones CRT/OS esa llamada main
(_tmainCRTstartup
etc) - debemos ignorarlos ...
Así, podemos ver que somos capaces de recuperar seguimiento de la pila con la ayuda de .map
archivo.Con el fin de generar seguimiento de la pila de excepción lanzada, todo lo que tiene que hacer es colocar el código en GenerateReport()
constructor de excepción (de hecho, esta GenerateReport()
fue tomada de mi código de excepción personalizada constructor de la clase (una parte de ella que)).
Check out my aplicación: http://www.dima.to/blog/?p=13 – Alexandru
Según sus comentarios en el blog de su implementación requiere AP-s. –