Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

gflags와 _CrtIsMemoryBlock을 이용한 Heap 메모리 손상 여부 체크

지난 글에서,

windbg 분석 사례 - ODP.NET 사용 시 Finalizer에서 System.AccessViolationException 예외 발생으로 인한 비정상 종료
; https://www.sysnet.pe.kr/2/0/11785

windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12058

CoTaskMemFree에서 발생한 예외의 원인이 Heap이 깨진 것이라고 설명했습니다.

ntdll!RtlReportCriticalFailure+97
ntdll!RtlpHeapHandleError+12
ntdll!RtlpLogHeapFailure+96
ntdll!RtlFreeHeap+823
OraOps19!ssmem_free+f
OraOps19!OpsMetFreeValCtx+89
DomainNeutralILStubClass.IL_STUB_PInvoke(Oracle.DataAccess.Client.OpoMetValCtx*)+6e
Oracle.DataAccess.Client.MetaData.Finalize()+25

결국 해결을 하긴 했지만,

windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12086

그래도 혹시나 이 문제가 제가 만든 응용 프로그램에서 발생했다고 가정하고 문제를 빠르게 찾을 수 있는 방법이 없을까 고민을 하게 되었는데요. 가령 지난 글에서 사용한 예제 코드의 경우,

AcLayers의 API 후킹과 FaultTolerantHeap
; https://www.sysnet.pe.kr/2/0/12050

char *cp = new char[10];
(*(cp - 5))++;

CRT Heap의 헤더를 깨먹은 것이기 때문에 "delete cp;"를 수행 시 비정상 종료할 수 있습니다. C++의 경우에는 대개 할당을 한 함수 내에서 해제도 함께 하는 경우가 많기 때문에 문제를 쉽게 발견할 수 있는데, 반면 C#처럼 Finalizer로 인해 해제되는 경우에는 원인 분석이 쉽지 않습니다.




이런 유의 원인 분석을 위해 도움이 되는 것이 있다면 gflags의 heap 관련 옵션을 켜는 것입니다.

Detecting Heap Corruption Using GFlags and Dumps
; https://blogs.msdn.microsoft.com/webdav_101/2010/06/22/detecting-heap-corruption-using-gflags-and-dumps/

Debugging Heap corruption with Application Verifier and Debugdiag
; https://blogs.msdn.microsoft.com/lagdas/2008/06/24/debugging-heap-corruption-with-application-verifier-and-debugdiag/

gflags.exe -p /enable w3wp.exe /full
gflags.exe -p /enable ConsoleApplication1.exe /full

그러고 보니 예전에 ^^ 0xBAADF00D 쿠키를 확인하기 위해 사용해 본 적이 있었는데요.

Win32 Debug CRT Heap Internals의 0xBAADF00D 표시 재현
; https://www.sysnet.pe.kr/2/0/11255

옵션에 따라 할당 시점의 call stack을 가지고 있기 때문에 free/delete를 수행한 시점에 예외가 발생해도 어떤 메모리를 위해 할당한 것인지 구분할 수 있어 그와 연관된 코드를 좀 더 세심하게 찾아보는데 도움은 될 수 있습니다.

하지만, 저런 식으로 하면 Heap 관련 부가 정보가 더 점유하기 때문에 이전에는 CRT Header를 깨먹은 것도 gflags 이후에는 header가 아닐 수 있어 오히려 문제가 감춰지는 경우도 있습니다. (실제로 위의 예제 코드가 그런 경우입니다.) 이런저런 이유로 인해 이런 유의 문제를 해결하는 정답은 없습니다. 운이 좋다면, 깨진 header에 저장된 데이터를 보고... 만약 그것이 "파일 경로"인 듯한 데이터가 들어 있다면 프로그램에서 관련 데이터를 썼을 것 같은 코드를 범인으로 유추해 나가는 정도가 됩니다.

부가적으로 (Windows Heap이 아닌) CRT heap의 경우 _CrtIsMemoryBlock이라는 디버그 빌드에서만 사용 가능한 함수가 있습니다.

_CrtIsMemoryBlock
; https://learn.microsoft.com/ko-kr/cpp/c-runtime-library/reference/crtismemoryblock?view=vs-2019

