Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
(시리즈 글이 9개 있습니다.)
디버깅 기술: 74. x64 콜 스택 인자 추적과 windbg의 Child-SP, RetAddr, Args to Child 값 확인
; https://www.sysnet.pe.kr/2/0/10832

디버깅 기술: 77. windbg의 콜스택 함수 인자를 쉽게 확인하는 방법
; https://www.sysnet.pe.kr/2/0/10934

디버깅 기술: 106. windbg - x64 역어셈블 코드에서 닷넷 메서드 호출의 인자를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/11348

디버깅 기술: 107. windbg - x64 SOS 확장의 !clrstack 명령어가 출력하는 Child SP 값의 의미
; https://www.sysnet.pe.kr/2/0/11349

디버깅 기술: 111. windbg - x86 메모리 덤프 분석 시 닷넷 메서드의 호출 인자 값 확인
; https://www.sysnet.pe.kr/2/0/11451

디버깅 기술: 128. windbg - x64 환경에서 닷넷 예외가 발생한 경우 인자를 확인할 수 없었던 사례
; https://www.sysnet.pe.kr/2/0/11991

디버깅 기술: 139. windbg - x64 덤프 분석 시 메서드의 인자 또는 로컬 변수의 값을 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12069

디버깅 기술: 172. windbg - 파일 열기 시점에 bp를 걸어 파일명 알아내는 방법(Managed/Unmanaged)
; https://www.sysnet.pe.kr/2/0/12377

디버깅 기술: 181. windbg - 콜 스택의 "Call Site" 오프셋 값이 가리키는 위치
; https://www.sysnet.pe.kr/2/0/12750




windbg - x64 SOS 확장의 !clrstack 명령어가 출력하는 Child SP 값의 의미

예전에 windbg의 kv 명령어의 출력에 보이는 Child-SP 값에 대한 설명을 했었습니다.

x64 콜 스택 인자 추적과 windbg의 Child-SP, RetAddr, Args to Child 값 확인
; https://www.sysnet.pe.kr/2/0/10832

kv는 부모 함수에서 자식 함수를 call하고 나서의 (반환값이 적재된) RSP 값을 Child-SP로 출력한다고 했는데요. 이게 저 함수를 분석할 때의 상황에서만 맞을 뿐 다른 경우에는 자식의 SP 레지스터가 변경된 것도 포함합니다. 그런데, 디버거는 이런 정보를 (PDB가 없는 상황에서도) 어떻게 알고 Child SP 값을 계산할 수 있을까요? 이에 대해서는 다음의 글에서 설명하는 UNWIND_CODE 정보를 보시면 자세하게 나옵니다. ^^

X64 에 푹 빠져보기 (X64 Deep Dive)
; http://kuaaan.tistory.com/403

Unwind Data for Exception Handling, Debugger Support
; https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64

X64 Deep Dive
; https://codemachine.com/articles/x64_deep_dive.html

자, 그럼 !clrstack의 Child SP 값을 한번 실습해 보겠습니다. ^^

이를 위해 다음과 같은 예제를 만들고,

using System;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.ReadLine();
            Console.WriteLine(Encoding.UTF8.GetString(TestIt()));
        }

        private static byte[] TestIt()
        {
            try
            {
                string str = typeof(Program).ToString();
                byte[] buffer = Encoding.UTF8.GetBytes(string.Format("ClassName: {1}(len={0})", str.Length, str));

                Console.ReadLine();

                return buffer;
            }
            catch { }

            return null;
        }
    }
}

실행 후 windbg로 연결합니다. 연결 시점에 첫 번째 Console.ReadLine으로 인해 중지된 상태이므로 다음과 같이 TestIt 메서드에 BP를 걸고,

0:000> !bpmd ConsoleApp1 ConsoleApp1.Program.TestIt
Found 1 methods in module 00007ffb70605108...
MethodDesc = 00007ffb706059d0
Adding pending breakpoints...

실행을 계속하면 TestIt 시작 지점에서 BP가 걸립니다.

