C Runtime Library Exception Dump 남기기 첫번째방법

by Lyn posted Jun 11, 2013
?

단축키

Prev이전 문서

Next다음 문서

ESC닫기

크게 작게 위로 아래로 댓글로 가기 인쇄

사후 디버깅을 위해 가장 많이 쓰이는 방법중 하나가 메모리 덤프를 남기는 방법입니다.

보통 아래와 같은 형태로 구현 하게 되는데요. 과연 이 프로그램은 덤프가 남을까요?


#include <Windows.h>
#include <DbgHelp.h>
#include <cstdio>
#include <string>


using namespace std;

#pragma comment(lib, "dbghelp.lib")

LONG __stdcall TopLvFilter(PEXCEPTION_POINTERS pExceptionPointer)
{
	MINIDUMP_EXCEPTION_INFORMATION MinidumpExceptionInformation;
	std::wstring DumpFileName = L"dmpfile.dmp";

	MinidumpExceptionInformation.ThreadId = ::GetCurrentThreadId();
	MinidumpExceptionInformation.ExceptionPointers = pExceptionPointer;
	MinidumpExceptionInformation.ClientPointers = FALSE;

	if (DumpFileName.empty() == true)
	{
		::TerminateProcess(::GetCurrentProcess(), 0);
	}

	HANDLE hDumpFile = ::CreateFile(DumpFileName.c_str(),
		GENERIC_WRITE,
		FILE_SHARE_WRITE,
		nullptr,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL, nullptr);

	MiniDumpWriteDump(GetCurrentProcess(),
		GetCurrentProcessId(),
		hDumpFile,
		MiniDumpNormal,
		&MinidumpExceptionInformation,
		nullptr,
		nullptr);

	::TerminateProcess(::GetCurrentProcess(), 0);

	return 0;
}

void main()
{
	SetUnhandledExceptionFilter(TopLvFilter);	
	atoi(nullptr);
}


네. 결론부터 말하자면 위 코드에서는 덤프가 남지 않습니다.
왜냐면 CRT 에서 에러가 발생하면 UnhandledExceptionFilter 를 CRT가 자신의 예외 핸들러로 다시 세팅 하기 때문입니다.

그러나 우리가 바라는건 별로 친절하지도 않은 (...) CRT의 에러메세지가 아니라 메모리 덤프지요.

우리가 원하는대로 덤프를 남기는 방법은 여러 가지가 있지만 그 중 한가지로 해결 해 보겠습니다.

이번에 사용 할 방법은 코드를 패치해서 CRT가 감히 내가 만든 예외처리기를 바꾸지 못하도록 (...) 하는 방법입니다



#include <Windows.h>
#include <DbgHelp.h>
#include <cstdio>
#include <string>

using namespace std;

#pragma comment(lib, "dbghelp.lib")

BYTE HotPatchingBackup[5];

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI NullSetUnhandledExceptionFilter(
  _In_  LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
)
{
    return nullptr;
}

void InstallAPIHook_SetUnhandledExceptionFilter()
{
    BYTE *pSetUnhandledExceptionFilter;

    DWORD ProtectOption = 0;
    DWORD Temp = 0;

    pSetUnhandledExceptionFilter = (BYTE*)&SetUnhandledExceptionFilter;

	HANDLE hSelfProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , FALSE, ::GetCurrentProcessId());
	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, PAGE_EXECUTE_WRITECOPY, &ProtectOption);     	

    memcpy(HotPatchingBackup, pSetUnhandledExceptionFilter, 5);

    *pSetUnhandledExceptionFilter = 0xE9;
	pSetUnhandledExceptionFilter++;
    *((DWORD *)(pSetUnhandledExceptionFilter)) = ((DWORD)NullSetUnhandledExceptionFilter) - ((DWORD)pSetUnhandledExceptionFilter + 4);	
	pSetUnhandledExceptionFilter--;

	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, ProtectOption, &Temp);

	CloseHandle(hSelfProcess);
}

void UnInstallAPIHook_SetUnhandledExceptionFilter()
{
    BYTE *pSetUnhandledExceptionFilter;

    DWORD ProtectOption;
    DWORD Temp;

    pSetUnhandledExceptionFilter = (BYTE*)&SetUnhandledExceptionFilter;
	
	HANDLE hSelfProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , FALSE, ::GetCurrentProcessId());
	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, PAGE_EXECUTE_READWRITE, &ProtectOption);    

    memcpy(pSetUnhandledExceptionFilter, HotPatchingBackup, 5);

	VirtualProtectEx(hSelfProcess, (void *)pSetUnhandledExceptionFilter, 5, ProtectOption, &Temp);     

	CloseHandle(hSelfProcess);
}

LONG __stdcall TopLvFilter(PEXCEPTION_POINTERS pExceptionPointer)
{
    MINIDUMP_EXCEPTION_INFORMATION MinidumpExceptionInformation;
    std::wstring DumpFileName = L"dmpfile.dmp";

    MinidumpExceptionInformation.ThreadId = ::GetCurrentThreadId();
    MinidumpExceptionInformation.ExceptionPointers = pExceptionPointer;
    MinidumpExceptionInformation.ClientPointers = FALSE;

    if (DumpFileName.empty() == true)
    {
        ::TerminateProcess(::GetCurrentProcess(), 0);
    }

    HANDLE hDumpFile = ::CreateFile(DumpFileName.c_str(),
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        nullptr,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, nullptr);

    MiniDumpWriteDump(GetCurrentProcess(),
        GetCurrentProcessId(),
        hDumpFile,
        MiniDumpNormal,
        &MinidumpExceptionInformation,
        nullptr,
        nullptr);

    ::TerminateProcess(::GetCurrentProcess(), 0);

    return 0;
}

void main()
{
    SetUnhandledExceptionFilter(TopLvFilter);
    InstallAPIHook_SetUnhandledExceptionFilter();
    atoi(nullptr);
    UnInstallAPIHook_SetUnhandledExceptionFilter();
}


API 훅을 해서 필터를 바꾸려 하면 아무 일도 하지 않는 NullSetUnhandledExceptionFilter 이 호출되도록 하여 막는 방법입니다...
멋있어 보이기는 하지만 이 코드는 64bit/32bit 를 따로 만들어야 하고, 심지어 윈도우 8 에서는 이 코드가 정상적으로 동작 하지 않습니다 (....) 아마도 강화된 보안때문에 그런게 아닌가 싶은데...

어쨋든 윈도우7 까지는 정상적으로 덤프가 남습니다.


그럼 이런 편법 말고 좀 정석적인 방법이 필요한데... 그건 다음글로...

TAG •

Articles

1 2 3 4 5 6 7 8