그래도 이게 그나마 쓸만한데요, 다음과 같이 사용하면,

long requestNumber;
char* filename[MAX_PATH];
int linenumber;

printf("_CrtIsMemoryBlock == %d\n", _CrtIsMemoryBlock(cp, sizeof(char) * 10, &requestNumber,
    filename, &linenumber));

"(*(cp - 5))++;"로 깨진 바로 저 포인터를 넘겨서 해당 메모리가 유효한 CRT Heap의 구조를 가지고 있는지 체크해 줍니다. 물론, 그렇다고 해서 Header를 깬 위치를 알 수 있는 것은 아니고 의심이 가는 부분에 디버깅 용으로 저 코드를 심어놓고 assert 등의 처리를 해야 합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 3/9/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12235정성태6/19/202010074오류 유형: 620. Windows 10 - Inaccessible boot device 블루 스크린
12234정성태6/19/20209800개발 환경 구성: 494. NuGet - nuspec의 패키지 스키마 버전(네임스페이스) 업데이트 방법
12233정성태6/19/20209516오류 유형: 619. SQL 서버 - The transaction log for database '...' is full due to 'LOG_BACKUP'. - 두 번째 이야기
12232정성태6/19/20208454오류 유형: 618. SharePoint - StoreBusyRetryLater 오류
12231정성태6/15/202010984.NET Framework: 911. Console/Service Application을 위한 SynchronizationContext - AsyncContext
12230정성태6/15/202010318오류 유형: 617. IMetaDataImport::GetMethodProps가 반환하는 IL 코드 주소(RVA) 문제
12229정성태6/13/202012156.NET Framework: 910. USB/IP PROJECT를 이용해 C#으로 USB Keyboard + Mouse 가상 장치 만들기 [1]
12228정성태6/12/202012236.NET Framework: 909. C# - Source Generator를 적용한 XmlCodeGenerator파일 다운로드1
12227정성태6/12/202016222오류 유형: 616. Visual Studio의 느린 업데이트 속도에 대한 원인 분석 [5]
12226정성태6/11/202013484개발 환경 구성: 493. OpenVPN의 네트워크 구성 [4]파일 다운로드1
12225정성태6/11/202012486개발 환경 구성: 492. 윈도우에 OpenVPN 설치 - 클라이언트 측 구성
12224정성태6/11/202020432개발 환경 구성: 491. 윈도우에 OpenVPN 설치 - 서버 측 구성 [1]
12223정성태6/9/202014370.NET Framework: 908. C# - Source Generator 소개 [10]파일 다운로드2
12222정성태6/3/202010252VS.NET IDE: 146. error information: "CryptQueryObject" (-2147024893/0x80070003)
12221정성태6/3/20209986Windows: 170. 비어 있지 않은 디렉터리로 symbolic link(junction) 연결하는 방법
12220정성태6/3/202012461.NET Framework: 907. C# DLL로부터 TLB 및 C/C++ 헤더 파일(TLH)을 생성하는 방법
12219정성태6/1/202011567.NET Framework: 906. C# - lock (this), lock (typeof(...))를 사용하면 안 되는 이유파일 다운로드1
12218정성태5/27/202011468.NET Framework: 905. C# - DirectX 게임 클라이언트 실행 중 키보드 입력을 감지하는 방법 [3]
12217정성태5/24/20209932오류 유형: 615. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
12216정성태5/15/202013090.NET Framework: 904. USB/IP PROJECT를 이용해 C#으로 USB Keyboard 가상 장치 만들기 [14]파일 다운로드1
12215정성태5/12/202018130개발 환경 구성: 490. C# - (Wireshark의) USBPcap을 이용한 USB 패킷 모니터링 [10]파일 다운로드1
12214정성태5/5/202010448개발 환경 구성: 489. 정식 인증서가 있는 경우 Device Driver 서명하는 방법 (2) - UEFI/SecureBoot [1]
12213정성태5/3/202012086개발 환경 구성: 488. (User-mode 코드로 가상 USB 장치를 만들 수 있는) USB/IP PROJECT 소개
12212정성태5/1/20209780개발 환경 구성: 487. UEFI / Secure Boot 상태인지 확인하는 방법
12211정성태4/27/202012100개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202012539.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...