0:000> g
ModLoad: 00007ffb`cd470000 00007ffb`cd584000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll
(1170.65a8): CLR notification exception - code e0444143 (first chance)
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\System32\KERNELBASE.dll - 
JITTED ConsoleApp1!ConsoleApp1.Program.TestIt()
Setting breakpoint: bp 00007FFB70710556 [ConsoleApp1.Program.TestIt()]
Breakpoint 1 hit
00007ffb`70710556 90              nop

이 상태에서 !clrstack을 실행하면 다음과 같이 TestIt 메서드에 대해 Child SP 값을 확인할 수 있습니다.

0:000> !clrstack
OS Thread Id: 0x65a8 (0)
        Child SP               IP Call Site
0000003d97fcefa0 00007ffb70710556 ConsoleApp1.Program.TestIt() [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 19]
0000003d97fcf060 00007ffb707104c0 ConsoleApp1.Program.Main(System.String[]) [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 13]
0000003d97fcf2f0 00007ffbcfd26793 [GCFrame: 0000003d97fcf2f0] 

ConsoleApp1.Program.TestIt
    child sp 0000003d97fcefa0 
    ip       00007ffb70710556 

그리고 IP 값을 통해 역어셈블을 하면 prologue 코드를 확인할 수 있습니다.

0:000> !U /d 00007ffb70710556
Normal JIT generated code
ConsoleApp1.Program.TestIt()
Begin 00007ffb70710520, size 15b

E:\ConsoleApp1\ConsoleApp1\Program.cs @ 19:
00007ffb`70710520 55              push    rbp
00007ffb`70710521 57              push    rdi
00007ffb`70710522 4881eca8000000  sub     rsp,0A8h
00007ffb`70710529 488dac24b0000000 lea     rbp,[rsp+0B0h]
00007ffb`70710531 488dbd78ffffff  lea     rdi,[rbp-88h]
00007ffb`70710538 b920000000      mov     ecx,20h
00007ffb`7071053d 33c0            xor     eax,eax
00007ffb`7071053f f3ab            rep stos dword ptr [rdi]
00007ffb`70710541 4889a570ffffff  mov     qword ptr [rbp-90h],rsp
00007ffb`70710548 833d3950efff00  cmp     dword ptr [00007ffb`70605588],0
00007ffb`7071054f 7405            je      00007ffb`70710556
00007ffb`70710551 e80abfaa5f      call    clr!JIT_DbgIsJustMyCode (00007ffb`d01bc460)
>>> 00007ffb`70710556 90              nop
...[생략]...

그러니까, "X64 에 푹 빠져보기 (X64 Deep Dive)" 글에 의하면 TestIt 함수에 대한 UNWIND_INFO는 다음과 같이 구성되어 있는 것입니다.

UNWIND_INFO
    ALLOC_SMALL, size=0xa8
    PUSH_NONVOL, register=rdi
    PUSH_NONVOL, register=rbp

현재 windbg로 막 진입했는데 IP 주솟값은 00007ffb`70710556을 가리킵니다. 따라서 RSP 레지스터의 현재 값은 이미 prologue를 모두 지난 상태입니다. 그럼 레지스터의 값들을 볼까요?

0:000> r
rax=0000000000000000 rbx=0000003d97fcf1d8 rcx=0000000000000000
rdx=000001e13dc73e18 rsi=000001e13dc73e00 rdi=0000003d97fcf048
rip=00007ffb70710556 rsp=0000003d97fcefa0 rbp=0000003d97fcf050
 r8=0000000000000000  r9=00007ffb00000000 r10=0000000000000000
r11=0000003d97fceaf0 r12=0000000000000000 r13=0000003d97fcf160
r14=0000003d97fcf1d8 r15=0000000000000004
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
00007ffb`70710556 90              nop

RSP = 0000003d97fcefa0
RBP = 0000003d97fcf050

그렇군요. Child SP였던 0000003d97fcefa0 값이 RSP 레지스터의 값과 일치합니다. 따라서 UNWIND_INFO에 있던 스택의 변경이 모두 반영된 값을 Child SP가 가리키고 있는 것입니다.

반면 RBP 값은 prologue 코드 바로 다음에 이어지는 코드를 통해서도 확인할 수 있습니다.

00007ffb`70710520 55              push    rbp
00007ffb`70710521 57              push    rdi
00007ffb`70710522 4881eca8000000  sub     rsp,0A8h
00007ffb`70710529 488dac24b0000000 lea     rbp,[rsp+0B0h]

즉, RSP 값의 + 0xb0이므로 다음과 같이 간단하게 계산됩니다.

RBP = [Child SP + 0xb0]
    = 0000003d`97fcf050

