好的,第二个问题就在一天。 看起来Windows编程让我开心…:S
我目前正在试图获取Win32可执行文件的函数调用堆栈。
今天早上,我也问了一个关于这个问题:
Win32 – 从C代码回溯
现在,我很确定StackWalk64
函数是这个关键。 我已经阅读了一些关于如何使用它的文章,以及MS文档。
它实际上显示我的testing程序框架,所以它有点工作…
问题是我无法从堆栈信息中检索符号名称。
我正在使用SymGetSymFromAddr64
函数与UnDecorateSymbolName
。 但我只有垃圾人物。
这是我的代码。 希望它不要乱,因为我不习惯Windows编程:
void printStack( void ) { BOOL result; HANDLE process; HANDLE thread; CONTEXT context; STACKFRAME64 stack; ULONG frame; IMAGEHLP_SYMBOL64 symbol; DWORD64 displacement; char name[ 256 ]; RtlCaptureContext( &context ); memset( &stack, 0, sizeof( STACKFRAME64 ) ); process = GetCurrentProcess(); thread = GetCurrentThread(); displacement = 0; stack.AddrPC.Offset = context.Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Ebp; stack.AddrFrame.Mode = AddrModeFlat; for( frame = 0; ; frame++ ) { result = StackWalk64 ( IMAGE_FILE_MACHINE_I386, process, thread, &stack, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ); symbol.SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64 ); symbol.MaxNameLength = 255; SymGetSymFromAddr64( process, ( ULONG64 )stack.AddrPC.Offset, &displacement, &symbol ); UnDecorateSymbolName( symbol.Name, ( PSTR )name, 256, UNDNAME_COMPLETE ); printf ( "Frame %lu:\n" " Symbol name: %s\n" " PC address: 0x%08LX\n" " Stack address: 0x%08LX\n" " Frame address: 0x%08LX\n" "\n", frame, symbol.Name, ( ULONG64 )stack.AddrPC.Offset, ( ULONG64 )stack.AddrStack.Offset, ( ULONG64 )stack.AddrFrame.Offset ); if( !result ) { break; } } }
实际产出是:
Frame 0: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠ PC address: 0x00BA2763 Stack address: 0x00000000 Frame address: 0x0031F7E8 Frame 1: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠☺ PC address: 0x00BB4FFF Stack address: 0x00000000 Frame address: 0x0031F940 Frame 2: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠☻ PC address: 0x00BB4E2F Stack address: 0x00000000 Frame address: 0x0031F990 Frame 3: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♥ PC address: 0x75BE3677 Stack address: 0x00000000 Frame address: 0x0031F998 Frame 4: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♦ PC address: 0x770F9D72 Stack address: 0x00000000 Frame address: 0x0031F9A4 Frame 5: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♣ PC address: 0x770F9D45 Stack address: 0x00000000 Frame address: 0x0031F9E4 Frame 6: Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠♠ PC address: 0x770F9D45 Stack address: 0x00000000 Frame address: 0x0031F9E4
似乎奇怪的堆栈地址总是0的方式…任何帮助表示赞赏:)
谢谢大家!
编辑
我正在寻找一个普通的C解决scheme,没有第三方库…
您已经将symbol.MaxNameLength
设置为255,但您使用IMAGEHLP_SYMBOL64 symbol;
在堆栈上分配了“符号” IMAGEHLP_SYMBOL64 symbol;
。 该类型被定义为:
typedef struct _IMAGEHLP_SYMBOL64 { DWORD SizeOfStruct; DWORD64 Address; DWORD Size; DWORD Flags; DWORD MaxNameLength; TCHAR Name[1]; } IMAGEHLP_SYMBOL64;
注意Name字段默认只有一个字符。 如果你想存储更大的名字,你需要做的事情如:
const int MaxNameLen = 255; IMAGEHLP_SYMBOL64* pSymbol = malloc(sizeof(IMAGEHLP_SYMBOL64)+MaxNameLen*sizeof(TCHAR)); pSymbol->MaxNameLength = MaxNameLen;
否则, SymGetSymFromAddr64()
很可能会覆盖内存。 以下是结构的帮助页面 (添加了重点):
MaxNameLength :Name成员可以包含的字符串的最大长度,不包括空终止字符。 因为符号名称的长度可能不同,所以这个数据结构是由调用者分配的 。 这个成员被使用,所以库知道有多少内存可供符号名称使用。
看看codeplex上的Stackwalker项目 – 它是开源的。 很好地工作。
我使用你的代码,它也不起作用,直到我注意到在文档中,你首先需要调用SymInitialize,像SymInitialize(process,NULL,TRUE)。 你可以在RtlCaptureContext之前调用它。
首先要解决两个问题:
1)AShelly指出的名字需要预先分配。 你不需要malloc来做到这一点:
#define MY_MAX_SYM_LEN 255 printStack() { struct sym_pack_tag { IMAGEHLP_SYMBOL64 sym; char name[MY_MAX_SYM_LEN]; } sym_pack; IMAGEHLP_SYMBOL64 *symbol = &sym_pack.sym; ... symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64 ); symbol->MaxNameLength = MY_MAX_SYM_LEN; if (!SymGetSymFromAddr64( process, stack.AddrPC.Offset, &displacement, symbol )) ...
2)使用RtlCaptureContext()在32位构建中获取上下文是不好的。 如果您有64位机器,则将IMAGE_FILE_MACHINE_I386更改为适当的64位类型。 如果您有32位版本,那么使用内联汇编来正确设置EBP,ESP和EIP。 这是一个办法:
__declspec(naked) void WINAPI CaptureContext_X86ControlOnly(CONTEXT *context) { __asm { push ebp mov ebp, esp mov ecx, context //ecx = [ebp + 8] pop ebp //restore old frame pop eax //pop return address pop ecx //pop context as WINAPI needs. Note: ecx will stay the same mov [ecx]CONTEXT.ContextFlags, CONTEXT_CONTROL mov [ecx]CONTEXT.Ebp, ebp mov [ecx]CONTEXT.Eip, eax mov [ecx]CONTEXT.Esp, esp jmp eax } } //I'm writing from my memory - so step through the code above to double check.
小点 – SymGetSymFromAddr64是好的,但建议使用SymFromAddr来代替。
祝你好运,所有跟踪在Windows上的堆栈。
看到这个答案基本上是一样的问题:
https://stackoverflow.com/a/28276227/10592
请注意,您需要确保您的用户具有.pdb文件,并且他们的流程可以找到它 – 请参阅该答案以获取更多详细信息。