제가 예전에 디버거 만들어보려고 깔짝 깔짝댔는데 회사일때문에
차일피일 미뤄지고 해서 제대로된 모양 못내고 그냥 특정 프로세스에
Attach Debug 시도후 내부 스레드들 각각의 함수호출 스택을 볼 수 있는
간단한 툴로 만들어서 쓰고 있는데요.. ( 사실 거의 쓸모없음 --; 인터페이스도
제대로 안되어있고 그래서 그걸 보여드리고 싶지만 창피해서 *--*.. )
그중에 Win32 Debug API를 이용한 Create Debug Process / Attach Debugging
도 있겠지만 더 중요하다고 보여지는 Dbghelp 라이브러리를 활용한 현재 스레드의
함수 호출 상태를 알 수 있는 StackWalk 및 심볼 이름 얻기에 대한 간단한 코드 조각을
보여드립니다. 제가 짠건 아니고요. 열심히 공부하고 이해하려고 한거니까요
같이 공유해서 같이 공부해 BoA요!

------------------

아직 디버거 클래스도 보기 민망할정도라서 전체소스는 ㅠㅠ..;;

#include
#pragma comment(lib,"Dbghelp")

// 프로세스 심볼을 로드한다.
SymInitialize(pi.hProcess, 0, FALSE);
SymLoadModule(pi.hProcess, pi.hFile, 0, 0, 0, 0);
// 이때 hFile은 심볼파일 패스를 의미하고요 . *.pdb파일 입니다. 아시다싶이 pdb파일에서
// 여러디버깅 정보및 함수 심볼 이름도 갖고 있거든요.
// 마치 SoftICE에서 심볼이름등 디버깅 정보를 얻기위해서 nms파일을 로드하는것과
//유사하다고 보시면 될듯합니다.

// 그런 후 해당 프로세스의 특정 스레드의 함수 호출 스택을 walk해 봅니다.
// 퍼온 소스기때문에 영문 주석은 그대루 보관합니다 *^^*; 출처는 msdn 쪽인가할꺼고요..
// stk 스택프레임을 저장하는 구조체에서 계속 스택을 검사하다가 stk.AddrReturn.Offset
// 리턴할 주소가 없을떄까지 walk하게 되어있는 구조입니다.

DWORD CBroDebugger::StackWalk( HANDLE hProcess, HANDLE hThread, DWORD exceptionAddress /*=0*/)
{
DWORD dwDisplacement = 0;
DWORD frames = 0;
LPSTR szSymName;
IMAGEHLP_MODULE mi;
DWORD machType;
CONTEXT Context;
STACKFRAME stk;
char buffer[1024];

PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL)buffer;

/* Get the thread context of hThread for calling StackWalk */
Context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(hThread, &Context))
{
DEBUG_TRACE( "GetThreadContext returned error code : %d\n", GetLastError());
return EXCEPTION_EXECUTE_HANDLER;
}
ZeroMemory(&stk, sizeof(stk));

machType = IMAGE_FILE_MACHINE_I386;

// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn''t mentioned in the documentation.
stk.AddrPC.Offset = Context.Eip;
stk.AddrPC.Mode = AddrModeFlat;
stk.AddrStack.Offset = Context.Esp;
stk.AddrStack.Mode = AddrModeFlat;
stk.AddrFrame.Offset = Context.Ebp;
stk.AddrFrame.Mode = AddrModeFlat;
/*
stk.AddrPC.Offset = exceptionAddress;
stk.AddrPC.Mode = AddrModeFlat;
stk.AddrStack.Offset = Context.Esp;
stk.AddrStack.Mode = AddrModeFlat;
stk.AddrFrame.Offset = Context.Ebp;
stk.AddrFrame.Mode = AddrModeFlat;
*/

memset(buffer, 0, sizeof(buffer));
sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
sym->MaxNameLength = sizeof(buffer) - sizeof(IMAGEHLP_SYMBOL);

/* Call StackWalk to retrieve all the stack frames */
while ( ::StackWalk(machType,
hProcess,
hThread,
&stk,
&Context,
(PREAD_PROCESS_MEMORY_ROUTINE) ReadProcessMemory,
SymFunctionTableAccess,
SymGetModuleBase,
0) && stk.AddrPC.Offset)
{
sym->Address = stk.AddrPC.Offset;
dwDisplacement = 0;

/* Get the function name corresponding to the code address */
if (SymGetSymFromAddr(hProcess, stk.AddrPC.Offset, &dwDisplacement, sym))
{
szSymName = sym->Name;
}
else
{
// 심볼을 찾을 수 없다면 아래처럼 찍히고요. 이는 release 실행본의 함수는
// 아래처럼 나오게 된다는 얘기입니다.
szSymName = "";
DEBUG_TRACE( "SymGetSymFromAddr returned error code : %d\r\n",
GetLastError());
}
DEBUG_TRACE( "%08x %08x %08x %08x %08x %08x ",
stk.AddrFrame.Offset,
stk.AddrReturn.Offset,
stk.Params[0],
stk.Params[1],
stk.Params[2],
stk.Params[3]);

/* Get the module name corresponding to the code address */
if (SymGetModuleInfo(hProcess, stk.AddrPC.Offset, &mi))
{
DEBUG_TRACE( "%s!", mi.ModuleName);
}


DEBUG_TRACE( "%s\r\n", szSymName);

if (!stk.AddrReturn.Offset)
{
break;
}
}

DEBUG_TRACE( "\r\n");

return EXCEPTION_EXECUTE_HANDLER;
}

+ Recent posts