물론 저 값은 r 명령어를 통해 구한 RBP의 값과 일치합니다.




참고로 이번 글을 먼저 읽고 지난 글을 보면 좀 더 이해가 쉬울 것입니다. ^^

windbg - x64 역어셈블 코드에서 닷넷 메서드 호출의 인자를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/11348




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/6/2021]

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

비밀번호

댓글 작성자
 



2023-04-04 10시39분
정성태

1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13718정성태8/27/20247447오류 유형: 921. Visual C++ - error C1083: Cannot open include file: 'float.h': No such file or directory [2]
13717정성태8/26/20247037VS.NET IDE: 192. Visual Studio 2022 - Windows XP / 2003용 C/C++ 프로젝트 빌드
13716정성태8/21/20246768C/C++: 167. Visual C++ - 윈도우 환경에서 _execv 동작 [1]
13715정성태8/19/20247386Linux: 78. 리눅스 C/C++ - 특정 버전의 glibc 빌드 (docker-glibc-builder)
13714정성태8/19/20246767닷넷: 2295. C# 12 - 기본 생성자(Primary constructors) (책 오타 수정) [3]
13713정성태8/16/20247496개발 환경 구성: 721. WSL 2에서의 Hyper-V Socket 연동
13712정성태8/14/20247231개발 환경 구성: 720. Synology NAS - docker 원격 제어를 위한 TCP 바인딩 추가
13711정성태8/13/20248077Linux: 77. C# / Linux - zombie process (defunct process) [1]파일 다운로드1
13710정성태8/8/20248009닷넷: 2294. C# 13 - (6) iterator 또는 비동기 메서드에서 ref와 unsafe 사용을 부분적으로 허용파일 다운로드1
13709정성태8/7/20247767닷넷: 2293. C# - safe/unsafe 문맥에 대한 C# 13의 (하위 호환을 깨는) 변화파일 다운로드1
13708정성태8/7/20247559개발 환경 구성: 719. ffmpeg / YoutubeExplode - mp4 동영상 파일로부터 Audio 파일 추출
13707정성태8/6/20247793닷넷: 2292. C# - 자식 프로세스의 출력이 4,096보다 많은 경우 Process.WaitForExit 호출 시 hang 현상파일 다운로드1
13706정성태8/5/20247910개발 환경 구성: 718. Hyper-V - 리눅스 VM에 새로운 디스크 추가
13705정성태8/4/20248170닷넷: 2291. C# 13 - (5) params 인자 타입으로 컬렉션 허용 [2]파일 다운로드1
13704정성태8/2/20248127닷넷: 2290. C# - 간이 dotnet-dump 프로그램 만들기파일 다운로드1
13703정성태8/1/20247454닷넷: 2289. "dotnet-dump ps" 명령어가 닷넷 프로세스를 찾는 방법
13702정성태7/31/20247865닷넷: 2288. Collection 식을 지원하는 사용자 정의 타입을 CollectionBuilder 특성으로 성능 보완파일 다운로드1
13701정성태7/30/20248138닷넷: 2287. C# 13 - (4) Indexer를 이용한 개체 초기화 구문에서 System.Index 연산자 허용파일 다운로드1
13700정성태7/29/20247759디버깅 기술: 200. DLL Export/Import의 Hint 의미
13699정성태7/27/20248252닷넷: 2286. C# 13 - (3) Monitor를 대체할 Lock 타입파일 다운로드1
13698정성태7/27/20248216닷넷: 2285. C# - async 메서드에서의 System.Threading.Lock 잠금 처리파일 다운로드1
13697정성태7/26/20247934닷넷: 2284. C# - async 메서드에서의 lock/Monitor.Enter/Exit 잠금 처리파일 다운로드1
13696정성태7/26/20247469오류 유형: 920. dotnet publish - error NETSDK1047: Assets file '...\obj\project.assets.json' doesn't have a target for '...'
13695정성태7/25/20247457닷넷: 2283. C# - Lock / Wait 상태에서도 STA COM 메서드 호출 처리파일 다운로드1
13694정성태7/25/20247920닷넷: 2282. C# - ASP.NET Core Web App의 Request 용량 상한값 (Kestrel, IIS)
13693정성태7/24/20247245개발 환경 구성: 717. Visual Studio - C# 프로젝트에서 레지스트리에 등록하지 않은 COM 개체 참조 및 사용 방법파일 다운로드1
1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...